public static function compile(Compiler $compiler, array $rest, array $tokens) { // load if plugin if (!class_exists('\\Dwoo\\Plugins\\Blocks\\BlockIf', false)) { try { $compiler->getCore()->getLoader()->loadPlugin('if'); } catch (\Exception $e) { throw new CompilationException($compiler, 'Tif: the if plugin is required to use Tif'); } } if (count($rest) == 1) { return $rest[0]; } // fetch false result and remove the ":" if it was present $falseResult = array_pop($rest); if (trim(end($rest), '"\'') === ':') { // remove the ':' if present array_pop($rest); } elseif (trim(end($rest), '"\'') === '?' || count($rest) === 1) { if ($falseResult === '?' || $falseResult === ':') { throw new CompilationException($compiler, 'Tif: incomplete tif statement, value missing after ' . $falseResult); } // there was in fact no false result provided, so we move it to be the true result instead $trueResult = $falseResult; $falseResult = "''"; } // fetch true result if needed if (!isset($trueResult)) { $trueResult = array_pop($rest); // no true result provided so we use the expression arg if ($trueResult === '?') { $trueResult = true; } } // remove the '?' if present if (trim(end($rest), '"\'') === '?') { array_pop($rest); } // check params were correctly provided if (empty($rest) || $trueResult === null || $falseResult === null) { throw new CompilationException($compiler, 'Tif: you must provide three parameters serving as <expression> ? <true value> : <false value>'); } // parse condition $condition = BlockIf::replaceKeywords($rest, $tokens, $compiler); return '((' . implode(' ', $condition) . ') ? ' . ($trueResult === true ? implode(' ', $condition) : $trueResult) . ' : ' . $falseResult . ')'; }
/** * parses a function call * * @param string $in the string within which we must parse something * @param int $from the starting offset of the parsed area * @param int $to the ending offset of the parsed area * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default * @param string $curBlock the current parser-block being processed * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default * * @throws Exception * * @return string parsed values */ protected function parseFunction($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null) { $cmdstr = substr($in, $from, $to - $from); preg_match('/^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*(?:::[a-z_][a-z0-9_]*)?)(\\s*' . $this->rdr . '|\\s*;)?/i', $cmdstr, $match); if (empty($match[1])) { throw new Exception\CompilationException($this, 'Parse error, invalid function name : ' . substr($cmdstr, 0, 15)); } $func = $match[1]; if (!empty($match[2])) { $cmdstr = $match[1]; } if ($this->debug) { Debug::setMessage('FUNC FOUND (' . $func . ')<br />'); } $paramsep = ''; if (is_array($parsingParams) || $curBlock != 'root') { $paramspos = strpos($cmdstr, '('); $paramsep = ')'; } else { if (preg_match_all('#^\\s*[\\\\:a-z0-9_]+(\\s*\\(|\\s+[^(])#i', $cmdstr, $match, PREG_OFFSET_CAPTURE)) { $paramspos = $match[1][0][1]; $paramsep = substr($match[1][0][0], -1) === '(' ? ')' : ''; if ($paramsep === ')') { $paramspos += strlen($match[1][0][0]) - 1; if (substr($cmdstr, 0, 2) === 'if' || substr($cmdstr, 0, 6) === 'elseif') { $paramsep = ''; if (strlen($match[1][0][0]) > 1) { $paramspos--; } } } } else { $paramspos = false; } } $state = 0; if ($paramspos === false) { $params = array(); if ($curBlock !== 'root') { return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer); } } else { if ($curBlock === 'condition') { // load if plugin $this->getPluginType('if'); if (Plugins\Blocks\BlockIf::replaceKeywords(array($func), array(self::T_UNQUOTED_STRING), $this) !== array($func)) { return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer); } } $whitespace = strlen(substr($cmdstr, strlen($func), $paramspos - strlen($func))); $paramstr = substr($cmdstr, $paramspos + 1); if (substr($paramstr, -1, 1) === $paramsep) { $paramstr = substr($paramstr, 0, -1); } if (strlen($paramstr) === 0) { $params = array(); $paramstr = ''; } else { $ptr = 0; $params = array(); if ($func === 'empty') { $params = $this->parseVar($paramstr, $ptr, strlen($paramstr), $params, 'root', $ptr); } else { while ($ptr < strlen($paramstr)) { while (true) { if ($ptr >= strlen($paramstr)) { break 2; } if ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === ')') { if ($this->debug) { Debug::setMessage('PARAM PARSING ENDED, ")" FOUND, POINTER AT ' . $ptr . '<br/>'); } break 2; } elseif ($paramstr[$ptr] === ';') { $ptr++; if ($this->debug) { Debug::setMessage('PARAM PARSING ENDED, ";" FOUND, POINTER AT ' . $ptr . '<br/>'); } break 2; } elseif ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === '/') { if ($this->debug) { Debug::setMessage('PARAM PARSING ENDED, "/" FOUND, POINTER AT ' . $ptr . '<br/>'); } break 2; } elseif (substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) { if ($this->debug) { Debug::setMessage('PARAM PARSING ENDED, RIGHT DELIMITER FOUND, POINTER AT ' . $ptr . '<br/>'); } break 2; } if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === ',' || $paramstr[$ptr] === "\r" || $paramstr[$ptr] === "\n" || $paramstr[$ptr] === "\t") { $ptr++; } else { break; } } if ($this->debug) { Debug::setMessage('FUNC START PARAM PARSING WITH POINTER AT ' . $ptr . '<br/>'); } if ($func === 'if' || $func === 'elseif' || $func === 'tif') { $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'condition', $ptr); } elseif ($func === 'array') { $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'array', $ptr); } else { $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'function', $ptr); } if ($this->debug) { Debug::setMessage('PARAM PARSED, POINTER AT ' . $ptr . ' (' . substr($paramstr, $ptr - 1, 3) . ')<br/>'); } } } $paramstr = substr($paramstr, 0, $ptr); $state = 0; foreach ($params as $k => $p) { if (is_array($p) && is_array($p[1])) { $state |= 2; } else { if ($state & 2 && preg_match('#^(["\'])(.+?)\\1$#', $p[0], $m) && $func !== 'array') { $params[$k] = array($m[2], array('true', 'true')); } else { if ($state & 2 && $func !== 'array') { throw new Exception\CompilationException($this, 'You can not use an unnamed parameter after a named one'); } $state |= 1; } } } } } if ($pointer !== null) { $pointer += (isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func) + (isset($whitespace) ? $whitespace : 0); if ($this->debug) { Debug::setMessage('FUNC ADDS ' . ((isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func)) . ' TO POINTER<br/>'); } } if ($curBlock === 'method' || $func === 'do' || strstr($func, '::') !== false) { // handle static method calls with security policy if (strstr($func, '::') !== false && $this->securityPolicy !== null && $this->securityPolicy->isMethodAllowed(explode('::', strtolower($func))) !== true) { throw new Exception\SecurityException('Call to a disallowed php function : ' . $func); } $pluginType = Core::NATIVE_PLUGIN; } else { $pluginType = $this->getPluginType($func); } // blocks if ($pluginType & Core::BLOCK_PLUGIN) { if ($curBlock !== 'root' || is_array($parsingParams)) { throw new Exception\CompilationException($this, 'Block plugins can not be used as other plugin\'s arguments'); } if ($pluginType & Core::CUSTOM_PLUGIN) { return $this->addCustomBlock($func, $params, $state); } else { return $this->addBlock($func, $params, $state); } } elseif ($pluginType & Core::SMARTY_BLOCK) { if ($curBlock !== 'root' || is_array($parsingParams)) { throw new Exception\CompilationException($this, 'Block plugins can not be used as other plugin\'s arguments'); } if ($state & 2) { array_unshift($params, array('__functype', array($pluginType, $pluginType))); array_unshift($params, array('__funcname', array($func, $func))); } else { array_unshift($params, array($pluginType, $pluginType)); array_unshift($params, array($func, $func)); } return $this->addBlock('smartyinterface', $params, $state); } // funcs if ($pluginType & Core::NATIVE_PLUGIN || $pluginType & Core::SMARTY_FUNCTION || $pluginType & Core::SMARTY_BLOCK) { $params = $this->mapParams($params, null, $state); } elseif ($pluginType & Core::CLASS_PLUGIN) { if ($pluginType & Core::CUSTOM_PLUGIN) { $params = $this->mapParams($params, array($this->customPlugins[$func]['class'], $this->customPlugins[$func]['function']), $state); } else { foreach (array(Core::PLUGIN_BLOCK_CLASS_PREFIX_NAME, Core::PLUGIN_FUNC_CLASS_PREFIX_NAME) as $value) { try { $reflectionClass = new \ReflectionClass($value . Core::underscoreToCamel($func)); $params = $this->mapParams($params, array($reflectionClass->getName(), $pluginType & Core::COMPILABLE_PLUGIN ? 'compile' : 'process'), $state); } catch (\ReflectionException $Exception) { } } } } elseif ($pluginType & Core::FUNC_PLUGIN) { if ($pluginType & Core::CUSTOM_PLUGIN) { $params = $this->mapParams($params, $this->customPlugins[$func]['callback'], $state); } else { $params = $this->mapParams($params, Core::PLUGIN_FUNC_FUNCTION_PREFIX_NAME . Core::underscoreToCamel($func) . ($pluginType & Core::COMPILABLE_PLUGIN ? 'Compile' : ''), $state); } } elseif ($pluginType & Core::SMARTY_MODIFIER) { $output = 'smarty_modifier_' . $func . '(' . implode(', ', $params) . ')'; } elseif ($pluginType & Core::PROXY_PLUGIN) { $params = $this->mapParams($params, $this->getDwoo()->getPluginProxy()->getCallback($func), $state); } elseif ($pluginType & Core::TEMPLATE_PLUGIN) { // transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values)) $map = array(); foreach ($this->templatePlugins[$func]['params'] as $param => $defValue) { if ($param == 'rest') { $param = '*'; } $hasDefault = $defValue !== null; if ($defValue === 'null') { $defValue = null; } elseif ($defValue === 'false') { $defValue = false; } elseif ($defValue === 'true') { $defValue = true; } elseif (preg_match('#^([\'"]).*?\\1$#', $defValue)) { $defValue = substr($defValue, 1, -1); } $map[] = array($param, $hasDefault, $defValue); } $params = $this->mapParams($params, null, $state, $map); } // only keep php-syntax-safe values for non-block plugins $tokens = array(); foreach ($params as $k => $p) { $tokens[$k] = isset($p[2]) ? $p[2] : 0; /** * Transform string true/false to boolean * @todo check with all plugins, there are no error returned */ /*if ($p[0] === 'true' || $p[0] === 'false') { $params[$k] = filter_var($p[0], FILTER_VALIDATE_BOOLEAN); } else {*/ $params[$k] = $p[0]; //} } if ($pluginType & Core::NATIVE_PLUGIN) { if ($func === 'do') { if (isset($params['*'])) { $output = implode(';', $params['*']) . ';'; } else { $output = ''; } if (is_array($parsingParams) || $curBlock !== 'root') { throw new Exception\CompilationException($this, 'Do can not be used inside another function or block'); } else { return self::PHP_OPEN . $output . self::PHP_CLOSE; } } else { if (isset($params['*'])) { $output = $func . '(' . implode(', ', $params['*']) . ')'; } else { $output = $func . '()'; } } } elseif ($pluginType & Core::FUNC_PLUGIN) { if ($pluginType & Core::COMPILABLE_PLUGIN) { if ($pluginType & Core::CUSTOM_PLUGIN) { $funcCompiler = $this->customPlugins[$func]['callback']; } else { $funcCompiler = Core::PLUGIN_FUNC_FUNCTION_PREFIX_NAME . Core::underscoreToCamel($func) . 'Compile'; } array_unshift($params, $this); if ($func === 'tif') { $params[] = $tokens; } $output = call_user_func_array($funcCompiler, $params); } else { array_unshift($params, '$this'); $params = self::implode_r($params); if ($pluginType & Core::CUSTOM_PLUGIN) { $callback = $this->customPlugins[$func]['callback']; $output = 'call_user_func(\'' . $callback . '\', ' . $params . ')'; } else { foreach (array(Core::PLUGIN_BLOCK_FUNCTION_PREFIX_NAME, Core::PLUGIN_FUNC_FUNCTION_PREFIX_NAME) as $value) { try { $reflectionFunction = new \ReflectionFunction($value . Core::underscoreToCamel($func)); $output = '\\' . $reflectionFunction->getName() . '(' . $params . ')'; } catch (\ReflectionException $exception) { } } } } } elseif ($pluginType & Core::CLASS_PLUGIN) { if ($pluginType & Core::COMPILABLE_PLUGIN) { if ($pluginType & Core::CUSTOM_PLUGIN) { $callback = $this->customPlugins[$func]['callback']; if (!is_array($callback)) { if (!method_exists($callback, 'compile')) { throw new Exception('Custom plugin ' . $func . ' must implement the "compile" method to be compilable, or you should provide a full callback to the method to use'); } if (($ref = new \ReflectionMethod($callback, 'compile')) && $ref->isStatic()) { $funcCompiler = array($callback, 'compile'); } else { $funcCompiler = array(new $callback(), 'compile'); } } else { $funcCompiler = $callback; } $output = call_user_func_array($funcCompiler, $params); } else { foreach (array(Core::PLUGIN_BLOCK_CLASS_PREFIX_NAME, Core::PLUGIN_FUNC_CLASS_PREFIX_NAME) as $value) { try { $reflectionClass = new \ReflectionClass($value . Core::underscoreToCamel($func)); array_unshift($params, $this); $output = $reflectionClass->getMethod('compile')->invokeArgs(null, $params); } catch (\ReflectionException $Exception) { } } } } else { $params = self::implode_r($params); if ($pluginType & Core::CUSTOM_PLUGIN) { $callback = $this->customPlugins[$func]['callback']; if (!is_array($callback)) { if (!method_exists($callback, 'process')) { throw new Exception('Custom plugin ' . $func . ' must implement the "process" method to be usable, or you should provide a full callback to the method to use'); } if (($ref = new \ReflectionMethod($callback, 'process')) && $ref->isStatic()) { $output = 'call_user_func(array(\'' . $callback . '\', \'process\'), ' . $params . ')'; } else { $output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback . '\'), \'process\'), ' . $params . ')'; } } elseif (is_object($callback[0])) { $output = 'call_user_func(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), ' . $params . ')'; } elseif (($ref = new \ReflectionMethod($callback[0], $callback[1])) && $ref->isStatic()) { $output = 'call_user_func(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), ' . $params . ')'; } else { $output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback[0] . '\'), \'' . $callback[1] . '\'), ' . $params . ')'; } if (empty($params)) { $output = substr($output, 0, -3) . ')'; } } else { $output = '$this->classCall(\'' . $func . '\', array(' . $params . '))'; } } } elseif ($pluginType & Core::PROXY_PLUGIN) { $output = call_user_func(array($this->dwoo->getPluginProxy(), 'getCode'), $func, $params); } elseif ($pluginType & Core::SMARTY_FUNCTION) { if (isset($params['*'])) { $params = self::implode_r($params['*'], true); } else { $params = ''; } if ($pluginType & Core::CUSTOM_PLUGIN) { $callback = $this->customPlugins[$func]['callback']; if (is_array($callback)) { if (is_object($callback[0])) { $output = 'call_user_func_array(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(array(' . $params . '), $this))'; } else { $output = 'call_user_func_array(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(array(' . $params . '), $this))'; } } else { $output = $callback . '(array(' . $params . '), $this)'; } } else { $output = 'smarty_function_' . $func . '(array(' . $params . '), $this)'; } } elseif ($pluginType & Core::TEMPLATE_PLUGIN) { array_unshift($params, '$this'); $params = self::implode_r($params); $output = $func . $this->templatePlugins[$func]['uuid'] . '(' . $params . ')'; $this->templatePlugins[$func]['called'] = true; } if (is_array($parsingParams)) { $parsingParams[] = array($output, $output); return $parsingParams; } elseif ($curBlock === 'namedparam') { return array($output, $output); } else { return $output; } }