2 /***************************************************************
5 * (c) 2009-2011 Oliver Hader <oliver@typo3.org>
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
28 * Matching TypoScript conditions
30 * Used with the TypoScript parser.
31 * Matches browserinfo, IPnumbers for use with templates
33 * @author Oliver Hader <oliver@typo3.org>
37 abstract class t3lib_matchCondition_abstract
{
39 * Id of the current page.
44 * The rootline for the current page.
50 * Whether to simulate the behaviour and match all conditions
51 * (used in TypoScript object browser).
54 protected $simulateMatchResult = FALSE;
56 * Whether to simulat the behaviour and match specific conditions
57 * (used in TypoScript object browser).
60 protected $simulateMatchConditions = array();
63 * Sets the id of the page to evaluate conditions for.
65 * @param integer $pageId Id of the page (must be positive)
68 public function setPageId($pageId) {
69 if (is_integer($pageId) && $pageId > 0) {
70 $this->pageId
= $pageId;
75 * Gets the id of the page to evaluate conditions for.
77 * @return integer Id of the page
79 public function getPageId() {
86 * @param array $rootline The rootline to be used for matching (must have elements)
89 public function setRootline(array $rootline) {
90 if (count($rootline)) {
91 $this->rootline
= $rootline;
98 * @return array The rootline to be used for matching
100 public function getRootline() {
101 return $this->rootline
;
105 * Sets whether to simulate the behaviour and match all conditions.
107 * @param boolean $simulateMatchResult Whether to simulate positive matches
110 public function setSimulateMatchResult($simulateMatchResult) {
111 if (is_bool($simulateMatchResult)) {
112 $this->simulateMatchResult
= $simulateMatchResult;
117 * Sets whether to simulate the behaviour and match specific conditions.
119 * @param array $simulateMatchConditions Conditions to simulate a match for
122 public function setSimulateMatchConditions(array $simulateMatchConditions) {
123 $this->simulateMatchConditions
= $simulateMatchConditions;
127 * Normalizes an expression and removes the first and last square bracket.
128 * + OR normalization: "...]OR[...", "...]||[...", "...][..." --> "...]||[..."
129 * + AND normalization: "...]AND[...", "...]&&[..." --> "...]&&[..."
131 * @param string $expression The expression to be normalized (e.g. "[A] && [B] OR [C]")
132 * @return string The normalized expression (e.g. "[A]&&[B]||[C]")
134 protected function normalizeExpression($expression) {
135 $normalizedExpression = preg_replace(
137 '/\]\s*(OR|\|\|)?\s*\[/i',
138 '/\]\s*(AND|&&)\s*\[/i',
147 return $normalizedExpression;
151 * Matches a TypoScript condition expression.
153 * @param string $expression The expression to match
154 * @return boolean Whether the expression matched
156 public function match($expression) {
157 // Return directly if result should be simulated:
158 if ($this->simulateMatchResult
) {
159 return $this->simulateMatchResult
;
161 // Return directly if matching for specific condition is simulated only:
162 if (count($this->simulateMatchConditions
)) {
163 return in_array($expression, $this->simulateMatchConditions
);
165 // Sets the current pageId if not defined yet:
166 if (!isset($this->pageId
)) {
167 $this->pageId
= $this->determinePageId();
169 // Sets the rootline if not defined yet:
170 if (!isset($this->rootline
)) {
171 $this->rootline
= $this->determineRootline();
175 $normalizedExpression = $this->normalizeExpression($expression);
177 // First and last character must be square brackets (e.g. "[A]&&[B]":
178 if (substr($normalizedExpression, 0, 1) === '[' && substr($normalizedExpression, -1) === ']') {
179 $innerExpression = substr($normalizedExpression, 1, -1);
181 $orParts = explode(']||[', $innerExpression);
182 foreach ($orParts as $orPart) {
183 $andParts = explode(']&&[', $orPart);
184 foreach ($andParts as $andPart) {
185 $result = $this->evaluateCondition($andPart);
186 // If condition in AND context fails, the whole block is FALSE:
187 if ($result === FALSE) {
191 // If condition in OR context succeeds, the whole expression is TRUE:
192 if ($result === TRUE) {
202 * Evaluates a TypoScript condition given as input, eg. "[browser=net][...(other conditions)...]"
204 * @param string $key The condition to match against its criterias.
205 * @param string $value
206 * @return mixed Returns TRUE or FALSE based on the evaluation
208 protected function evaluateConditionCommon($key, $value) {
209 if (t3lib_div
::inList('browser,version,system,useragent', strtolower($key))) {
210 $browserInfo = $this->getBrowserInfo(t3lib_div
::getIndpEnv('HTTP_USER_AGENT'));
212 $keyParts = t3lib_div
::trimExplode('|', $key);
214 switch ($keyParts[0]) {
216 $values = t3lib_div
::trimExplode(',', $value, TRUE);
217 // take all identified browsers into account, eg chrome deliver
218 // webkit=>532.5, chrome=>4.1, safari=>532.5
219 // so comparing string will be
220 // "webkit532.5 chrome4.1 safari532.5"
222 foreach ($browserInfo['all'] as $key => $value) {
223 $all .= $key . $value . ' ';
225 foreach ($values as $test) {
226 if (stripos($all, $test) !== FALSE) {
232 $values = t3lib_div
::trimExplode(',', $value, TRUE);
233 foreach ($values as $test) {
234 if (strcspn($test, '=<>') == 0) {
235 switch (substr($test, 0, 1)) {
237 if (doubleval(substr($test, 1)) == $browserInfo['version']) {
242 if (doubleval(substr($test, 1)) > $browserInfo['version']) {
247 if (doubleval(substr($test, 1)) < $browserInfo['version']) {
252 } elseif (strpos(' ' . $browserInfo['version'], $test) == 1) {
258 $values = t3lib_div
::trimExplode(',', $value, TRUE);
259 // Take all identified systems into account, e.g. mac for iOS, Linux
260 // for android and Windows NT for Windows XP
261 $allSystems .= ' ' . implode(' ', $browserInfo['all_systems']);
262 foreach ($values as $test) {
263 if (stripos($allSystems, $test) !== FALSE) {
269 if (!isset($this->deviceInfo
)) {
270 $this->deviceInfo
= $this->getDeviceType(t3lib_div
::getIndpEnv('HTTP_USER_AGENT'));
272 $values = t3lib_div
::trimExplode(',', $value, TRUE);
273 foreach ($values as $test) {
274 if ($this->deviceInfo
== $test) {
280 $test = trim($value);
282 return $this->searchStringWildcard($browserInfo['useragent'], $test);
286 $values = t3lib_div
::trimExplode(',', $value, TRUE);
287 foreach ($values as $test) {
288 if (preg_match('/^\*.+\*$/', $test)) {
289 $allLanguages = preg_split('/[,;]/', t3lib_div
::getIndpEnv('HTTP_ACCEPT_LANGUAGE'));
290 if (in_array(substr($test, 1, -1), $allLanguages)) {
293 } elseif (t3lib_div
::getIndpEnv('HTTP_ACCEPT_LANGUAGE') == $test) {
299 if (t3lib_div
::cmpIP(t3lib_div
::getIndpEnv('REMOTE_ADDR'), $value)) {
304 if (t3lib_div
::cmpFQDN(t3lib_div
::getIndpEnv('REMOTE_ADDR'), $value)) {
308 // hour, minute, dayofweek, dayofmonth, month, year, julianday
316 // In order to simulate time properly in templates.
317 $theEvalTime = $GLOBALS['SIM_EXEC_TIME'];
320 $theTestValue = date('H', $theEvalTime);
323 $theTestValue = date('i', $theEvalTime);
326 $theTestValue = date('m', $theEvalTime);
329 $theTestValue = date('Y', $theEvalTime);
332 $theTestValue = date('w', $theEvalTime);
335 $theTestValue = date('d', $theEvalTime);
338 $theTestValue = date('z', $theEvalTime);
341 $theTestValue = intval($theTestValue);
343 $values = t3lib_div
::trimExplode(',', $value, TRUE);
344 foreach ($values as $test) {
345 if (t3lib_utility_Math
::canBeInterpretedAsInteger($test)) {
348 if ($this->compareNumber($test, $theTestValue)) {
353 case 'compatVersion':
354 return t3lib_div
::compat_version($value);
357 if ($this->isUserLoggedIn()) {
358 $values = t3lib_div
::trimExplode(',', $value, TRUE);
359 foreach ($values as $test) {
360 if ($test == '*' ||
!strcmp($this->getUserId(), $test)) {
364 } elseif ($value === '') {
370 $page = $this->getPage();
371 $property = $keyParts[1];
372 if (!empty($page) && isset($page[$property])) {
373 if (strcmp($page[$property], $value) === 0) {
380 $values = t3lib_div
::trimExplode(',', $value, TRUE);
381 foreach ($values as $test) {
382 $point = strcspn($test, '!=<>');
383 $theVarName = substr($test, 0, $point);
384 $nv = $this->getVariable(trim($theVarName));
385 $testValue = substr($test, $point);
387 if ($this->compareNumber($testValue, $nv)) {
393 $values = t3lib_div
::trimExplode(',', $value, TRUE);
394 foreach ($values as $test) {
395 $point = strcspn($test, '=');
396 $theVarName = substr($test, 0, $point);
397 $nv = $this->getVariable(trim($theVarName));
398 $testValue = substr($test, $point +
1);
400 if ($this->searchStringWildcard($nv, trim($testValue))) {
406 $values = preg_split('/\(|\)/', $value);
407 $funcName = trim($values[0]);
408 $funcValue = t3lib_div
::trimExplode(',', $values[1]);
409 if (function_exists($funcName) && call_user_func($funcName, $funcValue[0])) {
419 * Get variable common
422 * @return mixed Whatever value. If none, then NULL.
424 protected function getVariableCommon(array $vars) {
427 if (count($vars) == 1) {
428 $value = $this->getGlobal($vars[0]);
430 $splitAgain = explode('|', $vars[1], 2);
431 $k = trim($splitAgain[0]);
434 switch ((string) trim($vars[0])) {
436 $value = t3lib_div
::_GP($k);
442 $value = t3lib_div
::getIndpEnv($k);
444 // return literal value:
446 return trim($vars[1]);
452 if (count($splitAgain) > 1) {
453 if (is_array($value) && trim($splitAgain[1])) {
454 $value = $this->getGlobal($splitAgain[1], $value);
466 * Evaluates a $leftValue based on an operator: "<", ">", "<=", ">=", "!=" or "="
468 * @param string $test The value to compare with on the form [operator][number]. Eg. "< 123"
469 * @param integer $leftValue The value on the left side
470 * @return boolean If $value is "50" and $test is "< 123" then it will return TRUE.
472 protected function compareNumber($test, $leftValue) {
473 if (preg_match('/^(!?=+|<=?|>=?)\s*([^\s]*)\s*$/', $test, $matches)) {
474 $operator = $matches[1];
475 $rightValue = $matches[2];
479 return ($leftValue >= doubleval($rightValue));
482 return ($leftValue <= doubleval($rightValue));
485 // multiple values may be split with '|'
486 // see if none matches ("not in list")
489 $rightValueParts = t3lib_div
::trimExplode('|', $rightValue);
490 foreach ($rightValueParts as $rightValueSingle) {
491 if ($leftValue === doubleval($rightValueSingle)) {
497 return ($found === FALSE);
500 return ($leftValue < doubleval($rightValue));
503 return ($leftValue > doubleval($rightValue));
506 // nothing valid found except '=', use '='
508 // multiple values may be split with '|'
509 // see if one matches ("in list")
512 $rightValueParts = t3lib_div
::trimExplode('|', $rightValue);
513 foreach ($rightValueParts as $rightValueSingle) {
514 if ($leftValue == $rightValueSingle) {
529 * Matching two strings against each other, supporting a "*" wildcard or (if wrapped in "/") PCRE regular expressions
531 * @param string $haystack The string in which to find $needle.
532 * @param string $needle The string to find in $haystack
533 * @return boolean Returns TRUE if $needle matches or is found in (according to wildcards) in $haystack. Eg. if $haystack is "Netscape 6.5" and $needle is "Net*" or "Net*ape" then it returns TRUE.
535 protected function searchStringWildcard($haystack, $needle) {
539 if (preg_match('/^\/.+\/$/', $needle)) {
540 // Regular expression, only "//" is allowed as delimiter
543 $needle = str_replace(array('*', '?'), array('###MANY###', '###ONE###'), $needle);
544 $regex = '/^' . preg_quote($needle, '/') . '$/';
545 // Replace the marker with .* to match anything (wildcard)
546 $regex = str_replace(array('###MANY###', '###ONE###'), array('.*', '.'), $regex);
549 $result = (boolean
) preg_match($regex, (string) $haystack);
556 * Generates an array with abstracted browser information
558 * @param string $userAgent The useragent string, t3lib_div::getIndpEnv('HTTP_USER_AGENT')
559 * @return array Contains keys "browser", "version", "system"
561 protected function getBrowserInfo($userAgent) {
562 return t3lib_utility_Client
::getBrowserInfo($userAgent);
566 * Gets a code for a browsing device based on the input useragent string.
568 * @param string $userAgent The useragent string, t3lib_div::getIndpEnv('HTTP_USER_AGENT')
569 * @return string Code for the specific device type
571 protected function getDeviceType($userAgent) {
572 return t3lib_utility_Client
::getDeviceType($userAgent);
576 * Return global variable where the input string $var defines array keys separated by "|"
577 * Example: $var = "HTTP_SERVER_VARS | something" will return the value $GLOBALS['HTTP_SERVER_VARS']['something'] value
579 * @param string $var Global var key, eg. "HTTP_GET_VAR" or "HTTP_GET_VARS|id" to get the GET parameter "id" back.
580 * @param array $source Alternative array than $GLOBAL to get variables from.
581 * @return mixed Whatever value. If none, then blank string.
583 protected function getGlobal($var, $source = NULL) {
584 $vars = explode('|', $var);
587 $theVar = isset($source) ?
$source[$k] : $GLOBALS[$k];
589 for ($a = 1; $a < $c; $a++
) {
590 if (!isset($theVar)) {
594 $key = trim($vars[$a]);
595 if (is_object($theVar)) {
596 $theVar = $theVar->$key;
597 } elseif (is_array($theVar)) {
598 $theVar = $theVar[$key];
604 if (!is_array($theVar) && !is_object($theVar)) {
612 * Evaluates a TypoScript condition given as input, eg. "[browser=net][...(other conditions)...]"
614 * @param string $string The condition to match against its criterias.
615 * @return boolean Whether the condition matched
616 * @see t3lib_tsparser::parse()
618 abstract protected function evaluateCondition($string);
621 * Gets the value of a variable.
625 * + GP:firstLevel|secondLevel
626 * + _GET|firstLevel|secondLevel
627 * + LIT:someLiteralValue
629 * @param string $name The name of the variable to fetch the value from
630 * @return mixed The value of the given variable (string) or NULL if variable did not exist
632 abstract protected function getVariable($name);
635 * Gets the usergroup list of the current user.
637 * @return string The usergroup list of the current user
639 abstract protected function getGroupList();
642 * Determines the current page Id.
644 * @return integer The current page Id
646 abstract protected function determinePageId();
649 * Gets the properties for the current page.
651 * @return array The properties for the current page.
653 abstract protected function getPage();
656 * Determines the rootline for the current page.
658 * @return array The rootline for the current page.
660 abstract protected function determineRootline();
663 * Gets the id of the current user.
665 * @return integer The id of the current user
667 abstract protected function getUserId();
670 * Determines if a user is logged in.
672 * @return boolean Determines if a user is logged in
674 abstract protected function isUserLoggedIn();
677 * Sets a log message.
679 * @param string $message The log message to set/write
682 abstract protected function log($message);