X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=Civi%2FApi4%2FQuery%2FSqlFunction.php;h=ef5d7747f259a5b5252f137a4e1dd08a1d808e3a;hb=3e5a5b8db16f8b01eaf13fcab27582b09ce0958f;hp=dbc245f7bfc40dfd5e3ed0c5e6dbc74a47614b75;hpb=aef4c7b3f337105999421301c2f7297fc0a47176;p=civicrm-core.git diff --git a/Civi/Api4/Query/SqlFunction.php b/Civi/Api4/Query/SqlFunction.php index dbc245f7bf..ef5d7747f2 100644 --- a/Civi/Api4/Query/SqlFunction.php +++ b/Civi/Api4/Query/SqlFunction.php @@ -18,8 +18,14 @@ namespace Civi\Api4\Query; */ abstract class SqlFunction extends SqlExpression { + /** + * @var array + */ protected static $params = []; + /** + * @var array[] + */ protected $args = []; /** @@ -40,17 +46,22 @@ abstract class SqlFunction extends SqlExpression { */ protected function initialize() { $arg = trim(substr($this->expr, strpos($this->expr, '(') + 1, -1)); - foreach ($this->getParams() as $param) { + foreach ($this->getParams() as $idx => $param) { $prefix = $this->captureKeyword($param['prefix'], $arg); + $this->args[$idx] = [ + 'prefix' => $prefix, + 'expr' => [], + 'suffix' => NULL, + ]; if ($param['expr'] && isset($prefix) || in_array('', $param['prefix']) || !$param['optional']) { - $this->captureExpressions($arg, $param['expr'], $param['must_be'], $param['cant_be']); - $this->captureKeyword($param['suffix'], $arg); + $this->args[$idx]['expr'] = $this->captureExpressions($arg, $param['expr'], $param['must_be'], $param['cant_be']); + $this->args[$idx]['suffix'] = $this->captureKeyword($param['suffix'], $arg); } } } /** - * Shift a keyword off the beginning of the argument string and into the argument array. + * Shift a keyword off the beginning of the argument string and return it. * * @param array $keywords * Whitelist of keywords @@ -60,7 +71,6 @@ abstract class SqlFunction extends SqlExpression { private function captureKeyword($keywords, &$arg) { foreach (array_filter($keywords) as $key) { if (strpos($arg, $key . ' ') === 0) { - $this->args[] = $key; $arg = ltrim(substr($arg, strlen($key))); return $key; } @@ -69,35 +79,34 @@ abstract class SqlFunction extends SqlExpression { } /** - * Shifts 0 or more expressions off the argument string and into the argument array + * Shifts 0 or more expressions off the argument string and returns them * * @param string $arg * @param int $limit * @param array $mustBe * @param array $cantBe + * @return array * @throws \API_Exception */ private function captureExpressions(&$arg, $limit, $mustBe, $cantBe) { - $captured = 0; + $captured = []; $arg = ltrim($arg); while ($arg) { $item = $this->captureExpression($arg); $arg = ltrim(substr($arg, strlen($item))); $expr = SqlExpression::convert($item, FALSE, $mustBe, $cantBe); $this->fields = array_merge($this->fields, $expr->getFields()); - if ($captured) { - $this->args[] = ','; - } - $this->args[] = $expr; + $captured[] = $expr; $captured++; // Keep going if we have a comma indicating another expression follows - if ($captured < $limit && substr($arg, 0, 1) === ',') { + if (count($captured) < $limit && substr($arg, 0, 1) === ',') { $arg = ltrim(substr($arg, 1)); } else { - return; + break; } } + return $captured; } /** @@ -147,20 +156,50 @@ abstract class SqlFunction extends SqlExpression { return $item; } + /** + * Render the expression for insertion into the sql query + * + * @param array $fieldList + * @return string + */ public function render(array $fieldList): string { - $output = $this->getName() . '('; + $output = ''; + $params = $this->getParams(); foreach ($this->args as $index => $arg) { - if ($index && $arg !== ',') { - $output .= ' '; - } - if (is_object($arg)) { - $output .= $arg->render($fieldList); + $rendered = $this->renderArg($arg, $params[$index], $fieldList); + if (strlen($rendered)) { + $output .= (strlen($output) ? ' ' : '') . $rendered; } - else { - $output .= $arg; + } + return $this->getName() . '(' . $output . ')'; + } + + /** + * @param array $arg + * @param array $param + * @param array $fieldList + * @return string + */ + private function renderArg($arg, $param, $fieldList): string { + // Supply api_default + if (!isset($arg['prefix']) && !isset($arg['suffix']) && empty($arg['expr']) && !empty($param['api_default'])) { + $arg = [ + 'prefix' => $param['api_default']['prefix'] ?? reset($param['prefix']), + 'expr' => array_map([parent::class, 'convert'], $param['api_default']['expr'] ?? []), + 'suffix' => $param['api_default']['suffix'] ?? reset($param['suffix']), + ]; + } + $rendered = $arg['prefix'] ?? ''; + foreach ($arg['expr'] ?? [] as $idx => $expr) { + if (strlen($rendered) || $idx) { + $rendered .= $idx ? ', ' : ' '; } + $rendered .= $expr->render($fieldList); + } + if (isset($arg['suffix'])) { + $rendered .= (strlen($rendered) ? ' ' : '') . $arg['suffix']; } - return $output . ')'; + return $rendered; } /** @@ -194,11 +233,20 @@ abstract class SqlFunction extends SqlExpression { 'optional' => FALSE, 'must_be' => [], 'cant_be' => ['SqlWild'], + 'api_default' => NULL, ]; } return $params; } + /** + * Get the arguments passed to this sql function instance. + * @return array[] + */ + public function getArgs(): array { + return $this->args; + } + /** * @return string */