Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sun, Feb 2, 7:36 PM

in-portal

Index: branches/RC/core/kernel/utility/debugger.php
===================================================================
--- branches/RC/core/kernel/utility/debugger.php (revision 11933)
+++ branches/RC/core/kernel/utility/debugger.php (revision 11934)
@@ -1,1470 +1,1473 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.net/license/ for copyright notices and details.
*/
if( !class_exists('Debugger') ) {
class Debugger {
/**
* Holds reference to global KernelApplication instance
*
* @access public
* @var kApplication
*/
var $Application = null;
/**
* Set to true if fatal error occured
*
* @var bool
*/
var $IsFatalError = false;
/**
* Allows to track compile errors, like "stack-overflow"
*
* @var bool
*/
var $_compileError = false;
/**
* Debugger data for building report
*
* @var Array
*/
var $Data = Array();
var $ProfilerData = Array();
var $ProfilerTotals = Array();
var $ProfilerTotalCount = Array();
var $ProfilePoints = Array();
/**
* Prevent recursion when processing debug_backtrace() function results
*
* @var Array
*/
var $RecursionStack = Array();
var $scrollbarWidth = 0;
/**
* Long errors are saved here, because trigger_error doesn't support error messages over 1KB in size
*
* @var Array
*/
var $longErrors = Array();
var $IncludesData = Array();
var $IncludeLevel = 0;
var $reportDone = false;
/**
* Transparent spacer image used in case of none spacer image defined via SPACER_URL contant.
* Used while drawing progress bars (memory usage, time usage, etc.)
*
* @var string
*/
var $dummyImage = '';
/**
* Temporary files created by debugger will be stored here
*
* @var string
*/
var $tempFolder = '';
/**
* Debug rows will be separated using this string before writing to debug file
*
* @var string
*/
var $rowSeparator = '@@';
/**
* Base URL for debugger includes
*
* @var string
*/
var $baseURL = '';
/**
* Holds last recorded timestamp (for appendTimestamp)
*
* @var int
*/
var $LastMoment;
/**
* Determines, that current request is AJAX request
*
* @var bool
*/
var $_isAjax = false;
function Debugger()
{
global $start, $dbg_options;
// check if user haven't defined DEBUG_MODE contant directly
if (defined('DEBUG_MODE') && DEBUG_MODE) {
die('error: contant DEBUG_MODE defined directly, please use <strong>$dbg_options</strong> array instead');
}
// check IP before enabling debug mode
$ip_match = $this->ipMatch(isset($dbg_options['DBG_IP']) ? $dbg_options['DBG_IP'] : '');
if (!$ip_match) {
define('DEBUG_MODE', 0);
return ;
}
// debug is allowed for user, continue initialization
$this->InitDebugger();
$this->profileStart('kernel4_startup', 'Startup and Initialization of kernel4', $start);
$this->profileStart('script_runtime', 'Script runtime', $start);
$this->LastMoment = $start;
error_reporting(E_ALL);
ini_set('display_errors', $this->constOn('DBG_ZEND_PRESENT') ? 0 : 1); // show errors on screen in case if not in Zend Studio debugging
$this->scrollbarWidth = $this->isGecko() ? 22 : 25; // vertical scrollbar width differs in Firefox and other browsers
$this->appendRequest();
}
/**
* Checks, that user IP address is within allowed range
*
* @param string $ip_list semi-column (by default) separated ip address list
* @param string $separator ip address separator (default ";")
*
* @return bool
*/
function ipMatch($ip_list, $separator = ';')
{
$ip_match = false;
$ip_addresses = $ip_list ? explode($separator, $ip_list) : Array ();
foreach ($ip_addresses as $ip_address) {
if ($this->netMatch($ip_address, $_SERVER['REMOTE_ADDR'])) {
$ip_match = true;
break;
}
}
return $ip_match;
}
/**
* Set's default values to constants debugger uses
*
*/
function InitDebugger()
{
global $dbg_options;
unset($_REQUEST['debug_host'], $_REQUEST['debug_fastfile'], $_REQUEST['debug_session_id'], $_REQUEST['debug_start_session'], $dbg_options['DBG_IP']); // this var messed up whole detection stuff :(
// Detect fact, that this session beeing debugged by Zend Studio
foreach ($_REQUEST as $rq_name => $rq_value) {
if (substr($rq_name, 0, 6) == 'debug_') {
$this->safeDefine('DBG_ZEND_PRESENT', 1);
break;
}
}
$this->safeDefine('DBG_ZEND_PRESENT', 0); // set this constant value to 0 (zero) to debug debugger using Zend Studio
// set default values for debugger constants
$dbg_constMap = Array (
'DBG_USE_HIGHLIGHT' => 1, // highlight output same as php code using "highlight_string" function
'DBG_WINDOW_WIDTH' => 700, // set width of debugger window (in pixels) for better viewing large amount of debug data
'DBG_USE_SHUTDOWN_FUNC' => DBG_ZEND_PRESENT ? 0 : 1, // use shutdown function to include debugger code into output
'DBG_HANDLE_ERRORS' => DBG_ZEND_PRESENT ? 0 : 1, // handle all allowed by php (see php manual) errors instead of default handler
'DBG_IGNORE_STRICT_ERRORS' => 1, // ignore PHP5 errors about private/public view modified missing in class declarations
'DBG_DOMVIEWER' => '/temp/domviewer.html', // path to DOMViewer on website
'DOC_ROOT' => str_replace('\\', '/', realpath($_SERVER['DOCUMENT_ROOT']) ), // windows hack
'DBG_LOCAL_BASE_PATH' => 'w:', // replace DOC_ROOT in filenames (in errors) using this path
);
// only for IE, in case if no windows php script editor defined
if (!defined('DBG_EDITOR')) {
// $dbg_constMap['DBG_EDITOR'] = 'c:\Program Files\UltraEdit\uedit32.exe %F/%L';
$dbg_constMap['DBG_EDITOR'] = 'c:\Program Files\Zend\ZendStudio-5.2.0\bin\ZDE.exe %F';
}
// debugger is initialized before kHTTPQuery, so do jQuery headers check here too
if (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
$this->_isAjax = true;
}
elseif (array_key_exists('ajax', $_GET) && $_GET['ajax'] == 'yes') {
$this->_isAjax = true;
}
// user defined options override debugger defaults
$dbg_constMap = $this->array_merge_recursive2($dbg_constMap, $dbg_options);
if ($this->_isAjax && array_key_exists('DBG_SKIP_AJAX', $dbg_constMap) && $dbg_constMap['DBG_SKIP_AJAX']) {
$dbg_constMap['DBG_SKIP_REPORTING'] = 1;
}
// when validation configs, don't show sqls for better validation error displaying
if (array_key_exists('DBG_VALIDATE_CONFIGS', $dbg_constMap) && $dbg_constMap['DBG_VALIDATE_CONFIGS']) {
$dbg_constMap['DBG_SQL_PROFILE'] = 0;
}
// when showing explain make shure, that debugger window is large enough
if (array_key_exists('DBG_SQL_EXPLAIN', $dbg_constMap) && $dbg_constMap['DBG_SQL_EXPLAIN']) {
$dbg_constMap['DBG_WINDOW_WIDTH'] = 1000;
}
foreach ($dbg_constMap as $dbg_constName => $dbg_constValue) {
$this->safeDefine($dbg_constName, $dbg_constValue);
}
}
function constOn($const_name)
{
return defined($const_name) && constant($const_name);
}
function safeDefine($const_name, $const_value) {
if (!defined($const_name)) {
define($const_name, $const_value);
}
}
function array_merge_recursive2($paArray1, $paArray2)
{
if (!is_array($paArray1) or !is_array($paArray2)) {
return $paArray2;
}
foreach ($paArray2 AS $sKey2 => $sValue2) {
$paArray1[$sKey2] = isset($paArray1[$sKey2]) ? array_merge_recursive2($paArray1[$sKey2], $sValue2) : $sValue2;
}
return $paArray1;
}
function netMatch($network, $ip) {
$network = trim($network);
$ip = trim($ip);
if ($network == $ip) {
// comparing two ip addresses directly
return true;
}
$d = strpos($network, '-');
if ($d !== false) {
// ip address range specified
$from = ip2long(trim(substr($network, 0, $d)));
$to = ip2long(trim(substr($network, $d + 1)));
$ip = ip2long($ip);
return ($ip >= $from && $ip <= $to);
}
elseif (strpos($network, '/') !== false) {
// sigle subnet specified
$ip_arr = explode('/', $network);
if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) {
$ip_arr[0] .= '.0'; // Alternate form 194.1.4/24
}
$network_long = ip2long($ip_arr[0]);
$x = ip2long($ip_arr[1]);
$mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1]));
$ip_long = ip2long($ip);
return ($ip_long & $mask) == ($network_long & $mask);
}
return false;
}
function InitReport()
{
if (!class_exists('kApplication')) return false;
$application =& kApplication::Instance();
// string used to separate debugger records while in file (used in debugger dump filename too)
$this->rowSeparator = '@'.(is_object($application->Factory) ? $application->GetSID() : 0).'@';
// $this->rowSeparator = '@'.rand(0,100000).'@';
// include debugger files from this url
$reg_exp = '/^'.preg_quote(FULL_PATH, '/').'/';
$kernel_path = preg_replace($reg_exp, '', KERNEL_PATH, 1);
$this->baseURL = PROTOCOL.SERVER_NAME.rtrim(BASE_PATH, '/').$kernel_path.'/utility/debugger';
// save debug output in this folder
$this->tempFolder = defined('WRITEABLE') ? WRITEABLE.'/cache' : FULL_PATH.'/kernel/cache';
}
function mapLongError($msg)
{
$key = $this->generateID();
$this->longErrors[$key] = $msg;
return $key;
}
/**
* Appends all passed variable values (wihout variable names) to debug output
*
*/
function dumpVars()
{
$dump_mode = 'var_dump';
$dumpVars = func_get_args();
if ($dumpVars[count($dumpVars) - 1] === 'STRICT') {
$dump_mode = 'strict_var_dump';
array_pop($dumpVars);
}
foreach ($dumpVars as $varValue) {
$this->Data[] = Array('value' => $varValue, 'debug_type' => $dump_mode);
}
}
function prepareHTML($dataIndex)
{
$Data =& $this->Data[$dataIndex];
if ($Data['debug_type'] == 'html') {
return $Data['html'];
}
switch ($Data['debug_type']) {
case 'error':
$fileLink = $this->getFileLink($Data['file'], $Data['line']);
$ret = '<b class="debug_error">'.$this->getErrorNameByCode($Data['no']).'</b>: '.$Data['str'];
$ret .= ' in <b>'.$fileLink.'</b> on line <b>'.$Data['line'].'</b>';
return $ret;
break;
case 'var_dump':
return $this->highlightString( $this->print_r($Data['value'], true) );
break;
case 'strict_var_dump':
return $this->highlightString( var_export($Data['value'], true) );
break;
case 'trace':
ini_set('memory_limit', '500M');
$trace =& $Data['trace'];
$i = 0; $traceCount = count($trace);
$ret = '';
while ($i < $traceCount) {
$traceRec =& $trace[$i];
$argsID = 'trace_args_'.$dataIndex.'_'.$i;
$has_args = isset($traceRec['args']);
if (isset($traceRec['file'])) {
$func_name = isset($traceRec['class']) ? $traceRec['class'].$traceRec['type'].$traceRec['function'] : $traceRec['function'];
$args_link = $has_args ? '<a href="javascript:$Debugger.ToggleTraceArgs(\''.$argsID.'\');" title="Show/Hide Function Arguments"><b>Function</b></a>' : '<b>Function</b>';
$ret .= $args_link.': '.$this->getFileLink($traceRec['file'], $traceRec['line'], $func_name);
$ret .= ' in <b>'.basename($traceRec['file']).'</b> on line <b>'.$traceRec['line'].'</b><br>';
}
else {
$ret .= 'no file information available';
}
if ($has_args) {
// if parameter value is longer then 200 symbols, then leave only first 50
$args = $this->highlightString($this->print_r($traceRec['args'], true));
$ret .= '<div id="'.$argsID.'" style="display: none;">'.$args.'</div>';
}
$i++;
}
return $ret;
break;
case 'profiler':
$profileKey = $Data['profile_key'];
$Data =& $this->ProfilerData[$profileKey];
$runtime = ($Data['ends'] - $Data['begins']); // in seconds
$totals_key = getArrayValue($Data, 'totalsKey');
if ($totals_key) {
$total_before = $Data['totalsBefore'];
$total = $this->ProfilerTotals[$totals_key];
$div_width = Array();
$total_width = ($this->getWindowWidth() - 10);
$div_width['before'] = round(($total_before / $total) * $total_width);
$div_width['current'] = round(($runtime / $total) * $total_width);
$div_width['left'] = round((($total - $total_before - $runtime) / $total) * $total_width);
$ret = '<b>Name</b>: '.$Data['description'].'<br />';
$additional = isset($Data['additional']) ? $Data['additional'] : Array ();
if (isset($Data['file'])) {
array_unshift($additional, Array('name' => 'File', 'value' => $this->getFileLink($Data['file'], $Data['line'], basename($Data['file']).':'.$Data['line'])));
}
array_unshift($additional, Array('name' => 'Runtime', 'value' => $runtime.'s'));
+ $ret .= '<div>'; //FF 3.5 needs this!
foreach ($additional as $mixed_param) {
$ret .= '[<strong>'.$mixed_param['name'].'</strong>: '.$mixed_param['value'].'] ';
}
/*if (isset($Data['file'])) {
$ret .= '[<b>Runtime</b>: '.$runtime.'s] [<b>File</b>: '.$this->getFileLink($Data['file'], $Data['line'], basename($Data['file']).':'.$Data['line']).']<br />';
}
else {
$ret .= '<b>Runtime</b>: '.$runtime.'s<br />';
}*/
+
+ $ret .= '</div>';
$ret .= '<div class="dbg_profiler" style="width: '.$div_width['before'].'px; border-right: 0px; background-color: #298DDF;"><img src="'.$this->dummyImage.'" width="1" height="1"/></div>';
$ret .= '<div class="dbg_profiler" style="width: '.$div_width['current'].'px; border-left: 0px; border-right: 0px; background-color: #EF4A4A;"><img src="'.$this->dummyImage.'" width="1" height="1"/></div>';
$ret .= '<div class="dbg_profiler" style="width: '.$div_width['left'].'px; border-left: 0px; background-color: #DFDFDF;"><img src="'.$this->dummyImage.'" width="1" height="1"/></div>';
return $ret;
}
else {
return '<b>Name</b>: '.$Data['description'].'<br><b>Runtime</b>: '.$runtime.'s';
}
break;
default:
return 'incorrect debug data';
break;
}
}
function getWindowWidth()
{
return DBG_WINDOW_WIDTH - $this->scrollbarWidth - 8;
}
/**
* Tells debugger to skip objects that are heavy in plan of memory usage while printing debug_backtrace results
*
* @param Object $object
* @return bool
*/
function IsBigObject(&$object)
{
$skip_classes = Array(
defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication',
'kFactory',
'kUnitConfigReader',
'TemplateParser',
);
foreach ($skip_classes as $class_name) {
if (strtolower(get_class($object)) == strtolower($class_name)) {
return true;
}
}
return false;
}
/**
* Advanced version of print_r (for debugger only). Don't print objects recursively
*
* @param Array $array
* @param bool $return_output return output or print it out
* @param int $tab_count offset in tabs
* @return string
*/
function print_r(&$array, $return_output = false, $tab_count = -1)
{
static $first_line = true;
// not an array at all
if (!is_array($array)) {
switch (gettype($array)) {
case 'NULL':
return 'NULL'."\n";
break;
case 'object':
return $this->processObject($array, $tab_count);
break;
default:
// number or string
if (strlen($array) > 200) {
$array = substr($array, 0, 50).' ...';
}
return $array."\n";
break;
}
}
$output = '';
$tab_count++;
$output .= "Array\n".str_repeat(' ', $tab_count)."(\n";
$tab_count++;
$tabsign = $tab_count ? str_repeat(' ', $tab_count) : '';
$array_keys = array_keys($array);
foreach ($array_keys as $key) {
switch (gettype($array[$key])) {
case 'array':
$output .= $tabsign.'['.$key.'] = '.$this->print_r($array[$key], true, $tab_count);
break;
case 'boolean':
$output .= $tabsign.'['.$key.'] = '.($array[$key] ? 'true' : 'false')."\n";
break;
case 'integer':
case 'double':
case 'string':
if (strlen($array[$key]) > 200) {
$array[$key] = substr($array[$key], 0, 50).' ...';
}
$output .= $tabsign.'['.$key.'] = '.$array[$key]."\n";
break;
case 'NULL':
$output .= $tabsign.'['.$key."] = NULL\n";
break;
case 'object':
$output .= $tabsign.'['.$key."] = ";
$output .= "Object (".get_class($array[$key]).") = \n".str_repeat(' ', $tab_count + 1)."(\n";
$output .= $this->processObject($array[$key], $tab_count + 2);
$output .= str_repeat(' ', $tab_count + 1).")\n";
break;
default:
$output .= $tabsign.'['.$key.'] unknown = '.gettype($array[$key])."\n";
break;
}
}
$tab_count--;
$output .= str_repeat(' ', $tab_count).")\n";
if ($first_line) {
$first_line = false;
$output .= "\n";
}
$tab_count--;
if ($return_output) {
return $output;
}
else {
echo $output;
}
return true;
}
function processObject(&$object, $tab_count)
{
$object_class = get_class($object);
if (!in_array($object_class, $this->RecursionStack)) {
if ($this->IsBigObject($object)) {
return 'SKIPPED (class: '.$object_class.")\n";
}
$attribute_names = get_class_vars($object_class);
if (!$attribute_names) {
return "NO_ATTRIBUTES\n";
}
else {
$output = '';
array_push($this->RecursionStack, $object_class);
$tabsign = $tab_count ? str_repeat(' ', $tab_count) : '';
foreach ($attribute_names as $attribute_name => $attribute_value) {
if (is_object($object->$attribute_name)) {
// it is object
$output .= $tabsign.'['.$attribute_name.'] = '.$this->processObject($object->$attribute_name, $tab_count + 1);
}
else {
$output .= $tabsign.'['.$attribute_name.'] = '.$this->print_r($object->$attribute_name, true, $tab_count);
}
}
array_pop($this->RecursionStack);
return $output;
}
}
else {
// object [in recursion stack]
return '*** RECURSION *** (class: '.$object_class.")\n";
}
}
/**
* Format SQL Query using predefined formatting
* and highlighting techniques
*
* @param string $sql
* @return string
*/
function formatSQL($sql)
{
$sql = trim( preg_replace('/(\n|\t| )+/is', ' ', $sql) );
// whitespace in the beginning of the regex is to avoid splitting inside words, for exmaple "FROM int_ConfigurationValues" into "FROM intConfiguration\n\tValues"
$formatted_sql = preg_replace('/\s(CREATE TABLE|DROP TABLE|SELECT|UPDATE|SET|REPLACE|INSERT|DELETE|VALUES|FROM|LEFT JOIN|INNER JOIN|LIMIT|WHERE|HAVING|GROUP BY|ORDER BY)\s/is', "\n\t$1 ", ' ' . $sql);
$formatted_sql = $this->highlightString($formatted_sql);
if (defined('DBG_SQL_EXPLAIN') && DBG_SQL_EXPLAIN) {
if (substr($sql, 0, 6) == 'SELECT') {
$formatted_sql .= '<br/>' . '<strong>Explain</strong>:<br /><br />';
$explain_result = $this->Application->Conn->Query('EXPLAIN ' . $sql, null, true);
$explain_table = '';
foreach ($explain_result as $explain_row) {
if (!$explain_table) {
// first row -> draw header
$explain_table .= '<tr class="explain_header"><td>' . implode('</td><td>', array_keys($explain_row)) . '</td></tr>';
}
$explain_table .= '<tr><td>' . implode('</td><td>', $explain_row) . '</td></tr>';
}
$formatted_sql .= '<table class="dbg_explain_table">' . $explain_table . '</table>';
}
}
return $formatted_sql;
}
function highlightString($string)
{
if (!(defined('DBG_USE_HIGHLIGHT') && DBG_USE_HIGHLIGHT) || $this->_compileError) {
return nl2br($string);
}
$string = str_replace( Array('\\', '/') , Array('_no_match_string_', '_n_m_s_'), $string);
$this->_compileError = true; // next line is possible cause of compile error
$string = highlight_string('<?php '.$string.' ?>', true);
$this->_compileError = false;
$string = str_replace( Array('_no_match_string_', '_n_m_s_'), Array('\\', '/'), $string);
if (strlen($string) >= 65536) {
// preg_replace will fail, when string is longer, then 65KB
return str_replace(Array ('&lt;?php&nbsp;', '?&gt;'), '', $string);
}
return preg_replace('/&lt;\?(.*)php&nbsp;(.*)\?&gt;/Us', '\\2', $string);
}
/**
* Determine by php type of browser used to show debugger
*
* @return bool
*/
function isGecko()
{
// we need isset because we may run scripts from shell with no user_agent at all
return isset($_SERVER['HTTP_USER_AGENT']) && strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'firefox') !== false;
}
/**
* Returns link for editing php file (from error) in external editor
*
* @param string $file filename with path from root folder
* @param int $lineno line number in file where error is found
* @param string $title text to show on file edit link
* @return string
*/
function getFileLink($file, $lineno = 1, $title = '')
{
if (!$title) {
$title = str_replace('/', '\\', $this->getLocalFile($file));
}
if ($this->isGecko()) {
return '<a href="file://'.$this->getLocalFile($file).'">'.$title.'</a>';
}
else {
return '<a href="javascript:$Debugger.editFile(\''.$this->getLocalFile($file).'\', '.$lineno.');" title="'.$file.'">'.$title.'</a>';
}
}
/**
* Converts filepath on server to filepath in mapped DocumentRoot on developer pc
*
* @param string $remoteFile
* @return string
*/
function getLocalFile($remoteFile)
{
return preg_replace('/^'.preg_quote(DOC_ROOT, '/').'/', DBG_LOCAL_BASE_PATH, $remoteFile, 1);
}
/**
* Appends call trace till this method call
*
*/
function appendTrace()
{
$trace = debug_backtrace();
array_shift($trace);
$this->Data[] = Array('trace' => $trace, 'debug_type' => 'trace');
}
function appendMemoryUsage($msg, $used = null)
{
if (!isset($used)) {
$used = round(memory_get_usage() / 1024);
}
$this->appendHTML('<b>Memory usage</b> '.$msg.' '.$used.'Kb');
}
/**
* Appends HTML code whithout transformations
*
* @param string $html
*/
function appendHTML($html)
{
$this->Data[] = Array('html' => $html, 'debug_type' => 'html');
}
/**
* Change debugger info that was already generated before.
* Returns true if html was set.
*
* @param int $index
* @param string $html
* @param string $type = {'append','prepend','replace'}
* @return bool
*/
function setHTMLByIndex($index, $html, $type = 'append')
{
if (!isset($this->Data[$index]) || $this->Data[$index]['debug_type'] != 'html') {
return false;
}
switch ($type) {
case 'append':
$this->Data[$index]['html'] .= '<br>'.$html;
break;
case 'prepend':
$this->Data[$index]['html'] = $this->Data[$index]['html'].'<br>'.$html;
break;
case 'replace':
$this->Data[$index]['html'] = $html;
break;
}
return true;
}
/**
* Move $debugLineCount lines of input from debug output
* end to beginning.
*
* @param int $debugLineCount
*/
function moveToBegin($debugLineCount)
{
$lines = array_splice($this->Data,count($this->Data)-$debugLineCount,$debugLineCount);
$this->Data = array_merge($lines,$this->Data);
}
function moveAfterRow($new_row, $debugLineCount)
{
$lines = array_splice($this->Data,count($this->Data)-$debugLineCount,$debugLineCount);
$rows_before = array_splice($this->Data,0,$new_row,$lines);
$this->Data = array_merge($rows_before,$this->Data);
}
function appendRequest()
{
if (isset($_SERVER['SCRIPT_FILENAME'])) {
$script = $_SERVER['SCRIPT_FILENAME'];
}
else {
$script = $_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF'];
}
$this->appendHTML('ScriptName: <b>'.$this->getFileLink($script, 1, basename($script)).'</b> (<b>'.dirname($script).'</b>)');
if ($this->_isAjax) {
$this->appendHTML('RequestURI: '.$_SERVER['REQUEST_URI'].' (QS Length:'.strlen($_SERVER['QUERY_STRING']).')');
}
$tools_html = ' <table style="width: ' . $this->getWindowWidth() . 'px;">
<tr>
<td>' . $this->_getDomViewerHTML() . '</td>
<td>' . $this->_getToolsHTML() . '</td>
</tr>
</table>';
$this->appendHTML($tools_html);
ob_start();
?>
<table border="0" cellspacing="0" cellpadding="0" class="dbg_flat_table" style="width: <?php echo $this->getWindowWidth(); ?>px;">
<thead style="font-weight: bold;">
<td width="20">Src</td><td>Name</td><td>Value</td>
</thead>
<?php
foreach ($_REQUEST as $key => $value) {
if(!is_array($value) && trim($value) == '') {
$value = '<b class="debug_error">no value</b>';
}
else {
$value = htmlspecialchars($this->print_r($value, true));
}
$in_cookie = isset($_COOKIE[$key]);
$src = isset($_GET[$key]) && !$in_cookie ? 'GE' : (isset($_POST[$key]) && !$in_cookie ? 'PO' : ($in_cookie ? 'CO' : '?') );
echo '<tr><td>'.$src.'</td><td>'.$key.'</td><td>'.$value.'</td></tr>';
}
?>
</table>
<?php
$this->appendHTML(ob_get_contents());
ob_end_clean();
}
/**
* Appends php session content to debugger output
*
*/
function appendSession()
{
if (isset($_SESSION) && $_SESSION) {
$this->appendHTML('PHP Session: [<b>'.ini_get('session.name').'</b>]');
$this->dumpVars($_SESSION);
$this->moveToBegin(2);
}
}
function profileStart($key, $description = null, $timeStamp = null)
{
if (!isset($timeStamp)) {
$timeStamp = $this->getMoment();
}
$this->ProfilerData[$key] = Array('begins' => $timeStamp, 'ends' => 5000, 'debuggerRowID' => count($this->Data));
if (isset($description)) {
$this->ProfilerData[$key]['description'] = $description;
}
if (substr($key, 0, 4) == 'sql_') {
// append place from what was called
$trace_results = debug_backtrace();
$trace_count = count($trace_results);
$i = 0;
while ($i < $trace_count) {
$trace_file = basename($trace_results[$i]['file']);
if ($trace_file != 'db_connection.php' && $trace_file != 'adodb.inc.php') {
break;
}
$i++;
}
$this->ProfilerData[$key]['file'] = $trace_results[$i]['file'];
$this->ProfilerData[$key]['line'] = $trace_results[$i]['line'];
unset($trace_results);
}
$this->Data[] = Array('profile_key' => $key, 'debug_type' => 'profiler');
}
function profileFinish($key, $description = null, $timeStamp = null)
{
if (!isset($timeStamp)) {
$timeStamp = $this->getMoment();
}
$this->ProfilerData[$key]['ends'] = $timeStamp;
if (isset($description)) {
$this->ProfilerData[$key]['description'] = $description;
}
if (substr($key, 0, 4) == 'sql_') {
$func_arguments = func_get_args();
$rows_affected = $func_arguments[3];
$additional = Array ();
if ($rows_affected > 0) {
$additional[] = Array ('name' => 'Affected Rows', 'value' => $rows_affected);
if (isset($func_arguments[4])) {
if (strlen($func_arguments[4]) > 200) {
$func_arguments[4] = substr($func_arguments[4], 0, 50) . ' ...';
}
$additional[] = Array ('name' => 'Result', 'value' => $func_arguments[4]);
}
}
$additional[] = Array ('name' => 'Query Number', 'value' => $func_arguments[5]);
$this->ProfilerData[$key]['additional'] =& $additional;
}
}
function profilerAddTotal($total_key, $key = null, $value = null)
{
if (!isset($this->ProfilerTotals[$total_key])) {
$this->ProfilerTotals[$total_key] = 0;
$this->ProfilerTotalCount[$total_key] = 0;
}
if (!isset($value)) {
$value = $this->ProfilerData[$key]['ends'] - $this->ProfilerData[$key]['begins'];
}
if (isset($key)) {
$this->ProfilerData[$key]['totalsKey'] = $total_key;
$this->ProfilerData[$key]['totalsBefore'] = $this->ProfilerTotals[$total_key];
}
$this->ProfilerTotals[$total_key] += $value;
$this->ProfilerTotalCount[$total_key]++;
}
function getMoment()
{
list($usec, $sec) = explode(' ', microtime());
return ((float)$usec + (float)$sec);
}
function appendTimestamp($message)
{
global $start;
$time = $this->getMoment();
$from_last = $time - $this->LastMoment;
$from_start = $time - $start;
$this->appendHTML(sprintf("<b>%s</b> %.5f from last %.5f from start", $message, $from_last, $from_start));
$this->LastMoment = $time;
}
function generateID()
{
list($usec, $sec) = explode(' ', microtime());
$id_part_1 = substr($usec, 4, 4);
$id_part_2 = mt_rand(1,9);
$id_part_3 = substr($sec, 6, 4);
$digit_one = substr($id_part_1, 0, 1);
if ($digit_one == 0) {
$digit_one = mt_rand(1,9);
$id_part_1 = ereg_replace("^0",'',$id_part_1);
$id_part_1=$digit_one.$id_part_1;
}
return $id_part_1.$id_part_2.$id_part_3;
}
function getErrorNameByCode($error_code)
{
$error_map = Array(
'Fatal Error' => Array(E_USER_ERROR),
'Warning' => Array(E_WARNING, E_USER_WARNING),
'Notice' => Array(E_NOTICE, E_USER_NOTICE),
);
if (defined('E_STRICT')) {
$error_map['PHP5 Strict'] = Array(E_STRICT);
}
if (defined('E_RECOVERABLE_ERROR')) {
$error_map['Fatal Error (recoverable)'] = Array(E_RECOVERABLE_ERROR);
}
foreach ($error_map as $error_name => $error_codes) {
if (in_array($error_code, $error_codes)) {
return $error_name;
}
}
return '';
}
/**
* Returns profile total key (check against unexisting key too)
*
* @param string $key
* @return int
*/
function getProfilerTotal($key)
{
if (isset($this->ProfilerTotalCount[$key])) {
return (int)$this->ProfilerTotalCount[$key];
}
return 0;
}
function ProfilePoint($title, $level=1)
{
$trace_results = debug_backtrace();
$level = min($level,count($trace_results)-1);
do {
$point = $trace_results[$level];
$location = $point['file'].':'.$point['line'];
$level++;
$has_more = isset($trace_results[$level]);
} while ($has_more && $point['function'] == $trace_results[$level]['function'] );
if (!isset($this->ProfilePoints[$title])) {
$this->ProfilePoints[$title] = array();
}
if (!isset($this->ProfilePoints[$title][$location])) {
$this->ProfilePoints[$title][$location] = 0;
}
$this->ProfilePoints[$title][$location]++;
}
/**
* Generates report
*
*/
function printReport($returnResult = false, $clean_output_buffer = true)
{
if ($this->reportDone) {
// don't print same report twice (in case if shutdown function used + compression + fatal error)
return '';
}
$this->profileFinish('script_runtime');
$this->breakOutofBuffering(!$returnResult);
$debugger_start = memory_get_usage();
if (defined('SPACER_URL')) {
$this->dummyImage = SPACER_URL;
}
$this->InitReport(); // set parameters required by AJAX
// defined here, because user can define this contant while script is running, not event before debugger is started
$this->safeDefine('DBG_RAISE_ON_WARNINGS', 0);
$this->safeDefine('DBG_TOOLBAR_BUTTONS', 1);
$this->appendSession(); // show php session if any
// ensure, that 1st line of debug output always is this one:
$top_line = '<table cellspacing="0" cellpadding="0" style="width: '.$this->getWindowWidth().'px; margin: 0px;"><tr><td align="left" width="50%">[<a href="javascript:window.location.reload();">Reload Frame</a>] [<a href="javascript:$Debugger.Toggle(27);">Hide Debugger</a>] [<a href="javascript:$Debugger.Clear();">Clear Debugger</a>]</td><td align="right" width="50%">[Current Time: <b>'.date('H:i:s').'</b>] [File Size: <b>#DBG_FILESIZE#</b>]</td></tr></table>';
$this->appendHTML($top_line);
$this->moveToBegin(1);
if (count($this->ProfilePoints)>0) {
foreach($this->ProfilePoints as $point => $locations) {
arsort($this->ProfilePoints[$point]);
}
$this->appendHTML($this->highlightString($this->print_r($this->ProfilePoints, true)));
/*foreach ($this->ProfilePoints as $point => $locations) {
foreach ($locations as $location => $occurences) {
}
}*/
}
if ($this->constOn('DBG_SQL_PROFILE') && isset($this->ProfilerTotals['sql'])) {
// sql query profiling was enabled -> show totals
$this->appendHTML('<b>SQL Total time:</b> '.$this->ProfilerTotals['sql'].' <b>Number of queries</b>: '.$this->ProfilerTotalCount['sql']);
}
if ($this->constOn('DBG_PROFILE_INCLUDES') && isset($this->ProfilerTotals['includes'])) {
// included file profiling was enabled -> show totals
$this->appendHTML('<b>Included Files Total time:</b> '.$this->ProfilerTotals['includes'].' Number of includes: '.$this->ProfilerTotalCount['includes']);
}
if ($this->constOn('DBG_PROFILE_MEMORY')) {
// detailed memory usage reporting by objects was enabled -> show totals
$this->appendHTML('<b>Memory used by Objects:</b> '.round($this->ProfilerTotals['objects'] / 1024, 2).'Kb');
}
if ($this->constOn('DBG_INCLUDED_FILES')) {
$files = get_included_files();
$this->appendHTML('<b>Included files:</b>');
foreach ($files as $file) {
$this->appendHTML($this->getFileLink($this->getLocalFile($file)).' ('.round(filesize($file) / 1024, 2).'Kb)');
}
}
if ($this->constOn('DBG_PROFILE_INCLUDES')) {
$this->appendHTML('<b>Included files statistics:</b>'.( $this->constOn('DBG_SORT_INCLUDES_MEM') ? ' (sorted by memory usage)':''));
$totals = Array( 'mem' => 0, 'time' => 0);
$totals_configs = Array( 'mem' => 0, 'time' => 0);
if (is_array($this->IncludesData['mem'])) {
if ( $this->constOn('DBG_SORT_INCLUDES_MEM') ) {
array_multisort($this->IncludesData['mem'], SORT_DESC, $this->IncludesData['file'], $this->IncludesData['time'], $this->IncludesData['level']);
}
foreach ($this->IncludesData['file'] as $key => $file_name) {
$this->appendHTML( str_repeat('&nbsp;->&nbsp;', ($this->IncludesData['level'][$key] >= 0 ? $this->IncludesData['level'][$key] : 0)).$file_name.' Mem: '.sprintf("%.4f Kb", $this->IncludesData['mem'][$key]/1024).' Time: '.sprintf("%.4f", $this->IncludesData['time'][$key]));
if ($this->IncludesData['level'][$key] == 0) {
$totals['mem'] += $this->IncludesData['mem'][$key];
$totals['time'] += $this->IncludesData['time'][$key];
}
else if ($this->IncludesData['level'][$key] == -1) {
$totals_configs['mem'] += $this->IncludesData['mem'][$key];
$totals_configs['time'] += $this->IncludesData['time'][$key];
}
}
$this->appendHTML('<b>Sub-Total classes:</b> '.' Mem: '.sprintf("%.4f Kb", $totals['mem']/1024).' Time: '.sprintf("%.4f", $totals['time']));
$this->appendHTML('<b>Sub-Total configs:</b> '.' Mem: '.sprintf("%.4f Kb", $totals_configs['mem']/1024).' Time: '.sprintf("%.4f", $totals_configs['time']));
$this->appendHTML('<span class="error"><b>Grand Total:</b></span> '.' Mem: '.sprintf("%.4f Kb", ($totals['mem']+$totals_configs['mem'])/1024).' Time: '.sprintf("%.4f", $totals['time']+$totals_configs['time']));
}
}
$skip_reporting = $this->constOn('DBG_SKIP_REPORTING') || $this->constOn('DBG_ZEND_PRESENT');
if (($this->_isAjax && !$this->constOn('DBG_SKIP_AJAX')) || !$skip_reporting) {
$debug_file = $this->tempFolder.'/debug_'.$this->rowSeparator.'.txt';
if (file_exists($debug_file)) unlink($debug_file);
$i = 0;
$fp = fopen($debug_file, 'a');
$lineCount = count($this->Data);
while ($i < $lineCount) {
fwrite($fp, $this->prepareHTML($i).$this->rowSeparator);
$i++;
}
fclose($fp);
}
if ($skip_reporting) {
// let debugger write report and then don't output anything
$this->reportDone = true;
return '';
}
$application =& kApplication::Instance();
$dbg_path = str_replace(FULL_PATH, '', $this->tempFolder);
ob_start();
// the <script .. /script> and hidden div helps browser to break out of script tag or attribute esacped
// with " or ' in case fatal error (or user-error) occurs inside it in compiled template,
// otherwise it has no effect
?>
<div style="display: none" x='nothing'><script></script></div><html><body></body></html>
<script type="text/javascript" src="<?php echo $this->baseURL; ?>/debugger.js"></script>
<link rel="stylesheet" rev="stylesheet" href="<?php echo $this->baseURL; ?>/debugger.css" type="text/css" media="screen" />
<script type="text/javascript">
var $Debugger = new Debugger(<?php echo "'".$this->rowSeparator."', ".$this->getProfilerTotal('error_handling').', '.($this->IsFatalError ? 'true' : 'false').', '.$this->getProfilerTotal('sql'); ?>);
$Debugger.createEnvironment(<?php echo DBG_WINDOW_WIDTH; ?>, <?php echo $this->getWindowWidth(); ?>);
$Debugger.DOMViewerURL = '<?php echo constant('DBG_DOMVIEWER'); ?>';
$Debugger.EditorPath = '<?php echo defined('DBG_EDITOR') ? addslashes(DBG_EDITOR) : '' ?>';
$Debugger.DebugURL = '<?php echo $this->baseURL.'/debugger_responce.php?sid='.$this->rowSeparator.'&path='.urlencode($dbg_path); ?>';
$Debugger.EventURL = '<?php echo is_object($application->Factory) ? $application->HREF('dummy', '', Array ('pass' => 'm')) : ''; ?>';
<?php
if ($this->IsFatalError || DBG_RAISE_ON_WARNINGS) {
echo '$Debugger.Toggle();';
}
if (DBG_TOOLBAR_BUTTONS) {
echo '$Debugger.AddToolbar("$Debugger");';
}
?>
window.focus();
</script>
<?php
if (!isset($this->ProfilerTotals['error_handling'])) {
$memory_used = $debugger_start;
$this->ProfilerTotalCount['error_handling'] = 0;
}
else {
$memory_used = $debugger_start - $this->ProfilerTotals['error_handling'];
}
if ($returnResult) {
$ret = ob_get_contents();
if ($clean_output_buffer) {
ob_end_clean();
}
$ret .= $this->getShortReport($memory_used);
$this->reportDone = true;
return $ret;
}
else {
if (!$this->constOn('DBG_HIDE_FULL_REPORT')) {
$this->breakOutofBuffering();
}
elseif ($clean_output_buffer) {
ob_clean();
}
echo $this->getShortReport($memory_used);
$this->reportDone = true;
}
}
/**
* Format's memory usage report by debugger
*
* @return string
* @access private
*/
function getShortReport($memory_used)
{
if ($this->constOn('DBG_TOOLBAR_BUTTONS')) {
// we have sql & error count in toolbar, don't duplicate here
$info = Array(
'Script Runtime' => 'PROFILE:script_runtime',
'SQL\'s Runtime' => 'PROFILE_T:sql',
);
}
else {
// toolbar not visible, then show sql & error count too
$info = Array (
'Script Runtime' => 'PROFILE:script_runtime',
'SQL\'s Runtime' => 'PROFILE_T:sql',
'-' => 'SEP:-',
'Notice / Warning' => 'PROFILE_TC:error_handling',
'SQLs Count' => 'PROFILE_TC:sql',
);
}
$ret = '<tr><td>Application:</td><td><b>'.$this->formatSize($memory_used).'</b> ('.$memory_used.')</td></tr>';
foreach ($info as $title => $value_key) {
list ($record_type, $record_data) = explode(':', $value_key, 2);
switch ($record_type) {
case 'PROFILE': // profiler totals value
$Data =& $this->ProfilerData[$record_data];
$profile_time = ($Data['ends'] - $Data['begins']); // in seconds
$ret .= '<tr><td>'.$title.':</td><td><b>'.sprintf('%.5f', $profile_time).' s</b></td></tr>';
break;
case 'PROFILE_TC': // profile totals record count
$record_cell = '<td>';
if ($record_data == 'error_handling' && $this->ProfilerTotalCount[$record_data] > 0) {
$record_cell = '<td class="debug_error">';
}
$ret .= '<tr>'.$record_cell.$title.':</td>'.$record_cell.'<b>'.$this->ProfilerTotalCount[$record_data].'</b></td></tr>';
break;
case 'PROFILE_T': // profile total
$record_cell = '<td>';
$total = array_key_exists($record_data, $this->ProfilerTotals) ? $this->ProfilerTotals[$record_data] : 0;
$ret .= '<tr>'.$record_cell.$title.':</td>'.$record_cell.'<b>'.sprintf('%.5f', $total).' s</b></td></tr>';
break;
case 'SEP':
$ret .= '<tr><td colspan="2" style="height: 1px; background-color: #000000; padding: 0px;"><img src="'.$this->dummyImage.'" height="1" alt=""/></td></tr>';
break;
}
}
return '<br /><table class="dbg_stats_table"><tr><td style="border-color: #FFFFFF;"><table class="dbg_stats_table" align="left">'.$ret.'</table></td></tr></table>';
}
/**
* User-defined error handler
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @param array $errcontext
*/
function saveError($errno, $errstr, $errfile = '', $errline = '', $errcontext = '')
{
$this->ProfilerData['error_handling']['begins'] = memory_get_usage();
$errorType = $this->getErrorNameByCode($errno);
if (!$errorType) {
trigger_error('Unknown error type ['.$errno.']', E_USER_ERROR);
return false;
}
if ($this->constOn('DBG_IGNORE_STRICT_ERRORS') && defined('E_STRICT') && ($errno == E_STRICT)) return;
if (preg_match('/(.*)#([\d]+)$/', $errstr, $rets) ) {
// replace short message with long one (due triger_error limitations on message size)
$long_id = $rets[2];
$errstr = $this->longErrors[$long_id];
unset($this->longErrors[$long_id]);
}
if (strpos($errfile,'eval()\'d code') !== false) {
$errstr = '[<b>EVAL</b>, line <b>'.$errline.'</b>]: '.$errstr;
$tmpStr = $errfile;
$pos = strpos($tmpStr,'(');
$errfile = substr($tmpStr, 0, $pos);
$pos++;
$errline = substr($tmpStr,$pos,strpos($tmpStr,')',$pos)-$pos);
}
$this->Data[] = Array('no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'context' => $errcontext, 'debug_type' => 'error');
$this->ProfilerData['error_handling']['ends'] = memory_get_usage();
$this->profilerAddTotal('error_handling', 'error_handling');
if (substr($errorType, 0, 5) == 'Fatal') {
$this->IsFatalError = true;
// append debugger report to data in buffer & clean buffer afterwards
die( $this->breakOutofBuffering(false) . $this->printReport(true) );
}
}
function breakOutofBuffering($flush = true)
{
$buffer_content = Array();
while (ob_get_level()) {
$buffer_content[] = ob_get_clean();
}
$ret = implode('', array_reverse($buffer_content));
if ($flush) {
echo $ret;
flush();
}
return $ret;
}
function saveToFile($msg)
{
$fp = fopen($_SERVER['DOCUMENT_ROOT'].'/vb_debug.txt', 'a');
fwrite($fp, $msg."\n");
fclose($fp);
}
/**
* Formats file/memory size in nice way
*
* @param int $bytes
* @return string
* @access public
*/
function formatSize($bytes)
{
if ($bytes >= 1099511627776) {
$return = round($bytes / 1024 / 1024 / 1024 / 1024, 2);
$suffix = "TB";
} elseif ($bytes >= 1073741824) {
$return = round($bytes / 1024 / 1024 / 1024, 2);
$suffix = "GB";
} elseif ($bytes >= 1048576) {
$return = round($bytes / 1024 / 1024, 2);
$suffix = "MB";
} elseif ($bytes >= 1024) {
$return = round($bytes / 1024, 2);
$suffix = "KB";
} else {
$return = $bytes;
$suffix = "Byte";
}
$return .= ' '.$suffix;
return $return;
}
function printConstants($constants)
{
if (!is_array($constants)) {
$constants = explode(',', $constants);
}
$contant_tpl = '<tr><td>%s</td><td><b>%s</b></td></tr>';
$ret = '<table class="dbg_flat_table" style="width: '.$this->getWindowWidth().'px;">';
foreach ($constants as $constant_name) {
$ret .= sprintf($contant_tpl, $constant_name, constant($constant_name));
}
$ret .= '</table>';
$this->appendHTML($ret);
}
function AttachToApplication() {
if (!$this->constOn('DBG_HANDLE_ERRORS')) {
return true;
}
if (class_exists('kApplication')) {
restore_error_handler(); // replace application error handler with own
$this->Application =& kApplication::Instance();
$this->Application->Debugger =& $this;
$this->Application->errorHandlers[] = Array (&$this, 'saveError');
}
else {
set_error_handler(Array(&$this, 'saveError'));
}
}
/**
* Returns HTML for tools section
*
* @return string
*/
function _getToolsHTML()
{
$html = '<table>
<tr>
<td>System Tools:</td>
<td>
<select id="reset_cache" style="border: 1px solid #000000;">
<option value=""></option>
<option value="events[adm][OnResetModRwCache]">Reset mod_rewrite Cache</option>
<option value="events[adm][OnResetCMSMenuCache]">Reset SMS Menu Cache</option>
<option value="events[adm][OnResetSections]">Reset Sections Cache</option>
<option value="events[adm][OnResetConfigsCache]">Reset Configs Cache</option>
<option value="events[adm][OnRebuildThemes]">Re-build Themes Files</option>
<option value="events[lang][OnReflectMultiLingualFields]">Re-build Multilanguage Fields</option>
</select>
</td>
<td>
<input type="button" class="button" onclick="$Debugger.resetCache(\'reset_cache\');" value="Go"/>
</td>
</tr>
</table>';
return $html;
}
/**
* Returns HTML for dom viewer section
*
* @return string
*/
function _getDomViewerHTML()
{
$html = '<table>
<tr>
<td>
<a href="http://www.brainjar.com/dhtml/domviewer/" target="_blank">DomViewer</a>:
</td>
<td>
<input id="dbg_domviewer" type="text" value="window" style="border: 1px solid #000000;"/>
</td>
<td>
<button class="button" onclick="return $Debugger.OpenDOMViewer();">Show</button>
</td>
</tr>
</table>';
return $html;
}
}
if (!function_exists('memory_get_usage')) {
function memory_get_usage(){ return -1; }
}
if (!Debugger::constOn('DBG_ZEND_PRESENT')) {
$debugger = new Debugger();
}
if (Debugger::constOn('DBG_USE_SHUTDOWN_FUNC')) {
register_shutdown_function( Array(&$debugger, 'printReport') );
}
}
?>
\ No newline at end of file
Index: branches/RC/core/kernel/nparser/nparser.php
===================================================================
--- branches/RC/core/kernel/nparser/nparser.php (revision 11933)
+++ branches/RC/core/kernel/nparser/nparser.php (revision 11934)
@@ -1,723 +1,727 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.net/license/ for copyright notices and details.
*/
include_once(KERNEL_PATH.'/nparser/ntags.php');
define('TAG_NAMESPACE', 'inp2:');
define('TAG_NAMESPACE_LENGTH', 5);
class NParser extends kBase {
var $Stack = array();
var $Level = 0;
var $Buffers = array();
var $InsideComment = false;
var $Params = array();
var $ParamsStack = array();
var $ParamsLevel = 0;
var $Definitions = '';
var $Elements = array(); // holds dynamic elements to function names mapping during execution
/**
* Holds location of element definitions inside templates.
* key - element function name, value - array of 2 keys: {from_pos, to_pos}
*
* @var Array
*/
var $ElementLocations = Array ();
var $DataExists = false;
var $TemplateName = null;
var $TempalteFullPath = null;
var $CachePointers = array();
var $Cachable = array();
function NParser()
{
parent::kBase();
}
function Compile($pre_parsed, $template_name = 'unknown')
{
$data = file_get_contents($pre_parsed['tname']);
if (!$this->CompileRaw($data, $pre_parsed['tname'], $template_name)) {
// compilation failed during errors in template
// trigger_error('Template "<strong>' . $template_name . '</strong>" not compiled because of errors', E_USER_WARNING);
return false;
}
// saving compiled version (only when compilation was successful)
if (defined('SAFE_MODE') && SAFE_MODE) { // store cache files in database since can't save on filesystem
if (!isset($conn)) $conn =& $this->Application->GetADODBConnection();
$conn->Query('REPLACE INTO '.TABLE_PREFIX.'Cache (VarName, Data, Cached) VALUES ('.$conn->qstr($pre_parsed['fname']).','.$conn->qstr($this->Buffers[0]).','.adodb_mktime().')');
}
else {
$compiled = fopen($pre_parsed['fname'], 'w');
if (!fwrite($compiled, $this->Buffers[0])) {
trigger_error('Saving compiled template failed', E_USER_ERROR);
}
fclose($compiled);
}
return true;
}
function Parse($raw_template, $name = null)
{
$this->CompileRaw($raw_template, $name);
ob_start();
$_parser =& $this;
eval('?'.'>'.$this->Buffers[0]);
return ob_get_clean();
}
function CompileRaw($data, $t_name, $template_name = 'unknown')
{
$code = "extract (\$_parser->Params);\n";
$code .= "\$_parser->ElementLocations['{$template_name}'] = Array('template' => '{$template_name}', 'start_pos' => 0, 'end_pos' => " . strlen($data) . ");\n";
// $code .= "__@@__DefinitionsMarker__@@__\n";
// $code .= "if (!\$this->CacheStart('".abs(crc32($t_name))."_0')) {\n";
$this->Buffers[0] = '<?'."php $code ?>\n";
$this->Cacheable[0] = true;
$this->Definitions = '';
// finding all the tags
$reg = '(.*?)(<[\\/]?)' . TAG_NAMESPACE . '([^>]*?)([\\/]?>)(\r\n){0,1}';
preg_match_all('/'.$reg.'/s', $data, $results, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
$this->InsideComment = false;
foreach ($results as $tag_data) {
$tag = array(
'opening' => $tag_data[2][0],
'tag' => $tag_data[3][0],
'closing' => $tag_data[4][0],
'line' => substr_count(substr($data, 0, $tag_data[2][1]), "\n")+1,
'pos' => $tag_data[2][1],
'file' => $t_name,
'template' => $template_name,
);
// the idea is to count number of comment openings and closings before current tag
// if the numbers do not match we inverse the status of InsideComment
if (substr_count($tag_data[1][0], '<!--') != substr_count($tag_data[1][0], '-->')) {
$this->InsideComment = !$this->InsideComment;
}
// appending any text/html data found before tag
$this->Buffers[$this->Level] .= $tag_data[1][0];
if (!$this->InsideComment) {
$tmp_tag = $this->Application->CurrentNTag;
$this->Application->CurrentNTag = $tag;
if ($this->ProcessTag($tag) === false) {
$this->Application->CurrentNTag = $tmp_tag;
return false;
}
$this->Application->CurrentNTag = $tmp_tag;
}
else {
$this->Buffers[$this->Level] .= $tag_data[2][0].$tag_data[3][0].$tag_data[4][0];
}
}
if ($this->Level > 0) {
$this->Application->handleError(E_USER_ERROR, 'Unclosed tag opened by '.$this->TagInfo($this->Stack[$this->Level]->Tag), $this->Stack[$this->Level]->Tag['file'], $this->Stack[$this->Level]->Tag['line']);
return false;
}
// appending text data after last tag (after its closing pos),
// 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] .= '<?'.'php '."\n\$_parser->CacheEnd();\n}\n"." ?".">\n";
// $this->Buffers[$this->Level] = str_replace('__@@__DefinitionsMarker__@@__', $this->Definitions, $this->Buffers[$this->Level]);
return true;
}
function SplitParamsStr($params_str)
{
preg_match_all('/([\${}a-zA-Z0-9_.\\-\\\\#\\[\\]]+)=(["\']{1,1})(.*?)(?<!\\\)\\2/s', $params_str, $rets, PREG_SET_ORDER);
$values = Array();
// we need to replace all occurences of any current param $key with {$key} for correct variable substitution
foreach ($rets AS $key => $val){
$values[$val[1]] = str_replace('\\' . $val[2], $val[2], $val[3]);
}
return $values;
}
function SplitTag($tag)
{
if (!preg_match('/([^_ \t\r\n]*)[_]?([^ \t\r\n]*)[ \t\r\n]*(.*)$$/s', $tag['tag'], $parts)) {
// this is virtually impossible, but just in case
$this->Application->handleError(E_USER_ERROR, 'Incorrect tag format: '.$tag['tag'], $tag['file'], $tag['line']);
return false;
}
$splited['prefix'] = $parts[2] ? $parts[1] : '__auto__';
$splited['name'] = $parts[2] ? $parts[2] : $parts[1];
$splited['attrs'] = $parts[3];
return $splited;
}
function ProcessTag($tag)
{
$splited = $this->SplitTag($tag);
if ($splited === false) {
return false;
}
$tag = array_merge($tag, $splited);
$tag['processed'] = false;
$tag['NP'] = $this->SplitParamsStr($tag['attrs']);
$o = '';
$tag['is_closing'] = $tag['opening'] == '</' || $tag['closing'] == '/>';
if (class_exists('_Tag_'.$tag['name'])) { // block tags should have special handling class
if ($tag['opening'] == '<') {
$class = '_Tag_'.$tag['name'];
$instance = new $class($tag);
$instance->Parser =& $this;
/* @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;
}
if ($tag['is_closing']) { // not ELSE here, because tag may be <empty/> and still has a handler-class
if ($this->Level == 0) {
$dump = array();
foreach ($this->Stack as $instance) {
$dump[] = $instance->Tag;
}
print_pre($dump);
$this->Application->handleError(E_USER_ERROR, 'Closing tag without an opening: '.$this->TagInfo($tag).' <b> - probably opening tag was removed or nested tags error</b>', $tag['file'], $tag['line']);
return false;
}
if ($this->Stack[$this->Level]->Tag['name'] != $tag['name']) {
$opening_tag = $this->Stack[$this->Level]->Tag;
$this->Application->handleError(E_USER_ERROR,
'Closing tag '.$this->TagInfo($tag).' does not match
opening tag at current nesting level ('.$this->TagInfo($opening_tag).'
opened at line '.$opening_tag['line'].')', $tag['file'], $tag['line']);
return false;
}
$o .= $this->Stack[$this->Level]->Close($tag); // DO NOT use $this->Level-- here because it's used inside Close
$this->Level--;
}
}
else { // regular tags - just compile
if (!$tag['is_closing']) {
$this->Application->handleError(E_USER_ERROR, 'Tag without a handler: '.$this->TagInfo($tag).' <b> - probably missing &lt;empty <span style="color: red">/</span>&gt; tag closing</b>', $tag['file'], $tag['line']);
return false;
}
if ($this->Level > 0) $o .= $this->Stack[$this->Level]->PassThrough($tag);
if (!$tag['processed']) {
$compiled = $this->CompileTag($tag);
if ($compiled === false) return false;
if (isset($tag['NP']['cachable']) && (!$tag['NP']['cachable'] || $tag['NP']['cachable'] == 'false')) {
$this->Cachable[$this->Level] = false;
}
$o .= '<?'.'php ' . $compiled . " ?>\n";
// $o .= '<?'.'php ';
// $o .= (isset($tag['NP']['cachable']) && (!$tag['NP']['cachable'] || $tag['NP']['cachable'] == 'false')) ? $this->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)
{
return "<b>{$tag['prefix']}_{$tag['name']}".($with_params ? ' '.$tag['attrs'] : '')."</b>";
}
function CompileParamsArray($arr)
{
$to_pass = 'Array(';
foreach ($arr as $name => $val) {
$to_pass .= '"'.$name.'" => "'.str_replace('"', '\"', $val).'",';
}
$to_pass .= ')';
return $to_pass;
}
function CompileTag($tag)
{
$to_pass = $this->CompileParamsArray($tag['NP']);
$code = '';
if ($tag['prefix'] == '__auto__') {
$prefix = $this->GetParam('PrefixSpecial');
$code .= '$_p_ =& $_parser->GetProcessor($PrefixSpecial);'."\n";
$code .= 'echo $_p_->ProcessParsedTag(\''.$tag['name'].'\', '.$to_pass.', "$PrefixSpecial", \''.$tag['file'].'\', '.$tag['line'].');'."\n";
}
else {
$prefix = $tag['prefix'];
$code .= '$_p_ =& $_parser->GetProcessor("'.$tag['prefix'].'");'."\n";
$code .= 'echo $_p_->ProcessParsedTag(\''.$tag['name'].'\', '.$to_pass.', "'.$tag['prefix'].'", \''.$tag['file'].'\', '.$tag['line'].');'."\n";
}
if (isset($tag['NP']['result_to_var'])) {
$code .= "\$params['{$tag['NP']['result_to_var']}'] = \$_parser->GetParam('{$tag['NP']['result_to_var']}');\n";
$code .= "\${$tag['NP']['result_to_var']} = \$params['{$tag['NP']['result_to_var']}'];\n";
}
if ($prefix && strpos($prefix, '$') === false) {
$p =& $this->GetProcessor($prefix);
if (!is_object($p) || !$p->CheckTag($tag['name'], $tag['prefix'])) {
$this->Application->handleError(E_USER_ERROR, 'Unknown tag: '.$this->TagInfo($tag).' <b> - incorrect tag name or prefix </b>', $tag['file'], $tag['line']);
return false;
}
}
return $code;
}
function CheckTemplate($t, $silent=null)
{
$pre_parsed = $this->Application->TemplatesCache->GetPreParsed($t);
if (!$pre_parsed) {
if (!$silent) {
if ($this->Application->isDebugMode()) $this->Application->Debugger->appendTrace();
trigger_error('Cannot include "<strong>' . $t . '</strong>" - file does not exist', E_USER_ERROR);
}
return false;
}
$force_compile = defined('DBG_NPARSER_FORCE_COMPILE') && DBG_NPARSER_FORCE_COMPILE;
if (!$pre_parsed || !$pre_parsed['active'] || $force_compile) {
$inc_parser = new NParser();
if ($force_compile) {
// remove Front-End theme markings during total compilation
$t = preg_replace('/^theme:.*?\//', '', $t);
}
if (!$inc_parser->Compile($pre_parsed, $t)) return false;
}
return $pre_parsed;
}
function Run($t, $silent=null)
{
$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 (defined('SAFE_MODE') && SAFE_MODE) { // read cache files from database since can't save on filesystem
$conn =& $this->Application->GetADODBConnection();
$cached = $conn->GetRow('SELECT * FROM '.TABLE_PREFIX.'Cache WHERE VarName = "'.$pre_parsed['fname'].'"');
if ($cached !== false && $cached['Cached'] > filemtime($pre_parsed['tname'])) {
eval('?'.'>'.$cached['Data']);
}
}
else {
if ($pre_parsed['mode'] == 'file') {
include($pre_parsed['fname']);
}
else {
eval('?'.'>'.$pre_parsed['content']);
}
}
$output = ob_get_contents();
ob_end_clean();
$this->TemplateName = $backup_template;
$this->TempalteFullPath = $backup_fullpath;
return $output;
}
function &GetProcessor($prefix)
{
static $Processors = array();
if (!isset($Processors[$prefix])) {
$Processors[$prefix] = $this->Application->recallObject($prefix.'_TagProcessor');
}
return $Processors[$prefix];
}
function SelectParam($params, $possible_names)
{
if (!is_array($params)) return;
if (!is_array($possible_names))
$possible_names = explode(',', $possible_names);
foreach ($possible_names as $name)
{
if( isset($params[$name]) ) return $params[$name];
}
return false;
}
function SetParams($params)
{
$this->Params = $params;
$keys = array_keys($this->Params);
}
function GetParam($name)
{
return isset($this->Params[$name]) ? $this->Params[$name] : false;
}
function SetParam($name, $value)
{
$this->Params[$name] = $value;
}
function PushParams($params)
{
$this->ParamsStack[$this->ParamsLevel++] = $this->Params;
$this->Params = $params;
}
function PopParams()
{
$this->Params = $this->ParamsStack[--$this->ParamsLevel];
}
function ParseBlock($params, $pass_params=false)
{
if (isset($params['cache_timeout']) && ($ret = $this->CacheGet($this->FormCacheKey('element_'.$params['name'])))) {
return $ret;
}
+ if (substr($params['name'], 0, 5) == 'html:') {
+ return substr($params['name'], 6);
+ }
+
if (!array_key_exists($params['name'], $this->Elements) && array_key_exists('default_element', $params)) {
// when given element not found, but default element name given, then render it instead
$params['name'] = $params['default_element'];
unset($params['default_element']);
return $this->ParseBlock($params, $pass_params);
}
$original_params = $params;
if ($pass_params || isset($params['pass_params'])) $params = array_merge($this->Params, $params);
$this->PushParams($params);
$data_exists_bak = $this->DataExists;
// if we are parsing design block and we have block_no_data - we need to wrap block_no_data into design,
// so we should set DataExists to true manually, otherwise the design block will be skipped because of data_exists in params (by Kostja)
//
// keep_data_exists is used by block RenderElement (always added in ntags.php), to keep the DataExists value
// from inside-content block, otherwise when parsing the design block DataExists will be reset to false resulting missing design block (by Kostja)
//
// Inside-content block parsing result is given to design block in "content" parameter (ntags.php) and "keep_data_exists"
// is only passed, when parsing design block. In case, when $this->DataExists is set to true, but
// zero-length content (in 2 cases: method NParser::CheckNoData set it OR really empty block content)
// is returned from inside-content block, then design block also should not be shown (by Alex)
$this->DataExists = (isset($params['keep_data_exists']) && isset($params['content']) && $params['content'] != '' && $this->DataExists) || (isset($params['design']) && isset($params['block_no_data']) && $params['name'] == $params['design']);
if (!array_key_exists($params['name'], $this->Elements)) {
$pre_parsed = $this->Application->TemplatesCache->GetPreParsed($params['name']);
if ($pre_parsed) {
$ret = $this->IncludeTemplate($params);
if (array_key_exists('no_editing', $params) && $params['no_editing']) {
// when individual render element don't want to be edited
return $ret;
}
return defined('EDITING_MODE') ? $this->DecorateBlock($ret, $params, true) : $ret;
}
if ($this->Application->isDebugMode()) {
$this->Application->Debugger->appendTrace();
}
$trace_results = debug_backtrace();
$this->Application->handleError(E_USER_ERROR, '<b>Rendering of undefined element '.$params['name'].'</b>', $trace_results[0]['file'], $trace_results[0]['line']);
return false;
}
$m_processor =& $this->GetProcessor('m');
$flag_values = $m_processor->PreparePostProcess($params);
$f_name = $this->Elements[$params['name']];
$ret = $f_name($this, $params);
$ret = $m_processor->PostProcess($ret, $flag_values);
$block_params = $this->Params; // input parameters, but modified inside rendered block
$this->PopParams();
$this->CheckNoData($ret, $params);
$this->DataExists = $data_exists_bak || $this->DataExists;
if (isset($original_params['cache_timeout'])) {
$this->CacheSet($this->FormCacheKey('element_'.$original_params['name']), $ret, $original_params['cache_timeout']);
}
if (array_key_exists('no_editing', $block_params) && $block_params['no_editing']) {
// when individual render element don't want to be edited
return $ret;
}
return defined('EDITING_MODE') ? $this->DecorateBlock($ret, $params) : $ret;
}
function DecorateBlock($block_content, $block_params, $is_template = false)
{
static $used_ids = Array (), $base_url = null;
if (!isset($base_url)) {
$base_url = $this->Application->BaseURL();
}
// $prepend = '[name: ' . $block_params['name'] . '] [params: ' . implode(', ', array_keys($block_params)) . ']';
$decorate = false;
if ($is_template) {
// content inside pair RenderElement tag
// $prepend = '<strong>CONTENT_OF_DESIGN:</strong> ' . $prepend;
if (EDITING_MODE == EDITING_MODE_INSIDES) {
$decorate = true;
}
}
else {
if (strpos($block_params['name'], '__capture_') === 0) {
// capture tag (usually inside pair RenderElement)
// $prepend = '<strong>CAPTURE:</strong> ' . $prepend;
if (EDITING_MODE == EDITING_MODE_INSIDES) {
$decorate = true;
}
}
elseif (array_key_exists('content', $block_params)) {
// pair RenderElement (on template, were it's used)
// $prepend = '<strong>PAIR_RENDER_ELEMENT:</strong> ' . $prepend;
if (EDITING_MODE == EDITING_MODE_DESIGN) {
$decorate = true;
}
}
else {
// non-pair RenderElement
// $prepend = '<strong>SINGLE_RENDER_ELEMENT:</strong> ' . $prepend;
if (EDITING_MODE == EDITING_MODE_INSIDES) {
$decorate = true;
}
if (array_key_exists('layout_view', $block_params) && $block_params['layout_view'] && (EDITING_MODE == EDITING_MODE_LAYOUT)) {
$decorate = true;
}
}
}
if (!$decorate) {
return $block_content;
}
else {
$block_content = /*$prepend .*/ $block_content;
}
$block_name = $block_params['name'];
$function_name = $is_template ? $block_name : $this->Elements[$block_name];
// ensure unique id for every div (used from print lists)
$container_num = 1;
$container_id = 'parser_block[' . $function_name . ']';
while (in_array($container_id . '_' . $container_num, $used_ids)) {
$container_num++;
}
$container_id .= '_' . $container_num;
$used_ids[] = $container_id;
// prepare parameter string
$param_string = $block_name . ':' . $function_name;
$block_editor = '
<div id="' . $container_id . '" params="' . $param_string . '" class="block-edit-btn-container">
<div class="cms-edit-btn">
<div class="cms-btn-image">
<img src="' . $base_url . 'core/admin_templates/img/top_frame/icons/content_mode.gif" width="15" height="16" alt=""/>
</div>
<div class="cms-btn-text" id="' . $container_id . '_btn">Edit</div>
</div>
<div class="cms-btn-content">
%s
</div>
</div>';
// 1 - text before, 2 - open tag, 3 - open tag attributes, 4 - content inside tag, 5 - closing tag, 6 - text after closing tag
if (preg_match('/^(\s*)<(td|span)(.*?)>(.*)<\/(td|span)>(.*)$/is', $block_content, $regs)) {
// div inside span -> put div outside span
return $regs[1] . '<' . $regs[2] . ' ' . $regs[3] . '>' . str_replace('%s', $regs[4], $block_editor) . '</' . $regs[5] . '>' . $regs[6];
}
return str_replace('%s', $block_content, $block_editor);
}
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;
if (!isset($silent) && array_key_exists('is_silent', $params)) {
$silent = $params['is_silent'];
}
if (isset($params['pass_params'])) {
// ability to pass params from block to template
$params = array_merge($this->Params, $params);
}
$this->PushParams($params);
$ret = $this->Run($t, $silent);
$this->PopParams();
$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;
}
function CheckNoData(&$ret, $params)
{
if (array_key_exists('data_exists', $params) && $params['data_exists'] && !$this->DataExists) {
$block_no_data = isset($params['BlockNoData']) ? $params['BlockNoData'] : (isset($params['block_no_data']) ? $params['block_no_data'] : false);
if ($block_no_data) {
$ret = $this->ParseBlock(array('name'=>$block_no_data));
}
else {
$ret = '';
}
}
}
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/nparser/ntags.php
===================================================================
--- branches/RC/core/kernel/nparser/ntags.php (revision 11933)
+++ branches/RC/core/kernel/nparser/ntags.php (revision 11934)
@@ -1,574 +1,601 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.net/license/ for copyright notices and details.
*/
class _BlockTag extends kBase {
/**
* Enter description here...
*
* @var NParser
*/
var $Parser = null;
var $Tag = null;
/**
* Contains parameter names, that should be given to tag in any case
*
* @var Array
*/
var $_requiredParams = Array ();
function _BlockTag($tag)
{
parent::kBase();
$this->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 <b> without required ' . implode(', ', $missing_params) . ' </b> attribute';
if (count($missing_params) > 1) {
$error_msg .= '(-s)';
}
$this->Application->handleError(E_USER_ERROR, $error_msg, $tag['file'], $tag['line']);
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 <inp2:m_else/>
*
* @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 .= '<?'."php\n" . ( is_array($code) ? "\t".implode("\n\t", $code)."\n" : $code) .'?>';
}
else {
$o .= ( is_array($code) ? "\t".implode("\n\t", $code)."\n" : $code);
}
}
}
class _Tag_Comment extends _BlockTag {
function PassThrough(&$tag)
{
$tag['processed'] = true;
}
function Close($tag)
{
return '';
}
}
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 .= '<?'.'php';
$this->AppendCode($o, $code, false);
return $o;
// $this->Parser->Definitions .= $o."\n";
// return '';
} */
function _Tag_DefineElement($tag)
{
parent::_BlockTag($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);";
$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 {
function _Tag_Capture($tag)
{
parent::_Tag_DefineElement($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 = '';
function _Tag_RenderElement($tag)
{
parent::_Tag_DefineElement($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 .= '<?'.'php ';
$this->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;
}
$this->Single = false;
$this->OriginalTag = $tag;
$tag['NP']['name'] = $tag['NP']['design'] . '_' . abs(crc32($tag['file'])) . '_' . $tag['line']; //'__lambda';
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->OriginalTag['NP']['design'] . '_' . abs(crc32($this->OriginalTag['file'])) . '_' . $this->OriginalTag['line']; //'__lambda';
$this->RenderDesignCode($o, $this->OriginalTag['NP']);
return $o;
}
}
class _Tag_RenderElements extends _BlockTag {
function _Tag_RenderElements($tag)
{
parent::_BlockTag($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';
$instance = new $class($tag);
/* @var $instance _Tag_RenderElement */
$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 {
function _Tag_Param($tag)
{
parent::_BlockTag($tag);
$this->_requiredParams = Array ('name');
}
function Open($tag)
{
$o = parent::Open($tag);
if ($o === false) {
// some required params not passed
return $o;
}
$capture_params = $tag['NP'];
$capture_params['name'] = '__capture_' . $tag['NP']['name'];
$capture_to_pass = $this->Parser->CompileParamsArray($capture_params);
$code[] = "if (isset(\$_parser->Captures['{$tag['NP']['name']}'])) {";
$code[] = "\${$tag['NP']['name']} = \$_parser->ParseBlock($capture_to_pass);";
$code[] = "}";
$code[] = "else {";
$to_pass = $this->Parser->CompileParamsArray($tag['NP']);
$code[] = '$_p_ =& $_parser->GetProcessor(\'m\');';
$code[] = '$_tag_params = ' . $to_pass . ';';
$code[] = "\${$tag['NP']['name']} = \$_p_->PostProcess(\${$tag['NP']['name']}, \$_p_->PreparePostProcess(\$_tag_params));";
$code[] = "}";
if (array_key_exists('plus', $tag['NP'])) {
$code[] = "\${$tag['NP']['name']} += {$tag['NP']['plus']};";
}
$code[] = "\$params['{$tag['NP']['name']}'] = \${$tag['NP']['name']};";
$code[] = "echo (\${$tag['NP']['name']});";
$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;
function _Tag_If($tag)
{
parent::_BlockTag($tag);
$this->_requiredParams = Array ('check');
}
function Open($tag)
{
$o = parent::Open($tag);
if ($o === false) {
// some required params not passed
return $o;
}
$to_pass = $this->Parser->CompileParamsArray($tag['NP']);
$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);
return $o;
}
function PassThrough(&$tag)
{
$o = '';
if ($tag['name'] == 'else') {
$this->AppendCode($o, '} else {');
$tag['processed'] = true;
}
if ($tag['name'] == 'elseif') {
if (!$this->_checkRequiredParams($tag)) {
return '';
}
$to_pass = $this->Parser->CompileParamsArray($tag['NP']);
$this->_elseIfCount++; // add same count of closing brackets in closing tag
$this->AppendCode($o, "} else {");
$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);
$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 {
function _Tag_IfNot($tag)
{
parent::_Tag_If($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)
{
$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;
}
}
+
+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;}';
+ $code[] = '$_parser->DataExists = $_tmp_data_exists;';
+
+ $this->AppendCode($o, $code);
+ return $o;
+ }
+
+}
\ No newline at end of file

Event Timeline