|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com) 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 * Contains the TypoScript parser class 00029 * 00030 * $Id: class.t3lib_tsparser.php 10663 2011-02-28 19:33:43Z lolli $ 00031 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj 00032 * 00033 * @author Kasper Skårhøj <kasperYYYY@typo3.com> 00034 */ 00035 /** 00036 * [CLASS/FUNCTION INDEX of SCRIPT] 00037 * 00038 * 00039 * 00040 * 80: class t3lib_TSparser 00041 * 133: function parse($string,$matchObj='') 00042 * 169: function nextDivider() 00043 * 185: function parseSub(&$setup) 00044 * 389: function rollParseSub($string,&$setup) 00045 * 413: function getVal($string,$setup) 00046 * 439: function setVal($string,&$setup,$value,$wipeOut=0) 00047 * 485: function error($err,$num=2) 00048 * 497: function checkIncludeLines($string, $cycle_counter=1, $returnFiles=false) 00049 * 541: function checkIncludeLines_array($array) 00050 * 00051 * SECTION: Syntax highlighting 00052 * 584: function doSyntaxHighlight($string,$lineNum='',$highlightBlockMode=0) 00053 * 605: function regHighLight($code,$pointer,$strlen=-1) 00054 * 623: function syntaxHighlight_print($lineNumDat,$highlightBlockMode) 00055 * 00056 * TOTAL FUNCTIONS: 12 00057 * (This index is automatically created/updated by the extension "extdeveval") 00058 * 00059 */ 00060 00061 00062 /** 00063 * The TypoScript parser 00064 * 00065 * @author Kasper Skårhøj <kasperYYYY@typo3.com> 00066 * @package TYPO3 00067 * @subpackage t3lib 00068 * @see t3lib_tstemplate, t3lib_BEfunc::getPagesTSconfig(), t3lib_userAuthGroup::fetchGroupData(), t3lib_TStemplate::generateConfig() 00069 */ 00070 class t3lib_TSparser { 00071 var $strict = 1; // If set, then key names cannot contain characters other than [:alnum:]_\.- 00072 00073 // Internal 00074 var $setup = array(); // TypoScript hierarchy being build during parsing. 00075 var $raw; // raw data, the input string exploded by LF 00076 var $rawP; // pointer to entry in raw data array 00077 var $lastComment = ''; // Holding the value of the last comment 00078 var $commentSet = 0; // Internally set, used as internal flag to create a multi-line comment (one of those like /*... */) 00079 var $multiLineEnabled = 0; // Internally set, when multiline value is accumulated 00080 var $multiLineObject = ''; // Internally set, when multiline value is accumulated 00081 var $multiLineValue = array(); // Internally set, when multiline value is accumulated 00082 var $inBrace = 0; // Internally set, when in brace. Counter. 00083 var $lastConditionTrue = 1; // For each condition this flag is set, if the condition is true, else it's cleared. Then it's used by the [ELSE] condition to determine if the next part should be parsed. 00084 var $sections = array(); // Tracking all conditions found 00085 var $sectionsMatch = array(); // Tracking all matching conditions found 00086 var $syntaxHighLight = 0; // If set, then syntax highlight mode is on; Call the function syntaxHighlight() to use this function 00087 var $highLightData = array(); // Syntax highlight data is accumulated in this array. Used by syntaxHighlight_print() to construct the output. 00088 var $highLightData_bracelevel = array(); // Syntax highlight data keeping track of the curly brace level for each line 00089 00090 // Debugging, analysis: 00091 var $regComments = 0; // DO NOT register the comments. This is default for the ordinary sitetemplate! 00092 var $regLinenumbers = 0; // DO NOT register the linenumbers. This is default for the ordinary sitetemplate! 00093 var $errors = array(); // Error accumulation array. 00094 var $lineNumberOffset = 0; // Used for the error messages line number reporting. Set externally. 00095 var $breakPointLN = 0; // Line for break point. 00096 var $highLightStyles = array( 00097 'prespace' => array('<span class="ts-prespace">', '</span>'), // Space before any content on a line 00098 'objstr_postspace' => array('<span class="ts-objstr_postspace">', '</span>'), // Space after the object string on a line 00099 'operator_postspace' => array('<span class="ts-operator_postspace">', '</span>'), // Space after the operator on a line 00100 'operator' => array('<span class="ts-operator">', '</span>'), // The operator char 00101 'value' => array('<span class="ts-value">', '</span>'), // The value of a line 00102 'objstr' => array('<span class="ts-objstr">', '</span>'), // The object string of a line 00103 'value_copy' => array('<span class="ts-value_copy">', '</span>'), // The value when the copy syntax (<) is used; that means the object reference 00104 'value_unset' => array('<span class="ts-value_unset">', '</span>'), // The value when an object is unset. Should not exist. 00105 'ignored' => array('<span class="ts-ignored">', '</span>'), // The "rest" of a line which will be ignored. 00106 'default' => array('<span class="ts-default">', '</span>'), // The default style if none other is applied. 00107 'comment' => array('<span class="ts-comment">', '</span>'), // Comment lines 00108 'condition' => array('<span class="ts-condition">', '</span>'), // Conditions 00109 'error' => array('<span class="ts-error">', '</span>'), // Error messages 00110 'linenum' => array('<span class="ts-linenum">', '</span>'), // Line numbers 00111 ); 00112 var $highLightBlockStyles = ''; // Additional attributes for the <span> tags for a blockmode line 00113 var $highLightBlockStyles_basecolor = '#cccccc'; // The hex-HTML color for the blockmode 00114 00115 public $parentObject; //Instance of parentObject, used by t3lib_tsparser_ext 00116 00117 /** 00118 * Start parsing the input TypoScript text piece. The result is stored in $this->setup 00119 * 00120 * @param string The TypoScript text 00121 * @param object If is object, then this is used to match conditions found in the TypoScript code. If matchObj not specified, then no conditions will work! (Except [GLOBAL]) 00122 * @return void 00123 */ 00124 function parse($string, $matchObj = '') { 00125 $this->raw = explode(LF, $string); 00126 $this->rawP = 0; 00127 $pre = '[GLOBAL]'; 00128 while ($pre) { 00129 if ($this->breakPointLN && $pre == '[_BREAK]') { 00130 $this->error('Breakpoint at ' . ($this->lineNumberOffset + $this->rawP - 2) . ': Line content was "' . $this->raw[$this->rawP - 2] . '"', 1); 00131 break; 00132 } 00133 00134 if (strtoupper($pre) == '[GLOBAL]' || strtoupper($pre) == '[END]' || (!$this->lastConditionTrue && strtoupper($pre) == '[ELSE]')) { 00135 $pre = trim($this->parseSub($this->setup)); 00136 $this->lastConditionTrue = 1; 00137 } else { 00138 if (strtoupper($pre) != '[ELSE]') { 00139 $this->sections[md5($pre)] = $pre; 00140 } // we're in a specific section. Therefore we log this section 00141 if ((is_object($matchObj) && $matchObj->match($pre)) || $this->syntaxHighLight) { 00142 if (strtoupper($pre) != '[ELSE]') { 00143 $this->sectionsMatch[md5($pre)] = $pre; 00144 } 00145 $pre = trim($this->parseSub($this->setup)); 00146 $this->lastConditionTrue = 1; 00147 } else { 00148 $pre = trim($this->nextDivider()); 00149 $this->lastConditionTrue = 0; 00150 } 00151 } 00152 } 00153 if ($this->inBrace) { 00154 $this->error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': The script is short of ' . $this->inBrace . ' end brace(s)', 1); 00155 } 00156 if ($this->multiLineEnabled) { 00157 $this->error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': A multiline value section is not ended with a parenthesis!', 1); 00158 } 00159 $this->lineNumberOffset += count($this->raw) + 1; 00160 } 00161 00162 /** 00163 * Will search for the next condition. When found it will return the line content (the condition value) and have advanced the internal $this->rawP pointer to point to the next line after the condition. 00164 * 00165 * @return string The condition value 00166 * @see parse() 00167 */ 00168 function nextDivider() { 00169 while (isset($this->raw[$this->rawP])) { 00170 $line = ltrim($this->raw[$this->rawP]); 00171 $this->rawP++; 00172 if ($line && substr($line, 0, 1) == '[') { 00173 return $line; 00174 } 00175 } 00176 } 00177 00178 /** 00179 * Parsing the $this->raw TypoScript lines from pointer, $this->rawP 00180 * 00181 * @param array Reference to the setup array in which to accumulate the values. 00182 * @return string Returns the string of the condition found, the exit signal or possible nothing (if it completed parsing with no interruptions) 00183 */ 00184 function parseSub(&$setup) { 00185 global $TYPO3_CONF_VARS; 00186 00187 while (isset($this->raw[$this->rawP])) { 00188 $line = ltrim($this->raw[$this->rawP]); 00189 $lineP = $this->rawP; 00190 $this->rawP++; 00191 if ($this->syntaxHighLight) { 00192 $this->regHighLight("prespace", $lineP, strlen($line)); 00193 } 00194 00195 // Breakpoint? 00196 if ($this->breakPointLN && ($this->lineNumberOffset + $this->rawP - 1) == ($this->breakPointLN + 1)) { // by adding 1 we get that line processed 00197 return '[_BREAK]'; 00198 } 00199 00200 // Set comment flag? 00201 if (!$this->multiLineEnabled && substr($line, 0, 2) == '/*') { 00202 $this->commentSet = 1; 00203 } 00204 00205 if (!$this->commentSet && ($line || $this->multiLineEnabled)) { // If $this->multiLineEnabled we will go and get the line values here because we know, the first if() will be true. 00206 if ($this->multiLineEnabled) { // If multiline is enabled. Escape by ')' 00207 if (substr($line, 0, 1) == ')') { // Multiline ends... 00208 if ($this->syntaxHighLight) { 00209 $this->regHighLight("operator", $lineP, strlen($line) - 1); 00210 } 00211 $this->multiLineEnabled = 0; // Disable multiline 00212 $theValue = implode($this->multiLineValue, LF); 00213 if (strstr($this->multiLineObject, '.')) { 00214 $this->setVal($this->multiLineObject, $setup, array($theValue)); // Set the value deeper. 00215 } else { 00216 $setup[$this->multiLineObject] = $theValue; // Set value regularly 00217 if ($this->lastComment && $this->regComments) { 00218 $setup[$this->multiLineObject . '..'] .= $this->lastComment; 00219 } 00220 if ($this->regLinenumbers) { 00221 $setup[$this->multiLineObject . '.ln..'][] = ($this->lineNumberOffset + $this->rawP - 1); 00222 } 00223 } 00224 } else { 00225 if ($this->syntaxHighLight) { 00226 $this->regHighLight("value", $lineP); 00227 } 00228 $this->multiLineValue[] = $this->raw[($this->rawP - 1)]; 00229 } 00230 } elseif ($this->inBrace == 0 && substr($line, 0, 1) == '[') { // Beginning of condition (only on level zero compared to brace-levels 00231 if ($this->syntaxHighLight) { 00232 $this->regHighLight("condition", $lineP); 00233 } 00234 return $line; 00235 } else { 00236 if (substr($line, 0, 1) == '[' && strtoupper(trim($line)) == '[GLOBAL]') { // Return if GLOBAL condition is set - no matter what. 00237 if ($this->syntaxHighLight) { 00238 $this->regHighLight("condition", $lineP); 00239 } 00240 $this->error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': On return to [GLOBAL] scope, the script was short of ' . $this->inBrace . ' end brace(s)', 1); 00241 $this->inBrace = 0; 00242 return $line; 00243 } elseif (strcspn($line, '}#/') != 0) { // If not brace-end or comment 00244 $varL = strcspn($line, ' {=<>:('); // Find object name string until we meet an operator 00245 $objStrName = trim(substr($line, 0, $varL)); 00246 if ($this->syntaxHighLight) { 00247 $this->regHighLight("objstr", $lineP, strlen(substr($line, $varL))); 00248 } 00249 if (strlen($objStrName)) { 00250 $r = array(); 00251 if ($this->strict && preg_match('/[^[:alnum:]_\.-]/i', $objStrName, $r)) { 00252 $this->error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': Object Name String, "' . htmlspecialchars($objStrName) . '" contains invalid character "' . $r[0] . '". Must be alphanumeric or one of: "_-."'); 00253 } else { 00254 $line = ltrim(substr($line, $varL)); 00255 if ($this->syntaxHighLight) { 00256 $this->regHighLight("objstr_postspace", $lineP, strlen($line)); 00257 if (strlen($line) > 0) { 00258 $this->regHighLight("operator", $lineP, strlen($line) - 1); 00259 $this->regHighLight("operator_postspace", $lineP, strlen(ltrim(substr($line, 1)))); 00260 } 00261 } 00262 00263 // Checking for special TSparser properties (to change TS values at parsetime) 00264 $match = array(); 00265 if (preg_match('/^:=([^\(]+)\((.+)\).*/', $line, $match)) { 00266 $tsFunc = trim($match[1]); 00267 $tsFuncArg = $match[2]; 00268 list ($currentValue) = $this->getVal($objStrName, $setup); 00269 00270 $tsFuncArg = str_replace( 00271 array('\\\\', '\n', '\t'), 00272 array('\\', LF, TAB), 00273 $tsFuncArg 00274 ); 00275 00276 switch ($tsFunc) { 00277 case 'prependString': 00278 $newValue = $tsFuncArg . $currentValue; 00279 break; 00280 case 'appendString': 00281 $newValue = $currentValue . $tsFuncArg; 00282 break; 00283 case 'removeString': 00284 $newValue = str_replace($tsFuncArg, '', $currentValue); 00285 break; 00286 case 'replaceString': 00287 list($fromStr, $toStr) = explode('|', $tsFuncArg, 2); 00288 $newValue = str_replace($fromStr, $toStr, $currentValue); 00289 break; 00290 case 'addToList': 00291 $newValue = (strcmp('', $currentValue) ? $currentValue . ',' : '') . trim($tsFuncArg); 00292 break; 00293 case 'removeFromList': 00294 $existingElements = t3lib_div::trimExplode(',', $currentValue); 00295 $removeElements = t3lib_div::trimExplode(',', $tsFuncArg); 00296 if (count($removeElements)) { 00297 $newValue = implode(',', array_diff($existingElements, $removeElements)); 00298 } 00299 break; 00300 default: 00301 if (isset($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc'][$tsFunc])) { 00302 $hookMethod = $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc'][$tsFunc]; 00303 $params = array('currentValue' => $currentValue, 'functionArgument' => $tsFuncArg); 00304 $fakeThis = FALSE; 00305 $newValue = t3lib_div::callUserFunction($hookMethod, $params, $fakeThis); 00306 } else { 00307 t3lib_div::sysLog('Missing function definition for ' . $tsFunc . ' on TypoScript line ' . $lineP, 'Core', 2); 00308 } 00309 } 00310 00311 if (isset($newValue)) { 00312 $line = '= ' . $newValue; 00313 } 00314 } 00315 00316 switch (substr($line, 0, 1)) { 00317 case '=': 00318 if ($this->syntaxHighLight) { 00319 $this->regHighLight('value', $lineP, strlen(ltrim(substr($line, 1))) - strlen(trim(substr($line, 1)))); 00320 } 00321 00322 if (strstr($objStrName, '.')) { 00323 $value = array(); 00324 $value[0] = trim(substr($line, 1)); 00325 $this->setVal($objStrName, $setup, $value); 00326 } else { 00327 $setup[$objStrName] = trim(substr($line, 1)); 00328 if ($this->lastComment && $this->regComments) { // Setting comment.. 00329 $setup[$objStrName . '..'] .= $this->lastComment; 00330 } 00331 if ($this->regLinenumbers) { 00332 $setup[$objStrName . '.ln..'][] = ($this->lineNumberOffset + $this->rawP - 1); 00333 } 00334 } 00335 break; 00336 case '{': 00337 $this->inBrace++; 00338 if (strstr($objStrName, '.')) { 00339 $exitSig = $this->rollParseSub($objStrName, $setup); 00340 if ($exitSig) { 00341 return $exitSig; 00342 } 00343 } else { 00344 if (!isset($setup[$objStrName . '.'])) { 00345 $setup[$objStrName . '.'] = array(); 00346 } 00347 $exitSig = $this->parseSub($setup[$objStrName . '.']); 00348 if ($exitSig) { 00349 return $exitSig; 00350 } 00351 } 00352 break; 00353 case '(': 00354 $this->multiLineObject = $objStrName; 00355 $this->multiLineEnabled = 1; 00356 $this->multiLineValue = array(); 00357 break; 00358 case '<': 00359 if ($this->syntaxHighLight) { 00360 $this->regHighLight("value_copy", $lineP, strlen(ltrim(substr($line, 1))) - strlen(trim(substr($line, 1)))); 00361 } 00362 $theVal = trim(substr($line, 1)); 00363 if (substr($theVal, 0, 1) == '.') { 00364 $res = $this->getVal(substr($theVal, 1), $setup); 00365 } else { 00366 $res = $this->getVal($theVal, $this->setup); 00367 } 00368 $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. 00369 break; 00370 case '>': 00371 if ($this->syntaxHighLight) { 00372 $this->regHighLight("value_unset", $lineP, strlen(ltrim(substr($line, 1))) - strlen(trim(substr($line, 1)))); 00373 } 00374 $this->setVal($objStrName, $setup, 'UNSET'); 00375 break; 00376 default: 00377 $this->error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': Object Name String, "' . htmlspecialchars($objStrName) . '" was not preceded by any operator, =<>({'); 00378 break; 00379 } 00380 } 00381 $this->lastComment = ''; 00382 } 00383 } elseif (substr($line, 0, 1) == '}') { 00384 $this->inBrace--; 00385 $this->lastComment = ''; 00386 if ($this->syntaxHighLight) { 00387 $this->regHighLight("operator", $lineP, strlen($line) - 1); 00388 } 00389 if ($this->inBrace < 0) { 00390 $this->error('Line ' . ($this->lineNumberOffset + $this->rawP - 1) . ': An end brace is in excess.', 1); 00391 $this->inBrace = 0; 00392 } else { 00393 break; 00394 } 00395 } else { 00396 if ($this->syntaxHighLight) { 00397 $this->regHighLight("comment", $lineP); 00398 } 00399 00400 // Comment. The comments are concatenated in this temporary string: 00401 if ($this->regComments) { 00402 $this->lastComment .= trim($line) . LF; 00403 } 00404 } 00405 } 00406 } 00407 00408 // Unset comment 00409 if ($this->commentSet) { 00410 if ($this->syntaxHighLight) { 00411 $this->regHighLight("comment", $lineP); 00412 } 00413 if (substr($line, 0, 2) == '*/') { 00414 $this->commentSet = 0; 00415 } 00416 } 00417 } 00418 } 00419 00420 /** 00421 * Parsing of TypoScript keys inside a curly brace where the key is composite of at least two keys, thus having to recursively call itself to get the value 00422 * 00423 * @param string The object sub-path, eg "thisprop.another_prot" 00424 * @param array The local setup array from the function calling this function 00425 * @return string Returns the exitSignal 00426 * @see parseSub() 00427 */ 00428 function rollParseSub($string, &$setup) { 00429 if ((string) $string != '') { 00430 $keyLen = strcspn($string, '.'); 00431 if ($keyLen == strlen($string)) { 00432 $key = $string . '.'; 00433 if (!isset($setup[$key])) { 00434 $setup[$key] = array(); 00435 } 00436 $exitSig = $this->parseSub($setup[$key]); 00437 if ($exitSig) { 00438 return $exitSig; 00439 } 00440 } else { 00441 $key = substr($string, 0, $keyLen) . '.'; 00442 if (!isset($setup[$key])) { 00443 $setup[$key] = array(); 00444 } 00445 $exitSig = $this->rollParseSub(substr($string, $keyLen + 1), $setup[$key]); 00446 if ($exitSig) { 00447 return $exitSig; 00448 } 00449 } 00450 } 00451 } 00452 00453 /** 00454 * Get a value/property pair for an object path in TypoScript, eg. "myobject.myvalue.mysubproperty". Here: Used by the "copy" operator, < 00455 * 00456 * @param string Object path for which to get the value 00457 * @param array Global setup code if $string points to a global object path. But if string is prefixed with "." then its the local setup array. 00458 * @return array An array with keys 0/1 being value/property respectively 00459 */ 00460 function getVal($string, $setup) { 00461 if ((string) $string != '') { 00462 $keyLen = strcspn($string, '.'); 00463 if ($keyLen == strlen($string)) { 00464 $retArr = array(); // Added 6/6/03. Shouldn't hurt 00465 if (isset($setup[$string])) { 00466 $retArr[0] = $setup[$string]; 00467 } 00468 if (isset($setup[$string . '.'])) { 00469 $retArr[1] = $setup[$string . '.']; 00470 } 00471 return $retArr; 00472 } else { 00473 $key = substr($string, 0, $keyLen) . '.'; 00474 if ($setup[$key]) { 00475 return $this->getVal(substr($string, $keyLen + 1), $setup[$key]); 00476 } 00477 } 00478 } 00479 } 00480 00481 /** 00482 * Setting a value/property of an object string in the setup array. 00483 * 00484 * @param string The object sub-path, eg "thisprop.another_prot" 00485 * @param array The local setup array from the function calling this function. 00486 * @param array The value/property pair array to set. If only one of them is set, then the other is not touched (unless $wipeOut is set, which it is when copies are made which must include both value and property) 00487 * @param boolean If set, then both value and property is wiped out when a copy is made of another value. 00488 * @return void 00489 */ 00490 function setVal($string, &$setup, $value, $wipeOut = 0) { 00491 if ((string) $string != '') { 00492 $keyLen = strcspn($string, '.'); 00493 if ($keyLen == strlen($string)) { 00494 if ($value == 'UNSET') { 00495 unset($setup[$string]); 00496 unset($setup[$string . '.']); 00497 if ($this->regLinenumbers) { 00498 $setup[$string . '.ln..'][] = ($this->lineNumberOffset + $this->rawP - 1) . '>'; 00499 } 00500 } else { 00501 $lnRegisDone = 0; 00502 if ($wipeOut && $this->strict) { 00503 if ((isset($setup[$string]) && !isset($value[0])) || (isset($setup[$string . '.']) && !isset($value[1]))) { 00504 $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); 00505 } 00506 unset($setup[$string]); 00507 unset($setup[$string . '.']); 00508 if ($this->regLinenumbers) { 00509 $setup[$string . '.ln..'][] = ($this->lineNumberOffset + $this->rawP - 1) . '<'; 00510 $lnRegisDone = 1; 00511 } 00512 } 00513 if (isset($value[0])) { 00514 $setup[$string] = $value[0]; 00515 } 00516 if (isset($value[1])) { 00517 $setup[$string . '.'] = $value[1]; 00518 } 00519 if ($this->lastComment && $this->regComments) { 00520 $setup[$string . '..'] .= $this->lastComment; 00521 } 00522 if ($this->regLinenumbers && !$lnRegisDone) { 00523 $setup[$string . '.ln..'][] = ($this->lineNumberOffset + $this->rawP - 1); 00524 } 00525 } 00526 } else { 00527 $key = substr($string, 0, $keyLen) . '.'; 00528 if (!isset($setup[$key])) { 00529 $setup[$key] = array(); 00530 } 00531 $this->setVal(substr($string, $keyLen + 1), $setup[$key], $value); 00532 } 00533 } 00534 } 00535 00536 /** 00537 * Stacks errors/messages from the TypoScript parser into an internal array, $this->error 00538 * If "TT" is a global object (as it is in the frontend when backend users are logged in) the message will be registered here as well. 00539 * 00540 * @param string The error message string 00541 * @param integer The error severity (in the scale of $GLOBALS['TT']->setTSlogMessage: Approx: 2=warning, 1=info, 0=nothing, 3=fatal.) 00542 * @return void 00543 */ 00544 function error($err, $num = 2) { 00545 if (is_object($GLOBALS['TT'])) { 00546 $GLOBALS['TT']->setTSlogMessage($err, $num); 00547 } 00548 $this->errors[] = array($err, $num, $this->rawP - 1, $this->lineNumberOffset); 00549 } 00550 00551 /** 00552 * Checks the input string (un-parsed TypoScript) for include-commands ("<INCLUDE_TYPOSCRIPT: ....") 00553 * Use: t3lib_TSparser::checkIncludeLines() 00554 * 00555 * @param string Unparsed TypoScript 00556 * @param integer Counter for detecting endless loops 00557 * @param boolean When set an array containing the resulting typoscript and all included files will get returned 00558 * @return string Complete TypoScript with includes added. 00559 * @static 00560 */ 00561 function checkIncludeLines($string, $cycle_counter = 1, $returnFiles = FALSE) { 00562 $includedFiles = array(); 00563 if ($cycle_counter > 100) { 00564 t3lib_div::sysLog('It appears like TypoScript code is looping over itself. Check your templates for "<INCLUDE_TYPOSCRIPT: ..." tags', 'Core', 2); 00565 if ($returnFiles) { 00566 return array( 00567 'typoscript' => '', 00568 'files' => $includedFiles, 00569 ); 00570 } 00571 return "\n###\n### ERROR: Recursion!\n###\n"; 00572 } 00573 $splitStr = '<INCLUDE_TYPOSCRIPT:'; 00574 if (strstr($string, $splitStr)) { 00575 $newString = ''; 00576 $allParts = explode($splitStr, LF . $string . LF); // adds line break char before/after 00577 foreach ($allParts as $c => $v) { 00578 if (!$c) { // first goes through 00579 $newString .= $v; 00580 } elseif (preg_match('/\r?\n\s*$/', $allParts[$c - 1])) { // There must be a line-break char before. 00581 $subparts = explode('>', $v, 2); 00582 if (preg_match('/^\s*\r?\n/', $subparts[1])) { // There must be a line-break char after 00583 // SO, the include was positively recognized: 00584 $newString .= '### ' . $splitStr . $subparts[0] . '> BEGIN:' . LF; 00585 $params = t3lib_div::get_tag_attributes($subparts[0]); 00586 if ($params['source']) { 00587 $sourceParts = explode(':', $params['source'], 2); 00588 switch (strtolower(trim($sourceParts[0]))) { 00589 case 'file': 00590 $filename = t3lib_div::getFileAbsFileName(trim($sourceParts[1])); 00591 if (strcmp($filename, '')) { // Must exist and must not contain '..' and must be relative 00592 if (t3lib_div::verifyFilenameAgainstDenyPattern($filename)) { // Check for allowed files 00593 if (@is_file($filename) && filesize($filename) < 100000) { // Max. 100 KB include files! 00594 // check for includes in included text 00595 $includedFiles[] = $filename; 00596 $included_text = self::checkIncludeLines(t3lib_div::getUrl($filename), $cycle_counter + 1, $returnFiles); 00597 // If the method also has to return all included files, merge currently included 00598 // files with files included by recursively calling itself 00599 if ($returnFiles && is_array($included_text)) { 00600 $includedFiles = array_merge($includedFiles, $included_text['files']); 00601 $included_text = $included_text['typoscript']; 00602 } 00603 $newString .= $included_text . LF; 00604 } 00605 } else { 00606 t3lib_div::sysLog('File "' . $filename . '" was not included since it is not allowed due to fileDenyPattern', 'Core', 2); 00607 } 00608 } 00609 break; 00610 } 00611 } 00612 $newString .= '### ' . $splitStr . $subparts[0] . '> END:' . LF; 00613 $newString .= $subparts[1]; 00614 } else { 00615 $newString .= $splitStr . $v; 00616 } 00617 } else { 00618 $newString .= $splitStr . $v; 00619 } 00620 } 00621 $string = substr($newString, 1, -1); // not the first/last linebreak char. 00622 } 00623 // When all included files should get returned, simply return an compound array containing 00624 // the TypoScript with all "includes" processed and the files which got included 00625 if ($returnFiles) { 00626 return array( 00627 'typoscript' => $string, 00628 'files' => $includedFiles, 00629 ); 00630 } 00631 return $string; 00632 } 00633 00634 /** 00635 * Parses the string in each value of the input array for include-commands 00636 * 00637 * @param array Array with TypoScript in each value 00638 * @return array Same array but where the values has been parsed for include-commands 00639 */ 00640 function checkIncludeLines_array($array) { 00641 foreach ($array as $k => $v) { 00642 $array[$k] = t3lib_TSparser::checkIncludeLines($array[$k]); 00643 } 00644 return $array; 00645 } 00646 00647 /** 00648 * Search for commented INCLUDE_TYPOSCRIPT statements 00649 * and save the content between the BEGIN and the END line to the specified file 00650 * 00651 * @param string template content 00652 * @param int Counter for detecting endless loops 00653 * @return string template content with uncommented include statements 00654 * @author Fabrizio Branca <typo3@fabrizio-branca.de> 00655 */ 00656 function extractIncludes($string, $cycle_counter = 1, $extractedFileNames = array()) { 00657 00658 if ($cycle_counter > 10) { 00659 t3lib_div::sysLog('It appears like TypoScript code is looping over itself. Check your templates for "<INCLUDE_TYPOSCRIPT: ..." tags', 'Core', 2); 00660 return "\n###\n### ERROR: Recursion!\n###\n"; 00661 } 00662 00663 $fileContent = array(); 00664 $restContent = array(); 00665 $fileName = NULL; 00666 $inIncludePart = FALSE; 00667 $lines = explode("\n", $string); 00668 $skipNextLineIfEmpty = FALSE; 00669 $openingCommentedIncludeStatement = NULL; 00670 foreach ($lines as $line) { 00671 00672 // t3lib_TSparser::checkIncludeLines inserts an additional empty line, remove this again 00673 if ($skipNextLineIfEmpty) { 00674 if (trim($line) == '') { 00675 continue; 00676 } 00677 $skipNextLineIfEmpty = FALSE; 00678 } 00679 00680 if (!$inIncludePart) { // outside commented include statements 00681 00682 // search for beginning commented include statements 00683 $matches = array(); 00684 if (preg_match('/###\s*<INCLUDE_TYPOSCRIPT:\s*source\s*=\s*"\s*FILE\s*:\s*(.*)\s*">\s*BEGIN/i', $line, $matches)) { 00685 00686 // save this line in case there is no ending tag 00687 $openingCommentedIncludeStatement = trim($line); 00688 $openingCommentedIncludeStatement = trim(preg_replace('/### Warning: .*###/', '', $openingCommentedIncludeStatement)); 00689 00690 // found a commented include statement 00691 $fileName = trim($matches[1]); 00692 $inIncludePart = TRUE; 00693 00694 $expectedEndTag = '### <INCLUDE_TYPOSCRIPT: source="FILE:' . $fileName . '"> END'; 00695 // strip all whitespace characters to make comparision safer 00696 $expectedEndTag = strtolower(preg_replace('/\s/', '', $expectedEndTag)); 00697 } else { 00698 // if this is not a beginning commented include statement this line goes into the rest content 00699 $restContent[] = $line; 00700 } 00701 00702 } else { // inside commented include statements 00703 00704 // search for the matching ending commented include statement 00705 $strippedLine = strtolower(preg_replace('/\s/', '', $line)); 00706 if (strpos($strippedLine, $expectedEndTag) !== FALSE) { 00707 00708 // found the matching ending include statement 00709 $fileContentString = implode("\n", $fileContent); 00710 00711 // write the content to the file 00712 $realFileName = t3lib_div::getFileAbsFileName($fileName); 00713 00714 // some file checks 00715 if (empty($realFileName)) { 00716 throw new Exception(sprintf('"%s" is not a valid file location.', $fileName)); 00717 } 00718 00719 if (!is_writable($realFileName)) { 00720 throw new Exception(sprintf('"%s" is not writable.', $fileName)); 00721 } 00722 00723 if (in_array($realFileName, $extractedFileNames)) { 00724 throw new Exception(sprintf('Recursive/multiple inclusion of file "%s"', $realFileName)); 00725 } 00726 $extractedFileNames[] = $realFileName; 00727 00728 // recursive call to detected nested commented include statements 00729 $fileContentString = self::extractIncludes($fileContentString, ++$cycle_counter, $extractedFileNames); 00730 00731 if (!t3lib_div::writeFile($realFileName, $fileContentString)) { 00732 throw new Exception(sprintf('Could not write file "%s"', $realFileName)); 00733 } 00734 00735 // insert reference to the file in the rest content 00736 $restContent[] = "<INCLUDE_TYPOSCRIPT: source=\"FILE:$fileName\">"; 00737 00738 // reset variables (preparing for the next commented include statement) 00739 $fileContent = array(); 00740 $fileName = NULL; 00741 $inIncludePart = FALSE; 00742 $openingCommentedIncludeStatement = NULL; 00743 // t3lib_TSparser::checkIncludeLines inserts an additional empty line, remove this again 00744 $skipNextLineIfEmpty = TRUE; 00745 } else { 00746 // if this is not a ending commented include statement this line goes into the file content 00747 $fileContent[] = $line; 00748 } 00749 00750 } 00751 00752 } 00753 00754 // if we're still inside commented include statements copy the lines back to the rest content 00755 if ($inIncludePart) { 00756 $restContent[] = $openingCommentedIncludeStatement . ' ### Warning: Corresponding end line missing! ###'; 00757 $restContent = array_merge($restContent, $fileContent); 00758 } 00759 00760 $restContentString = implode("\n", $restContent); 00761 return $restContentString; 00762 } 00763 00764 /** 00765 * Processes the string in each value of the input array with extractIncludes 00766 * 00767 * @param array Array with TypoScript in each value 00768 * @return array Same array but where the values has been processed with extractIncludes 00769 * @author Fabrizio Branca <typo3@fabrizio-branca.de> 00770 */ 00771 function extractIncludes_array($array) { 00772 foreach ($array as $k => $v) { 00773 $array[$k] = t3lib_TSparser::extractIncludes($array[$k]); 00774 } 00775 return $array; 00776 } 00777 00778 00779 /********************************** 00780 * 00781 * Syntax highlighting 00782 * 00783 *********************************/ 00784 00785 /** 00786 * Syntax highlight a TypoScript text 00787 * Will parse the content. Remember, the internal setup array may contain invalid parsed content since conditions are ignored! 00788 * 00789 * @param string The TypoScript text 00790 * @param mixed If blank, linenumbers are NOT printed. If array then the first key is the linenumber offset to add to the internal counter. 00791 * @param boolean If set, then the highlighted output will be formatted in blocks based on the brace levels. prespace will be ignored and empty lines represented with a single no-break-space. 00792 * @return string HTML code for the syntax highlighted string 00793 */ 00794 function doSyntaxHighlight($string, $lineNum = '', $highlightBlockMode = 0) { 00795 $this->syntaxHighLight = 1; 00796 $this->highLightData = array(); 00797 $this->error = array(); 00798 $string = str_replace(CR, '', $string); // This is done in order to prevent empty <span>..</span> sections around CR content. Should not do anything but help lessen the amount of HTML code. 00799 00800 $this->parse($string); 00801 00802 return $this->syntaxHighlight_print($lineNum, $highlightBlockMode); 00803 } 00804 00805 /** 00806 * Registers a part of a TypoScript line for syntax highlighting. 00807 * 00808 * @param string Key from the internal array $this->highLightStyles 00809 * @param integer Pointer to the line in $this->raw which this is about 00810 * @param integer The number of chars LEFT on this line before the end is reached. 00811 * @return void 00812 * @access private 00813 * @see parse() 00814 */ 00815 function regHighLight($code, $pointer, $strlen = -1) { 00816 if ($strlen == -1) { 00817 $this->highLightData[$pointer] = array(array($code, 0)); 00818 } else { 00819 $this->highLightData[$pointer][] = array($code, $strlen); 00820 } 00821 $this->highLightData_bracelevel[$pointer] = $this->inBrace; 00822 } 00823 00824 /** 00825 * Formatting the TypoScript code in $this->raw based on the data collected by $this->regHighLight in $this->highLightData 00826 * 00827 * @param mixed If blank, linenumbers are NOT printed. If array then the first key is the linenumber offset to add to the internal counter. 00828 * @param boolean If set, then the highlighted output will be formatted in blocks based on the brace levels. prespace will be ignored and empty lines represented with a single no-break-space. 00829 * @return string HTML content 00830 * @access private 00831 * @see doSyntaxHighlight() 00832 */ 00833 function syntaxHighlight_print($lineNumDat, $highlightBlockMode) { 00834 // Registers all error messages in relation to their linenumber 00835 $errA = array(); 00836 foreach ($this->errors as $err) { 00837 $errA[$err[2]][] = $err[0]; 00838 } 00839 // Generates the syntax highlighted output: 00840 $lines = array(); 00841 foreach ($this->raw as $rawP => $value) { 00842 $start = 0; 00843 $strlen = strlen($value); 00844 $lineC = ''; 00845 00846 if (is_array($this->highLightData[$rawP])) { 00847 foreach ($this->highLightData[$rawP] as $set) { 00848 $len = $strlen - $start - $set[1]; 00849 if ($len > 0) { 00850 $part = substr($value, $start, $len); 00851 $start += $len; 00852 $st = $this->highLightStyles[(isset($this->highLightStyles[$set[0]]) ? $set[0] : 'default')]; 00853 if (!$highlightBlockMode || $set[0] != 'prespace') { 00854 $lineC .= $st[0] . htmlspecialchars($part) . $st[1]; 00855 } 00856 } elseif ($len < 0) { 00857 debug(array($len, $value, $rawP)); 00858 } 00859 } 00860 } else { 00861 debug(array($value)); 00862 } 00863 00864 if (strlen(substr($value, $start))) { 00865 $lineC .= $this->highLightStyles['ignored'][0] . htmlspecialchars(substr($value, $start)) . $this->highLightStyles['ignored'][1]; 00866 } 00867 00868 if ($errA[$rawP]) { 00869 $lineC .= $this->highLightStyles['error'][0] . '<strong> - ERROR:</strong> ' . htmlspecialchars(implode(';', $errA[$rawP])) . $this->highLightStyles['error'][1]; 00870 } 00871 00872 if ($highlightBlockMode && $this->highLightData_bracelevel[$rawP]) { 00873 $lineC = str_pad('', $this->highLightData_bracelevel[$rawP] * 2, ' ', STR_PAD_LEFT) . '<span style="' . $this->highLightBlockStyles . ($this->highLightBlockStyles_basecolor ? 'background-color: ' . t3lib_div::modifyHTMLColorAll($this->highLightBlockStyles_basecolor, -$this->highLightData_bracelevel[$rawP] * 16) : '') . '">' . (strcmp($lineC, '') ? $lineC : ' ') . '</span>'; 00874 } 00875 00876 if (is_array($lineNumDat)) { 00877 $lineNum = $rawP + $lineNumDat[0]; 00878 if ($this->parentObject instanceof t3lib_tsparser_ext) { 00879 $lineNum = $this->parentObject->ext_lnBreakPointWrap($lineNum, $lineNum); 00880 } 00881 $lineC = $this->highLightStyles['linenum'][0] . str_pad($lineNum, 4, ' ', STR_PAD_LEFT) . ':' . $this->highLightStyles['linenum'][1] . ' ' . $lineC; 00882 } 00883 00884 00885 $lines[] = $lineC; 00886 } 00887 00888 return '<pre class="ts-hl">' . implode(LF, $lines) . '</pre>'; 00889 } 00890 } 00891 00892 00893 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tsparser.php'])) { 00894 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tsparser.php']); 00895 } 00896 00897 ?>
1.8.0