Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'core/SegmentExpression.php')
-rw-r--r--core/SegmentExpression.php362
1 files changed, 172 insertions, 190 deletions
diff --git a/core/SegmentExpression.php b/core/SegmentExpression.php
index aaae328462..9340aec4bd 100644
--- a/core/SegmentExpression.php
+++ b/core/SegmentExpression.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -17,7 +17,7 @@ class Piwik_SegmentExpression
{
const AND_DELIMITER = ';';
const OR_DELIMITER = ',';
-
+
const MATCH_EQUAL = '==';
const MATCH_NOT_EQUAL = '!=';
const MATCH_GREATER_OR_EQUAL = '>=';
@@ -26,226 +26,222 @@ class Piwik_SegmentExpression
const MATCH_LESS = '<';
const MATCH_CONTAINS = '=@';
const MATCH_DOES_NOT_CONTAIN = '!@';
-
+
// Special case, since we look up Page URLs/Page titles in a sub SQL query
const MATCH_ACTIONS_CONTAINS = 'IN';
-
+
const INDEX_BOOL_OPERATOR = 0;
const INDEX_OPERAND = 1;
-
+
function __construct($string)
{
$this->string = $string;
$this->tree = $this->parseTree();
}
+
protected $joins = array();
protected $valuesBind = array();
protected $parsedTree = array();
protected $tree = array();
- protected $parsedSubExpressions = array();
-
- /**
- * Given the array of parsed filters containing, for each filter,
- * the boolean operator (AND/OR) and the operand,
- * Will return the array where the filters are in SQL representation
- *
- * @throws Exception
- * @return array
- */
+ protected $parsedSubExpressions = array();
+
+ /**
+ * Given the array of parsed filters containing, for each filter,
+ * the boolean operator (AND/OR) and the operand,
+ * Will return the array where the filters are in SQL representation
+ *
+ * @throws Exception
+ * @return array
+ */
public function parseSubExpressions()
{
$parsedSubExpressions = array();
- foreach($this->tree as $id => $leaf)
- {
+ foreach ($this->tree as $id => $leaf) {
$operand = $leaf[self::INDEX_OPERAND];
$operator = $leaf[self::INDEX_BOOL_OPERATOR];
- $pattern = '/^(.+?)(' .self::MATCH_EQUAL.'|'
- .self::MATCH_NOT_EQUAL.'|'
- .self::MATCH_GREATER_OR_EQUAL.'|'
- .self::MATCH_GREATER.'|'
- .self::MATCH_LESS_OR_EQUAL.'|'
- .self::MATCH_LESS.'|'
- .self::MATCH_CONTAINS.'|'
- .self::MATCH_DOES_NOT_CONTAIN
- .'){1}(.+)/';
- $match = preg_match( $pattern, $operand, $matches );
- if($match == 0)
- {
- throw new Exception('Segment parameter \''.$operand.'\' does not appear to have a valid format.');
+ $pattern = '/^(.+?)(' . self::MATCH_EQUAL . '|'
+ . self::MATCH_NOT_EQUAL . '|'
+ . self::MATCH_GREATER_OR_EQUAL . '|'
+ . self::MATCH_GREATER . '|'
+ . self::MATCH_LESS_OR_EQUAL . '|'
+ . self::MATCH_LESS . '|'
+ . self::MATCH_CONTAINS . '|'
+ . self::MATCH_DOES_NOT_CONTAIN
+ . '){1}(.+)/';
+ $match = preg_match($pattern, $operand, $matches);
+ if ($match == 0) {
+ throw new Exception('Segment parameter \'' . $operand . '\' does not appear to have a valid format.');
}
$leftMember = $matches[1];
$operation = $matches[2];
$valueRightMember = $matches[3];
- $parsedSubExpressions[] = array(
+ $parsedSubExpressions[] = array(
self::INDEX_BOOL_OPERATOR => $operator,
- self::INDEX_OPERAND => array(
+ self::INDEX_OPERAND => array(
$leftMember,
- $operation,
- $valueRightMember,
- ));
+ $operation,
+ $valueRightMember,
+ ));
}
$this->parsedSubExpressions = $parsedSubExpressions;
return $parsedSubExpressions;
}
- /**
- * Set the given expression
- * @param $parsedSubExpressions
- */
+ /**
+ * Set the given expression
+ * @param $parsedSubExpressions
+ */
public function setSubExpressionsAfterCleanup($parsedSubExpressions)
{
$this->parsedSubExpressions = $parsedSubExpressions;
}
- /**
- * Returns the current sub expression
- * @return array
- */
+ /**
+ * Returns the current sub expression
+ * @return array
+ */
public function getSubExpressions()
{
return $this->parsedSubExpressions;
}
- /**
- * @param array $availableTables
- */
- public function parseSubExpressionsIntoSqlExpressions(&$availableTables=array())
+ /**
+ * @param array $availableTables
+ */
+ public function parseSubExpressionsIntoSqlExpressions(&$availableTables = array())
{
$sqlSubExpressions = array();
$this->valuesBind = array();
$this->joins = array();
-
- foreach($this->parsedSubExpressions as $leaf)
- {
+
+ foreach ($this->parsedSubExpressions as $leaf) {
$operator = $leaf[self::INDEX_BOOL_OPERATOR];
$operandDefinition = $leaf[self::INDEX_OPERAND];
-
+
$operand = $this->getSqlMatchFromDefinition($operandDefinition, $availableTables);
-
+
if ($operand[1] !== null) {
$this->valuesBind[] = $operand[1];
}
$operand = $operand[0];
$sqlSubExpressions[] = array(
self::INDEX_BOOL_OPERATOR => $operator,
- self::INDEX_OPERAND => $operand,
- );
+ self::INDEX_OPERAND => $operand,
+ );
}
-
+
$this->tree = $sqlSubExpressions;
}
- /**
- * Given an array representing one filter operand ( left member , operation , right member)
- * Will return an array containing
- * - the SQL substring,
- * - the values to bind to this substring
- *
- * @param array $def
- * @param array $availableTables
- * @throws Exception
- * @return array
- */
+ /**
+ * Given an array representing one filter operand ( left member , operation , right member)
+ * Will return an array containing
+ * - the SQL substring,
+ * - the values to bind to this substring
+ *
+ * @param array $def
+ * @param array $availableTables
+ * @throws Exception
+ * @return array
+ */
// @todo case insensitive?
protected function getSqlMatchFromDefinition($def, &$availableTables)
{
- $field = $def[0];
- $matchType = $def[1];
+ $field = $def[0];
+ $matchType = $def[1];
$value = $def[2];
-
- switch($matchType)
- {
- case self::MATCH_EQUAL:
- $sqlMatch = '=';
- break;
- case self::MATCH_NOT_EQUAL:
- $sqlMatch = '<>';
- break;
- case self::MATCH_GREATER:
- $sqlMatch = '>';
- break;
- case self::MATCH_LESS:
- $sqlMatch = '<';
- break;
- case self::MATCH_GREATER_OR_EQUAL:
- $sqlMatch = '>=';
- break;
- case self::MATCH_LESS_OR_EQUAL:
- $sqlMatch = '<=';
- break;
- case self::MATCH_CONTAINS:
- $sqlMatch = 'LIKE';
- $value = '%'.$this->escapeLikeString($value).'%';
- break;
- case self::MATCH_DOES_NOT_CONTAIN:
- $sqlMatch = 'NOT LIKE';
- $value = '%'.$this->escapeLikeString($value).'%';
- break;
-
+
+ switch ($matchType) {
+ case self::MATCH_EQUAL:
+ $sqlMatch = '=';
+ break;
+ case self::MATCH_NOT_EQUAL:
+ $sqlMatch = '<>';
+ break;
+ case self::MATCH_GREATER:
+ $sqlMatch = '>';
+ break;
+ case self::MATCH_LESS:
+ $sqlMatch = '<';
+ break;
+ case self::MATCH_GREATER_OR_EQUAL:
+ $sqlMatch = '>=';
+ break;
+ case self::MATCH_LESS_OR_EQUAL:
+ $sqlMatch = '<=';
+ break;
+ case self::MATCH_CONTAINS:
+ $sqlMatch = 'LIKE';
+ $value = '%' . $this->escapeLikeString($value) . '%';
+ break;
+ case self::MATCH_DOES_NOT_CONTAIN:
+ $sqlMatch = 'NOT LIKE';
+ $value = '%' . $this->escapeLikeString($value) . '%';
+ break;
+
case self::MATCH_ACTIONS_CONTAINS:
// this match type is not accessible from the outside
// (it won't be matched in self::parseSubExpressions())
// it can be used internally to inject sub-expressions into the query.
// see Piwik_Segment::getCleanedExpression()
- $sqlMatch = 'IN ('.$value['SQL'].')';
+ $sqlMatch = 'IN (' . $value['SQL'] . ')';
$value = $this->escapeLikeString($value['bind']);
break;
- default:
- throw new Exception("Filter contains the match type '".$matchType."' which is not supported");
- break;
+ default:
+ throw new Exception("Filter contains the match type '" . $matchType . "' which is not supported");
+ break;
}
-
+
if ($matchType === self::MATCH_ACTIONS_CONTAINS) {
$sqlExpression = "$field $sqlMatch";
} else {
$sqlExpression = "$field $sqlMatch ?";
}
-
+
$this->checkFieldIsAvailable($field, $availableTables);
-
+
return array($sqlExpression, $value);
}
- /**
- * Check whether the field is available
- * If not, add it to the available tables
- *
- * @param string $field
- * @param array $availableTables
- */
+ /**
+ * Check whether the field is available
+ * If not, add it to the available tables
+ *
+ * @param string $field
+ * @param array $availableTables
+ */
private function checkFieldIsAvailable($field, &$availableTables)
{
$fieldParts = explode('.', $field);
-
+
$table = count($fieldParts) == 2 ? $fieldParts[0] : false;
-
+
// remove sql functions from field name
// example: `HOUR(log_visit.visit_last_action_time)` gets `HOUR(log_visit` => remove `HOUR(`
$table = preg_replace('/^[A-Z_]+\(/', '', $table);
$tableExists = !$table || in_array($table, $availableTables);
-
- if (!$tableExists)
- {
- $availableTables[] = $table;
+
+ if (!$tableExists) {
+ $availableTables[] = $table;
}
}
- /**
- * Escape the characters % and _ in the given string
- * @param string $str
- * @return string
- */
+ /**
+ * Escape the characters % and _ in the given string
+ * @param string $str
+ * @return string
+ */
private function escapeLikeString($str)
{
- $str = str_replace("%", "\%", $str);
- $str = str_replace("_", "\_", $str);
- return $str;
+ $str = str_replace("%", "\%", $str);
+ $str = str_replace("_", "\_", $str);
+ return $str;
}
-
+
/**
- * Given a filter string,
- * will parse it into an array where each row contains the boolean operator applied to it,
+ * Given a filter string,
+ * will parse it into an array where each row contains the boolean operator applied to it,
* and the operand
*
* @return array
@@ -253,7 +249,7 @@ class Piwik_SegmentExpression
protected function parseTree()
{
$string = $this->string;
- if(empty($string)) {
+ if (empty($string)) {
return array();
}
$tree = array();
@@ -261,101 +257,87 @@ class Piwik_SegmentExpression
$length = strlen($string);
$isBackslash = false;
$operand = '';
- while($i <= $length)
- {
+ while ($i <= $length) {
$char = $string[$i];
$isAND = ($char == self::AND_DELIMITER);
$isOR = ($char == self::OR_DELIMITER);
- $isEnd = ($length == $i+1);
-
- if($isEnd)
- {
- if($isBackslash && ($isAND || $isOR))
- {
- $operand = substr($operand, 0, -1);
- }
+ $isEnd = ($length == $i + 1);
+
+ if ($isEnd) {
+ if ($isBackslash && ($isAND || $isOR)) {
+ $operand = substr($operand, 0, -1);
+ }
$operand .= $char;
$tree[] = array(self::INDEX_BOOL_OPERATOR => '', self::INDEX_OPERAND => $operand);
break;
}
-
- if($isAND && !$isBackslash)
- {
- $tree[] = array(self::INDEX_BOOL_OPERATOR => 'AND', self::INDEX_OPERAND => $operand);
- $operand = '';
- }
- elseif($isOR && !$isBackslash)
- {
- $tree[] = array(self::INDEX_BOOL_OPERATOR => 'OR', self::INDEX_OPERAND => $operand);
- $operand = '';
- }
- else
- {
- if($isBackslash && ($isAND || $isOR))
- {
- $operand = substr($operand, 0, -1);
- }
- $operand .= $char;
- }
+
+ if ($isAND && !$isBackslash) {
+ $tree[] = array(self::INDEX_BOOL_OPERATOR => 'AND', self::INDEX_OPERAND => $operand);
+ $operand = '';
+ } elseif ($isOR && !$isBackslash) {
+ $tree[] = array(self::INDEX_BOOL_OPERATOR => 'OR', self::INDEX_OPERAND => $operand);
+ $operand = '';
+ } else {
+ if ($isBackslash && ($isAND || $isOR)) {
+ $operand = substr($operand, 0, -1);
+ }
+ $operand .= $char;
+ }
$isBackslash = ($char == "\\");
$i++;
}
return $tree;
}
- /**
- * Given the array of parsed boolean logic, will return
- * an array containing the full SQL string representing the filter,
- * the needed joins and the values to bind to the query
- *
- * @throws Exception
- * @return array SQL Query, Joins and Bind parameters
- */
+ /**
+ * Given the array of parsed boolean logic, will return
+ * an array containing the full SQL string representing the filter,
+ * the needed joins and the values to bind to the query
+ *
+ * @throws Exception
+ * @return array SQL Query, Joins and Bind parameters
+ */
public function getSql()
{
- if(count($this->tree) == 0)
- {
+ if (count($this->tree) == 0) {
throw new Exception("Invalid segment, please specify a valid segment.");
}
$bind = array();
$sql = '';
$subExpression = false;
- foreach($this->tree as $expression)
- {
+ foreach ($this->tree as $expression) {
$operator = $expression[self::INDEX_BOOL_OPERATOR];
$operand = $expression[self::INDEX_OPERAND];
-
- if($operator == 'OR'
- && !$subExpression)
- {
+
+ if ($operator == 'OR'
+ && !$subExpression
+ ) {
$sql .= ' (';
$subExpression = true;
- }
- else
- {
+ } else {
$sql .= ' ';
}
-
+
$sql .= $operand;
-
- if($operator == 'AND'
- && $subExpression)
- {
+
+ if ($operator == 'AND'
+ && $subExpression
+ ) {
$sql .= ')';
$subExpression = false;
}
-
+
$sql .= " $operator";
}
- if($subExpression)
- {
+ if ($subExpression) {
$sql .= ')';
}
return array(
- 'where' => $sql,
- 'bind' => $this->valuesBind,
- 'join' => implode(' ', $this->joins)
+ 'where' => $sql,
+ 'bind' => $this->valuesBind,
+ 'join' => implode(' ', $this->joins)
);
}
}