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 . ')';
 }
Пример #2
0
 /**
  * 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;
     }
 }