[TASK] Streamline class prefix handling in the core
authorSusanne Moog <typo3@susannemoog.de>
Mon, 22 Aug 2011 15:53:51 +0000 (17:53 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Wed, 24 Aug 2011 19:59:34 +0000 (21:59 +0200)
At the moment the frontend option userFuncClassPrefix is also checked in
various backend scenarios (for example the registry) and in general
everywhere t3lib_div::hasValidPrefix is called. Therefore it should be a system
option. Additionally to ensure "user_" is working as a prefix this was given
hardcoded as additionalPrefix to the function in some cases.

This patch streamlines the handling of prefixes, introduces a [SYS] option
additionalAllowedClassPrefixes, deprecates the [FE] option and allows
the prefixes 'tx_', 'Tx_', 'user_' and 'User_' by default.

Change-Id: I70fa89120dbe8c7790e3d6de4b173b69beb8a6f9
Resolves: #29166
Releases: 4.6
Reviewed-on: http://review.typo3.org/4526
Reviewed-by: Philipp Gampe
Tested-by: Philipp Gampe
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
t3lib/class.t3lib_div.php
t3lib/class.t3lib_extmgm.php
t3lib/class.t3lib_registry.php
t3lib/config_default.php
t3lib/matchcondition/class.t3lib_matchcondition_abstract.php
t3lib/matchcondition/class.t3lib_matchcondition_backend.php
t3lib/matchcondition/class.t3lib_matchcondition_frontend.php
tests/t3lib/class.t3lib_divTest.php
typo3/sysext/cms/tslib/class.tslib_content.php

index 90b77ee..60dddc7 100644 (file)
@@ -4602,7 +4602,7 @@ final class t3lib_div {
         * @param string $funcName Function/Method reference, '[file-reference":"]["&"]class/function["->"method-name]'. You can prefix this reference with "[file-reference]:" and t3lib_div::getFileAbsFileName() will then be used to resolve the filename and subsequently include it by "require_once()" which means you don't have to worry about including the class file either! Example: "EXT:realurl/class.tx_realurl.php:&tx_realurl->encodeSpURL". Finally; you can prefix the class name with "&" if you want to reuse a former instance of the same object call ("singleton").
         * @param mixed $params Parameters to be pass along (typically an array) (REFERENCE!)
         * @param mixed $ref Reference to be passed along (typically "$this" - being a reference to the calling object) (REFERENCE!)
-        * @param string $checkPrefix Required prefix of class or function name
+        * @param string $checkPrefix Alternative allowed prefix of class or function name
         * @param integer $errorMode Error mode (when class/function could not be found): 0 - call debug(), 1 - do nothing, 2 - raise an exception (allows to call a user function that may return FALSE)
         * @return mixed Content from method/function call or FALSE if the class/method/function was not found
         * @see getUserObj()
@@ -4780,14 +4780,14 @@ final class t3lib_div {
         * @return bool TRUE if name is allowed
         */
        public static function hasValidClassPrefix($classRef, array $additionalPrefixes = array()) {
-               if(empty($classRef)) {
+               if (empty($classRef)) {
                        return FALSE;
                }
-               if(!is_string($classRef)) {
+               if (!is_string($classRef)) {
                        throw new InvalidArgumentException('$classRef has to be of type string', 1313917992);
                }
                $hasValidPrefix = FALSE;
-               $validPrefixes = array('tx_', 'Tx_', $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix']);
+               $validPrefixes = self::getValidClassPrefixes();
                $classRef = trim($classRef);
 
                if (count($additionalPrefixes)) {
@@ -4804,6 +4804,25 @@ final class t3lib_div {
        }
 
        /**
+        * Returns all valid class prefixes.
+        *
+        * @return array Array of valid prefixed of class names
+        */
+       public static function getValidClassPrefixes() {
+               $validPrefixes = array('tx_', 'Tx_', 'user_', 'User_');
+               if (
+                       isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'])
+                       && is_string($GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'])
+               ) {
+                       $validPrefixes = array_merge(
+                               $validPrefixes,
+                               t3lib_div::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'])
+                       );
+               }
+               return $validPrefixes;
+       }
+
+       /**
         * Creates an instance of a class taking into account the class-extensions
         * API of TYPO3. USE THIS method instead of the PHP "new" keyword.
         * Eg. "$obj = new myclass;" should be "$obj = t3lib_div::makeInstance("myclass")" instead!
index 3673177..502e7fc 100644 (file)
@@ -999,13 +999,7 @@ final class t3lib_extMgm {
         * @author      RenĂ© Fritz <r.fritz@colorcube.de>
         */
        public static function addService($extKey, $serviceType, $serviceKey, $info) {
-                       // even not available services will be included to make it possible to give the admin a feedback of non-available services.
-                       // but maybe it's better to move non-available services to a different array??
-
-               if ($serviceType &&
-                               t3lib_div::hasValidClassPrefix($serviceKey, array('user_')) &&
-                               is_array($info)) {
-
+               if ($serviceType && t3lib_div::hasValidClassPrefix($serviceKey) && is_array($info)) {
                        $info['priority'] = max(0, min(100, $info['priority']));
 
                        $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey] = $info;
@@ -1705,4 +1699,4 @@ $GLOBALS[\'TYPO3_LOADED_EXT\'] = unserialize(stripslashes(\'' . addslashes(seria
        }
 }
 
-?>
+?>
\ No newline at end of file
index c6fed3b..e345f0c 100644 (file)
@@ -181,7 +181,7 @@ class t3lib_Registry implements t3lib_Singleton {
         * @throws      InvalidArgumentException        Throws an exception if the given namespace is not valid
         */
        protected function validateNamespace($namespace) {
-               if (t3lib_div::hasValidClassPrefix($namespace, array('user_'))) {
+               if (t3lib_div::hasValidClassPrefix($namespace)) {
                        return;
                }
 
index d7e4199..58f1a03 100644 (file)
@@ -154,6 +154,7 @@ $TYPO3_CONF_VARS = array(
                        ),
                ),
                'useCachingFramework' => -1,    // <i>Obsolete setting</i>. Please remove manually from <tt>localconf.php</tt>, if it is defined there. Caching Framework is now always enabled.
+               'additionalAllowedClassPrefixes' => NULL,       // Class names in TYPO3 must usually start with tx_, Tx, user_ or User_. This setting allows to register additional prefixes in a comma separated list.
                'displayErrors' => -1,                                  // <p>Integer (-1, 0, 1, 2). Configures whether PHP errors should be displayed.</p><dl><dt>0</dt><dd>Do not display any PHP error messages. Overrides the value of "exceptionalErrors" and sets it to 0 (= no errors are turned into exceptions), the configured "productionExceptionHandler" is used as exception handler</dd><dt>1</dt><dd>Display error messages with the registered errorhandler. The configured "debugExceptionHandler" is used as exception handler</dd><dt>2</dt><dd>Display errors only if client matches <a href="#SYS-devIPmask">[SYS][devIPmask]</a>. If devIPmask matches the users IP address  the configured "debugExceptionHandler" is used  for exceptions, if not "productionExceptionHandler" will be used</dd><dt>-1</dt><dd>Default setting. With this option, you can override the PHP setting "display_errors". If devIPmask matches the users IP address  the configured "debugExceptionHandler" is used  for exceptions, if not "productionExceptionHandler" will be used.</dd></dl>
                'productionExceptionHandler'  => 't3lib_error_ProductionExceptionHandler',      // String: Classname to handle exceptions that might happen in the TYPO3-code. Leave empty to disable exception handling. Default: "t3lib_error_ProductionExceptionHandler". This exception handler displays a nice error message when something went wrong. The error message is logged to the configured logs. Note: The configured "productionExceptionHandler" is used if displayErrors is set to "0" or to "-1" and devIPmask doesn't match the users IP.
                'debugExceptionHandler' => 't3lib_error_DebugExceptionHandler',                         // String: Classname to handle exceptions that might happen in the TYPO3-code. Leave empty to disable exception handling. Default: "t3lib_error_DebugExceptionHandler". This exception handler displays the complete stack trace of any encountered exception. The error message and the stack trace  is logged to the configured logs. Note: The configured "debugExceptionHandler" is used if displayErrors is set to "1" and if displayErrors is "-1"  or "2" and the devIPmask matches the users IP.
@@ -542,7 +543,6 @@ $TYPO3_CONF_VARS = array(
                'pageUnavailable_handling' => '',               // <p>How TYPO3 should handle requests when pages are unavailable due to system problems.</p><dl><dt>empty (default)</dt><dd>An error message is shown.</dd><dt>String</dt><dd>HTML file or URL to show (reads content and outputs with correct headers), e.g. 'unavailable.html' or 'http://www.example.org/errors/unavailable.html'.</dd><dt>Prefix "REDIRECT:"</dt><dd>If prefixed "REDIRECT:" it will redirect to the URL/script after the prefix.</dd><dt>Prefix "READFILE:"</dt><dd>If prefixed with "READFILE:" then it will expect the remaining string to be a HTML file which will be read and outputted directly after having the marker "###CURRENT_URL###" substituted with REQUEST_URI and ###REASON### with reason text, for example: "READFILE:fileadmin/unavailable.html".</dd><dt>Prefix "USER_FUNCTION:"</dt><dd>If prefixed "USER_FUNCTION:" then it will call a user function, eg. "USER_FUNCTION:fileadmin/class.user_unavailable.php:user_unavailable->pageUnavailable" where the file must contain a class "user_unavailable" with a method "pageUnavailable" inside with two parameters $param and $ref. If the client matches <a href="#SYS-devIPmask">[SYS][devIPmask]</a>, this setting is ignored and the page is shown as normal.</dd></dl>
                'pageUnavailable_handling_statheader' => 'HTTP/1.0 503 Service Temporarily Unavailable',                // If 'pageUnavailable_handling' is enabled, this string will always be sent as header before the actual handling.
                'pageUnavailable_force' => FALSE,               // Boolean: If TRUE, pageUnavailable_handling is used for every frontend page. If the client matches <a href="#SYS-devIPmask">[SYS][devIPmask]</a>, the page is shown as normal. This is useful during temporary site maintenance.
-               'userFuncClassPrefix' => 'user_',               // This prefix must be the first part of any function or class name called from TypoScript, for instance in the stdWrap function.
                'addRootLineFields' => '',                              // Comma-list of fields from the 'pages'-table. These fields are added to the select query for fields in the rootline.
                'checkFeUserPid' => TRUE,                               // Boolean: If set, the pid of fe_user logins must be sent in the form as the field 'pid' and then the user must be located in the pid. If you unset this, you should change the fe_users.username eval-flag 'uniqueInPid' to 'unique' in $TCA. This will do: $TCA['fe_users']['columns']['username']['config']['eval']= 'nospace,lower,required,unique';
                'lockIP' => 2,                                                  // Integer (0-4). If >0, fe_users are locked to (a part of) their REMOTE_ADDR IP for their session. Enhances security but may throw off users that may change IP during their session (in which case you can lower it to 2 or 3). The integer indicates how many parts of the IP address to include in the check. Reducing to 1-3 means that only first, second or third part of the IP address is used. 4 is the FULL IP address and recommended. 0 (zero) disables checking of course.
@@ -841,6 +841,19 @@ if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['useCachingFramework'] !== -1) {
                // Deprecation log since 4.6, can be removed in 4.8. Checks if obsolete useCachingFramework is set
        t3lib_div::deprecationLog('Setting $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'useCachingFramework\'] is obsolete since TYPO3 4.6 and should be removed from localconf.php.');
 }
+
+if (isset($GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'])) {
+       if(is_string($GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'])) {
+               if(is_null($GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'])) {
+                       $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'];
+               } else if(is_string($GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'])) {
+                       $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] .= ',' . $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'];
+               }
+       }
+               // Deprecation log since 4.6, can be removed in 4.8
+       t3lib_div::deprecationLog('$GLOBALS[\'TYPO3_CONF_VARS\'][\'FE\'][\'userFuncClassPrefix\'] is deprecated, use $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'additionalAllowedClassPrefixes\'] instead');
+}
+
        // Force enabled caching framework
        // @deprecated, constant can be removed in 4.8
 define('TYPO3_UseCachingFramework', TRUE);
@@ -1098,4 +1111,4 @@ $SIM_EXEC_TIME = $EXEC_TIME;                      // $SIM_EXEC_TIME is set to $EXEC_TIME but can be
 $ACCESS_TIME = $EXEC_TIME - ($EXEC_TIME % 60);         // $ACCESS_TIME is a common time in minutes for access control
 $SIM_ACCESS_TIME = $ACCESS_TIME;               // if $SIM_EXEC_TIME is changed this value must be set accordingly
 
-?>
+?>
\ No newline at end of file
index cfebb39..a9de498 100644 (file)
@@ -404,11 +404,9 @@ abstract class t3lib_matchCondition_abstract {
                                $values = preg_split('/\(|\)/', $value);
                                $funcName = trim($values[0]);
                                $funcValue = t3lib_div::trimExplode(',', $values[1]);
-                               $prefix = $this->getUserFuncClassPrefix();
-                               if ($prefix &&
-                                       !t3lib_div::hasValidClassPrefix($funcName, array($prefix))
+                               if (!t3lib_div::hasValidClassPrefix($funcName)
                                ) {
-                                       $this->log('Match condition: Function "' . $funcName . '" was not prepended with "' . $prefix . '"');
+                                       $this->log('Match condition: Function "' . $funcName . '" was not prepended with one of "' . implode(', ', t3lib_div::getValidClassPrefixes()) . '"');
                                        return FALSE;
                                }
                                if (function_exists($funcName) && call_user_func($funcName, $funcValue[0])) {
@@ -635,13 +633,6 @@ abstract class t3lib_matchCondition_abstract {
        abstract protected function determineRootline();
 
        /**
-        * Gets prefix for user functions (normally 'user_').
-        *
-        * @return      string          The prefix for user functions (normally 'user_').
-        */
-       abstract protected function getUserFuncClassPrefix();
-
-       /**
         * Gets the id of the current user.
         *
         * @return      integer         The id of the current user
index 5517605..aab4ff8 100644 (file)
@@ -261,16 +261,6 @@ class t3lib_matchCondition_backend extends t3lib_matchCondition_abstract {
        }
 
        /**
-        * Get prefix for user functions (normally 'user_').
-        *
-        * @return      string          The prefix for user functions (normally 'user_').
-        */
-       protected function getUserFuncClassPrefix() {
-               $userFuncClassPrefix = 'user_';
-               return $userFuncClassPrefix;
-       }
-
-       /**
         * Get the id of the current user.
         *
         * @return      integer         The id of the current user
index 26a06aa..bd5144f 100644 (file)
@@ -157,16 +157,6 @@ class t3lib_matchCondition_frontend extends t3lib_matchCondition_abstract {
        }
 
        /**
-        * Get prefix for user functions (normally 'user_').
-        *
-        * @return      string          The prefix for user functions (normally 'user_').
-        */
-       protected function getUserFuncClassPrefix() {
-               $userFuncClassPrefix = $GLOBALS['TSFE']->TYPO3_CONF_VARS['FE']['userFuncClassPrefix'];
-               return $userFuncClassPrefix;
-       }
-
-       /**
         * Get the id of the current user.
         *
         * @return      integer         The id of the current user
index e467590..3c772b8 100644 (file)
@@ -3331,7 +3331,7 @@ class t3lib_divTest extends tx_phpunit_testcase {
         * @param string $className Class name to test
         */
        public function hasValidClassPrefixAcceptsValidPrefixes($className) {
-               $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'] = 'user_';
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = 'foo_';
                $this->assertTrue(
                        t3lib_div::hasValidClassPrefix($className)
                );
@@ -3346,7 +3346,7 @@ class t3lib_divTest extends tx_phpunit_testcase {
                        array('txfoo'),
                        array('Txfoo'),
                        array('userfoo'),
-                       array('User_foo'),
+                       array('aser_foo'),
                );
        }
 
@@ -3356,7 +3356,7 @@ class t3lib_divTest extends tx_phpunit_testcase {
         * @param string $className Class name to test
         */
        public function hasValidClassPrefixRefusesInvalidPrefixes($className) {
-               $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'] = 'user_';
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = 'foo_';
                $this->assertFalse(
                        t3lib_div::hasValidClassPrefix($className)
                );
@@ -3399,13 +3399,38 @@ class t3lib_divTest extends tx_phpunit_testcase {
         * @param string $className Class name to test
         */
        public function hasValidClassPrefixAllowsEmptyPrefix($className) {
-               $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'] = '';
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = '';
                $this->assertTrue(
                        t3lib_div::hasValidClassPrefix($className)
                );
        }
 
        /**
+        * @return array
+        */
+       public function getValidClassPrefixesReturnsListOfValidClassPrefixesDataProvider() {
+               return array(
+                       array('user_'),
+                       array('User_'),
+                       array('Tx_'),
+                       array('tx_'),
+                       array('foo_'),
+                       array('bar_'),
+                       array('BAZ_')
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider getValidClassPrefixesReturnsListOfValidClassPrefixesDataProvider
+        * @param string $prefix prefix to test
+        */
+       public function getValidClassPrefixesReturnsListOfValidClassPrefixes($prefix) {
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = 'foo_,bar_,BAZ_';
+               $this->assertTrue(in_array($prefix, t3lib_div::getValidClassPrefixes()));
+       }
+
+       /**
         * @test
         */
        public function hasValidClassPrefixAcceptsAdditionalPrefixes() {
index 700fbbc..5723048 100644 (file)
@@ -6338,9 +6338,8 @@ class tslib_cObj {
         * @see USER(), stdWrap(), typoLink(), _parseFunc()
         */
        function callUserFunction($funcName, $conf, $content) {
-               $pre = $GLOBALS['TSFE']->TYPO3_CONF_VARS['FE']['userFuncClassPrefix'];
-               if ($pre && !t3lib_div::hasValidClassPrefix($funcName, array($pre))) {
-                       $GLOBALS['TT']->setTSlogMessage('Function "' . $funcName . '" was not prepended with "' . $pre . '"', 3);
+               if (!t3lib_div::hasValidClassPrefix($funcName)) {
+                       $GLOBALS['TT']->setTSlogMessage('Function "' . $funcName . '" was not prepended with one of "' . implode(', ', t3lib_div::getValidClassPrefixes()) . '"', 3);
                        return $content;
                }
                        // Split parts