Tag = $tag; } function Open($tag) { if (!$this->_checkRequiredParams($tag)) { return false; } return ''; } /** * Checks, that all required attributes for tag are passed * * @param Array $tag * @return bool */ function _checkRequiredParams($tag) { $missing_params = array_diff($this->_requiredParams, array_keys($tag['NP'])); if (!$missing_params) { return true; } $error_msg = 'Tag ' . $this->Parser->TagInfo($tag, true) . ' called without required ' . implode(', ', $missing_params) . ' attribute'; if (count($missing_params) > 1) { $error_msg .= '(-s)'; } throw new ParserException($error_msg, 0, null, $tag); return false; } /** * All tags inside block tag are passed through to this method * Any returned result is appened to current level's buffer * This can be used to implement special handling of such tags as * * @param unknown_type $tag * @return unknown */ function PassThrough(&$tag) { return ''; } function Close($tag) { return $this->Parser->Buffers[$this->Parser->Level]; } function AppendCode(&$o, $code, $php_tags=true) { if ($php_tags) { $o .= ''; } else { $o .= ( is_array($code) ? "\t".implode("\n\t", $code)."\n" : $code); } } } class _Tag_Comment extends _BlockTag { function Open($tag) { $this->Parser->SkipComments = false; return ''; } function Close($tag) { $this->Parser->SkipComments = true; return $this->Parser->Buffers[$this->Parser->Level]; } } class _Tag_DefineElement extends _BlockTag { /*var $ElemName; function Open($tag) { $o = ''; $pointer = abs(crc32($tag['file'])).'_'.$tag['line']; $f_name = $tag['NP']['name'].'_'.$pointer; $this->ElemName = $tag['NP']['name']; $code[] = "\$_parser->Elements['{$tag['NP']['name']}'] = '$f_name';"; // function_exists is required here, because a template may be included more than once // leading to Cannot redeclare error $code[] = "if (!function_exists('{$f_name}')) {"; $code[] = "function $f_name(&\$_parser, \$params) {"; $code[] = "global \$application;"; $code[] = "if (!\$_output = \$_parser->CacheStart('{$pointer}')) {"; $defaults = $this->Parser->CompileParamsArray($tag['NP']); $code[] = "\$params = array_merge($defaults, \$params);"; $code[] = "if (!isset(\$params['PrefixSpecial']) && isset(\$params['prefix'])) {\$params['PrefixSpecial'] = \$params['prefix'];};"; $code[] = "extract(\$params);"; $code[] = "\$_parser->SetParams(\$params);"; $code[] = 'ob_start();'; $this->AppendCode($o, $code, false); return $o . '?'.'>'; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $code[] = "\$_parser->CacheEnd();\n"; $code[] = '$_output = ob_get_contents();'; $code[] = 'ob_end_clean();'; $code[] = '}'; $code[] = "return \$_output === true ? '' : \$_output;"; $code[] = '}}'; $code[] = "\$_parser->CachableElements['".$this->ElemName."'] = ".($this->Parser->Cachable[$this->Parser->Level] ? 'true':'false').';'; $o .= 'AppendCode($o, $code, false); return $o; // $this->Parser->Definitions .= $o."\n"; // return ''; } */ public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('name'); } function Open($tag) { $o = parent::Open($tag); if ($o === false) { // some required params not passed return $o; } $f_name = $tag['NP']['name'].'_'.abs(crc32($tag['file'])).'_'.$tag['line']; $this->Tag['function_name'] = $f_name; // for later use in closing tag $code[] = "\$_parser->Elements['{$tag['NP']['name']}'] = '$f_name';"; // function_exists is required here, because a template may be included more than once // leading to Cannot redeclare error $code[] = "if (!function_exists('{$f_name}')) {"; $code[] = "function $f_name(&\$_parser, \$params) {"; $code[] = "global \$application;"; $tag['NP'] = $this->_extractParams($tag['NP']); $defaults = $this->Parser->CompileParamsArray($tag['NP']); $code[] = "\$params = array_merge($defaults, \$params);"; $code[] = "if (!isset(\$params['PrefixSpecial']) && isset(\$params['prefix'])) {\$params['PrefixSpecial'] = \$params['prefix'];};"; $code[] = 'extract($params, EXTR_SKIP);'; $code[] = "\$_parser->SetParams(\$params);"; $code[] = 'ob_start();'; $this->AppendCode($o, $code); return $o; } /** * Converts $param_name to $params['param_name'] * * @param Array $params * @return Array */ function _extractParams($params) { foreach ($params as $param_name => $param_value) { $params[$param_name] = preg_replace('/[\{]{0,1}([\$])(.*?[^\$\s\{\}]*)[\}]{0,1}/', '{$params[\'\\2\']}', $param_value); } return $params; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $code[] = '$_output = ob_get_contents();'; $code[] = 'ob_end_clean();'; $code[] = 'return $_output;'; $code[] = '}}'; $end_pos = $tag['pos'] + strlen($tag['opening']) + strlen($tag['tag']) + strlen($tag['closing']) + TAG_NAMESPACE_LENGTH; $code[] = "\$_parser->ElementLocations['{$this->Tag['function_name']}'] = Array('template' => '{$this->Tag['template']}', 'start_pos' => {$this->Tag['pos']}, 'end_pos' => {$end_pos});"; $this->AppendCode($o, $code); return $o; } } class _Tag_Capture extends _Tag_DefineElement { public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('to_var'); } function Open($tag) { if (!$this->_checkRequiredParams($tag)) { return false; } $tag['NP']['name'] = '__capture_'.$tag['NP']['to_var']; $o = ''; // $this->AppendCode($o, "\$_parser->Captures['{$tag['NP']['to_var']}'] = 1;", false); $this->AppendCode($o, "\$_parser->Captures['{$tag['NP']['to_var']}'] = 1;"); $o .= parent::Open($tag); return $o; } } class _Tag_RenderElement extends _Tag_DefineElement { var $Single = true; var $OriginalTag; var $_lambdaName = ''; public function __construct($tag) { parent::__construct($tag); if (!$tag['is_closing']) { $this->_requiredParams = Array ('design'); } } function Open($tag) { if (!$this->_checkRequiredParams($tag)) { return false; } $o = ''; if ($tag['is_closing']) { if (isset($tag['NP']['design'])) { $this->RenderDesignCode($o, $tag['NP']); return $o; } $to_pass = $this->Parser->CompileParamsArray($tag['NP']); /* $pointer = abs(crc32($tag['file'])).'_'.$tag['line']; // $code[] = "}"; // $code[] = "if (!\$_parser->CachableElements['".$tag['NP']['name']."']) {"; // $code[] = "\$_parser->CacheEndInside();"; // $code[] = "}"; $o .= 'AppendCode($o, $this->Parser->BreakCache('', $pointer.'a', "\$_parser->CachableElements['".$tag['NP']['name']."']"), false); $this->AppendCode($o, "echo (\$_parser->ParseBlock($to_pass));\n", false); $this->AppendCode($o, $this->Parser->BreakCache('', $pointer.'b') . " ?".">\n", false); // $this->AppendCode($o, "if (!\$_parser->CacheStartOrContinue(\$_parser->CachableElements['".$tag['NP']['name']."'], '{$pointer}')) {".' ?'.'>', false); */ $code = array("echo (\$_parser->ParseBlock($to_pass));"); if ( array_key_exists('result_to_var', $tag['NP']) && $tag['NP']['result_to_var'] ) { $param_name = $tag['NP']['result_to_var']; $code[] = "\$params['{$param_name}'] = \$_parser->GetParam('{$param_name}');"; $code[] = "\${$param_name} = \$params['{$param_name}'];"; } $this->AppendCode($o, $code); return $o; } $this->Single = false; $this->OriginalTag = $tag; $tag['NP']['name'] = $this->createSafeFunctionName($tag); return parent::Open($tag); } function RenderDesignCode(&$o, $params) { $to_pass = $this->Parser->CompileParamsArray($params); $code[] = "echo (\$_parser->ParseBlock(array_merge($to_pass, array('name'=>\"{$params['design']}\",'content'=>\$_parser->ParseBlock($to_pass), 'keep_data_exists'=>1))));"; $this->AppendCode($o, $code); } function Close($tag) { if ($this->Single) { return $this->Parser->Buffers[$this->Parser->Level]; } $o = parent::Close($tag); $this->OriginalTag['NP']['name'] = $this->createSafeFunctionName($this->OriginalTag); $this->RenderDesignCode($o, $this->OriginalTag['NP']); return $o; } /** * Creates safe function name based on design used in a given tag. * * @param array $tag Tag. * * @return string */ protected function createSafeFunctionName(array $tag) { $safe_design = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $tag['NP']['design']); return $safe_design . '_' . abs(crc32($tag['file'])) . '_' . $tag['line']; // Former name '__lambda'. } } class _Tag_RenderElements extends _BlockTag { public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('elements'); } function Open($tag) { $o = parent::Open($tag); if ($o === false) { // some required params not passed return $o; } $element_names = array_map('trim', explode(',', $tag['NP']['elements'])); unset($tag['NP']['elements']); $class = '_Tag_RenderElement'; /** @var _Tag_RenderElement $instance */ $instance = new $class($tag); $instance->Parser =& $this->Parser; $skip_elements = array_key_exists('skip', $tag['NP']) ? array_map('trim', explode(',', $tag['NP']['skip'])) : Array (); foreach ($element_names as $element_name) { if (in_array($element_name, $skip_elements) || !$element_name) { // empty element name OR element should be excluded continue; } $tag['NP']['name'] = $element_name; $o .= $instance->Open($tag); } return $o; } } class _Tag_Param extends _BlockTag { public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('name'); } function Open($tag) { $o = parent::Open($tag); if ( $o === false ) { // some required params not passed return $o; } $param_name = $tag['NP']['name']; $capture_params = $tag['NP']; $capture_params['name'] = '__capture_' . $param_name; $capture_to_pass = $this->Parser->CompileParamsArray($capture_params); $code[] = "if (isset(\$_parser->Captures[\"{$param_name}\"])) {"; $code[] = "\t\${$param_name} = \$_parser->ParseBlock($capture_to_pass);"; $code[] = "\t\$params[\"{$param_name}\"] = \${$param_name};"; $code[] = "\t\$_param_value = \${$param_name};"; $code[] = "}"; $code[] = "else {"; $to_pass = $this->Parser->CompileParamsArray($tag['NP']); $code[] = "\t" . '$_p_ =& $_parser->GetProcessor(\'m\');'; $code[] = "\t" . '$_tag_params = ' . $to_pass . ';'; $code[] = "\t\$_param_value = \$_p_->PostProcess(\${$param_name}, \$_p_->PreparePostProcess(\$_tag_params));"; $code[] = "}"; if ( array_key_exists('result_to_var', $tag['NP']) && $tag['NP']['result_to_var'] ) { $result_to_var = $tag['NP']['result_to_var']; $code[] = "\${$result_to_var} = \$_parser->GetParam('{$result_to_var}');"; if ( array_key_exists('plus', $tag['NP']) ) { $code[] = "\${$result_to_var} += {$tag['NP']['plus']};"; } $code[] = "\$params['{$result_to_var}'] = \${$result_to_var};"; } elseif ( array_key_exists('plus', $tag['NP']) ) { $code[] = "\$_param_value += {$tag['NP']['plus']};"; } $code[] = "echo (\$_param_value);"; $this->AppendCode($o, $code); return $o; } } class _Tag_Include extends _BlockTag { function Open($tag) { $o = ''; $to_pass = $this->Parser->CompileParamsArray($tag['NP']); $this->AppendCode($o, "echo (\$_parser->IncludeTemplate($to_pass))"); return $o; } } class _Tag_If extends _BlockTag { /** * Permanently inverses if tag (used in ifnot tag) * * @var bool */ var $_Inversed = false; /** * Count of "elseif" tags inside * * @var int */ var $_elseIfCount = 0; public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('check'); } function Open($tag) { $o = parent::Open($tag); if ($o === false) { // some required params not passed return $o; } $this->AppendCheckCode($o, $tag); return $o; } /** * Adds check code to $o * * @param string $o * @param Array $tag * @return void * @access protected */ protected function AppendCheckCode(&$o, $tag) { $to_pass = $this->Parser->CompileParamsArray($tag['NP']); $code = Array (); $code[] = "\$_splited = \$_parser->SplitTag(array('tag'=>\"{$tag['NP']['check']}\", 'file'=>'{$tag['file']}', 'line'=>{$tag['line']}));"; $code[] = 'if ($_splited[\'prefix\'] == \'__auto__\') {$_splited[\'prefix\'] = $PrefixSpecial;}'; $code[] = '$_p_ =& $_parser->GetProcessor($_splited[\'prefix\']);'; if ( isset($tag['NP']['inverse']) || $this->_Inversed ) { $code[] = "if (!\$_p_->ProcessParsedTag(\$_splited['name'], $to_pass, \$_splited['prefix'], '{$tag['file']}', {$tag['line']})) {"; } else { $code[] = "if (\$_p_->ProcessParsedTag(\$_splited['name'], $to_pass, \$_splited['prefix'], '{$tag['file']}', {$tag['line']})) {"; } $this->AppendCode($o, $code); } function PassThrough(&$tag) { $o = ''; if ($tag['name'] == 'else') { $this->AppendCode($o, '} else {'); $tag['processed'] = true; } if ($tag['name'] == 'elseif') { if (!$this->_checkRequiredParams($tag)) { return ''; } $this->_elseIfCount++; // add same count of closing brackets in closing tag $this->AppendCode($o, "} else {"); $this->AppendCheckCode($o, $tag); $tag['processed'] = true; } return $o; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $code = str_repeat("}\n", $this->_elseIfCount + 1); $this->AppendCode($o, $code); return $o; } } class _Tag_IfNot extends _Tag_If { public function __construct($tag) { parent::__construct($tag); $this->_Inversed = true; } } class _Tag_DefaultParam extends _BlockTag { function Open($tag) { $o = ''; foreach ($tag['NP'] as $key => $val) { $code[] = 'if (!isset($' . $key . ')) $params["' . $key . '"] = "' . $val . '";'; $code[] = '$' . $key . ' = isset($' . $key . ') ? $' . $key . ' : "' . $val . '";'; $code[] = '$_parser->SetParam("' . $key . '", $' . $key . ');'; } $this->AppendCode($o, $code); return $o; } } class _Tag_SetParam extends _BlockTag { function Open($tag) { $o = ''; foreach ($tag['NP'] as $key => $val) { $code[] = '$params[\'' . $key . '\'] = "' . $val . '";'; $code[] = '$' . $key . ' = "' . $val . '";'; $code[] = '$_parser->SetParam(\'' . $key . '\', $' . $key . ');'; } $this->AppendCode($o, $code); return $o; } } class _Tag_Cache extends _BlockTag { function Open($tag) { $o = ''; $pointer = abs(crc32($tag['file'])).'_'.$tag['line']; $key = array_key_exists('key', $tag['NP']) ? $tag['NP']['key'] : ''; $this->AppendCode($o, "if (!\$_parser->CacheStart('{$pointer}', \"{$key}\")) {\n"); return $o; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $cache_timeout = array_key_exists('cache_timeout', $this->Tag['NP']) ? $this->Tag['NP']['cache_timeout'] : 0; $this->AppendCode($o, "\$_parser->CacheEnd(" . (int)$cache_timeout . ");\n}\n"); return $o; } } class _Tag_IfDataExists extends _BlockTag { function Open($tag) { $o = ''; $code = array(); $code[] = "ob_start();\n"; $code[] = '$_tmp_data_exists = $_parser->DataExists;'; $code[] = '$_parser->DataExists = false;'; $this->AppendCode($o, $code); return $o; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $code = array(); $code[] = '$res = ob_get_clean();'; $code[] = 'if ($_parser->DataExists) {echo $res;}'; if (array_key_exists('block_no_data', $this->Tag['NP'])) { $tag_params = $this->Tag['NP']; $tag_params['name'] = $tag_params['block_no_data']; unset($tag_params['block_no_data']); $to_pass = $this->Parser->CompileParamsArray($tag_params); $code[] = "else { echo (\$_parser->ParseBlock($to_pass)); }"; } $code[] = '$_parser->DataExists = $_tmp_data_exists;'; $this->AppendCode($o, $code); return $o; } } class _Tag_Compress extends _BlockTag { public function __construct($tag) { parent::__construct($tag); if ($tag['is_closing']) { if (!array_key_exists('files', $tag['NP'])) { $this->_requiredParams = Array ('from'); } else { $this->_requiredParams = Array ('files'); } } else { $this->_requiredParams = Array ('type'); // js/css } } /** * When used as non-block tag, then compress given files and return url to result * * @param Array $tag * @return string */ function Open($tag) { $o = parent::Open($tag); if ($o === false) { // some required params not passed return $o; } if ($tag['is_closing']) { $to_pass = $this->Parser->CompileParamsArray($tag['NP']); $this->AppendCode($o, "echo \$_parser->CompressScript($to_pass, false);"); } else { $this->AppendCode($o, "ob_start();"); } return $o; } /** * When used as block tag, then compress contents between tags * * @param Array $tag * @return string */ function Close($tag) { if ($this->Tag['is_closing']) { return parent::Close($tag); } $o = parent::Close($tag); $code = Array (); $code[] = '$res = ob_get_clean();'; $code[] = 'echo $_parser->CompressScript($res, true, \'' . $this->Tag['NP']['type'] . '\');'; $this->AppendCode($o, $code); return $o; } }