protected function evaluate($context, $expression, $tokens, $silent) { $expression_path = []; foreach ($tokens as $i => $part) { $identifier = $part[self::TOKEN_VALUE]; $expression_path[] = $identifier; switch ($part[self::TOKEN_TYPE]) { case self::TOKEN_TYPE_IDENTIFIER: if (!is_array($context) && !is_object($context)) { throw new \InvalidArgumentException(\ICanBoogie\format('Unexpected variable type: %type (%value) for %identifier in expression %expression, should be either an array or an object', ['%type' => gettype($context), '%value' => $context, '%identifier' => $identifier, '%expression' => $expression])); } $exists = false; $next_value = $this->extract_value($context, $identifier, $exists); if (!$exists) { if ($silent) { return null; } throw new ReferenceError(\ICanBoogie\format('Reference to undefined property %path of expression %expression (defined: :keys) in: :value', ['path' => implode('.', $expression_path), 'expression' => $expression, 'keys' => implode(', ', $context instanceof Context ? $context->keys() : array_keys((array) $context)), 'value' => \ICanBoogie\dump($context)])); } $context = $next_value; break; case self::TOKEN_TYPE_FUNCTION: $method = $identifier; $args = $part[self::TOKEN_ARGS]; $args_evaluate = $part[self::TOKEN_ARGS_EVALUATE]; if ($args_evaluate) { $this->engine->error('we should evaluate %eval', ['%eval' => $args_evaluate]); } # # if value is an object, we check if the object has the method # if (is_object($context) && method_exists($context, $method)) { $context = call_user_func_array([$context, $method], $args); break; } # # well, the object didn't have the method, # we check internal functions # $callback = $this->engine->functions->find($method); # # if no internal function matches, we try string and array functions # depending on the type of the value # if (!$callback) { if (is_string($context)) { if (function_exists('str' . $method)) { $callback = 'str' . $method; } else { if (function_exists('str_' . $method)) { $callback = 'str_' . $method; } } } else { if (is_array($context) || is_object($context)) { if (function_exists('ICanBoogie\\array_' . $method)) { $callback = 'ICanBoogie\\array_' . $method; } else { if (function_exists('array_' . $method)) { $callback = 'array_' . $method; } } } } } # # our last hope is to try the function "as is" # if (!$callback) { if (function_exists($method)) { $callback = $method; } } if (!$callback) { if (is_object($context) && method_exists($context, '__call')) { $context = call_user_func_array([$context, $method], $args); break; } } # # # if (!$callback) { throw new \Exception(\ICanBoogie\format('Unknown method %method for expression %expression.', ['%method' => $method, '%expression' => $expression])); } # # create evaluation # array_unshift($args, $context); if (PHP_MAJOR_VERSION > 5 || PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 2) { if ($callback == 'array_shift') { $context = array_shift($context); } else { $context = call_user_func_array($callback, $args); } } else { $context = call_user_func_array($callback, $args); } break; } } return $context; }
/** * Parses a template with a bounded value. * * <pre> * <p:with * select = expression> * <!-- Content: p:with-param*, template --> * </p:with> * </pre> * * Example: * * <pre> * <p:with select="articles.first().comments.last()"> * Last comment: <a href="#{@url}">#{@title}</a> * </p:with> * </pre> * * @param array $args * @param Engine $patron * @param mixed $template * * @return string */ public static function markup_with(array $args, Engine $patron, $template) { if ($template === null) { $patron->error('A template is required.'); return null; } $select = $args['select']; return $patron($template, $select); }