static function command($stmt) { # !<cmd>[:]<param> list($cmd, $param) = self::split_on_optional_char($stmt); $orig_cmd = $cmd; list($accepted, $cmd, $param) = static::resolve_alias($cmd, $param); if (!$accepted) { return static::error('Could not resolve alias for !' . $orig_cmd . ', possibly circular definition'); } $scope =& static::current_scope(); $scope['cmd'] = $cmd; $ColonLF_Exptected = 'colon followed by linefeed and indented code was expected'; switch ($cmd) { case 'php': list($status, $return_value, $output) = static::eval_php('!php', $param); if ($status != 'ok') { return static::error($return_value); } # $output is ignored if ($return_value === false) { # cancel output return ''; } elseif (is_null($return_value)) { return $output; } else { return $return_value; } # $output is ignored # $output is ignored case 'if': @(list($expr, $code) = self::split_on_colonLF($param)); if (is_null($code)) { return static::error('Bad syntax for !if, ' . $ColonLF_Exptected); } list($status, $return_value, $output) = static::eval_expr('!if expression', $expr); if ($status != 'ok') { return static::error($return_value); } $expr_result = $return_value; if ($expr_result) { $scope['if_state'] = true; return static::run_script(Indentation::unindent("\n" . $code), '', '[!if block]'); } else { $scope['if_state'] = false; } break; case 'elseif': if (!isset($scope['prev_cmd']) || !in_array($scope['prev_cmd'], array('if', 'elseif'))) { return static::error('Bad syntax, !elseif only allowed after !if or !elseif'); } if ($scope['if_state'] === true || is_null($scope['if_state'])) { # NULL: error in !if expression return ''; } @(list($expr, $code) = self::split_on_colonLF($param)); if (is_null($code)) { return static::error('Bad syntax for !elseif, ' . $ColonLF_Exptected); } list($status, $return_value, $output) = static::eval_expr('!elseif expression', $expr); if ($status != 'ok') { return static::error($return_value); } $expr_result = $return_value; if ($expr_result) { $scope['if_state'] = true; return static::run_script(Indentation::unindent("\n" . $code), '', '[!elseif block]'); } else { $scope['if_state'] = false; } break; case 'else': if (!isset($scope['prev_cmd']) || !in_array($scope['prev_cmd'], array('if', 'elseif'))) { return static::error('Bad syntax, !else only allowed after !if or !elseif'); } if ($scope['if_state'] === false) { # !! because leading WS is removed from $param @(list($tmp, $param) = explode(':', $stmt, 2)); if (!$param || trim($tmp) != $orig_cmd) { return static::error('Bad syntax for !else, ' . $ColonLF_Exptected); } return static::run_script(Indentation::unindent($param), '', '[!else block]'); } break; case 'loop': @(list($expr, $code) = self::split_on_colonLF($param)); if (is_null($code)) { return static::error('Bad syntax for !loop, ' . $ColonLF_Exptected); } $var_pattern = static::name_pattern('variable'); if (!preg_match('/^(.+)\\s+as\\s+\\$(' . $var_pattern . ')(\\s*=>\\s*\\$(' . $var_pattern . '))?\\s*$/i', $expr, $m)) { return static::error('Invalid syntax for !loop'); } $expr = trim($m[1]); if ($expr && $expr[0] == '[' && substr($expr, -1) == ']') { $expr = 'array(' . trim(substr($expr, 1, -1)) . ')'; } list($status, $return_value, $output) = static::eval_expr('!loop expression', $expr); if ($status != 'ok') { return static::error($return_value); } $arr = $return_value; $varname = isset($m[4]) ? $m[4] : $m[2]; $keyname = isset($m[4]) ? $m[2] : NULL; if (!is_array($arr) && !$arr instanceof Traversable) { return static::error('Array or traversable is required for !loop, got ' . gettype($arr)); } $code = Indentation::unindent("\n" . $code); $loop_output = array(); foreach ($arr as $key => $item) { if (!is_null($keyname)) { $scope['vars'][$keyname] = $key; } $scope['vars'][$varname] = $item; $loop_output[] = static::run_script($code, '', '[!loop block]'); if (self::$break_counter > 0) { self::$break_counter--; break; } if (self::$continue_loop) { self::$continue_loop = false; } } return implode('', $loop_output); case 'while': @(list($expr, $code) = self::split_on_colonLF($param)); if (is_null($code)) { return static::error('Bad syntax for !while, ' . $ColonLF_Exptected); } $code = Indentation::unindent("\n" . $code); $loop_output = array(); list($status, $return_value, $output) = static::eval_expr('!while expression', $expr); if ($status != 'ok') { return static::error($return_value); } while ($return_value) { $loop_output[] = static::run_script($code, '', '[!while block]'); if (self::$break_counter > 0) { self::$break_counter--; break; } if (self::$continue_loop) { self::$continue_loop = false; } list($status, $return_value, $output) = static::eval_expr('!while expression', $expr); if ($status != 'ok') { return static::error($return_value); } } return implode('', $loop_output); case 'break': self::$break_counter = $param && is_numeric($param) ? (int) $param : 1; break; case 'continue': self::$continue_loop = true; break; case 'return': # !return [<num>|to <layout>|with <var-list>] self::$return = $scope['layout_name']; break; case 'scope': list($type, $vars) = self::split_on_optional_char($param); if (!$vars) { return static::error('Bad syntax for !scope, variable name(s) required'); } $type = strtolower($type); switch ($type) { case 'from': list($layout_name, $vars) = array_map('trim', explode(':', $vars, 2)); if (!$vars) { return static::error('Bad syntax for !scope, variable name(s) required'); } $fscope =& static::find_scope($layout_name); if (is_null($fscope)) { return static::error("Scope '{$layout_name}' was not found"); } foreach (array_map('trim', explode(',', $vars)) as $var) { $var = ltrim($var, '$'); $scope['vars'][$var] =& $fscope['vars'][$var]; } break; #case 'static': # vars stored in $layouts[$layout_name]['vars'] #case 'static': # vars stored in $layouts[$layout_name]['vars'] case 'caller': case 'parent': if ($type == 'parent') { $layout_name = $scope['layout_name']; if (!isset(self::$layouts[$layout_name])) { return static::error("!scope parent: Did not find layout metadata for '{$layout_name}'"); } if (!isset(self::$layouts[$layout_name]['parent'])) { static::error("!scope parent: Did not find parent"); } $parent = self::$layouts[$layout_name]['parent']; $pscope =& static::find_scope($parent); if (!$pscope) { return static::error("!scope parent: Did not find scope for '{$parent}'"); } } else { # caller if (count(self::$scope) < 2) { return static::error('There is no caller scope!'); } $pscope =& static::parent_scope(); } foreach (array_map('trim', explode(',', $vars)) as $var) { $var = ltrim($var, '$'); $scope['vars'][$var] =& $pscope['vars'][$var]; } break; case 'global': foreach (array_map('trim', explode(',', $vars)) as $var) { $var = ltrim($var, '$'); $scope['vars'][$var] =& $GLOBALS[$var]; } break; default: return static::error("Invalid scope type '{$type}', expected 'from', 'parent', 'caller' or 'global'"); } break; case 'param': list($accepted, $res) = static::param($param, $scope['vars']['_param']); if (!$accepted) { return static::error($res); } $scope['vars']['_param'] = $res; break; default: if (in_array($cmd, array_keys(self::$custom_commands))) { $callback = self::$custom_commands[$cmd]; return call_user_func($callback, $param); } else { return static::error("Unknown command '!{$cmd}'"); } } return ''; }