= 'A' && $id[0] <= 'Z') { // Object: New/default instance. return new $id(); } else { // Callback: Function. return $id; } } /** * Invoke a callback expression. * * @param string|callable $id * @param array $args * Ordered parameters. To call-by-reference, set an array-parameter by reference. * * @return mixed */ public function call($id, $args) { $cb = $this->get($id); return $cb ? call_user_func_array($cb, $args) : NULL; } } /** * Private helper which produces a dummy callback. * * @package Civi\Core */ class ResolverConstantCallback { /** * @var mixed */ private $value; /** * Class constructor. * * @param mixed $value * The value to be returned by the dummy callback. */ public function __construct($value) { $this->value = $value; } /** * Invoke function. * * @return mixed */ public function __invoke() { return $this->value; } } /** * Private helper which treats an API as a callable function. * * @package Civi\Core */ class ResolverApi { /** * @var array * - string scheme * - string host * - string path * - string query (optional) */ private $url; /** * Class constructor. * * @param array $url * Parsed URL (e.g. "api3://EntityName/action?foo=bar"). * * @see parse_url */ public function __construct($url) { $this->url = $url; } /** * Fire an API call. */ public function __invoke() { $apiParams = []; if (isset($this->url['query'])) { parse_str($this->url['query'], $apiParams); } if (count($apiParams)) { $args = func_get_args(); if (count($args)) { $this->interpolate($apiParams, $this->createPlaceholders('@', $args)); } } $result = civicrm_api3($this->url['host'], ltrim($this->url['path'], '/'), $apiParams); return $result['values'] ?? NULL; } /** * Create placeholders. * * @param string $prefix * @param array $args * Positional arguments. * * @return array * Named placeholders based on the positional arguments * (e.g. "@1" => "firstValue"). */ protected function createPlaceholders($prefix, $args) { $result = []; foreach ($args as $offset => $arg) { $result[$prefix . (1 + $offset)] = $arg; } return $result; } /** * Recursively interpolate values. * * ``` * $params = array('foo' => '@1'); * $this->interpolate($params, array('@1'=> $object)) * assert $data['foo'] == $object; * ``` * * @param array $array * Array which may or many not contain a mix of tokens. * @param array $replacements * A list of tokens to substitute. */ protected function interpolate(&$array, $replacements) { foreach (array_keys($array) as $key) { if (is_array($array[$key])) { $this->interpolate($array[$key], $replacements); continue; } foreach ($replacements as $oldVal => $newVal) { if ($array[$key] === $oldVal) { $array[$key] = $newVal; } } } } } class ResolverGlobalCallback { private $mode; private $path; /** * Class constructor. * * @param string $mode * 'getter' or 'setter'. * @param string $path */ public function __construct($mode, $path) { $this->mode = $mode; $this->path = $path; } /** * Invoke function. * * @param mixed $arg1 * * @return mixed */ public function __invoke($arg1 = NULL) { if ($this->mode === 'getter') { return \CRM_Utils_Array::pathGet($GLOBALS, explode('/', $this->path)); } elseif ($this->mode === 'setter') { \CRM_Utils_Array::pathSet($GLOBALS, explode('/', $this->path), $arg1); return NULL; } else { throw new \RuntimeException("Resolver failed: global:// must specify getter or setter mode."); } } }