[TASK] Change "false" into "FALSE"
[Packages/TYPO3.CMS.git] / t3lib / matchcondition / class.t3lib_matchcondition_abstract.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009-2011 Oliver Hader <oliver@typo3.org>
6 * All rights reserved
7 *
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.
13 *
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.
18 *
19 *
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.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Matching TypoScript conditions
29 *
30 * Used with the TypoScript parser.
31 * Matches browserinfo, IPnumbers for use with templates
32 *
33 * @author Oliver Hader <oliver@typo3.org>
34 * @package TYPO3
35 * @subpackage t3lib
36 */
37 abstract class t3lib_matchCondition_abstract {
38 /**
39 * Id of the current page.
40 * @var integer
41 */
42 protected $pageId;
43 /**
44 * The rootline for the current page.
45 * @var array
46 */
47 protected $rootline;
48
49 /**
50 * Whether to simulate the behaviour and match all conditions
51 * (used in TypoScript object browser).
52 * @var boolean
53 */
54 protected $simulateMatchResult = FALSE;
55 /**
56 * Whether to simulat the behaviour and match specific conditions
57 * (used in TypoScript object browser).
58 * @var array
59 */
60 protected $simulateMatchConditions = array();
61
62 /**
63 * Sets the id of the page to evaluate conditions for.
64 *
65 * @param integer $pageId: Id of the page (must be positive)
66 * @return void
67 */
68 public function setPageId($pageId) {
69 if (is_integer($pageId) && $pageId > 0) {
70 $this->pageId = $pageId;
71 }
72 }
73
74 /**
75 * Gets the id of the page to evaluate conditions for.
76 *
77 * @return integer Id of the page
78 */
79 public function getPageId() {
80 return $this->pageId;
81 }
82
83 /**
84 * Sets the rootline.
85 *
86 * @param array $rootline: The rootline to be used for matching (must have elements)
87 * @return void
88 */
89 public function setRootline(array $rootline) {
90 if (count($rootline)) {
91 $this->rootline = $rootline;
92 }
93 }
94
95 /**
96 * Gets the rootline.
97 *
98 * @return array The rootline to be used for matching
99 */
100 public function getRootline() {
101 return $this->rootline;
102 }
103
104 /**
105 * Sets whether to simulate the behaviour and match all conditions.
106 *
107 * @param boolean $simulateMatchResult: Whether to simulate positive matches
108 * @return void
109 */
110 public function setSimulateMatchResult($simulateMatchResult) {
111 if (is_bool($simulateMatchResult)) {
112 $this->simulateMatchResult = $simulateMatchResult;
113 }
114 }
115
116 /**
117 * Sets whether to simulate the behaviour and match specific conditions.
118 *
119 * @param array $simulateMatchConditions: Conditions to simulate a match for
120 * @return void
121 */
122 public function setSimulateMatchConditions(array $simulateMatchConditions) {
123 $this->simulateMatchConditions = $simulateMatchConditions;
124 }
125
126 /**
127 * Normalizes an expression and removes the first and last square bracket.
128 * + OR normalization: "...]OR[...", "...]||[...", "...][..." --> "...]||[..."
129 * + AND normalization: "...]AND[...", "...]&&[..." --> "...]&&[..."
130 *
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]")
133 */
134 protected function normalizeExpression($expression) {
135 $normalizedExpression = preg_replace(
136 array(
137 '/\]\s*(OR|\|\|)?\s*\[/i',
138 '/\]\s*(AND|&&)\s*\[/i',
139 ),
140 array(
141 ']||[',
142 ']&&[',
143 ),
144 trim($expression)
145 );
146
147 return $normalizedExpression;
148 }
149
150 /**
151 * Matches a TypoScript condition expression.
152 *
153 * @param string $expression: The expression to match
154 * @return boolean Whether the expression matched
155 */
156 public function match($expression) {
157 // Return directly if result should be simulated:
158 if ($this->simulateMatchResult) {
159 return $this->simulateMatchResult;
160 }
161 // Return directly if matching for specific condition is simulated only:
162 if (count($this->simulateMatchConditions)) {
163 return in_array($expression, $this->simulateMatchConditions);
164 }
165 // Sets the current pageId if not defined yet:
166 if (!isset($this->pageId)) {
167 $this->pageId = $this->determinePageId();
168 }
169 // Sets the rootline if not defined yet:
170 if (!isset($this->rootline)) {
171 $this->rootline = $this->determineRootline();
172 }
173
174 $result = FALSE;
175 $normalizedExpression = $this->normalizeExpression($expression);
176
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);
180
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) {
188 break;
189 }
190 }
191 // If condition in OR context succeeds, the whole expression is TRUE:
192 if ($result === TRUE) {
193 break;
194 }
195 }
196 }
197
198 return $result;
199 }
200
201 /**
202 * Evaluates a TypoScript condition given as input, eg. "[browser=net][...(other conditions)...]"
203 *
204 * @param string The condition to match against its criterias.
205 * @return mixed Returns TRUE or FALSE based on the evaluation
206 */
207 protected function evaluateConditionCommon($key, $value) {
208 if (t3lib_div::inList('browser,version,system,useragent', strtolower($key))) {
209 $browserInfo = $this->getBrowserInfo(t3lib_div::getIndpEnv('HTTP_USER_AGENT'));
210 }
211 $keyParts = t3lib_div::trimExplode('|', $key);
212
213 switch ($keyParts[0]) {
214 case 'browser':
215 $values = t3lib_div::trimExplode(',', $value, TRUE);
216 // take all identified browsers into account, eg chrome deliver
217 // webkit=>532.5, chrome=>4.1, safari=>532.5
218 // so comparing string will be
219 // "webkit532.5 chrome4.1 safari532.5"
220 $all = '';
221 foreach ($browserInfo['all'] as $key => $value) {
222 $all .= $key . $value . ' ';
223 }
224 foreach ($values as $test) {
225 if (stripos($all, $test) !== FALSE) {
226 return TRUE;
227 }
228 }
229 break;
230 case 'version':
231 $values = t3lib_div::trimExplode(',', $value, TRUE);
232 foreach ($values as $test) {
233 if (strcspn($test, '=<>') == 0) {
234 switch (substr($test, 0, 1)) {
235 case '=':
236 if (doubleval(substr($test, 1)) == $browserInfo['version']) {
237 return TRUE;
238 }
239 break;
240 case '<':
241 if (doubleval(substr($test, 1)) > $browserInfo['version']) {
242 return TRUE;
243 }
244 break;
245 case '>':
246 if (doubleval(substr($test, 1)) < $browserInfo['version']) {
247 return TRUE;
248 }
249 break;
250 }
251 } elseif (strpos(' ' . $browserInfo['version'], $test) == 1) {
252 return TRUE;
253 }
254 }
255 break;
256 case 'system':
257 $values = t3lib_div::trimExplode(',', $value, TRUE);
258 // Take all identified systems into account, e.g. mac for iOS, Linux
259 // for android and Windows NT for Windows XP
260 $allSystems .= ' ' . implode(' ', $browserInfo['all_systems']);
261 foreach ($values as $test) {
262 if (stripos($allSystems, $test) !== FALSE) {
263 return TRUE;
264 }
265 }
266 break;
267 case 'device':
268 if (!isset($this->deviceInfo)) {
269 $this->deviceInfo = $this->getDeviceType(t3lib_div::getIndpEnv('HTTP_USER_AGENT'));
270 }
271 $values = t3lib_div::trimExplode(',', $value, TRUE);
272 foreach ($values as $test) {
273 if ($this->deviceInfo == $test) {
274 return TRUE;
275 }
276 }
277 break;
278 case 'useragent':
279 $test = trim($value);
280 if (strlen($test)) {
281 return $this->searchStringWildcard($browserInfo['useragent'], $test);
282 }
283 break;
284 case 'language':
285 $values = t3lib_div::trimExplode(',', $value, TRUE);
286 foreach ($values as $test) {
287 if (preg_match('/^\*.+\*$/', $test)) {
288 $allLanguages = preg_split('/[,;]/', t3lib_div::getIndpEnv('HTTP_ACCEPT_LANGUAGE'));
289 if (in_array(substr($test, 1, -1), $allLanguages)) {
290 return TRUE;
291 }
292 } elseif (t3lib_div::getIndpEnv('HTTP_ACCEPT_LANGUAGE') == $test) {
293 return TRUE;
294 }
295 }
296 break;
297 case 'IP':
298 if (t3lib_div::cmpIP(t3lib_div::getIndpEnv('REMOTE_ADDR'), $value)) {
299 return TRUE;
300 }
301 break;
302 case 'hostname':
303 if (t3lib_div::cmpFQDN(t3lib_div::getIndpEnv('REMOTE_ADDR'), $value)) {
304 return TRUE;
305 }
306 break;
307 // hour, minute, dayofweek, dayofmonth, month, year, julianday
308 case 'hour':
309 case 'minute':
310 case 'month':
311 case 'year':
312 case 'dayofweek':
313 case 'dayofmonth':
314 case 'dayofyear':
315 $theEvalTime = $GLOBALS['SIM_EXEC_TIME']; // In order to simulate time properly in templates.
316 switch ($key) {
317 case 'hour':
318 $theTestValue = date('H', $theEvalTime);
319 break;
320 case 'minute':
321 $theTestValue = date('i', $theEvalTime);
322 break;
323 case 'month':
324 $theTestValue = date('m', $theEvalTime);
325 break;
326 case 'year':
327 $theTestValue = date('Y', $theEvalTime);
328 break;
329 case 'dayofweek':
330 $theTestValue = date('w', $theEvalTime);
331 break;
332 case 'dayofmonth':
333 $theTestValue = date('d', $theEvalTime);
334 break;
335 case 'dayofyear':
336 $theTestValue = date('z', $theEvalTime);
337 break;
338 }
339 $theTestValue = intval($theTestValue);
340 // comp
341 $values = t3lib_div::trimExplode(',', $value, TRUE);
342 foreach ($values as $test) {
343 if (t3lib_div::testInt($test)) {
344 $test = '=' . $test;
345 }
346 if ($this->compareNumber($test, $theTestValue)) {
347 return TRUE;
348 }
349 }
350 break;
351 case 'compatVersion':
352 return t3lib_div::compat_version($value);
353 break;
354 case 'loginUser':
355 if ($this->isUserLoggedIn()) {
356 $values = t3lib_div::trimExplode(',', $value, TRUE);
357 foreach ($values as $test) {
358 if ($test == '*' || !strcmp($this->getUserId(), $test)) {
359 return TRUE;
360 }
361 }
362 } elseif ($value === '') {
363 return TRUE;
364 }
365 break;
366 case 'page':
367 if ($keyParts[1]) {
368 $page = $this->getPage();
369 $property = $keyParts[1];
370 if (!empty($page) && isset($page[$property])) {
371 if (strcmp($page[$property], $value) === 0) {
372 return TRUE;
373 }
374 }
375 }
376 break;
377 case 'globalVar':
378 $values = t3lib_div::trimExplode(',', $value, TRUE);
379 foreach ($values as $test) {
380 $point = strcspn($test, '!=<>');
381 $theVarName = substr($test, 0, $point);
382 $nv = $this->getVariable(trim($theVarName));
383 $testValue = substr($test, $point);
384
385 if ($this->compareNumber($testValue, $nv)) {
386 return TRUE;
387 }
388 }
389 break;
390 case 'globalString':
391 $values = t3lib_div::trimExplode(',', $value, TRUE);
392 foreach ($values as $test) {
393 $point = strcspn($test, '=');
394 $theVarName = substr($test, 0, $point);
395 $nv = $this->getVariable(trim($theVarName));
396 $testValue = substr($test, $point + 1);
397
398 if ($this->searchStringWildcard($nv, trim($testValue))) {
399 return TRUE;
400 }
401 }
402 break;
403 case 'userFunc':
404 $values = preg_split('/\(|\)/', $value);
405 $funcName = trim($values[0]);
406 $funcValue = t3lib_div::trimExplode(',', $values[1]);
407 $prefix = $this->getUserFuncClassPrefix();
408 if ($prefix &&
409 !t3lib_div::isFirstPartOfStr(trim($funcName), $prefix) &&
410 !t3lib_div::isFirstPartOfStr(trim($funcName), 'tx_')
411 ) {
412 $this->log('Match condition: Function "' . $funcName . '" was not prepended with "' . $prefix . '"');
413 return FALSE;
414 }
415 if (function_exists($funcName) && call_user_func($funcName, $funcValue[0])) {
416 return TRUE;
417 }
418 break;
419 }
420
421 return NULL;
422 }
423
424 protected function getVariableCommon(array $vars) {
425 $value = NULL;
426
427 if (count($vars) == 1) {
428 $value = $this->getGlobal($vars[0]);
429 } else {
430 $splitAgain = explode('|', $vars[1], 2);
431 $k = trim($splitAgain[0]);
432
433 if ($k) {
434 switch ((string) trim($vars[0])) {
435 case 'GP':
436 $value = t3lib_div::_GP($k);
437 break;
438 case 'ENV':
439 $value = getenv($k);
440 break;
441 case 'IENV':
442 $value = t3lib_div::getIndpEnv($k);
443 break;
444 // return litteral value:
445 case 'LIT':
446 return trim($vars[1]);
447 break;
448 default:
449 return NULL;
450 }
451 // If array:
452 if (count($splitAgain) > 1) {
453 if (is_array($value) && trim($splitAgain[1])) {
454 $value = $this->getGlobal($splitAgain[1], $value);
455 } else {
456 $value = '';
457 }
458 }
459 }
460 }
461
462 return $value;
463 }
464
465 /**
466 * Evaluates a $leftValue based on an operator: "<", ">", "<=", ">=", "!=" or "="
467 *
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.
471 */
472 protected function compareNumber($test, $leftValue) {
473 if (preg_match('/^(!?=+|<=?|>=?)\s*([^\s]*)\s*$/', $test, $matches)) {
474 $operator = $matches[1];
475 $rightValue = $matches[2];
476
477 switch ($operator) {
478 case '>=':
479 return ($leftValue >= doubleval($rightValue));
480 break;
481 case '<=':
482 return ($leftValue <= doubleval($rightValue));
483 break;
484 case '!=':
485 return ($leftValue != doubleval($rightValue));
486 break;
487 case '<':
488 return ($leftValue < doubleval($rightValue));
489 break;
490 case '>':
491 return ($leftValue > doubleval($rightValue));
492 break;
493 default:
494 // nothing valid found except '=', use '='
495 return ($leftValue == trim($rightValue));
496 break;
497 }
498 }
499
500 return FALSE;
501 }
502
503 /**
504 * Matching two strings against each other, supporting a "*" wildcard or (if wrapped in "/") PCRE regular expressions
505 *
506 * @param string The string in which to find $needle.
507 * @param string The string to find in $haystack
508 * @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.
509 */
510 protected function searchStringWildcard($haystack, $needle) {
511 $result = FALSE;
512
513 if ($needle) {
514 if (preg_match('/^\/.+\/$/', $needle)) {
515 // Regular expression, only "//" is allowed as delimiter
516 $regex = $needle;
517 } else {
518 $needle = str_replace(array('*', '?'), array('###MANY###', '###ONE###'), $needle);
519 $regex = '/^' . preg_quote($needle, '/') . '$/';
520 // Replace the marker with .* to match anything (wildcard)
521 $regex = str_replace(array('###MANY###', '###ONE###'), array('.*', '.'), $regex);
522 }
523
524 $result = (boolean) preg_match($regex, (string) $haystack);
525 }
526
527 return $result;
528 }
529
530 /**
531 * Generates an array with abstracted browser information
532 *
533 * @param string $userAgent: The useragent string, t3lib_div::getIndpEnv('HTTP_USER_AGENT')
534 * @return array Contains keys "browser", "version", "system"
535 */
536 protected function getBrowserInfo($userAgent) {
537 return t3lib_utility_Client::getBrowserInfo($userAgent);
538 }
539
540 /**
541 * Gets a code for a browsing device based on the input useragent string.
542 *
543 * @param string $userAgent: The useragent string, t3lib_div::getIndpEnv('HTTP_USER_AGENT')
544 * @return string Code for the specific device type
545 */
546 protected function getDeviceType($userAgent) {
547 return t3lib_utility_Client::getDeviceType($userAgent);
548 }
549
550 /**
551 * Return global variable where the input string $var defines array keys separated by "|"
552 * Example: $var = "HTTP_SERVER_VARS | something" will return the value $GLOBALS['HTTP_SERVER_VARS']['something'] value
553 *
554 * @param string Global var key, eg. "HTTP_GET_VAR" or "HTTP_GET_VARS|id" to get the GET parameter "id" back.
555 * @param array Alternative array than $GLOBAL to get variables from.
556 * @return mixed Whatever value. If none, then blank string.
557 */
558 protected function getGlobal($var, $source = NULL) {
559 $vars = explode('|', $var);
560 $c = count($vars);
561 $k = trim($vars[0]);
562 $theVar = isset($source) ? $source[$k] : $GLOBALS[$k];
563
564 for ($a = 1; $a < $c; $a++) {
565 if (!isset($theVar)) {
566 break;
567 }
568
569 $key = trim($vars[$a]);
570 if (is_object($theVar)) {
571 $theVar = $theVar->$key;
572 } elseif (is_array($theVar)) {
573 $theVar = $theVar[$key];
574 } else {
575 return '';
576 }
577 }
578
579 if (!is_array($theVar) && !is_object($theVar)) {
580 return $theVar;
581 } else {
582 return '';
583 }
584 }
585
586
587 /**
588 * Evaluates a TypoScript condition given as input, eg. "[browser=net][...(other conditions)...]"
589 *
590 * @param string $string: The condition to match against its criterias.
591 * @return boolean Whether the condition matched
592 * @see t3lib_tsparser::parse()
593 */
594 abstract protected function evaluateCondition($string);
595
596 /**
597 * Gets the value of a variable.
598 *
599 * Examples of names:
600 * + TSFE:id
601 * + GP:firstLevel|secondLevel
602 * + _GET|firstLevel|secondLevel
603 * + LIT:someLiteralValue
604 *
605 * @param string $name: The name of the variable to fetch the value from
606 * @return mixed The value of the given variable (string) or NULL if variable did not exist
607 */
608 abstract protected function getVariable($name);
609
610 /**
611 * Gets the usergroup list of the current user.
612 *
613 * @return string The usergroup list of the current user
614 */
615 abstract protected function getGroupList();
616
617 /**
618 * Determines the current page Id.
619 *
620 * @return integer The current page Id
621 */
622 abstract protected function determinePageId();
623
624 /**
625 * Gets the properties for the current page.
626 *
627 * @return array The properties for the current page.
628 */
629 abstract protected function getPage();
630
631 /**
632 * Determines the rootline for the current page.
633 *
634 * @return array The rootline for the current page.
635 */
636 abstract protected function determineRootline();
637
638 /**
639 * Gets prefix for user functions (normally 'user_').
640 *
641 * @return string The prefix for user functions (normally 'user_').
642 */
643 abstract protected function getUserFuncClassPrefix();
644
645 /**
646 * Gets the id of the current user.
647 *
648 * @return integer The id of the current user
649 */
650 abstract protected function getUserId();
651
652 /**
653 * Determines if a user is logged in.
654 *
655 * @return boolean Determines if a user is logged in
656 */
657 abstract protected function isUserLoggedIn();
658
659 /**
660 * Sets a log message.
661 *
662 * @param string $message: The log message to set/write
663 * @return void
664 */
665 abstract protected function log($message);
666 }
667
668 ?>