Update DatabaseMysql and VMXTemplate, fix some notices
parent
5b8968e5e5
commit
c10676624d
|
@ -3,7 +3,8 @@
|
|||
/**
|
||||
* Very stable interface for MySQL - object-oriented at last :)
|
||||
* Select builder is inspired by MediaWiki's one.
|
||||
* (c) Vitaliy Filippov, 2012
|
||||
* Also usable for querying SphinxQL.
|
||||
* (c) Vitaliy Filippov, 2012-2013
|
||||
*/
|
||||
|
||||
if (!defined('MS_HASH'))
|
||||
|
@ -31,6 +32,8 @@ class DatabaseMysql implements Database
|
|||
var $tableNames = array();
|
||||
var $queryLogFile;
|
||||
var $reconnect = true;
|
||||
var $autoBegin;
|
||||
var $ondestroy = 'commit';
|
||||
|
||||
var $queryCount = 0;
|
||||
var $link;
|
||||
|
@ -46,8 +49,11 @@ class DatabaseMysql implements Database
|
|||
* dbname DB name to use
|
||||
* username Username
|
||||
* password Password
|
||||
* reconnect Whether to reconnect on idle timeout [true]
|
||||
* tableNames Table name mappings (virtual => real)
|
||||
* queryLogFile Path to query log file
|
||||
* reconnect Whether to reconnect on idle timeout [true]
|
||||
* autoBegin Whether to automatically begin a transaction of first query [false]
|
||||
* ondestroy commit/rollback/none during destruction [commit]
|
||||
*/
|
||||
function __construct($options)
|
||||
{
|
||||
|
@ -59,7 +65,10 @@ class DatabaseMysql implements Database
|
|||
'username' => '',
|
||||
'password' => '',
|
||||
'reconnect' => true,
|
||||
'tableNames' => array(),
|
||||
'queryLogFile' => '',
|
||||
'autoBegin' => false,
|
||||
'ondestroy' => 'commit',
|
||||
);
|
||||
$options += $defOpts;
|
||||
if ($options['socket'])
|
||||
|
@ -72,6 +81,16 @@ class DatabaseMysql implements Database
|
|||
}
|
||||
}
|
||||
|
||||
function __destruct()
|
||||
{
|
||||
$o = $this->ondestroy;
|
||||
if (($o === 'commit' || $o === 'rollback') && $this->transactions)
|
||||
{
|
||||
$this->transactions = array(false);
|
||||
$this->$o();
|
||||
}
|
||||
}
|
||||
|
||||
function connect()
|
||||
{
|
||||
if ($this->socket !== NULL)
|
||||
|
@ -96,6 +115,10 @@ class DatabaseMysql implements Database
|
|||
else
|
||||
{
|
||||
$this->link->set_charset('utf8');
|
||||
if ($this->autoBegin)
|
||||
{
|
||||
$this->begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,6 +132,18 @@ class DatabaseMysql implements Database
|
|||
return "`".str_replace("`", "``", $name)."`";
|
||||
}
|
||||
|
||||
function quoteInto($str, $params)
|
||||
{
|
||||
$i = 0;
|
||||
$r = '';
|
||||
while (($p = strpos($str, '?')) !== false)
|
||||
{
|
||||
$r .= substr($str, 0, $p) . $this->quote($params[$i++]);
|
||||
$str = substr($str, $p+1);
|
||||
}
|
||||
return $r.$str;
|
||||
}
|
||||
|
||||
function quote($value)
|
||||
{
|
||||
if ($value === NULL)
|
||||
|
@ -124,26 +159,88 @@ class DatabaseMysql implements Database
|
|||
|
||||
function query($sql, $fetchMode = MYSQLI_STORE_RESULT)
|
||||
{
|
||||
if (!$this->link)
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
$this->queryCount++;
|
||||
if ($this->queryLogFile)
|
||||
{
|
||||
file_put_contents($this->queryLogFile, date("[Y-m-d H:i:s] ").$sql."\n", FILE_APPEND);
|
||||
}
|
||||
$r = $this->link->query($sql, $fetchMode);
|
||||
if (!$r)
|
||||
{
|
||||
if ($this->link->errno == 2006 && $this->reconnect && !$this->transactions)
|
||||
{
|
||||
// "MySQL server has gone away"
|
||||
$this->connect();
|
||||
$r = $this->link->query($sql, $fetchMode);
|
||||
if (!$r && $this->link->errno == 2006)
|
||||
{
|
||||
$this->link = false;
|
||||
}
|
||||
}
|
||||
if (!$r)
|
||||
{
|
||||
throw new DatabaseException($this->link->error . "\nQuery: $sql", $this->link->errno);
|
||||
}
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
function multi_select(array $queries, $format = 0)
|
||||
{
|
||||
if (!$this->link)
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
$r = $this->link->query($sql, $fetchMode);
|
||||
$this->queryCount += count($queries);
|
||||
$log = '';
|
||||
foreach ($queries as &$sql)
|
||||
{
|
||||
if (!is_string($sql))
|
||||
{
|
||||
$sql = $this->select_builder($sql[0], $sql[1], $sql[2], @$sql[3]);
|
||||
}
|
||||
$log .= date("[Y-m-d H:i:s] ").$sql."\n";
|
||||
}
|
||||
unset($sql);
|
||||
if ($this->queryLogFile)
|
||||
{
|
||||
file_put_contents($this->queryLogFile, $log, FILE_APPEND);
|
||||
}
|
||||
$sql = implode('; ', $queries);
|
||||
$r = $this->link->multi_query($sql);
|
||||
if (!$r)
|
||||
{
|
||||
if ($this->link->errno == 2006 && $this->reconnect)
|
||||
if ($this->link->errno == 2006 && $this->reconnect && !$this->transactions)
|
||||
{
|
||||
// "MySQL server has gone away"
|
||||
$this->link = false;
|
||||
$this->connect();
|
||||
$r = $this->link->multi_query($sql);
|
||||
if (!$r && $this->link->errno == 2006)
|
||||
{
|
||||
$this->link = false;
|
||||
}
|
||||
}
|
||||
if (!$r)
|
||||
{
|
||||
throw new DatabaseException($this->link->error, $this->link->errno);
|
||||
}
|
||||
throw new DatabaseException($this->link->error, $this->link->errno);
|
||||
}
|
||||
return $r;
|
||||
$results = array();
|
||||
$i = 0;
|
||||
foreach ($queries as $k => $q)
|
||||
{
|
||||
if ($i++)
|
||||
{
|
||||
$this->link->next_result();
|
||||
}
|
||||
$r = $this->link->store_result();
|
||||
$results[$k] = $this->fetch_all($r, $format);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -220,6 +317,8 @@ class DatabaseMysql implements Database
|
|||
* 'conditional expression',
|
||||
* 'field_name' => 'value',
|
||||
* 'field_name' => array('one', 'of', 'values'),
|
||||
* 'field_name < ?' => 'value',
|
||||
* 'field_name < DATE_SUB(?, ?)' => array('arg1', 'arg2'),
|
||||
* 'field1,field2' => array(array(1, 2), array(3, 4)),
|
||||
* )
|
||||
*/
|
||||
|
@ -231,7 +330,21 @@ class DatabaseMysql implements Database
|
|||
foreach ($where as $k => $v)
|
||||
{
|
||||
if (ctype_digit("$k"))
|
||||
$wh[] = $v;
|
||||
{
|
||||
if (is_array($v))
|
||||
{
|
||||
$str = array_shift($v);
|
||||
$wh[] = $this->quoteInto($str, $v);
|
||||
}
|
||||
else
|
||||
{
|
||||
$wh[] = $v;
|
||||
}
|
||||
}
|
||||
elseif (($p = strrpos($k, '?')) !== false)
|
||||
{
|
||||
$wh[] = $this->quoteInto($k, (array)$v);
|
||||
}
|
||||
elseif (is_array($v))
|
||||
{
|
||||
if (!$v)
|
||||
|
@ -240,7 +353,7 @@ class DatabaseMysql implements Database
|
|||
}
|
||||
else
|
||||
{
|
||||
if (is_array($v[0]))
|
||||
if (is_array(reset($v)))
|
||||
foreach ($v as &$l)
|
||||
$l = "(" . implode(",", array_map(array($this, 'quote'), $l)) . ")";
|
||||
else
|
||||
|
@ -248,15 +361,22 @@ class DatabaseMysql implements Database
|
|||
$wh[] = "$k IN (" . implode(",", $v) . ")";
|
||||
}
|
||||
}
|
||||
elseif (preg_match('/^-?\d+(\.\d+)?$/s', $v)) // int/float
|
||||
$wh[] = "$k=$v";
|
||||
elseif ($v !== NULL)
|
||||
$wh[] = "$k=".$this->quote($v);
|
||||
else
|
||||
$wh[] = "$k IS NULL";
|
||||
}
|
||||
if (!$wh)
|
||||
$where = '1';
|
||||
else
|
||||
if (!$this->username && !$this->password && !$this->dbname)
|
||||
{
|
||||
// Sphinx supports neither brackets nor OR operator as of 2.0.6-release O_o
|
||||
$where = join(' AND ', $wh);
|
||||
}
|
||||
elseif ($where)
|
||||
$where = '(' . join(') AND (', $wh) . ')';
|
||||
else
|
||||
$where = '';
|
||||
return $where;
|
||||
}
|
||||
|
||||
|
@ -275,8 +395,11 @@ class DatabaseMysql implements Database
|
|||
* 'FOR UPDATE' or 'LOCK IN SHARE MODE'
|
||||
* 'GROUP BY' => array($groupby_field1 => 'ASC', $groupby_field2 => 'DESC')
|
||||
* 'ORDER BY' => array($orderby_field1 => 'ASC', $orderby_field2 => 'DESC')
|
||||
* 'LIMIT' => array($limit, $offset) or array($limit) or just $limit
|
||||
* 'LIMIT' => array($offset, $limit) or array($limit) or just $limit
|
||||
* 'OFFSET' => $offset, for the case when 'LIMIT' is just $limit
|
||||
*
|
||||
* Sphinx Search extensions:
|
||||
* 'WITHIN GROUP ORDER BY' => array($orderby_field => 'ASC')
|
||||
*/
|
||||
function select_builder($tables, $fields, $where, $options = NULL)
|
||||
{
|
||||
|
@ -304,40 +427,23 @@ class DatabaseMysql implements Database
|
|||
$sql .= 'SQL_NO_CACHE ';
|
||||
elseif (isset($options['CACHE']) || isset($options['SQL_CACHE']))
|
||||
$sql .= 'SQL_CACHE ';
|
||||
$sql .= "$fields FROM $tables WHERE $where";
|
||||
if (isset($options['GROUP BY']))
|
||||
$sql .= "$fields FROM $tables";
|
||||
if ($where)
|
||||
{
|
||||
$g = $options['GROUP BY'];
|
||||
if (is_array($g))
|
||||
{
|
||||
$g1 = array();
|
||||
foreach ($g as $k => $v)
|
||||
{
|
||||
if (ctype_digit("$k"))
|
||||
$g1[] = $v;
|
||||
else
|
||||
$g1[] = "$k $v";
|
||||
}
|
||||
$g = join(',', $g1);
|
||||
}
|
||||
$sql .= " GROUP BY $g";
|
||||
$sql .= " WHERE $where";
|
||||
}
|
||||
if (isset($options['ORDER BY']))
|
||||
if (!empty($options['GROUP BY']) && $options['GROUP BY'] !== '0')
|
||||
{
|
||||
$g = $options['ORDER BY'];
|
||||
if (is_array($g))
|
||||
{
|
||||
$g1 = array();
|
||||
foreach ($g as $k => $v)
|
||||
{
|
||||
if (ctype_digit("$k"))
|
||||
$g1[] = $v;
|
||||
else
|
||||
$g1[] = "$k $v";
|
||||
}
|
||||
$g = join(',', $g1);
|
||||
}
|
||||
$sql .= " ORDER BY $g";
|
||||
$sql .= " GROUP BY ".$this->order_option($options['GROUP BY']);
|
||||
}
|
||||
if (!empty($options['ORDER BY']) && $options['GROUP BY'] !== '0')
|
||||
{
|
||||
$sql .= " ORDER BY ".$this->order_option($options['ORDER BY']);
|
||||
}
|
||||
if (!empty($options['WITHIN GROUP ORDER BY']) && $options['WITHIN GROUP GROUP BY'] !== '0')
|
||||
{
|
||||
// Sphinx Search extension
|
||||
$sql .= " WITHIN GROUP ORDER BY ".$this->order_option($options['WITHIN GROUP ORDER BY']);
|
||||
}
|
||||
$sql .= $this->limit_option($options);
|
||||
if (isset($options['FOR UPDATE']))
|
||||
|
@ -347,10 +453,30 @@ class DatabaseMysql implements Database
|
|||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles ORDER BY or GROUP BY options
|
||||
*/
|
||||
protected function order_option($g)
|
||||
{
|
||||
if (is_array($g))
|
||||
{
|
||||
$g1 = array();
|
||||
foreach ($g as $k => $v)
|
||||
{
|
||||
if (ctype_digit("$k"))
|
||||
$g1[] = $v;
|
||||
else
|
||||
$g1[] = "$k $v";
|
||||
}
|
||||
$g = join(',', $g1);
|
||||
}
|
||||
return $g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a single LIMIT or LIMIT and OFFSET options.
|
||||
*/
|
||||
function limit_option($options)
|
||||
protected function limit_option($options)
|
||||
{
|
||||
if (isset($options['LIMIT']))
|
||||
{
|
||||
|
@ -400,7 +526,7 @@ class DatabaseMysql implements Database
|
|||
else
|
||||
$table = (isset($this->tableNames[$v[1]]) ? $this->quoteId($this->tableNames[$v[1]]) : $v[1]) . ' ' . $k;
|
||||
if ($t)
|
||||
$t .= " $join JOIN $table ON ".$this->where_builder($v[2]);
|
||||
$t .= " $join JOIN $table ON ".($this->where_builder($v[2]) ?: '1=1');
|
||||
else
|
||||
$t = $table;
|
||||
continue;
|
||||
|
@ -448,7 +574,7 @@ class DatabaseMysql implements Database
|
|||
*/
|
||||
function select($tables, $fields = '*', $where = 1, $options = NULL, $format = 0)
|
||||
{
|
||||
if (is_int($fields) || is_string($fields) && ctype_digit($fields))
|
||||
if (is_int($fields))
|
||||
{
|
||||
$sql = $tables;
|
||||
$format = $fields;
|
||||
|
@ -457,10 +583,16 @@ class DatabaseMysql implements Database
|
|||
$sql = $this->select_builder($tables, $fields, $where, $options);
|
||||
if ($format & MS_RESULT)
|
||||
return $this->query($sql, MYSQLI_USE_RESULT);
|
||||
$res = $this->query($sql);
|
||||
return $this->fetch_all($res, $format);
|
||||
}
|
||||
|
||||
function fetch_all($res, $format = 0)
|
||||
{
|
||||
if ($format & MS_LIST)
|
||||
$r = $this->get_rows($sql);
|
||||
$r = $this->get_rows($res);
|
||||
else
|
||||
$r = $this->get_assocs($sql);
|
||||
$r = $this->get_assocs($res);
|
||||
if (!$r)
|
||||
$r = array();
|
||||
if ($format & MS_ROW)
|
||||
|
@ -478,6 +610,7 @@ class DatabaseMysql implements Database
|
|||
}
|
||||
elseif ($format & MS_COL)
|
||||
{
|
||||
$k = false;
|
||||
foreach ($r as $i => $v)
|
||||
{
|
||||
if (!$k)
|
||||
|
@ -507,7 +640,7 @@ class DatabaseMysql implements Database
|
|||
function delete($tables, $where, $options = NULL)
|
||||
{
|
||||
$tables = $this->tables_builder($tables);
|
||||
$where = $this->where_builder($where);
|
||||
$where = $this->where_builder($where) ?: '1=1';
|
||||
$sql = "DELETE FROM $tables WHERE $where";
|
||||
$sql .= $this->limit_option($options);
|
||||
$this->query($sql);
|
||||
|
@ -518,8 +651,9 @@ class DatabaseMysql implements Database
|
|||
* Builds an INSERT query.
|
||||
* @param string $table Table name to insert rows to.
|
||||
* @param array $rows Array of table rows to be inserted.
|
||||
* @param boolean $onduplicatekey If true, include
|
||||
* ON DUPLICATE KEY UPDATE column=VALUES(column) for all columns.
|
||||
* @param boolean $onduplicatekey If true, create MySQL-specific "UPSERT" query using
|
||||
* INSERT .. ON DUPLICATE KEY UPDATE column=VALUES(column) for all columns.
|
||||
* @param boolean $replace If true, use REPLACE instead of INSERT
|
||||
*/
|
||||
function insert_builder($table, $rows, $onduplicatekey = false, $replace = false)
|
||||
{
|
||||
|
@ -535,8 +669,9 @@ class DatabaseMysql implements Database
|
|||
$rs[] = $this->quote($r[$k]);
|
||||
$r = "(".implode(",", $rs).")";
|
||||
}
|
||||
$sphinx = !$this->username && !$this->password && !$this->dbname;
|
||||
foreach ($key as &$k)
|
||||
if (strpos($k, '`') === false)
|
||||
if (strpos($k, '`') === false && (!$sphinx || $k !== 'id'))
|
||||
$k = $this->quoteId($k);
|
||||
$sql = ($replace ? "REPLACE" : "INSERT").
|
||||
" INTO $table (".implode(",",$key).") VALUES ".implode(",",$rows);
|
||||
|
@ -592,21 +727,19 @@ class DatabaseMysql implements Database
|
|||
return false;
|
||||
if (!is_array(@$rows[0]))
|
||||
$rows = array($rows);
|
||||
$sql = $this->insert_builder($table, $rows, true, !empty($options['replace']));
|
||||
$sql = $this->insert_builder($table, $rows, empty($options['REPLACE']), !empty($options['REPLACE']));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = array();
|
||||
if (!is_array($rows))
|
||||
$rows = array($rows);
|
||||
foreach ($rows as $k => $v)
|
||||
foreach ((array)$rows as $k => $v)
|
||||
{
|
||||
if (!ctype_digit("$k"))
|
||||
$sql[] = $k.'='.$this->quote($v);
|
||||
else
|
||||
$sql[] = $v;
|
||||
}
|
||||
$where = $this->where_builder($where);
|
||||
$where = $this->where_builder($where) ?: '1=1';
|
||||
$sql = 'UPDATE ' . $this->tables_builder($table) . ' SET ' . implode(', ', $sql) . ' WHERE ' . $where;
|
||||
$sql .= $this->limit_option($options);
|
||||
}
|
||||
|
@ -615,10 +748,16 @@ class DatabaseMysql implements Database
|
|||
return false;
|
||||
}
|
||||
|
||||
function get_rows($sql)
|
||||
function replace($table, $rows, $where = NULL, $options = array())
|
||||
{
|
||||
$options['REPLACE'] = true;
|
||||
return $this->update($table, $rows, $where, $options);
|
||||
}
|
||||
|
||||
protected function get_rows($res)
|
||||
{
|
||||
$r = array();
|
||||
if ($res = $this->query($sql))
|
||||
if ($res)
|
||||
{
|
||||
while ($row = $res->fetch_row())
|
||||
$r[] = $row;
|
||||
|
@ -627,10 +766,10 @@ class DatabaseMysql implements Database
|
|||
return $r;
|
||||
}
|
||||
|
||||
function get_assocs($sql)
|
||||
protected function get_assocs($res)
|
||||
{
|
||||
$r = array();
|
||||
if ($res = $this->query($sql))
|
||||
if ($res)
|
||||
{
|
||||
while ($row = $res->fetch_assoc())
|
||||
$r[] = $row;
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
# Homepage: http://yourcmc.ru/wiki/VMX::Template
|
||||
# Author: Vitaliy Filippov, 2006-2013
|
||||
# $Id$
|
||||
# $Id: template.php 1394 2013-04-09 15:01:39Z vitalif $
|
||||
|
||||
# TODO for perl version - rewrite it and prevent auto-vivification on a.b
|
||||
|
||||
class VMXTemplateState
|
||||
{
|
||||
|
@ -48,7 +50,7 @@ class VMXTemplate
|
|||
static $Mon, $mon, $Wday;
|
||||
static $cache_type = NULL;
|
||||
static $cache = array();
|
||||
static $safe_tags = '<div> <span> <a> <b> <i> <u> <p> <h1> <h2> <h3> <h4> <h5> <h6> <strike> <strong> <small> <big> <blink> <center> <ol> <pre> <sub> <sup> <font> <br> <table> <tr> <td> <th> <tbody> <tfoot> <thead> <tt> <ul> <li> <em> <img> <marquee>';
|
||||
static $safe_tags = '<div> <blockquote> <span> <a> <b> <i> <u> <p> <h1> <h2> <h3> <h4> <h5> <h6> <strike> <strong> <small> <big> <blink> <center> <ol> <pre> <sub> <sup> <font> <br> <table> <tr> <td> <th> <tbody> <tfoot> <thead> <tt> <ul> <li> <em> <img> <marquee>';
|
||||
|
||||
// Timestamp format constants
|
||||
const TS_UNIX = 0;
|
||||
|
@ -281,8 +283,7 @@ class VMXTemplate
|
|||
}
|
||||
if (!class_exists($class) || !isset($class::$version) || $class::$version < self::CODE_VERSION)
|
||||
{
|
||||
// Cache file from some older version - reset it
|
||||
$this->options->error("Please, clear template cache path after upgrading VMX::Template", true);
|
||||
$this->options->error("MD5 collision :) file=$fn, cache=$file", true);
|
||||
$this->failed[$fn] = true;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -440,8 +441,8 @@ class VMXTemplate
|
|||
*/
|
||||
static function filter_strip_space(&$text)
|
||||
{
|
||||
$text = preg_replace('/^[ \t\v]+/m', '', $text);
|
||||
$text = preg_replace('/[ \t\v]+$/m', '', $text);
|
||||
$text = preg_replace('/^[ \t]+/m', '', $text);
|
||||
$text = preg_replace('/[ \t]+$/m', '', $text);
|
||||
}
|
||||
|
||||
/*** Function implementations ***/
|
||||
|
@ -615,6 +616,22 @@ class VMXTemplate
|
|||
return mb_strtoupper(mb_substr($str, 0, 1)) . mb_substr($str, 0, 1);
|
||||
}
|
||||
|
||||
// Select one of 3 plural forms for russian language
|
||||
static function plural_ru($count, $one, $few, $many)
|
||||
{
|
||||
$sto = $count % 100;
|
||||
if ($sto >= 10 && $sto <= 20)
|
||||
return $many;
|
||||
switch ($count % 10)
|
||||
{
|
||||
case 1: return $one;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: return $few;
|
||||
}
|
||||
return $many;
|
||||
}
|
||||
|
||||
// Limited-edition timestamp parser
|
||||
static function timestamp($ts = 0, $format = 0)
|
||||
{
|
||||
|
@ -628,7 +645,7 @@ class VMXTemplate
|
|||
{
|
||||
// TS_UNIX or Epoch
|
||||
if (!$ts)
|
||||
$ts = time;
|
||||
$ts = time();
|
||||
}
|
||||
elseif (preg_match('/^\D*(\d{4,})\D*(\d{2})\D*(\d{2})\D*(?:(\d{2})\D*(\d{2})\D*(\d{2})\D*([\+\- ]\d{2}\D*)?)?$/s', $ts, $m))
|
||||
{
|
||||
|
@ -804,16 +821,17 @@ class VMXTemplateParser
|
|||
var $tokens, $tokpos, $tokline, $ptr;
|
||||
|
||||
// Possible tokens consisting of special characters
|
||||
static $chartokens = '+ - = * / % ! , . < > ( ) { } [ ] | .. || && == != <= >= =>';
|
||||
static $chartokens = '# + - = * / % ! , . < > ( ) { } [ ] & .. || && == != <= >= =>';
|
||||
|
||||
// ops_and: ops_eq | ops_eq "&&" ops_and | ops_eq "AND" ops_and
|
||||
// ops_and: ops_bitand | ops_bitand "&&" ops_and | ops_bitand "AND" ops_and
|
||||
// ops_bitand: ops_eq | ops_eq "&" ops_bitand
|
||||
// ops_eq: ops_cmp | ops_cmp "==" ops_cmp | ops_cmp "!=" ops_cmp
|
||||
// ops_cmp: ops_add | ops_add '<' ops_add | ops_add '>' ops_add | ops_add "<=" ops_add | ops_add ">=" ops_add
|
||||
// ops_add: ops_mul | ops_mul '+' ops_add | ops_mul '-' ops_add
|
||||
// ops_mul: exp_neg | exp_neg '*' ops_mul | exp_neg '/' ops_mul | exp_neg '%' ops_mul
|
||||
static $ops = array(
|
||||
'or' => array(array('||', '$or', '$xor'), 'and', true),
|
||||
'and' => array(array('&&', '$and'), 'eq', true),
|
||||
'and' => array(array('&&', '$and'), 'bitand', true),
|
||||
'bitand' => array(array('&'), 'eq', true),
|
||||
'eq' => array(array('==', '!='), 'cmp', false),
|
||||
'cmp' => array(array('<', '>', '<=', '>='), 'add', false),
|
||||
'add' => array(array('+', '-'), 'mul', true),
|
||||
|
@ -1093,8 +1111,8 @@ class VMXTemplateParser
|
|||
}
|
||||
$text = "Unexpected $tok, expected ";
|
||||
if (count($expected) > 1)
|
||||
$text .= "one of '";
|
||||
$text .= implode("', '", $expected)."'";
|
||||
$text .= "one of ";
|
||||
$text .= "'".implode("', '", $expected)."'";
|
||||
$this->raise($text);
|
||||
}
|
||||
|
||||
|
@ -1200,10 +1218,23 @@ class VMXTemplateParser
|
|||
try
|
||||
{
|
||||
// Try to parse from here, skip invalid parts
|
||||
$r = $this->$handler();
|
||||
$this->consume($this->eod);
|
||||
// Add newline count from code fragment
|
||||
$this->lineno += substr_count($this->code, "\n", $pos, $this->pos-$pos);
|
||||
if ($this->tok() == '#')
|
||||
{
|
||||
// Comment!
|
||||
$this->pos = strpos($this->code, $this->eod, $this->pos);
|
||||
if ($this->pos === false)
|
||||
{
|
||||
throw new VMXTemplateParseException($this->eod . ' not found');
|
||||
}
|
||||
$this->pos += strlen($this->eod);
|
||||
}
|
||||
else
|
||||
{
|
||||
$r = $this->$handler();
|
||||
$this->consume($this->eod);
|
||||
// Add newline count from code fragment
|
||||
$this->lineno += substr_count($this->code, "\n", $pos, $this->pos-$pos);
|
||||
}
|
||||
}
|
||||
catch (VMXTemplateParseException $e)
|
||||
{
|
||||
|
@ -1502,6 +1533,7 @@ $varref = array_pop(\$stack);";
|
|||
if ($this->tok() == ',')
|
||||
$this->ptr++;
|
||||
}
|
||||
$this->ptr++;
|
||||
}
|
||||
$code = false;
|
||||
if ($this->tok() == '=')
|
||||
|
@ -1842,7 +1874,7 @@ $varref_index = \$stack[count(\$stack)-1]++;";
|
|||
}
|
||||
$r = "\$this->parent->call_block($parts[0], $args, \"".addslashes($this->errorinfo())."\")";
|
||||
}
|
||||
if (count($parts) == 1)
|
||||
elseif (count($parts) == 1)
|
||||
{
|
||||
$fn = strtolower($parts[0]);
|
||||
if (isset(self::$functions[$fn]))
|
||||
|
@ -2005,6 +2037,7 @@ $varref_index = \$stack[count(\$stack)-1]++;";
|
|||
function function_strip($e, $t='') { return "strip_tags($e".($t?",$t":"").")"; }
|
||||
|
||||
/* удаление "небезопасных" HTML-тегов */
|
||||
/* TODO: м.б исправлять некорректную разметку? */
|
||||
function function_strip_unsafe($e) { return "strip_tags($e, self::\$safe_tags)"; }
|
||||
|
||||
/* заменить \n на <br /> */
|
||||
|
@ -2036,10 +2069,9 @@ $varref_index = \$stack[count(\$stack)-1]++;";
|
|||
}
|
||||
|
||||
/* strftime */
|
||||
function function_strftime($fmt, $date, $time = '')
|
||||
function function_strftime($fmt, $date)
|
||||
{
|
||||
$e = $time ? "($date).' '.($time)" : $date;
|
||||
return "strftime($fmt, self::timestamp($e))";
|
||||
return "strftime($fmt, self::timestamp($date))";
|
||||
}
|
||||
|
||||
/* ограничение длины строки $maxlen символами на границе пробелов и добавление '...', если что. */
|
||||
|
@ -2050,6 +2082,12 @@ $varref_index = \$stack[count(\$stack)-1]++;";
|
|||
return "self::" . ($this->options->use_utf8 ? "mb_" : "") . "strlimit(".join(",", $a).")";
|
||||
}
|
||||
|
||||
/* выбор правильной формы множественного числа для русского языка */
|
||||
function function_plural_ru($count, $one, $few, $many)
|
||||
{
|
||||
return "self::plural_ru($count, $one, $few, $many)";
|
||||
}
|
||||
|
||||
/** Массивы и хеши **/
|
||||
|
||||
/* создание хеша */
|
||||
|
|
4
olap.php
4
olap.php
|
@ -343,7 +343,7 @@ class OLAP
|
|||
}
|
||||
|
||||
$vars = array('build' => 1);
|
||||
if ($this->group_fields[0]['type'] == 'graph')
|
||||
if ($this->group_fields && $this->group_fields[0]['type'] == 'graph')
|
||||
{
|
||||
$vars['graphs'] = $this->build_graphs($this->group_fields, $data, $tdkeys);
|
||||
$vars['groups'] = $this->tpl_jsgraphs($vars['graphs']);
|
||||
|
@ -682,7 +682,7 @@ class OLAP
|
|||
$i = count($tdhead);
|
||||
foreach ($cells as &$v)
|
||||
{
|
||||
if (!$rows[$i])
|
||||
if (empty($rows[$i]))
|
||||
$rows[$i] = array();
|
||||
$rows[$i] = array_merge($rows[$i], $v);
|
||||
$i++;
|
||||
|
|
|
@ -236,7 +236,7 @@ function doPlot(logarithmic)
|
|||
<!-- SET o = 0 -->
|
||||
<!-- FOR d = graph.desc -->
|
||||
<!--# Описание графика (поле: значение, ..., только значения, не равные значениям предыдущего графика) -->
|
||||
<!-- SET o = or(o, not(graph_index), ne(get(get(get(get(graphs,sub(graph_index,1)),'desc'),d_index),'value'),d.value)) -->
|
||||
<!-- SET o = o || !graph_index || graphs[graph_index-1].desc[d_index].value != d.value -->
|
||||
<!-- IF o -->
|
||||
<p style="margin-left: {mul(d_index,20)}px">{d.field}<!-- IF d.func --> ({lc d.func})<!-- END -->: {d.value}</p>
|
||||
<!-- END -->
|
||||
|
@ -271,7 +271,7 @@ function doPlot(logarithmic)
|
|||
<!-- IF table.desc -->
|
||||
<!-- SET o = 0 -->
|
||||
<!-- FOR d = table.desc -->
|
||||
<!-- SET o = or(o, not(table_index), ne(get(get(get(get(tables,sub(table_index,1)),'desc'),d_index),'value'),d.value)) -->
|
||||
<!-- SET o = o || !table_index || tables[table_index-1].desc[d_index].value != d.value -->
|
||||
<!-- IF o -->
|
||||
<p style="margin-left: {mul(d_index,20)}px">{d.field}<!-- IF d.func --> ({lc d.func})<!-- END -->: {d.value}</p>
|
||||
<!-- END -->
|
||||
|
|
Loading…
Reference in New Issue