TYPO3 API  SVNRelease
class.t3lib_matchcondition_abstract.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003  *  Copyright notice
00004  *
00005  *  (c) 2009-2011 Oliver Hader <oliver@typo3.org>
00006  *  All rights reserved
00007  *
00008  *  This script is part of the TYPO3 project. The TYPO3 project is
00009  *  free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  The GNU General Public License can be found at
00015  *  http://www.gnu.org/copyleft/gpl.html.
00016  *  A copy is found in the textfile GPL.txt and important notices to the license
00017  *  from the author is found in LICENSE.txt distributed with these scripts.
00018  *
00019  *
00020  *  This script is distributed in the hope that it will be useful,
00021  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023  *  GNU General Public License for more details.
00024  *
00025  *  This copyright notice MUST APPEAR in all copies of the script!
00026  ***************************************************************/
00027 /**
00028  * Matching TypoScript conditions
00029  *
00030  * $Id: class.t3lib_matchcondition_abstract.php 10220 2011-01-21 18:08:15Z baschny $
00031  *
00032  * Used with the TypoScript parser.
00033  * Matches browserinfo, IPnumbers for use with templates
00034  *
00035  * @author  Oliver Hader <oliver@typo3.org>
00036  * @package TYPO3
00037  * @subpackage t3lib
00038  */
00039 abstract class t3lib_matchCondition_abstract {
00040     /**
00041      * Id of the current page.
00042      * @var integer
00043      */
00044     protected $pageId;
00045     /**
00046      * The rootline for the current page.
00047      * @var array
00048      */
00049     protected $rootline;
00050 
00051     /**
00052      * Whether to simulate the behaviour and match all conditions
00053      * (used in TypoScript object browser).
00054      * @var boolean
00055      */
00056     protected $simulateMatchResult = FALSE;
00057     /**
00058      * Whether to simulat the behaviour and match specific conditions
00059      * (used in TypoScript object browser).
00060      * @var array
00061      */
00062     protected $simulateMatchConditions = array();
00063 
00064     /**
00065      * Sets the id of the page to evaluate conditions for.
00066      *
00067      * @param   integer     $pageId: Id of the page (must be positive)
00068      * @return  void
00069      */
00070     public function setPageId($pageId) {
00071         if (is_integer($pageId) && $pageId > 0) {
00072             $this->pageId = $pageId;
00073         }
00074     }
00075 
00076     /**
00077      * Gets the id of the page to evaluate conditions for.
00078      *
00079      * @return  integer     Id of the page
00080      */
00081     public function getPageId() {
00082         return $this->pageId;
00083     }
00084 
00085     /**
00086      * Sets the rootline.
00087      *
00088      * @param   array       $rootline: The rootline to be used for matching (must have elements)
00089      * @return  void
00090      */
00091     public function setRootline(array $rootline) {
00092         if (count($rootline)) {
00093             $this->rootline = $rootline;
00094         }
00095     }
00096 
00097     /**
00098      * Gets the rootline.
00099      *
00100      * @return  array       The rootline to be used for matching
00101      */
00102     public function getRootline() {
00103         return $this->rootline;
00104     }
00105 
00106     /**
00107      * Sets whether to simulate the behaviour and match all conditions.
00108      *
00109      * @param   boolean     $simulateMatchResult: Whether to simulate positive matches
00110      * @return  void
00111      */
00112     public function setSimulateMatchResult($simulateMatchResult) {
00113         if (is_bool($simulateMatchResult)) {
00114             $this->simulateMatchResult = $simulateMatchResult;
00115         }
00116     }
00117 
00118     /**
00119      * Sets whether to simulate the behaviour and match specific conditions.
00120      *
00121      * @param   array       $simulateMatchConditions: Conditions to simulate a match for
00122      * @return  void
00123      */
00124     public function setSimulateMatchConditions(array $simulateMatchConditions) {
00125         $this->simulateMatchConditions = $simulateMatchConditions;
00126     }
00127 
00128     /**
00129      * Normalizes an expression and removes the first and last square bracket.
00130      *  + OR normalization: "...]OR[...", "...]||[...", "...][..." --> "...]||[..."
00131      *  + AND normalization: "...]AND[...", "...]&&[..."           --> "...]&&[..."
00132      *
00133      * @param   string      $expression: The expression to be normalized (e.g. "[A] && [B] OR [C]")
00134      * @return  string      The normalized expression (e.g. "[A]&&[B]||[C]")
00135      */
00136     protected function normalizeExpression($expression) {
00137         $normalizedExpression = preg_replace(
00138             array(
00139                  '/\]\s*(OR|\|\|)?\s*\[/i',
00140                  '/\]\s*(AND|&&)\s*\[/i',
00141             ),
00142             array(
00143                  ']||[',
00144                  ']&&[',
00145             ),
00146             trim($expression)
00147         );
00148 
00149         return $normalizedExpression;
00150     }
00151 
00152     /**
00153      * Matches a TypoScript condition expression.
00154      *
00155      * @param   string      $expression: The expression to match
00156      * @return  boolean     Whether the expression matched
00157      */
00158     public function match($expression) {
00159             // Return directly if result should be simulated:
00160         if ($this->simulateMatchResult) {
00161             return $this->simulateMatchResult;
00162         }
00163             // Return directly if matching for specific condition is simulated only:
00164         if (count($this->simulateMatchConditions)) {
00165             return in_array($expression, $this->simulateMatchConditions);
00166         }
00167             // Sets the current pageId if not defined yet:
00168         if (!isset($this->pageId)) {
00169             $this->pageId = $this->determinePageId();
00170         }
00171             // Sets the rootline if not defined yet:
00172         if (!isset($this->rootline)) {
00173             $this->rootline = $this->determineRootline();
00174         }
00175 
00176         $result = FALSE;
00177         $normalizedExpression = $this->normalizeExpression($expression);
00178 
00179             // First and last character must be square brackets (e.g. "[A]&&[B]":
00180         if (substr($normalizedExpression, 0, 1) === '[' && substr($normalizedExpression, -1) === ']') {
00181             $innerExpression = substr($normalizedExpression, 1, -1);
00182 
00183             $orParts = explode(']||[', $innerExpression);
00184             foreach ($orParts as $orPart) {
00185                 $andParts = explode(']&&[', $orPart);
00186                 foreach ($andParts as $andPart) {
00187                     $result = $this->evaluateCondition($andPart);
00188                         // If condition in AND context fails, the whole block is false:
00189                     if ($result === FALSE) {
00190                         break;
00191                     }
00192                 }
00193                     // If condition in OR context succeeds, the whole expression is true:
00194                 if ($result === TRUE) {
00195                     break;
00196                 }
00197             }
00198         }
00199 
00200         return $result;
00201     }
00202 
00203     /**
00204      * Evaluates a TypoScript condition given as input, eg. "[browser=net][...(other conditions)...]"
00205      *
00206      * @param   string      The condition to match against its criterias.
00207      * @return  mixed       Returns true or false based on the evaluation
00208      */
00209     protected function evaluateConditionCommon($key, $value) {
00210         if (t3lib_div::inList('browser,version,system,useragent', strtolower($key))) {
00211             $browserInfo = $this->getBrowserInfo(t3lib_div::getIndpEnv('HTTP_USER_AGENT'));
00212         }
00213         $keyParts = t3lib_div::trimExplode('|', $key);
00214 
00215         switch ($keyParts[0]) {
00216             case 'browser':
00217                 $values = t3lib_div::trimExplode(',', $value, TRUE);
00218                     // take all identified browsers into account, eg chrome deliver
00219                     // webkit=>532.5, chrome=>4.1, safari=>532.5
00220                     // so comparing string will be
00221                     // "webkit532.5 chrome4.1 safari532.5"
00222                 $all = '';
00223                 foreach ($browserInfo['all'] as $key => $value) {
00224                     $all .= $key . $value . ' ';
00225                 }
00226                 foreach ($values as $test) {
00227                     if (stripos($all, $test) !== FALSE) {
00228                         return TRUE;
00229                     }
00230                 }
00231             break;
00232             case 'version':
00233                 $values = t3lib_div::trimExplode(',', $value, TRUE);
00234                 foreach ($values as $test) {
00235                     if (strcspn($test, '=<>') == 0) {
00236                         switch (substr($test, 0, 1)) {
00237                             case '=':
00238                                 if (doubleval(substr($test, 1)) == $browserInfo['version']) {
00239                                     return TRUE;
00240                                 }
00241                             break;
00242                             case '<':
00243                                 if (doubleval(substr($test, 1)) > $browserInfo['version']) {
00244                                     return TRUE;
00245                                 }
00246                             break;
00247                             case '>':
00248                                 if (doubleval(substr($test, 1)) < $browserInfo['version']) {
00249                                     return TRUE;
00250                                 }
00251                             break;
00252                         }
00253                     } elseif (strpos(' ' . $browserInfo['version'], $test) == 1) {
00254                         return TRUE;
00255                     }
00256                 }
00257             break;
00258             case 'system':
00259                 $values = t3lib_div::trimExplode(',', $value, TRUE);
00260                     // Take all identified systems into account, e.g. mac for iOS, Linux
00261                     // for android and Windows NT for Windows XP
00262                 $allSystems .= ' ' . implode(' ', $browserInfo['all_systems']);
00263                 foreach ($values as $test) {
00264                     if (stripos($allSystems, $test) !== FALSE) {
00265                         return TRUE;
00266                     }
00267                 }
00268             break;
00269             case 'device':
00270                 if (!isset($this->deviceInfo)) {
00271                     $this->deviceInfo = $this->getDeviceType(t3lib_div::getIndpEnv('HTTP_USER_AGENT'));
00272                 }
00273                 $values = t3lib_div::trimExplode(',', $value, TRUE);
00274                 foreach ($values as $test) {
00275                     if ($this->deviceInfo == $test) {
00276                         return TRUE;
00277                     }
00278                 }
00279             break;
00280             case 'useragent':
00281                 $test = trim($value);
00282                 if (strlen($test)) {
00283                     return $this->searchStringWildcard($browserInfo['useragent'], $test);
00284                 }
00285             break;
00286             case 'language':
00287                 $values = t3lib_div::trimExplode(',', $value, TRUE);
00288                 foreach ($values as $test) {
00289                     if (preg_match('/^\*.+\*$/', $test)) {
00290                         $allLanguages = preg_split('/[,;]/', t3lib_div::getIndpEnv('HTTP_ACCEPT_LANGUAGE'));
00291                         if (in_array(substr($test, 1, -1), $allLanguages)) {
00292                             return TRUE;
00293                         }
00294                     } elseif (t3lib_div::getIndpEnv('HTTP_ACCEPT_LANGUAGE') == $test) {
00295                         return TRUE;
00296                     }
00297                 }
00298             break;
00299             case 'IP':
00300                 if (t3lib_div::cmpIP(t3lib_div::getIndpEnv('REMOTE_ADDR'), $value)) {
00301                     return TRUE;
00302                 }
00303             break;
00304             case 'hostname':
00305                 if (t3lib_div::cmpFQDN(t3lib_div::getIndpEnv('REMOTE_ADDR'), $value)) {
00306                     return TRUE;
00307                 }
00308             break;
00309                 // hour, minute, dayofweek, dayofmonth, month, year, julianday
00310             case 'hour':
00311             case 'minute':
00312             case 'month':
00313             case 'year':
00314             case 'dayofweek':
00315             case 'dayofmonth':
00316             case 'dayofyear':
00317                 $theEvalTime = $GLOBALS['SIM_EXEC_TIME']; // In order to simulate time properly in templates.
00318                 switch ($key) {
00319                     case 'hour':
00320                         $theTestValue = date('H', $theEvalTime);
00321                     break;
00322                     case 'minute':
00323                         $theTestValue = date('i', $theEvalTime);
00324                     break;
00325                     case 'month':
00326                         $theTestValue = date('m', $theEvalTime);
00327                     break;
00328                     case 'year':
00329                         $theTestValue = date('Y', $theEvalTime);
00330                     break;
00331                     case 'dayofweek':
00332                         $theTestValue = date('w', $theEvalTime);
00333                     break;
00334                     case 'dayofmonth':
00335                         $theTestValue = date('d', $theEvalTime);
00336                     break;
00337                     case 'dayofyear':
00338                         $theTestValue = date('z', $theEvalTime);
00339                     break;
00340                 }
00341                 $theTestValue = intval($theTestValue);
00342                     // comp
00343                 $values = t3lib_div::trimExplode(',', $value, TRUE);
00344                 foreach ($values as $test) {
00345                     if (t3lib_div::testInt($test)) {
00346                         $test = '=' . $test;
00347                     }
00348                     if ($this->compareNumber($test, $theTestValue)) {
00349                         return TRUE;
00350                     }
00351                 }
00352             break;
00353             case 'compatVersion':
00354                 return t3lib_div::compat_version($value);
00355             break;
00356             case 'loginUser':
00357                 if ($this->isUserLoggedIn()) {
00358                     $values = t3lib_div::trimExplode(',', $value, TRUE);
00359                     foreach ($values as $test) {
00360                         if ($test == '*' || !strcmp($this->getUserId(), $test)) {
00361                             return TRUE;
00362                         }
00363                     }
00364                 } elseif ($value === '') {
00365                     return TRUE;
00366                 }
00367             break;
00368             case 'page':
00369                 if ($keyParts[1]) {
00370                     $page = $this->getPage();
00371                     $property = $keyParts[1];
00372                     if (!empty($page) && isset($page[$property])) {
00373                         if (strcmp($page[$property], $value) === 0) {
00374                             return TRUE;
00375                         }
00376                     }
00377                 }
00378             break;
00379             case 'globalVar':
00380                 $values = t3lib_div::trimExplode(',', $value, TRUE);
00381                 foreach ($values as $test) {
00382                     $point = strcspn($test, '!=<>');
00383                     $theVarName = substr($test, 0, $point);
00384                     $nv = $this->getVariable(trim($theVarName));
00385                     $testValue = substr($test, $point);
00386 
00387                     if ($this->compareNumber($testValue, $nv)) {
00388                         return TRUE;
00389                     }
00390                 }
00391             break;
00392             case 'globalString':
00393                 $values = t3lib_div::trimExplode(',', $value, TRUE);
00394                 foreach ($values as $test) {
00395                     $point = strcspn($test, '=');
00396                     $theVarName = substr($test, 0, $point);
00397                     $nv = $this->getVariable(trim($theVarName));
00398                     $testValue = substr($test, $point + 1);
00399 
00400                     if ($this->searchStringWildcard($nv, trim($testValue))) {
00401                         return TRUE;
00402                     }
00403                 }
00404             break;
00405             case 'userFunc':
00406                 $values = preg_split('/\(|\)/', $value);
00407                 $funcName = trim($values[0]);
00408                 $funcValue = t3lib_div::trimExplode(',', $values[1]);
00409                 $prefix = $this->getUserFuncClassPrefix();
00410                 if ($prefix &&
00411                     !t3lib_div::isFirstPartOfStr(trim($funcName), $prefix) &&
00412                     !t3lib_div::isFirstPartOfStr(trim($funcName), 'tx_')
00413                 ) {
00414                     $this->log('Match condition: Function "' . $funcName . '" was not prepended with "' . $prefix . '"');
00415                     return FALSE;
00416                 }
00417                 if (function_exists($funcName) && call_user_func($funcName, $funcValue[0])) {
00418                     return TRUE;
00419                 }
00420             break;
00421         }
00422 
00423         return NULL;
00424     }
00425 
00426     protected function getVariableCommon(array $vars) {
00427         $value = NULL;
00428 
00429         if (count($vars) == 1) {
00430             $value = $this->getGlobal($vars[0]);
00431         } else {
00432             $splitAgain = explode('|', $vars[1], 2);
00433             $k = trim($splitAgain[0]);
00434 
00435             if ($k) {
00436                 switch ((string) trim($vars[0])) {
00437                     case 'GP':
00438                         $value = t3lib_div::_GP($k);
00439                     break;
00440                     case 'ENV':
00441                         $value = getenv($k);
00442                     break;
00443                     case 'IENV':
00444                         $value = t3lib_div::getIndpEnv($k);
00445                     break;
00446                         // return litteral value:
00447                     case 'LIT':
00448                         return trim($vars[1]);
00449                     break;
00450                     default:
00451                         return NULL;
00452                 }
00453                     // If array:
00454                 if (count($splitAgain) > 1) {
00455                     if (is_array($value) && trim($splitAgain[1])) {
00456                         $value = $this->getGlobal($splitAgain[1], $value);
00457                     } else {
00458                         $value = '';
00459                     }
00460                 }
00461             }
00462         }
00463 
00464         return $value;
00465     }
00466 
00467     /**
00468      * Evaluates a $leftValue based on an operator: "<", ">", "<=", ">=", "!=" or "="
00469      *
00470      * @param   string      $test: The value to compare with on the form [operator][number]. Eg. "< 123"
00471      * @param   integer     $leftValue: The value on the left side
00472      * @return  boolean     If $value is "50" and $test is "< 123" then it will return true.
00473      */
00474     protected function compareNumber($test, $leftValue) {
00475         if (preg_match('/^(!?=+|<=?|>=?)\s*([^\s]*)\s*$/', $test, $matches)) {
00476             $operator = $matches[1];
00477             $rightValue = $matches[2];
00478 
00479             switch ($operator) {
00480                 case '>=':
00481                     return ($leftValue >= doubleval($rightValue));
00482                 break;
00483                 case '<=':
00484                     return ($leftValue <= doubleval($rightValue));
00485                 break;
00486                 case '!=':
00487                     return ($leftValue != doubleval($rightValue));
00488                 break;
00489                 case '<':
00490                     return ($leftValue < doubleval($rightValue));
00491                 break;
00492                 case '>':
00493                     return ($leftValue > doubleval($rightValue));
00494                 break;
00495                 default:
00496                         // nothing valid found except '=', use '='
00497                     return ($leftValue == trim($rightValue));
00498                 break;
00499             }
00500         }
00501 
00502         return FALSE;
00503     }
00504 
00505     /**
00506      * Matching two strings against each other, supporting a "*" wildcard or (if wrapped in "/") PCRE regular expressions
00507      *
00508      * @param   string      The string in which to find $needle.
00509      * @param   string      The string to find in $haystack
00510      * @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.
00511      */
00512     protected function searchStringWildcard($haystack, $needle) {
00513         $result = FALSE;
00514 
00515         if ($needle) {
00516             if (preg_match('/^\/.+\/$/', $needle)) {
00517                     // Regular expression, only "//" is allowed as delimiter
00518                 $regex = $needle;
00519             } else {
00520                 $needle = str_replace(array('*', '?'), array('###MANY###', '###ONE###'), $needle);
00521                 $regex = '/^' . preg_quote($needle, '/') . '$/';
00522                     // Replace the marker with .* to match anything (wildcard)
00523                 $regex = str_replace(array('###MANY###', '###ONE###'), array('.*', '.'), $regex);
00524             }
00525 
00526             $result = (boolean) preg_match($regex, (string) $haystack);
00527         }
00528 
00529         return $result;
00530     }
00531 
00532     /**
00533      * Generates an array with abstracted browser information
00534      *
00535      * @param   string      $userAgent: The useragent string, t3lib_div::getIndpEnv('HTTP_USER_AGENT')
00536      * @return  array       Contains keys "browser", "version", "system"
00537      */
00538     protected function getBrowserInfo($userAgent) {
00539         return t3lib_utility_Client::getBrowserInfo($userAgent);
00540     }
00541 
00542     /**
00543      * Gets a code for a browsing device based on the input useragent string.
00544      *
00545      * @param   string      $userAgent: The useragent string, t3lib_div::getIndpEnv('HTTP_USER_AGENT')
00546      * @return  string      Code for the specific device type
00547      */
00548     protected function getDeviceType($userAgent) {
00549         return t3lib_utility_Client::getDeviceType($userAgent);
00550     }
00551 
00552     /**
00553      * Return global variable where the input string $var defines array keys separated by "|"
00554      * Example: $var = "HTTP_SERVER_VARS | something" will return the value $GLOBALS['HTTP_SERVER_VARS']['something'] value
00555      *
00556      * @param   string      Global var key, eg. "HTTP_GET_VAR" or "HTTP_GET_VARS|id" to get the GET parameter "id" back.
00557      * @param   array       Alternative array than $GLOBAL to get variables from.
00558      * @return  mixed       Whatever value. If none, then blank string.
00559      */
00560     protected function getGlobal($var, $source = NULL) {
00561         $vars = explode('|', $var);
00562         $c = count($vars);
00563         $k = trim($vars[0]);
00564         $theVar = isset($source) ? $source[$k] : $GLOBALS[$k];
00565 
00566         for ($a = 1; $a < $c; $a++) {
00567             if (!isset($theVar)) {
00568                 break;
00569             }
00570 
00571             $key = trim($vars[$a]);
00572             if (is_object($theVar)) {
00573                 $theVar = $theVar->$key;
00574             } elseif (is_array($theVar)) {
00575                 $theVar = $theVar[$key];
00576             } else {
00577                 return '';
00578             }
00579         }
00580 
00581         if (!is_array($theVar) && !is_object($theVar)) {
00582             return $theVar;
00583         } else {
00584             return '';
00585         }
00586     }
00587 
00588 
00589     /**
00590      * Evaluates a TypoScript condition given as input, eg. "[browser=net][...(other conditions)...]"
00591      *
00592      * @param   string      $string: The condition to match against its criterias.
00593      * @return  boolean     Whether the condition matched
00594      * @see t3lib_tsparser::parse()
00595      */
00596     abstract protected function evaluateCondition($string);
00597 
00598     /**
00599      * Gets the value of a variable.
00600      *
00601      * Examples of names:
00602      * + TSFE:id
00603      * + GP:firstLevel|secondLevel
00604      * + _GET|firstLevel|secondLevel
00605      * + LIT:someLiteralValue
00606      *
00607      * @param   string      $name: The name of the variable to fetch the value from
00608      * @return  mixed       The value of the given variable (string) or NULL if variable did not exist
00609      */
00610     abstract protected function getVariable($name);
00611 
00612     /**
00613      * Gets the usergroup list of the current user.
00614      *
00615      * @return  string      The usergroup list of the current user
00616      */
00617     abstract protected function getGroupList();
00618 
00619     /**
00620      * Determines the current page Id.
00621      *
00622      * @return  integer     The current page Id
00623      */
00624     abstract protected function determinePageId();
00625 
00626     /**
00627      * Gets the properties for the current page.
00628      *
00629      * @return  array       The properties for the current page.
00630      */
00631     abstract protected function getPage();
00632 
00633     /**
00634      * Determines the rootline for the current page.
00635      *
00636      * @return  array       The rootline for the current page.
00637      */
00638     abstract protected function determineRootline();
00639 
00640     /**
00641      * Gets prefix for user functions (normally 'user_').
00642      *
00643      * @return  string      The prefix for user functions (normally 'user_').
00644      */
00645     abstract protected function getUserFuncClassPrefix();
00646 
00647     /**
00648      * Gets the id of the current user.
00649      *
00650      * @return  integer     The id of the current user
00651      */
00652     abstract protected function getUserId();
00653 
00654     /**
00655      * Determines if a user is logged in.
00656      *
00657      * @return  boolean     Determines if a user is logged in
00658      */
00659     abstract protected function isUserLoggedIn();
00660 
00661     /**
00662      * Sets a log message.
00663      *
00664      * @param   string      $message: The log message to set/write
00665      * @return  void
00666      */
00667     abstract protected function log($message);
00668 }
00669 
00670 ?>