2 /***************************************************************
5 * (c) 2009 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
32 * Used with the TypoScript parser.
33 * Matches browserinfo, IPnumbers for use with templates
35 * @author Oliver Hader <oliver@typo3.org>
39 abstract class t3lib_matchCondition_abstract
{
41 * Id of the current page.
46 * The rootline for the current page.
52 * Whether to simulate the behaviour and match all conditions
53 * (used in TypoScript object browser).
56 protected $simulateMatchResult = false;
58 * Whether to simulat the behaviour and match specific conditions
59 * (used in TypoScript object browser).
62 protected $simulateMatchConditions = array();
65 * Sets the id of the page to evaluate conditions for.
67 * @param integer $pageId: Id of the page (must be positive)
70 public function setPageId($pageId) {
71 if (is_integer($pageId) && $pageId > 0) {
72 $this->pageId
= $pageId;
77 * Gets the id of the page to evaluate conditions for.
79 * @return integer Id of the page
81 public function getPageId() {
88 * @param array $rootline: The rootline to be used for matching (must have elements)
91 public function setRootline(array $rootline) {
92 if (count($rootline)) {
93 $this->rootline
= $rootline;
100 * @return array The rootline to be used for matching
102 public function getRootline() {
103 return $this->rootline
;
107 * Sets whether to simulate the behaviour and match all conditions.
109 * @param boolean $simulateMatchResult: Whether to simulate positive matches
112 public function setSimulateMatchResult($simulateMatchResult) {
113 if (is_bool($simulateMatchResult)) {
114 $this->simulateMatchResult
= $simulateMatchResult;
119 * Sets whether to simulate the behaviour and match specific conditions.
121 * @param array $simulateMatchConditions: Conditions to simulate a match for
124 public function setSimulateMatchConditions(array $simulateMatchConditions) {
125 $this->simulateMatchConditions
= $simulateMatchConditions;
129 * Normalizes an expression and removes the first and last square bracket.
130 * + OR normalization: "...]OR[...", "...]||[...", "...][..." --> "...]||[..."
131 * + AND normalization: "...]AND[...", "...]&&[..." --> "...]&&[..."
133 * @param string $expression: The expression to be normalized (e.g. "[A] && [B] OR [C]")
134 * @return string The normalized expression (e.g. "[A]&&[B]||[C]")
136 protected function normalizeExpression($expression) {
137 $normalizedExpression = preg_replace(
139 '/\]\s*(OR|\|\|)?\s*\[/i',
140 '/\]\s*(AND|&&)\s*\[/i',
149 return $normalizedExpression;
153 * Matches a TypoScript condition expression.
155 * @param string $expression: The expression to match
156 * @return boolean Whether the expression matched
158 public function match($expression) {
159 // Return directly if result should be simulated:
160 if ($this->simulateMatchResult
) {
161 return $this->simulateMatchResult
;
163 // Return directly if matching for specific condition is simulated only:
164 if (count($this->simulateMatchConditions
)) {
165 return in_array($expression, $this->simulateMatchConditions
);
167 // Sets the current pageId if not defined yet:
168 if (!isset($this->pageId
)) {
169 $this->pageId
= $this->determinePageId();
171 // Sets the rootline if not defined yet:
172 if (!isset($this->rootline
)) {
173 $this->rootline
= $this->determineRootline();
177 $normalizedExpression = $this->normalizeExpression($expression);
179 // First and last character must be square brackets (e.g. "[A]&&[B]":
180 if (substr($normalizedExpression, 0, 1) === '[' && substr($normalizedExpression, -1) === ']') {
181 $innerExpression = substr($normalizedExpression, 1, -1);
183 $orParts = explode(']||[', $innerExpression);
184 foreach ($orParts as $orPart) {
185 $andParts = explode(']&&[', $orPart);
186 foreach ($andParts as $andPart) {
187 $result = $this->evaluateCondition($andPart);
188 // If condition in AND context fails, the whole block is false:
189 if ($result === false) {
193 // If condition in OR context succeeds, the whole expression is true:
194 if ($result === true) {
204 * Evaluates a TypoScript condition given as input, eg. "[browser=net][...(other conditions)...]"
206 * @param string The condition to match against its criterias.
207 * @return mixed Returns true or false based on the evaluation
209 protected function evaluateConditionCommon($key, $value) {
210 if (t3lib_div
::inList('browser,version,system,useragent', strtolower($key))) {
211 $browserInfo = $this->getBrowserInfo(t3lib_div
::getIndpEnv('HTTP_USER_AGENT'));
216 $values = t3lib_div
::trimExplode(',', $value, true);
217 foreach ($values as $test) {
218 if (strpos($browserInfo['browser'] . $browserInfo['version'], $test) !== false) {
224 $values = t3lib_div
::trimExplode(',', $value, true);
225 foreach ($values as $test) {
226 if (strcspn($test, '=<>') == 0) {
227 switch (substr($test, 0, 1)) {
229 if (doubleval(substr($test, 1)) == $browserInfo['version']) {
234 if (doubleval(substr($test, 1)) > $browserInfo['version']) {
239 if (doubleval(substr($test, 1)) < $browserInfo['version']) {
245 if (strpos(' ' . $browserInfo['version'], $test) == 1) {
252 $values = t3lib_div
::trimExplode(',', $value, true);
253 foreach ($values as $test) {
254 if (strpos(' ' . $browserInfo['system'], $test) == 1) {
260 if (!isset($this->deviceInfo
)) {
261 $this->deviceInfo
= $this->getDeviceType(t3lib_div
::getIndpEnv('HTTP_USER_AGENT'));
263 $values = t3lib_div
::trimExplode(',', $value, true);
264 foreach ($values as $test) {
265 if ($this->deviceInfo
== $test) {
271 $test = trim($value);
273 return $this->searchStringWildcard($browserInfo['useragent'], $test);
277 $values = t3lib_div
::trimExplode(',', $value, true);
278 foreach ($values as $test) {
279 if (preg_match('/^\*.+\*$/', $test)) {
280 $allLanguages = preg_split('/[,;]/', t3lib_div
::getIndpEnv('HTTP_ACCEPT_LANGUAGE'));
281 if (in_array(substr($test, 1, -1), $allLanguages)) {
284 } else if (t3lib_div
::getIndpEnv('HTTP_ACCEPT_LANGUAGE') == $test) {
290 if (t3lib_div
::cmpIP(t3lib_div
::getIndpEnv('REMOTE_ADDR'), $value)) {
295 if (t3lib_div
::cmpFQDN(t3lib_div
::getIndpEnv('REMOTE_ADDR'), $value)) {
299 // hour, minute, dayofweek, dayofmonth, month, year, julianday
307 $theEvalTime = $GLOBALS['SIM_EXEC_TIME']; // In order to simulate time properly in templates.
309 case 'hour': $theTestValue = date('H', $theEvalTime); break;
310 case 'minute': $theTestValue = date('i', $theEvalTime); break;
311 case 'month': $theTestValue = date('m', $theEvalTime); break;
312 case 'year': $theTestValue = date('Y', $theEvalTime); break;
313 case 'dayofweek': $theTestValue = date('w', $theEvalTime); break;
314 case 'dayofmonth': $theTestValue = date('d', $theEvalTime); break;
315 case 'dayofyear': $theTestValue = date('z', $theEvalTime); break;
317 $theTestValue = intval($theTestValue);
319 $values = t3lib_div
::trimExplode(',', $value, true);
320 foreach ($values as $test) {
321 if (t3lib_div
::testInt($test)) {
324 if ($this->compareNumber($test, $theTestValue)) {
329 case 'compatVersion':
330 return t3lib_div
::compat_version($value);
333 if ($this->isUserLoggedIn()) {
334 $values = t3lib_div
::trimExplode(',', $value, true);
335 foreach ($values as $test) {
336 if ($test == '*' ||
!strcmp($this->getUserId(), $test)) {
343 $values = t3lib_div
::trimExplode(',', $value, true);
344 foreach ($values as $test) {
345 $point = strcspn($test, '!=<>');
346 $theVarName = substr($test, 0, $point);
347 $nv = $this->getVariable(trim($theVarName));
348 $testValue = substr($test, $point);
350 if ($this->compareNumber($testValue, $nv)) {
356 $values = t3lib_div
::trimExplode(',', $value, true);
357 foreach ($values as $test) {
358 $point = strcspn($test, '=');
359 $theVarName = substr($test, 0, $point);
360 $nv = $this->getVariable(trim($theVarName));
361 $testValue = substr($test, $point+
1);
363 if ($this->searchStringWildcard($nv, trim($testValue))) {
369 $values = preg_split('/\(|\)/', $value);
370 $funcName = trim($values[0]);
371 $funcValue = t3lib_div
::trimExplode(',', $values[1]);
372 $prefix = $this->getUserFuncClassPrefix();
374 !t3lib_div
::isFirstPartOfStr(trim($funcName), $prefix) &&
375 !t3lib_div
::isFirstPartOfStr(trim($funcName),'tx_')
377 $this->log('Match condition: Function "' . $funcName . '" was not prepended with "' . $prefix.'"');
380 if (function_exists($funcName) && call_user_func($funcName, $funcValue[0])) {
389 protected function getVariableCommon(array $vars) {
392 if (count($vars) == 1) {
393 $value = $this->getGlobal($vars[0]);
395 $splitAgain = explode('|',$vars[1], 2);
396 $k = trim($splitAgain[0]);
399 switch((string)trim($vars[0])) {
401 $value = t3lib_div
::_GP($k);
407 $value = t3lib_div
::getIndpEnv($k);
409 // return litteral value:
411 return trim($vars[1]);
417 if (count($splitAgain) > 1) {
418 if (is_array($value) && trim($splitAgain[1])) {
419 $value = $this->getGlobal($splitAgain[1], $value);
431 * Evaluates a $leftValue based on an operator: "<", ">", "<=", ">=", "!=" or "="
433 * @param string $test: The value to compare with on the form [operator][number]. Eg. "< 123"
434 * @param integer $leftValue: The value on the left side
435 * @return boolean If $value is "50" and $test is "< 123" then it will return true.
437 protected function compareNumber($test, $leftValue) {
438 if (preg_match('/^(!?=+|<=?|>=?)\s*([^\s]*)\s*$/', $test, $matches)) {
439 $operator = $matches[1];
440 $rightValue = $matches[2];
444 return ($leftValue >= doubleval($rightValue));
447 return ($leftValue <= doubleval($rightValue));
450 return ($leftValue != doubleval($rightValue));
453 return ($leftValue < doubleval($rightValue));
456 return ($leftValue > doubleval($rightValue));
459 // nothing valid found except '=', use '='
460 return ($leftValue == trim($rightValue));
469 * Matching two strings against each other, supporting a "*" wildcard or (if wrapped in "/") PCRE regular expressions
471 * @param string The string in which to find $needle.
472 * @param string The string to find in $haystack
473 * @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.
475 protected function searchStringWildcard($haystack, $needle) {
479 if (preg_match('/^\/.+\/$/', $needle)) {
480 // Regular expression, only "//" is allowed as delimiter
483 $needle = str_replace(array('*', '?'), array('###MANY###', '###ONE###'), $needle);
484 $regex = '/^' . preg_quote($needle, '/') . '$/';
485 // Replace the marker with .* to match anything (wildcard)
486 $regex = str_replace(array('###MANY###', '###ONE###'), array('.*' , '.'), $regex);
489 $result = (boolean
)preg_match($regex, (string)$haystack);
496 * Generates an array with abstracted browser information
498 * @param string $userAgent: The useragent string, t3lib_div::getIndpEnv('HTTP_USER_AGENT')
499 * @return array Contains keys "browser", "version", "system"
501 protected function getBrowserInfo($userAgent) {
502 return t3lib_utility_Client
::getBrowserInfo($userAgent);
506 * Gets a code for a browsing device based on the input useragent string.
508 * @param string $userAgent: The useragent string, t3lib_div::getIndpEnv('HTTP_USER_AGENT')
509 * @return string Code for the specific device type
511 protected function getDeviceType($userAgent) {
512 return t3lib_utility_Client
::getDeviceType($userAgent);
516 * Return global variable where the input string $var defines array keys separated by "|"
517 * Example: $var = "HTTP_SERVER_VARS | something" will return the value $GLOBALS['HTTP_SERVER_VARS']['something'] value
519 * @param string Global var key, eg. "HTTP_GET_VAR" or "HTTP_GET_VARS|id" to get the GET parameter "id" back.
520 * @param array Alternative array than $GLOBAL to get variables from.
521 * @return mixed Whatever value. If none, then blank string.
523 protected function getGlobal($var, $source=NULL) {
524 $vars = explode('|', $var);
527 $theVar = isset($source) ?
$source[$k] : $GLOBALS[$k];
529 for ($a=1;$a<$c;$a++
) {
530 if (!isset($theVar)) { break; }
532 $key = trim($vars[$a]);
533 if (is_object($theVar)) {
534 $theVar = $theVar->$key;
535 } elseif (is_array($theVar)) {
536 $theVar = $theVar[$key];
542 if (!is_array($theVar) && !is_object($theVar)) {
551 * Evaluates a TypoScript condition given as input, eg. "[browser=net][...(other conditions)...]"
553 * @param string $string: The condition to match against its criterias.
554 * @return boolean Whether the condition matched
555 * @see t3lib_tsparser::parse()
557 abstract protected function evaluateCondition($string);
560 * Gets the value of a variable.
564 * + GP:firstLevel|secondLevel
565 * + _GET|firstLevel|secondLevel
566 * + LIT:someLiteralValue
568 * @param string $name: The name of the variable to fetch the value from
569 * @return mixed The value of the given variable (string) or NULL if variable did not exist
571 abstract protected function getVariable($name);
574 * Gets the usergroup list of the current user.
576 * @return string The usergroup list of the current user
578 abstract protected function getGroupList();
581 * Determines the current page Id.
583 * @return integer The current page Id
585 abstract protected function determinePageId();
588 * Determines the rootline for the current page.
590 * @return array The rootline for the current page.
592 abstract protected function determineRootline();
595 * Gets prefix for user functions (normally 'user_').
597 * @return string The prefix for user functions (normally 'user_').
599 abstract protected function getUserFuncClassPrefix();
602 * Gets the id of the current user.
604 * @return integer The id of the current user
606 abstract protected function getUserId();
609 * Determines if a user is logged in.
611 * @return boolean Determines if a user is logged in
613 abstract protected function isUserLoggedIn();
616 * Sets a log message.
618 * @param string $message: The log message to set/write
621 abstract protected function log($message);