├── CHANGELOG.md ├── README.md ├── composer.json ├── tbs_class.php └── tbs_us_manual.htm /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changes in TinyButStrong Template Engine 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) 6 | 7 | ## [3.15.2] - 2024-05-08 8 | 9 | ### Fixed 10 | 11 | - OnData plug-ins are not triggered, because of a private property. 12 | 13 | ### Added 14 | 15 | - New parameter « ope=debug_val » for debugging values. 16 | 17 | 18 | ## [3.15.1] - 2024-01-25 19 | 20 | - PHP 8.2 compatibility improved 21 | 22 | ## [3.15.0] - 2023-05-15 23 | 24 | ### Enhancements 25 | 26 | - PHP 8.2 compatibility : utf8_encode is deprecated + creation of dynamic property is deprecated. 27 | 28 | ### Fixed 29 | 30 | - Error message « invalid query XXX because VarRef item YYY is not found » when item YYY is stored in $GLOBALS. 31 | - Error message « Undefined variable $NoDelim1 » 32 | - Error message « Error in field [onload...]: it doesn't have any subname. » when using subtemplates containing automatic fields. 33 | 34 | ## [3.14.1] - 2022-11-01 35 | 36 | ### Fixed 37 | 38 | - PHP error "Unsupported operand types" when using a TBS comparison (if/when) and one expression has no string delimiter and is not numerical. 39 | 40 | ## [3.14.0] - 2022-07-25 41 | 42 | ### Added 43 | 44 | - Support Doctrine DBAL in native. 45 | - Parameter 'strconv=utf8' is PHP 8.2 compliant. The feature no longer uses the deprecated PHP function utf8_encode(). 46 | - New TBS option 'scripts_allowed' enables you to unlock the parameter 'script' which becomes forbidden by default. 47 | 48 | ### Changed 49 | 50 | - By default, parameter 'script' is forbidden. It can be allowed using the new TBS option 'scripts_allowed'. 51 | 52 | ## [3.13.2] - 2022-02-23 53 | 54 | ### Fixed 55 | 56 | - Version number 57 | 58 | ## [3.13.1] - 2022-02-13 59 | 60 | ### Enhancements 61 | 62 | - method SetVarRefItem() supports an array of items 63 | 64 | ## [3.13.0] - 2022-02-07 65 | 66 | ### Enhancements 67 | 68 | - Compatibility with PHP 8.1 69 | 70 | ### Added 71 | 72 | - New methods SetVarRefItem() and GetVarRefItem() : uniformize and ensure compatibility to set a VarRef items. 73 | 74 | ## [3.12.2] - 2020-11-03 75 | 76 | ### Fixed 77 | 78 | - Parameter 'magnet=#' used with MergeBlock() makes erroneous merging when magnet is activated, except for the first occurence. 79 | 80 | ## [3.12.1] - 2020-10-24 81 | 82 | ### Fixed 83 | 84 | - Parameter 'frm' displays '1970-01-01' for a date over '2038-01-19 03:14:07' on a PHP 32-bits plateform. 85 | 86 | ## [3.12.0] - 2020-10-12 87 | 88 | ### Added 89 | 90 | - Compatibility with PHP 8 91 | - New parameters for blocks : 'firstingrp', 'lastingrp', 'singleingrp'. They define sections of a block displayed for the first, the last or the single record in a group. 92 | 93 | ### Changed 94 | 95 | - MergeBlock() return false instead of 0 when no block and no field have been merged. 96 | 97 | ### Fixed 98 | 99 | - Infinite loop when a plugin event OnOperation returns false during a mergin of an automatic field such as [onload.my_var]. 100 | 101 | ## [3.11.0] - 2019-02-10 102 | 103 | ### Added 104 | 105 | - New method GetAttValue($Name[,$delete]) : enables you to read an attribute of an HTML or XML element in the template. 106 | - New method ReplaceFields(array($name=>$prms) [, $BlocName]) : replace simple TBS fields with new definitions of fields. 107 | - New parameter 'combo' : apply a set of parameters defined at the PHP side using SetOption('prm_combo'). 108 | - Subnames for fields, array queries and ObjectRef path can support methods overloaded with magic function _call(). 109 | 110 | ### Fixed 111 | 112 | - PHP error: one meth_Misc_Alert() called with a wrong number of arguments. 113 | 114 | ## [3.10.1] 2015-12-03 115 | 116 | 117 | ### Fixed 118 | 119 | - Coding OnCacheField event in a plug-in: in some circumstances, moved locators may be ignored by TBS. 120 | Could happen with OpenTBS when using parameter "ope=tbs:num" or a similar operator. 121 | - Coding OnCacheField event in a plug-in: stronger way to manage embedding fields + support property DelMe. 122 | 123 | ## [3.10.0] - 2015-11-08 124 | 125 | ### Added 126 | 127 | - New operator "*" for block definition syntax. Example: "block=3*tr". 128 | 129 | - New operator "!" for block definition syntax. Use it on first or last tag in order to exclude the tag from the block bound. Example: "block=!div". 130 | 131 | - New marker "." for block definition. Use it to represents the bound of the TBS field itself. Example: "block=(.)". 132 | 133 | - New arguments $LocLst and $Pos for the event OnCacheField. It enables a plug-in to move, add or delete TBS fields. 134 | 135 | - Native support for SQLite3. 136 | 137 | - Parameter "parallel=tbs:table" now supports , , and . The tags must be closed even if HTML actually allows unclosed tags. 138 | 139 | - Better management of fields moved in a set of other fields, when using parameter "att". 140 | Parameter "att" can make a TBS field moving forward another of the same block. 141 | 142 | - Support GetOption('parallel_conf'). 143 | 144 | - Support GetOption('block_alias'). 145 | 146 | ### Fixed 147 | 148 | - Parameter "att" does not find the self-closing tag if there is no space before "/>". Example : 149 | - "Notice: Undefined property: clsTbsLocator::$AttName" can occurs if parameter "atttrue" is used for an attribute which is not already present in the target element. 150 | 151 | - Parameter "ope=upperw" works only with lower case characters. 152 | 153 | - Error message "Notice: Undefined property: clsTbsLocator::$AttName in tbs_class.php on line 1492". 154 | 155 | 156 | ## [3.9.0] - 2014-01-26 157 | 158 | ### Added 159 | 160 | - New parameter "parallel" for merging a block in columns (or any other parallel ways). 161 | - New way for merging sub-template: PHP error messages are not absorbed any more. 162 | This new way may not be compatible with subscripts that uses the echo command (very rare). 163 | In this case you should add set option $TBS->SetOptions('old_subtemplate') for compatibility. 164 | - Now can merge DateTime objects and also objects with the magic method __toString(). 165 | 166 | ### Changed 167 | 168 | - Error messages are in plain text instead of HTML when PHP is used in command line (CLI). 169 | - Some code enhancements. 170 | - replace or with || 171 | - replace and with && 172 | 173 | ### Fixed 174 | 175 | - HTML plug-in version 1.0.8: parameter "select" now works with values containing special HTML characters. 176 | - Parameter "frm": leading zero coming with a prefix or a suffix may not format the number as expected. 177 | http://www.tinybutstrong.com/forum.php?thr=3208 178 | - There use to have an error message about $Loc->AttForward when the entity of parameter "att" is not found. 179 | 180 | ## [3.8.2] - 2013-04-20 181 | 182 | 183 | ### Added 184 | 185 | - Explicit error message when a colmun is missing for a grouping paremeter (headergrp, footergrp, splittergrp) 186 | 187 | - Add option methods_allowed 188 | 189 | 190 | ## [3.8.1] - 2012-04-01 191 | 192 | ### Fixed 193 | 194 | - PHP 5.4 : 195 | Unexpected PHP error [Array to string conversion] severity [E_NOTICE] in [D:\Users\Qwerty\Dev HTML\Site Dev\svn_TinyButStrong\trunk\tbs_class.php line 72] 196 | Unexpected PHP error [Array to string conversion] severity [E_NOTICE] in [D:\Users\Qwerty\Dev HTML\Site Dev\svn_TinyButStrong\trunk\tbs_class.php line 1369] 197 | 198 | ### Added 199 | 200 | - Plugin MergeOnFly 201 | 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyButStrong template engine 2 | 3 | www.tinybutstrong.com 4 | 5 | TBS is a PHP template engine for pro and beginners. 6 | Only 1 class with few methods and properties, but it can do may things for any text templates, including HTML and XML. 7 | The only engine that enables W3C compliant templates. 8 | It has many plugins, including OpenTBS for merging LibreOffice and Ms Office documents. 9 | 10 | ## Documentation 11 | 12 | For documentation see 13 | www.tinybutstrong.com/manual.php 14 | 15 | ## Licence 16 | 17 | TinyButStrong is released under the LGPL (Lesser General Public Licence) version 3.0. 18 | 19 | ## Distribution repository 20 | 21 | TinyButStrong in on GitHub since version 3.5.3. 22 | 23 | TinyButStrong versions 3.10.1 or later can be installed with [Composer](http://getcomposer.org/download/). 24 | 25 | The package is "tinybutstrong/tinybutstrong" 26 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tinybutstrong/tinybutstrong", 3 | "description": "Template Engine for Pro and Beginners ", 4 | "type": "library", 5 | "keywords": ["templating"], 6 | "homepage": "http://www.tinybutstrong.com", 7 | "license": "LGPL-3.0-only", 8 | "authors": [ 9 | { 10 | "name": "Skrol29", 11 | "email": "skrol29dev@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.0" 16 | }, 17 | "autoload": { 18 | "classmap": ["tbs_class.php"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tbs_class.php: -------------------------------------------------------------------------------- 1 | TinyButStrong Error (PHP Version Check) : Your PHP version is '.PHP_VERSION.' while TinyButStrong needs PHP version 5.0 or higher. You should try with TinyButStrong Edition for PHP 4.'; 19 | 20 | // Render flags 21 | define('TBS_NOTHING', 0); 22 | define('TBS_OUTPUT', 1); 23 | define('TBS_EXIT', 2); 24 | 25 | // Plug-ins actions 26 | define('TBS_INSTALL', -1); 27 | define('TBS_ISINSTALLED', -3); 28 | 29 | // ********************************************* 30 | 31 | class clsTbsLocator { 32 | 33 | public $PosBeg = false; 34 | public $PosEnd = false; 35 | public $Enlarged = false; 36 | public $FullName = false; 37 | public $SubName = ''; 38 | public $SubOk = false; 39 | public $SubLst = array(); 40 | public $SubNbr = 0; 41 | public $PrmLst = array(); 42 | public $PrmPos; // positions of the parameters, if asked 43 | public $PrmIfNbr = false; 44 | public $MagnetId = false; 45 | public $BlockFound = false; 46 | public $FirstMerge = true; 47 | public $ConvProtect = true; 48 | public $ConvStr = true; 49 | public $ConvMode = 1; // Normal 50 | public $ConvBr = true; 51 | 52 | // Compatibility with PHP 8.2 53 | public $Prop = array(); // dynamic properties, used by OpenTBS 54 | 55 | public $Ope; 56 | public $OpeEnd; 57 | public $PosNext; 58 | public $PrmIf; 59 | public $PrmThen; 60 | public $PrmThenVar; 61 | public $PrmIfVar; 62 | public $PrmElseVar; 63 | 64 | // other 65 | public $ConvEsc; 66 | public $ConvWS; 67 | public $ConvJS; 68 | public $ConvUrl; 69 | public $ConvUtf8; 70 | 71 | public $OnFrmInfo; 72 | public $OnFrmArg; 73 | 74 | public $OpeUtf8; 75 | public $OpeAct; 76 | public $OpePrm; 77 | public $OpeArg; 78 | 79 | public $OpeMOK; 80 | public $OpeMKO; 81 | public $MSave; 82 | 83 | // Sub-template 84 | public $SaveSrc; 85 | public $SaveMode; 86 | public $SaveVarRef; 87 | public $SaveRender; 88 | 89 | // Att 90 | public $AttForward; 91 | public $AttTagBeg; 92 | public $AttTagEnd; 93 | public $AttDelimChr; 94 | public $AttName; 95 | public $AttBeg; 96 | public $AttEnd; 97 | public $AttDelimCnt; 98 | public $AttValBeg; 99 | public $PosBeg0; 100 | public $PosEnd0; 101 | public $InsPos; 102 | public $InsLen; 103 | public $DelPos; 104 | public $DelLen; 105 | public $PosBeg2; 106 | public $PosEnd2; 107 | 108 | // blocks 109 | public $P1; 110 | public $FieldOutside; 111 | public $FOStop; 112 | public $BDefLst; 113 | public $NoData; 114 | public $Special; 115 | public $HeaderFound; 116 | public $FooterFound; 117 | public $SerialEmpty; 118 | public $GrpBreak; 119 | public $BoundFound; 120 | public $CheckNext; 121 | public $CheckPrev; 122 | public $WhenFound; 123 | public $WhenDefault; 124 | public $WhenDefaultBeforeNS; 125 | public $SectionNbr; 126 | public $SectionLst; 127 | public $PosDefBeg; 128 | public $RightLevel; 129 | public $BlockSrc; 130 | public $PosDefEnd; 131 | public $IsRecInfo; 132 | public $RecInfo; 133 | public $WhenSeveral; 134 | public $WhenNbr; 135 | public $WhenLst; 136 | public $FooterNbr; 137 | public $FooterDef; 138 | public $HeaderNbr; 139 | public $HeaderDef; 140 | public $ValPrev; 141 | public $BoundLst; 142 | public $BoundNb; 143 | public $BoundSingleNb; 144 | public $ValNext; 145 | 146 | } 147 | 148 | // ********************************************* 149 | 150 | class clsTbsDataSource { 151 | 152 | public $Type = false; 153 | public $SubType = 0; 154 | public $SrcId = false; 155 | public $Query = ''; 156 | public $RecSet = false; 157 | public $RecNumInit = 0; // Used by ByPage plugin 158 | public $RecSaving = false; 159 | public $RecSaved = false; 160 | public $RecBuffer = false; 161 | public $TBS = false; 162 | public $OnDataOk = false; 163 | public $OnDataPrm = false; 164 | public $OnDataPrmDone = array(); 165 | public $OnDataPi = false; 166 | public $OnDataPiRef = false; 167 | 168 | // Info relative to the current record : 169 | public $CurrRec = false; // Used by ByPage plugin 170 | public $RecKey = ''; // Used by ByPage plugin 171 | public $RecNum = 0; // Used by ByPage plugin 172 | 173 | public $PrevRec = null; 174 | public $NextRec = null; 175 | 176 | public $PrevSave = false; 177 | public $NextSave = false; 178 | 179 | // Compatibility with PHP 8.2 180 | public $Prop = array(); // Used by ByPage plugin 181 | public $RecNbr; 182 | public $RSIsFirst; 183 | public $NumMin; 184 | public $NumMax; 185 | public $NumStep; 186 | public $NumVal; 187 | public $OnDataPrmRef; 188 | public $OnDataArgs; 189 | 190 | public function DataAlert($Msg) { 191 | if (is_array($this->TBS->_CurrBlock)) { 192 | return $this->TBS->meth_Misc_Alert('when merging block "'.implode(',',$this->TBS->_CurrBlock).'"',$Msg); 193 | } else { 194 | return $this->TBS->meth_Misc_Alert('when merging block '.$this->TBS->_ChrOpen.$this->TBS->_CurrBlock.$this->TBS->_ChrClose,$Msg); 195 | } 196 | } 197 | 198 | public function DataPrepare(&$SrcId,&$TBS) { 199 | 200 | $this->SrcId = &$SrcId; 201 | $this->TBS = &$TBS; 202 | $FctInfo = false; 203 | $FctObj = false; 204 | 205 | if (is_array($SrcId)) { 206 | $this->Type = 0; 207 | } elseif (is_resource($SrcId)) { 208 | 209 | $Key = get_resource_type($SrcId); 210 | switch ($Key) { 211 | case 'mysql link' : $this->Type = 6; break; 212 | case 'mysql link persistent' : $this->Type = 6; break; 213 | case 'mysql result' : $this->Type = 6; $this->SubType = 1; break; 214 | case 'pgsql link' : $this->Type = 7; break; 215 | case 'pgsql link persistent' : $this->Type = 7; break; 216 | case 'pgsql result' : $this->Type = 7; $this->SubType = 1; break; 217 | case 'sqlite database' : $this->Type = 8; break; 218 | case 'sqlite database (persistent)' : $this->Type = 8; break; 219 | case 'sqlite result' : $this->Type = 8; $this->SubType = 1; break; 220 | default : 221 | $FctInfo = $Key; 222 | $FctCat = 'r'; 223 | } 224 | 225 | } elseif (is_string($SrcId)) { 226 | 227 | switch (strtolower($SrcId)) { 228 | case 'array' : $this->Type = 0; $this->SubType = 1; break; 229 | case 'clear' : $this->Type = 0; $this->SubType = 3; break; 230 | case 'mysql' : $this->Type = 6; $this->SubType = 2; break; 231 | case 'text' : $this->Type = 2; break; 232 | case 'num' : $this->Type = 1; break; 233 | default : 234 | $FctInfo = $SrcId; 235 | $FctCat = 'k'; 236 | } 237 | 238 | } elseif ($SrcId instanceof Iterator) { 239 | $this->Type = 9; $this->SubType = 1; 240 | } elseif ($SrcId instanceof ArrayObject) { 241 | $this->Type = 9; $this->SubType = 2; 242 | } elseif ($SrcId instanceof IteratorAggregate) { 243 | $this->Type = 9; $this->SubType = 3; 244 | } elseif ($SrcId instanceof MySQLi) { 245 | $this->Type = 10; 246 | } elseif ($SrcId instanceof PDO) { 247 | $this->Type = 11; 248 | } elseif ($SrcId instanceof Zend_Db_Adapter_Abstract) { 249 | $this->Type = 12; 250 | } elseif ($SrcId instanceof SQLite3) { 251 | $this->Type = 13; $this->SubType = 1; 252 | } elseif ($SrcId instanceof SQLite3Stmt) { 253 | $this->Type = 13; $this->SubType = 2; 254 | } elseif ($SrcId instanceof SQLite3Result) { 255 | $this->Type = 13; $this->SubType = 3; 256 | } elseif (is_a($SrcId, 'Doctrine\DBAL\Connection')) { 257 | $this->Type = 14; 258 | } elseif (is_object($SrcId)) { 259 | $FctInfo = get_class($SrcId); 260 | $FctCat = 'o'; 261 | $FctObj = &$SrcId; 262 | $this->SrcId = &$SrcId; 263 | } elseif ($SrcId===false) { 264 | $this->DataAlert('the specified source is set to FALSE. Maybe your connection has failed.'); 265 | } else { 266 | $this->DataAlert('unsupported variable type : \''.gettype($SrcId).'\'.'); 267 | } 268 | 269 | if ($FctInfo!==false) { 270 | $ErrMsg = false; 271 | if ($TBS->meth_Misc_UserFctCheck($FctInfo,$FctCat,$FctObj,$ErrMsg,false)) { 272 | $this->Type = $FctInfo['type']; 273 | if ($this->Type!==5) { 274 | if ($this->Type===4) { 275 | $this->FctPrm = array(false,0); 276 | $this->SrcId = &$FctInfo['open'][0]; 277 | } 278 | $this->FctOpen = &$FctInfo['open']; 279 | $this->FctFetch = &$FctInfo['fetch']; 280 | $this->FctClose = &$FctInfo['close']; 281 | } 282 | } else { 283 | $this->Type = $this->DataAlert($ErrMsg); 284 | } 285 | } 286 | 287 | return ($this->Type!==false); 288 | 289 | } 290 | 291 | public function DataOpen(&$Query,$QryPrms=false) { 292 | 293 | // Init values 294 | unset($this->CurrRec); 295 | $this->CurrRec = true; 296 | 297 | if ($this->RecSaved) { 298 | $this->RSIsFirst = true; 299 | unset($this->RecKey); $this->RecKey = ''; 300 | $this->RecNum = $this->RecNumInit; 301 | if ($this->OnDataOk) $this->OnDataArgs[1] = &$this->CurrRec; 302 | return true; 303 | } 304 | 305 | unset($this->RecSet); 306 | $this->RecSet = false; 307 | $this->RecNumInit = 0; 308 | $this->RecNum = 0; 309 | 310 | // Previous and next records 311 | $this->PrevRec = (object) null; 312 | $this->NextRec = false; 313 | 314 | if (isset($this->TBS->_piOnData)) { 315 | $this->OnDataPi = true; 316 | $this->OnDataPiRef = &$this->TBS->_piOnData; 317 | $this->OnDataOk = true; 318 | } 319 | if ($this->OnDataOk) { 320 | $this->OnDataArgs = array(); 321 | $this->OnDataArgs[0] = &$this->TBS->_CurrBlock; 322 | $this->OnDataArgs[1] = &$this->CurrRec; 323 | $this->OnDataArgs[2] = &$this->RecNum; 324 | $this->OnDataArgs[3] = &$this->TBS; 325 | } 326 | 327 | switch ($this->Type) { 328 | case 0: // Array 329 | if (($this->SubType===1) && (is_string($Query))) $this->SubType = 2; 330 | if ($this->SubType===0) { 331 | $this->RecSet = &$this->SrcId; 332 | } elseif ($this->SubType===1) { 333 | if (is_array($Query)) { 334 | $this->RecSet = &$Query; 335 | } else { 336 | $this->DataAlert('type \''.gettype($Query).'\' not supported for the Query Parameter going with \'array\' Source Type.'); 337 | } 338 | } elseif ($this->SubType===2) { 339 | // TBS query string for array and objects, syntax: "var[item1][item2]->item3[item4]..." 340 | $x = trim($Query); 341 | $z = chr(0); 342 | $x = str_replace(array(']->','][','->','['),$z,$x); 343 | if (substr($x,strlen($x)-1,1)===']') $x = substr($x,0,strlen($x)-1); 344 | $ItemLst = explode($z,$x); 345 | $ItemNbr = count($ItemLst); 346 | $Item0 = &$ItemLst[0]; 347 | // Check first item 348 | if ($Item0[0]==='~') { 349 | $Item0 = substr($Item0,1); 350 | if ($this->TBS->ObjectRef!==false) { 351 | $Var = &$this->TBS->ObjectRef; 352 | $i = 0; 353 | } else { 354 | $i = $this->DataAlert('invalid query \''.$Query.'\' because property ObjectRef is not set.'); 355 | } 356 | } else { 357 | if ( is_null($this->TBS->VarRef) && isset($GLOBALS[$Item0]) ) { 358 | $Var = &$GLOBALS[$Item0]; 359 | $i = 1; 360 | } elseif (isset($this->TBS->VarRef[$Item0])) { 361 | $Var = &$this->TBS->VarRef[$Item0]; 362 | $i = 1; 363 | } else { 364 | $i = $this->DataAlert('invalid query \''.$Query.'\' because VarRef item \''.$Item0.'\' is not found.'); 365 | } 366 | } 367 | // Check sub-items 368 | $Empty = false; 369 | while (($i!==false) && ($i<$ItemNbr) && ($Empty===false)) { 370 | $x = $ItemLst[$i]; 371 | if (is_array($Var)) { 372 | if (isset($Var[$x])) { 373 | $Var = &$Var[$x]; 374 | } else { 375 | $Empty = true; 376 | } 377 | } elseif (is_object($Var)) { 378 | $form = $this->TBS->f_Misc_ParseFctForm($x); 379 | $n = $form['name']; 380 | if ( method_exists($Var, $n) || ($form['as_fct'] && method_exists($Var,'__call')) ) { 381 | $f = array(&$Var,$n); unset($Var); 382 | $Var = call_user_func_array($f,$form['args']); 383 | } elseif (property_exists(get_class($Var),$n)) { 384 | if (isset($Var->$n)) $Var = &$Var->$n; 385 | } elseif (isset($Var->$n)) { 386 | $Var = $Var->$n; // useful for overloaded property 387 | } else { 388 | $Empty = true; 389 | } 390 | } else { 391 | $i = $this->DataAlert('invalid query \''.$Query.'\' because item \''.$ItemLst[$i].'\' is neither an Array nor an Object. Its type is \''.gettype($Var).'\'.'); 392 | } 393 | if ($i!==false) $i++; 394 | } 395 | // Assign data 396 | if ($i!==false) { 397 | if ($Empty) { 398 | $this->RecSet = array(); 399 | } else { 400 | $this->RecSet = &$Var; 401 | } 402 | } 403 | } elseif ($this->SubType===3) { // Clear 404 | $this->RecSet = array(); 405 | } 406 | // First record 407 | if ($this->RecSet!==false) { 408 | $this->RecNbr = $this->RecNumInit + count($this->RecSet); 409 | $this->RSIsFirst = true; 410 | $this->RecSaved = true; 411 | $this->RecSaving = false; 412 | } 413 | break; 414 | case 6: // MySQL 415 | switch ($this->SubType) { 416 | case 0: $this->RecSet = @mysql_query($Query,$this->SrcId); break; 417 | case 1: $this->RecSet = $this->SrcId; break; 418 | case 2: $this->RecSet = @mysql_query($Query); break; 419 | } 420 | if ($this->RecSet===false) $this->DataAlert('MySql error message when opening the query: '.mysql_error()); 421 | break; 422 | case 1: // Num 423 | $this->RecSet = true; 424 | $this->NumMin = 1; 425 | $this->NumMax = 1; 426 | $this->NumStep = 1; 427 | if (is_array($Query)) { 428 | if (isset($Query['min'])) $this->NumMin = $Query['min']; 429 | if (isset($Query['step'])) $this->NumStep = $Query['step']; 430 | if (isset($Query['max'])) { 431 | $this->NumMax = $Query['max']; 432 | } else { 433 | $this->RecSet = $this->DataAlert('the \'num\' source is an array that has no value for the \'max\' key.'); 434 | } 435 | if ($this->NumStep==0) $this->RecSet = $this->DataAlert('the \'num\' source is an array that has a step value set to zero.'); 436 | } else { 437 | $this->NumMax = ceil($Query); 438 | } 439 | if ($this->RecSet) { 440 | if ($this->NumStep>0) { 441 | $this->NumVal = $this->NumMin; 442 | } else { 443 | $this->NumVal = $this->NumMax; 444 | } 445 | } 446 | break; 447 | case 2: // Text 448 | if (is_string($Query)) { 449 | $this->RecSet = &$Query; 450 | } else { 451 | $this->RecSet = $this->TBS->meth_Misc_ToStr($Query); 452 | } 453 | break; 454 | case 3: // Custom function 455 | $FctOpen = $this->FctOpen; 456 | $this->RecSet = $FctOpen($this->SrcId,$Query,$QryPrms); 457 | if ($this->RecSet===false) $this->DataAlert('function '.$FctOpen.'() has failed to open query {'.$Query.'}'); 458 | break; 459 | case 4: // Custom method from ObjectRef 460 | $this->RecSet = call_user_func_array($this->FctOpen,array(&$this->SrcId,&$Query,&$QryPrms)); 461 | if ($this->RecSet===false) $this->DataAlert('method '.get_class($this->FctOpen[0]).'::'.$this->FctOpen[1].'() has failed to open query {'.$Query.'}'); 462 | break; 463 | case 5: // Custom method of object 464 | $this->RecSet = $this->SrcId->tbsdb_open($this->SrcId,$Query,$QryPrms); 465 | if ($this->RecSet===false) $this->DataAlert('method '.get_class($this->SrcId).'::tbsdb_open() has failed to open query {'.$Query.'}'); 466 | break; 467 | case 7: // PostgreSQL 468 | switch ($this->SubType) { 469 | case 0: $this->RecSet = @pg_query($this->SrcId,$Query); break; 470 | case 1: $this->RecSet = $this->SrcId; break; 471 | } 472 | if ($this->RecSet===false) $this->DataAlert('PostgreSQL error message when opening the query: '.pg_last_error($this->SrcId)); 473 | break; 474 | case 8: // SQLite 475 | switch ($this->SubType) { 476 | case 0: $this->RecSet = @sqlite_query($this->SrcId,$Query); break; 477 | case 1: $this->RecSet = $this->SrcId; break; 478 | } 479 | if ($this->RecSet===false) $this->DataAlert('SQLite error message when opening the query:'.sqlite_error_string(sqlite_last_error($this->SrcId))); 480 | break; 481 | case 9: // Iterator 482 | if ($this->SubType==1) { 483 | $this->RecSet = $this->SrcId; 484 | } else { // 2 or 3 485 | $this->RecSet = $this->SrcId->getIterator(); 486 | } 487 | $this->RecSet->rewind(); 488 | break; 489 | case 10: // MySQLi 490 | $this->RecSet = $this->SrcId->query($Query); 491 | if ($this->RecSet===false) $this->DataAlert('MySQLi error message when opening the query:'.$this->SrcId->error); 492 | break; 493 | case 11: // PDO 494 | $this->RecSet = $this->SrcId->prepare($Query); 495 | if ($this->RecSet===false) { 496 | $ok = false; 497 | } else { 498 | if (!is_array($QryPrms)) $QryPrms = array(); 499 | $ok = $this->RecSet->execute($QryPrms); 500 | } 501 | if (!$ok) { 502 | $err = $this->SrcId->errorInfo(); 503 | $this->DataAlert('PDO error message when opening the query:'.$err[2]); 504 | } 505 | break; 506 | case 12: // Zend_DB_Adapter 507 | try { 508 | if (!is_array($QryPrms)) $QryPrms = array(); 509 | $this->RecSet = $this->SrcId->query($Query, $QryPrms); 510 | } catch (Exception $e) { 511 | $this->DataAlert('Zend_DB_Adapter error message when opening the query: '.$e->getMessage()); 512 | } 513 | break; 514 | case 13: // SQLite3 515 | try { 516 | if ($this->SubType==3) { 517 | $this->RecSet = $this->SrcId; 518 | } elseif (($this->SubType==1) && (!is_array($QryPrms))) { 519 | // SQL statement without parameters 520 | $this->RecSet = $this->SrcId->query($Query); 521 | } else { 522 | if ($this->SubType==2) { 523 | $stmt = $this->SrcId; 524 | $prms = $Query; 525 | } else { 526 | // SQL statement with parameters 527 | $stmt = $this->SrcId->prepare($Query); 528 | $prms = $QryPrms; 529 | } 530 | // bind parameters 531 | if (is_array($prms)) { 532 | foreach ($prms as $p => $v) { 533 | if (is_numeric($p)) { 534 | $p = $p + 1; 535 | } 536 | if (is_array($v)) { 537 | $stmt->bindValue($p, $v[0], $v[1]); 538 | } else { 539 | $stmt->bindValue($p, $v); 540 | } 541 | } 542 | } 543 | $this->RecSet = $stmt->execute(); 544 | } 545 | } catch (Exception $e) { 546 | $this->DataAlert('SQLite3 error message when opening the query: '.$e->getMessage()); 547 | } 548 | break; 549 | case 14: // Doctrine DBAL 550 | try { 551 | if (!is_array($QryPrms)) $QryPrms = array(); 552 | $this->RecSet = $this->SrcId->executeQuery($Query, $QryPrms); 553 | } catch (Exception $e) { 554 | $this->DataAlert('Doctrine DBAL error message when opening the query: '.$e->getMessage()); 555 | } 556 | break; 557 | } 558 | 559 | if (($this->Type===0) || ($this->Type===9)) { 560 | unset($this->RecKey); $this->RecKey = ''; 561 | } else { 562 | if ($this->RecSaving) { 563 | unset($this->RecBuffer); $this->RecBuffer = array(); 564 | } 565 | $this->RecKey = &$this->RecNum; // Not array: RecKey = RecNum 566 | } 567 | 568 | return ($this->RecSet!==false); 569 | 570 | } 571 | 572 | public function DataFetch() { 573 | 574 | // Save previous record 575 | if ($this->PrevSave) { 576 | $this->_CopyRec($this, $this->PrevRec); 577 | } 578 | 579 | if ($this->NextSave) { 580 | // set current record 581 | if ($this->NextRec === false) { 582 | // first record 583 | $this->NextRec = (object) array('RecNum' => 1); // prepare for getting properties, RecNum needed for the first fetch 584 | $this->_DataFetchOn($this); 585 | } else { 586 | // other records 587 | $this->_CopyRec($this->NextRec, $this); 588 | } 589 | // set next record 590 | if ($this->CurrRec === false) { 591 | // no more record 592 | $this->NextRec = (object) null; // clear properties 593 | } else { 594 | $this->_DataFetchOn($this->NextRec); 595 | } 596 | } else { 597 | // Classic fetch 598 | $this->_DataFetchOn($this); 599 | } 600 | 601 | } 602 | 603 | public function DataClose() { 604 | $this->OnDataOk = false; 605 | $this->OnDataPrm = false; 606 | $this->OnDataPi = false; 607 | if ($this->RecSaved) return; 608 | switch ($this->Type) { 609 | case 6: mysql_free_result($this->RecSet); break; 610 | case 3: $FctClose=$this->FctClose; $FctClose($this->RecSet); break; 611 | case 4: call_user_func_array($this->FctClose,array(&$this->RecSet)); break; 612 | case 5: $this->SrcId->tbsdb_close($this->RecSet); break; 613 | case 7: pg_free_result($this->RecSet); break; 614 | case 10: $this->RecSet->free(); break; // MySQLi 615 | case 13: // SQLite3 616 | if ($this->SubType!=3) { 617 | $this->RecSet->finalize(); 618 | } 619 | break; 620 | //case 11: $this->RecSet->closeCursor(); break; // PDO 621 | } 622 | if ($this->RecSaving) { 623 | $this->RecSet = &$this->RecBuffer; 624 | $this->RecNbr = $this->RecNumInit + count($this->RecSet); 625 | $this->RecSaving = false; 626 | $this->RecSaved = true; 627 | } 628 | } 629 | 630 | /** 631 | * Copy the record information from an object to another. 632 | */ 633 | private function _CopyRec($from, $to) { 634 | 635 | $to->CurrRec = $from->CurrRec; 636 | $to->RecNum = $from->RecNum; 637 | $to->RecKey = $from->RecKey; 638 | 639 | } 640 | 641 | /** 642 | * Fetch the next record on the object $obj. 643 | * This wil set the proiperties : 644 | * $obj->CurrRec 645 | * $obj->RecKey 646 | * $obj->RecNum 647 | */ 648 | private function _DataFetchOn($obj) { 649 | 650 | // Check if the records are saved in an array 651 | if ($this->RecSaved) { 652 | if ($obj->RecNum < $this->RecNbr) { 653 | if ($this->RSIsFirst) { 654 | if ($this->SubType===2) { // From string 655 | reset($this->RecSet); 656 | $obj->RecKey = key($this->RecSet); 657 | $obj->CurrRec = &$this->RecSet[$obj->RecKey]; 658 | } else { 659 | $obj->CurrRec = reset($this->RecSet); 660 | $obj->RecKey = key($this->RecSet); 661 | } 662 | $this->RSIsFirst = false; 663 | } else { 664 | if ($this->SubType===2) { // From string 665 | next($this->RecSet); 666 | $obj->RecKey = key($this->RecSet); 667 | $obj->CurrRec = &$this->RecSet[$obj->RecKey]; 668 | } else { 669 | $obj->CurrRec = next($this->RecSet); 670 | $obj->RecKey = key($this->RecSet); 671 | } 672 | } 673 | if ((!is_array($obj->CurrRec)) && (!is_object($obj->CurrRec))) $obj->CurrRec = array('key'=>$obj->RecKey, 'val'=>$obj->CurrRec); 674 | $obj->RecNum++; 675 | if ($this->OnDataOk) { 676 | $this->OnDataArgs[1] = &$obj->CurrRec; // Reference has changed if ($this->SubType===2) 677 | if ($this->OnDataPrm) call_user_func_array($this->OnDataPrmRef,$this->OnDataArgs); 678 | if ($this->OnDataPi) $this->TBS->meth_PlugIn_RunAll($this->OnDataPiRef,$this->OnDataArgs); 679 | if ($this->SubType!==2) $this->RecSet[$obj->RecKey] = $obj->CurrRec; // save modifications because array reading is done without reference :( 680 | } 681 | } else { 682 | unset($obj->CurrRec); $obj->CurrRec = false; 683 | } 684 | return; 685 | } 686 | 687 | switch ($this->Type) { 688 | case 6: // MySQL 689 | $obj->CurrRec = mysql_fetch_assoc($this->RecSet); 690 | break; 691 | case 1: // Num 692 | if (($this->NumVal>=$this->NumMin) && ($this->NumVal<=$this->NumMax)) { 693 | $obj->CurrRec = array('val'=>$this->NumVal); 694 | $this->NumVal += $this->NumStep; 695 | } else { 696 | $obj->CurrRec = false; 697 | } 698 | break; 699 | case 2: // Text 700 | if ($obj->RecNum===0) { 701 | if ($this->RecSet==='') { 702 | $obj->CurrRec = false; 703 | } else { 704 | $obj->CurrRec = &$this->RecSet; 705 | } 706 | } else { 707 | $obj->CurrRec = false; 708 | } 709 | break; 710 | case 3: // Custom function 711 | $FctFetch = $this->FctFetch; 712 | $obj->CurrRec = $FctFetch($this->RecSet,$obj->RecNum+1); 713 | break; 714 | case 4: // Custom method from ObjectRef 715 | $this->FctPrm[0] = &$this->RecSet; $this->FctPrm[1] = $obj->RecNum+1; 716 | $obj->CurrRec = call_user_func_array($this->FctFetch,$this->FctPrm); 717 | break; 718 | case 5: // Custom method of object 719 | $obj->CurrRec = $this->SrcId->tbsdb_fetch($this->RecSet,$obj->RecNum+1); 720 | break; 721 | case 7: // PostgreSQL 722 | $obj->CurrRec = pg_fetch_assoc($this->RecSet); 723 | break; 724 | case 8: // SQLite 725 | $obj->CurrRec = sqlite_fetch_array($this->RecSet,SQLITE_ASSOC); 726 | break; 727 | case 9: // Iterator 728 | if ($this->RecSet->valid()) { 729 | $obj->CurrRec = $this->RecSet->current(); 730 | $obj->RecKey = $this->RecSet->key(); 731 | $this->RecSet->next(); 732 | } else { 733 | $obj->CurrRec = false; 734 | } 735 | break; 736 | case 10: // MySQLi 737 | $obj->CurrRec = $this->RecSet->fetch_assoc(); 738 | if (is_null($obj->CurrRec)) $obj->CurrRec = false; 739 | break; 740 | case 11: // PDO 741 | $obj->CurrRec = $this->RecSet->fetch(PDO::FETCH_ASSOC); 742 | break; 743 | case 12: // Zend_DB_Adapater 744 | $obj->CurrRec = $this->RecSet->fetch(Zend_Db::FETCH_ASSOC); 745 | break; 746 | case 13: // SQLite3 747 | $obj->CurrRec = $this->RecSet->fetchArray(SQLITE3_ASSOC); 748 | break; 749 | case 14: // Doctrine DBAL 750 | $obj->CurrRec = $this->RecSet->fetchAssociative(); 751 | break; 752 | } 753 | 754 | // Set the row count 755 | if ($obj->CurrRec!==false) { 756 | $obj->RecNum++; 757 | if ($this->OnDataOk) { 758 | if ($this->OnDataPrm) call_user_func_array($this->OnDataPrmRef,$this->OnDataArgs); 759 | if ($this->OnDataPi) $this->TBS->meth_PlugIn_RunAll($this->OnDataPiRef,$this->OnDataArgs); 760 | } 761 | if ($this->RecSaving) $this->RecBuffer[$obj->RecKey] = $obj->CurrRec; 762 | } 763 | 764 | } 765 | 766 | } 767 | 768 | // ********************************************* 769 | 770 | class clsTinyButStrong { 771 | 772 | // Public properties 773 | public $Source = ''; 774 | public $Render = 3; 775 | public $TplVars = array(); 776 | public $ObjectRef = false; 777 | public $NoErr = false; 778 | public $Assigned = array(); 779 | public $ExtendedMethods = array(); 780 | public $ErrCount = 0; 781 | // Undocumented (can change at any version) 782 | public $Version = '3.15.0'; 783 | public $Charset = ''; 784 | public $TurboBlock = true; 785 | public $VarPrefix = ''; 786 | public $VarRef = null; 787 | public $FctPrefix = ''; 788 | public $Protect = true; 789 | public $ErrMsg = ''; 790 | public $AttDelim = false; 791 | public $MethodsAllowed = false; 792 | public $ScriptsAllowed = false; 793 | public $OnLoad = true; 794 | public $OnShow = true; 795 | public $IncludePath = array(); 796 | public $TplStore = array(); 797 | public $OldSubTpl = false; // turn to true to have compatibility with the old way to perform subtemplates, that is get output buffuring 798 | // Private 799 | public $_ErrMsgName = ''; 800 | public $_LastFile = ''; 801 | public $_CharsetFct = false; 802 | public $_Mode = 0; 803 | public $_CurrBlock = ''; 804 | public $_ChrOpen = '['; 805 | public $_ChrClose = ']'; 806 | public $_ChrVal = '[val]'; 807 | public $_ChrProtect = '['; 808 | public $_PlugIns = array(); 809 | public $_PlugIns_Ok = false; 810 | public $_piOnFrm_Ok = false; 811 | 812 | // Compatibility with PHP 8.2 813 | private $_UserFctLst; 814 | private $_Subscript; 815 | public $CurrPrm; 816 | 817 | // Plug-in events 818 | public $_piOnData; // must be public because used by clsTbsDataSource, otherwise the plugi is never 819 | private $_piBeforeLoadTemplate; 820 | private $_piAfterLoadTemplate; 821 | private $_piOnMergeField; 822 | private $_piBeforeShow; 823 | private $_piAfterShow; 824 | private $_piOnCommand; 825 | private $_piOnOperation; 826 | private $_piOnCacheField; 827 | private $_PlugIns_Ok_save; 828 | private $_piOnFrm_Ok_save; 829 | private $_piOnFormat; 830 | private $_piBeforeMergeBlock; 831 | private $_piOnMergeSection; 832 | private $_piOnMergeGroup; 833 | private $_piAfterMergeBlock; 834 | private $_piOnSpecialVar; 835 | 836 | // OpenTBS 837 | public $OtbsAutoLoad; 838 | public $OtbsConvBr; 839 | public $OtbsAutoUncompress; 840 | public $OtbsConvertApostrophes; 841 | public $OtbsSpacePreserve; 842 | public $OtbsClearWriter; 843 | public $OtbsClearMsWord; 844 | public $OtbsMsExcelConsistent; 845 | public $OtbsMsExcelExplicitRef; 846 | public $OtbsClearMsPowerpoint; 847 | public $OtbsGarbageCollector; 848 | public $OtbsMsExcelCompatibility; 849 | public $OtbsCurrFile; 850 | public $OtbsSubFileLst; 851 | public $TbsZip; 852 | 853 | function __construct($Options=null,$VarPrefix='',$FctPrefix='') { 854 | 855 | // Compatibility 856 | if (is_string($Options)) { 857 | $Chrs = $Options; 858 | $Options = array('var_prefix'=>$VarPrefix, 'fct_prefix'=>$FctPrefix); 859 | if ($Chrs!=='') { 860 | $Err = true; 861 | $Len = strlen($Chrs); 862 | if ($Len===2) { // For compatibility 863 | $Options['chr_open'] = $Chrs[0]; 864 | $Options['chr_close'] = $Chrs[1]; 865 | $Err = false; 866 | } else { 867 | $Pos = strpos($Chrs,','); 868 | if (($Pos!==false) && ($Pos>0) && ($Pos<$Len-1)) { 869 | $Options['chr_open'] = substr($Chrs,0,$Pos); 870 | $Options['chr_close'] = substr($Chrs,$Pos+1); 871 | $Err = false; 872 | } 873 | } 874 | if ($Err) $this->meth_Misc_Alert('with clsTinyButStrong() function','value \''.$Chrs.'\' is a bad tag delimitor definition.'); 875 | } 876 | } 877 | 878 | // Set VarRef initial value 879 | $this->ResetVarRef(true); 880 | 881 | // Set options 882 | if (is_array($Options)) $this->SetOption($Options); 883 | 884 | // Links to global variables (cannot be converted to static yet because of compatibility) 885 | global $_TBS_FormatLst, $_TBS_UserFctLst, $_TBS_BlockAlias, $_TBS_PrmCombo, $_TBS_AutoInstallPlugIns, $_TBS_ParallelLst; 886 | if (!isset($_TBS_FormatLst)) $_TBS_FormatLst = array(); 887 | if (!isset($_TBS_UserFctLst)) $_TBS_UserFctLst = array(); 888 | if (!isset($_TBS_BlockAlias)) $_TBS_BlockAlias = array(); 889 | if (!isset($_TBS_PrmCombo)) $_TBS_PrmCombo = array(); 890 | if (!isset($_TBS_ParallelLst)) $_TBS_ParallelLst = array(); 891 | $this->_UserFctLst = &$_TBS_UserFctLst; 892 | 893 | // Auto-installing plug-ins 894 | if (isset($_TBS_AutoInstallPlugIns)) foreach ($_TBS_AutoInstallPlugIns as $pi) $this->PlugIn(TBS_INSTALL,$pi); 895 | 896 | } 897 | 898 | function __call($meth, $args) { 899 | if (isset($this->ExtendedMethods[$meth])) { 900 | if ( is_array($this->ExtendedMethods[$meth]) || is_string($this->ExtendedMethods[$meth]) ) { 901 | return call_user_func_array($this->ExtendedMethods[$meth], $args); 902 | } else { 903 | return call_user_func_array(array(&$this->ExtendedMethods[$meth], $meth), $args); 904 | } 905 | } else { 906 | $this->meth_Misc_Alert('Method not found','\''.$meth.'\' is neither a native nor an extended method of TinyButStrong.'); 907 | } 908 | } 909 | 910 | function SetOption($o, $v=false, $d=false) { 911 | if (!is_array($o)) $o = array($o=>$v); 912 | if (isset($o['var_prefix'])) $this->VarPrefix = $o['var_prefix']; 913 | if (isset($o['fct_prefix'])) $this->FctPrefix = $o['fct_prefix']; 914 | if (isset($o['noerr'])) $this->NoErr = $o['noerr']; 915 | if (isset($o['old_subtemplate'])) $this->OldSubTpl = $o['old_subtemplate']; 916 | if (isset($o['auto_merge'])) { 917 | $this->OnLoad = $o['auto_merge']; 918 | $this->OnShow = $o['auto_merge']; 919 | } 920 | if (isset($o['onload'])) $this->OnLoad = $o['onload']; 921 | if (isset($o['onshow'])) $this->OnShow = $o['onshow']; 922 | if (isset($o['att_delim'])) $this->AttDelim = $o['att_delim']; 923 | if (isset($o['protect'])) $this->Protect = $o['protect']; 924 | if (isset($o['turbo_block'])) $this->TurboBlock = $o['turbo_block']; 925 | if (isset($o['charset'])) $this->meth_Misc_Charset($o['charset']); 926 | 927 | $UpdateChr = false; 928 | if (isset($o['chr_open'])) { 929 | $this->_ChrOpen = $o['chr_open']; 930 | $UpdateChr = true; 931 | } 932 | if (isset($o['chr_close'])) { 933 | $this->_ChrClose = $o['chr_close']; 934 | $UpdateChr = true; 935 | } 936 | if ($UpdateChr) { 937 | $this->_ChrVal = $this->_ChrOpen.'val'.$this->_ChrClose; 938 | $this->_ChrProtect = '&#'.ord($this->_ChrOpen[0]).';'.substr($this->_ChrOpen,1); 939 | } 940 | if (array_key_exists('tpl_frms',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_FormatLst'], 'frm', $o['tpl_frms'], $d); 941 | if (array_key_exists('block_alias',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_BlockAlias'], false, $o['block_alias'], $d); 942 | if (array_key_exists('prm_combo',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_PrmCombo'], 'prm', $o['prm_combo'], $d); 943 | if (array_key_exists('parallel_conf',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_ParallelLst'], false, $o['parallel_conf'], $d); 944 | if (array_key_exists('include_path',$o)) self::f_Misc_UpdateArray($this->IncludePath, true, $o['include_path'], $d); 945 | if (isset($o['render'])) $this->Render = $o['render']; 946 | if (isset($o['methods_allowed'])) $this->MethodsAllowed = $o['methods_allowed']; 947 | if (isset($o['scripts_allowed'])) $this->ScriptsAllowed = $o['scripts_allowed']; 948 | } 949 | 950 | function GetOption($o) { 951 | if ($o==='all') { 952 | $x = explode(',', 'var_prefix,fct_prefix,noerr,auto_merge,onload,onshow,att_delim,protect,turbo_block,charset,chr_open,chr_close,tpl_frms,block_alias,parallel_conf,include_path,render,prm_combo'); 953 | $r = array(); 954 | foreach ($x as $o) $r[$o] = $this->GetOption($o); 955 | return $r; 956 | } 957 | if ($o==='var_prefix') return $this->VarPrefix; 958 | if ($o==='fct_prefix') return $this->FctPrefix; 959 | if ($o==='noerr') return $this->NoErr; 960 | if ($o==='auto_merge') return ($this->OnLoad && $this->OnShow); 961 | if ($o==='onload') return $this->OnLoad; 962 | if ($o==='onshow') return $this->OnShow; 963 | if ($o==='att_delim') return $this->AttDelim; 964 | if ($o==='protect') return $this->Protect; 965 | if ($o==='turbo_block') return $this->TurboBlock; 966 | if ($o==='charset') return $this->Charset; 967 | if ($o==='chr_open') return $this->_ChrOpen; 968 | if ($o==='chr_close') return $this->_ChrClose; 969 | if ($o==='tpl_frms') { 970 | // simplify the list of formats 971 | $x = array(); 972 | foreach ($GLOBALS['_TBS_FormatLst'] as $s=>$i) $x[$s] = $i['Str']; 973 | return $x; 974 | } 975 | if ($o==='include_path') return $this->IncludePath; 976 | if ($o==='render') return $this->Render; 977 | if ($o==='methods_allowed') return $this->MethodsAllowed; 978 | if ($o==='scripts_allowed') return $this->ScriptsAllowed; 979 | if ($o==='parallel_conf') return $GLOBALS['_TBS_ParallelLst']; 980 | if ($o==='block_alias') return $GLOBALS['_TBS_BlockAlias']; 981 | if ($o==='prm_combo') return $GLOBALS['_TBS_PrmCombo']; 982 | return $this->meth_Misc_Alert('with GetOption() method','option \''.$o.'\' is not supported.');; 983 | } 984 | 985 | public function ResetVarRef($ToGlobal) { 986 | // We set a new variable in order to force the reference 987 | // value NULL means that VarRef refers to $GLOBALS 988 | $x = ($ToGlobal) ? null : array(); 989 | $this->VarRef = &$x; 990 | } 991 | 992 | /** 993 | * Get an item value from VarRef. 994 | * Ensure the compatibility with PHP 8.1 if VarRef is set to Global. 995 | * 996 | * @param string $key The item key. 997 | * @param mixed $default The default value. 998 | * 999 | * @return mixed 1000 | */ 1001 | public function GetVarRefItem($key, $default) { 1002 | 1003 | if (is_null($this->VarRef)) { 1004 | 1005 | if (array_key_exists($key, $GLOBALS)) { 1006 | return $GLOBALS[$key]; 1007 | } else { 1008 | return $default; 1009 | } 1010 | 1011 | } else { 1012 | 1013 | if (array_key_exists($key, $this->VarRef)) { 1014 | return $this->VarRef[$key]; 1015 | } else { 1016 | return $default; 1017 | } 1018 | 1019 | } 1020 | 1021 | } 1022 | 1023 | /** 1024 | * Set an item value to VarRef. 1025 | * Ensure the compatibility with PHP 8.1 if VarRef is set to Global. 1026 | * 1027 | * @param string|array $keyOrList A list of keys and items to add, or the item key. 1028 | * @param mixed $value (optional) The item value. Use NULL in order to delete the item. 1029 | */ 1030 | public function SetVarRefItem($keyOrList, $value = null) { 1031 | 1032 | if (is_array($keyOrList)) { 1033 | $list = $keyOrList; 1034 | } else { 1035 | $list = array($keyOrList => $value); 1036 | } 1037 | 1038 | if (is_null($this->VarRef)) { 1039 | 1040 | foreach ($list as $key => $value) { 1041 | if (is_null($value)) { 1042 | unset($GLOBALS[$key]); 1043 | } else { 1044 | $GLOBALS[$key] = $value; 1045 | } 1046 | } 1047 | 1048 | } else { 1049 | 1050 | foreach ($list as $key => $value) { 1051 | if (is_null($value)) { 1052 | unset($this->VarRef[$key]); 1053 | } else { 1054 | $this->VarRef[$key] = $value; 1055 | } 1056 | } 1057 | 1058 | } 1059 | 1060 | } 1061 | 1062 | // Public methods 1063 | public function LoadTemplate($File,$Charset='') { 1064 | if ($File==='') { 1065 | $this->meth_Misc_Charset($Charset); 1066 | return true; 1067 | } 1068 | $Ok = true; 1069 | if ($this->_PlugIns_Ok) { 1070 | if (isset($this->_piBeforeLoadTemplate) || isset($this->_piAfterLoadTemplate)) { 1071 | // Plug-ins 1072 | $ArgLst = func_get_args(); 1073 | $ArgLst[0] = &$File; 1074 | $ArgLst[1] = &$Charset; 1075 | if (isset($this->_piBeforeLoadTemplate)) $Ok = $this->meth_PlugIn_RunAll($this->_piBeforeLoadTemplate,$ArgLst); 1076 | } 1077 | } 1078 | // Load the file 1079 | if ($Ok!==false) { 1080 | if (!is_null($File)) { 1081 | $x = ''; 1082 | if (!$this->f_Misc_GetFile($x, $File, $this->_LastFile, $this->IncludePath)) return $this->meth_Misc_Alert('with LoadTemplate() method','file \''.$File.'\' is not found or not readable.'); 1083 | if ($Charset==='+') { 1084 | $this->Source .= $x; 1085 | } else { 1086 | $this->Source = $x; 1087 | } 1088 | } 1089 | if ($this->meth_Misc_IsMainTpl()) { 1090 | if (!is_null($File)) $this->_LastFile = $File; 1091 | if ($Charset!=='+') $this->TplVars = array(); 1092 | $this->meth_Misc_Charset($Charset); 1093 | } 1094 | // Automatic fields and blocks 1095 | if ($this->OnLoad) $this->meth_Merge_AutoOn($this->Source,'onload',true,true); 1096 | } 1097 | // Plug-ins 1098 | if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterLoadTemplate)) $Ok = $this->meth_PlugIn_RunAll($this->_piAfterLoadTemplate,$ArgLst); 1099 | return $Ok; 1100 | } 1101 | 1102 | public function GetBlockSource($BlockName,$AsArray=false,$DefTags=true,$ReplaceWith=false) { 1103 | $RetVal = array(); 1104 | $Nbr = 0; 1105 | $Pos = 0; 1106 | $FieldOutside = false; 1107 | $P1 = false; 1108 | $Mode = ($DefTags) ? 3 : 2; 1109 | $PosBeg1 = 0; 1110 | while ($Loc = $this->meth_Locator_FindBlockNext($this->Source,$BlockName,$Pos,'.',$Mode,$P1,$FieldOutside)) { 1111 | $Nbr++; 1112 | $Sep = ''; 1113 | if ($Nbr==1) { 1114 | $PosBeg1 = $Loc->PosBeg; 1115 | } elseif (!$AsArray) { 1116 | $Sep = substr($this->Source,$PosSep,$Loc->PosBeg-$PosSep); // part of the source between sections 1117 | } 1118 | $RetVal[$Nbr] = $Sep.$Loc->BlockSrc; 1119 | $Pos = $Loc->PosEnd; 1120 | $PosSep = $Loc->PosEnd+1; 1121 | $P1 = false; 1122 | } 1123 | if ($Nbr==0) return false; 1124 | if (!$AsArray) { 1125 | if ($DefTags) { 1126 | // Return the true part of the template 1127 | $RetVal = substr($this->Source,$PosBeg1,$Pos-$PosBeg1+1); 1128 | } else { 1129 | // Return the concatenated section without def tags 1130 | $RetVal = implode('', $RetVal); 1131 | } 1132 | } 1133 | if ($ReplaceWith!==false) $this->Source = substr($this->Source,0,$PosBeg1).$ReplaceWith.substr($this->Source,$Pos+1); 1134 | return $RetVal; 1135 | } 1136 | 1137 | /** 1138 | * Get the value of a XML-HTML attribute targeted thanks to a TBS fields having parameter att. 1139 | * @param string $Name Name of the TBS fields. It must have parameter att. 1140 | * @param boolean $delete (optional, true by default) Use true to delete the TBS field. 1141 | * @return string|true|null|false The value of the attribute, 1142 | * true if the attribute is found without value, 1143 | * null if the TBS field, the target element is not found, 1144 | * or false for other error. 1145 | */ 1146 | public function GetAttValue($Name, $delete = true) { 1147 | $Pos = 0; 1148 | $val = null; 1149 | while ($Loc = $this->meth_Locator_FindTbs($this->Source,$Name,$Pos,'.')) { 1150 | if (isset($Loc->PrmLst['att'])) { 1151 | if ($this->f_Xml_AttFind($this->Source,$Loc,false,$this->AttDelim)) { 1152 | $val = false; 1153 | if ($Loc->AttBeg !== false) { 1154 | if ($Loc->AttValBeg !== false) { 1155 | $val = substr($this->Source, $Loc->AttValBeg, $Loc->AttEnd - $Loc->AttValBeg + 1); 1156 | $val = substr($val, 1, -1); 1157 | } else { 1158 | $val = true; 1159 | } 1160 | } else { 1161 | // not found 1162 | } 1163 | } else { 1164 | // att not found 1165 | } 1166 | } else { 1167 | // no att parameter 1168 | } 1169 | 1170 | if ($delete) { 1171 | $this->Source = substr_replace($this->Source, '', $Loc->PosBeg, $Loc->PosEnd - $Loc->PosBeg + 1); 1172 | $Pos = $Loc->PosBeg; 1173 | } else { 1174 | $Pos = $Loc->PosEnd; 1175 | } 1176 | } 1177 | return $val; 1178 | } 1179 | 1180 | public function MergeBlock($BlockLst,$SrcId='assigned',$Query='',$QryPrms=false) { 1181 | 1182 | if ($SrcId==='assigned') { 1183 | $Arg = array($BlockLst,&$SrcId,&$Query,&$QryPrms); 1184 | if (!$this->meth_Misc_Assign($BlockLst, $Arg, 'MergeBlock')) return 0; 1185 | $BlockLst = $Arg[0]; $SrcId = &$Arg[1]; $Query = &$Arg[2]; 1186 | } 1187 | 1188 | if (is_string($BlockLst)) $BlockLst = explode(',',$BlockLst); 1189 | 1190 | if ($SrcId==='cond') { 1191 | $Nbr = 0; 1192 | foreach ($BlockLst as $Block) { 1193 | $Block = trim($Block); 1194 | if ($Block!=='') $Nbr += $this->meth_Merge_AutoOn($this->Source,$Block,true,true); 1195 | } 1196 | return $Nbr; 1197 | } else { 1198 | return $this->meth_Merge_Block($this->Source,$BlockLst,$SrcId,$Query,false,0,$QryPrms); 1199 | } 1200 | 1201 | } 1202 | 1203 | public function MergeField($NameLst,$Value='assigned',$IsUserFct=false,$DefaultPrm=false) { 1204 | 1205 | $FctCheck = $IsUserFct; 1206 | if ($PlugIn = isset($this->_piOnMergeField)) $ArgPi = array('','',&$Value,0,&$this->Source,0,0); 1207 | $SubStart = 0; 1208 | $Ok = true; 1209 | $Prm = is_array($DefaultPrm); 1210 | 1211 | if ( ($Value==='assigned') && ($NameLst!=='var') && ($NameLst!=='onshow') && ($NameLst!=='onload') ) { 1212 | $Arg = array($NameLst,&$Value,&$IsUserFct,&$DefaultPrm); 1213 | if (!$this->meth_Misc_Assign($NameLst, $Arg, 'MergeField')) return false; 1214 | $NameLst = $Arg[0]; $Value = &$Arg[1]; $IsUserFct = &$Arg[2]; $DefaultPrm = &$Arg[3]; 1215 | } 1216 | 1217 | $NameLst = explode(',',$NameLst); 1218 | 1219 | foreach ($NameLst as $Name) { 1220 | $Name = trim($Name); 1221 | $Cont = false; 1222 | switch ($Name) { 1223 | case '': $Cont=true;break; 1224 | case 'onload': $this->meth_Merge_AutoOn($this->Source,'onload',true,true);$Cont=true;break; 1225 | case 'onshow': $this->meth_Merge_AutoOn($this->Source,'onshow',true,true);$Cont=true;break; 1226 | case 'var': $this->meth_Merge_AutoVar($this->Source,true);$Cont=true;break; 1227 | } 1228 | if ($Cont) continue; 1229 | if ($PlugIn) $ArgPi[0] = $Name; 1230 | $PosBeg = 0; 1231 | // Initilize the user function (only once) 1232 | if ($FctCheck) { 1233 | $FctInfo = $Value; 1234 | $ErrMsg = false; 1235 | if (!$this->meth_Misc_UserFctCheck($FctInfo,'f',$ErrMsg,$ErrMsg,false)) return $this->meth_Misc_Alert('with MergeField() method',$ErrMsg); 1236 | $FctArg = array('',''); 1237 | $SubStart = false; 1238 | $FctCheck = false; 1239 | } 1240 | while ($Loc = $this->meth_Locator_FindTbs($this->Source,$Name,$PosBeg,'.')) { 1241 | if ($Prm) $Loc->PrmLst = array_merge($DefaultPrm,$Loc->PrmLst); 1242 | // Apply user function 1243 | if ($IsUserFct) { 1244 | $FctArg[0] = &$Loc->SubName; $FctArg[1] = &$Loc->PrmLst; 1245 | $Value = call_user_func_array($FctInfo,$FctArg); 1246 | } 1247 | // Plug-ins 1248 | if ($PlugIn) { 1249 | $ArgPi[1] = $Loc->SubName; $ArgPi[3] = &$Loc->PrmLst; $ArgPi[5] = &$Loc->PosBeg; $ArgPi[6] = &$Loc->PosEnd; 1250 | $Ok = $this->meth_PlugIn_RunAll($this->_piOnMergeField,$ArgPi); 1251 | } 1252 | // Merge the field 1253 | if ($Ok) { 1254 | $PosBeg = $this->meth_Locator_Replace($this->Source,$Loc,$Value,$SubStart); 1255 | } else { 1256 | $PosBeg = $Loc->PosEnd; 1257 | } 1258 | } 1259 | } 1260 | } 1261 | 1262 | /** 1263 | * Replace a set of simple TBS fields (that is fields without any parameters) with more complexe TBS fields. 1264 | * @param array $fields An associative array of items to replace. 1265 | * Keys are the name of the simple field to replace. 1266 | * Values are the parameters of the field as an array or as a string. 1267 | * Parameter 'name' will be used as the new name of the field, by default it is the same name as the simple field. 1268 | * @param string $blockName (optional) The name of the block for prefixing fields. 1269 | */ 1270 | public function ReplaceFields($fields, $blockName = false) { 1271 | 1272 | $prefix = ($blockName) ? $blockName . '.' : ''; 1273 | 1274 | // calling the replace using array is faster than a loop 1275 | $what = array(); 1276 | $with = array(); 1277 | foreach ($fields as $name => $prms) { 1278 | $what[] = $this->_ChrOpen . $name . $this->_ChrClose; 1279 | if (is_array($prms)) { 1280 | // field replace 1281 | $lst = ''; 1282 | foreach ($prms as $p => $v) { 1283 | if ($p === 'name') { 1284 | $name = $v; 1285 | } else { 1286 | if ($v === true) { 1287 | $lst .= ';' . $p; 1288 | } elseif (is_array($v)) { 1289 | foreach($v as $x) { 1290 | $lst .= ';' . $p . '=' . $x; 1291 | } 1292 | } else { 1293 | $lst .= ';' . $p . '=' . $v; 1294 | } 1295 | } 1296 | } 1297 | $with[] = $this->_ChrOpen . $prefix . $name . $lst . $this->_ChrClose; 1298 | } else { 1299 | // simple string replace 1300 | $with[] = $prms; 1301 | } 1302 | } 1303 | 1304 | $this->Source = str_replace($what, $with, $this->Source); 1305 | 1306 | } 1307 | 1308 | public function Show($Render=false) { 1309 | $Ok = true; 1310 | if ($Render===false) $Render = $this->Render; 1311 | if ($this->_PlugIns_Ok) { 1312 | if (isset($this->_piBeforeShow) || isset($this->_piAfterShow)) { 1313 | // Plug-ins 1314 | $ArgLst = func_get_args(); 1315 | $ArgLst[0] = &$Render; 1316 | if (isset($this->_piBeforeShow)) $Ok = $this->meth_PlugIn_RunAll($this->_piBeforeShow,$ArgLst); 1317 | } 1318 | } 1319 | if ($Ok!==false) { 1320 | if ($this->OnShow) $this->meth_Merge_AutoOn($this->Source,'onshow',true,true); 1321 | $this->meth_Merge_AutoVar($this->Source,true); 1322 | } 1323 | if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterShow)) $this->meth_PlugIn_RunAll($this->_piAfterShow,$ArgLst); 1324 | if ($this->_ErrMsgName!=='') $this->MergeField($this->_ErrMsgName, $this->ErrMsg); 1325 | if ($this->meth_Misc_IsMainTpl()) { 1326 | if (($Render & TBS_OUTPUT)==TBS_OUTPUT) echo $this->Source; 1327 | if (($Render & TBS_EXIT)==TBS_EXIT) exit; 1328 | } elseif ($this->OldSubTpl) { 1329 | if (($Render & TBS_OUTPUT)==TBS_OUTPUT) echo $this->Source; 1330 | } 1331 | return $Ok; 1332 | } 1333 | 1334 | public function PlugIn($Prm1,$Prm2=0) { 1335 | 1336 | if (is_numeric($Prm1)) { 1337 | switch ($Prm1) { 1338 | case TBS_INSTALL: // Try to install the plug-in 1339 | $PlugInId = $Prm2; 1340 | if (isset($this->_PlugIns[$PlugInId])) { 1341 | return $this->meth_Misc_Alert('with PlugIn() method','plug-in \''.$PlugInId.'\' is already installed.'); 1342 | } else { 1343 | $ArgLst = func_get_args(); 1344 | array_shift($ArgLst); array_shift($ArgLst); 1345 | return $this->meth_PlugIn_Install($PlugInId,$ArgLst,false); 1346 | } 1347 | case TBS_ISINSTALLED: // Check if the plug-in is installed 1348 | return isset($this->_PlugIns[$Prm2]); 1349 | case -4: // Deactivate special plug-ins 1350 | $this->_PlugIns_Ok_save = $this->_PlugIns_Ok; 1351 | $this->_PlugIns_Ok = false; 1352 | return true; 1353 | case -5: // Deactivate OnFormat 1354 | $this->_piOnFrm_Ok_save = $this->_piOnFrm_Ok; 1355 | $this->_piOnFrm_Ok = false; 1356 | return true; 1357 | case -10: // Restore 1358 | if (isset($this->_PlugIns_Ok_save)) $this->_PlugIns_Ok = $this->_PlugIns_Ok_save; 1359 | if (isset($this->_piOnFrm_Ok_save)) $this->_piOnFrm_Ok = $this->_piOnFrm_Ok_save; 1360 | return true; 1361 | } 1362 | 1363 | } elseif (is_string($Prm1)) { 1364 | // Plug-in's command 1365 | $p = strpos($Prm1,'.'); 1366 | if ($p===false) { 1367 | $PlugInId = $Prm1; 1368 | } else { 1369 | $PlugInId = substr($Prm1,0,$p); // direct command 1370 | } 1371 | if (!isset($this->_PlugIns[$PlugInId])) { 1372 | if (!$this->meth_PlugIn_Install($PlugInId,array(),true)) return false; 1373 | } 1374 | if (!isset($this->_piOnCommand[$PlugInId])) return $this->meth_Misc_Alert('with PlugIn() method','plug-in \''.$PlugInId.'\' can\'t run any command because the OnCommand event is not defined or activated.'); 1375 | $ArgLst = func_get_args(); 1376 | if ($p===false) array_shift($ArgLst); 1377 | $Ok = call_user_func_array($this->_piOnCommand[$PlugInId],$ArgLst); 1378 | if (is_null($Ok)) $Ok = true; 1379 | return $Ok; 1380 | } 1381 | return $this->meth_Misc_Alert('with PlugIn() method','\''.$Prm1.'\' is an invalid plug-in key, the type of the value is \''.gettype($Prm1).'\'.'); 1382 | 1383 | } 1384 | 1385 | // *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- 1386 | 1387 | function meth_Locator_FindTbs(&$Txt,$Name,$Pos,$ChrSub) { 1388 | // Find a TBS Locator 1389 | 1390 | $PosEnd = false; 1391 | $PosMax = strlen($Txt) -1; 1392 | $Start = $this->_ChrOpen.$Name; 1393 | 1394 | do { 1395 | // Search for the opening char 1396 | if ($Pos>$PosMax) return false; 1397 | $Pos = strpos($Txt,$Start,$Pos); 1398 | 1399 | // If found => next chars are analyzed 1400 | if ($Pos===false) { 1401 | return false; 1402 | } else { 1403 | $Loc = new clsTbsLocator; 1404 | $ReadPrm = false; 1405 | $PosX = $Pos + strlen($Start); 1406 | $x = $Txt[$PosX]; 1407 | 1408 | if ($x===$this->_ChrClose) { 1409 | $PosEnd = $PosX; 1410 | } elseif ($x===$ChrSub) { 1411 | $Loc->SubOk = true; // it is no longer the false value 1412 | $ReadPrm = true; 1413 | $PosX++; 1414 | } elseif (strpos(';',$x)!==false) { 1415 | $ReadPrm = true; 1416 | $PosX++; 1417 | } else { 1418 | $Pos++; 1419 | } 1420 | 1421 | $Loc->PosBeg = $Pos; 1422 | if ($ReadPrm) { 1423 | self::f_Loc_PrmRead($Txt,$PosX,false,'\'',$this->_ChrOpen,$this->_ChrClose,$Loc,$PosEnd); 1424 | if ($PosEnd===false) { 1425 | $this->meth_Misc_Alert('','can\'t found the end of the tag \''.substr($Txt,$Pos,$PosX-$Pos+10).'...\'.'); 1426 | $Pos++; 1427 | } else { 1428 | self::meth_Misc_ApplyPrmCombo($Loc->PrmLst, $Loc); 1429 | } 1430 | } 1431 | 1432 | } 1433 | 1434 | } while ($PosEnd===false); 1435 | 1436 | $Loc->PosEnd = $PosEnd; 1437 | if ($Loc->SubOk) { 1438 | $Loc->FullName = $Name.'.'.$Loc->SubName; 1439 | $Loc->SubLst = explode('.',$Loc->SubName); 1440 | $Loc->SubNbr = count($Loc->SubLst); 1441 | } else { 1442 | $Loc->FullName = $Name; 1443 | } 1444 | if ( $ReadPrm && ( isset($Loc->PrmLst['enlarge']) || isset($Loc->PrmLst['comm']) ) ) { 1445 | $Loc->PosBeg0 = $Loc->PosBeg; 1446 | $Loc->PosEnd0 = $Loc->PosEnd; 1447 | $enlarge = (isset($Loc->PrmLst['enlarge'])) ? $Loc->PrmLst['enlarge'] : $Loc->PrmLst['comm']; 1448 | if (($enlarge===true) || ($enlarge==='')) { 1449 | $Loc->Enlarged = self::f_Loc_EnlargeToStr($Txt,$Loc,''); 1450 | } else { 1451 | $Loc->Enlarged = self::f_Loc_EnlargeToTag($Txt,$Loc,$enlarge,false); 1452 | } 1453 | } 1454 | 1455 | return $Loc; 1456 | 1457 | } 1458 | 1459 | /** 1460 | * Note: keep the « & » if the function is called with it. 1461 | * 1462 | * @return object 1463 | */ 1464 | function meth_Locator_SectionNewBDef(&$LocR,$BlockName,$Txt,$PrmLst,$Cache) { 1465 | 1466 | $Chk = true; 1467 | $LocLst = array(); 1468 | $Pos = 0; 1469 | $Sort = false; 1470 | 1471 | if ($this->_PlugIns_Ok && isset($this->_piOnCacheField)) { 1472 | $pi = true; 1473 | $ArgLst = array(0=>$BlockName, 1=>false, 2=>&$Txt, 3=>array('att'=>true), 4=>&$LocLst, 5=>&$Pos); 1474 | } else { 1475 | $pi = false; 1476 | } 1477 | 1478 | // Cache TBS locators 1479 | $Cache = ($Cache && $this->TurboBlock); 1480 | if ($Cache) { 1481 | 1482 | $Chk = false; 1483 | while ($Loc = $this->meth_Locator_FindTbs($Txt,$BlockName,$Pos,'.')) { 1484 | 1485 | $LocNbr = 1 + count($LocLst); 1486 | $LocLst[$LocNbr] = &$Loc; 1487 | 1488 | // Next search position : always ("original PosBeg" + 1). 1489 | // Must be done here because loc can be moved by the plug-in. 1490 | if ($Loc->Enlarged) { 1491 | // Enlarged 1492 | $Pos = $Loc->PosBeg0 + 1; 1493 | $Loc->Enlarged = false; 1494 | } else { 1495 | // Normal 1496 | $Pos = $Loc->PosBeg + 1; 1497 | } 1498 | 1499 | // Note: the plug-in may move, delete and add one or several locs. 1500 | // Move : backward or forward (will be sorted) 1501 | // Delete : add property DelMe=true 1502 | // Add : at the end of $LocLst (will be sorted) 1503 | if ($pi) { 1504 | $ArgLst[1] = &$Loc; 1505 | $this->meth_Plugin_RunAll($this->_piOnCacheField,$ArgLst); 1506 | } 1507 | 1508 | if (($Loc->SubName==='#') || ($Loc->SubName==='$')) { 1509 | $Loc->IsRecInfo = true; 1510 | $Loc->RecInfo = $Loc->SubName; 1511 | $Loc->SubName = ''; 1512 | } else { 1513 | $Loc->IsRecInfo = false; 1514 | } 1515 | 1516 | // Process parameter att for new added locators. 1517 | $NewNbr = count($LocLst); 1518 | for ($i=$LocNbr;$i<=$NewNbr;$i++) { 1519 | $li = &$LocLst[$i]; 1520 | if (isset($li->PrmLst['att'])) { 1521 | $LocSrc = substr($Txt,$li->PosBeg,$li->PosEnd-$li->PosBeg+1); // for error message 1522 | if ($this->f_Xml_AttFind($Txt,$li,$LocLst,$this->AttDelim)) { 1523 | if (isset($Loc->PrmLst['atttrue'])) { 1524 | $li->PrmLst['magnet'] = '#'; 1525 | $li->PrmLst['ope'] = (isset($li->PrmLst['ope'])) ? $li->PrmLst['ope'].',attbool' : 'attbool'; 1526 | } 1527 | if ($i==$LocNbr) { 1528 | $Pos = $Loc->DelPos; 1529 | } 1530 | } else { 1531 | $this->meth_Misc_Alert('','TBS is not able to merge the field '.$LocSrc.' because the entity targeted by parameter \'att\' cannot be found.'); 1532 | } 1533 | } 1534 | } 1535 | 1536 | unset($Loc); 1537 | 1538 | } 1539 | 1540 | // Re-order loc 1541 | $e = self::f_Loc_Sort($LocLst, true, 1); 1542 | $Chk = ($e > 0); 1543 | 1544 | } 1545 | 1546 | // Create the object 1547 | $o = (object) null; 1548 | $o->Prm = $PrmLst; 1549 | $o->LocLst = $LocLst; 1550 | $o->LocNbr = count($LocLst); 1551 | $o->Name = $BlockName; 1552 | $o->Src = $Txt; 1553 | $o->Chk = $Chk; 1554 | $o->IsSerial = false; 1555 | $o->AutoSub = false; 1556 | $i = 1; 1557 | while (isset($PrmLst['sub'.$i])) { 1558 | $o->AutoSub = $i; 1559 | $i++; 1560 | } 1561 | 1562 | $LocR->BDefLst[] = &$o; // Can be usefull for plug-in 1563 | return $o; 1564 | 1565 | } 1566 | 1567 | /** 1568 | * Add a special section to the LocR. 1569 | * 1570 | * @param object $LocR 1571 | * @param string $BlockName 1572 | * @param object $BDef 1573 | * @param string $Field Name of the field on which the group of values is defined. 1574 | * @param string $FromPrm Parameter that induced the section. 1575 | * 1576 | * @return object 1577 | */ 1578 | function meth_Locator_MakeBDefFromField(&$LocR,$BlockName,$Field,$FromPrm) { 1579 | 1580 | if (strpos($Field,$this->_ChrOpen)===false) { 1581 | // The field is a simple colmun name 1582 | $src = $this->_ChrOpen.$BlockName.'.'.$Field.';tbstype='.$FromPrm.$this->_ChrClose; // tbstype is an internal parameter for catching errors 1583 | } else { 1584 | // The fields is a TBS field's expression 1585 | $src = $Field; 1586 | } 1587 | 1588 | $BDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,$src,array(),true); 1589 | 1590 | if ($BDef->LocNbr==0) $this->meth_Misc_Alert('Parameter '.$FromPrm,'The value \''.$Field.'\' is unvalide for this parameter.'); 1591 | 1592 | return $BDef; 1593 | 1594 | } 1595 | 1596 | /** 1597 | * Add a special section to the LocR. 1598 | * 1599 | * @param object $LocR 1600 | * @param string $BlockName 1601 | * @param object $BDef 1602 | * @param string $Type Type of behavior: 'H' header, 'F' footer, 'S' splitter. 1603 | * @param string $Field Name of the field on which the group of values is defined. 1604 | * @param string $FromPrm Parameter that induced the section. 1605 | */ 1606 | function meth_Locator_SectionAddGrp(&$LocR,$BlockName,&$BDef,$Type,$Field,$FromPrm) { 1607 | 1608 | $BDef->PrevValue = false; 1609 | $BDef->Type = $Type; // property not used in native, but designed for plugins 1610 | 1611 | // Save sub items in a structure near to Locator. 1612 | $BDef->FDef = $this->meth_Locator_MakeBDefFromField($LocR,$BlockName,$Field,$FromPrm); 1613 | 1614 | if ($Type==='H') { 1615 | // Header behavior 1616 | if ($LocR->HeaderFound===false) { 1617 | $LocR->HeaderFound = true; 1618 | $LocR->HeaderNbr = 0; 1619 | $LocR->HeaderDef = array(); // 1 to HeaderNbr 1620 | } 1621 | $i = ++$LocR->HeaderNbr; 1622 | $LocR->HeaderDef[$i] = $BDef; 1623 | } else { 1624 | // Footer behavior (footer or splitter) 1625 | if ($LocR->FooterFound===false) { 1626 | $LocR->FooterFound = true; 1627 | $LocR->FooterNbr = 0; 1628 | $LocR->FooterDef = array(); // 1 to FooterNbr 1629 | } 1630 | $BDef->AddLastGrp = ($Type==='F'); 1631 | $i = ++$LocR->FooterNbr; 1632 | $LocR->FooterDef[$i] = $BDef; 1633 | } 1634 | 1635 | } 1636 | 1637 | /** 1638 | * Merge a locator with a text. 1639 | * 1640 | * @param string $Txt The source string to modify. 1641 | * @param object $Loc The locator of the field to replace. 1642 | * @param mixed $Value The value to merge with. 1643 | * @param integer|false $SubStart The offset of subname to considere. 1644 | * 1645 | * @return integer The position just after the replaced field. Or the position of the start if the replace is canceled. 1646 | * This position can be useful because we don't know in advance how $Value will be replaced. 1647 | * $Loc->PosNext is also set to the next search position when embedded fields are allowed. 1648 | */ 1649 | function meth_Locator_Replace(&$Txt,&$Loc,&$Value,$SubStart) { 1650 | 1651 | // Found the value if there is a subname 1652 | if (($SubStart!==false) && $Loc->SubOk) { 1653 | for ($i=$SubStart;$i<$Loc->SubNbr;$i++) { 1654 | $x = $Loc->SubLst[$i]; // &$Loc... brings an error with Event Example, I don't know why. 1655 | if (is_array($Value)) { 1656 | if (isset($Value[$x])) { 1657 | $Value = &$Value[$x]; 1658 | } elseif (array_key_exists($x,$Value)) {// can happens when value is NULL 1659 | $Value = &$Value[$x]; 1660 | } else { 1661 | if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'item \''.$x.'\' is not an existing key in the array.',true); 1662 | unset($Value); $Value = ''; break; 1663 | } 1664 | } elseif (is_object($Value)) { 1665 | $form = $this->f_Misc_ParseFctForm($x); 1666 | $n = $form['name']; 1667 | if ( method_exists($Value,$n) || ($form['as_fct'] && method_exists($Value,'__call')) ) { 1668 | if ($this->MethodsAllowed || !in_array(strtok($Loc->FullName,'.'),array('onload','onshow','var')) ) { 1669 | $x = call_user_func_array(array(&$Value,$n),$form['args']); 1670 | } else { 1671 | if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'\''.$n.'\' is a method and the current TBS settings do not allow to call methods on automatic fields.',true); 1672 | $x = ''; 1673 | } 1674 | } elseif (property_exists($Value,$n)) { 1675 | $x = &$Value->$n; 1676 | } elseif (isset($Value->$n)) { 1677 | $x = $Value->$n; // useful for overloaded property 1678 | } else { 1679 | if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'item '.$n.'\' is neither a method nor a property in the class \''.get_class($Value).'\'. Overloaded properties must also be available for the __isset() magic method.',true); 1680 | unset($Value); $Value = ''; break; 1681 | } 1682 | $Value = &$x; unset($x); $x = ''; 1683 | } else { 1684 | if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'item before \''.$x.'\' is neither an object nor an array. Its type is '.gettype($Value).'.',true); 1685 | unset($Value); $Value = ''; break; 1686 | } 1687 | } 1688 | } 1689 | 1690 | $CurrVal = $Value; // Unlink 1691 | 1692 | if (isset($Loc->PrmLst['onformat'])) { 1693 | if ($Loc->FirstMerge) { 1694 | $Loc->OnFrmInfo = $Loc->PrmLst['onformat']; 1695 | $Loc->OnFrmArg = array($Loc->FullName,'',&$Loc->PrmLst,&$this); 1696 | $ErrMsg = false; 1697 | if (!$this->meth_Misc_UserFctCheck($Loc->OnFrmInfo,'f',$ErrMsg,$ErrMsg,true)) { 1698 | unset($Loc->PrmLst['onformat']); 1699 | if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'(parameter onformat) '.$ErrMsg); 1700 | $Loc->OnFrmInfo = false; 1701 | } 1702 | } else { 1703 | $Loc->OnFrmArg[3] = &$this; // bugs.php.net/51174 1704 | } 1705 | if ($Loc->OnFrmInfo !== false) { 1706 | $Loc->OnFrmArg[1] = &$CurrVal; 1707 | if (isset($Loc->PrmLst['subtpl'])) { 1708 | $this->meth_Misc_ChangeMode(true,$Loc,$CurrVal); 1709 | call_user_func_array($Loc->OnFrmInfo,$Loc->OnFrmArg); 1710 | $this->meth_Misc_ChangeMode(false,$Loc,$CurrVal); 1711 | $Loc->ConvProtect = false; 1712 | $Loc->ConvStr = false; 1713 | } else { 1714 | call_user_func_array($Loc->OnFrmInfo,$Loc->OnFrmArg); 1715 | } 1716 | } 1717 | } 1718 | 1719 | if ($Loc->FirstMerge) { 1720 | if (isset($Loc->PrmLst['frm'])) { 1721 | $Loc->ConvMode = 0; // Frm 1722 | $Loc->ConvProtect = false; 1723 | } else { 1724 | // Analyze parameter 'strconv' 1725 | if (isset($Loc->PrmLst['strconv'])) { 1726 | $this->meth_Conv_Prepare($Loc, $Loc->PrmLst['strconv']); 1727 | } elseif (isset($Loc->PrmLst['htmlconv'])) { // compatibility 1728 | $this->meth_Conv_Prepare($Loc, $Loc->PrmLst['htmlconv']); 1729 | } else { 1730 | if ($this->Charset===false) $Loc->ConvStr = false; // No conversion 1731 | } 1732 | // Analyze parameter 'protect' 1733 | if (isset($Loc->PrmLst['protect'])) { 1734 | $x = strtolower($Loc->PrmLst['protect']); 1735 | if ($x==='no') { 1736 | $Loc->ConvProtect = false; 1737 | } elseif ($x==='yes') { 1738 | $Loc->ConvProtect = true; 1739 | } 1740 | } elseif ($this->Protect===false) { 1741 | $Loc->ConvProtect = false; 1742 | } 1743 | } 1744 | if ($Loc->Ope = isset($Loc->PrmLst['ope'])) { 1745 | $OpeLst = explode(',',$Loc->PrmLst['ope']); 1746 | $Loc->OpeAct = array(); 1747 | $Loc->OpeArg = array(); 1748 | $Loc->OpeUtf8 = false; 1749 | foreach ($OpeLst as $i=>$ope) { 1750 | if ($ope==='list') { 1751 | $Loc->OpeAct[$i] = 1; 1752 | $Loc->OpePrm[$i] = (isset($Loc->PrmLst['valsep'])) ? $Loc->PrmLst['valsep'] : ','; 1753 | if (($Loc->ConvMode===1) && $Loc->ConvStr) $Loc->ConvMode = -1; // special mode for item list conversion 1754 | } elseif ($ope==='minv') { 1755 | $Loc->OpeAct[$i] = 11; 1756 | $Loc->MSave = $Loc->MagnetId; 1757 | } elseif ($ope==='attbool') { // this operation key is set when a loc is cached with paremeter atttrue 1758 | $Loc->OpeAct[$i] = 14; 1759 | } elseif ($ope==='utf8') { $Loc->OpeUtf8 = true; 1760 | } elseif ($ope==='upper') { $Loc->OpeAct[$i] = 15; 1761 | } elseif ($ope==='lower') { $Loc->OpeAct[$i] = 16; 1762 | } elseif ($ope==='upper1') { $Loc->OpeAct[$i] = 17; 1763 | } elseif ($ope==='upperw') { $Loc->OpeAct[$i] = 18; 1764 | } elseif ($ope==='debug_val') { $Loc->OpeAct[$i] = 19; 1765 | } else { 1766 | $x = substr($ope,0,4); 1767 | if ($x==='max:') { 1768 | $Loc->OpeAct[$i] = (isset($Loc->PrmLst['maxhtml'])) ? 2 : 3; 1769 | if (isset($Loc->PrmLst['maxutf8'])) $Loc->OpeUtf8 = true; 1770 | $Loc->OpePrm[$i] = intval(trim(substr($ope,4))); 1771 | $Loc->OpeEnd = (isset($Loc->PrmLst['maxend'])) ? $Loc->PrmLst['maxend'] : '...'; 1772 | if ($Loc->OpePrm[$i]<=0) $Loc->Ope = false; 1773 | } elseif ($x==='mod:') {$Loc->OpeAct[$i] = 5; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4)); 1774 | } elseif ($x==='add:') {$Loc->OpeAct[$i] = 6; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4)); 1775 | } elseif ($x==='mul:') {$Loc->OpeAct[$i] = 7; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4)); 1776 | } elseif ($x==='div:') {$Loc->OpeAct[$i] = 8; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4)); 1777 | } elseif ($x==='mok:') {$Loc->OpeAct[$i] = 9; $Loc->OpeMOK[] = trim(substr($ope,4)); $Loc->MSave = $Loc->MagnetId; 1778 | } elseif ($x==='mko:') {$Loc->OpeAct[$i] =10; $Loc->OpeMKO[] = trim(substr($ope,4)); $Loc->MSave = $Loc->MagnetId; 1779 | } elseif ($x==='nif:') {$Loc->OpeAct[$i] =12; $Loc->OpePrm[$i] = trim(substr($ope,4)); 1780 | } elseif ($x==='msk:') {$Loc->OpeAct[$i] =13; $Loc->OpePrm[$i] = trim(substr($ope,4)); 1781 | } elseif (isset($this->_piOnOperation)) { 1782 | $Loc->OpeAct[$i] = 0; 1783 | $Loc->OpePrm[$i] = $ope; 1784 | $Loc->OpeArg[$i] = array($Loc->FullName,&$CurrVal,&$Loc->PrmLst,&$Txt,$Loc->PosBeg,$Loc->PosEnd,&$Loc); 1785 | $Loc->PrmLst['_ope'] = $Loc->PrmLst['ope']; 1786 | } elseif (!isset($Loc->PrmLst['noerr'])) { 1787 | $this->meth_Misc_Alert($Loc,'parameter ope doesn\'t support value \''.$ope.'\'.',true); 1788 | } 1789 | } 1790 | } 1791 | } 1792 | $Loc->FirstMerge = false; 1793 | } 1794 | $ConvProtect = $Loc->ConvProtect; 1795 | 1796 | // Plug-in OnFormat 1797 | if ($this->_piOnFrm_Ok) { 1798 | if (isset($Loc->OnFrmArgPi)) { 1799 | $Loc->OnFrmArgPi[1] = &$CurrVal; 1800 | $Loc->OnFrmArgPi[3] = &$this; // bugs.php.net/51174 1801 | } else { 1802 | $Loc->OnFrmArgPi = array($Loc->FullName,&$CurrVal,&$Loc->PrmLst,&$this); 1803 | } 1804 | $this->meth_PlugIn_RunAll($this->_piOnFormat,$Loc->OnFrmArgPi); 1805 | } 1806 | 1807 | // Operation 1808 | if ($Loc->Ope) { 1809 | foreach ($Loc->OpeAct as $i=>$ope) { 1810 | switch ($ope) { 1811 | case 0: 1812 | $Loc->PrmLst['ope'] = $Loc->OpePrm[$i]; // for compatibility 1813 | $OpeArg = &$Loc->OpeArg[$i]; 1814 | $OpeArg[1] = &$CurrVal; $OpeArg[3] = &$Txt; 1815 | if (!$this->meth_PlugIn_RunAll($this->_piOnOperation,$OpeArg)) { 1816 | $Loc->PosNext = $Loc->PosBeg + 1; // +1 in order to avoid infinit loop 1817 | return $Loc->PosNext; 1818 | } 1819 | break; 1820 | case 1: 1821 | if ($Loc->ConvMode===-1) { 1822 | if (is_array($CurrVal)) { 1823 | foreach ($CurrVal as $k=>$v) { 1824 | $v = $this->meth_Misc_ToStr($v); 1825 | $this->meth_Conv_Str($v,$Loc->ConvBr); 1826 | $CurrVal[$k] = $v; 1827 | } 1828 | $CurrVal = implode($Loc->OpePrm[$i],$CurrVal); 1829 | } else { 1830 | $CurrVal = $this->meth_Misc_ToStr($CurrVal); 1831 | $this->meth_Conv_Str($CurrVal,$Loc->ConvBr); 1832 | } 1833 | } else { 1834 | if (is_array($CurrVal)) $CurrVal = implode($Loc->OpePrm[$i],$CurrVal); 1835 | } 1836 | break; 1837 | case 2: 1838 | $x = $this->meth_Misc_ToStr($CurrVal); 1839 | if (strlen($x)>$Loc->OpePrm[$i]) { 1840 | $this->f_Xml_Max($x,$Loc->OpePrm[$i],$Loc->OpeEnd); 1841 | } 1842 | break; 1843 | case 3: 1844 | $x = $this->meth_Misc_ToStr($CurrVal); 1845 | if (strlen($x)>$Loc->OpePrm[$i]) { 1846 | if ($Loc->OpeUtf8) { 1847 | $CurrVal = mb_substr($x,0,$Loc->OpePrm[$i],'UTF-8').$Loc->OpeEnd; 1848 | } else { 1849 | $CurrVal = substr($x,0,$Loc->OpePrm[$i]).$Loc->OpeEnd; 1850 | } 1851 | } 1852 | break; 1853 | case 5: $CurrVal = ('0'+$CurrVal) % $Loc->OpePrm[$i]; break; 1854 | case 6: $CurrVal = ('0'+$CurrVal) + $Loc->OpePrm[$i]; break; 1855 | case 7: $CurrVal = ('0'+$CurrVal) * $Loc->OpePrm[$i]; break; 1856 | case 8: $CurrVal = ('0'+$CurrVal) / $Loc->OpePrm[$i]; break; 1857 | case 9; case 10: 1858 | if ($ope===9) { 1859 | $CurrVal = (in_array($this->meth_Misc_ToStr($CurrVal),$Loc->OpeMOK)) ? ' ' : ''; 1860 | } else { 1861 | $CurrVal = (in_array($this->meth_Misc_ToStr($CurrVal),$Loc->OpeMKO)) ? '' : ' '; 1862 | } // no break here 1863 | case 11: 1864 | if ($this->meth_Misc_ToStr($CurrVal)==='') { 1865 | if ($Loc->MagnetId===0) $Loc->MagnetId = $Loc->MSave; 1866 | } else { 1867 | if ($Loc->MagnetId!==0) { 1868 | $Loc->MSave = $Loc->MagnetId; 1869 | $Loc->MagnetId = 0; 1870 | } 1871 | $CurrVal = ''; 1872 | } 1873 | break; 1874 | case 12: if ($this->meth_Misc_ToStr($CurrVal)===$Loc->OpePrm[$i]) $CurrVal = ''; break; 1875 | case 13: $CurrVal = str_replace('*',$CurrVal,$Loc->OpePrm[$i]); break; 1876 | case 14: $CurrVal = self::f_Loc_AttBoolean($CurrVal, $Loc->PrmLst['atttrue'], $Loc->AttName); break; 1877 | case 15: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_UPPER, 'UTF-8') : strtoupper($CurrVal); break; 1878 | case 16: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_LOWER, 'UTF-8') : strtolower($CurrVal); break; 1879 | case 17: $CurrVal = ucfirst($CurrVal); break; 1880 | case 18: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_TITLE, 'UTF-8') : ucwords(strtolower($CurrVal)); break; 1881 | case 19: $CurrVal = '(' . gettype($CurrVal) . ') ' . var_export($CurrVal, true); break; 1882 | } 1883 | } 1884 | } 1885 | 1886 | // String conversion or format 1887 | if ($Loc->ConvMode===1) { // Usual string conversion 1888 | $CurrVal = $this->meth_Misc_ToStr($CurrVal); 1889 | if ($Loc->ConvStr) $this->meth_Conv_Str($CurrVal,$Loc->ConvBr); 1890 | } elseif ($Loc->ConvMode===0) { // Format 1891 | $CurrVal = $this->meth_Misc_Format($CurrVal,$Loc->PrmLst); 1892 | } elseif ($Loc->ConvMode===2) { // Special string conversion 1893 | $CurrVal = $this->meth_Misc_ToStr($CurrVal); 1894 | if ($Loc->ConvStr) $this->meth_Conv_Str($CurrVal,$Loc->ConvBr); 1895 | if ($Loc->ConvEsc) $CurrVal = str_replace('\'','\'\'',$CurrVal); 1896 | if ($Loc->ConvWS) { 1897 | $check = ' '; 1898 | $nbsp = ' '; 1899 | do { 1900 | $pos = strpos($CurrVal,$check); 1901 | if ($pos!==false) $CurrVal = substr_replace($CurrVal,$nbsp,$pos,1); 1902 | } while ($pos!==false); 1903 | } 1904 | if ($Loc->ConvJS) { 1905 | $CurrVal = addslashes($CurrVal); // apply to ('), ("), (\) and (null) 1906 | $CurrVal = str_replace(array("\n","\r","\t"),array('\n','\r','\t'),$CurrVal); 1907 | } 1908 | if ($Loc->ConvUrl) $CurrVal = urlencode($CurrVal); 1909 | if ($Loc->ConvUtf8) $CurrVal = iconv('ISO-8859-1', 'UTF-8', $CurrVal); 1910 | } 1911 | 1912 | // if/then/else process, there may be several if/then 1913 | if ($Loc->PrmIfNbr) { 1914 | $z = false; 1915 | $i = 1; 1916 | while ($i!==false) { 1917 | if ($Loc->PrmIfVar[$i]) $Loc->PrmIfVar[$i] = $this->meth_Merge_AutoVar($Loc->PrmIf[$i],true); 1918 | $x = str_replace($this->_ChrVal,$CurrVal,$Loc->PrmIf[$i]); 1919 | if ($this->f_Misc_CheckCondition($x)) { 1920 | if (isset($Loc->PrmThen[$i])) { 1921 | if ($Loc->PrmThenVar[$i]) $Loc->PrmThenVar[$i] = $this->meth_Merge_AutoVar($Loc->PrmThen[$i],true); 1922 | $z = $Loc->PrmThen[$i]; 1923 | } 1924 | $i = false; 1925 | } else { 1926 | $i++; 1927 | if ($i>$Loc->PrmIfNbr) { 1928 | if (isset($Loc->PrmLst['else'])) { 1929 | if ($Loc->PrmElseVar) $Loc->PrmElseVar = $this->meth_Merge_AutoVar($Loc->PrmLst['else'],true); 1930 | $z =$Loc->PrmLst['else']; 1931 | } 1932 | $i = false; 1933 | } 1934 | } 1935 | } 1936 | if ($z!==false) { 1937 | if ($ConvProtect) { 1938 | $CurrVal = str_replace($this->_ChrOpen,$this->_ChrProtect,$CurrVal); // TBS protection 1939 | $ConvProtect = false; 1940 | } 1941 | $CurrVal = str_replace($this->_ChrVal,$CurrVal,$z); 1942 | } 1943 | } 1944 | 1945 | $IsTpl = false; // Indicates is $CurrVal is a sub-template 1946 | 1947 | if (isset($Loc->PrmLst['file'])) { 1948 | $x = $Loc->PrmLst['file']; 1949 | if ($x===true) $x = $CurrVal; 1950 | $this->meth_Merge_AutoVar($x,false); 1951 | $x = trim(str_replace($this->_ChrVal,$CurrVal,$x)); 1952 | $CurrVal = ''; 1953 | if ($x!=='') { 1954 | if ($this->f_Misc_GetFile($CurrVal, $x, $this->_LastFile, $this->IncludePath)) { 1955 | $this->meth_Locator_PartAndRename($CurrVal, $Loc->PrmLst); 1956 | $IsTpl = true; 1957 | } else { 1958 | if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'the file \''.$x.'\' given by parameter file is not found or not readable.',true); 1959 | } 1960 | $ConvProtect = false; 1961 | } 1962 | } 1963 | 1964 | if (isset($Loc->PrmLst['script'])) {// Include external PHP script 1965 | $x = $Loc->PrmLst['script']; 1966 | if ($this->ScriptsAllowed) { 1967 | if ($x===true) $x = $CurrVal; 1968 | $this->meth_Merge_AutoVar($x,false); 1969 | $x = trim(str_replace($this->_ChrVal,$CurrVal,$x)); 1970 | if (basename($x) == basename($this->_LastFile)) { 1971 | if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'the file \''.$x.'\' given by parameter script cannot be called because it has the same name as the current template and this is suspicious.',true); 1972 | $x= ''; 1973 | } 1974 | } else { 1975 | if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'parameter \'script\' is forbidden by default. It can be allowed by a TBS option.',true); 1976 | $x = ''; 1977 | } 1978 | if ($x!=='') { 1979 | $this->_Subscript = $x; 1980 | $this->CurrPrm = &$Loc->PrmLst; 1981 | $sub = isset($Loc->PrmLst['subtpl']); 1982 | if ($sub) $this->meth_Misc_ChangeMode(true,$Loc,$CurrVal); 1983 | if ($this->meth_Misc_RunSubscript($CurrVal,$Loc->PrmLst)===false) { 1984 | if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'the file \''.$x.'\' given by parameter script is not found or not readable.',true); 1985 | } 1986 | if ($sub) $this->meth_Misc_ChangeMode(false,$Loc,$CurrVal); 1987 | $this->meth_Locator_PartAndRename($CurrVal, $Loc->PrmLst); 1988 | $IsTpl = true; 1989 | unset($this->CurrPrm); 1990 | $ConvProtect = false; 1991 | } 1992 | } 1993 | 1994 | if (isset($Loc->PrmLst['att'])) { 1995 | $this->f_Xml_AttFind($Txt,$Loc,true,$this->AttDelim); 1996 | if (isset($Loc->PrmLst['atttrue'])) { 1997 | $CurrVal = self::f_Loc_AttBoolean($CurrVal, $Loc->PrmLst['atttrue'], $Loc->AttName); 1998 | $Loc->PrmLst['magnet'] = '#'; 1999 | } 2000 | } 2001 | 2002 | // Case when it's an empty string 2003 | if ($CurrVal==='') { 2004 | 2005 | if ($Loc->MagnetId===false) { 2006 | if (isset($Loc->PrmLst['.'])) { 2007 | $Loc->MagnetId = -1; 2008 | } elseif (isset($Loc->PrmLst['ifempty'])) { 2009 | $Loc->MagnetId = -2; 2010 | } elseif (isset($Loc->PrmLst['magnet'])) { 2011 | $Loc->MagnetId = 1; 2012 | $Loc->PosBeg0 = $Loc->PosBeg; 2013 | $Loc->PosEnd0 = $Loc->PosEnd; 2014 | if ($Loc->PrmLst['magnet']==='#') { 2015 | if (!isset($Loc->AttBeg)) { 2016 | $Loc->PrmLst['att'] = '.'; 2017 | // no moving because att info would be modified and thus become wrong regarding to the eventually cached source 2018 | $this->f_Xml_AttFind($Txt,$Loc,false,$this->AttDelim); 2019 | } 2020 | if (isset($Loc->AttBeg)) { 2021 | $Loc->MagnetId = -3; 2022 | } else { 2023 | $this->meth_Misc_Alert($Loc,'parameter \'magnet=#\' cannot be processed because the corresponding attribute is not found.',true); 2024 | } 2025 | } elseif (isset($Loc->PrmLst['mtype'])) { 2026 | switch ($Loc->PrmLst['mtype']) { 2027 | case 'm+m': $Loc->MagnetId = 2; break; 2028 | case 'm*': $Loc->MagnetId = 3; break; 2029 | case '*m': $Loc->MagnetId = 4; break; 2030 | } 2031 | } 2032 | } elseif (isset($Loc->PrmLst['attadd'])) { 2033 | // In order to delete extra space 2034 | $Loc->PosBeg0 = $Loc->PosBeg; 2035 | $Loc->PosEnd0 = $Loc->PosEnd; 2036 | $Loc->MagnetId = 5; 2037 | } else { 2038 | $Loc->MagnetId = 0; 2039 | } 2040 | } 2041 | 2042 | switch ($Loc->MagnetId) { 2043 | case 0: break; 2044 | case -1: $CurrVal = ' '; break; // Enables to avoid null cells in HTML tables 2045 | case -2: $CurrVal = $Loc->PrmLst['ifempty']; break; 2046 | case -3: 2047 | // magnet=# 2048 | $Loc->Enlarged = true; 2049 | $Loc->PosBeg = ($Txt[$Loc->AttBeg-1]===' ') ? $Loc->AttBeg-1 : $Loc->AttBeg; 2050 | $Loc->PosEnd = $Loc->AttEnd; 2051 | break; 2052 | case 1: 2053 | $Loc->Enlarged = true; 2054 | $this->f_Loc_EnlargeToTag($Txt,$Loc,$Loc->PrmLst['magnet'],false); 2055 | break; 2056 | case 2: 2057 | $Loc->Enlarged = true; 2058 | $CurrVal = $this->f_Loc_EnlargeToTag($Txt,$Loc,$Loc->PrmLst['magnet'],true); 2059 | break; 2060 | case 3: 2061 | $Loc->Enlarged = true; 2062 | $Loc2 = $this->f_Xml_FindTag($Txt,$Loc->PrmLst['magnet'],true,$Loc->PosBeg,false,false,false); 2063 | if ($Loc2!==false) { 2064 | $Loc->PosBeg = $Loc2->PosBeg; 2065 | if ($Loc->PosEnd<$Loc2->PosEnd) $Loc->PosEnd = $Loc2->PosEnd; 2066 | } 2067 | break; 2068 | case 4: 2069 | $Loc->Enlarged = true; 2070 | $Loc2 = $this->f_Xml_FindTag($Txt,$Loc->PrmLst['magnet'],true,$Loc->PosBeg,true,false,false); 2071 | if ($Loc2!==false) $Loc->PosEnd = $Loc2->PosEnd; 2072 | break; 2073 | case 5: 2074 | $Loc->Enlarged = true; 2075 | if (substr($Txt,$Loc->PosBeg-1,1)==' ') $Loc->PosBeg--; 2076 | break; 2077 | } 2078 | $NewEnd = $Loc->PosBeg; // Useful when mtype='m+m' 2079 | } else { 2080 | 2081 | if ($ConvProtect) $CurrVal = str_replace($this->_ChrOpen,$this->_ChrProtect,$CurrVal); // TBS protection 2082 | $NewEnd = $Loc->PosBeg + ($IsTpl ? 0 : strlen($CurrVal)); 2083 | 2084 | } 2085 | 2086 | $Txt = substr_replace($Txt,$CurrVal,$Loc->PosBeg,$Loc->PosEnd-$Loc->PosBeg+1); 2087 | 2088 | $Loc->PosNext = $NewEnd; 2089 | return $NewEnd; // Return the new end position of the field 2090 | 2091 | } 2092 | 2093 | /** 2094 | * Return the first block locator just after the PosBeg position 2095 | * 2096 | * @param integer $Mode 2097 | * 1 : Merge_Auto => doesn't save $Loc->BlockSrc, save the bounds of TBS Def tags instead, return also fields 2098 | * 2 : FindBlockLst or GetBlockSource => save $Loc->BlockSrc without TBS Def tags 2099 | * 3 : GetBlockSource => save $Loc->BlockSrc with TBS Def tags 2100 | */ 2101 | function meth_Locator_FindBlockNext(&$Txt,$BlockName,$PosBeg,$ChrSub,$Mode,&$P1,&$FieldBefore) { 2102 | 2103 | $SearchDef = true; 2104 | $FirstField = false; 2105 | // Search for the first tag with parameter "block" 2106 | while ($SearchDef && ($Loc = $this->meth_Locator_FindTbs($Txt,$BlockName,$PosBeg,$ChrSub))) { 2107 | if (isset($Loc->PrmLst['block'])) { 2108 | if (isset($Loc->PrmLst['p1'])) { 2109 | if ($P1) return false; 2110 | $P1 = true; 2111 | } 2112 | $Block = $Loc->PrmLst['block']; 2113 | $SearchDef = false; 2114 | } elseif ($Mode===1) { 2115 | return $Loc; 2116 | } elseif ($FirstField===false) { 2117 | $FirstField = $Loc; 2118 | } 2119 | $PosBeg = $Loc->PosEnd; 2120 | } 2121 | 2122 | if ($SearchDef) { 2123 | if ($FirstField!==false) $FieldBefore = true; 2124 | return false; 2125 | } 2126 | 2127 | $Loc->PosDefBeg = -1; 2128 | 2129 | if ($Block==='begin') { // Block definied using begin/end 2130 | 2131 | if (($FirstField!==false) && ($FirstField->PosEnd<$Loc->PosBeg)) $FieldBefore = true; 2132 | 2133 | $Opened = 1; 2134 | while ($Loc2 = $this->meth_Locator_FindTbs($Txt,$BlockName,$PosBeg,$ChrSub)) { 2135 | if (isset($Loc2->PrmLst['block'])) { 2136 | switch ($Loc2->PrmLst['block']) { 2137 | case 'end': $Opened--; break; 2138 | case 'begin': $Opened++; break; 2139 | } 2140 | if ($Opened==0) { 2141 | if ($Mode===1) { 2142 | $Loc->PosBeg2 = $Loc2->PosBeg; 2143 | $Loc->PosEnd2 = $Loc2->PosEnd; 2144 | } else { 2145 | if ($Mode===2) { 2146 | $Loc->BlockSrc = substr($Txt,$Loc->PosEnd+1,$Loc2->PosBeg-$Loc->PosEnd-1); 2147 | } else { 2148 | $Loc->BlockSrc = substr($Txt,$Loc->PosBeg,$Loc2->PosEnd-$Loc->PosBeg+1); 2149 | } 2150 | $Loc->PosEnd = $Loc2->PosEnd; 2151 | } 2152 | $Loc->BlockFound = true; 2153 | return $Loc; 2154 | } 2155 | } 2156 | $PosBeg = $Loc2->PosEnd; 2157 | } 2158 | 2159 | return $this->meth_Misc_Alert($Loc,'a least one tag with parameter \'block=end\' is missing.',false,'in block\'s definition'); 2160 | 2161 | } 2162 | 2163 | if ($Mode===1) { 2164 | $Loc->PosBeg2 = false; 2165 | } else { 2166 | $beg = $Loc->PosBeg; 2167 | $end = $Loc->PosEnd; 2168 | if ($this->f_Loc_EnlargeToTag($Txt,$Loc,$Block,false)===false) return $this->meth_Misc_Alert($Loc,'at least one tag corresponding to '.$Loc->PrmLst['block'].' is not found. Check opening tags, closing tags and embedding levels.',false,'in block\'s definition'); 2169 | if ($Loc->SubOk || ($Mode===3)) { 2170 | $Loc->BlockSrc = substr($Txt,$Loc->PosBeg,$Loc->PosEnd-$Loc->PosBeg+1); 2171 | $Loc->PosDefBeg = $beg - $Loc->PosBeg; 2172 | $Loc->PosDefEnd = $end - $Loc->PosBeg; 2173 | } else { 2174 | $Loc->BlockSrc = substr($Txt,$Loc->PosBeg,$beg-$Loc->PosBeg).substr($Txt,$end+1,$Loc->PosEnd-$end); 2175 | } 2176 | } 2177 | 2178 | $Loc->BlockFound = true; 2179 | if (($FirstField!==false) && ($FirstField->PosEnd<$Loc->PosBeg)) $FieldBefore = true; 2180 | return $Loc; // methods return by ref by default 2181 | 2182 | } 2183 | 2184 | function meth_Locator_PartAndRename(&$CurrVal, &$PrmLst) { 2185 | 2186 | // Store part 2187 | if (isset($PrmLst['store'])) { 2188 | $storename = (isset($PrmLst['storename'])) ? $PrmLst['storename'] : 'default'; 2189 | if (!isset($this->TplStore[$storename])) $this->TplStore[$storename] = ''; 2190 | $this->TplStore[$storename] .= $this->f_Xml_GetPart($CurrVal, $PrmLst['store'], false); 2191 | } 2192 | 2193 | // Get part 2194 | if (isset($PrmLst['getpart'])) { 2195 | $part = $PrmLst['getpart']; 2196 | } elseif (isset($PrmLst['getbody'])) { 2197 | $part = $PrmLst['getbody']; 2198 | } else { 2199 | $part = false; 2200 | } 2201 | if ($part!=false) { 2202 | $CurrVal = $this->f_Xml_GetPart($CurrVal, $part, true); 2203 | } 2204 | 2205 | // Rename or delete TBS tags names 2206 | if (isset($PrmLst['rename'])) { 2207 | 2208 | $Replace = $PrmLst['rename']; 2209 | 2210 | if (is_string($Replace)) $Replace = explode(',',$Replace); 2211 | foreach ($Replace as $x) { 2212 | if (is_string($x)) $x = explode('=', $x); 2213 | if (count($x)==2) { 2214 | $old = trim($x[0]); 2215 | $new = trim($x[1]); 2216 | if ($old!=='') { 2217 | if ($new==='') { 2218 | $q = false; 2219 | $s = 'clear'; 2220 | $this->meth_Merge_Block($CurrVal, $old, $s, $q, false, false, false); 2221 | } else { 2222 | $old = $this->_ChrOpen.$old; 2223 | $old = array($old.'.', $old.' ', $old.';', $old.$this->_ChrClose); 2224 | $new = $this->_ChrOpen.$new; 2225 | $new = array($new.'.', $new.' ', $new.';', $new.$this->_ChrClose); 2226 | $CurrVal = str_replace($old,$new,$CurrVal); 2227 | } 2228 | } 2229 | } 2230 | } 2231 | 2232 | } 2233 | 2234 | } 2235 | 2236 | /** 2237 | * Retrieve the list of all sections and their finition for a given block name. 2238 | * 2239 | * @param string $Txt 2240 | * @param string $BlockName 2241 | * @param integer $Pos 2242 | * @param string|false $SpePrm The parameter's name for Special section (used for navigation bar), or false if none. 2243 | * 2244 | * @return object 2245 | */ 2246 | function meth_Locator_FindBlockLst(&$Txt,$BlockName,$Pos,$SpePrm) { 2247 | // Return a locator object covering all block definitions, even if there is no block definition found. 2248 | 2249 | $LocR = new clsTbsLocator; 2250 | $LocR->P1 = false; 2251 | $LocR->FieldOutside = false; 2252 | $LocR->FOStop = false; 2253 | $LocR->BDefLst = array(); 2254 | 2255 | $LocR->NoData = false; 2256 | $LocR->Special = false; 2257 | $LocR->HeaderFound = false; 2258 | $LocR->FooterFound = false; 2259 | $LocR->SerialEmpty = false; 2260 | $LocR->GrpBreak = false; // Only for plug-ins 2261 | 2262 | $LocR->BoundFound = false; 2263 | $LocR->CheckNext = false; 2264 | $LocR->CheckPrev = false; 2265 | 2266 | $LocR->WhenFound = false; 2267 | $LocR->WhenDefault = false; 2268 | 2269 | $LocR->SectionNbr = 0; // Normal sections 2270 | $LocR->SectionLst = array(); // 1 to SectionNbr 2271 | 2272 | $BDef = false; 2273 | $ParentLst = array(); 2274 | $Pid = 0; 2275 | 2276 | do { 2277 | 2278 | if ($BlockName==='') { 2279 | $Loc = false; 2280 | } else { 2281 | $Loc = $this->meth_Locator_FindBlockNext($Txt,$BlockName,$Pos,'.',2,$LocR->P1,$LocR->FieldOutside); 2282 | } 2283 | 2284 | if ($Loc===false) { 2285 | 2286 | if ($Pid>0) { // parentgrp mode => disconnect $Txt from the source 2287 | $Parent = &$ParentLst[$Pid]; 2288 | $Src = $Txt; 2289 | $Txt = &$Parent->Txt; 2290 | if ($LocR->BlockFound) { 2291 | // Redefine the Header block 2292 | $Parent->Src = substr($Src,0,$LocR->PosBeg); 2293 | // Add a Footer block 2294 | $BDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,substr($Src,$LocR->PosEnd+1),$Parent->Prm,true); 2295 | $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'F',$Parent->Fld,'parentgrp'); 2296 | } 2297 | // Now go down to previous level 2298 | $Pos = $Parent->Pos; 2299 | $LocR->PosBeg = $Parent->Beg; 2300 | $LocR->PosEnd = $Parent->End; 2301 | $LocR->BlockFound = true; 2302 | unset($Parent); 2303 | unset($ParentLst[$Pid]); 2304 | $Pid--; 2305 | $Loc = true; 2306 | } 2307 | 2308 | } else { 2309 | 2310 | $Pos = $Loc->PosEnd; 2311 | 2312 | // Define the block limits 2313 | if ($LocR->BlockFound) { 2314 | if ( $LocR->PosBeg > $Loc->PosBeg ) $LocR->PosBeg = $Loc->PosBeg; 2315 | if ( $LocR->PosEnd < $Loc->PosEnd ) $LocR->PosEnd = $Loc->PosEnd; 2316 | } else { 2317 | $LocR->BlockFound = true; 2318 | $LocR->PosBeg = $Loc->PosBeg; 2319 | $LocR->PosEnd = $Loc->PosEnd; 2320 | } 2321 | 2322 | // Merge block parameters 2323 | if (count($Loc->PrmLst)>0) $LocR->PrmLst = array_merge($LocR->PrmLst,$Loc->PrmLst); 2324 | 2325 | // Force dynamic parameter to be cachable 2326 | if ($Loc->PosDefBeg>=0) { 2327 | $dynprm = array('when','headergrp','footergrp','parentgrp'); 2328 | foreach($dynprm as $dp) { 2329 | $n = 0; 2330 | if ((isset($Loc->PrmLst[$dp])) && (strpos($Loc->PrmLst[$dp],$this->_ChrOpen.$BlockName)!==false)) { 2331 | $n++; 2332 | if ($n==1) { 2333 | $len = $Loc->PosDefEnd - $Loc->PosDefBeg + 1; 2334 | $x = substr($Loc->BlockSrc,$Loc->PosDefBeg,$len); 2335 | } 2336 | $x = str_replace($Loc->PrmLst[$dp],'',$x); 2337 | } 2338 | if ($n>0) $Loc->BlockSrc = substr_replace($Loc->BlockSrc,$x,$Loc->PosDefBeg,$len); 2339 | } 2340 | } 2341 | // Save the block and cache its tags 2342 | $IsParentGrp = isset($Loc->PrmLst['parentgrp']); 2343 | $BDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Loc->BlockSrc,$Loc->PrmLst,!$IsParentGrp); 2344 | 2345 | // Bounds 2346 | $BoundPrm = false; 2347 | $lst = array('firstingrp'=>1, 'lastingrp'=>2, 'singleingrp'=>3); // 1=prev, 2=next, 3=1+2=prev+next 2348 | foreach ($lst as $prm => $chk) { 2349 | if (isset($Loc->PrmLst[$prm])) { 2350 | $BoundPrm = $prm; 2351 | $BoundChk = $chk; 2352 | } 2353 | } 2354 | 2355 | // Add the text in the list of blocks 2356 | if (isset($Loc->PrmLst['nodata'])) { // Nodata section 2357 | $LocR->NoData = $BDef; 2358 | } elseif (($SpePrm!==false) && isset($Loc->PrmLst[$SpePrm])) { // Special section (used for navigation bar) 2359 | $LocR->Special = $BDef; 2360 | } elseif (isset($Loc->PrmLst['when'])) { 2361 | if ($LocR->WhenFound===false) { 2362 | $LocR->WhenFound = true; 2363 | $LocR->WhenSeveral = false; 2364 | $LocR->WhenNbr = 0; 2365 | $LocR->WhenLst = array(); 2366 | } 2367 | $this->meth_Merge_AutoVar($Loc->PrmLst['when'],false); 2368 | $BDef->WhenCond = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Loc->PrmLst['when'],array(),true); 2369 | $BDef->WhenBeforeNS = ($LocR->SectionNbr===0); // position of the When section relativley to the Normal Section 2370 | $i = ++$LocR->WhenNbr; 2371 | $LocR->WhenLst[$i] = $BDef; 2372 | if (isset($Loc->PrmLst['several'])) $LocR->WhenSeveral = true; 2373 | } elseif (isset($Loc->PrmLst['default'])) { 2374 | $LocR->WhenDefault = $BDef; 2375 | $LocR->WhenDefaultBeforeNS = ($LocR->SectionNbr===0); 2376 | } elseif (isset($Loc->PrmLst['headergrp'])) { 2377 | $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'H',$Loc->PrmLst['headergrp'],'headergrp'); 2378 | } elseif (isset($Loc->PrmLst['footergrp'])) { 2379 | $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'F',$Loc->PrmLst['footergrp'],'footergrp'); 2380 | } elseif (isset($Loc->PrmLst['splittergrp'])) { 2381 | $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'S',$Loc->PrmLst['splittergrp'],'splittergrp'); 2382 | } elseif ($IsParentGrp) { 2383 | $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'H',$Loc->PrmLst['parentgrp'],'parentgrp'); 2384 | $BDef->Fld = $Loc->PrmLst['parentgrp']; 2385 | $BDef->Txt = &$Txt; 2386 | $BDef->Pos = $Pos; 2387 | $BDef->Beg = $LocR->PosBeg; 2388 | $BDef->End = $LocR->PosEnd; 2389 | $Pid++; 2390 | $ParentLst[$Pid] = $BDef; 2391 | $Txt = &$BDef->Src; 2392 | $Pos = $Loc->PosDefBeg + 1; 2393 | $LocR->BlockFound = false; 2394 | $LocR->PosBeg = false; 2395 | $LocR->PosEnd = false; 2396 | } elseif (isset($Loc->PrmLst['serial'])) { 2397 | // Section with serial subsections 2398 | $SrSrc = &$BDef->Src; 2399 | // Search the empty item 2400 | if ($LocR->SerialEmpty===false) { 2401 | $SrName = $BlockName.'_0'; 2402 | $x = false; 2403 | $SrLoc = $this->meth_Locator_FindBlockNext($SrSrc,$SrName,0,'.',2,$x,$x); 2404 | if ($SrLoc!==false) { 2405 | $LocR->SerialEmpty = $SrLoc->BlockSrc; 2406 | $SrSrc = substr_replace($SrSrc,'',$SrLoc->PosBeg,$SrLoc->PosEnd-$SrLoc->PosBeg+1); 2407 | } 2408 | } 2409 | $SrName = $BlockName.'_1'; 2410 | $x = false; 2411 | $SrLoc = $this->meth_Locator_FindBlockNext($SrSrc,$SrName,0,'.',2,$x,$x); 2412 | if ($SrLoc!==false) { 2413 | $SrId = 1; 2414 | do { 2415 | // Save previous subsection 2416 | $SrBDef = $this->meth_Locator_SectionNewBDef($LocR,$SrName,$SrLoc->BlockSrc,$SrLoc->PrmLst,true); 2417 | $SrBDef->SrBeg = $SrLoc->PosBeg; 2418 | $SrBDef->SrLen = $SrLoc->PosEnd - $SrLoc->PosBeg + 1; 2419 | $SrBDef->SrTxt = false; 2420 | $BDef->SrBDefLst[$SrId] = $SrBDef; 2421 | // Put in order 2422 | $BDef->SrBDefOrdered[$SrId] = $SrBDef; 2423 | $i = $SrId; 2424 | while (($i>1) && ($SrBDef->SrBeg<$BDef->SrBDefOrdered[$SrId-1]->SrBeg)) { 2425 | $BDef->SrBDefOrdered[$i] = $BDef->SrBDefOrdered[$i-1]; 2426 | $BDef->SrBDefOrdered[$i-1] = $SrBDef; 2427 | $i--; 2428 | } 2429 | // Search next subsection 2430 | $SrId++; 2431 | $SrName = $BlockName.'_'.$SrId; 2432 | $x = false; 2433 | $SrLoc = $this->meth_Locator_FindBlockNext($SrSrc,$SrName,0,'.',2,$x,$x); 2434 | } while ($SrLoc!==false); 2435 | $BDef->SrBDefNbr = $SrId-1; 2436 | $BDef->IsSerial = true; 2437 | $i = ++$LocR->SectionNbr; 2438 | $LocR->SectionLst[$i] = $BDef; 2439 | } 2440 | } elseif (isset($Loc->PrmLst['parallel'])) { 2441 | $BlockLst = $this->meth_Locator_FindParallel($Txt, $Loc->PosBeg, $Loc->PosEnd, $Loc->PrmLst['parallel']); 2442 | if ($BlockLst) { 2443 | // Store BDefs 2444 | foreach ($BlockLst as $i => $Blk) { 2445 | if ($Blk['IsRef']) { 2446 | $PrBDef = $BDef; 2447 | } else { 2448 | $PrBDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Blk['Src'],array(),true); 2449 | } 2450 | $PrBDef->PosBeg = $Blk['PosBeg']; 2451 | $PrBDef->PosEnd = $Blk['PosEnd']; 2452 | $i = ++$LocR->SectionNbr; 2453 | $LocR->SectionLst[$i] = $PrBDef; 2454 | } 2455 | $LocR->PosBeg = $BlockLst[0]['PosBeg']; 2456 | $LocR->PosEnd = $BlockLst[$LocR->SectionNbr-1]['PosEnd']; 2457 | } 2458 | } elseif ($BoundPrm !== false) { 2459 | $BDef->BoundExpr = $this->meth_Locator_MakeBDefFromField($LocR,$BlockName,$Loc->PrmLst[$BoundPrm],$BoundPrm); 2460 | $BDef->ValCurr = null; 2461 | $BDef->ValNext = null; 2462 | $BDef->CheckPrev = (($BoundChk & 1) != 0); // bitwise check 2463 | if ($BDef->CheckPrev) { 2464 | $LocR->CheckPrev = true; 2465 | $LocR->ValPrev = null; 2466 | } 2467 | $BDef->CheckNext = (($BoundChk & 2) != 0); // bitwise check 2468 | if ($BDef->CheckNext) { 2469 | $LocR->CheckNext = true; 2470 | $LocR->ValNext = null; 2471 | } 2472 | if (!$LocR->BoundFound) { 2473 | $LocR->BoundFound = true; 2474 | $LocR->BoundLst = array(); 2475 | $LocR->BoundNb = 0; 2476 | $LocR->BoundSingleNb = 0; 2477 | } 2478 | if ($BoundChk == 3) { 2479 | // Insert the singleingrp before all other types 2480 | array_splice($LocR->BoundLst, $LocR->BoundSingleNb, 0, array($BDef)); 2481 | $LocR->BoundSingleNb++; 2482 | } else { 2483 | // Insert other types at the end 2484 | $LocR->BoundLst[] = $BDef; 2485 | } 2486 | $LocR->BoundNb++; 2487 | } else { 2488 | // Normal section 2489 | $i = ++$LocR->SectionNbr; 2490 | $LocR->SectionLst[$i] = $BDef; 2491 | } 2492 | 2493 | } 2494 | 2495 | } while ($Loc!==false); 2496 | 2497 | if ($LocR->WhenFound && ($LocR->SectionNbr===0)) { 2498 | // Add a blank section if When is used without a normal section 2499 | $BDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,'',array(),false); 2500 | $LocR->SectionNbr = 1; 2501 | $LocR->SectionLst[1] = &$BDef; 2502 | } 2503 | 2504 | return $LocR; // methods return by ref by default 2505 | 2506 | } 2507 | 2508 | function meth_Locator_FindParallel(&$Txt, $ZoneBeg, $ZoneEnd, $ConfId) { 2509 | 2510 | // Define configurations 2511 | global $_TBS_ParallelLst; 2512 | 2513 | if ( ($ConfId=='tbs:table') && (!isset($_TBS_ParallelLst['tbs:table'])) ) { 2514 | $_TBS_ParallelLst['tbs:table'] = array( 2515 | 'parent' => 'table', 2516 | 'ignore' => array('!--', 'caption', 'thead', 'tbody', 'tfoot'), 2517 | 'cols' => array(), 2518 | 'rows' => array('tr', 'colgroup'), 2519 | 'cells' => array('td'=>'colspan', 'th'=>'colspan', 'col'=>'span'), 2520 | ); 2521 | } 2522 | 2523 | if (!isset($_TBS_ParallelLst[$ConfId])) return $this->meth_Misc_Alert("Parallel", "The configuration '$ConfId' is not found."); 2524 | 2525 | $conf = $_TBS_ParallelLst[$ConfId]; 2526 | 2527 | $Parent = $conf['parent']; 2528 | 2529 | // Search parent bounds 2530 | $par_o = self::f_Xml_FindTag($Txt,$Parent,true ,$ZoneBeg,false,1,false); 2531 | if ($par_o===false) return $this->meth_Misc_Alert("Parallel", "The opening tag '$Parent' is not found."); 2532 | 2533 | $par_c = self::f_Xml_FindTag($Txt,$Parent,false,$ZoneBeg,true,-1,false); 2534 | if ($par_c===false) return $this->meth_Misc_Alert("Parallel", "The closing tag '$Parent' is not found."); 2535 | 2536 | $SrcPOffset = $par_o->PosEnd + 1; 2537 | $SrcP = substr($Txt, $SrcPOffset, $par_c->PosBeg - $SrcPOffset); 2538 | 2539 | // temporary variables 2540 | $tagR = ''; 2541 | $tagC = ''; 2542 | $z = ''; 2543 | $pRO = false; 2544 | $pROe = false; 2545 | $pCO = false; 2546 | $pCOe = false; 2547 | $p = false; 2548 | $Loc = new clsTbsLocator; 2549 | 2550 | $Rows = array(); 2551 | $RowIdx = 0; 2552 | $RefRow = false; 2553 | $RefCellB= false; 2554 | $RefCellE = false; 2555 | 2556 | $RowType = array(); 2557 | 2558 | // Loop on entities inside the parent entity 2559 | $PosR = 0; 2560 | 2561 | $mode_column = true; 2562 | $Cells = array(); 2563 | $ColNum = 1; 2564 | $IsRef = false; 2565 | 2566 | // Search for the next Row Opening tag 2567 | while (self::f_Xml_GetNextEntityName($SrcP, $PosR, $tagR, $pRO, $p)) { 2568 | 2569 | $pROe = strpos($SrcP, '>', $p) + 1; 2570 | $singleR = ($SrcP[$pROe-2] === '/'); 2571 | 2572 | // If the tag is not a closing, a self-closing and has a name 2573 | if ($tagR!=='') { 2574 | 2575 | if (in_array($tagR, $conf['ignore'])) { 2576 | // This tag must be ignored 2577 | $PosR = $p; 2578 | } elseif (isset($conf['cols'][$tagR])) { 2579 | // Column definition that must be merged as a cell 2580 | if ($mode_column === false) return $this->meth_Misc_Alert("Parallel", "There is a column definition ($tagR) after a row (".$Rows[$RowIdx-1]['tag'].")."); 2581 | if (isset($RowType['_column'])) { 2582 | $RowType['_column']++; 2583 | } else { 2584 | $RowType['_column'] = 1; 2585 | } 2586 | $att = $conf['cols'][$tagR]; 2587 | $this->meth_Locator_FindParallelCol($SrcP, $PosR, $tagR, $pRO, $p, $SrcPOffset, $RowIdx, $ZoneBeg, $ZoneEnd, $att, $Loc, $Cells, $ColNum, $IsRef, $RefCellB, $RefCellE, $RefRow); 2588 | 2589 | } elseif (!$singleR) { 2590 | 2591 | // Search the Row Closing tag 2592 | $locRE = self::f_Xml_FindTag($SrcP, $tagR, false, $pROe, true, -1, false); 2593 | if ($locRE===false) return $this->meth_Misc_Alert("Parallel", "The row closing tag is not found. (tagR=$tagR, p=$p, pROe=$pROe)"); 2594 | 2595 | // Inner source 2596 | $SrcR = substr($SrcP, $pROe, $locRE->PosBeg - $pROe); 2597 | $SrcROffset = $SrcPOffset + $pROe; 2598 | 2599 | if (in_array($tagR, $conf['rows'])) { 2600 | 2601 | if ( $mode_column && isset($RowType['_column']) ) { 2602 | $Rows[$RowIdx] = array('tag'=>'_column', 'cells' => $Cells, 'isref' => $IsRef, 'count' => $RowType['_column']); 2603 | $RowIdx++; 2604 | } 2605 | 2606 | $mode_column = false; 2607 | 2608 | if (isset($RowType[$tagR])) { 2609 | $RowType[$tagR]++; 2610 | } else { 2611 | $RowType[$tagR] = 1; 2612 | } 2613 | 2614 | // Now we've got the row entity, we search for cell entities 2615 | $Cells = array(); 2616 | $ColNum = 1; 2617 | $PosC = 0; 2618 | $IsRef = false; 2619 | 2620 | // Loop on Cell Opening tags 2621 | while (self::f_Xml_GetNextEntityName($SrcR, $PosC, $tagC, $pCO, $p)) { 2622 | if (isset($conf['cells'][$tagC]) ) { 2623 | $att = $conf['cells'][$tagC]; 2624 | $this->meth_Locator_FindParallelCol($SrcR, $PosC, $tagC, $pCO, $p, $SrcROffset, $RowIdx, $ZoneBeg, $ZoneEnd, $att, $Loc, $Cells, $ColNum, $IsRef, $RefCellB, $RefCellE, $RefRow); 2625 | } else { 2626 | $PosC = $p; 2627 | } 2628 | } 2629 | 2630 | $Rows[$RowIdx] = array('tag'=>$tagR, 'cells' => $Cells, 'isref' => $IsRef, 'count' => $RowType[$tagR]); 2631 | $RowIdx++; 2632 | 2633 | } 2634 | 2635 | $PosR = $locRE->PosEnd; 2636 | 2637 | } else { 2638 | $PosR = $pROe; 2639 | } 2640 | } else { 2641 | $PosR = $pROe; 2642 | } 2643 | } 2644 | 2645 | //return $Rows; 2646 | 2647 | $Blocks = array(); 2648 | $rMax = count($Rows) -1; 2649 | foreach ($Rows as $r=>$Row) { 2650 | $Cells = $Row['cells']; 2651 | if (isset($Cells[$RefCellB]) && $Cells[$RefCellB]['IsBegin']) { 2652 | if ( isset($Cells[$RefCellE]) && $Cells[$RefCellE]['IsEnd'] ) { 2653 | $PosBeg = $Cells[$RefCellB]['PosBeg']; 2654 | $PosEnd = $Cells[$RefCellE]['PosEnd']; 2655 | $Blocks[$r] = array( 2656 | 'PosBeg' => $PosBeg, 2657 | 'PosEnd' => $PosEnd, 2658 | 'IsRef' => $Row['isref'], 2659 | 'Src' => substr($Txt, $PosBeg, $PosEnd - $PosBeg + 1), 2660 | ); 2661 | } else { 2662 | return $this->meth_Misc_Alert("Parallel", "At row ".$Row['count']." having entity [".$Row['tag']."], the column $RefCellE is missing or is not the last in a set of spanned columns. (The block is defined from column $RefCellB to $RefCellE)"); 2663 | } 2664 | } else { 2665 | return $this->meth_Misc_Alert("Parallel", "At row ".$Row['count']." having entity [".$Row['tag']."],the column $RefCellB is missing or is not the first in a set of spanned columns. (The block is defined from column $RefCellB to $RefCellE)"); 2666 | } 2667 | } 2668 | 2669 | return $Blocks; 2670 | 2671 | } 2672 | 2673 | function meth_Locator_FindParallelCol($SrcR, &$PosC, $tagC, $pCO, $p, $SrcROffset, $RowIdx, $ZoneBeg, $ZoneEnd, &$att, &$Loc, &$Cells, &$ColNum, &$IsRef, &$RefCellB, &$RefCellE, &$RefRow) { 2674 | 2675 | $pCOe = false; 2676 | 2677 | // Read parameters 2678 | $Loc->PrmLst = array(); 2679 | self::f_Loc_PrmRead($SrcR,$p,true,'\'"','<','>',$Loc,$pCOe,true); 2680 | 2681 | $singleC = ($SrcR[$pCOe-1] === '/'); 2682 | if ($singleC) { 2683 | $pCEe = $pCOe; 2684 | } else { 2685 | // Find the Cell Closing tag 2686 | $locCE = self::f_Xml_FindTag($SrcR, $tagC, false, $pCOe, true, -1, false); 2687 | if ($locCE===false) return $this->meth_Misc_Alert("Parallel", "The cell closing tag is not found. (pCOe=$pCOe)"); 2688 | $pCEe = $locCE->PosEnd; 2689 | } 2690 | 2691 | // Check the cell of reference 2692 | $Width = (isset($Loc->PrmLst[$att])) ? intval($Loc->PrmLst[$att]) : 1; 2693 | $ColNumE = $ColNum + $Width -1; // Ending Cell 2694 | $PosBeg = $SrcROffset + $pCO; 2695 | $PosEnd = $SrcROffset + $pCEe; 2696 | $OnZone = false; 2697 | if ( ($PosBeg <= $ZoneBeg) && ($ZoneBeg <= $PosEnd) && ($RefRow===false) ) { 2698 | $RefRow = $RowIdx; 2699 | $RefCellB = $ColNum; 2700 | $OnZone = true; 2701 | $IsRef = true; 2702 | } 2703 | if ( ($PosBeg <= $ZoneEnd) && ($ZoneEnd <= $PosEnd) ) { 2704 | $RefCellE = $ColNum; 2705 | $OnZone = true; 2706 | } 2707 | 2708 | // Save info 2709 | $Cell = array( 2710 | //'_tagR' => $tagR, '_tagC' => $tagC, '_att' => $att, '_OnZone' => $OnZone, '_PrmLst' => $Loc->PrmLst, '_Offset' => $SrcROffset, '_Src' => substr($SrcR, $pCO, $locCE->PosEnd - $pCO + 1), 2711 | 'PosBeg' => $PosBeg, 2712 | 'PosEnd' => $PosEnd, 2713 | 'ColNum' => $ColNum, 2714 | 'Width' => $Width, 2715 | 'IsBegin' => true, 2716 | 'IsEnd' => false, 2717 | ); 2718 | $Cells[$ColNum] = $Cell; 2719 | 2720 | // add a virtual column to say if its a ending 2721 | if (!isset($Cells[$ColNumE])) $Cells[$ColNumE] = array('IsBegin' => false); 2722 | 2723 | $Cells[$ColNumE]['IsEnd'] = true; 2724 | $Cells[$ColNumE]['PosEnd'] = $Cells[$ColNum]['PosEnd']; 2725 | 2726 | $PosC = $pCEe; 2727 | $ColNum += $Width; 2728 | 2729 | } 2730 | 2731 | function meth_Merge_Block(&$Txt,$BlockLst,&$SrcId,&$Query,$SpePrm,$SpeRecNum,$QryPrms=false) { 2732 | 2733 | $BlockSave = $this->_CurrBlock; 2734 | $this->_CurrBlock = $BlockLst; 2735 | 2736 | // Get source type and info 2737 | $Src = new clsTbsDataSource; 2738 | if (!$Src->DataPrepare($SrcId,$this)) { 2739 | $this->_CurrBlock = $BlockSave; 2740 | return 0; 2741 | } 2742 | 2743 | if (is_string($BlockLst)) $BlockLst = explode(',', $BlockLst); 2744 | $BlockNbr = count($BlockLst); 2745 | $BlockId = 0; 2746 | $WasP1 = false; 2747 | $NbrRecTot = 0; 2748 | $QueryZ = &$Query; 2749 | $ReturnData = false; 2750 | $Nothing = true; 2751 | 2752 | while ($BlockId<$BlockNbr) { 2753 | 2754 | $RecSpe = 0; // Row with a special block's definition (used for the navigation bar) 2755 | $QueryOk = true; 2756 | $this->_CurrBlock = trim($BlockLst[$BlockId]); 2757 | if ($this->_CurrBlock==='*') { 2758 | $ReturnData = true; 2759 | if ($Src->RecSaved===false) $Src->RecSaving = true; 2760 | $this->_CurrBlock = ''; 2761 | } 2762 | 2763 | // Search the block 2764 | $LocR = $this->meth_Locator_FindBlockLst($Txt,$this->_CurrBlock,0,$SpePrm); 2765 | 2766 | if ($LocR->BlockFound) { 2767 | 2768 | $Nothing = false; 2769 | 2770 | if ($LocR->Special!==false) $RecSpe = $SpeRecNum; 2771 | // OnData 2772 | if ($Src->OnDataPrm = isset($LocR->PrmLst['ondata'])) { 2773 | $Src->OnDataPrmRef = $LocR->PrmLst['ondata']; 2774 | if (isset($Src->OnDataPrmDone[$Src->OnDataPrmRef])) { 2775 | $Src->OnDataPrm = false; 2776 | } else { 2777 | $ErrMsg = false; 2778 | if ($this->meth_Misc_UserFctCheck($Src->OnDataPrmRef,'f',$ErrMsg,$ErrMsg,true)) { 2779 | $Src->OnDataOk = true; 2780 | } else { 2781 | $LocR->FullName = $this->_CurrBlock; 2782 | $Src->OnDataPrm = $this->meth_Misc_Alert($LocR,'(parameter ondata) '.$ErrMsg,false,'block'); 2783 | } 2784 | } 2785 | } 2786 | // Dynamic query 2787 | if ($LocR->P1) { 2788 | if ( ($LocR->PrmLst['p1']===true) && ((!is_string($Query)) || (strpos($Query,'%p1%')===false)) ) { // p1 with no value is a trick to perform new block with same name 2789 | if ($Src->RecSaved===false) $Src->RecSaving = true; 2790 | } elseif (is_string($Query)) { 2791 | $Src->RecSaved = false; 2792 | unset($QueryZ); $QueryZ = ''.$Query; 2793 | $i = 1; 2794 | do { 2795 | $x = 'p'.$i; 2796 | if (isset($LocR->PrmLst[$x])) { 2797 | $QueryZ = str_replace('%p'.$i.'%',$LocR->PrmLst[$x],$QueryZ); 2798 | $i++; 2799 | } else { 2800 | $i = false; 2801 | } 2802 | } while ($i!==false); 2803 | } 2804 | $WasP1 = true; 2805 | } elseif (($Src->RecSaved===false) && ($BlockNbr-$BlockId>1)) { 2806 | $Src->RecSaving = true; 2807 | } 2808 | } elseif ($WasP1) { 2809 | $QueryOk = false; 2810 | $WasP1 = false; 2811 | } 2812 | 2813 | // Open the recordset 2814 | if ($QueryOk) { 2815 | if ((!$LocR->BlockFound) && (!$LocR->FieldOutside)) { 2816 | // Special case: return data without any block to merge 2817 | $QueryOk = false; 2818 | if ($ReturnData && (!$Src->RecSaved)) { 2819 | if ($Src->DataOpen($QueryZ,$QryPrms)) { 2820 | do {$Src->DataFetch();} while ($Src->CurrRec!==false); 2821 | $Src->DataClose(); 2822 | } 2823 | } 2824 | } else { 2825 | $QueryOk = $Src->DataOpen($QueryZ,$QryPrms); 2826 | if (!$QueryOk) { 2827 | if ($WasP1) { $WasP1 = false;} else {$LocR->FieldOutside = false;} // prevent from infinit loop 2828 | } 2829 | } 2830 | } 2831 | 2832 | // Merge sections 2833 | if ($QueryOk) { 2834 | if ($Src->Type===2) { // Special for Text merge 2835 | if ($LocR->BlockFound) { 2836 | $Txt = substr_replace($Txt,$Src->RecSet,$LocR->PosBeg,$LocR->PosEnd-$LocR->PosBeg+1); 2837 | $Src->DataFetch(); // store data, may be needed for multiple blocks 2838 | $Src->RecNum = 1; 2839 | $Src->CurrRec = false; 2840 | } else { 2841 | $Src->DataAlert('can\'t merge the block with a text value because the block definition is not found.'); 2842 | } 2843 | } elseif ($LocR->BlockFound===false) { 2844 | $Src->DataFetch(); // Merge first record only 2845 | } elseif (isset($LocR->PrmLst['parallel'])) { 2846 | $this->meth_Merge_BlockParallel($Txt,$LocR,$Src); 2847 | } else { 2848 | $this->meth_Merge_BlockSections($Txt,$LocR,$Src,$RecSpe); 2849 | } 2850 | $Src->DataClose(); // Close the resource 2851 | } 2852 | 2853 | if (!$WasP1) { 2854 | $NbrRecTot += $Src->RecNum; 2855 | $BlockId++; 2856 | } 2857 | if ($LocR->FieldOutside) { 2858 | $Nothing = false; 2859 | $this->meth_Merge_FieldOutside($Txt,$Src->CurrRec,$Src->RecNum,$LocR->FOStop); 2860 | } 2861 | 2862 | } 2863 | 2864 | // End of the merge 2865 | unset($LocR); 2866 | $this->_CurrBlock = $BlockSave; 2867 | if ($ReturnData) { 2868 | return $Src->RecSet; 2869 | } else { 2870 | unset($Src); 2871 | return ($Nothing) ? false : $NbrRecTot; 2872 | } 2873 | 2874 | } 2875 | 2876 | function meth_Merge_BlockParallel(&$Txt,&$LocR,&$Src) { 2877 | 2878 | // Main loop 2879 | $Src->DataFetch(); 2880 | 2881 | // Prepare sources 2882 | $BlockRes = array(); 2883 | for ($i=1 ; $i<=$LocR->SectionNbr ; $i++) { 2884 | if ($i>1) { 2885 | // Add txt source between the BDefs 2886 | $BlockRes[$i] = substr($Txt, $LocR->SectionLst[$i-1]->PosEnd + 1, $LocR->SectionLst[$i]->PosBeg - $LocR->SectionLst[$i-1]->PosEnd -1); 2887 | } else { 2888 | $BlockRes[$i] = ''; 2889 | } 2890 | } 2891 | 2892 | while($Src->CurrRec!==false) { 2893 | // Merge the current record with all sections 2894 | for ($i=1 ; $i<=$LocR->SectionNbr ; $i++) { 2895 | $SecDef = &$LocR->SectionLst[$i]; 2896 | $SecSrc = $this->meth_Merge_SectionNormal($SecDef,$Src); 2897 | $BlockRes[$i] .= $SecSrc; 2898 | } 2899 | // Next row 2900 | $Src->DataFetch(); 2901 | } 2902 | 2903 | $BlockRes = implode('', $BlockRes); 2904 | $Txt = substr_replace($Txt,$BlockRes,$LocR->PosBeg,$LocR->PosEnd-$LocR->PosBeg+1); 2905 | 2906 | } 2907 | 2908 | function meth_Merge_BlockSections(&$Txt,&$LocR,&$Src,&$RecSpe) { 2909 | 2910 | // Initialise 2911 | $SecId = 0; 2912 | $SecOk = ($LocR->SectionNbr>0); 2913 | $SecSrc = ''; 2914 | $BlockRes = ''; // The result of the chained merged blocks 2915 | $IsSerial = false; 2916 | $SrId = 0; 2917 | $SrNbr = 0; 2918 | $GrpFound = false; 2919 | if ($LocR->HeaderFound || $LocR->FooterFound) { 2920 | $GrpFound = true; 2921 | $piOMG = false; 2922 | if ($LocR->FooterFound) { 2923 | $Src->PrevSave = true; // $Src->PrevRec will be saved then 2924 | } 2925 | } 2926 | if ($LocR->CheckPrev) $Src->PrevSave = true; 2927 | if ($LocR->CheckNext) $Src->NextSave = true; 2928 | // Plug-ins 2929 | $piOMS = false; 2930 | if ($this->_PlugIns_Ok) { 2931 | if (isset($this->_piBeforeMergeBlock)) { 2932 | $ArgLst = array(&$Txt,&$LocR->PosBeg,&$LocR->PosEnd,$LocR->PrmLst,&$Src,&$LocR); 2933 | $this->meth_Plugin_RunAll($this->_piBeforeMergeBlock,$ArgLst); 2934 | } 2935 | if (isset($this->_piOnMergeSection)) { 2936 | $ArgLst = array(&$BlockRes,&$SecSrc); 2937 | $piOMS = true; 2938 | } 2939 | if ($GrpFound && isset($this->_piOnMergeGroup)) { 2940 | $ArgLst2 = array(0,0,&$Src,&$LocR); 2941 | $piOMG = true; 2942 | } 2943 | } 2944 | 2945 | // Main loop 2946 | $Src->DataFetch(); 2947 | 2948 | while($Src->CurrRec!==false) { 2949 | 2950 | // Headers and Footers 2951 | if ($GrpFound) { 2952 | $brk_any = false; 2953 | $brk_src = ''; // concatenated source to insert as of breaked groups (header and footer) 2954 | if ($LocR->FooterFound) { 2955 | $brk = false; 2956 | for ($i=$LocR->FooterNbr;$i>=1;$i--) { 2957 | $GrpDef = &$LocR->FooterDef[$i]; 2958 | $x = $this->meth_Merge_SectionNormal($GrpDef->FDef,$Src); // value of the group expression for the current record 2959 | if ($Src->RecNum===1) { 2960 | // no footer break on first record 2961 | $GrpDef->PrevValue = $x; 2962 | $brk_i = false; 2963 | } else { 2964 | // default state for breaked group 2965 | if ($GrpDef->AddLastGrp) { 2966 | $brk_i = &$brk; // cascading breakings 2967 | } else { 2968 | unset($brk_i); $brk_i = false; // independent breaking 2969 | } 2970 | if (!$brk_i) $brk_i = !($GrpDef->PrevValue===$x); 2971 | if ($brk_i) { 2972 | $brk_any = true; 2973 | $ok = true; 2974 | if ($piOMG) {$ArgLst2[0]=&$Src->PrevRec; $ArgLst2[1]=&$GrpDef; $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup,$ArgLst2);} 2975 | if ($ok!==false) $brk_src = $this->meth_Merge_SectionNormal($GrpDef,$Src->PrevRec).$brk_src; 2976 | $GrpDef->PrevValue = $x; 2977 | } 2978 | } 2979 | } 2980 | } 2981 | if ($LocR->HeaderFound) { 2982 | // Check if the current record breaks any header group 2983 | $brk = ($Src->RecNum===1); // there is always a header break on first record 2984 | for ($i=1;$i<=$LocR->HeaderNbr;$i++) { 2985 | $GrpDef = &$LocR->HeaderDef[$i]; 2986 | $x = $this->meth_Merge_SectionNormal($GrpDef->FDef,$Src); // value of the group expression for the current record 2987 | if (!$brk) $brk = !($GrpDef->PrevValue===$x); // cascading breakings 2988 | if ($brk) { 2989 | $ok = true; 2990 | if ($piOMG) {$ArgLst2[0]=&$Src; $ArgLst2[1]=&$GrpDef; $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup,$ArgLst2);} 2991 | if ($ok!==false) $brk_src .= $this->meth_Merge_SectionNormal($GrpDef,$Src); 2992 | $GrpDef->PrevValue = $x; 2993 | } 2994 | } 2995 | $brk_any = ($brk_any || $brk); 2996 | } 2997 | if ($brk_any) { 2998 | if ($IsSerial) { 2999 | $BlockRes .= $this->meth_Merge_SectionSerial($SecDef,$SrId,$LocR); 3000 | $IsSerial = false; 3001 | } 3002 | $BlockRes .= $brk_src; 3003 | } 3004 | } // end of header and footer 3005 | 3006 | // Increment Section 3007 | if (($IsSerial===false) && $SecOk) { 3008 | $SecId++; 3009 | if ($SecId>$LocR->SectionNbr) $SecId = 1; 3010 | $SecDef = &$LocR->SectionLst[$SecId]; 3011 | $IsSerial = $SecDef->IsSerial; 3012 | if ($IsSerial) { 3013 | $SrId = 0; 3014 | $SrNbr = $SecDef->SrBDefNbr; 3015 | } 3016 | } 3017 | 3018 | // Serial Mode Activation 3019 | if ($IsSerial) { // Serial Merge 3020 | $SrId++; 3021 | $SrBDef = &$SecDef->SrBDefLst[$SrId]; 3022 | $SrBDef->SrTxt = $this->meth_Merge_SectionNormal($SrBDef,$Src); 3023 | if ($SrId>=$SrNbr) { 3024 | $SecSrc = $this->meth_Merge_SectionSerial($SecDef,$SrId,$LocR); 3025 | $BlockRes .= $SecSrc; 3026 | $IsSerial = false; 3027 | } 3028 | } else { // Classic merge 3029 | if ($SecOk) { 3030 | // There is some normal sections 3031 | if ($Src->RecNum===$RecSpe) { 3032 | $SecDef = &$LocR->Special; 3033 | } elseif ($LocR->BoundFound) { 3034 | $first = true; 3035 | for ($i = 0 ; $i < $LocR->BoundNb ; $i++) { 3036 | // all bounds must be tested in order to update next and prev values, but only the first found must be kept 3037 | if ($this->meth_Merge_CheckBounds($LocR->BoundLst[$i],$Src)) { 3038 | if ($first) $SecDef = &$LocR->BoundLst[$i]; 3039 | $first = false; 3040 | } 3041 | } 3042 | } 3043 | $SecSrc = $this->meth_Merge_SectionNormal($SecDef,$Src); 3044 | } else { 3045 | // No normal section 3046 | $SecSrc = ''; 3047 | } 3048 | // Conditional blocks 3049 | if ($LocR->WhenFound) { 3050 | $found = false; 3051 | $continue = true; 3052 | $i = 1; 3053 | do { 3054 | $WhenBDef = &$LocR->WhenLst[$i]; 3055 | $cond = $this->meth_Merge_SectionNormal($WhenBDef->WhenCond,$Src); // conditional expression for the current record 3056 | if ($this->f_Misc_CheckCondition($cond)) { 3057 | $x_when = $this->meth_Merge_SectionNormal($WhenBDef,$Src); 3058 | $SecSrc = ($WhenBDef->WhenBeforeNS) ? $x_when.$SecSrc : $SecSrc.$x_when; 3059 | $found = true; 3060 | if ($LocR->WhenSeveral===false) $continue = false; 3061 | } 3062 | $i++; 3063 | if ($i>$LocR->WhenNbr) $continue = false; 3064 | } while ($continue); 3065 | if (($found===false) && ($LocR->WhenDefault!==false)) { 3066 | $x_when = $this->meth_Merge_SectionNormal($LocR->WhenDefault,$Src); 3067 | $SecSrc = ($LocR->WhenDefaultBeforeNS) ? $x_when.$SecSrc : $SecSrc.$x_when; 3068 | } 3069 | } 3070 | if ($piOMS) $this->meth_PlugIn_RunAll($this->_piOnMergeSection,$ArgLst); 3071 | $BlockRes .= $SecSrc; 3072 | } 3073 | 3074 | // Next record 3075 | $Src->DataFetch(); 3076 | 3077 | } //--> while($CurrRec!==false) { 3078 | 3079 | // At this point, all data has been fetched. 3080 | 3081 | // Source to add after the last record 3082 | $SecSrc = ''; 3083 | 3084 | // Serial: merge the extra the sub-blocks 3085 | if ($IsSerial) $SecSrc .= $this->meth_Merge_SectionSerial($SecDef,$SrId,$LocR); 3086 | 3087 | // Add all footers after the last record 3088 | if ($LocR->FooterFound) { 3089 | if ($Src->RecNum>0) { 3090 | for ($i=1;$i<=$LocR->FooterNbr;$i++) { 3091 | $GrpDef = &$LocR->FooterDef[$i]; 3092 | if ($GrpDef->AddLastGrp) { 3093 | $ok = true; 3094 | if ($piOMG) {$ArgLst2[0]=&$Src->PrevRec; $ArgLst2[1]=&$GrpDef; $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup,$ArgLst2);} 3095 | if ($ok!==false) $SecSrc .= $this->meth_Merge_SectionNormal($GrpDef,$Src->PrevRec); 3096 | } 3097 | } 3098 | } 3099 | } 3100 | 3101 | // NoData 3102 | if ($Src->RecNum===0) { 3103 | if ($LocR->NoData!==false) { 3104 | $SecSrc = $LocR->NoData->Src; 3105 | } elseif(isset($LocR->PrmLst['bmagnet'])) { 3106 | $this->f_Loc_EnlargeToTag($Txt,$LocR,$LocR->PrmLst['bmagnet'],false); 3107 | } 3108 | } 3109 | 3110 | // Plug-ins 3111 | if ($piOMS && ($SecSrc!=='')) $this->meth_PlugIn_RunAll($this->_piOnMergeSection,$ArgLst); 3112 | 3113 | $BlockRes .= $SecSrc; 3114 | 3115 | // Plug-ins 3116 | if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterMergeBlock)) { 3117 | $ArgLst = array(&$BlockRes,&$Src,&$LocR); 3118 | $this->meth_PlugIn_RunAll($this->_piAfterMergeBlock,$ArgLst); 3119 | } 3120 | 3121 | // Merge the result 3122 | $Txt = substr_replace($Txt,$BlockRes,$LocR->PosBeg,$LocR->PosEnd-$LocR->PosBeg+1); 3123 | if ($LocR->P1) $LocR->FOStop = $LocR->PosBeg + strlen($BlockRes) -1; 3124 | 3125 | } 3126 | 3127 | function meth_Merge_AutoVar(&$Txt,$ConvStr,$Id='var') { 3128 | // Merge automatic fields with VarRef 3129 | 3130 | $Pref = &$this->VarPrefix; 3131 | $PrefL = strlen($Pref); 3132 | $PrefOk = ($PrefL>0); 3133 | 3134 | if ($ConvStr===false) { 3135 | $Charset = $this->Charset; 3136 | $this->Charset = false; 3137 | } 3138 | 3139 | // Then we scann all fields in the model 3140 | $x = ''; 3141 | $Pos = 0; 3142 | while ($Loc = $this->meth_Locator_FindTbs($Txt,$Id,$Pos,'.')) { 3143 | if ($Loc->SubNbr==0) $Loc->SubLst[0]=''; // In order to force error message 3144 | if ($Loc->SubLst[0]==='') { 3145 | $Pos = $this->meth_Merge_AutoSpe($Txt,$Loc); 3146 | } elseif ($Loc->SubLst[0][0]==='~') { 3147 | if (!isset($ObjOk)) $ObjOk = (is_object($this->ObjectRef) || is_array($this->ObjectRef)); 3148 | if ($ObjOk) { 3149 | $Loc->SubLst[0] = substr($Loc->SubLst[0],1); 3150 | $Pos = $this->meth_Locator_Replace($Txt,$Loc,$this->ObjectRef,0); 3151 | } elseif (isset($Loc->PrmLst['noerr'])) { 3152 | $Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false); 3153 | } else { 3154 | $this->meth_Misc_Alert($Loc,'property ObjectRef is neither an object nor an array. Its type is \''.gettype($this->ObjectRef).'\'.',true); 3155 | $Pos = $Loc->PosEnd + 1; 3156 | } 3157 | } elseif ($PrefOk && (substr($Loc->SubLst[0],0,$PrefL)!==$Pref)) { 3158 | if (isset($Loc->PrmLst['noerr'])) { 3159 | $Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false); 3160 | } else { 3161 | $this->meth_Misc_Alert($Loc,'does not match the allowed prefix.',true); 3162 | $Pos = $Loc->PosEnd + 1; 3163 | } 3164 | } elseif ( isset($this->VarRef) && isset($this->VarRef[$Loc->SubLst[0]])) { 3165 | $Pos = $this->meth_Locator_Replace($Txt,$Loc, $this->VarRef[$Loc->SubLst[0]], 1); 3166 | } elseif ( is_null($this->VarRef) && isset($GLOBALS[$Loc->SubLst[0]]) ) { 3167 | $Pos = $this->meth_Locator_Replace($Txt,$Loc, $GLOBALS[$Loc->SubLst[0]], 1); 3168 | } else { 3169 | if (isset($Loc->PrmLst['noerr'])) { 3170 | $Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false); 3171 | } else { 3172 | $Pos = $Loc->PosEnd + 1; 3173 | $msg = (is_null($this->VarRef)) ? 'VarRef seems refers to $GLOBALS' : 'VarRef seems refers to a custom array of values'; 3174 | $this->meth_Misc_Alert($Loc,'the key \''.$Loc->SubLst[0].'\' does not exist or is not set in VarRef. ('.$msg.')',true); 3175 | } 3176 | } 3177 | } 3178 | 3179 | if ($ConvStr===false) $this->Charset = $Charset; 3180 | 3181 | return false; // Useful for properties PrmIfVar & PrmThenVar 3182 | 3183 | } 3184 | 3185 | function meth_Merge_AutoSpe(&$Txt,&$Loc) { 3186 | // Merge Special Var Fields ([var..*]) 3187 | 3188 | $ErrMsg = false; 3189 | $SubStart = false; 3190 | if (isset($Loc->SubLst[1])) { 3191 | switch ($Loc->SubLst[1]) { 3192 | case 'now': $x = time(); break; 3193 | case 'version': $x = $this->Version; break; 3194 | case 'script_name': $x = basename(((isset($_SERVER)) ? $_SERVER['PHP_SELF'] : $GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'] )); break; 3195 | case 'template_name': $x = $this->_LastFile; break; 3196 | case 'template_date': $x = ''; if ($this->f_Misc_GetFile($x,$this->_LastFile,'',array(),false)) $x = $x['mtime']; break; 3197 | case 'template_path': $x = dirname($this->_LastFile).'/'; break; 3198 | case 'name': $x = 'TinyButStrong'; break; 3199 | case 'logo': $x = '**TinyButStrong**'; break; 3200 | case 'charset': $x = $this->Charset; break; 3201 | case 'error_msg': $this->_ErrMsgName = $Loc->FullName; return $Loc->PosEnd; break; 3202 | case '': $ErrMsg = 'it doesn\'t have any keyword.'; break; 3203 | case 'tplvars': 3204 | if ($Loc->SubNbr==2) { 3205 | $SubStart = 2; 3206 | $x = implode(',',array_keys($this->TplVars)); // list of all template variables 3207 | } else { 3208 | if (isset($this->TplVars[$Loc->SubLst[2]])) { 3209 | $SubStart = 3; 3210 | $x = &$this->TplVars[$Loc->SubLst[2]]; 3211 | } else { 3212 | $ErrMsg = 'property TplVars doesn\'t have any item named \''.$Loc->SubLst[2].'\'.'; 3213 | } 3214 | } 3215 | break; 3216 | case 'store': 3217 | if ($Loc->SubNbr==2) { 3218 | $SubStart = 2; 3219 | $x = implode('',$this->TplStore); // concatenation of all stores 3220 | } else { 3221 | if (isset($this->TplStore[$Loc->SubLst[2]])) { 3222 | $SubStart = 3; 3223 | $x = &$this->TplStore[$Loc->SubLst[2]]; 3224 | } else { 3225 | $ErrMsg = 'Store named \''.$Loc->SubLst[2].'\' is not defined yet.'; 3226 | } 3227 | } 3228 | if (!isset($Loc->PrmLst['strconv'])) {$Loc->PrmLst['strconv'] = 'no'; $Loc->PrmLst['protect'] = 'no';} 3229 | break; 3230 | case 'cst': $x = @constant($Loc->SubLst[2]); break; 3231 | case 'tbs_info': 3232 | $x = 'TinyButStrong version '.$this->Version.' for PHP 5'; 3233 | $x .= "\r\nInstalled plug-ins: ".count($this->_PlugIns); 3234 | foreach (array_keys($this->_PlugIns) as $pi) { 3235 | $o = &$this->_PlugIns[$pi]; 3236 | $x .= "\r\n- plug-in [".(isset($o->Name) ? $o->Name : $pi ).'] version '.(isset($o->Version) ? $o->Version : '?' ); 3237 | } 3238 | break; 3239 | case 'php_info': 3240 | ob_start(); 3241 | phpinfo(); 3242 | $x = ob_get_contents(); 3243 | ob_end_clean(); 3244 | $x = self::f_Xml_GetPart($x, '(style)+body', false); 3245 | if (!isset($Loc->PrmLst['strconv'])) {$Loc->PrmLst['strconv'] = 'no'; $Loc->PrmLst['protect'] = 'no';} 3246 | break; 3247 | default: 3248 | $IsSupported = false; 3249 | if (isset($this->_piOnSpecialVar)) { 3250 | $x = ''; 3251 | $ArgLst = array(substr($Loc->SubName,1),&$IsSupported ,&$x, &$Loc->PrmLst,&$Txt,&$Loc->PosBeg,&$Loc->PosEnd,&$Loc); 3252 | $this->meth_PlugIn_RunAll($this->_piOnSpecialVar,$ArgLst); 3253 | } 3254 | if (!$IsSupported) $ErrMsg = '\''.$Loc->SubLst[1].'\' is an unsupported keyword.'; 3255 | } 3256 | } else { 3257 | $ErrMsg = 'it doesn\'t have any subname.'; 3258 | } 3259 | if ($ErrMsg!==false) { 3260 | $this->meth_Misc_Alert($Loc,$ErrMsg); 3261 | $x = ''; 3262 | } 3263 | if ($Loc->PosBeg===false) { 3264 | return $Loc->PosEnd; 3265 | } else { 3266 | return $this->meth_Locator_Replace($Txt,$Loc,$x,$SubStart); 3267 | } 3268 | } 3269 | 3270 | function meth_Merge_FieldOutside(&$Txt, &$CurrRec, $RecNum, $PosMax) { 3271 | $Pos = 0; 3272 | $SubStart = ($CurrRec===false) ? false : 0; 3273 | do { 3274 | $Loc = $this->meth_Locator_FindTbs($Txt,$this->_CurrBlock,$Pos,'.'); 3275 | if ($Loc!==false) { 3276 | if (($PosMax!==false) && ($Loc->PosEnd>$PosMax)) return; 3277 | if ($Loc->SubName==='#') { 3278 | $NewEnd = $this->meth_Locator_Replace($Txt,$Loc,$RecNum,false); 3279 | } else { 3280 | $NewEnd = $this->meth_Locator_Replace($Txt,$Loc,$CurrRec,$SubStart); 3281 | } 3282 | if ($PosMax!==false) $PosMax += $NewEnd - $Loc->PosEnd; 3283 | $Pos = $NewEnd; 3284 | } 3285 | } while ($Loc!==false); 3286 | } 3287 | 3288 | /** 3289 | * Check the values of previous and next record for expression. 3290 | * 3291 | * @return boolean 3292 | */ 3293 | function meth_Merge_CheckBounds($BDef,$Src) { 3294 | 3295 | // Retrieve values considering that a new record is fetched 3296 | // The order is important 3297 | if ($BDef->CheckPrev) { 3298 | $BDef->ValPrev = $BDef->ValCurr; 3299 | } 3300 | if ($BDef->CheckNext) { 3301 | if (is_null($BDef->ValNext)) { 3302 | // ValNext is not set at this point for the very first record 3303 | $BDef->ValCurr = $this->meth_Merge_SectionNormal($BDef->BoundExpr,$Src); 3304 | } else { 3305 | $BDef->ValCurr = $BDef->ValNext; 3306 | } 3307 | if ($Src->NextRec->CurrRec === false) { 3308 | // No next record 3309 | $diff_next = true; 3310 | } else { 3311 | $BDef->ValNext = $this->meth_Merge_SectionNormal($BDef->BoundExpr,$Src->NextRec); // merge with next record 3312 | $diff_next = ($BDef->ValCurr !== $BDef->ValNext); 3313 | } 3314 | } else { 3315 | $BDef->ValCurr = $this->meth_Merge_SectionNormal($BDef->BoundExpr,$Src); // merge with current record 3316 | } 3317 | 3318 | // Check values 3319 | $result = false; // this state must never happen 3320 | if ($BDef->CheckPrev) { 3321 | $diff_prev = ($BDef->ValCurr !== $BDef->ValPrev); 3322 | if ($BDef->CheckNext) { 3323 | $result = $diff_prev && $diff_next; 3324 | } else { 3325 | $result = $diff_prev; 3326 | } 3327 | } elseif ($BDef->CheckNext) { 3328 | $result = $diff_next; 3329 | } 3330 | 3331 | return $result; 3332 | 3333 | } 3334 | 3335 | function meth_Merge_SectionNormal(&$BDef,&$Src) { 3336 | 3337 | $Txt = $BDef->Src; 3338 | $LocLst = &$BDef->LocLst; 3339 | $iMax = $BDef->LocNbr; 3340 | $PosMax = strlen($Txt); 3341 | 3342 | if ($Src===false) { // Erase all fields 3343 | 3344 | $x = ''; 3345 | 3346 | // Chached locators 3347 | for ($i=$iMax;$i>0;$i--) { 3348 | if ($LocLst[$i]->PosBeg<$PosMax) { 3349 | $this->meth_Locator_Replace($Txt,$LocLst[$i],$x,false); 3350 | if ($LocLst[$i]->Enlarged) { 3351 | $PosMax = $LocLst[$i]->PosBeg; 3352 | $LocLst[$i]->PosBeg = $LocLst[$i]->PosBeg0; 3353 | $LocLst[$i]->PosEnd = $LocLst[$i]->PosEnd0; 3354 | $LocLst[$i]->Enlarged = false; 3355 | } 3356 | } 3357 | } 3358 | 3359 | // Uncached locators 3360 | if ($BDef->Chk) { 3361 | $BlockName = &$BDef->Name; 3362 | $Pos = 0; 3363 | while ($Loc = $this->meth_Locator_FindTbs($Txt,$BlockName,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false); 3364 | } 3365 | 3366 | } else { 3367 | 3368 | // Cached locators 3369 | for ($i=$iMax;$i>0;$i--) { 3370 | if ($LocLst[$i]->PosBeg<$PosMax) { 3371 | if ($LocLst[$i]->IsRecInfo) { 3372 | if ($LocLst[$i]->RecInfo==='#') { 3373 | $this->meth_Locator_Replace($Txt,$LocLst[$i],$Src->RecNum,false); 3374 | } else { 3375 | $this->meth_Locator_Replace($Txt,$LocLst[$i],$Src->RecKey,false); 3376 | } 3377 | } else { 3378 | $this->meth_Locator_Replace($Txt,$LocLst[$i],$Src->CurrRec,0); 3379 | } 3380 | if ($LocLst[$i]->Enlarged) { 3381 | $PosMax = $LocLst[$i]->PosBeg; 3382 | $LocLst[$i]->PosBeg = $LocLst[$i]->PosBeg0; 3383 | $LocLst[$i]->PosEnd = $LocLst[$i]->PosEnd0; 3384 | $LocLst[$i]->Enlarged = false; 3385 | } 3386 | } 3387 | } 3388 | 3389 | // Unchached locators 3390 | if ($BDef->Chk) { 3391 | $BlockName = &$BDef->Name; 3392 | foreach ($Src->CurrRec as $key => $val) { 3393 | $Pos = 0; 3394 | $Name = $BlockName.'.'.$key; 3395 | while ($Loc = $this->meth_Locator_FindTbs($Txt,$Name,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$val,0); 3396 | } 3397 | $Pos = 0; 3398 | $Name = $BlockName.'.#'; 3399 | while ($Loc = $this->meth_Locator_FindTbs($Txt,$Name,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$Src->RecNum,0); 3400 | $Pos = 0; 3401 | $Name = $BlockName.'.$'; 3402 | while ($Loc = $this->meth_Locator_FindTbs($Txt,$Name,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$Src->RecKey,0); 3403 | } 3404 | 3405 | } 3406 | 3407 | // Automatic sub-blocks 3408 | if (isset($BDef->AutoSub)) { 3409 | for ($i=1;$i<=$BDef->AutoSub;$i++) { 3410 | $name = $BDef->Name.'_sub'.$i; 3411 | $query = ''; 3412 | $col = $BDef->Prm['sub'.$i]; 3413 | if ($col===true) $col = ''; 3414 | $col_opt = (substr($col,0,1)==='(') && (substr($col,-1,1)===')'); 3415 | if ($col_opt) $col = substr($col,1,strlen($col)-2); 3416 | if ($col==='') { 3417 | // $col_opt cannot be used here because values which are not array nore object are reformated by $Src into an array with keys 'key' and 'val' 3418 | $data = &$Src->CurrRec; 3419 | } elseif (is_object($Src->CurrRec)) { 3420 | $data = &$Src->CurrRec->$col; 3421 | } else { 3422 | if (array_key_exists($col, $Src->CurrRec)) { 3423 | $data = &$Src->CurrRec[$col]; 3424 | } else { 3425 | if (!$col_opt) $this->meth_Misc_Alert('for merging the automatic sub-block ['.$name.']','key \''.$col.'\' is not found in record #'.$Src->RecNum.' of block ['.$BDef->Name.']. This key can become optional if you designate it with parenthesis in the main block, i.e.: sub'.$i.'=('.$col.')'); 3426 | unset($data); $data = array(); 3427 | } 3428 | } 3429 | if (is_string($data)) { 3430 | $data = explode(',',$data); 3431 | } elseif (is_null($data) || ($data===false)) { 3432 | $data = array(); 3433 | } 3434 | $this->meth_Merge_Block($Txt, $name, $data, $query, false, 0, false); 3435 | } 3436 | } 3437 | 3438 | return $Txt; 3439 | 3440 | } 3441 | 3442 | function meth_Merge_SectionSerial(&$BDef,&$SrId,&$LocR) { 3443 | 3444 | $Txt = $BDef->Src; 3445 | $SrBDefOrdered = &$BDef->SrBDefOrdered; 3446 | $Empty = &$LocR->SerialEmpty; 3447 | 3448 | // All Items 3449 | $F = false; 3450 | for ($i=$BDef->SrBDefNbr;$i>0;$i--) { 3451 | $SrBDef = &$SrBDefOrdered[$i]; 3452 | if ($SrBDef->SrTxt===false) { // Subsection not merged with a record 3453 | if ($Empty===false) { 3454 | $SrBDef->SrTxt = $this->meth_Merge_SectionNormal($SrBDef,$F); 3455 | } else { 3456 | $SrBDef->SrTxt = $Empty; 3457 | } 3458 | } 3459 | $Txt = substr_replace($Txt,$SrBDef->SrTxt,$SrBDef->SrBeg,$SrBDef->SrLen); 3460 | $SrBDef->SrTxt = false; 3461 | } 3462 | 3463 | $SrId = 0; 3464 | return $Txt; 3465 | 3466 | } 3467 | 3468 | /** 3469 | * Merge [onload] or [onshow] fields and blocks 3470 | */ 3471 | function meth_Merge_AutoOn(&$Txt,$Name,$TplVar,$MergeVar) { 3472 | 3473 | $GrpDisplayed = array(); 3474 | $GrpExclusive = array(); 3475 | $P1 = false; 3476 | $FieldBefore = false; 3477 | $Pos = 0; 3478 | 3479 | while ($LocA=$this->meth_Locator_FindBlockNext($Txt,$Name,$Pos,'_',1,$P1,$FieldBefore)) { 3480 | 3481 | if ($LocA->BlockFound) { 3482 | 3483 | if (!isset($GrpDisplayed[$LocA->SubName])) { 3484 | $GrpDisplayed[$LocA->SubName] = false; 3485 | $GrpExclusive[$LocA->SubName] = ($LocA->SubName!==''); 3486 | } 3487 | $Displayed = &$GrpDisplayed[$LocA->SubName]; 3488 | $Exclusive = &$GrpExclusive[$LocA->SubName]; 3489 | 3490 | $DelBlock = false; 3491 | $DelField = false; 3492 | if ($Displayed && $Exclusive) { 3493 | $DelBlock = true; 3494 | } else { 3495 | if (isset($LocA->PrmLst['when'])) { 3496 | if (isset($LocA->PrmLst['several'])) $Exclusive=false; 3497 | $x = $LocA->PrmLst['when']; 3498 | $this->meth_Merge_AutoVar($x,false); 3499 | if ($this->f_Misc_CheckCondition($x)) { 3500 | $DelField = true; 3501 | $Displayed = true; 3502 | } else { 3503 | $DelBlock = true; 3504 | } 3505 | } elseif(isset($LocA->PrmLst['default'])) { 3506 | if ($Displayed) { 3507 | $DelBlock = true; 3508 | } else { 3509 | $Displayed = true; 3510 | $DelField = true; 3511 | } 3512 | $Exclusive = true; // No more block displayed for the group after 3513 | } 3514 | } 3515 | 3516 | // Del parts 3517 | if ($DelField) { 3518 | if ($LocA->PosBeg2!==false) $Txt = substr_replace($Txt, '', $LocA->PosBeg2, $LocA->PosEnd2 - $LocA->PosBeg2 + 1); 3519 | $Txt = substr_replace($Txt,'',$LocA->PosBeg,$LocA->PosEnd-$LocA->PosBeg+1); 3520 | $Pos = $LocA->PosBeg; 3521 | } else { 3522 | $FldPos = $LocA->PosBeg; 3523 | $FldLen = $LocA->PosEnd - $LocA->PosBeg + 1; 3524 | if ($LocA->PosBeg2===false) { 3525 | if ($this->f_Loc_EnlargeToTag($Txt,$LocA,$LocA->PrmLst['block'],false)===false) $this->meth_Misc_Alert($LocA,'at least one tag corresponding to '.$LocA->PrmLst['block'].' is not found. Check opening tags, closing tags and embedding levels.',false,'in block\'s definition'); 3526 | } else { 3527 | $LocA->PosEnd = $LocA->PosEnd2; 3528 | } 3529 | if ($DelBlock) { 3530 | $parallel = false; 3531 | if (isset($LocA->PrmLst['parallel'])) { 3532 | // may return false if error 3533 | $parallel = $this->meth_Locator_FindParallel($Txt, $LocA->PosBeg, $LocA->PosEnd, $LocA->PrmLst['parallel']); 3534 | if ($parallel===false) { 3535 | $Txt = substr_replace($Txt,'',$FldPos,$FldLen); 3536 | } else { 3537 | // delete in reverse order 3538 | for ($r = count($parallel)-1 ; $r >= 0 ; $r--) { 3539 | $p = $parallel[$r]; 3540 | $Txt = substr_replace($Txt,'',$p['PosBeg'],$p['PosEnd']-$p['PosBeg']+1); 3541 | } 3542 | } 3543 | } else { 3544 | $Txt = substr_replace($Txt,'',$LocA->PosBeg,$LocA->PosEnd-$LocA->PosBeg+1); 3545 | } 3546 | $Pos = $LocA->PosBeg; 3547 | } else { 3548 | // Merge the block as if it was a field 3549 | $x = ''; 3550 | $this->meth_Locator_Replace($Txt,$LocA,$x,false); 3551 | $Pos = $LocA->PosNext; 3552 | } 3553 | } 3554 | 3555 | } else { // Field (the Loc has no subname at this point) 3556 | 3557 | // Check for Template Var 3558 | if ($TplVar) { 3559 | if (isset($LocA->PrmLst['tplvars']) || isset($LocA->PrmLst['tplfrms'])) { 3560 | $Scan = ''; 3561 | foreach ($LocA->PrmLst as $Key => $Val) { 3562 | if ($Scan=='v') { 3563 | $this->TplVars[$Key] = $Val; 3564 | } elseif ($Scan=='f') { 3565 | self::f_Misc_FormatSave($Val,$Key); 3566 | } elseif ($Key==='tplvars') { 3567 | $Scan = 'v'; 3568 | } elseif ($Key==='tplfrms') { 3569 | $Scan = 'f'; 3570 | } 3571 | } 3572 | } 3573 | } 3574 | 3575 | $x = ''; 3576 | $this->meth_Locator_Replace($Txt,$LocA,$x,false); 3577 | $Pos = $LocA->PosNext; // continue at the start so embedded fields can be merged 3578 | 3579 | } 3580 | 3581 | } 3582 | 3583 | if ($MergeVar) $this->meth_Merge_AutoVar($Txt,true,$Name); // merge other fields (must have subnames) 3584 | 3585 | foreach ($this->Assigned as $n=>$a) { 3586 | if (isset($a['auto']) && ($a['auto']===$Name)) { 3587 | $x = array(); 3588 | $this->meth_Misc_Assign($n,$x,false); 3589 | } 3590 | } 3591 | 3592 | } 3593 | 3594 | // Prepare the strconv parameter 3595 | function meth_Conv_Prepare(&$Loc, $StrConv) { 3596 | $x = strtolower($StrConv); 3597 | $x = '+'.str_replace(' ','',$x).'+'; 3598 | if (strpos($x,'+esc+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvEsc = true; } 3599 | if (strpos($x,'+wsp+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvWS = true; } 3600 | if (strpos($x,'+js+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvJS = true; } 3601 | if (strpos($x,'+url+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvUrl = true; } 3602 | if (strpos($x,'+utf8+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvUtf8 = true; } 3603 | if (strpos($x,'+no+')!==false) $Loc->ConvStr = false; 3604 | if (strpos($x,'+yes+')!==false) $Loc->ConvStr = true; 3605 | if (strpos($x,'+nobr+')!==false) {$Loc->ConvStr = true; $Loc->ConvBr = false; } 3606 | } 3607 | 3608 | // Convert a string with charset or custom function 3609 | function meth_Conv_Str(&$Txt,$ConvBr=true) { 3610 | if ($this->Charset==='') { // Html by default 3611 | $Txt = htmlspecialchars($Txt, ENT_COMPAT); // ENT_COMPAT is no more the default value since PHP 8.1 3612 | if ($ConvBr) $Txt = nl2br($Txt); 3613 | } elseif ($this->_CharsetFct) { 3614 | $Txt = call_user_func($this->Charset, $Txt,$ConvBr); 3615 | } else { 3616 | $Txt = htmlspecialchars($Txt, ENT_COMPAT, $this->Charset); 3617 | if ($ConvBr) $Txt = nl2br($Txt); 3618 | } 3619 | } 3620 | 3621 | // Standard alert message provided by TinyButStrong, return False is the message is cancelled. 3622 | function meth_Misc_Alert($Src,$Msg,$NoErrMsg=false,$SrcType=false) { 3623 | $this->ErrCount++; 3624 | if ($this->NoErr || (PHP_SAPI==='cli') ) { 3625 | $t = array('','','','',''); 3626 | } else { 3627 | $t = array('
','','','','
'); 3628 | $Msg = htmlentities($Msg); 3629 | } 3630 | if (!is_string($Src)) { 3631 | if ($SrcType===false) $SrcType='in field'; 3632 | if (isset($Src->PrmLst['tbstype'])) { 3633 | $Msg = 'Column \''.$Src->SubName.'\' is expected but missing in the current record.'; 3634 | $Src = 'Parameter \''.$Src->PrmLst['tbstype'].'='.$Src->SubName.'\''; 3635 | $NoErrMsg = false; 3636 | } else { 3637 | $Src = $SrcType.' '.$this->_ChrOpen.$Src->FullName.'...'.$this->_ChrClose; 3638 | } 3639 | } 3640 | $x = $t[0].'TinyButStrong Error'.$t[1].' '.$Src.': '.$Msg; 3641 | if ($NoErrMsg) $x = $x.' '.$t[2].'This message can be cancelled using parameter \'noerr\'.'.$t[3]; 3642 | $x = $x.$t[4]."\n"; 3643 | if ($this->NoErr) { 3644 | $this->ErrMsg .= $x; 3645 | } else { 3646 | if (PHP_SAPI!=='cli') { 3647 | $x = str_replace($this->_ChrOpen,$this->_ChrProtect,$x); 3648 | } 3649 | echo $x; 3650 | } 3651 | return false; 3652 | } 3653 | 3654 | function meth_Misc_Assign($Name,&$ArgLst,$CallingMeth) { 3655 | // $ArgLst must be by reference in order to have its inner items by reference too. 3656 | 3657 | if (!isset($this->Assigned[$Name])) { 3658 | if ($CallingMeth===false) return true; 3659 | return $this->meth_Misc_Alert('with '.$CallingMeth.'() method','key \''.$Name.'\' is not defined in property Assigned.'); 3660 | } 3661 | 3662 | $a = &$this->Assigned[$Name]; 3663 | $meth = (isset($a['type'])) ? $a['type'] : 'MergeBlock'; 3664 | if (($CallingMeth!==false) && (strcasecmp($CallingMeth,$meth)!=0)) return $this->meth_Misc_Alert('with '.$CallingMeth.'() method','the assigned key \''.$Name.'\' cannot be used with method '.$CallingMeth.' because it is defined to run with '.$meth.'.'); 3665 | 3666 | $n = count($a); 3667 | for ($i=0;$i<$n;$i++) { 3668 | if (isset($a[$i])) $ArgLst[$i] = &$a[$i]; 3669 | } 3670 | 3671 | if ($CallingMeth===false) { 3672 | if (in_array(strtolower($meth),array('mergeblock','mergefield'))) { 3673 | call_user_func_array(array(&$this,$meth), $ArgLst); 3674 | } else { 3675 | return $this->meth_Misc_Alert('', 'The assigned field \''.$Name.'\'. cannot be merged because its type \''.$a[0].'\' is not supported.'); 3676 | } 3677 | } 3678 | if (!isset($a['merged'])) $a['merged'] = 0; 3679 | $a['merged']++; 3680 | return true; 3681 | } 3682 | 3683 | function meth_Misc_IsMainTpl() { 3684 | return ($this->_Mode==0); 3685 | } 3686 | 3687 | function meth_Misc_ChangeMode($Init,&$Loc,&$CurrVal) { 3688 | if ($Init) { 3689 | // Save contents configuration 3690 | $Loc->SaveSrc = &$this->Source; 3691 | $Loc->SaveMode = $this->_Mode; 3692 | $Loc->SaveVarRef = &$this->VarRef; 3693 | unset($this->Source); $this->Source = ''; 3694 | $this->_Mode++; // Mode>0 means subtemplate mode 3695 | if ($this->OldSubTpl) { 3696 | ob_start(); // Start buffuring output 3697 | $Loc->SaveRender = $this->Render; 3698 | } 3699 | $this->Render = TBS_OUTPUT; 3700 | } else { 3701 | // Restore contents configuration 3702 | if ($this->OldSubTpl) { 3703 | $CurrVal = ob_get_contents(); 3704 | ob_end_clean(); 3705 | $this->Render = $Loc->SaveRender; 3706 | } else { 3707 | $CurrVal = $this->Source; 3708 | } 3709 | $this->Source = &$Loc->SaveSrc; 3710 | $this->_Mode = $Loc->SaveMode; 3711 | $this->VarRef = &$Loc->SaveVarRef; 3712 | } 3713 | } 3714 | 3715 | function meth_Misc_UserFctCheck(&$FctInfo,$FctCat,&$FctObj,&$ErrMsg,$FctCheck=false) { 3716 | 3717 | $FctId = $FctCat.':'.$FctInfo; 3718 | if (isset($this->_UserFctLst[$FctId])) { 3719 | $FctInfo = $this->_UserFctLst[$FctId]; 3720 | return true; 3721 | } 3722 | 3723 | // Check and put in cache 3724 | $FctStr = $FctInfo; 3725 | $IsData = ($FctCat!=='f'); 3726 | $Save = true; 3727 | if ($FctStr[0]==='~') { 3728 | $ObjRef = &$this->ObjectRef; 3729 | $Lst = explode('.',substr($FctStr,1)); 3730 | $iMax = count($Lst) - 1; 3731 | $Suff = 'tbsdb'; 3732 | $iMax0 = $iMax; 3733 | if ($IsData) { 3734 | $Suff = $Lst[$iMax]; 3735 | $iMax--; 3736 | } 3737 | // Reading sub items 3738 | for ($i=0;$i<=$iMax;$i++) { 3739 | $x = &$Lst[$i]; 3740 | if (is_object($ObjRef)) { 3741 | $form = $this->f_Misc_ParseFctForm($x); 3742 | $n = $form['name']; 3743 | if ($i === $iMax0) { 3744 | // last item is supposed to be a function's name, without parenthesis 3745 | if ( method_exists($ObjRef,$n) || (method_exists($ObjRef, '__call'))) { 3746 | // Ok, continue. If $form['as_fct'] is true, then it will produce an error when try to call function $x 3747 | } else { 3748 | $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because \''.$n.'\' is not a method in the class \''.get_class($ObjRef).'\'.'; 3749 | return false; 3750 | } 3751 | } elseif ( method_exists($ObjRef,$n) || ($form['as_fct'] && method_exists($ObjRef, 'x__call')) ) { 3752 | $f = array(&$ObjRef,$n); 3753 | unset($ObjRef); 3754 | $ObjRef = call_user_func_array($f,$form['args']); 3755 | } elseif (isset($ObjRef->$n)) { 3756 | $ObjRef = &$ObjRef->$n; 3757 | } else { 3758 | $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because sub-item \''.$n.'\' is neither a method nor a property in the class \''.get_class($ObjRef).'\'.'; 3759 | return false; 3760 | } 3761 | } elseif (($i<$iMax0) && is_array($ObjRef)) { 3762 | if (isset($ObjRef[$x])) { 3763 | $ObjRef = &$ObjRef[$x]; 3764 | } else { 3765 | $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because sub-item \''.$x.'\' is not a existing key in the array.'; 3766 | return false; 3767 | } 3768 | } else { 3769 | $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because '.(($i===0)?'property ObjectRef':'sub-item \''.$x.'\'').' is not an object'.(($i<$iMax)?' or an array.':'.'); 3770 | return false; 3771 | } 3772 | } 3773 | // Referencing last item 3774 | if ($IsData) { 3775 | $FctInfo = array('open'=>'','fetch'=>'','close'=>''); 3776 | foreach ($FctInfo as $act=>$x) { 3777 | $FctName = $Suff.'_'.$act; 3778 | if (method_exists($ObjRef,$FctName)) { 3779 | $FctInfo[$act] = array(&$ObjRef,$FctName); 3780 | } else { 3781 | $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because method '.$FctName.' is not found.'; 3782 | return false; 3783 | } 3784 | } 3785 | $FctInfo['type'] = 4; 3786 | if (isset($this->RecheckObj) && $this->RecheckObj) $Save = false; 3787 | } else { 3788 | $FctInfo = array(&$ObjRef,$x); 3789 | } 3790 | } elseif ($IsData) { 3791 | 3792 | $IsObj = ($FctCat==='o'); 3793 | 3794 | if ($IsObj && method_exists($FctObj,'tbsdb_open') && (!method_exists($FctObj,'+'))) { // '+' avoid a bug in PHP 5 3795 | 3796 | if (!method_exists($FctObj,'tbsdb_fetch')) { 3797 | $ErrMsg = 'the expected method \'tbsdb_fetch\' is not found for the class '.$Cls.'.'; 3798 | return false; 3799 | } 3800 | if (!method_exists($FctObj,'tbsdb_close')) { 3801 | $ErrMsg = 'the expected method \'tbsdb_close\' is not found for the class '.$Cls.'.'; 3802 | return false; 3803 | } 3804 | $FctInfo = array('type'=>5); 3805 | 3806 | } else { 3807 | 3808 | if ($FctCat==='r') { // Resource 3809 | $x = strtolower($FctStr); 3810 | $x = str_replace('-','_',$x); 3811 | $Key = ''; 3812 | $i = 0; 3813 | $iMax = strlen($x); 3814 | while ($i<$iMax) { 3815 | if (($x[$i]==='_') || (($x[$i]>='a') && ($x[$i]<='z')) || (($x[$i]>='0') && ($x[$i]<='9'))) { 3816 | $Key .= $x[$i]; 3817 | $i++; 3818 | } else { 3819 | $i = $iMax; 3820 | } 3821 | } 3822 | } else { 3823 | $Key = $FctStr; 3824 | } 3825 | 3826 | $FctInfo = array('open'=>'','fetch'=>'','close'=>''); 3827 | foreach ($FctInfo as $act=>$x) { 3828 | $FctName = 'tbsdb_'.$Key.'_'.$act; 3829 | if (function_exists($FctName)) { 3830 | $FctInfo[$act] = $FctName; 3831 | } else { 3832 | $err = true; 3833 | if ($act==='open') { // Try simplified key 3834 | $p = strpos($Key,'_'); 3835 | if ($p!==false) { 3836 | $Key2 = substr($Key,0,$p); 3837 | $FctName2 = 'tbsdb_'.$Key2.'_'.$act; 3838 | if (function_exists($FctName2)) { 3839 | $err = false; 3840 | $Key = $Key2; 3841 | $FctInfo[$act] = $FctName2; 3842 | } 3843 | } 3844 | } 3845 | if ($err) { 3846 | $ErrMsg = 'Data source Id \''.$FctStr.'\' is unsupported because function \''.$FctName.'\' is not found.'; 3847 | return false; 3848 | } 3849 | } 3850 | } 3851 | 3852 | $FctInfo['type'] = 3; 3853 | 3854 | } 3855 | 3856 | } else { 3857 | if ( $FctCheck && ($this->FctPrefix!=='') && (strncmp($this->FctPrefix,$FctStr,strlen($this->FctPrefix))!==0) ) { 3858 | $ErrMsg = 'user function \''.$FctStr.'\' does not match the allowed prefix.'; return false; 3859 | } else if (!function_exists($FctStr)) { 3860 | $x = explode('.',$FctStr); 3861 | if (count($x)==2) { 3862 | if (class_exists($x[0])) { 3863 | $FctInfo = $x; 3864 | } else { 3865 | $ErrMsg = 'user function \''.$FctStr.'\' is not correct because \''.$x[0].'\' is not a class name.'; return false; 3866 | } 3867 | } else { 3868 | $ErrMsg = 'user function \''.$FctStr.'\' is not found.'; return false; 3869 | } 3870 | } 3871 | } 3872 | 3873 | if ($Save) $this->_UserFctLst[$FctId] = $FctInfo; 3874 | return true; 3875 | 3876 | } 3877 | 3878 | function meth_Misc_RunSubscript(&$CurrVal,$CurrPrm) { 3879 | // Run a subscript without any local variable damage 3880 | return @include($this->_Subscript); 3881 | } 3882 | 3883 | function meth_Misc_Charset($Charset) { 3884 | if ($Charset==='+') return; 3885 | $this->_CharsetFct = false; 3886 | if (is_string($Charset)) { 3887 | if (($Charset!=='') && ($Charset[0]==='=')) { 3888 | $ErrMsg = false; 3889 | $Charset = substr($Charset,1); 3890 | if ($this->meth_Misc_UserFctCheck($Charset,'f',$ErrMsg,$ErrMsg,false)) { 3891 | $this->_CharsetFct = true; 3892 | } else { 3893 | $this->meth_Misc_Alert('with charset option',$ErrMsg); 3894 | $Charset = ''; 3895 | } 3896 | } 3897 | } elseif (is_array($Charset)) { 3898 | $this->_CharsetFct = true; 3899 | } elseif ($Charset===false) { 3900 | $this->Protect = false; 3901 | } else { 3902 | $this->meth_Misc_Alert('with charset option','the option value is not a string nor an array.'); 3903 | $Charset = ''; 3904 | } 3905 | $this->Charset = $Charset; 3906 | } 3907 | 3908 | function meth_PlugIn_RunAll(&$FctBank,&$ArgLst) { 3909 | $OkAll = true; 3910 | foreach ($FctBank as $FctInfo) { 3911 | $Ok = call_user_func_array($FctInfo,$ArgLst); 3912 | if (!is_null($Ok)) $OkAll = ($OkAll && $Ok); 3913 | } 3914 | return $OkAll; 3915 | } 3916 | 3917 | function meth_PlugIn_Install($PlugInId,$ArgLst,$Auto) { 3918 | 3919 | $ErrMsg = 'with plug-in \''.$PlugInId.'\''; 3920 | 3921 | if (class_exists($PlugInId)) { 3922 | // Create an instance 3923 | $IsObj = true; 3924 | $PiRef = new $PlugInId; 3925 | $PiRef->TBS = &$this; // public $TBS property is madatory since PHP 8.2 3926 | if (!method_exists($PiRef,'OnInstall')) return $this->meth_Misc_Alert($ErrMsg,'OnInstall() method is not found.'); 3927 | $FctRef = array(&$PiRef,'OnInstall'); 3928 | } else { 3929 | $FctRef = 'tbspi_'.$PlugInId.'_OnInstall'; 3930 | if(function_exists($FctRef)) { 3931 | $IsObj = false; 3932 | $PiRef = true; 3933 | } else { 3934 | return $this->meth_Misc_Alert($ErrMsg,'no class named \''.$PlugInId.'\' is found, and no function named \''.$FctRef.'\' is found.'); 3935 | } 3936 | } 3937 | 3938 | $this->_PlugIns[$PlugInId] = &$PiRef; 3939 | 3940 | $EventLst = call_user_func_array($FctRef,$ArgLst); 3941 | if (is_string($EventLst)) $EventLst = explode(',',$EventLst); 3942 | if (!is_array($EventLst)) return $this->meth_Misc_Alert($ErrMsg,'OnInstall() method does not return an array.'); 3943 | 3944 | // Add activated methods 3945 | foreach ($EventLst as $Event) { 3946 | $Event = trim($Event); 3947 | if (!$this->meth_PlugIn_SetEvent($PlugInId, $Event)) return false; 3948 | } 3949 | 3950 | return true; 3951 | 3952 | } 3953 | 3954 | function meth_PlugIn_SetEvent($PlugInId, $Event, $NewRef='') { 3955 | // Enable or disable a plug-in event. It can be called by a plug-in, even during the OnInstall event. $NewRef can be used to change the method associated to the event. 3956 | 3957 | // Check the event's name 3958 | if (strpos(',OnCommand,BeforeLoadTemplate,AfterLoadTemplate,BeforeShow,AfterShow,OnData,OnFormat,OnOperation,BeforeMergeBlock,OnMergeSection,OnMergeGroup,AfterMergeBlock,OnSpecialVar,OnMergeField,OnCacheField,', ','.$Event.',')===false) return $this->meth_Misc_Alert('with plug-in \''.$PlugInId.'\'','The plug-in event named \''.$Event.'\' is not supported by TinyButStrong (case-sensitive). This event may come from the OnInstall() method.'); 3959 | 3960 | $PropName = '_pi'.$Event; 3961 | 3962 | if ($NewRef===false) { 3963 | // Disable the event 3964 | if (!isset($this->$PropName)) return false; 3965 | $PropRef = &$this->$PropName; 3966 | unset($PropRef[$PlugInId]); 3967 | return true; 3968 | } 3969 | 3970 | // Prepare the reference to be called 3971 | $PiRef = &$this->_PlugIns[$PlugInId]; 3972 | if (is_object($PiRef)) { 3973 | if ($NewRef==='') $NewRef = $Event; 3974 | if (!method_exists($PiRef, $NewRef)) return $this->meth_Misc_Alert('with plug-in \''.$PlugInId.'\'','The plug-in event named \''.$Event.'\' is declared but its corresponding method \''.$NewRef.'\' is found.'); 3975 | $FctRef = array(&$PiRef, $NewRef); 3976 | } else { 3977 | $FctRef = ($NewRef==='') ? 'tbspi_'.$PlugInId.'_'.$Event : $NewRef; 3978 | if (!function_exists($FctRef)) return $this->meth_Misc_Alert('with plug-in \''.$PlugInId.'\'','The expected function \''.$FctRef.'\' is not found.'); 3979 | } 3980 | 3981 | // Save information into the corresponding property 3982 | if (!isset($this->$PropName)) $this->$PropName = array(); 3983 | $PropRef = &$this->$PropName; 3984 | $PropRef[$PlugInId] = $FctRef; 3985 | 3986 | // Flags saying if a plugin is installed 3987 | switch ($Event) { 3988 | case 'OnCommand': break; 3989 | case 'OnSpecialVar': break; 3990 | case 'OnOperation': break; 3991 | case 'OnFormat': $this->_piOnFrm_Ok = true; break; 3992 | default: $this->_PlugIns_Ok = true; break; 3993 | } 3994 | 3995 | return true; 3996 | 3997 | } 3998 | 3999 | /** 4000 | * Convert any value to a string without specific formating. 4001 | */ 4002 | static function meth_Misc_ToStr($Value) { 4003 | if (is_string($Value)) { 4004 | return $Value; 4005 | } elseif(is_object($Value)) { 4006 | if (method_exists($Value,'__toString')) { 4007 | return $Value->__toString(); 4008 | } elseif (is_a($Value, 'DateTime')) { 4009 | // ISO date-time format 4010 | return $Value->format('c'); 4011 | } 4012 | } 4013 | return @(string)$Value; // (string) is faster than strval() and settype() 4014 | } 4015 | 4016 | /** 4017 | * Return the formated representation of a Date/Time or numeric variable using a 'VB like' format syntax instead of the PHP syntax. 4018 | */ 4019 | function meth_Misc_Format(&$Value,&$PrmLst) { 4020 | 4021 | $FrmStr = $PrmLst['frm']; 4022 | $CheckNumeric = true; 4023 | if (is_string($Value)) $Value = trim($Value); 4024 | 4025 | if ($FrmStr==='') return ''; 4026 | $Frm = self::f_Misc_FormatSave($FrmStr); 4027 | 4028 | // Manage Multi format strings 4029 | if ($Frm['type']=='multi') { 4030 | 4031 | // Found the format according to the value (positive|negative|zero|null) 4032 | 4033 | if (is_numeric($Value)) { 4034 | // Numeric: 4035 | if (is_string($Value)) $Value = 0.0 + $Value; 4036 | if ($Value>0) { 4037 | $FrmStr = &$Frm[0]; 4038 | } elseif ($Value<0) { 4039 | $FrmStr = &$Frm[1]; 4040 | if ($Frm['abs']) $Value = abs($Value); 4041 | } else { 4042 | // zero 4043 | $FrmStr = &$Frm[2]; 4044 | $Minus = ''; 4045 | } 4046 | $CheckNumeric = false; 4047 | } else { 4048 | // date| 4049 | $Value = $this->meth_Misc_ToStr($Value); 4050 | if ($Value==='') { 4051 | // Null value 4052 | return $Frm[3]; 4053 | } else { 4054 | // Date conversion 4055 | $t = strtotime($Value); // We look if it's a date 4056 | if (($t===-1) || ($t===false)) { 4057 | // Date not recognized 4058 | return $Frm[1]; 4059 | } elseif ($t===943916400) { 4060 | // Date to zero in some softwares 4061 | return $Frm[2]; 4062 | } else { 4063 | // It's a date 4064 | $Value = $t; 4065 | $FrmStr = &$Frm[0]; 4066 | } 4067 | } 4068 | } 4069 | 4070 | // Retrieve the correct simple format 4071 | if ($FrmStr==='') return ''; 4072 | $Frm = self::f_Misc_FormatSave($FrmStr); 4073 | 4074 | } 4075 | 4076 | switch ($Frm['type']) { 4077 | case 'num': 4078 | // NUMERIC 4079 | if ($CheckNumeric) { 4080 | if (is_numeric($Value)) { 4081 | if (is_string($Value)) $Value = 0.0 + $Value; 4082 | } else { 4083 | return $this->meth_Misc_ToStr($Value); 4084 | } 4085 | } 4086 | if ($Frm['PerCent']) $Value = $Value * 100; 4087 | $Value = number_format($Value,$Frm['DecNbr'],$Frm['DecSep'],$Frm['ThsSep']); 4088 | if ($Frm['Pad']!==false) $Value = str_pad($Value, $Frm['Pad'], '0', STR_PAD_LEFT); 4089 | if ($Frm['ThsRpl']!==false) $Value = str_replace($Frm['ThsSep'], $Frm['ThsRpl'], $Value); 4090 | $Value = substr_replace($Frm['Str'],$Value,$Frm['Pos'],$Frm['Len']); 4091 | return $Value; 4092 | break; 4093 | case 'date': 4094 | // DATE 4095 | return $this->meth_Misc_DateFormat($Value, $Frm); 4096 | break; 4097 | default: 4098 | return $Frm['string']; 4099 | break; 4100 | } 4101 | 4102 | } 4103 | 4104 | function meth_Misc_DateFormat(&$Value, $Frm) { 4105 | 4106 | if (is_object($Value)) { 4107 | $Value = $this->meth_Misc_ToStr($Value); 4108 | } 4109 | 4110 | if ($Value==='') return ''; 4111 | 4112 | // Note : DateTime object is supported since PHP 5.2 4113 | // So we could simplify this function using only DateTime instead of timestamp. 4114 | 4115 | // Now we try to get the timestamp 4116 | if (is_string($Value)) { 4117 | // Any string value is assumed to be a formated date. 4118 | // If you whant a string value to be a considered to a a time stamp, then use prefixe '@' accordding to the 4119 | $x = strtotime($Value); 4120 | // In case of error return false (return -1 for PHP < 5.1.0) 4121 | if (($x===false) || ($x===-1)) { 4122 | if (!is_numeric($Value)) { 4123 | // At this point the value is not recognized as a date 4124 | // Special fix for PHP 32-bit and date > '2038-01-19 03:14:07' => strtotime() failes 4125 | if (PHP_INT_SIZE === 4) { // 32-bit 4126 | try { 4127 | $date = new DateTime($Value); 4128 | return $date->format($Frm['str_us']); 4129 | // 'locale' cannot be supported in this case because strftime() has to equilavent with DateTime 4130 | } catch (Exception $e) { 4131 | // We take an arbitrary value in order to avoid formating error 4132 | $Value = 0; // '1970-01-01' 4133 | // echo $e->getMessage(); 4134 | } 4135 | } else { 4136 | // We take an arbirtary value in order to avoid formating error 4137 | $Value = 0; // '1970-01-01' 4138 | } 4139 | } 4140 | } else { 4141 | $Value = &$x; 4142 | } 4143 | } else { 4144 | if (!is_numeric($Value)) { 4145 | // It’s not a timestamp, thus we return the non formated value 4146 | return $this->meth_Misc_ToStr($Value); 4147 | } 4148 | } 4149 | 4150 | if ($Frm['loc'] || isset($PrmLst['locale'])) { 4151 | $x = strftime($Frm['str_loc'],$Value); 4152 | $this->meth_Conv_Str($x,false); // may have accent 4153 | return $x; 4154 | } else { 4155 | return date($Frm['str_us'],$Value); 4156 | } 4157 | 4158 | } 4159 | 4160 | /** 4161 | * Apply combo parameters. 4162 | * @param array $PrmLst The existing list of combo 4163 | * @param object|false $Loc The current locator, of false if called from an combo definition 4164 | */ 4165 | static function meth_Misc_ApplyPrmCombo(&$PrmLst, $Loc) { 4166 | 4167 | if (isset($PrmLst['combo'])) { 4168 | 4169 | $name_lst = explode(',', $PrmLst['combo']); 4170 | $DefLst = &$GLOBALS['_TBS_PrmCombo']; 4171 | 4172 | foreach ($name_lst as $name) { 4173 | if (isset($DefLst[$name])) { 4174 | $ap = $DefLst[$name]; 4175 | if (isset($PrmLst['ope']) && isset($ap['ope'])) { 4176 | $PrmLst['ope'] .= ',' . $ap['ope']; // ope will be processed fifo 4177 | unset($ap['ope']); 4178 | } 4179 | if ($Loc !== false) { 4180 | if ( isset($ap['if']) && is_array($ap['if']) ) { 4181 | foreach($ap['if'] as $v) { 4182 | self::f_Loc_PrmIfThen($Loc, true, $v, false); 4183 | } 4184 | unset($ap['if']); 4185 | } 4186 | if ( isset($ap['then']) && is_array($ap['then'])) { 4187 | foreach($ap['then'] as $v) { 4188 | self::f_Loc_PrmIfThen($Loc, false, $v, false); 4189 | } 4190 | unset($ap['then']); 4191 | } 4192 | } 4193 | $PrmLst = array_merge($ap, $PrmLst); 4194 | } else { 4195 | $this->meth_Misc_Alert("with parameter 'combo'", "Combo '". $a. "' is not yet set."); 4196 | } 4197 | } 4198 | 4199 | $PrmLst['_combo'] = $PrmLst['combo']; // for debug 4200 | unset($PrmLst['combo']); // for security 4201 | 4202 | } 4203 | } 4204 | 4205 | /** 4206 | * Simply update an array with another array. 4207 | * It works for both indexed or associativ arrays. 4208 | * NULL value will be deleted from the target array. 4209 | * 4210 | * @param array $array The target array to be updated. 4211 | * @param mixed $numerical True if the keys ar numerical. Use special keyword 'frm' for TBS formats, and 'prm' for a set of parameters. 4212 | * @param mixed $v An associative array of items to modify. Use value NULL for reset $array to an empty array. Other single value will be used with $d. 4213 | * @param mixed $d To be used when $v is a single not null value. Will apply the key $v with value $d. 4214 | */ 4215 | static function f_Misc_UpdateArray(&$array, $numerical, $v, $d) { 4216 | if (!is_array($v)) { 4217 | if (is_null($v)) { 4218 | $array = array(); 4219 | return; 4220 | } else { 4221 | $v = array($v=>$d); 4222 | } 4223 | } 4224 | foreach ($v as $p=>$a) { 4225 | if ($numerical===true) { // numerical keys 4226 | if (is_string($p)) { 4227 | // syntax: item => true/false 4228 | $i = array_search($p, $array, true); 4229 | if ($i===false) { 4230 | if (!is_null($a)) $array[] = $p; 4231 | } else { 4232 | if (is_null($a)) array_splice($array, $i, 1); 4233 | } 4234 | } else { 4235 | // syntax: i => item 4236 | $i = array_search($a, $array, true); 4237 | if ($i==false) $array[] = $a; 4238 | } 4239 | } else { // string keys 4240 | if (is_null($a)) { 4241 | unset($array[$p]); 4242 | } elseif ($numerical==='frm') { 4243 | self::f_Misc_FormatSave($a, $p); 4244 | } else { 4245 | if ($numerical==='prm') { 4246 | // apply existing combo on the new combo, so that all combo are translated into basic parameters 4247 | if ( isset($a['if']) && (!is_array($a['if'])) ) { 4248 | $a['if'] = array($a['if']); 4249 | } 4250 | if ( isset($a['then']) && (!is_array($a['then'])) ) { 4251 | $a['then'] = array($a['then']); 4252 | } 4253 | self::meth_Misc_ApplyPrmCombo($a, false); 4254 | } 4255 | $array[$p] = $a; 4256 | } 4257 | } 4258 | } 4259 | } 4260 | 4261 | static function f_Misc_FormatSave(&$FrmStr,$Alias='') { 4262 | 4263 | $FormatLst = &$GLOBALS['_TBS_FormatLst']; 4264 | 4265 | if (isset($FormatLst[$FrmStr])) { 4266 | if ($Alias!='') $FormatLst[$Alias] = &$FormatLst[$FrmStr]; 4267 | return $FormatLst[$FrmStr]; 4268 | } 4269 | 4270 | if (strpos($FrmStr,'|')!==false) { 4271 | 4272 | // Multi format 4273 | $Frm = explode('|',$FrmStr); // syntax: Postive|Negative|Zero|Null 4274 | $FrmNbr = count($Frm); 4275 | $Frm['abs'] = ($FrmNbr>1); 4276 | if ($FrmNbr<3) $Frm[2] = &$Frm[0]; // zero 4277 | if ($FrmNbr<4) $Frm[3] = ''; // null 4278 | $Frm['type'] = 'multi'; 4279 | $FormatLst[$FrmStr] = $Frm; 4280 | 4281 | } elseif (($nPosEnd = strrpos($FrmStr,'0'))!==false) { 4282 | 4283 | // Numeric format 4284 | $nDecSep = '.'; 4285 | $nDecNbr = 0; 4286 | $nDecOk = true; 4287 | $nPad = false; 4288 | $nPadZ = 0; 4289 | 4290 | if (substr($FrmStr,$nPosEnd+1,1)==='.') { 4291 | $nPosEnd++; 4292 | $nPos = $nPosEnd; 4293 | $nPadZ = 1; 4294 | } else { 4295 | $nPos = $nPosEnd - 1; 4296 | while (($nPos>=0) && ($FrmStr[$nPos]==='0')) { 4297 | $nPos--; 4298 | } 4299 | if (($nPos>=1) && ($FrmStr[$nPos-1]==='0')) { 4300 | $nDecSep = $FrmStr[$nPos]; 4301 | $nDecNbr = $nPosEnd - $nPos; 4302 | } else { 4303 | $nDecOk = false; 4304 | } 4305 | } 4306 | 4307 | // Thousand separator 4308 | $nThsSep = ''; 4309 | $nThsRpl = false; 4310 | if (($nDecOk) && ($nPos>=5)) { 4311 | if ((substr($FrmStr,$nPos-3,3)==='000') && ($FrmStr[$nPos-4]!=='0')) { 4312 | $p = strrpos(substr($FrmStr,0,$nPos-4), '0'); 4313 | if ($p!==false) { 4314 | $len = $nPos-4-$p; 4315 | $x = substr($FrmStr, $p+1, $len); 4316 | if ($len>1) { 4317 | // for compatibility for number_format() with PHP < 5.4.0 4318 | $nThsSep = ($nDecSep=='*') ? '.' : '*'; 4319 | $nThsRpl = $x; 4320 | } else { 4321 | $nThsSep = $x; 4322 | } 4323 | $nPos = $p+1; 4324 | } 4325 | } 4326 | } 4327 | 4328 | // Pass next zero 4329 | if ($nDecOk) $nPos--; 4330 | while (($nPos>=0) && ($FrmStr[$nPos]==='0')) { 4331 | $nPos--; 4332 | } 4333 | 4334 | $nLen = $nPosEnd-$nPos; 4335 | if ( ($nThsSep==='') && ($nLen>($nDecNbr+$nPadZ+1)) ) $nPad = $nLen - $nPadZ; 4336 | 4337 | // Percent 4338 | $nPerCent = (strpos($FrmStr,'%')===false) ? false : true; 4339 | 4340 | $FormatLst[$FrmStr] = array('type'=>'num','Str'=>$FrmStr,'Pos'=>($nPos+1),'Len'=>$nLen,'ThsSep'=>$nThsSep,'ThsRpl'=>$nThsRpl,'DecSep'=>$nDecSep,'DecNbr'=>$nDecNbr,'PerCent'=>$nPerCent,'Pad'=>$nPad); 4341 | 4342 | } else { 4343 | 4344 | // Date format 4345 | $x = $FrmStr; 4346 | $FrmPHP = ''; 4347 | $FrmLOC = ''; 4348 | $StrIn = false; 4349 | $Cnt = 0; 4350 | $i = strpos($FrmStr,'(locale)'); 4351 | $Locale = ($i!==false); 4352 | if ($Locale) $x = substr_replace($x,'',$i,8); 4353 | 4354 | $iEnd = strlen($x); 4355 | for ($i=0;$i<$iEnd;$i++) { 4356 | 4357 | if ($StrIn) { 4358 | // We are in a string part 4359 | if ($x[$i]==='"') { 4360 | if (substr($x,$i+1,1)==='"') { 4361 | $FrmPHP .= '\\"'; // protected char 4362 | $FrmLOC .= $x[$i]; 4363 | $i++; 4364 | } else { 4365 | $StrIn = false; 4366 | } 4367 | } else { 4368 | $FrmPHP .= '\\'.$x[$i]; // protected char 4369 | $FrmLOC .= $x[$i]; 4370 | } 4371 | } else { 4372 | if ($x[$i]==='"') { 4373 | $StrIn = true; 4374 | } else { 4375 | $Cnt++; 4376 | if (strcasecmp(substr($x,$i,2),'hh' )===0) { $FrmPHP .= 'H'; $FrmLOC .= '%H'; $i += 1;} 4377 | elseif (strcasecmp(substr($x,$i,2),'hm' )===0) { $FrmPHP .= 'h'; $FrmLOC .= '%I'; $i += 1;} // for compatibility 4378 | elseif (strcasecmp(substr($x,$i,1),'h' )===0) { $FrmPHP .= 'G'; $FrmLOC .= '%H';} 4379 | elseif (strcasecmp(substr($x,$i,2),'rr' )===0) { $FrmPHP .= 'h'; $FrmLOC .= '%I'; $i += 1;} 4380 | elseif (strcasecmp(substr($x,$i,1),'r' )===0) { $FrmPHP .= 'g'; $FrmLOC .= '%I';} 4381 | elseif (strcasecmp(substr($x,$i,4),'ampm')===0) { $FrmPHP .= substr($x,$i,1); $FrmLOC .= '%p'; $i += 3;} // $Fmp = 'A' or 'a' 4382 | elseif (strcasecmp(substr($x,$i,2),'nn' )===0) { $FrmPHP .= 'i'; $FrmLOC .= '%M'; $i += 1;} 4383 | elseif (strcasecmp(substr($x,$i,2),'ss' )===0) { $FrmPHP .= 's'; $FrmLOC .= '%S'; $i += 1;} 4384 | elseif (strcasecmp(substr($x,$i,2),'xx' )===0) { $FrmPHP .= 'S'; $FrmLOC .= '' ; $i += 1;} 4385 | elseif (strcasecmp(substr($x,$i,4),'yyyy')===0) { $FrmPHP .= 'Y'; $FrmLOC .= '%Y'; $i += 3;} 4386 | elseif (strcasecmp(substr($x,$i,2),'yy' )===0) { $FrmPHP .= 'y'; $FrmLOC .= '%y'; $i += 1;} 4387 | elseif (strcasecmp(substr($x,$i,4),'mmmm')===0) { $FrmPHP .= 'F'; $FrmLOC .= '%B'; $i += 3;} 4388 | elseif (strcasecmp(substr($x,$i,3),'mmm' )===0) { $FrmPHP .= 'M'; $FrmLOC .= '%b'; $i += 2;} 4389 | elseif (strcasecmp(substr($x,$i,2),'mm' )===0) { $FrmPHP .= 'm'; $FrmLOC .= '%m'; $i += 1;} 4390 | elseif (strcasecmp(substr($x,$i,1),'m' )===0) { $FrmPHP .= 'n'; $FrmLOC .= '%m';} 4391 | elseif (strcasecmp(substr($x,$i,4),'wwww')===0) { $FrmPHP .= 'l'; $FrmLOC .= '%A'; $i += 3;} 4392 | elseif (strcasecmp(substr($x,$i,3),'www' )===0) { $FrmPHP .= 'D'; $FrmLOC .= '%a'; $i += 2;} 4393 | elseif (strcasecmp(substr($x,$i,1),'w' )===0) { $FrmPHP .= 'w'; $FrmLOC .= '%u';} 4394 | elseif (strcasecmp(substr($x,$i,4),'dddd')===0) { $FrmPHP .= 'l'; $FrmLOC .= '%A'; $i += 3;} 4395 | elseif (strcasecmp(substr($x,$i,3),'ddd' )===0) { $FrmPHP .= 'D'; $FrmLOC .= '%a'; $i += 2;} 4396 | elseif (strcasecmp(substr($x,$i,2),'dd' )===0) { $FrmPHP .= 'd'; $FrmLOC .= '%d'; $i += 1;} 4397 | elseif (strcasecmp(substr($x,$i,1),'d' )===0) { $FrmPHP .= 'j'; $FrmLOC .= '%d';} 4398 | else { 4399 | $FrmPHP .= '\\'.$x[$i]; // protected char 4400 | $FrmLOC .= $x[$i]; // protected char 4401 | $Cnt--; 4402 | } 4403 | } 4404 | } 4405 | 4406 | } 4407 | 4408 | if ($Cnt>0) { 4409 | $FormatLst[$FrmStr] = array('type'=>'date','str_us'=>$FrmPHP,'str_loc'=>$FrmLOC,'loc'=>$Locale); 4410 | } else { 4411 | $FormatLst[$FrmStr] = array('type'=>'else','string'=>$FrmStr); 4412 | } 4413 | 4414 | } 4415 | 4416 | if ($Alias!='') $FormatLst[$Alias] = &$FormatLst[$FrmStr]; 4417 | 4418 | return $FormatLst[$FrmStr]; 4419 | 4420 | } 4421 | 4422 | static function f_Misc_ConvSpe(&$Loc) { 4423 | if ($Loc->ConvMode!==2) { 4424 | $Loc->ConvMode = 2; 4425 | $Loc->ConvEsc = false; 4426 | $Loc->ConvWS = false; 4427 | $Loc->ConvJS = false; 4428 | $Loc->ConvUrl = false; 4429 | $Loc->ConvUtf8 = false; 4430 | } 4431 | } 4432 | 4433 | /** 4434 | * Return the information if parsing a form which can be either a property of a function. 4435 | * @param string $Str The form. Example : 'my_func(aaa,bbb)' 4436 | * @return array Information about the form. Example : array('name' => 'my_func', 'as_fct' => true, 'args' => array('aaa', 'bbb'),) 4437 | * name: the name of the function of the property. 4438 | * as_fct: true if the form is as a function 4439 | * args: arguments of the function, or empty array if it's a property 4440 | */ 4441 | static function f_Misc_ParseFctForm($Str) { 4442 | $info = array('name' => $Str, 'as_fct' => false, 'args' => array()); 4443 | if (substr($Str,-1,1)===')') { 4444 | $pos = strpos($Str,'('); 4445 | if ($pos!==false) { 4446 | $info['args'] = explode(',',substr($Str,$pos+1,strlen($Str)-$pos-2)); 4447 | $info['name'] = substr($Str,0,$pos); 4448 | $info['as_fct'] = true; 4449 | } 4450 | } 4451 | return $info; 4452 | } 4453 | 4454 | /** 4455 | * Check if a string condition is true. 4456 | * @param string $Str The condition to check. 4457 | * @return boolean True if the condition if checked. 4458 | */ 4459 | static function f_Misc_CheckCondition($Str) { 4460 | // Check if an expression like "exrp1=expr2" is true or false. 4461 | 4462 | // Bluid $StrZ, wich is the same as $Str but with 'z' for each character that is protected with "'". 4463 | // This will help to search for operators outside protected strings. 4464 | $StrZ = $Str; 4465 | $Max = strlen($Str)-1; 4466 | $p = strpos($Str,'\''); 4467 | if ($Esc=($p!==false)) { 4468 | $In = true; 4469 | for ($p=$p+1;$p<=$Max;$p++) { 4470 | if ($StrZ[$p]==='\'') { 4471 | $In = !$In; 4472 | } elseif ($In) { 4473 | $StrZ[$p] = 'z'; 4474 | } 4475 | } 4476 | } 4477 | 4478 | // Find operator and position 4479 | $Ope = '='; 4480 | $Len = 1; 4481 | $p = strpos($StrZ,$Ope); 4482 | if ($p===false) { 4483 | $Ope = '+'; 4484 | $p = strpos($StrZ,$Ope); 4485 | if ($p===false) return false; 4486 | if (($p>0) && ($StrZ[$p-1]==='-')) { 4487 | $Ope = '-+'; $p--; $Len=2; 4488 | } elseif (($p<$Max) && ($StrZ[$p+1]==='-')) { 4489 | $Ope = '+-'; $Len=2; 4490 | } else { 4491 | return false; 4492 | } 4493 | } else { 4494 | if ($p>0) { 4495 | $x = $StrZ[$p-1]; 4496 | if ($x==='!') { 4497 | $Ope = '!='; $p--; $Len=2; 4498 | } elseif ($x==='~') { 4499 | $Ope = '~='; $p--; $Len=2; 4500 | } elseif ($p<$Max) { 4501 | $y = $StrZ[$p+1]; 4502 | if ($y==='=') { 4503 | $Len=2; 4504 | } elseif (($x==='+') && ($y==='-')) { 4505 | $Ope = '+=-'; $p--; $Len=3; 4506 | } elseif (($x==='-') && ($y==='+')) { 4507 | $Ope = '-=+'; $p--; $Len=3; 4508 | } 4509 | } else { 4510 | } 4511 | } 4512 | } 4513 | 4514 | // Read values 4515 | $Val1 = trim(substr($Str,0,$p)); 4516 | $Val2 = trim(substr($Str,$p+$Len)); 4517 | if ($Esc) { 4518 | $NoDelim1 = self::f_Misc_DelDelimiter($Val1,'\''); 4519 | $NoDelim2 = self::f_Misc_DelDelimiter($Val2,'\''); 4520 | } else { 4521 | $NoDelim1 = $NoDelim2 = false; 4522 | } 4523 | 4524 | // Compare values 4525 | if ($Ope==='=') { 4526 | return (strcasecmp($Val1,$Val2)==0); 4527 | } elseif ($Ope==='!=') { 4528 | return (strcasecmp($Val1,$Val2)!=0); 4529 | } elseif ($Ope==='~=') { 4530 | return (preg_match($Val2,$Val1)>0); 4531 | } else { 4532 | // If a value has no string delimiter, we assume it is supposed to be a numerical comparison. 4533 | if ($NoDelim1 && ($Val1 === '') ) $Val1 = '0'; 4534 | if ($NoDelim2 && ($Val2 === '') ) $Val2 ='0'; 4535 | // PHP makes a numerical comparison when each item is independently either a numeric value or a numeric string. Otherwise it makes a string comparison. 4536 | // So we let PHP doing the comparison on its onw way. 4537 | if ($Ope==='+-') { 4538 | return ($Val1 > $Val2); 4539 | } elseif ($Ope==='-+') { 4540 | return ($Val1 < $Val2); 4541 | } elseif ($Ope==='+=-') { 4542 | return ($Val1 >= $Val2); 4543 | } elseif ($Ope==='-=+') { 4544 | return ($Val1<=$Val2); 4545 | } else { 4546 | return false; 4547 | } 4548 | } 4549 | 4550 | } 4551 | 4552 | /** 4553 | * Delete the string delimiters that surrounds the string, if any. But not inside (no need). 4554 | * @param string $Txt The string to modifiy. 4555 | * @param string $Delim The character that can delimit the string. 4556 | * @return boolean True if the given string was not delimited with $Delim. 4557 | */ 4558 | static function f_Misc_DelDelimiter(&$Txt,$Delim) { 4559 | // Delete the string delimiters 4560 | $len = strlen($Txt); 4561 | if (($len>1) && ($Txt[0]===$Delim)) { 4562 | if ($Txt[$len-1]===$Delim) $Txt = substr($Txt,1,$len-2); 4563 | return false; 4564 | } else { 4565 | return true; 4566 | } 4567 | } 4568 | 4569 | static function f_Misc_GetFile(&$Res, &$File, $LastFile='', $IncludePath=false, $Contents=true) { 4570 | // Load the content of a file into the text variable. 4571 | 4572 | $Res = ''; 4573 | $fd = self::f_Misc_TryFile($File, false); 4574 | if ($fd===false) { 4575 | if (is_array($IncludePath)) { 4576 | foreach ($IncludePath as $d) { 4577 | $fd = self::f_Misc_TryFile($File, $d); 4578 | if ($fd!==false) break; 4579 | } 4580 | } 4581 | if (($fd===false) && ($LastFile!='')) $fd = self::f_Misc_TryFile($File, dirname($LastFile)); 4582 | if ($fd===false) return false; 4583 | } 4584 | 4585 | $fs = fstat($fd); 4586 | if ($Contents) { 4587 | // Return contents 4588 | if (isset($fs['size'])) { 4589 | if ($fs['size']>0) $Res = fread($fd,$fs['size']); 4590 | } else { 4591 | while (!feof($fd)) $Res .= fread($fd,4096); 4592 | } 4593 | } else { 4594 | // Return stats 4595 | $Res = $fs; 4596 | } 4597 | 4598 | fclose($fd); 4599 | return true; 4600 | 4601 | } 4602 | 4603 | /** 4604 | * Try to open the file for reading. 4605 | * @param string $File The file name. 4606 | * @param string|bolean $Dir A The directory where to search, of false to omit directory. 4607 | * @return ressource Return the file pointer, of false on error. Note that urgument $File will be updated to the file with directory. 4608 | */ 4609 | static function f_Misc_TryFile(&$File, $Dir) { 4610 | if ($Dir==='') return false; 4611 | $FileSearch = ($Dir===false) ? $File : $Dir.'/'.$File; 4612 | // 'rb' if binary for some OS. fopen() uses include_path and search on the __FILE__ directory while file_exists() doesn't. 4613 | $f = @fopen($FileSearch, 'r', true); 4614 | if ($f!==false) $File = $FileSearch; 4615 | return $f; 4616 | } 4617 | 4618 | /** 4619 | * Read TBS or XML tags, starting to the begining of the tag. 4620 | */ 4621 | static function f_Loc_PrmRead(&$Txt,$Pos,$XmlTag,$DelimChrs,$BegStr,$EndStr,&$Loc,&$PosEnd,$WithPos=false) { 4622 | 4623 | $BegLen = strlen($BegStr); 4624 | $BegChr = $BegStr[0]; 4625 | $BegIs1 = ($BegLen===1); 4626 | 4627 | $DelimIdx = false; 4628 | $DelimCnt = 0; 4629 | $DelimChr = ''; 4630 | $BegCnt = 0; 4631 | $SubName = $Loc->SubOk; 4632 | 4633 | $Status = 0; // 0: name not started, 1: name started, 2: name ended, 3: equal found, 4: value started 4634 | $PosName = 0; 4635 | $PosNend = 0; 4636 | $PosVal = 0; 4637 | 4638 | // Variables for checking the loop 4639 | $PosEnd = strpos($Txt,$EndStr,$Pos); 4640 | if ($PosEnd===false) return; 4641 | $Continue = ($Pos<$PosEnd); 4642 | 4643 | while ($Continue) { 4644 | 4645 | $Chr = $Txt[$Pos]; 4646 | 4647 | if ($DelimIdx) { // Reading in the string 4648 | 4649 | if ($Chr===$DelimChr) { // Quote found 4650 | if ($Chr===$Txt[$Pos+1]) { // Double Quote => the string continue with un-double the quote 4651 | $Pos++; 4652 | } else { // Simple Quote => end of string 4653 | $DelimIdx = false; 4654 | } 4655 | } 4656 | 4657 | } else { // Reading outside the string 4658 | 4659 | if ($BegCnt===0) { 4660 | 4661 | // Analyzing parameters 4662 | $CheckChr = false; 4663 | if (($Chr===' ') || ($Chr==="\r") || ($Chr==="\n")) { 4664 | if ($Status===1) { 4665 | if ($SubName && ($XmlTag===false)) { 4666 | // Accept spaces in TBS subname. 4667 | } else { 4668 | $Status = 2; 4669 | $PosNend = $Pos; 4670 | } 4671 | } elseif ($XmlTag && ($Status===4)) { 4672 | self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos); 4673 | $Status = 0; 4674 | } 4675 | } elseif (($XmlTag===false) && ($Chr===';')) { 4676 | self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos); 4677 | $Status = 0; 4678 | } elseif ($Status===4) { 4679 | $CheckChr = true; 4680 | } elseif ($Status===3) { 4681 | $Status = 4; 4682 | $DelimCnt = 0; 4683 | $PosVal = $Pos; 4684 | $CheckChr = true; 4685 | } elseif ($Status===2) { 4686 | if ($Chr==='=') { 4687 | $Status = 3; 4688 | } elseif ($XmlTag) { 4689 | self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos); 4690 | $Status = 1; 4691 | $PosName = $Pos; 4692 | $CheckChr = true; 4693 | } else { 4694 | $Status = 4; 4695 | $DelimCnt = 0; 4696 | $PosVal = $Pos; 4697 | $CheckChr = true; 4698 | } 4699 | } elseif ($Status===1) { 4700 | if ($Chr==='=') { 4701 | $Status = 3; 4702 | $PosNend = $Pos; 4703 | } else { 4704 | $CheckChr = true; 4705 | } 4706 | } else { 4707 | $Status = 1; 4708 | $PosName = $Pos; 4709 | $CheckChr = true; 4710 | } 4711 | 4712 | if ($CheckChr) { 4713 | $DelimIdx = strpos($DelimChrs,$Chr); 4714 | if ($DelimIdx===false) { 4715 | if ($Chr===$BegChr) { 4716 | if ($BegIs1) { 4717 | $BegCnt++; 4718 | } elseif(substr($Txt,$Pos,$BegLen)===$BegStr) { 4719 | $BegCnt++; 4720 | } 4721 | } 4722 | } else { 4723 | $DelimChr = $DelimChrs[$DelimIdx]; 4724 | $DelimCnt++; 4725 | $DelimIdx = true; 4726 | } 4727 | } 4728 | 4729 | } else { 4730 | if ($Chr===$BegChr) { 4731 | if ($BegIs1) { 4732 | $BegCnt++; 4733 | } elseif(substr($Txt,$Pos,$BegLen)===$BegStr) { 4734 | $BegCnt++; 4735 | } 4736 | } 4737 | } 4738 | 4739 | } 4740 | 4741 | // Next char 4742 | $Pos++; 4743 | 4744 | // We check if it's the end 4745 | if ($Pos===$PosEnd) { 4746 | if ($XmlTag) { 4747 | $Continue = false; 4748 | } elseif ($DelimIdx===false) { 4749 | if ($BegCnt>0) { 4750 | $BegCnt--; 4751 | } else { 4752 | $Continue = false; 4753 | } 4754 | } 4755 | if ($Continue) { 4756 | $PosEnd = strpos($Txt,$EndStr,$PosEnd+1); 4757 | if ($PosEnd===false) return; 4758 | } else { 4759 | if ($XmlTag && ($Txt[$Pos-1]==='/')) $Pos--; // In case last attribute is stuck to "/>" 4760 | self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos); 4761 | } 4762 | } 4763 | 4764 | } 4765 | 4766 | $PosEnd = $PosEnd + (strlen($EndStr)-1); 4767 | 4768 | } 4769 | 4770 | static function f_Loc_PrmCompute(&$Txt,&$Loc,&$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos) { 4771 | 4772 | if ($Status===0) { 4773 | $SubName = false; 4774 | } else { 4775 | if ($Status===1) { 4776 | $x = substr($Txt,$PosName,$Pos-$PosName); 4777 | } else { 4778 | $x = substr($Txt,$PosName,$PosNend-$PosName); 4779 | } 4780 | if ($XmlTag) $x = strtolower($x); 4781 | if ($SubName) { 4782 | $Loc->SubName = trim($x); 4783 | $SubName = false; 4784 | } else { 4785 | if ($Status===4) { 4786 | $v = trim(substr($Txt,$PosVal,$Pos-$PosVal)); 4787 | if ($DelimCnt===1) { // Delete quotes inside the value 4788 | if ($v[0]===$DelimChr) { 4789 | $len = strlen($v); 4790 | if ($v[$len-1]===$DelimChr) { 4791 | $v = substr($v,1,$len-2); 4792 | $v = str_replace($DelimChr.$DelimChr,$DelimChr,$v); 4793 | } 4794 | } 4795 | } 4796 | } else { 4797 | $v = true; 4798 | } 4799 | if ($x==='if') { 4800 | self::f_Loc_PrmIfThen($Loc, true, $v, true); 4801 | } elseif ($x==='then') { 4802 | self::f_Loc_PrmIfThen($Loc, false, $v, true); 4803 | } else { 4804 | $Loc->PrmLst[$x] = $v; 4805 | if ($WithPos) $Loc->PrmPos[$x] = array($PosName,$PosNend,$PosVal,$Pos,$DelimChr,$DelimCnt); 4806 | } 4807 | } 4808 | } 4809 | 4810 | } 4811 | 4812 | /** 4813 | * Add a new parameter 'if or 'then' to the locator. 4814 | * 4815 | * @param object $Loc The locator. 4816 | * @param boolean $IsIf Concerned parameter. True means 'if', false means 'then'. 4817 | * @param string $Val The value of the parameter. 4818 | * @param boolean $Ordered True means the parameter comes from the template and order must be checked. False means it comes from PHP and order is free. 4819 | * 4820 | */ 4821 | static function f_Loc_PrmIfThen(&$Loc, $IsIf, $Val, $Ordered) { 4822 | $nb_if = &$Loc->PrmIfNbr; 4823 | if ($nb_if===false) { 4824 | $nb_if = 0; 4825 | $Loc->PrmIf = array(); 4826 | $Loc->PrmIfVar = array(); 4827 | $Loc->PrmThen = array(); 4828 | $Loc->PrmThenVar = array(); 4829 | $Loc->PrmElseVar = true; 4830 | } 4831 | if ($IsIf) { 4832 | $nb_if++; 4833 | $Loc->PrmIf[$nb_if] = $Val; 4834 | $Loc->PrmIfVar[$nb_if] = true; 4835 | } else { 4836 | if ($Ordered) { 4837 | $nb_then = $nb_if; 4838 | if ($nb_then===false) $nb_then = 1; // Only the first 'then' can be placed before its 'if'. This is for compatibility. 4839 | } else { 4840 | $nb_then = count($Loc->PrmThen) + 1; 4841 | } 4842 | $Loc->PrmThen[$nb_then] = $Val; 4843 | $Loc->PrmThenVar[$nb_then] = true; 4844 | } 4845 | } 4846 | 4847 | /* 4848 | This function enables to enlarge the pos limits of the Locator. 4849 | If the search result is not correct, $PosBeg must not change its value, and $PosEnd must be False. 4850 | This is because of the calling function. 4851 | */ 4852 | static function f_Loc_EnlargeToStr(&$Txt,&$Loc,$StrBeg,$StrEnd) { 4853 | 4854 | // Search for the begining string 4855 | $Pos = $Loc->PosBeg; 4856 | $Ok = false; 4857 | do { 4858 | $Pos = strrpos(substr($Txt,0,$Pos),$StrBeg[0]); 4859 | if ($Pos!==false) { 4860 | if (substr($Txt,$Pos,strlen($StrBeg))===$StrBeg) $Ok = true; 4861 | } 4862 | } while ( (!$Ok) && ($Pos!==false) ); 4863 | 4864 | if ($Ok) { 4865 | $PosEnd = strpos($Txt,$StrEnd,$Loc->PosEnd + 1); 4866 | if ($PosEnd===false) { 4867 | $Ok = false; 4868 | } else { 4869 | $Loc->PosBeg = $Pos; 4870 | $Loc->PosEnd = $PosEnd + strlen($StrEnd) - 1; 4871 | } 4872 | } 4873 | 4874 | return $Ok; 4875 | 4876 | } 4877 | 4878 | static function f_Loc_EnlargeToTag(&$Txt,&$Loc,$TagStr,$RetInnerSrc) { 4879 | //Modify $Loc, return false if tags not found, returns the inner source of tag if $RetInnerSrc=true 4880 | 4881 | $AliasLst = &$GLOBALS['_TBS_BlockAlias']; 4882 | 4883 | // Analyze string 4884 | $Ref = 0; 4885 | $LevelStop = 0; 4886 | $i = 0; 4887 | $TagFct = array(); 4888 | $TagLst = array(); 4889 | $TagBnd = array(); 4890 | while ($TagStr!=='') { 4891 | // get next tag 4892 | $p = strpos($TagStr, '+'); 4893 | if ($p===false) { 4894 | $t = $TagStr; 4895 | $TagStr = ''; 4896 | } else { 4897 | $t = substr($TagStr,0,$p); 4898 | $TagStr = substr($TagStr,$p+1); 4899 | } 4900 | // Check parentheses, relative position and single tag 4901 | do { 4902 | $t = trim($t); 4903 | $e = strlen($t) - 1; // pos of last char 4904 | if (($e>1) && ($t[0]==='(') && ($t[$e]===')')) { 4905 | if ($Ref===0) $Ref = $i; 4906 | if ($Ref===$i) $LevelStop++; 4907 | $t = substr($t,1,$e-1); 4908 | } else { 4909 | if (($e>=0) && ($t[$e]==='/')) $t = substr($t,0,$e); // for compatibilty 4910 | $e = false; 4911 | } 4912 | } while ($e!==false); 4913 | // Check for multiples 4914 | $p = strpos($t, '*'); 4915 | if ($p!==false) { 4916 | $n = intval(substr($t, 0, $p)); 4917 | $t = substr($t, $p + 1); 4918 | $n = max($n ,1); // prevent for error: minimum valu is 1 4919 | $TagStr = str_repeat($t . '+', $n-1) . $TagStr; 4920 | } 4921 | // Reference 4922 | if (($t==='.') && ($Ref===0)) $Ref = $i; 4923 | // Take of the (!) prefix 4924 | $b = ''; 4925 | if (($t!=='') && ($t[0]==='!')) { 4926 | $t = substr($t, 1); 4927 | $b = '!'; 4928 | } 4929 | // Block alias 4930 | $a = false; 4931 | if (isset($AliasLst[$t])) { 4932 | $a = $AliasLst[$t]; // a string or a function 4933 | if (is_string($a)) { 4934 | if ($i>999) return false; // prevent from circular alias 4935 | $TagStr = $b . $a . (($TagStr==='') ? '' : '+') . $TagStr; 4936 | $t = false; 4937 | } 4938 | } 4939 | if ($t!==false) { 4940 | $TagLst[$i] = $t; // with prefix ! if specified 4941 | $TagFct[$i] = $a; 4942 | $TagBnd[$i] = ($b===''); 4943 | $i++; 4944 | } 4945 | } 4946 | 4947 | $TagMax = $i-1; 4948 | 4949 | // Find tags that embeds the locator 4950 | if ($LevelStop===0) $LevelStop = 1; 4951 | 4952 | // First tag of reference 4953 | if ($TagLst[$Ref] === '.') { 4954 | $TagO = new clsTbsLocator; 4955 | $TagO->PosBeg = $Loc->PosBeg; 4956 | $TagO->PosEnd = $Loc->PosEnd; 4957 | $PosBeg = $Loc->PosBeg; 4958 | $PosEnd = $Loc->PosEnd; 4959 | } else { 4960 | $TagO = self::f_Loc_Enlarge_Find($Txt,$TagLst[$Ref],$TagFct[$Ref],$Loc->PosBeg-1,false,$LevelStop); 4961 | if ($TagO===false) return false; 4962 | $PosBeg = $TagO->PosBeg; 4963 | $LevelStop += -$TagO->RightLevel; // RightLevel=1 only if the tag is single and embeds $Loc, otherwise it is 0 4964 | if ($LevelStop>0) { 4965 | $TagC = self::f_Loc_Enlarge_Find($Txt,$TagLst[$Ref],$TagFct[$Ref],$Loc->PosEnd+1,true,-$LevelStop); 4966 | if ($TagC==false) return false; 4967 | $PosEnd = $TagC->PosEnd; 4968 | $InnerLim = $TagC->PosBeg; 4969 | if ((!$TagBnd[$Ref]) && ($TagMax==0)) { 4970 | $PosBeg = $TagO->PosEnd + 1; 4971 | $PosEnd = $TagC->PosBeg - 1; 4972 | } 4973 | } else { 4974 | $PosEnd = $TagO->PosEnd; 4975 | $InnerLim = $PosEnd + 1; 4976 | } 4977 | } 4978 | 4979 | $RetVal = true; 4980 | if ($RetInnerSrc) { 4981 | $RetVal = ''; 4982 | if ($Loc->PosBeg>$TagO->PosEnd) $RetVal .= substr($Txt,$TagO->PosEnd+1,min($Loc->PosBeg,$InnerLim)-$TagO->PosEnd-1); 4983 | if ($Loc->PosEnd<$InnerLim) $RetVal .= substr($Txt,max($Loc->PosEnd,$TagO->PosEnd)+1,$InnerLim-max($Loc->PosEnd,$TagO->PosEnd)-1); 4984 | } 4985 | 4986 | // Other tags forward 4987 | $TagC = true; 4988 | for ($i=$Ref+1;$i<=$TagMax;$i++) { 4989 | $x = $TagLst[$i]; 4990 | if (($x!=='') && ($TagC!==false)) { 4991 | $level = ($TagBnd[$i]) ? 0 : 1; 4992 | $TagC = self::f_Loc_Enlarge_Find($Txt,$x,$TagFct[$i],$PosEnd+1,true,$level); 4993 | if ($TagC!==false) { 4994 | $PosEnd = ($TagBnd[$i]) ? $TagC->PosEnd : $TagC->PosBeg -1 ; 4995 | } 4996 | } 4997 | } 4998 | 4999 | // Other tags backward 5000 | $TagO = true; 5001 | for ($i=$Ref-1;$i>=0;$i--) { 5002 | $x = $TagLst[$i]; 5003 | if (($x!=='') && ($TagO!==false)) { 5004 | $level = ($TagBnd[$i]) ? 0 : -1; 5005 | $TagO = self::f_Loc_Enlarge_Find($Txt,$x,$TagFct[$i],$PosBeg-1,false,$level); 5006 | if ($TagO!==false) { 5007 | $PosBeg = ($TagBnd[$i]) ? $TagO->PosBeg : $TagO->PosEnd + 1; 5008 | } 5009 | } 5010 | } 5011 | 5012 | $Loc->PosBeg = $PosBeg; 5013 | $Loc->PosEnd = $PosEnd; 5014 | return $RetVal; 5015 | 5016 | } 5017 | 5018 | static function f_Loc_Enlarge_Find($Txt, $Tag, $Fct, $Pos, $Forward, $LevelStop) { 5019 | if ($Fct===false) { 5020 | return self::f_Xml_FindTag($Txt,$Tag,(!$Forward),$Pos,$Forward,$LevelStop,false); 5021 | } else { 5022 | $p = call_user_func_array($Fct,array($Tag,$Txt,$Pos,$Forward,$LevelStop)); 5023 | if ($p===false) { 5024 | return false; 5025 | } else { 5026 | return (object) array('PosBeg'=>$p, 'PosEnd'=>$p, 'RightLevel'=> 0); // it's a trick 5027 | } 5028 | } 5029 | } 5030 | 5031 | /** 5032 | * Return the expected value for a boolean attribute 5033 | */ 5034 | static function f_Loc_AttBoolean($CurrVal, $AttTrue, $AttName) { 5035 | 5036 | if ($AttTrue===true) { 5037 | if (self::meth_Misc_ToStr($CurrVal)==='') { 5038 | return ''; 5039 | } else { 5040 | return $AttName; 5041 | } 5042 | } elseif (self::meth_Misc_ToStr($CurrVal)===$AttTrue) { 5043 | return $AttName; 5044 | } else { 5045 | return ''; 5046 | } 5047 | 5048 | } 5049 | 5050 | /** 5051 | * Affects the positions of a list of locators regarding to a specific moving locator. 5052 | */ 5053 | static function f_Loc_Moving(&$LocM, &$LocLst) { 5054 | foreach ($LocLst as &$Loc) { 5055 | if ($Loc !== $LocM) { 5056 | if ($Loc->PosBeg >= $LocM->InsPos) { 5057 | $Loc->PosBeg += $LocM->InsLen; 5058 | $Loc->PosEnd += $LocM->InsLen; 5059 | } 5060 | if ($Loc->PosBeg > $LocM->DelPos) { 5061 | $Loc->PosBeg -= $LocM->DelLen; 5062 | $Loc->PosEnd -= $LocM->DelLen; 5063 | } 5064 | } 5065 | } 5066 | return true; 5067 | } 5068 | 5069 | /** 5070 | * Sort the locators in the list. Apply the bubble algorithm. 5071 | * Deleted locators maked with DelMe. 5072 | * @param array $LocLst An array of locators. 5073 | * @param boolean $DelEmbd True to deleted locators that embded other ones. 5074 | * @param boolean $iFirst Index of the first item. 5075 | * @return integer Return the number of met embedding locators. 5076 | */ 5077 | static function f_Loc_Sort(&$LocLst, $DelEmbd, $iFirst = 0) { 5078 | 5079 | $iLast = $iFirst + count($LocLst) - 1; 5080 | $embd = 0; 5081 | 5082 | for ($i = $iLast ; $i>=$iFirst ; $i--) { 5083 | $Loc = $LocLst[$i]; 5084 | $d = (isset($Loc->DelMe) && $Loc->DelMe); 5085 | $b = $Loc->PosBeg; 5086 | $e = $Loc->PosEnd; 5087 | for ($j=$i+1; $j<=$iLast ; $j++) { 5088 | // If DelMe, then the loc will be put at the end and deleted 5089 | $jb = $LocLst[$j]->PosBeg; 5090 | if ($d || ($b > $jb)) { 5091 | $LocLst[$j-1] = $LocLst[$j]; 5092 | $LocLst[$j] = $Loc; 5093 | } elseif ($e > $jb) { 5094 | $embd++; 5095 | if ($DelEmbd) { 5096 | $d = true; 5097 | $j--; // replay the current position 5098 | } else { 5099 | $j = $iLast; // quit the loop 5100 | } 5101 | } else { 5102 | $j = $iLast; // quit the loop 5103 | } 5104 | } 5105 | if ($d) { 5106 | unset($LocLst[$iLast]); 5107 | $iLast--; 5108 | } 5109 | } 5110 | 5111 | return $embd; 5112 | } 5113 | 5114 | /** 5115 | * Prepare all informations to move a locator according to parameter "att". 5116 | * 5117 | * @param false|true|array $MoveLocLst true to simple move the loc, or an array of loc to rearrange the list after the move. 5118 | * Note: rearrange doest not work with PHP4. 5119 | */ 5120 | static function f_Xml_AttFind(&$Txt,&$Loc,$MoveLocLst=false,$AttDelim=false,$LocLst=false) { 5121 | // att=div#class ; att=((div))#class ; att=+((div))#class 5122 | 5123 | $Att = $Loc->PrmLst['att']; 5124 | unset($Loc->PrmLst['att']); // prevent from processing the field twice 5125 | $Loc->PrmLst['att;'] = $Att; // for debug 5126 | 5127 | $p = strrpos($Att,'#'); 5128 | if ($p===false) { 5129 | $TagLst = ''; 5130 | } else { 5131 | $TagLst = substr($Att,0,$p); 5132 | $Att = substr($Att,$p+1); 5133 | } 5134 | 5135 | $Forward = (substr($TagLst,0,1)==='+'); 5136 | if ($Forward) $TagLst = substr($TagLst,1); 5137 | $TagLst = explode('+',$TagLst); 5138 | 5139 | $iMax = count($TagLst)-1; 5140 | $WithPrm = false; 5141 | $LocO = &$Loc; 5142 | foreach ($TagLst as $i=>$Tag) { 5143 | $LevelStop = false; 5144 | while ((strlen($Tag)>1) && (substr($Tag,0,1)==='(') && (substr($Tag,-1,1)===')')) { 5145 | if ($LevelStop===false) $LevelStop = 0; 5146 | $LevelStop++; 5147 | $Tag = trim(substr($Tag,1,strlen($Tag)-2)); 5148 | } 5149 | if ($i==$iMax) $WithPrm = true; 5150 | $Pos = ($Forward) ? $LocO->PosEnd+1 : $LocO->PosBeg-1; 5151 | unset($LocO); 5152 | $LocO = self::f_Xml_FindTag($Txt,$Tag,true,$Pos,$Forward,$LevelStop,$WithPrm,$WithPrm); 5153 | if ($LocO===false) return false; 5154 | } 5155 | 5156 | $Loc->AttForward = $Forward; 5157 | $Loc->AttTagBeg = $LocO->PosBeg; 5158 | $Loc->AttTagEnd = $LocO->PosEnd; 5159 | $Loc->AttDelimChr = false; 5160 | 5161 | if ($Att==='.') { 5162 | // this indicates that the TBS field is supposed to be inside an attribute's value 5163 | foreach ($LocO->PrmPos as $a=>$p ) { 5164 | if ( ($p[0]<$Loc->PosBeg) && ($Loc->PosEnd<$p[3]) ) $Att = $a; 5165 | } 5166 | if ($Att==='.') return false; 5167 | } 5168 | $Loc->AttName = $Att; 5169 | 5170 | $AttLC = strtolower($Att); 5171 | if (isset($LocO->PrmLst[$AttLC])) { 5172 | // The attribute is existing 5173 | $p = $LocO->PrmPos[$AttLC]; 5174 | $Loc->AttBeg = $p[0]; 5175 | $p[3]--; while ($Txt[$p[3]]===' ') $p[3]--; // external end of the attribute, may has an extra spaces 5176 | $Loc->AttEnd = $p[3]; 5177 | $Loc->AttDelimCnt = $p[5]; 5178 | $Loc->AttDelimChr = $p[4]; 5179 | if (($p[1]>$p[0]) && ($p[2]>$p[1])) { 5180 | //$Loc->AttNameEnd = $p[1]; 5181 | $Loc->AttValBeg = $p[2]; 5182 | } else { // attribute without value 5183 | //$Loc->AttNameEnd = $p[3]; 5184 | $Loc->AttValBeg = false; 5185 | } 5186 | } else { 5187 | // The attribute is not yet existing 5188 | $Loc->AttDelimCnt = 0; 5189 | $Loc->AttBeg = false; 5190 | } 5191 | 5192 | // Search for a delimitor 5193 | if (($Loc->AttDelimCnt==0) && (isset($LocO->PrmPos))) { 5194 | foreach ($LocO->PrmPos as $p) { 5195 | if ($p[5]>0) $Loc->AttDelimChr = $p[4]; 5196 | } 5197 | } 5198 | 5199 | if ($MoveLocLst) return self::f_Xml_AttMove($Txt,$Loc,$AttDelim,$MoveLocLst); 5200 | 5201 | return true; 5202 | 5203 | } 5204 | 5205 | /** 5206 | * Move a locator in the source from its original location to the attribute location. 5207 | * The new locator string is only '[]', no need to copy the full source since all parameters are saved in $Loc.* 5208 | * 5209 | * @param false|true|array $MoveLocLst If the function is called from the caching process, then this value is an array. 5210 | */ 5211 | static function f_Xml_AttMove(&$Txt, &$Loc, $AttDelim, &$MoveLocLst) { 5212 | 5213 | if ($AttDelim===false) $AttDelim = $Loc->AttDelimChr; 5214 | if ($AttDelim===false) $AttDelim = '"'; 5215 | 5216 | $DelPos = $Loc->PosBeg; 5217 | $DelLen = $Loc->PosEnd - $Loc->PosBeg + 1; 5218 | $Txt = substr_replace($Txt,'',$DelPos,$DelLen); // delete the current locator 5219 | if ($Loc->AttForward) { 5220 | $Loc->AttTagBeg += -$DelLen; 5221 | $Loc->AttTagEnd += -$DelLen; 5222 | } elseif ($Loc->PosBeg<$Loc->AttTagEnd) { 5223 | $Loc->AttTagEnd += -$DelLen; 5224 | } 5225 | 5226 | $InsPos = false; 5227 | if ($Loc->AttBeg===false) { 5228 | $InsPos = $Loc->AttTagEnd; 5229 | if ($Txt[$InsPos-1]==='/') $InsPos--; 5230 | if ($Txt[$InsPos-1]===' ') $InsPos--; 5231 | $Ins1 = ' '.$Loc->AttName.'='.$AttDelim; 5232 | $Ins2 = $AttDelim; 5233 | $Loc->AttBeg = $InsPos + 1; 5234 | $Loc->AttValBeg = $InsPos + strlen($Ins1) - 1; 5235 | } else { 5236 | if ($Loc->PosEnd<$Loc->AttBeg) $Loc->AttBeg += -$DelLen; 5237 | if ($Loc->PosEnd<$Loc->AttEnd) $Loc->AttEnd += -$DelLen; 5238 | if ($Loc->AttValBeg===false) { 5239 | $InsPos = $Loc->AttEnd+1; 5240 | $Ins1 = '='.$AttDelim; 5241 | $Ins2 = $AttDelim; 5242 | $Loc->AttValBeg = $InsPos+1; 5243 | } elseif (isset($Loc->PrmLst['attadd'])) { 5244 | $InsPos = $Loc->AttEnd; 5245 | $Ins1 = ' '; 5246 | $Ins2 = ''; 5247 | } else { 5248 | // value already existing 5249 | if ($Loc->PosEnd<$Loc->AttValBeg) $Loc->AttValBeg += -$DelLen; 5250 | $PosBeg = $Loc->AttValBeg; 5251 | $PosEnd = $Loc->AttEnd; 5252 | if ($Loc->AttDelimCnt>0) {$PosBeg++; $PosEnd--;} 5253 | } 5254 | } 5255 | 5256 | if ($InsPos===false) { 5257 | $InsLen = 0; 5258 | } else { 5259 | $InsTxt = $Ins1.'[]'.$Ins2; 5260 | $InsLen = strlen($InsTxt); 5261 | $PosBeg = $InsPos + strlen($Ins1); 5262 | $PosEnd = $PosBeg + 1; 5263 | $Txt = substr_replace($Txt,$InsTxt,$InsPos,0); 5264 | $Loc->AttEnd = $InsPos + $InsLen - 1; 5265 | $Loc->AttTagEnd += $InsLen; 5266 | } 5267 | 5268 | $Loc->PosBeg = $PosBeg; 5269 | $Loc->PosEnd = $PosEnd; 5270 | 5271 | // for CacheField 5272 | if (is_array($MoveLocLst)) { 5273 | $Loc->InsPos = $InsPos; 5274 | $Loc->InsLen = $InsLen; 5275 | $Loc->DelPos = $DelPos; 5276 | if ($Loc->InsPos < $Loc->DelPos) $Loc->DelPos += $InsLen; 5277 | $Loc->DelLen = $DelLen; 5278 | self::f_Loc_Moving($Loc, $MoveLocLst); 5279 | } 5280 | 5281 | return true; 5282 | 5283 | } 5284 | 5285 | static function f_Xml_Max(&$Txt,&$Nbr,$MaxEnd) { 5286 | // Limit the number of HTML chars 5287 | 5288 | $pMax = strlen($Txt)-1; 5289 | $p=0; 5290 | $n=0; 5291 | $in = false; 5292 | $ok = true; 5293 | 5294 | while ($ok) { 5295 | if ($in) { 5296 | if ($Txt[$p]===';') { 5297 | $in = false; 5298 | $n++; 5299 | } 5300 | } else { 5301 | if ($Txt[$p]==='&') { 5302 | $in = true; 5303 | } else { 5304 | $n++; 5305 | } 5306 | } 5307 | if (($n>=$Nbr) || ($p>=$pMax)) { 5308 | $ok = false; 5309 | } else { 5310 | $p++; 5311 | } 5312 | } 5313 | 5314 | if (($n>=$Nbr) && ($p<$pMax)) $Txt = substr($Txt,0,$p).$MaxEnd; 5315 | 5316 | } 5317 | 5318 | static function f_Xml_GetPart(&$Txt, $TagLst, $AllIfNothing=false) { 5319 | // Returns parts of the XML/HTML content, default is BODY. 5320 | 5321 | if (($TagLst===true) || ($TagLst==='')) $TagLst = 'body'; 5322 | 5323 | $x = ''; 5324 | $nothing = true; 5325 | $TagLst = explode('+',$TagLst); 5326 | 5327 | // Build a clean list of tags 5328 | foreach ($TagLst as $i=>$t) { 5329 | if ((substr($t,0,1)=='(') && (substr($t,-1,1)==')')) { 5330 | $t = substr($t,1,strlen($t)-2); 5331 | $Keep = true; 5332 | } else { 5333 | $Keep = false; 5334 | } 5335 | $TagLst[$i] = array('t'=>$t, 'k'=>$Keep, 'b'=>-1, 'e'=>-1, 's'=>false); 5336 | } 5337 | 5338 | $PosOut = strlen($Txt); 5339 | $Pos = 0; 5340 | 5341 | // Optimized search for all tag types 5342 | do { 5343 | 5344 | // Search next positions of each tag type 5345 | $TagMin = false; // idx of the tag at first position 5346 | $PosMin = $PosOut; // pos of the tag at first position 5347 | foreach ($TagLst as $i=>$Tag) { 5348 | if ($Tag['b']<$Pos) { 5349 | $Loc = self::f_Xml_FindTag($Txt,$Tag['t'],true,$Pos,true,false,false); 5350 | if ($Loc===false) { 5351 | $Tag['b'] = $PosOut; // tag not found, no more search on this tag 5352 | } else { 5353 | $Tag['b'] = $Loc->PosBeg; 5354 | $Tag['e'] = $Loc->PosEnd; 5355 | $Tag['s'] = (substr($Txt,$Loc->PosEnd-1,1)==='/'); // true if it's a single tag 5356 | } 5357 | $TagLst[$i] = $Tag; // update 5358 | } 5359 | if ($Tag['b']<$PosMin) { 5360 | $TagMin = $i; 5361 | $PosMin = $Tag['b']; 5362 | } 5363 | } 5364 | 5365 | // Add the part of tag types 5366 | if ($TagMin!==false) { 5367 | $Tag = &$TagLst[$TagMin]; 5368 | $Pos = $Tag['e']+1; 5369 | if ($Tag['s']) { 5370 | // single tag 5371 | if ($Tag['k']) $x .= substr($Txt,$Tag['b'] ,$Tag['e'] - $Tag['b'] + 1); 5372 | } else { 5373 | // search the closing tag 5374 | $Loc = self::f_Xml_FindTag($Txt,$Tag['t'],false,$Pos,true,false,false); 5375 | if ($Loc===false) { 5376 | $Tag['b'] = $PosOut; // closing tag not found, no more search on this tag 5377 | } else { 5378 | $nothing = false; 5379 | if ($Tag['k']) { 5380 | $x .= substr($Txt,$Tag['b'] ,$Loc->PosEnd - $Tag['b'] + 1); 5381 | } else { 5382 | $x .= substr($Txt,$Tag['e']+1,$Loc->PosBeg - $Tag['e'] - 1); 5383 | } 5384 | $Pos = $Loc->PosEnd + 1; 5385 | } 5386 | } 5387 | } 5388 | 5389 | } while ($TagMin!==false); 5390 | 5391 | if ($AllIfNothing && $nothing) return $Txt; 5392 | return $x; 5393 | 5394 | } 5395 | 5396 | /** 5397 | * Find the start position of an XML tag. Used by OpenTBS. 5398 | * $Case=false can be useful for HTML. 5399 | * $Tag='' should work and found the start of the first opening tag of any name. 5400 | * $Tag='/' should work and found the start of the first closing tag of any name. 5401 | * Encapsulation levels are not featured yet. 5402 | */ 5403 | static function f_Xml_FindTagStart(&$Txt,$Tag,$Opening,$PosBeg,$Forward,$Case=true) { 5404 | 5405 | if ($Txt==='') return false; 5406 | 5407 | $x = '<'.(($Opening) ? '' : '/').$Tag; 5408 | $xl = strlen($x); 5409 | 5410 | $p = $PosBeg - (($Forward) ? 1 : -1); 5411 | 5412 | if ($Case) { 5413 | do { 5414 | if ($Forward) $p = strpos($Txt,$x,$p+1); else $p = strrpos(substr($Txt,0,$p+1),$x); 5415 | if ($p===false) return false; 5416 | $z = substr($Txt,$p+$xl,1); 5417 | } while ( ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') && ($Tag!=='/') && ($Tag!=='') ); 5418 | } else { 5419 | do { 5420 | if ($Forward) $p = stripos($Txt,$x,$p+1); else $p = strripos(substr($Txt,0,$p+1),$x); 5421 | if ($p===false) return false; 5422 | $z = substr($Txt,$p+$xl,1); 5423 | } while ( ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') && ($Tag!=='/') && ($Tag!=='') ); 5424 | } 5425 | 5426 | return $p; 5427 | 5428 | } 5429 | 5430 | /** 5431 | * This function is a smart solution to find an XML tag. 5432 | * It allows to ignore full opening/closing couple of tags that could be inserted before the searched tag. 5433 | * It allows also to pass a number of encapsulations. 5434 | * To ignore encapsulation and opengin/closing just set $LevelStop=false. 5435 | * $Opening is used only when $LevelStop=false. 5436 | */ 5437 | static function f_Xml_FindTag(&$Txt,$Tag,$Opening,$PosBeg,$Forward,$LevelStop,$WithPrm,$WithPos=false) { 5438 | 5439 | if ($Tag==='_') { // New line 5440 | $p = self::f_Xml_FindNewLine($Txt,$PosBeg,$Forward,($LevelStop!==0)); 5441 | $Loc = new clsTbsLocator; 5442 | $Loc->PosBeg = ($Forward) ? $PosBeg : $p; 5443 | $Loc->PosEnd = ($Forward) ? $p : $PosBeg; 5444 | $Loc->RightLevel = 0; 5445 | return $Loc; 5446 | } 5447 | 5448 | $Pos = $PosBeg + (($Forward) ? -1 : +1); 5449 | $TagIsOpening = false; 5450 | $TagClosing = '/'.$Tag; 5451 | $LevelNum = 0; 5452 | $TagOk = false; 5453 | $PosEnd = false; 5454 | $TagL = strlen($Tag); 5455 | $TagClosingL = strlen($TagClosing); 5456 | $RightLevel = 0; 5457 | 5458 | do { 5459 | 5460 | // Look for the next tag def 5461 | if ($Forward) { 5462 | $Pos = strpos($Txt,'<',$Pos+1); 5463 | } else { 5464 | if ($Pos<=0) { 5465 | $Pos = false; 5466 | } else { 5467 | $Pos = strrpos(substr($Txt,0,$Pos - 1),'<'); // strrpos() syntax compatible with PHP 4 5468 | } 5469 | } 5470 | 5471 | if ($Pos!==false) { 5472 | 5473 | // Check the name of the tag 5474 | if (strcasecmp(substr($Txt,$Pos+1,$TagL),$Tag)==0) { 5475 | // It's an opening tag 5476 | $PosX = $Pos + 1 + $TagL; // The next char 5477 | $TagOk = true; 5478 | $TagIsOpening = true; 5479 | } elseif (strcasecmp(substr($Txt,$Pos+1,$TagClosingL),$TagClosing)==0) { 5480 | // It's a closing tag 5481 | $PosX = $Pos + 1 + $TagClosingL; // The next char 5482 | $TagOk = true; 5483 | $TagIsOpening = false; 5484 | } 5485 | 5486 | if ($TagOk) { 5487 | // Check the next char 5488 | $x = $Txt[$PosX]; 5489 | if (($x===' ') || ($x==="\r") || ($x==="\n") || ($x==='>') || ($x==='/') || ($Tag==='/') || ($Tag==='')) { 5490 | // Check the encapsulation count 5491 | if ($LevelStop===false) { // No encapsulation check 5492 | if ($TagIsOpening!==$Opening) $TagOk = false; 5493 | } else { // Count the number of level 5494 | if ($TagIsOpening) { 5495 | $PosEnd = strpos($Txt,'>',$PosX); 5496 | if ($PosEnd!==false) { 5497 | if ($Txt[$PosEnd-1]==='/') { 5498 | if (($Pos<$PosBeg) && ($PosEnd>$PosBeg)) {$RightLevel=1; $LevelNum++;} 5499 | } else { 5500 | $LevelNum++; 5501 | } 5502 | } 5503 | } else { 5504 | $LevelNum--; 5505 | } 5506 | // Check if it's the expected level 5507 | if ($LevelNum!=$LevelStop) { 5508 | $TagOk = false; 5509 | $PosEnd = false; 5510 | } 5511 | } 5512 | } else { 5513 | $TagOk = false; 5514 | } 5515 | } //--> if ($TagOk) 5516 | 5517 | } 5518 | } while (($Pos!==false) && ($TagOk===false)); 5519 | 5520 | // Search for the end of the tag 5521 | if ($TagOk) { 5522 | $Loc = new clsTbsLocator; 5523 | if ($WithPrm) { 5524 | self::f_Loc_PrmRead($Txt,$PosX,true,'\'"','<','>',$Loc,$PosEnd,$WithPos); 5525 | } elseif ($PosEnd===false) { 5526 | $PosEnd = strpos($Txt,'>',$PosX); 5527 | if ($PosEnd===false) { 5528 | $TagOk = false; 5529 | } 5530 | } 5531 | } 5532 | 5533 | // Result 5534 | if ($TagOk) { 5535 | $Loc->PosBeg = $Pos; 5536 | $Loc->PosEnd = $PosEnd; 5537 | $Loc->RightLevel = $RightLevel; 5538 | return $Loc; 5539 | } else { 5540 | return false; 5541 | } 5542 | 5543 | } 5544 | 5545 | static function f_Xml_FindNewLine(&$Txt,$PosBeg,$Forward,$IsRef) { 5546 | 5547 | $p = $PosBeg; 5548 | if ($Forward) { 5549 | $Inc = 1; 5550 | $Inf = &$p; 5551 | $Sup = strlen($Txt)-1; 5552 | } else { 5553 | $Inc = -1; 5554 | $Inf = 0; 5555 | $Sup = &$p; 5556 | } 5557 | 5558 | do { 5559 | if ($Inf>$Sup) return max($Sup,0); 5560 | $x = $Txt[$p]; 5561 | if (($x==="\r") || ($x==="\n")) { 5562 | $x2 = ($x==="\n") ? "\r" : "\n"; 5563 | $p0 = $p; 5564 | if (($Inf<$Sup) && ($Txt[$p+$Inc]===$x2)) $p += $Inc; // Newline char can have two chars. 5565 | if ($Forward) return $p; // Forward => return pos including newline char. 5566 | if ($IsRef || ($p0!=$PosBeg)) return $p0+1; // Backwars => return pos without newline char. Ignore newline if it is the very first char of the search. 5567 | } 5568 | $p += $Inc; 5569 | } while (true); 5570 | 5571 | } 5572 | 5573 | static function f_Xml_GetNextEntityName($Txt, $Pos, &$tag, &$PosBeg, &$p) { 5574 | /* 5575 | $tag : tag name 5576 | $PosBeg : position of the tag 5577 | $p : position where the read has stop 5578 | $z : first char after the name 5579 | */ 5580 | 5581 | $tag = ''; 5582 | $PosBeg = strpos($Txt, '<', $Pos); 5583 | 5584 | if ($PosBeg===false) return false; 5585 | 5586 | // Read the name of the tag 5587 | $go = true; 5588 | $p = $PosBeg; 5589 | while ($go) { 5590 | $p++; 5591 | $z = $Txt[$p]; 5592 | if ($go = ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') ) { 5593 | $tag .= $z; 5594 | } 5595 | } 5596 | 5597 | return true; 5598 | 5599 | } 5600 | 5601 | } 5602 | --------------------------------------------------------------------------------