/** * to create a token from source * @param string $source * @return fx_template_token */ public static function create($source) { if (!preg_match('~^\\{~', $source)) { $type = 'single'; $name = 'code'; $props['value'] = $source; return new Token($name, $type, $props); } $props = array(); $source = trim($source, '{}'); //preg_replace("~^\{|\}$~", '', $source); $is_close = preg_match('~^\\/~', $source); $source = ltrim($source, '/'); $first_char = substr($source, 0, 1); $is_var = in_array($first_char, array('$', '%')); $is_param = false; if ($is_var && !$is_close) { $ep = new ExpressionParser(); $name = $ep->findVarName(trim($source, '/ ')); } elseif ($first_char === '@') { $name = 'param'; $source = preg_replace_callback("~^@([^\\s]+)~", function ($m) use(&$props) { $props['name'] = $m[1]; return ''; }, $source); $source = trim($source); $is_param = true; } else { preg_match("~^[^\\s\\/\\|}]+~", $source, $name); $name = $name[0]; } if (!$is_param) { $source = substr($source, strlen($name)); } if ($name == 'apply') { $name = 'call'; $props['apply'] = true; } elseif ($name == 'default') { $name = 'set'; $props['default'] = 'true'; } if ($name == 'wtf') { $name = 'help'; } $type_info = self::getTokenInfo($name); $token_close_type = isset($type_info['type']) ? $type_info['type'] : null; //if (preg_match("~^[\\\$%]~", $name, $var_marker)) { if ($is_var) { $props['id'] = preg_replace("~^[\\\$%]~", '', $name); $props['var_type'] = $first_char == '%' ? 'visual' : 'data'; $name = 'var'; } if ($name == 'with-each') { $name = 'with_each'; } if (preg_match("~\\/\$~", $source)) { $type = 'single'; $source = preg_replace("~/\$~", '', $source); } elseif ($is_close) { $type = 'close'; } elseif ($token_close_type == 'single') { $type = 'single'; } elseif ($token_close_type == 'double') { $type = 'open'; } else { $type = 'unknown'; } if ($name === 'preset' && $type !== 'close' && !preg_match("~id\\s*?=~", $source)) { $source = preg_replace_callback("~([a-z0-9\\.\\_\\-\\:]+)\\#?([a-z0-9_-]+)?~", function ($m) { return 'template="' . trim($m[1]) . '" id="' . trim($m[2]) . '"'; }, $source, 1); } if (($name == 'if' || $name == 'elseif') && $type != 'close' && !preg_match('~test=~', $source)) { $props['test'] = $source; } elseif ($name == 'call' && $type != 'close' && !preg_match('~id=~', $source)) { $source = trim($source); $parts = preg_split("~\\s+(with|each)\\s+~", $source, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $props['id'] = array_shift($parts); foreach ($parts as $prop_num => $prop) { if ($prop_num % 2 == 0 && isset($parts[$prop_num + 1])) { $props[$prop] = $parts[$prop_num + 1]; } } } elseif (($name == 'each' || $name == 'with_each') && $type != 'close' && !preg_match('~select=~', $source)) { $props['select'] = trim($source); } elseif ($name == 'set') { $parts = explode("=", $source, 2); $props['var'] = trim($parts[0]); if (isset($parts[1])) { $props['value'] = trim($parts[1]); } } elseif ($name == 'with' && !preg_match("~select=~", $source)) { $props['select'] = trim($source); } else { $props = array_merge($props, TokenAttParser::getAtts($source)); if ($name == 'var' && preg_match("~^\\s*\\|~", $source)) { $props['modifiers'] = self::getVarModifiers($source); } elseif (isset($props['modifiers']) && is_string($props['modifiers'])) { $props['modifiers'] = json_decode($props['modifiers'], true); } } if ($name == 'each' || $name == 'with_each') { $arr_id_parts = null; $item_key = null; $item_alias = null; $arr_id = isset($props['select']) ? $props['select'] : null; if (preg_match("~(.+?)\\sas\\s(.+)\$~", $arr_id, $arr_id_parts)) { $arr_id = trim($arr_id_parts[1]); $as_parts = explode("=>", $arr_id_parts[2]); if (count($as_parts) == 2) { $item_key = trim($as_parts[0]); $item_alias = trim($as_parts[1]); } else { $item_alias = trim($as_parts[0]); } } $props['select'] = $arr_id; $props['as'] = $item_alias; $props['key'] = $item_key; } return new self($name, $type, $props); }
public function transformToFloxim() { $tokens = $this->tokenize(); $tree = $this->makeTree($tokens); $unnamed_replaces = array(); $tree->apply(function (HtmlToken $n) use(&$unnamed_replaces) { if ($n->name == 'text') { return; } if (preg_match('~\\{[\\%|\\$]~', $n->source)) { $n->source = Html::parseFloximVarsInAtts($n->source); } $subroot = $n->hasAttribute('fx:omit') ? '' : ' subroot="true"'; if (($n->name == 'script' || $n->name == 'style') && !$n->hasAttribute('fx:raw')) { $n->setAttribute('fx:raw', 'true'); } if ($n->hasAttribute('fx:raw')) { $raw_value = $n->getAttribute('fx:raw'); if ($raw_value != 'false') { $n->addChildFirst(HtmlToken::create('{raw}')); $n->addChild(HtmlToken::create('{/raw}')); } $n->removeAttribute('fx:raw'); } if ($n->name == 'meta' && ($layout_id = $n->getAttribute('fx:layout'))) { $layout_name = $n->getAttribute('fx:name'); $tpl_tag = '{template id="' . $layout_id . '" name="' . $layout_name . '" of="layout:show"}'; $tpl_tag .= '{apply id="_layout_body"}'; $content = $n->getAttribute('content'); $vars = explode(",", $content); foreach ($vars as $var) { $var = trim($var); $negative = false; if (preg_match("~^!~", $var)) { $negative = true; $var = preg_replace("~^!~", '', $var); } $tpl_tag .= '{$' . $var . ' select="' . ($negative ? 'false' : 'true') . '" /}'; } $tpl_tag .= '{/call}{/template}'; $n->parent->addChildBefore(HtmlToken::create($tpl_tag), $n); $n->remove(); return; } if ($fx_replace = $n->getAttribute('fx:replace')) { $replace_atts = explode(",", $fx_replace); foreach ($replace_atts as $replace_att) { if (!isset($unnamed_replaces[$replace_att])) { $unnamed_replaces[$replace_att] = 0; } $var_name = 'replace_' . $replace_att . '_' . $unnamed_replaces[$replace_att]; $unnamed_replaces[$replace_att]++; $default_val = $n->getAttribute($replace_att); switch ($replace_att) { case 'src': $var_title = fx::alang('Picture', 'system'); break; case 'href': $var_title = fx::alang('Link', 'system'); break; default: $var_title = $replace_att; break; } $n->setAttribute($replace_att, '{%' . $var_name . ' title="' . $var_title . '"}' . $default_val . '{/%' . $var_name . '}'); $n->removeAttribute('fx:replace'); } } if ($var_name = $n->getAttribute('fx:var')) { if (!preg_match("~^[\$\\%]~", $var_name)) { $var_name = '%' . $var_name; } $n->addChildFirst(HtmlToken::create('{' . $var_name . '}')); $n->addChild(HtmlToken::create('{/' . $var_name . '}')); $n->removeAttribute('fx:var'); } $tpl_id = $n->getAttribute('fx:template'); $macro_id = $n->getAttribute('fx:macro'); if ($tpl_id || $macro_id) { if ($macro_id) { $tpl_id = $macro_id; } if (preg_match("~\\[(.+?)\\]~s", $tpl_id, $tpl_test)) { $tpl_test = preg_replace("~[\r\n]~", ' ', $tpl_test[1]); $tpl_id = preg_replace("~\\[.+?\\]~s", '', $tpl_id); } $tpl_macro_tag = '{template id="' . $tpl_id . '" '; if ($macro_id) { $tpl_macro_tag .= ' is_macro="true" '; } $tpl_macro_tag .= $subroot; if ($n->hasAttribute('fx:abstract')) { $tpl_macro_tag .= ' is_abstract="true" '; $n->removeAttribute('fx:abstract'); } if ($tpl_for = $n->getAttribute('fx:of')) { $tpl_macro_tag .= ' of="' . $tpl_for . '"'; $n->removeAttribute('fx:of'); } if ($tpl_test || ($tpl_test = $n->getAttribute('fx:test'))) { $tpl_macro_tag .= ' test="' . $tpl_test . '" '; $n->removeAttribute('fx:test'); } if ($tpl_name = $n->getAttribute('fx:name')) { $tpl_macro_tag .= ' name="' . $tpl_name . '"'; $n->removeAttribute('fx:name'); } if ($n->offset && $n->end_offset) { $tpl_macro_tag .= ' offset="' . $n->offset[0] . ',' . $n->end_offset[1] . '" '; } if ($tpl_size = $n->getAttribute('fx:size')) { $tpl_macro_tag .= ' size="' . $tpl_size . '" '; $n->removeAttribute('fx:size'); } if ($tpl_suit = $n->getAttribute('fx:suit')) { $tpl_macro_tag .= ' suit="' . $tpl_suit . '"'; $n->removeAttribute('fx:suit'); } if ($n->hasAttribute('fx:priority')) { $tpl_priority = $n->getAttribute('fx:priority'); $tpl_macro_tag .= ' priority="' . $tpl_priority . '" '; $n->removeAttribute('fx:priority'); } $tpl_macro_tag .= '}'; $n->wrap($tpl_macro_tag, '{/template}'); $n->removeAttribute('fx:template'); $n->removeAttribute('fx:macro'); } if ($n->hasAttribute('fx:each')) { $each_id = $n->getAttribute('fx:each'); $each_id = trim($each_id, '{}'); $each_id = str_replace('"', '\\"', $each_id); $each_macro_tag = '{each '; $each_macro_tag .= $subroot; $each_macro_tag .= ' select="' . $each_id . '"'; if ($each_as = $n->getAttribute('fx:as')) { $each_macro_tag .= ' as="' . $each_as . '"'; $n->removeAttribute('fx:as'); } if ($each_key = $n->getAttribute('fx:key')) { $each_macro_tag .= ' key="' . $each_key . '"'; $n->removeAttribute('fx:key'); } if ($prefix = $n->getAttribute('fx:prefix')) { $each_macro_tag .= ' prefix="' . $prefix . '"'; $n->removeAttribute('fx:prefix'); } if ($extract = $n->getAttribute('fx:extract')) { $each_macro_tag .= ' extract="' . $extract . '"'; $n->removeAttribute('fx:extract'); } if ($separator = $n->getAttribute('fx:separator')) { $each_macro_tag .= ' separator="' . $separator . '"'; $n->removeAttribute('fx:separator'); } $each_macro_tag .= '}'; $n->wrap($each_macro_tag, '{/each}'); $n->removeAttribute('fx:each'); } if ($area_id = $n->getAttribute('fx:area')) { $n->removeAttribute('fx:area'); $area = '{area id="' . $area_id . '" '; if ($area_size = $n->getAttribute('fx:size')) { $area .= 'size="' . $area_size . '" '; $n->removeAttribute('fx:size'); } elseif ($area_size = $n->getAttribute('fx:area-size')) { // use when fx:area and fx:template are placed on the same node $area .= 'size="' . $area_size . '" '; $n->removeAttribute('fx:area-size'); } if ($area_suit = $n->getAttribute('fx:suit')) { $area .= 'suit="' . $area_suit . '" '; $n->removeAttribute('fx:suit'); } if ($area_render = $n->getAttribute('fx:area-render')) { $area .= 'render="' . $area_render . '" '; $n->removeAttribute('fx:area-render'); } if ($area_name = $n->getAttribute('fx:area-name')) { $area .= 'name="' . $area_name . '" '; $n->removeAttribute('fx:area-name'); } $area .= '}'; $n->addChildFirst(HtmlToken::create($area)); $n->addChild(HtmlToken::create('{/area}')); } if ($n->hasAttribute('fx:item')) { $item_att = $n->getAttribute('fx:item'); $n->removeAttribute('fx:item'); $n->wrap('{item' . ($item_att ? ' test="' . $item_att . '"' : '') . $subroot . '}', '{/item}'); } if ($n->hasAttribute('fx:aif')) { $if_test = $n->getAttribute('fx:aif'); $ep = new ExpressionParser(); $empty_cond = $ep->build($if_test); $class_code = '<?php echo (' . $empty_cond . ' ? "" : " fx_view_hidden ");?>'; $n->addClass($class_code); $n->removeAttribute('fx:aif'); $if_test .= ' || $_is_admin'; $n->wrap('{if test="' . $if_test . '"}', '{/if}'); } if ($n->hasAttribute('fx:if')) { $if_test = $n->getAttribute('fx:if'); $n->removeAttribute('fx:if'); $n->wrap('{if test="' . $if_test . '"}', '{/if}'); } if ($with_each = $n->getAttribute('fx:with-each')) { $n->removeAttribute('fx:with-each'); $weach_macro_tag = '{with-each ' . $with_each . '}'; if ($separator = $n->getAttribute('fx:separator')) { $weach_macro_tag .= '{separator}' . $separator . '{/separator}'; $n->removeAttribute('fx:separator'); } $n->wrap($weach_macro_tag, '{/with-each}'); } if ($with = $n->getAttribute('fx:with')) { $n->removeAttribute('fx:with'); $n->wrap('{with select="' . $with . '" ' . $subroot . '}', '{/with}'); } if ($n->hasAttribute('fx:separator')) { $n->wrap('{separator}', '{/separator}'); $n->removeAttribute('fx:separator'); } if ($elseif_test = $n->getAttribute('fx:elseif')) { $n->removeAttribute('fx:elseif'); $n->wrap('{elseif test="' . $elseif_test . '"}', '{/elseif}'); } if ($n->hasAttribute('fx:else')) { $n->removeAttribute('fx:else'); $n->wrap('{else}', '{/else}'); } if ($n->hasAttribute('fx:add')) { $add_mode = $n->getAttribute('fx:add'); $n->removeAttribute('fx:add'); $n->wrap('<?php $this->pushMode("add", "' . $add_mode . '"); ?>', '<?php $this->popMode("add"); ?>'); } if ($n->hasAttribute('fx:omit')) { $omit = $n->getAttribute('fx:omit'); if (empty($omit) || $omit == 'true') { $omit = true; } else { $ep = new ExpressionParser(); $omit = $ep->compile($ep->parse($omit)); } $n->omit = $omit; $n->removeAttribute('fx:omit'); } if ($n->hasAttribute('fx:e')) { $e_value = $n->getAttribute('fx:e'); $n->addClass('{bem_element}' . $e_value . '{/bem_element}'); $n->removeAttribute('fx:e'); } if ($n->hasAttribute('fx:b')) { $b_value = $n->getAttribute('fx:b'); $n->addClass('{bem_block}' . $b_value . '{/bem_block}'); $n->removeAttribute('fx:b'); $n->parent->addChildAfter(HtmlToken::create('<?php $this->bemStopBlock(); ?>'), $n); } }); $res = $tree->serialize(); //fx::log($res); return $res; }
protected function tokenCallToCode(Token $token) { $each = $token->getProp('each'); if ($each) { $each_token = Token::create('{each}'); $each_token->setProp('select', $each); $item = '$' . $this->varialize($each) . '_item'; $each_token->setProp('as', $item); $token->setProp('each', ''); $each_token->addChild($token); return $this->tokenEachToCode($each_token); } $code = "<?php\n"; $is_apply = $token->getProp('apply'); $tpl_name = $token->getProp('id'); $tpl = '$tpl_' . $this->varialize($tpl_name); $call_children = $token->getChildren(); /* * Converted: * {call id="wrap"}<div>Something</div>{/call} * like this: * {call id="wrap"}{var id="content"}<div>Something</div>{/var}{/call} */ $has_content_param = false; foreach ($call_children as $call_child) { if ($call_child->name == 'code' && $call_child->isEmpty()) { continue; } if ($call_child->name != 'var') { $has_content_param = true; break; } } if ($has_content_param) { $token->clearChildren(); $var_token = new Token('var', 'single', array('id' => 'content')); foreach ($call_children as $call_child) { $var_token->addChild($call_child); } $token->addChild($var_token); } $with_expr = $token->getProp('with'); if ($with_expr) { $ep = new ExpressionParser(); $with_expr = $ep->parseWith($with_expr); } $switch_context = is_array($with_expr) && isset($with_expr['$']); // && !$is_apply; if ($switch_context) { $new_context_expression = $this->parseExpression($with_expr['$']); } $passed_vars = array(); if (is_array($with_expr)) { foreach ($with_expr as $alias => $var) { if ($alias == '$') { continue; } $passed_vars[trim($alias, '$')] = array('string', $this->parseExpression($var)); } } foreach ($token->getChildren() as $param_var_token) { // internal call only handle var if ($param_var_token->name != 'var') { continue; } $value_to_set = ''; if ($param_var_token->hasChildren()) { // pass the inner html code $value_to_set .= "ob_start();\n"; $value_to_set .= $this->childrenToCode($param_var_token); $value_to_set .= "\n"; $passed_value_type = 'buffer'; } elseif ($select_att = $param_var_token->getProp('select')) { // pass the result of executing the php code $value_to_set = self::parseExpression($select_att); $passed_value_type = 'string'; } $passed_vars[$param_var_token->getProp('id')] = array($passed_value_type, $value_to_set); } $switch_context_local = $switch_context && count($passed_vars) > 0; if ($is_apply) { $context_var = '$context'; } else { if (count($passed_vars) > 0 || $switch_context) { $context_var = $tpl . '_context'; $code .= $context_var . " = new \\Floxim\\Floxim\\Template\\" . fx::config('templates.context_class') . "();\n"; } else { $context_var = 'new \\Floxim\\Floxim\\Template\\' . fx::config('templates.context_class') . '()'; } } // switch context to calculate passed vars inside it if ($switch_context_local) { $code .= '$context->push(' . $new_context_expression . ");\n"; } if (count($passed_vars) > 0) { $tpl_passed = $tpl . "_passed"; $code .= $tpl_passed . " = array();\n"; foreach ($passed_vars as $passed_var_key => $passed_var) { switch ($passed_var[0]) { case 'string': default: $code .= $tpl_passed . "['" . $passed_var_key . "'] = " . $passed_var[1] . ";\n"; break; case 'buffer': $code .= $passed_var[1]; $code .= $tpl_passed . "['" . $passed_var_key . "'] = ob_get_clean();\n"; } } // passed vars calculated, clear context if ($switch_context_local) { $code .= "\$context->pop();\n"; } } if ($switch_context) { $code .= $context_var . "->push(" . $new_context_expression . ");\n"; } if (isset($tpl_passed)) { $code .= $context_var . "->push(" . $tpl_passed . ", array('transparent' => true));\n"; } // ------------ $tpl_name_is_expression = !preg_match("~^[a-z0-9_\\,\\.\\:\\@\\#]+\$~", $tpl_name); $loader = "\\Floxim\\Floxim\\Template\\Loader"; // not a plain name if ($tpl_name_is_expression) { $tpl_name = self::parseExpression($tpl_name); $pn = $tpl . '_parsed'; $code .= $pn . ' = ' . $loader . '::parseTemplateName(' . $tpl_name . ', ' . var_export($this->template_set_name, 1) . ', ' . var_export($this->is_aliased, 1) . ");\n"; $code .= $tpl . " = "; $code .= $loader . "::loadTemplateVariant(" . $pn . '["group"], ' . $pn . '["action"], ' . $context_var . ', ' . $pn . '["forced_group"], ' . $pn . '["tags"]); ' . "\n"; } else { $parsed_name = \Floxim\Floxim\Template\Loader::parseTemplateName($tpl_name, $this->template_set_name, $this->is_aliased); foreach ($parsed_name as &$v) { $v = var_export($v, 1); } $code .= $tpl . " = "; $code .= $loader . "::loadTemplateVariant(" . $parsed_name['group'] . ", " . $parsed_name['action'] . ", " . $context_var . ", " . $parsed_name['forced_group'] . ', ' . $parsed_name['tags'] . ");\n"; } /* if (!preg_match("~[\:\@]~", $tpl_name)) { $tpl_name = $this->template_set_name . ":" . $tpl_name; } $tpl_at_parts = explode("@", $tpl_name); if (count($tpl_at_parts) === 1) { $forced_group = 'null'; list($set_name, $action_name) = explode(":", $tpl_name); // save @ for named ("aliased") template groups (like "@admin") if ($set_name === $this->template_set_name && $this->is_aliased) { $set_name = '@'.$set_name; } } else { $forced_group = !empty($tpl_at_parts[0]) ? $tpl_at_parts[0] : $this->template_set_name; $action_parts = explode(":", $tpl_at_parts[1]); if (count($action_parts) === 1) { array_unshift($action_parts, $forced_group); } list($set_name, $action_name) = $action_parts; $forced_group = "'".$forced_group."'"; } $tag_parts = explode("#", $action_name); if (count($tag_parts) > 1) { $action_name = $tag_parts[0]; $tags = "array('".join("', '", explode(",", $tag_parts[1]))."')"; } else { $tags = 'null'; } */ $code .= "if ( " . $tpl . " ) {\n"; $code .= "echo " . $tpl . "->setParent(\$this)->render();\n"; if ($subroot_var = $token->getProp('extract_subroot')) { $code .= $subroot_var . " = " . $tpl . "->is_subroot;\n"; } $code .= "}\n"; // ------------ // clear vars passed into child template from current context if ($is_apply && count($passed_vars) > 0) { $code .= "\$context->pop();\n"; } // clear context object if ($is_apply && $switch_context) { $code .= "\$context->pop();\n"; } $code .= "\n?>"; return $code; }