* Fixed bug #8009: Wrong TS-Code because appendString cannot handle multiline strings
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_tsparser.php
index aa97fff..cb41644 100755 (executable)
@@ -2,7 +2,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 1999-2004 Kasper Skaarhoj (kasper@typo3.com)
+*  (c) 1999-2008 Kasper Skaarhoj (kasperYYYY@typo3.com)
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -30,7 +30,7 @@
  * $Id$
  * Revised for TYPO3 3.6 July/2003 by Kasper Skaarhoj
  *
- * @author     Kasper Skaarhoj <kasper@typo3.com>
+ * @author     Kasper Skaarhoj <kasperYYYY@typo3.com>
  */
 /**
  * [CLASS/FUNCTION INDEX of SCRIPT]
  *
  *
  *   80: class t3lib_TSparser
- *  132:     function parse($string,$matchObj='')
+ *  133:     function parse($string,$matchObj='')
  *  169:     function nextDivider()
  *  185:     function parseSub(&$setup)
- *  331:     function rollParseSub($string,&$setup)
- *  355:     function getVal($string,$setup)
- *  381:     function setVal($string,&$setup,$value,$wipeOut=0)
- *  416:     function error($err,$num=2)
- *  428:     function checkIncludeLines($string)
- *  472:     function checkIncludeLines_array($array)
+ *  389:     function rollParseSub($string,&$setup)
+ *  413:     function getVal($string,$setup)
+ *  439:     function setVal($string,&$setup,$value,$wipeOut=0)
+ *  485:     function error($err,$num=2)
+ *  497:     function checkIncludeLines($string)
+ *  541:     function checkIncludeLines_array($array)
  *
  *              SECTION: Syntax highlighting
- *  515:     function doSyntaxHighlight($string,$lineNum='',$highlightBlockMode=0)
- *  536:     function regHighLight($code,$pointer,$strlen=-1)
- *  554:     function syntaxHighlight_print($lineNumDat,$highlightBlockMode)
+ *  584:     function doSyntaxHighlight($string,$lineNum='',$highlightBlockMode=0)
+ *  605:     function regHighLight($code,$pointer,$strlen=-1)
+ *  623:     function syntaxHighlight_print($lineNumDat,$highlightBlockMode)
  *
  * TOTAL FUNCTIONS: 12
  * (This index is automatically created/updated by the extension "extdeveval")
@@ -72,7 +72,7 @@
 /**
  * The TypoScript parser
  *
- * @author     Kasper Skaarhoj <kasper@typo3.com>
+ * @author     Kasper Skaarhoj <kasperYYYY@typo3.com>
  * @package TYPO3
  * @subpackage t3lib
  * @see t3lib_tstemplate, t3lib_matchcondition, t3lib_BEfunc::getPagesTSconfig(), t3lib_userAuthGroup::fetchGroupData(), t3lib_TStemplate::generateConfig()
@@ -99,6 +99,7 @@ class t3lib_TSparser {
 
                // Debugging, analysis:
        var $regComments = 0;                   // DO NOT register the comments. This is default for the ordinary sitetemplate!
+       var $regLinenumbers = 0;                // DO NOT register the linenumbers. This is default for the ordinary sitetemplate!
        var $errors=array();                    // Error accumulation array.
        var $lineNumberOffset=0;                // Used for the error messages line number reporting. Set externally.
        var $breakPointLN=0;                    // Line for break point.
@@ -132,7 +133,6 @@ class t3lib_TSparser {
        function parse($string,$matchObj='')    {
                $this->raw = explode(chr(10),$string);
                $this->rawP = 0;
-
                $pre = '[GLOBAL]';
                while($pre)     {
                        if ($this->breakPointLN && $pre=='[_BREAK]')    {
@@ -183,6 +183,8 @@ class t3lib_TSparser {
         * @return      string          Returns the string of the condition found, the exit signal or possible nothing (if it completed parsing with no interruptions)
         */
        function parseSub(&$setup)      {
+               global $TYPO3_CONF_VARS;
+
                while (isset($this->raw[$this->rawP]))  {
                        $line = ltrim($this->raw[$this->rawP]);
                        $lineP = $this->rawP;
@@ -212,8 +214,11 @@ class t3lib_TSparser {
                                                        if ($this->lastComment && $this->regComments)   {
                                                                $setup[$this->multiLineObject.'..'].=$this->lastComment;
                                                        }
+                                                       if ($this->regLinenumbers)      {
+                                                               $setup[$this->multiLineObject.'.ln..'][]=($this->lineNumberOffset+$this->rawP-1);
+                                                       }
                                                }
-                                       } else{
+                                       } else {
                                                if ($this->syntaxHighLight)     $this->regHighLight("value",$lineP);
                                                $this->multiLineValue[]=$this->raw[($this->rawP-1)];
                                        }
@@ -227,11 +232,12 @@ class t3lib_TSparser {
                                                $this->inBrace=0;
                                                return $line;
                                        } elseif (strcspn($line,'}#/')!=0)      {       // If not brace-end or comment
-                                               $varL = strcspn($line,' {=<>(');                // Find object name string until we meet an operator    VER2: Added '>'!!
+                                               $varL = strcspn($line,' {=<>:(');       // Find object name string until we meet an operator
                                                $objStrName=trim(substr($line,0,$varL));
                                                if ($this->syntaxHighLight)     $this->regHighLight("objstr",$lineP,strlen(substr($line,$varL)));
-                                               if ($objStrName)        {
-                                                       if ($this->strict && eregi('[^[:alnum:]_\.-]',$objStrName,$r))  {
+                                               if (strlen($objStrName)) {
+                                                       $r = array();
+                                                       if ($this->strict && preg_match('/[^[:alnum:]_\.-]/i',$objStrName,$r))  {
                                                                $this->error('Line '.($this->lineNumberOffset+$this->rawP-1).': Object Name String, "'.htmlspecialchars($objStrName).'" contains invalid character "'.$r[0].'". Must be alphanumeric or one of: "_-."');
                                                        } else {
                                                                $line = ltrim(substr($line,$varL));
@@ -242,9 +248,64 @@ class t3lib_TSparser {
                                                                                $this->regHighLight("operator_postspace", $lineP, strlen(ltrim(substr($line,1))));
                                                                        }
                                                                }
+
+                                                                       // Checking for special TSparser properties (to change TS values at parsetime)
+                                                               $match = array();
+                                                               if (preg_match('/^:=([^\(]+)\((.+)\).*/', $line, $match))       {
+                                                                       $tsFunc = trim($match[1]);
+                                                                       $tsFuncArg = $match[2];
+                                                                       list ($currentValue) = $this->getVal($objStrName,$setup);
+
+                                                                       $tsFuncArg = str_replace(
+                                                                               array('\\\\', '\n','\t'),
+                                                                               array('\\', chr(10),chr(9)),
+                                                                               $tsFuncArg
+                                                                       );
+
+                                                                       switch ($tsFunc)        {
+                                                                               case 'prependString':
+                                                                                       $newValue = $tsFuncArg . $currentValue;
+                                                                               break;
+                                                                               case 'appendString':
+                                                                                       $newValue = $currentValue . $tsFuncArg;
+                                                                               break;
+                                                                               case 'removeString':
+                                                                                       $newValue = str_replace($tsFuncArg, '', $currentValue);
+                                                                               break;
+                                                                               case 'replaceString':
+                                                                                       list($fromStr,$toStr) = explode('|', $tsFuncArg, 2);
+                                                                                       $newValue = str_replace($fromStr, $toStr, $currentValue);
+                                                                               break;
+                                                                               case 'addToList':
+                                                                                       $newValue = (strcmp('',$currentValue) ? $currentValue.',' : '') . trim($tsFuncArg);
+                                                                               break;
+                                                                               case 'removeFromList':
+                                                                                       $existingElements = t3lib_div::trimExplode(',',$currentValue);
+                                                                                       $removeElements = t3lib_div::trimExplode(',',$tsFuncArg);
+                                                                                       if (count($removeElements))     {
+                                                                                               $newValue = implode(',', array_diff($existingElements, $removeElements));
+                                                                                       }
+                                                                               break;
+                                                                               default:
+                                                                                       if (isset($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc'][$tsFunc]))   {
+                                                                                               $hookMethod = $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc'][$tsFunc];
+                                                                                               $params = array('currentValue'=>$currentValue, 'functionArgument'=>$tsFuncArg);
+                                                                                               $fakeThis = FALSE;
+                                                                                               $newValue = t3lib_div::callUserFunction($hookMethod,$params,$fakeThis);
+                                                                                       } else {
+                                                                                               t3lib_div::sysLog('Missing function definition for '.$tsFunc.' on TypoScript line '.$lineP,'Core',2);
+                                                                                       }
+                                                                       }
+
+                                                                       if (isset($newValue))   {
+                                                                               $line = '= '.$newValue;
+                                                                       }
+                                                               }
+
                                                                switch(substr($line,0,1))       {
                                                                        case '=':
-                                                                               if ($this->syntaxHighLight)     $this->regHighLight("value", $lineP, strlen(ltrim(substr($line,1)))-strlen(trim(substr($line,1))));
+                                                                               if ($this->syntaxHighLight)     $this->regHighLight('value', $lineP, strlen(ltrim(substr($line,1)))-strlen(trim(substr($line,1))));
+
                                                                                if (strstr($objStrName,'.'))    {
                                                                                        $value = Array();
                                                                                        $value[0] = trim(substr($line,1));
@@ -254,6 +315,9 @@ class t3lib_TSparser {
                                                                                        if ($this->lastComment && $this->regComments)   {       // Setting comment..
                                                                                                $setup[$objStrName.'..'].=$this->lastComment;
                                                                                        }
+                                                                                       if ($this->regLinenumbers)      {
+                                                                                               $setup[$objStrName.'.ln..'][]=($this->lineNumberOffset+$this->rawP-1);
+                                                                                       }
                                                                                }
                                                                        break;
                                                                        case '{':
@@ -280,14 +344,14 @@ class t3lib_TSparser {
                                                                                } else {
                                                                                        $res = $this->getVal($theVal,$this->setup);
                                                                                }
-                                                                               $this->setVal($objStrName,$setup,unserialize(serialize($res)),1);
+                                                                               $this->setVal($objStrName,$setup,unserialize(serialize($res)),1);       // unserialize(serialize(...)) may look stupid but is needed because of some reference issues. See Kaspers reply to "[TYPO3-core] good question" from December 15 2005.
                                                                        break;
                                                                        case '>':
                                                                                if ($this->syntaxHighLight)     $this->regHighLight("value_unset", $lineP, strlen(ltrim(substr($line,1)))-strlen(trim(substr($line,1))));
                                                                                $this->setVal($objStrName,$setup,'UNSET');
                                                                        break;
                                                                        default:
-                                                                               $this->error('Line '.($this->lineNumberOffset+$this->rawP-1).': Object Name String, "'.htmlspecialchars($objStrName).'" was not preceeded by any operator, =<>({');
+                                                                               $this->error('Line '.($this->lineNumberOffset+$this->rawP-1).': Object Name String, "'.htmlspecialchars($objStrName).'" was not preceded by any operator, =<>({');
                                                                        break;
                                                                }
                                                        }
@@ -385,17 +449,28 @@ class t3lib_TSparser {
                                if ($value=='UNSET')    {
                                        unset($setup[$string]);
                                        unset($setup[$string.'.']);
+                                       if ($this->regLinenumbers)      {
+                                               $setup[$string.'.ln..'][]=($this->lineNumberOffset+$this->rawP-1).'>';
+                                       }
                                } else {
+                                       $lnRegisDone=0;
                                        if ($wipeOut && $this->strict)  {
                                                if ((isset($setup[$string]) && !isset($value[0])) || (isset($setup[$string.'.']) && !isset($value[1]))) {$this->error('Line '.($this->lineNumberOffset+$this->rawP-1).': Object copied in this line "'.trim($this->raw[($this->rawP-1)]).'" would leave either the value or properties untouched in TypoScript Version 1. Please check that this is not a problem for you.',1);}
                                                unset($setup[$string]);
                                                unset($setup[$string.'.']);
+                                               if ($this->regLinenumbers)      {
+                                                       $setup[$string.'.ln..'][]=($this->lineNumberOffset+$this->rawP-1).'<';
+                                                       $lnRegisDone=1;
+                                               }
                                        }
                                        if (isset($value[0])) {$setup[$string] = $value[0];}
                                        if (isset($value[1])) {$setup[$string.'.'] = $value[1];}
                                        if ($this->lastComment && $this->regComments)   {
                                                $setup[$string.'..'].=$this->lastComment;
                                        }
+                                       if ($this->regLinenumbers && !$lnRegisDone)     {
+                                               $setup[$string.'.ln..'][]=($this->lineNumberOffset+$this->rawP-1);
+                                       }
                                }
                        } else {
                                $key = substr($string,0,$keyLen).'.';
@@ -423,9 +498,15 @@ class t3lib_TSparser {
         * Use: t3lib_TSparser::checkIncludeLines()
         *
         * @param       string          Unparsed TypoScript
+        * @param       integer         Counter for detecting endless loops
         * @return      string          Complete TypoScript with includes added.
+        * @static
         */
-       function checkIncludeLines($string)     {
+       function checkIncludeLines($string, $cycle_counter=1)   {
+               if ($cycle_counter>100) {
+                       t3lib_div::sysLog('It appears like TypoScript code is looping over itself. Check your templates for "&lt;INCLUDE_TYPOSCRIPT: ..." tags','Core',2);
+                       return '';
+               }
                $splitStr='<INCLUDE_TYPOSCRIPT:';
                if (strstr($string,$splitStr))  {
                        $newString='';
@@ -434,9 +515,9 @@ class t3lib_TSparser {
                        while(list($c,$v)=each($allParts))      {
                                if (!$c)        {        // first goes through
                                        $newString.=$v;
-                               } elseif (ereg("\r?\n[ ]*$",$allParts[$c-1]))   {       // There must be a line-break char before.
+                               } elseif (preg_match('/\r?\n\s*$/',$allParts[$c-1]))    {       // There must be a line-break char before.
                                        $subparts=explode('>',$v,2);
-                                       if (ereg("^[ ]*\r?\n",$subparts[1]))    {       // There must be a line-break char after
+                                       if (preg_match('/^\s*\r?\n/',$subparts[1]))     {       // There must be a line-break char after
                                                        // SO, the include was positively recognized:
                                                $newString.='### '.$splitStr.$subparts[0].'> BEGIN:'.chr(10);
                                                $params = t3lib_div::get_tag_attributes($subparts[0]);
@@ -447,7 +528,9 @@ class t3lib_TSparser {
                                                                        $filename = t3lib_div::getFileAbsFileName(trim($sourceParts[1]));
                                                                        if (strcmp($filename,''))       {       // Must exist and must not contain '..' and must be relative
                                                                                if (@is_file($filename) && filesize($filename)<100000)  {       // Max. 100 KB include files!
-                                                                                       $newString.=t3lib_div::getUrl($filename).chr(10);
+                                                                                               // check for includes in included text
+                                                                                       $included_text = self::checkIncludeLines(t3lib_div::getUrl($filename),$cycle_counter+1);
+                                                                                       $newString.= $included_text.chr(10);
                                                                                }
                                                                        }
                                                                break;
@@ -505,7 +588,7 @@ class t3lib_TSparser {
 
        /**
         * Syntax highlight a TypoScript text
-        * Will parse the content. Remember, the internal setup array may contain INvalid parsed content since conditions are ignored!
+        * Will parse the content. Remember, the internal setup array may contain invalid parsed content since conditions are ignored!
         *
         * @param       string          The TypoScript text
         * @param       mixed           If blank, linenumbers are NOT printed. If array then the first key is the linenumber offset to add to the internal counter.
@@ -603,4 +686,4 @@ class t3lib_TSparser {
 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tsparser.php']) {
        include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tsparser.php']);
 }
-?>
\ No newline at end of file
+?>