Index: branches/RC/core/kernel/nparser/ntags.php =================================================================== diff -u -N -r10604 -r10610 --- branches/RC/core/kernel/nparser/ntags.php (.../ntags.php) (revision 10604) +++ branches/RC/core/kernel/nparser/ntags.php (.../ntags.php) (revision 10610) @@ -72,9 +72,14 @@ return $this->Parser->Buffers[$this->Parser->Level]; } - function AppendCode(&$o, $code) + function AppendCode(&$o, $code, $php_tags=true) { - $o .= ''; + if ($php_tags) { + $o .= ''; + } + else { + $o .= ( is_array($code) ? "\t".implode("\n\t", $code)."\n" : $code); + } } } @@ -92,7 +97,56 @@ } 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 ''; + } */ + function _Tag_DefineElement($tag) { parent::_BlockTag($tag); @@ -155,6 +209,7 @@ $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; @@ -187,6 +242,18 @@ 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); +*/ $this->AppendCode($o, "echo (\$_parser->ParseBlock($to_pass));"); return $o; } @@ -344,3 +411,22 @@ return $o; } } + +class _Tag_Cache extends _BlockTag { + + function Open($tag) + { + $pointer = abs(crc32($tag['file'])).'_'.$tag['line']; + $o = ''; + $this->AppendCode($o, "if (!\$_parser->CacheStart('{$pointer}')) {\n"); + return $o; + } + + function Close($tag) + { + $o = $this->Parser->Buffers[$this->Parser->Level]; + $this->AppendCode($o, "\$_parser->CacheEnd();\n}\n"); + return $o; + } + +} Index: branches/RC/core/kernel/processors/tag_processor.php =================================================================== diff -u -N -r9969 -r10610 --- branches/RC/core/kernel/processors/tag_processor.php (.../tag_processor.php) (revision 9969) +++ branches/RC/core/kernel/processors/tag_processor.php (.../tag_processor.php) (revision 10610) @@ -59,6 +59,22 @@ } } + function FormCacheKey($tag, $params, $prefix) + { + $k = $this->Application->Parser->TemplateName; + $k .= $prefix.'_'.$tag; + foreach (array('m_cat_id','m_cat_page','m_lang','m_theme') as $m_var) { + $params[$m_var] = $this->Application->GetVar($m_var); + } + + ksort($params); + foreach ($params as $p => $v) { + $k .= "$p=$v&"; + } + + return md5($k); + } + function ProcessParsedTag($tag, $params, $prefix, $file='unknown', $line=0) { $Method = $tag; @@ -69,7 +85,14 @@ $backup_prefix = $this->Prefix; $backup_special = $this->Special; - + + if ($this->Application->ConfigValue('SystemTagCache') && isset($params['cache_timeout'])) { + if ($res = $this->Application->CacheGet($this->FormCacheKey($tag, $params, $prefix))) { + return $res; + } + } + + $original_params = $params; $flag_values = $this->PreparePostProcess($params); // pass_params for non ParseBlock tags :) @@ -82,7 +105,11 @@ $this->Prefix = $backup_prefix; $this->Special = $backup_special; - return $this->PostProcess($ret, $flag_values); + $ret = $this->PostProcess($ret, $flag_values); + if ($this->Application->ConfigValue('SystemTagCache') && $flag_values['cache_timeout'] && isset($this->Application->Memcached)) { + $this->Application->CacheSet($this->FormCacheKey($tag, $original_params, $prefix), $ret, $flag_values['cache_timeout']); + } + return $ret; } else { list ($ret, $tag_found) = $this->processAggregatedTag($tag, $params, $prefix, $file, $line); @@ -134,7 +161,7 @@ function PreparePostProcess(&$params) { - $flags = Array('js_escape', 'equals_to', 'result_to_var', 'pass_params', 'html_escape', 'strip_nl', 'trim'); + $flags = Array('js_escape', 'equals_to', 'result_to_var', 'pass_params', 'html_escape', 'strip_nl', 'trim', 'cache_timeout'); $flag_values = Array(); foreach ($flags as $flag_name) { Index: branches/RC/core/kernel/processors/main_processor.php =================================================================== diff -u -N -r10315 -r10610 --- branches/RC/core/kernel/processors/main_processor.php (.../main_processor.php) (revision 10315) +++ branches/RC/core/kernel/processors/main_processor.php (.../main_processor.php) (revision 10610) @@ -1149,4 +1149,10 @@ $db->Query($sql); } } + + function Timestamp($params) + { + $format = isset($params['format']) ? $params['format'] : 'd.m.Y H:i:s'; + return adodb_date($format); + } } Index: branches/RC/core/kernel/nparser/nparser.php =================================================================== diff -u -N -r10598 -r10610 --- branches/RC/core/kernel/nparser/nparser.php (.../nparser.php) (revision 10598) +++ branches/RC/core/kernel/nparser/nparser.php (.../nparser.php) (revision 10610) @@ -13,9 +13,19 @@ var $Params = array(); var $ParamsStack = array(); var $ParamsLevel = 0; + + var $Definitions = ''; var $Elements = array(); // holds dynamic elements to function names mapping during execution var $DataExists = false; + + var $TemplateName = null; + var $TempalteFullPath = null; + + var $CachePointers = array(); + var $Cachable = array(); + + function NParser() @@ -51,7 +61,13 @@ function CompileRaw($data, $t_name) { $code = "extract (\$_parser->Params);\n"; + +// $code .= "__@@__DefinitionsMarker__@@__\n"; + +// $code .= "if (!\$this->CacheStart('".abs(crc32($t_name))."_0')) {\n"; $this->Buffers[0] = '\n"; + $this->Cacheable[0] = true; + $this->Definitions = ''; // finding all the tags $reg = '(.*?)(<[\\/]?)inp2:([^>]*?)([\\/]?>)(\r\n){0,1}'; @@ -98,6 +114,8 @@ // if no tag was found at all ($tag_data is not set) - append the whole $data $this->Buffers[$this->Level] .= isset($tag_data) ? substr($data, $tag_data[4][1]+strlen($tag_data[4][0])) : $data; $this->Buffers[$this->Level] = preg_replace('//s', '', $this->Buffers[$this->Level]); // remove hidden comments IB#23065 +// $this->Buffers[$this->Level] .= 'CacheEnd();\n}\n"." ?".">\n"; +// $this->Buffers[$this->Level] = str_replace('__@@__DefinitionsMarker__@@__', $this->Definitions, $this->Buffers[$this->Level]); } function SplitParamsStr($params_str) @@ -145,6 +163,7 @@ /* @var $instance _BlockTag */ $this->Stack[++$this->Level] =& $instance; $this->Buffers[$this->Level] = ''; + $this->Cachable[$this->Level] = true; $open_code = $instance->Open($tag); if ($open_code === false) return false; $o .= $open_code; @@ -182,12 +201,28 @@ if (!$tag['processed']) { $compiled = $this->CompileTag($tag); if ($compiled === false) return false; - $o .= '\n"; + if (isset($tag['NP']['cachable']) && (!$tag['NP']['cachable'] || $tag['NP']['cachable'] == 'false')) { + $this->Cachable[$this->Level] = false; + } + $o .= '\n"; +// $o .= 'BreakCache($compiled, $this->GetPointer($tag)) : $compiled; +// $o .= " ?".">\n"; } } $this->Buffers[$this->Level] .= $o; return true; } + + function GetPointer($tag) + { + return abs(crc32($tag['file'])).'_'.$tag['line']; + } + + function BreakCache($code, $pointer, $condition='') + { + return "\$_parser->CacheEnd();\n}\n" . $code."\nif ( !\$_parser->CacheStart('{$pointer}'" . ($condition ? ", {$condition}" : '') . ") ) {\n"; + } function TagInfo($tag, $with_params=false) { @@ -254,22 +289,25 @@ { $pre_parsed = $this->CheckTemplate($t, $silent); if (!$pre_parsed) return false; + $backup_template = $this->TemplateName; + $backup_fullpath = $this->TempalteFullPath; + $this->TemplateName = $t; + $this->TempalteFullPath = $pre_parsed['tname']; ob_start(); $_parser =& $this; if ($pre_parsed['mode'] == 'file') { - if (false && $result = $this->Application->getCache('nparser', $pre_parsed['fname'])) { - return $result; - } include($pre_parsed['fname']); } else { eval('?'.'>'.$pre_parsed['content']); } $output = ob_get_contents(); - // $this->Application->setCache('nparser', $pre_parsed['fname'], $output); ob_end_clean(); + $this->TemplateName = $backup_template; + $this->TempalteFullPath = $backup_fullpath; + return $output; } @@ -325,6 +363,12 @@ function ParseBlock($params, $pass_params=false) { + if (isset($params['cache_timeout']) && ($ret = $this->CacheGet($this->FormCacheKey('element_'.$params['name'])))) { + return $ret; + } + + $original_params = $params; + if ($pass_params || isset($params['pass_params'])) $params = array_merge($this->Params, $params); $this->PushParams($params); $data_exists_bak = $this->DataExists; @@ -360,12 +404,32 @@ $this->CheckNoData($ret, $params); $this->DataExists = $data_exists_bak || $this->DataExists; - return $ret; + + if (isset($original_params['cache_timeout'])) { + $this->CacheSet($this->FormCacheKey('element_'.$original_params['name']), $ret, $original_params['cache_timeout']); + } + + return defined('DBG_DECORATE_BLOCKS') && DBG_DECORATE_BLOCKS ? $this->DecorateBlock($ret, $params) : $ret; } + + function DecorateBlock($block_content, $block_params) + { + if (preg_match('/^(\s*)(.*)<\/td>(.*)$/is', $block_content, $regs)) { + // block with td -> put div inside td + return $regs[1].'
'.$regs[3].'
'.$regs[4]; + } + return '
'.$block_content.'
'; + } + function IncludeTemplate($params, $silent=null) { $t = is_array($params) ? $this->SelectParam($params, 't,template,block,name') : $params; + + if (isset($params['cache_timeout']) && ($ret = $this->CacheGet('template:'.$t))) { + return $ret; + } + $t = eregi_replace("\.tpl$", '', $t); $data_exists_bak = $this->DataExists; $this->DataExists = false; @@ -380,6 +444,11 @@ $this->CheckNoData($ret, $params); $this->DataExists = $data_exists_bak || $this->DataExists; + + if (isset($params['cache_timeout'])) { + $this->CacheSet('template:'.$t, $ret, $params['cache_timeout']); + } + return $ret; } @@ -395,4 +464,77 @@ } } } + + function CacheGet($name) + { + if (!$this->Application->ConfigValue('SystemTagCache')) return false; + return $this->Application->CacheGet($name); + } + + function CacheSet($name, $value, $expiration=0) + { + if (!$this->Application->ConfigValue('SystemTagCache')) return false; + return $this->Application->CacheSet($name, $value, $expiration); + } + + function FormCacheKey($element, $file=null, $add_prefixes=null) + { + if (!isset($file)) { + $file = str_replace(FULL_PATH, '', $this->TempalteFullPath).':'.$this->Application->GetVar('t'); + } + $parts = array( + 'file_'.$file.'('.filemtime($this->TempalteFullPath).')' => 'serials:file_ts', // theme + template timestamp + 'm_lang_'.$this->Application->GetVar('m_lang') => 'serials:lang_ts', + 'm_cat_id_'.$this->Application->GetVar('m_cat_id') => 'serials:cat_'.$this->Application->GetVar('m_cat_id').'_ts', + 'm_cat_page'.$this->Application->GetVar('m_cat_page') => false, + ); + if (isset($add_prefixes)) { + foreach ($add_prefixes as $prefix) { + $parts[$prefix.'_id_'.$this->Application->GetVar("{$prefix}_id")] = "serials:$prefix_".$this->Application->GetVar("{$prefix}_id").'_ts'; + $parts[$prefix.'_page_'.$this->Application->GetVar("{$prefix}_Page")] = false; + } + } + $key = ''; + foreach ($parts as $part => $ts_name) { + if ($ts_name) { + $ts = $this->Application->CacheGet($ts_name); + $key .= "$part($ts):"; + } + else { + $key .= "$part:"; + } + } + $key .= $element; + + return crc32($key); + } + + function PushPointer($pointer) + { + $this->CachePointers[++$this->CacheLevel] = $this->FormCacheKey('pointer:'.$pointer); + return $this->CachePointers[$this->CacheLevel]; + } + + function PopPointer() + { + return $this->CachePointers[$this->CacheLevel--]; + } + + function CacheStart($pointer=null) + { + if ($ret = $this->CacheGet($this->PushPointer($pointer)) ) { + echo $ret; + $this->PopPointer(); + return true; + } + ob_start(); + return false; + } + + function CacheEnd($elem=null) + { + $ret = ob_get_clean(); + $this->CacheSet($this->PopPointer(), $ret); // . ($this->CurrentKeyPart ? ':'.$this->CurrentKeyPart : '') + echo $ret; + } } \ No newline at end of file Index: branches/RC/core/kernel/application.php =================================================================== diff -u -N -r10602 -r10610 --- branches/RC/core/kernel/application.php (.../application.php) (revision 10602) +++ branches/RC/core/kernel/application.php (.../application.php) (revision 10610) @@ -231,7 +231,25 @@ $this->Memcached->addServer($server, $port); } } + + //try to set something to cache, if not working - set $this->Memcached to null } + + function CacheSet($name, $value, $expiration) + { + if (isset($this->Memcached)) { + return $this->Memcached->set($name, $value, 0, $expiration); + } + return false; + } + + function CacheGet($name) + { + if (isset($this->Memcached)) { + return $this->Memcached->get($name); + } + return false; + } /** * Initializes the Application @@ -246,7 +264,7 @@ { if($this->InitDone) return false; -// $this->InitMemcached(); + $this->InitMemcached(); if (!constOn('SKIP_OUT_COMPRESSION')) { ob_start(); // collect any output from method (other then tags) into buffer