4
* TinyButStrong - Template Engine for Pro and Beginners
6
* @version 3.15.1 for PHP 5, 7, 8
8
* @link http://www.tinybutstrong.com Web site
9
* @author http://www.tinybutstrong.com/onlyyou.html
10
* @license http://opensource.org/licenses/LGPL-3.0 LGPL-3.0
12
* This library is free software.
13
* You can redistribute and modify it even for commercial usage,
14
* but you must accept and respect the LPGL License version 3.
18
if (version_compare(PHP_VERSION,'5.0')<0) echo '<br><b>TinyButStrong Error</b> (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.';
21
define('TBS_NOTHING', 0);
22
define('TBS_OUTPUT', 1);
26
define('TBS_INSTALL', -1);
27
define('TBS_ISINSTALLED', -3);
29
// *********************************************
33
public $PosBeg = false;
34
public $PosEnd = false;
35
public $Enlarged = false;
36
public $FullName = false;
38
public $SubOk = false;
39
public $SubLst = array();
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;
52
// Compatibility with PHP 8.2
53
public $Prop = array(); // dynamic properties, used by OpenTBS
110
public $FieldOutside;
124
public $WhenDefaultBeforeNS;
143
public $BoundSingleNb;
148
// *********************************************
150
class clsTbsDataSource {
154
public $SrcId = false;
156
public $RecSet = false;
157
public $RecNumInit = 0; // Used by ByPage plugin
158
public $RecSaving = false;
159
public $RecSaved = false;
160
public $RecBuffer = false;
162
public $OnDataOk = false;
163
public $OnDataPrm = false;
164
public $OnDataPrmDone = array();
165
public $OnDataPi = false;
167
// Info relative to the current record :
168
public $CurrRec = false; // Used by ByPage plugin
169
public $RecKey = ''; // Used by ByPage plugin
170
public $RecNum = 0; // Used by ByPage plugin
172
public $PrevRec = null;
173
public $NextRec = null;
175
public $PrevSave = false;
176
public $NextSave = false;
178
// Compatibility with PHP 8.2
179
public $Prop = array(); // Used by ByPage plugin
189
public function DataAlert($Msg) {
190
if (is_array($this->TBS->_CurrBlock)) {
191
return $this->TBS->meth_Misc_Alert('when merging block "'.implode(',',$this->TBS->_CurrBlock).'"',$Msg);
193
return $this->TBS->meth_Misc_Alert('when merging block '.$this->TBS->_ChrOpen.$this->TBS->_CurrBlock.$this->TBS->_ChrClose,$Msg);
197
public function DataPrepare(&$SrcId,&$TBS) {
199
$this->SrcId = &$SrcId;
204
if (is_array($SrcId)) {
206
} elseif (is_resource($SrcId)) {
208
$Key = get_resource_type($SrcId);
210
case 'mysql link' : $this->Type = 6; break;
211
case 'mysql link persistent' : $this->Type = 6; break;
212
case 'mysql result' : $this->Type = 6; $this->SubType = 1; break;
213
case 'pgsql link' : $this->Type = 7; break;
214
case 'pgsql link persistent' : $this->Type = 7; break;
215
case 'pgsql result' : $this->Type = 7; $this->SubType = 1; break;
216
case 'sqlite database' : $this->Type = 8; break;
217
case 'sqlite database (persistent)' : $this->Type = 8; break;
218
case 'sqlite result' : $this->Type = 8; $this->SubType = 1; break;
224
} elseif (is_string($SrcId)) {
226
switch (strtolower($SrcId)) {
227
case 'array' : $this->Type = 0; $this->SubType = 1; break;
228
case 'clear' : $this->Type = 0; $this->SubType = 3; break;
229
case 'mysql' : $this->Type = 6; $this->SubType = 2; break;
230
case 'text' : $this->Type = 2; break;
231
case 'num' : $this->Type = 1; break;
237
} elseif ($SrcId instanceof Iterator) {
238
$this->Type = 9; $this->SubType = 1;
239
} elseif ($SrcId instanceof ArrayObject) {
240
$this->Type = 9; $this->SubType = 2;
241
} elseif ($SrcId instanceof IteratorAggregate) {
242
$this->Type = 9; $this->SubType = 3;
243
} elseif ($SrcId instanceof MySQLi) {
245
} elseif ($SrcId instanceof PDO) {
247
} elseif ($SrcId instanceof Zend_Db_Adapter_Abstract) {
249
} elseif ($SrcId instanceof SQLite3) {
250
$this->Type = 13; $this->SubType = 1;
251
} elseif ($SrcId instanceof SQLite3Stmt) {
252
$this->Type = 13; $this->SubType = 2;
253
} elseif ($SrcId instanceof SQLite3Result) {
254
$this->Type = 13; $this->SubType = 3;
255
} elseif (is_a($SrcId, 'Doctrine\DBAL\Connection')) {
257
} elseif (is_object($SrcId)) {
258
$FctInfo = get_class($SrcId);
261
$this->SrcId = &$SrcId;
262
} elseif ($SrcId===false) {
263
$this->DataAlert('the specified source is set to FALSE. Maybe your connection has failed.');
265
$this->DataAlert('unsupported variable type : \''.gettype($SrcId).'\'.');
268
if ($FctInfo!==false) {
270
if ($TBS->meth_Misc_UserFctCheck($FctInfo,$FctCat,$FctObj,$ErrMsg,false)) {
271
$this->Type = $FctInfo['type'];
272
if ($this->Type!==5) {
273
if ($this->Type===4) {
274
$this->FctPrm = array(false,0);
275
$this->SrcId = &$FctInfo['open'][0];
277
$this->FctOpen = &$FctInfo['open'];
278
$this->FctFetch = &$FctInfo['fetch'];
279
$this->FctClose = &$FctInfo['close'];
282
$this->Type = $this->DataAlert($ErrMsg);
286
return ($this->Type!==false);
290
public function DataOpen(&$Query,$QryPrms=false) {
293
unset($this->CurrRec);
294
$this->CurrRec = true;
296
if ($this->RecSaved) {
297
$this->RSIsFirst = true;
298
unset($this->RecKey); $this->RecKey = '';
299
$this->RecNum = $this->RecNumInit;
300
if ($this->OnDataOk) $this->OnDataArgs[1] = &$this->CurrRec;
304
unset($this->RecSet);
305
$this->RecSet = false;
306
$this->RecNumInit = 0;
309
// Previous and next records
310
$this->PrevRec = (object) null;
311
$this->NextRec = false;
313
if (isset($this->TBS->_piOnData)) {
314
$this->OnDataPi = true;
315
$this->OnDataPiRef = &$this->TBS->_piOnData;
316
$this->OnDataOk = true;
318
if ($this->OnDataOk) {
319
$this->OnDataArgs = array();
320
$this->OnDataArgs[0] = &$this->TBS->_CurrBlock;
321
$this->OnDataArgs[1] = &$this->CurrRec;
322
$this->OnDataArgs[2] = &$this->RecNum;
323
$this->OnDataArgs[3] = &$this->TBS;
326
switch ($this->Type) {
328
if (($this->SubType===1) && (is_string($Query))) $this->SubType = 2;
329
if ($this->SubType===0) {
330
$this->RecSet = &$this->SrcId;
331
} elseif ($this->SubType===1) {
332
if (is_array($Query)) {
333
$this->RecSet = &$Query;
335
$this->DataAlert('type \''.gettype($Query).'\' not supported for the Query Parameter going with \'array\' Source Type.');
337
} elseif ($this->SubType===2) {
338
// TBS query string for array and objects, syntax: "var[item1][item2]->item3[item4]..."
341
$x = str_replace(array(']->','][','->','['),$z,$x);
342
if (substr($x,strlen($x)-1,1)===']') $x = substr($x,0,strlen($x)-1);
343
$ItemLst = explode($z,$x);
344
$ItemNbr = count($ItemLst);
345
$Item0 = &$ItemLst[0];
347
if ($Item0[0]==='~') {
348
$Item0 = substr($Item0,1);
349
if ($this->TBS->ObjectRef!==false) {
350
$Var = &$this->TBS->ObjectRef;
353
$i = $this->DataAlert('invalid query \''.$Query.'\' because property ObjectRef is not set.');
356
if ( is_null($this->TBS->VarRef) && isset($GLOBALS[$Item0]) ) {
357
$Var = &$GLOBALS[$Item0];
359
} elseif (isset($this->TBS->VarRef[$Item0])) {
360
$Var = &$this->TBS->VarRef[$Item0];
363
$i = $this->DataAlert('invalid query \''.$Query.'\' because VarRef item \''.$Item0.'\' is not found.');
368
while (($i!==false) && ($i<$ItemNbr) && ($Empty===false)) {
370
if (is_array($Var)) {
371
if (isset($Var[$x])) {
376
} elseif (is_object($Var)) {
377
$form = $this->TBS->f_Misc_ParseFctForm($x);
379
if ( method_exists($Var, $n) || ($form['as_fct'] && method_exists($Var,'__call')) ) {
380
$f = array(&$Var,$n); unset($Var);
381
$Var = call_user_func_array($f,$form['args']);
382
} elseif (property_exists(get_class($Var),$n)) {
383
if (isset($Var->$n)) $Var = &$Var->$n;
384
} elseif (isset($Var->$n)) {
385
$Var = $Var->$n; // useful for overloaded property
390
$i = $this->DataAlert('invalid query \''.$Query.'\' because item \''.$ItemLst[$i].'\' is neither an Array nor an Object. Its type is \''.gettype($Var).'\'.');
392
if ($i!==false) $i++;
397
$this->RecSet = array();
399
$this->RecSet = &$Var;
402
} elseif ($this->SubType===3) { // Clear
403
$this->RecSet = array();
406
if ($this->RecSet!==false) {
407
$this->RecNbr = $this->RecNumInit + count($this->RecSet);
408
$this->RSIsFirst = true;
409
$this->RecSaved = true;
410
$this->RecSaving = false;
414
switch ($this->SubType) {
415
case 0: $this->RecSet = @mysql_query($Query,$this->SrcId); break;
416
case 1: $this->RecSet = $this->SrcId; break;
417
case 2: $this->RecSet = @mysql_query($Query); break;
419
if ($this->RecSet===false) $this->DataAlert('MySql error message when opening the query: '.mysql_error());
422
$this->RecSet = true;
426
if (is_array($Query)) {
427
if (isset($Query['min'])) $this->NumMin = $Query['min'];
428
if (isset($Query['step'])) $this->NumStep = $Query['step'];
429
if (isset($Query['max'])) {
430
$this->NumMax = $Query['max'];
432
$this->RecSet = $this->DataAlert('the \'num\' source is an array that has no value for the \'max\' key.');
434
if ($this->NumStep==0) $this->RecSet = $this->DataAlert('the \'num\' source is an array that has a step value set to zero.');
436
$this->NumMax = ceil($Query);
439
if ($this->NumStep>0) {
440
$this->NumVal = $this->NumMin;
442
$this->NumVal = $this->NumMax;
447
if (is_string($Query)) {
448
$this->RecSet = &$Query;
450
$this->RecSet = $this->TBS->meth_Misc_ToStr($Query);
453
case 3: // Custom function
454
$FctOpen = $this->FctOpen;
455
$this->RecSet = $FctOpen($this->SrcId,$Query,$QryPrms);
456
if ($this->RecSet===false) $this->DataAlert('function '.$FctOpen.'() has failed to open query {'.$Query.'}');
458
case 4: // Custom method from ObjectRef
459
$this->RecSet = call_user_func_array($this->FctOpen,array(&$this->SrcId,&$Query,&$QryPrms));
460
if ($this->RecSet===false) $this->DataAlert('method '.get_class($this->FctOpen[0]).'::'.$this->FctOpen[1].'() has failed to open query {'.$Query.'}');
462
case 5: // Custom method of object
463
$this->RecSet = $this->SrcId->tbsdb_open($this->SrcId,$Query,$QryPrms);
464
if ($this->RecSet===false) $this->DataAlert('method '.get_class($this->SrcId).'::tbsdb_open() has failed to open query {'.$Query.'}');
466
case 7: // PostgreSQL
467
switch ($this->SubType) {
468
case 0: $this->RecSet = @pg_query($this->SrcId,$Query); break;
469
case 1: $this->RecSet = $this->SrcId; break;
471
if ($this->RecSet===false) $this->DataAlert('PostgreSQL error message when opening the query: '.pg_last_error($this->SrcId));
474
switch ($this->SubType) {
475
case 0: $this->RecSet = @sqlite_query($this->SrcId,$Query); break;
476
case 1: $this->RecSet = $this->SrcId; break;
478
if ($this->RecSet===false) $this->DataAlert('SQLite error message when opening the query:'.sqlite_error_string(sqlite_last_error($this->SrcId)));
481
if ($this->SubType==1) {
482
$this->RecSet = $this->SrcId;
484
$this->RecSet = $this->SrcId->getIterator();
486
$this->RecSet->rewind();
489
$this->RecSet = $this->SrcId->query($Query);
490
if ($this->RecSet===false) $this->DataAlert('MySQLi error message when opening the query:'.$this->SrcId->error);
493
$this->RecSet = $this->SrcId->prepare($Query);
494
if ($this->RecSet===false) {
497
if (!is_array($QryPrms)) $QryPrms = array();
498
$ok = $this->RecSet->execute($QryPrms);
501
$err = $this->SrcId->errorInfo();
502
$this->DataAlert('PDO error message when opening the query:'.$err[2]);
505
case 12: // Zend_DB_Adapter
507
if (!is_array($QryPrms)) $QryPrms = array();
508
$this->RecSet = $this->SrcId->query($Query, $QryPrms);
509
} catch (Exception $e) {
510
$this->DataAlert('Zend_DB_Adapter error message when opening the query: '.$e->getMessage());
515
if ($this->SubType==3) {
516
$this->RecSet = $this->SrcId;
517
} elseif (($this->SubType==1) && (!is_array($QryPrms))) {
518
// SQL statement without parameters
519
$this->RecSet = $this->SrcId->query($Query);
521
if ($this->SubType==2) {
522
$stmt = $this->SrcId;
525
// SQL statement with parameters
526
$stmt = $this->SrcId->prepare($Query);
530
if (is_array($prms)) {
531
foreach ($prms as $p => $v) {
532
if (is_numeric($p)) {
536
$stmt->bindValue($p, $v[0], $v[1]);
538
$stmt->bindValue($p, $v);
542
$this->RecSet = $stmt->execute();
544
} catch (Exception $e) {
545
$this->DataAlert('SQLite3 error message when opening the query: '.$e->getMessage());
548
case 14: // Doctrine DBAL
550
if (!is_array($QryPrms)) $QryPrms = array();
551
$this->RecSet = $this->SrcId->executeQuery($Query, $QryPrms);
552
} catch (Exception $e) {
553
$this->DataAlert('Doctrine DBAL error message when opening the query: '.$e->getMessage());
558
if (($this->Type===0) || ($this->Type===9)) {
559
unset($this->RecKey); $this->RecKey = '';
561
if ($this->RecSaving) {
562
unset($this->RecBuffer); $this->RecBuffer = array();
564
$this->RecKey = &$this->RecNum; // Not array: RecKey = RecNum
567
return ($this->RecSet!==false);
571
public function DataFetch() {
573
// Save previous record
574
if ($this->PrevSave) {
575
$this->_CopyRec($this, $this->PrevRec);
578
if ($this->NextSave) {
579
// set current record
580
if ($this->NextRec === false) {
582
$this->NextRec = (object) array('RecNum' => 1); // prepare for getting properties, RecNum needed for the first fetch
583
$this->_DataFetchOn($this);
586
$this->_CopyRec($this->NextRec, $this);
589
if ($this->CurrRec === false) {
591
$this->NextRec = (object) null; // clear properties
593
$this->_DataFetchOn($this->NextRec);
597
$this->_DataFetchOn($this);
602
public function DataClose() {
603
$this->OnDataOk = false;
604
$this->OnDataPrm = false;
605
$this->OnDataPi = false;
606
if ($this->RecSaved) return;
607
switch ($this->Type) {
608
case 6: mysql_free_result($this->RecSet); break;
609
case 3: $FctClose=$this->FctClose; $FctClose($this->RecSet); break;
610
case 4: call_user_func_array($this->FctClose,array(&$this->RecSet)); break;
611
case 5: $this->SrcId->tbsdb_close($this->RecSet); break;
612
case 7: pg_free_result($this->RecSet); break;
613
case 10: $this->RecSet->free(); break; // MySQLi
615
if ($this->SubType!=3) {
616
$this->RecSet->finalize();
619
//case 11: $this->RecSet->closeCursor(); break; // PDO
621
if ($this->RecSaving) {
622
$this->RecSet = &$this->RecBuffer;
623
$this->RecNbr = $this->RecNumInit + count($this->RecSet);
624
$this->RecSaving = false;
625
$this->RecSaved = true;
630
* Copy the record information from an object to another.
632
private function _CopyRec($from, $to) {
634
$to->CurrRec = $from->CurrRec;
635
$to->RecNum = $from->RecNum;
636
$to->RecKey = $from->RecKey;
641
* Fetch the next record on the object $obj.
642
* This wil set the proiperties :
647
private function _DataFetchOn($obj) {
649
// Check if the records are saved in an array
650
if ($this->RecSaved) {
651
if ($obj->RecNum < $this->RecNbr) {
652
if ($this->RSIsFirst) {
653
if ($this->SubType===2) { // From string
654
reset($this->RecSet);
655
$obj->RecKey = key($this->RecSet);
656
$obj->CurrRec = &$this->RecSet[$obj->RecKey];
658
$obj->CurrRec = reset($this->RecSet);
659
$obj->RecKey = key($this->RecSet);
661
$this->RSIsFirst = false;
663
if ($this->SubType===2) { // From string
665
$obj->RecKey = key($this->RecSet);
666
$obj->CurrRec = &$this->RecSet[$obj->RecKey];
668
$obj->CurrRec = next($this->RecSet);
669
$obj->RecKey = key($this->RecSet);
672
if ((!is_array($obj->CurrRec)) && (!is_object($obj->CurrRec))) $obj->CurrRec = array('key'=>$obj->RecKey, 'val'=>$obj->CurrRec);
674
if ($this->OnDataOk) {
675
$this->OnDataArgs[1] = &$obj->CurrRec; // Reference has changed if ($this->SubType===2)
676
if ($this->OnDataPrm) call_user_func_array($this->OnDataPrmRef,$this->OnDataArgs);
677
if ($this->OnDataPi) $this->TBS->meth_PlugIn_RunAll($this->OnDataPiRef,$this->OnDataArgs);
678
if ($this->SubType!==2) $this->RecSet[$obj->RecKey] = $obj->CurrRec; // save modifications because array reading is done without reference :(
681
unset($obj->CurrRec); $obj->CurrRec = false;
686
switch ($this->Type) {
688
$obj->CurrRec = mysql_fetch_assoc($this->RecSet);
691
if (($this->NumVal>=$this->NumMin) && ($this->NumVal<=$this->NumMax)) {
692
$obj->CurrRec = array('val'=>$this->NumVal);
693
$this->NumVal += $this->NumStep;
695
$obj->CurrRec = false;
699
if ($obj->RecNum===0) {
700
if ($this->RecSet==='') {
701
$obj->CurrRec = false;
703
$obj->CurrRec = &$this->RecSet;
706
$obj->CurrRec = false;
709
case 3: // Custom function
710
$FctFetch = $this->FctFetch;
711
$obj->CurrRec = $FctFetch($this->RecSet,$obj->RecNum+1);
713
case 4: // Custom method from ObjectRef
714
$this->FctPrm[0] = &$this->RecSet; $this->FctPrm[1] = $obj->RecNum+1;
715
$obj->CurrRec = call_user_func_array($this->FctFetch,$this->FctPrm);
717
case 5: // Custom method of object
718
$obj->CurrRec = $this->SrcId->tbsdb_fetch($this->RecSet,$obj->RecNum+1);
720
case 7: // PostgreSQL
721
$obj->CurrRec = pg_fetch_assoc($this->RecSet);
724
$obj->CurrRec = sqlite_fetch_array($this->RecSet,SQLITE_ASSOC);
727
if ($this->RecSet->valid()) {
728
$obj->CurrRec = $this->RecSet->current();
729
$obj->RecKey = $this->RecSet->key();
730
$this->RecSet->next();
732
$obj->CurrRec = false;
736
$obj->CurrRec = $this->RecSet->fetch_assoc();
737
if (is_null($obj->CurrRec)) $obj->CurrRec = false;
740
$obj->CurrRec = $this->RecSet->fetch(PDO::FETCH_ASSOC);
742
case 12: // Zend_DB_Adapater
743
$obj->CurrRec = $this->RecSet->fetch(Zend_Db::FETCH_ASSOC);
746
$obj->CurrRec = $this->RecSet->fetchArray(SQLITE3_ASSOC);
748
case 14: // Doctrine DBAL
749
$obj->CurrRec = $this->RecSet->fetchAssociative();
754
if ($obj->CurrRec!==false) {
756
if ($this->OnDataOk) {
757
if ($this->OnDataPrm) call_user_func_array($this->OnDataPrmRef,$this->OnDataArgs);
758
if ($this->OnDataPi) $this->TBS->meth_PlugIn_RunAll($this->OnDataPiRef,$this->OnDataArgs);
760
if ($this->RecSaving) $this->RecBuffer[$obj->RecKey] = $obj->CurrRec;
767
// *********************************************
769
class clsTinyButStrong {
774
public $TplVars = array();
775
public $ObjectRef = false;
776
public $NoErr = false;
777
public $Assigned = array();
778
public $ExtendedMethods = array();
780
// Undocumented (can change at any version)
781
public $Version = '3.15.0';
783
public $TurboBlock = true;
784
public $VarPrefix = '';
785
public $VarRef = null;
786
public $FctPrefix = '';
787
public $Protect = true;
789
public $AttDelim = false;
790
public $MethodsAllowed = false;
791
public $ScriptsAllowed = false;
792
public $OnLoad = true;
793
public $OnShow = true;
794
public $IncludePath = array();
795
public $TplStore = array();
796
public $OldSubTpl = false; // turn to true to have compatibility with the old way to perform subtemplates, that is get output buffuring
798
public $_ErrMsgName = '';
799
public $_LastFile = '';
800
public $_CharsetFct = false;
802
public $_CurrBlock = '';
803
public $_ChrOpen = '[';
804
public $_ChrClose = ']';
805
public $_ChrVal = '[val]';
806
public $_ChrProtect = '[';
807
public $_PlugIns = array();
808
public $_PlugIns_Ok = false;
809
public $_piOnFrm_Ok = false;
811
// Compatibility with PHP 8.2
817
private $_piBeforeLoadTemplate;
818
private $_piAfterLoadTemplate;
819
private $_piOnMergeField;
820
private $_piBeforeShow;
821
private $_piAfterShow;
822
private $_piOnCommand;
823
private $_piOnOperation;
824
private $_piOnCacheField;
825
private $_PlugIns_Ok_save;
826
private $_piOnFrm_Ok_save;
828
private $_piBeforeMergeBlock;
829
private $_piOnMergeSection;
830
private $_piOnMergeGroup;
831
private $_piAfterMergeBlock;
832
private $_piOnSpecialVar;
837
public $OtbsAutoUncompress;
838
public $OtbsConvertApostrophes;
839
public $OtbsSpacePreserve;
840
public $OtbsClearWriter;
841
public $OtbsClearMsWord;
842
public $OtbsMsExcelConsistent;
843
public $OtbsMsExcelExplicitRef;
844
public $OtbsClearMsPowerpoint;
845
public $OtbsGarbageCollector;
846
public $OtbsMsExcelCompatibility;
848
public $OtbsSubFileLst;
851
function __construct($Options=null,$VarPrefix='',$FctPrefix='') {
854
if (is_string($Options)) {
856
$Options = array('var_prefix'=>$VarPrefix, 'fct_prefix'=>$FctPrefix);
859
$Len = strlen($Chrs);
860
if ($Len===2) { // For compatibility
861
$Options['chr_open'] = $Chrs[0];
862
$Options['chr_close'] = $Chrs[1];
865
$Pos = strpos($Chrs,',');
866
if (($Pos!==false) && ($Pos>0) && ($Pos<$Len-1)) {
867
$Options['chr_open'] = substr($Chrs,0,$Pos);
868
$Options['chr_close'] = substr($Chrs,$Pos+1);
872
if ($Err) $this->meth_Misc_Alert('with clsTinyButStrong() function','value \''.$Chrs.'\' is a bad tag delimitor definition.');
876
// Set VarRef initial value
877
$this->ResetVarRef(true);
880
if (is_array($Options)) $this->SetOption($Options);
882
// Links to global variables (cannot be converted to static yet because of compatibility)
883
global $_TBS_FormatLst, $_TBS_UserFctLst, $_TBS_BlockAlias, $_TBS_PrmCombo, $_TBS_AutoInstallPlugIns, $_TBS_ParallelLst;
884
if (!isset($_TBS_FormatLst)) $_TBS_FormatLst = array();
885
if (!isset($_TBS_UserFctLst)) $_TBS_UserFctLst = array();
886
if (!isset($_TBS_BlockAlias)) $_TBS_BlockAlias = array();
887
if (!isset($_TBS_PrmCombo)) $_TBS_PrmCombo = array();
888
if (!isset($_TBS_ParallelLst)) $_TBS_ParallelLst = array();
889
$this->_UserFctLst = &$_TBS_UserFctLst;
891
// Auto-installing plug-ins
892
if (isset($_TBS_AutoInstallPlugIns)) foreach ($_TBS_AutoInstallPlugIns as $pi) $this->PlugIn(TBS_INSTALL,$pi);
896
function __call($meth, $args) {
897
if (isset($this->ExtendedMethods[$meth])) {
898
if ( is_array($this->ExtendedMethods[$meth]) || is_string($this->ExtendedMethods[$meth]) ) {
899
return call_user_func_array($this->ExtendedMethods[$meth], $args);
901
return call_user_func_array(array(&$this->ExtendedMethods[$meth], $meth), $args);
904
$this->meth_Misc_Alert('Method not found','\''.$meth.'\' is neither a native nor an extended method of TinyButStrong.');
908
function SetOption($o, $v=false, $d=false) {
909
if (!is_array($o)) $o = array($o=>$v);
910
if (isset($o['var_prefix'])) $this->VarPrefix = $o['var_prefix'];
911
if (isset($o['fct_prefix'])) $this->FctPrefix = $o['fct_prefix'];
912
if (isset($o['noerr'])) $this->NoErr = $o['noerr'];
913
if (isset($o['old_subtemplate'])) $this->OldSubTpl = $o['old_subtemplate'];
914
if (isset($o['auto_merge'])) {
915
$this->OnLoad = $o['auto_merge'];
916
$this->OnShow = $o['auto_merge'];
918
if (isset($o['onload'])) $this->OnLoad = $o['onload'];
919
if (isset($o['onshow'])) $this->OnShow = $o['onshow'];
920
if (isset($o['att_delim'])) $this->AttDelim = $o['att_delim'];
921
if (isset($o['protect'])) $this->Protect = $o['protect'];
922
if (isset($o['turbo_block'])) $this->TurboBlock = $o['turbo_block'];
923
if (isset($o['charset'])) $this->meth_Misc_Charset($o['charset']);
926
if (isset($o['chr_open'])) {
927
$this->_ChrOpen = $o['chr_open'];
930
if (isset($o['chr_close'])) {
931
$this->_ChrClose = $o['chr_close'];
935
$this->_ChrVal = $this->_ChrOpen.'val'.$this->_ChrClose;
936
$this->_ChrProtect = '&#'.ord($this->_ChrOpen[0]).';'.substr($this->_ChrOpen,1);
938
if (array_key_exists('tpl_frms',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_FormatLst'], 'frm', $o['tpl_frms'], $d);
939
if (array_key_exists('block_alias',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_BlockAlias'], false, $o['block_alias'], $d);
940
if (array_key_exists('prm_combo',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_PrmCombo'], 'prm', $o['prm_combo'], $d);
941
if (array_key_exists('parallel_conf',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_ParallelLst'], false, $o['parallel_conf'], $d);
942
if (array_key_exists('include_path',$o)) self::f_Misc_UpdateArray($this->IncludePath, true, $o['include_path'], $d);
943
if (isset($o['render'])) $this->Render = $o['render'];
944
if (isset($o['methods_allowed'])) $this->MethodsAllowed = $o['methods_allowed'];
945
if (isset($o['scripts_allowed'])) $this->ScriptsAllowed = $o['scripts_allowed'];
948
function GetOption($o) {
950
$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');
952
foreach ($x as $o) $r[$o] = $this->GetOption($o);
955
if ($o==='var_prefix') return $this->VarPrefix;
956
if ($o==='fct_prefix') return $this->FctPrefix;
957
if ($o==='noerr') return $this->NoErr;
958
if ($o==='auto_merge') return ($this->OnLoad && $this->OnShow);
959
if ($o==='onload') return $this->OnLoad;
960
if ($o==='onshow') return $this->OnShow;
961
if ($o==='att_delim') return $this->AttDelim;
962
if ($o==='protect') return $this->Protect;
963
if ($o==='turbo_block') return $this->TurboBlock;
964
if ($o==='charset') return $this->Charset;
965
if ($o==='chr_open') return $this->_ChrOpen;
966
if ($o==='chr_close') return $this->_ChrClose;
967
if ($o==='tpl_frms') {
968
// simplify the list of formats
970
foreach ($GLOBALS['_TBS_FormatLst'] as $s=>$i) $x[$s] = $i['Str'];
973
if ($o==='include_path') return $this->IncludePath;
974
if ($o==='render') return $this->Render;
975
if ($o==='methods_allowed') return $this->MethodsAllowed;
976
if ($o==='scripts_allowed') return $this->ScriptsAllowed;
977
if ($o==='parallel_conf') return $GLOBALS['_TBS_ParallelLst'];
978
if ($o==='block_alias') return $GLOBALS['_TBS_BlockAlias'];
979
if ($o==='prm_combo') return $GLOBALS['_TBS_PrmCombo'];
980
return $this->meth_Misc_Alert('with GetOption() method','option \''.$o.'\' is not supported.');;
983
public function ResetVarRef($ToGlobal) {
984
// We set a new variable in order to force the reference
985
// value NULL means that VarRef refers to $GLOBALS
986
$x = ($ToGlobal) ? null : array();
991
* Get an item value from VarRef.
992
* Ensure the compatibility with PHP 8.1 if VarRef is set to Global.
994
* @param string $key The item key.
995
* @param mixed $default The default value.
999
public function GetVarRefItem($key, $default) {
1001
if (is_null($this->VarRef)) {
1003
if (array_key_exists($key, $GLOBALS)) {
1004
return $GLOBALS[$key];
1011
if (array_key_exists($key, $this->VarRef)) {
1012
return $this->VarRef[$key];
1022
* Set an item value to VarRef.
1023
* Ensure the compatibility with PHP 8.1 if VarRef is set to Global.
1025
* @param string|array $keyOrList A list of keys and items to add, or the item key.
1026
* @param mixed $value (optional) The item value. Use NULL in order to delete the item.
1028
public function SetVarRefItem($keyOrList, $value = null) {
1030
if (is_array($keyOrList)) {
1033
$list = array($keyOrList => $value);
1036
if (is_null($this->VarRef)) {
1038
foreach ($list as $key => $value) {
1039
if (is_null($value)) {
1040
unset($GLOBALS[$key]);
1042
$GLOBALS[$key] = $value;
1048
foreach ($list as $key => $value) {
1049
if (is_null($value)) {
1050
unset($this->VarRef[$key]);
1052
$this->VarRef[$key] = $value;
1061
public function LoadTemplate($File,$Charset='') {
1063
$this->meth_Misc_Charset($Charset);
1067
if ($this->_PlugIns_Ok) {
1068
if (isset($this->_piBeforeLoadTemplate) || isset($this->_piAfterLoadTemplate)) {
1070
$ArgLst = func_get_args();
1071
$ArgLst[0] = &$File;
1072
$ArgLst[1] = &$Charset;
1073
if (isset($this->_piBeforeLoadTemplate)) $Ok = $this->meth_PlugIn_RunAll($this->_piBeforeLoadTemplate,$ArgLst);
1078
if (!is_null($File)) {
1080
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.');
1081
if ($Charset==='+') {
1082
$this->Source .= $x;
1087
if ($this->meth_Misc_IsMainTpl()) {
1088
if (!is_null($File)) $this->_LastFile = $File;
1089
if ($Charset!=='+') $this->TplVars = array();
1090
$this->meth_Misc_Charset($Charset);
1092
// Automatic fields and blocks
1093
if ($this->OnLoad) $this->meth_Merge_AutoOn($this->Source,'onload',true,true);
1096
if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterLoadTemplate)) $Ok = $this->meth_PlugIn_RunAll($this->_piAfterLoadTemplate,$ArgLst);
1100
public function GetBlockSource($BlockName,$AsArray=false,$DefTags=true,$ReplaceWith=false) {
1104
$FieldOutside = false;
1106
$Mode = ($DefTags) ? 3 : 2;
1108
while ($Loc = $this->meth_Locator_FindBlockNext($this->Source,$BlockName,$Pos,'.',$Mode,$P1,$FieldOutside)) {
1112
$PosBeg1 = $Loc->PosBeg;
1113
} elseif (!$AsArray) {
1114
$Sep = substr($this->Source,$PosSep,$Loc->PosBeg-$PosSep); // part of the source between sections
1116
$RetVal[$Nbr] = $Sep.$Loc->BlockSrc;
1117
$Pos = $Loc->PosEnd;
1118
$PosSep = $Loc->PosEnd+1;
1121
if ($Nbr==0) return false;
1124
// Return the true part of the template
1125
$RetVal = substr($this->Source,$PosBeg1,$Pos-$PosBeg1+1);
1127
// Return the concatenated section without def tags
1128
$RetVal = implode('', $RetVal);
1131
if ($ReplaceWith!==false) $this->Source = substr($this->Source,0,$PosBeg1).$ReplaceWith.substr($this->Source,$Pos+1);
1136
* Get the value of a XML-HTML attribute targeted thanks to a TBS fields having parameter att.
1137
* @param string $Name Name of the TBS fields. It must have parameter att.
1138
* @param boolean $delete (optional, true by default) Use true to delete the TBS field.
1139
* @return string|true|null|false The value of the attribute,
1140
* true if the attribute is found without value,
1141
* null if the TBS field, the target element is not found,
1142
* or false for other error.
1144
public function GetAttValue($Name, $delete = true) {
1147
while ($Loc = $this->meth_Locator_FindTbs($this->Source,$Name,$Pos,'.')) {
1148
if (isset($Loc->PrmLst['att'])) {
1149
if ($this->f_Xml_AttFind($this->Source,$Loc,false,$this->AttDelim)) {
1151
if ($Loc->AttBeg !== false) {
1152
if ($Loc->AttValBeg !== false) {
1153
$val = substr($this->Source, $Loc->AttValBeg, $Loc->AttEnd - $Loc->AttValBeg + 1);
1154
$val = substr($val, 1, -1);
1169
$this->Source = substr_replace($this->Source, '', $Loc->PosBeg, $Loc->PosEnd - $Loc->PosBeg + 1);
1170
$Pos = $Loc->PosBeg;
1172
$Pos = $Loc->PosEnd;
1178
public function MergeBlock($BlockLst,$SrcId='assigned',$Query='',$QryPrms=false) {
1180
if ($SrcId==='assigned') {
1181
$Arg = array($BlockLst,&$SrcId,&$Query,&$QryPrms);
1182
if (!$this->meth_Misc_Assign($BlockLst, $Arg, 'MergeBlock')) return 0;
1183
$BlockLst = $Arg[0]; $SrcId = &$Arg[1]; $Query = &$Arg[2];
1186
if (is_string($BlockLst)) $BlockLst = explode(',',$BlockLst);
1188
if ($SrcId==='cond') {
1190
foreach ($BlockLst as $Block) {
1191
$Block = trim($Block);
1192
if ($Block!=='') $Nbr += $this->meth_Merge_AutoOn($this->Source,$Block,true,true);
1196
return $this->meth_Merge_Block($this->Source,$BlockLst,$SrcId,$Query,false,0,$QryPrms);
1201
public function MergeField($NameLst,$Value='assigned',$IsUserFct=false,$DefaultPrm=false) {
1203
$FctCheck = $IsUserFct;
1204
if ($PlugIn = isset($this->_piOnMergeField)) $ArgPi = array('','',&$Value,0,&$this->Source,0,0);
1207
$Prm = is_array($DefaultPrm);
1209
if ( ($Value==='assigned') && ($NameLst!=='var') && ($NameLst!=='onshow') && ($NameLst!=='onload') ) {
1210
$Arg = array($NameLst,&$Value,&$IsUserFct,&$DefaultPrm);
1211
if (!$this->meth_Misc_Assign($NameLst, $Arg, 'MergeField')) return false;
1212
$NameLst = $Arg[0]; $Value = &$Arg[1]; $IsUserFct = &$Arg[2]; $DefaultPrm = &$Arg[3];
1215
$NameLst = explode(',',$NameLst);
1217
foreach ($NameLst as $Name) {
1218
$Name = trim($Name);
1221
case '': $Cont=true;break;
1222
case 'onload': $this->meth_Merge_AutoOn($this->Source,'onload',true,true);$Cont=true;break;
1223
case 'onshow': $this->meth_Merge_AutoOn($this->Source,'onshow',true,true);$Cont=true;break;
1224
case 'var': $this->meth_Merge_AutoVar($this->Source,true);$Cont=true;break;
1226
if ($Cont) continue;
1227
if ($PlugIn) $ArgPi[0] = $Name;
1229
// Initilize the user function (only once)
1233
if (!$this->meth_Misc_UserFctCheck($FctInfo,'f',$ErrMsg,$ErrMsg,false)) return $this->meth_Misc_Alert('with MergeField() method',$ErrMsg);
1234
$FctArg = array('','');
1238
while ($Loc = $this->meth_Locator_FindTbs($this->Source,$Name,$PosBeg,'.')) {
1239
if ($Prm) $Loc->PrmLst = array_merge($DefaultPrm,$Loc->PrmLst);
1240
// Apply user function
1242
$FctArg[0] = &$Loc->SubName; $FctArg[1] = &$Loc->PrmLst;
1243
$Value = call_user_func_array($FctInfo,$FctArg);
1247
$ArgPi[1] = $Loc->SubName; $ArgPi[3] = &$Loc->PrmLst; $ArgPi[5] = &$Loc->PosBeg; $ArgPi[6] = &$Loc->PosEnd;
1248
$Ok = $this->meth_PlugIn_RunAll($this->_piOnMergeField,$ArgPi);
1252
$PosBeg = $this->meth_Locator_Replace($this->Source,$Loc,$Value,$SubStart);
1254
$PosBeg = $Loc->PosEnd;
1261
* Replace a set of simple TBS fields (that is fields without any parameters) with more complexe TBS fields.
1262
* @param array $fields An associative array of items to replace.
1263
* Keys are the name of the simple field to replace.
1264
* Values are the parameters of the field as an array or as a string.
1265
* Parameter 'name' will be used as the new name of the field, by default it is the same name as the simple field.
1266
* @param string $blockName (optional) The name of the block for prefixing fields.
1268
public function ReplaceFields($fields, $blockName = false) {
1270
$prefix = ($blockName) ? $blockName . '.' : '';
1272
// calling the replace using array is faster than a loop
1275
foreach ($fields as $name => $prms) {
1276
$what[] = $this->_ChrOpen . $name . $this->_ChrClose;
1277
if (is_array($prms)) {
1280
foreach ($prms as $p => $v) {
1281
if ($p === 'name') {
1286
} elseif (is_array($v)) {
1288
$lst .= ';' . $p . '=' . $x;
1291
$lst .= ';' . $p . '=' . $v;
1295
$with[] = $this->_ChrOpen . $prefix . $name . $lst . $this->_ChrClose;
1297
// simple string replace
1302
$this->Source = str_replace($what, $with, $this->Source);
1306
public function Show($Render=false) {
1308
if ($Render===false) $Render = $this->Render;
1309
if ($this->_PlugIns_Ok) {
1310
if (isset($this->_piBeforeShow) || isset($this->_piAfterShow)) {
1312
$ArgLst = func_get_args();
1313
$ArgLst[0] = &$Render;
1314
if (isset($this->_piBeforeShow)) $Ok = $this->meth_PlugIn_RunAll($this->_piBeforeShow,$ArgLst);
1318
if ($this->OnShow) $this->meth_Merge_AutoOn($this->Source,'onshow',true,true);
1319
$this->meth_Merge_AutoVar($this->Source,true);
1321
if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterShow)) $this->meth_PlugIn_RunAll($this->_piAfterShow,$ArgLst);
1322
if ($this->_ErrMsgName!=='') $this->MergeField($this->_ErrMsgName, $this->ErrMsg);
1323
if ($this->meth_Misc_IsMainTpl()) {
1324
if (($Render & TBS_OUTPUT)==TBS_OUTPUT) echo $this->Source;
1325
if (($Render & TBS_EXIT)==TBS_EXIT) exit;
1326
} elseif ($this->OldSubTpl) {
1327
if (($Render & TBS_OUTPUT)==TBS_OUTPUT) echo $this->Source;
1332
public function PlugIn($Prm1,$Prm2=0) {
1334
if (is_numeric($Prm1)) {
1336
case TBS_INSTALL: // Try to install the plug-in
1338
if (isset($this->_PlugIns[$PlugInId])) {
1339
return $this->meth_Misc_Alert('with PlugIn() method','plug-in \''.$PlugInId.'\' is already installed.');
1341
$ArgLst = func_get_args();
1342
array_shift($ArgLst); array_shift($ArgLst);
1343
return $this->meth_PlugIn_Install($PlugInId,$ArgLst,false);
1345
case TBS_ISINSTALLED: // Check if the plug-in is installed
1346
return isset($this->_PlugIns[$Prm2]);
1347
case -4: // Deactivate special plug-ins
1348
$this->_PlugIns_Ok_save = $this->_PlugIns_Ok;
1349
$this->_PlugIns_Ok = false;
1351
case -5: // Deactivate OnFormat
1352
$this->_piOnFrm_Ok_save = $this->_piOnFrm_Ok;
1353
$this->_piOnFrm_Ok = false;
1355
case -10: // Restore
1356
if (isset($this->_PlugIns_Ok_save)) $this->_PlugIns_Ok = $this->_PlugIns_Ok_save;
1357
if (isset($this->_piOnFrm_Ok_save)) $this->_piOnFrm_Ok = $this->_piOnFrm_Ok_save;
1361
} elseif (is_string($Prm1)) {
1362
// Plug-in's command
1363
$p = strpos($Prm1,'.');
1367
$PlugInId = substr($Prm1,0,$p); // direct command
1369
if (!isset($this->_PlugIns[$PlugInId])) {
1370
if (!$this->meth_PlugIn_Install($PlugInId,array(),true)) return false;
1372
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.');
1373
$ArgLst = func_get_args();
1374
if ($p===false) array_shift($ArgLst);
1375
$Ok = call_user_func_array($this->_piOnCommand[$PlugInId],$ArgLst);
1376
if (is_null($Ok)) $Ok = true;
1379
return $this->meth_Misc_Alert('with PlugIn() method','\''.$Prm1.'\' is an invalid plug-in key, the type of the value is \''.gettype($Prm1).'\'.');
1383
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
1385
function meth_Locator_FindTbs(&$Txt,$Name,$Pos,$ChrSub) {
1386
// Find a TBS Locator
1389
$PosMax = strlen($Txt) -1;
1390
$Start = $this->_ChrOpen.$Name;
1393
// Search for the opening char
1394
if ($Pos>$PosMax) return false;
1395
$Pos = strpos($Txt,$Start,$Pos);
1397
// If found => next chars are analyzed
1401
$Loc = new clsTbsLocator;
1403
$PosX = $Pos + strlen($Start);
1406
if ($x===$this->_ChrClose) {
1408
} elseif ($x===$ChrSub) {
1409
$Loc->SubOk = true; // it is no longer the false value
1412
} elseif (strpos(';',$x)!==false) {
1419
$Loc->PosBeg = $Pos;
1421
self::f_Loc_PrmRead($Txt,$PosX,false,'\'',$this->_ChrOpen,$this->_ChrClose,$Loc,$PosEnd);
1422
if ($PosEnd===false) {
1423
$this->meth_Misc_Alert('','can\'t found the end of the tag \''.substr($Txt,$Pos,$PosX-$Pos+10).'...\'.');
1426
self::meth_Misc_ApplyPrmCombo($Loc->PrmLst, $Loc);
1432
} while ($PosEnd===false);
1434
$Loc->PosEnd = $PosEnd;
1436
$Loc->FullName = $Name.'.'.$Loc->SubName;
1437
$Loc->SubLst = explode('.',$Loc->SubName);
1438
$Loc->SubNbr = count($Loc->SubLst);
1440
$Loc->FullName = $Name;
1442
if ( $ReadPrm && ( isset($Loc->PrmLst['enlarge']) || isset($Loc->PrmLst['comm']) ) ) {
1443
$Loc->PosBeg0 = $Loc->PosBeg;
1444
$Loc->PosEnd0 = $Loc->PosEnd;
1445
$enlarge = (isset($Loc->PrmLst['enlarge'])) ? $Loc->PrmLst['enlarge'] : $Loc->PrmLst['comm'];
1446
if (($enlarge===true) || ($enlarge==='')) {
1447
$Loc->Enlarged = self::f_Loc_EnlargeToStr($Txt,$Loc,'<!--' ,'-->');
1449
$Loc->Enlarged = self::f_Loc_EnlargeToTag($Txt,$Loc,$enlarge,false);
1458
* Note: keep the « & » if the function is called with it.
1462
function meth_Locator_SectionNewBDef(&$LocR,$BlockName,$Txt,$PrmLst,$Cache) {
1469
if ($this->_PlugIns_Ok && isset($this->_piOnCacheField)) {
1471
$ArgLst = array(0=>$BlockName, 1=>false, 2=>&$Txt, 3=>array('att'=>true), 4=>&$LocLst, 5=>&$Pos);
1476
// Cache TBS locators
1477
$Cache = ($Cache && $this->TurboBlock);
1481
while ($Loc = $this->meth_Locator_FindTbs($Txt,$BlockName,$Pos,'.')) {
1483
$LocNbr = 1 + count($LocLst);
1484
$LocLst[$LocNbr] = &$Loc;
1486
// Next search position : always ("original PosBeg" + 1).
1487
// Must be done here because loc can be moved by the plug-in.
1488
if ($Loc->Enlarged) {
1490
$Pos = $Loc->PosBeg0 + 1;
1491
$Loc->Enlarged = false;
1494
$Pos = $Loc->PosBeg + 1;
1497
// Note: the plug-in may move, delete and add one or several locs.
1498
// Move : backward or forward (will be sorted)
1499
// Delete : add property DelMe=true
1500
// Add : at the end of $LocLst (will be sorted)
1503
$this->meth_Plugin_RunAll($this->_piOnCacheField,$ArgLst);
1506
if (($Loc->SubName==='#') || ($Loc->SubName==='$')) {
1507
$Loc->IsRecInfo = true;
1508
$Loc->RecInfo = $Loc->SubName;
1511
$Loc->IsRecInfo = false;
1514
// Process parameter att for new added locators.
1515
$NewNbr = count($LocLst);
1516
for ($i=$LocNbr;$i<=$NewNbr;$i++) {
1518
if (isset($li->PrmLst['att'])) {
1519
$LocSrc = substr($Txt,$li->PosBeg,$li->PosEnd-$li->PosBeg+1); // for error message
1520
if ($this->f_Xml_AttFind($Txt,$li,$LocLst,$this->AttDelim)) {
1521
if (isset($Loc->PrmLst['atttrue'])) {
1522
$li->PrmLst['magnet'] = '#';
1523
$li->PrmLst['ope'] = (isset($li->PrmLst['ope'])) ? $li->PrmLst['ope'].',attbool' : 'attbool';
1526
$Pos = $Loc->DelPos;
1529
$this->meth_Misc_Alert('','TBS is not able to merge the field '.$LocSrc.' because the entity targeted by parameter \'att\' cannot be found.');
1539
$e = self::f_Loc_Sort($LocLst, true, 1);
1544
// Create the object
1547
$o->LocLst = $LocLst;
1548
$o->LocNbr = count($LocLst);
1549
$o->Name = $BlockName;
1552
$o->IsSerial = false;
1553
$o->AutoSub = false;
1555
while (isset($PrmLst['sub'.$i])) {
1560
$LocR->BDefLst[] = &$o; // Can be usefull for plug-in
1566
* Add a special section to the LocR.
1568
* @param object $LocR
1569
* @param string $BlockName
1570
* @param object $BDef
1571
* @param string $Field Name of the field on which the group of values is defined.
1572
* @param string $FromPrm Parameter that induced the section.
1576
function meth_Locator_MakeBDefFromField(&$LocR,$BlockName,$Field,$FromPrm) {
1578
if (strpos($Field,$this->_ChrOpen)===false) {
1579
// The field is a simple colmun name
1580
$src = $this->_ChrOpen.$BlockName.'.'.$Field.';tbstype='.$FromPrm.$this->_ChrClose; // tbstype is an internal parameter for catching errors
1582
// The fields is a TBS field's expression
1586
$BDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,$src,array(),true);
1588
if ($BDef->LocNbr==0) $this->meth_Misc_Alert('Parameter '.$FromPrm,'The value \''.$Field.'\' is unvalide for this parameter.');
1595
* Add a special section to the LocR.
1597
* @param object $LocR
1598
* @param string $BlockName
1599
* @param object $BDef
1600
* @param string $Type Type of behavior: 'H' header, 'F' footer, 'S' splitter.
1601
* @param string $Field Name of the field on which the group of values is defined.
1602
* @param string $FromPrm Parameter that induced the section.
1604
function meth_Locator_SectionAddGrp(&$LocR,$BlockName,&$BDef,$Type,$Field,$FromPrm) {
1606
$BDef->PrevValue = false;
1607
$BDef->Type = $Type; // property not used in native, but designed for plugins
1609
// Save sub items in a structure near to Locator.
1610
$BDef->FDef = $this->meth_Locator_MakeBDefFromField($LocR,$BlockName,$Field,$FromPrm);
1614
if ($LocR->HeaderFound===false) {
1615
$LocR->HeaderFound = true;
1616
$LocR->HeaderNbr = 0;
1617
$LocR->HeaderDef = array(); // 1 to HeaderNbr
1619
$i = ++$LocR->HeaderNbr;
1620
$LocR->HeaderDef[$i] = $BDef;
1622
// Footer behavior (footer or splitter)
1623
if ($LocR->FooterFound===false) {
1624
$LocR->FooterFound = true;
1625
$LocR->FooterNbr = 0;
1626
$LocR->FooterDef = array(); // 1 to FooterNbr
1628
$BDef->AddLastGrp = ($Type==='F');
1629
$i = ++$LocR->FooterNbr;
1630
$LocR->FooterDef[$i] = $BDef;
1636
* Merge a locator with a text.
1638
* @param string $Txt The source string to modify.
1639
* @param object $Loc The locator of the field to replace.
1640
* @param mixed $Value The value to merge with.
1641
* @param integer|false $SubStart The offset of subname to considere.
1643
* @return integer The position just after the replaced field. Or the position of the start if the replace is canceled.
1644
* This position can be useful because we don't know in advance how $Value will be replaced.
1645
* $Loc->PosNext is also set to the next search position when embedded fields are allowed.
1647
function meth_Locator_Replace(&$Txt,&$Loc,&$Value,$SubStart) {
1649
// Found the value if there is a subname
1650
if (($SubStart!==false) && $Loc->SubOk) {
1651
for ($i=$SubStart;$i<$Loc->SubNbr;$i++) {
1652
$x = $Loc->SubLst[$i]; // &$Loc... brings an error with Event Example, I don't know why.
1653
if (is_array($Value)) {
1654
if (isset($Value[$x])) {
1655
$Value = &$Value[$x];
1656
} elseif (array_key_exists($x,$Value)) {// can happens when value is NULL
1657
$Value = &$Value[$x];
1659
if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'item \''.$x.'\' is not an existing key in the array.',true);
1660
unset($Value); $Value = ''; break;
1662
} elseif (is_object($Value)) {
1663
$form = $this->f_Misc_ParseFctForm($x);
1665
if ( method_exists($Value,$n) || ($form['as_fct'] && method_exists($Value,'__call')) ) {
1666
if ($this->MethodsAllowed || !in_array(strtok($Loc->FullName,'.'),array('onload','onshow','var')) ) {
1667
$x = call_user_func_array(array(&$Value,$n),$form['args']);
1669
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
} elseif (property_exists($Value,$n)) {
1674
} elseif (isset($Value->$n)) {
1675
$x = $Value->$n; // useful for overloaded property
1677
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);
1678
unset($Value); $Value = ''; break;
1680
$Value = &$x; unset($x); $x = '';
1682
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);
1683
unset($Value); $Value = ''; break;
1688
$CurrVal = $Value; // Unlink
1690
if (isset($Loc->PrmLst['onformat'])) {
1691
if ($Loc->FirstMerge) {
1692
$Loc->OnFrmInfo = $Loc->PrmLst['onformat'];
1693
$Loc->OnFrmArg = array($Loc->FullName,'',&$Loc->PrmLst,&$this);
1695
if (!$this->meth_Misc_UserFctCheck($Loc->OnFrmInfo,'f',$ErrMsg,$ErrMsg,true)) {
1696
unset($Loc->PrmLst['onformat']);
1697
if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'(parameter onformat) '.$ErrMsg);
1698
$Loc->OnFrmInfo = false;
1701
$Loc->OnFrmArg[3] = &$this; // bugs.php.net/51174
1703
if ($Loc->OnFrmInfo !== false) {
1704
$Loc->OnFrmArg[1] = &$CurrVal;
1705
if (isset($Loc->PrmLst['subtpl'])) {
1706
$this->meth_Misc_ChangeMode(true,$Loc,$CurrVal);
1707
call_user_func_array($Loc->OnFrmInfo,$Loc->OnFrmArg);
1708
$this->meth_Misc_ChangeMode(false,$Loc,$CurrVal);
1709
$Loc->ConvProtect = false;
1710
$Loc->ConvStr = false;
1712
call_user_func_array($Loc->OnFrmInfo,$Loc->OnFrmArg);
1717
if ($Loc->FirstMerge) {
1718
if (isset($Loc->PrmLst['frm'])) {
1719
$Loc->ConvMode = 0; // Frm
1720
$Loc->ConvProtect = false;
1722
// Analyze parameter 'strconv'
1723
if (isset($Loc->PrmLst['strconv'])) {
1724
$this->meth_Conv_Prepare($Loc, $Loc->PrmLst['strconv']);
1725
} elseif (isset($Loc->PrmLst['htmlconv'])) { // compatibility
1726
$this->meth_Conv_Prepare($Loc, $Loc->PrmLst['htmlconv']);
1728
if ($this->Charset===false) $Loc->ConvStr = false; // No conversion
1730
// Analyze parameter 'protect'
1731
if (isset($Loc->PrmLst['protect'])) {
1732
$x = strtolower($Loc->PrmLst['protect']);
1734
$Loc->ConvProtect = false;
1735
} elseif ($x==='yes') {
1736
$Loc->ConvProtect = true;
1738
} elseif ($this->Protect===false) {
1739
$Loc->ConvProtect = false;
1742
if ($Loc->Ope = isset($Loc->PrmLst['ope'])) {
1743
$OpeLst = explode(',',$Loc->PrmLst['ope']);
1744
$Loc->OpeAct = array();
1745
$Loc->OpeArg = array();
1746
$Loc->OpeUtf8 = false;
1747
foreach ($OpeLst as $i=>$ope) {
1748
if ($ope==='list') {
1749
$Loc->OpeAct[$i] = 1;
1750
$Loc->OpePrm[$i] = (isset($Loc->PrmLst['valsep'])) ? $Loc->PrmLst['valsep'] : ',';
1751
if (($Loc->ConvMode===1) && $Loc->ConvStr) $Loc->ConvMode = -1; // special mode for item list conversion
1752
} elseif ($ope==='minv') {
1753
$Loc->OpeAct[$i] = 11;
1754
$Loc->MSave = $Loc->MagnetId;
1755
} elseif ($ope==='attbool') { // this operation key is set when a loc is cached with paremeter atttrue
1756
$Loc->OpeAct[$i] = 14;
1757
} elseif ($ope==='utf8') { $Loc->OpeUtf8 = true;
1758
} elseif ($ope==='upper') { $Loc->OpeAct[$i] = 15;
1759
} elseif ($ope==='lower') { $Loc->OpeAct[$i] = 16;
1760
} elseif ($ope==='upper1') { $Loc->OpeAct[$i] = 17;
1761
} elseif ($ope==='upperw') { $Loc->OpeAct[$i] = 18;
1763
$x = substr($ope,0,4);
1765
$Loc->OpeAct[$i] = (isset($Loc->PrmLst['maxhtml'])) ? 2 : 3;
1766
if (isset($Loc->PrmLst['maxutf8'])) $Loc->OpeUtf8 = true;
1767
$Loc->OpePrm[$i] = intval(trim(substr($ope,4)));
1768
$Loc->OpeEnd = (isset($Loc->PrmLst['maxend'])) ? $Loc->PrmLst['maxend'] : '...';
1769
if ($Loc->OpePrm[$i]<=0) $Loc->Ope = false;
1770
} elseif ($x==='mod:') {$Loc->OpeAct[$i] = 5; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4));
1771
} elseif ($x==='add:') {$Loc->OpeAct[$i] = 6; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4));
1772
} elseif ($x==='mul:') {$Loc->OpeAct[$i] = 7; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4));
1773
} elseif ($x==='div:') {$Loc->OpeAct[$i] = 8; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4));
1774
} elseif ($x==='mok:') {$Loc->OpeAct[$i] = 9; $Loc->OpeMOK[] = trim(substr($ope,4)); $Loc->MSave = $Loc->MagnetId;
1775
} elseif ($x==='mko:') {$Loc->OpeAct[$i] =10; $Loc->OpeMKO[] = trim(substr($ope,4)); $Loc->MSave = $Loc->MagnetId;
1776
} elseif ($x==='nif:') {$Loc->OpeAct[$i] =12; $Loc->OpePrm[$i] = trim(substr($ope,4));
1777
} elseif ($x==='msk:') {$Loc->OpeAct[$i] =13; $Loc->OpePrm[$i] = trim(substr($ope,4));
1778
} elseif (isset($this->_piOnOperation)) {
1779
$Loc->OpeAct[$i] = 0;
1780
$Loc->OpePrm[$i] = $ope;
1781
$Loc->OpeArg[$i] = array($Loc->FullName,&$CurrVal,&$Loc->PrmLst,&$Txt,$Loc->PosBeg,$Loc->PosEnd,&$Loc);
1782
$Loc->PrmLst['_ope'] = $Loc->PrmLst['ope'];
1783
} elseif (!isset($Loc->PrmLst['noerr'])) {
1784
$this->meth_Misc_Alert($Loc,'parameter ope doesn\'t support value \''.$ope.'\'.',true);
1789
$Loc->FirstMerge = false;
1791
$ConvProtect = $Loc->ConvProtect;
1794
if ($this->_piOnFrm_Ok) {
1795
if (isset($Loc->OnFrmArgPi)) {
1796
$Loc->OnFrmArgPi[1] = &$CurrVal;
1797
$Loc->OnFrmArgPi[3] = &$this; // bugs.php.net/51174
1799
$Loc->OnFrmArgPi = array($Loc->FullName,&$CurrVal,&$Loc->PrmLst,&$this);
1801
$this->meth_PlugIn_RunAll($this->_piOnFormat,$Loc->OnFrmArgPi);
1806
foreach ($Loc->OpeAct as $i=>$ope) {
1809
$Loc->PrmLst['ope'] = $Loc->OpePrm[$i]; // for compatibility
1810
$OpeArg = &$Loc->OpeArg[$i];
1811
$OpeArg[1] = &$CurrVal; $OpeArg[3] = &$Txt;
1812
if (!$this->meth_PlugIn_RunAll($this->_piOnOperation,$OpeArg)) {
1813
$Loc->PosNext = $Loc->PosBeg + 1; // +1 in order to avoid infinit loop
1814
return $Loc->PosNext;
1818
if ($Loc->ConvMode===-1) {
1819
if (is_array($CurrVal)) {
1820
foreach ($CurrVal as $k=>$v) {
1821
$v = $this->meth_Misc_ToStr($v);
1822
$this->meth_Conv_Str($v,$Loc->ConvBr);
1825
$CurrVal = implode($Loc->OpePrm[$i],$CurrVal);
1827
$CurrVal = $this->meth_Misc_ToStr($CurrVal);
1828
$this->meth_Conv_Str($CurrVal,$Loc->ConvBr);
1831
if (is_array($CurrVal)) $CurrVal = implode($Loc->OpePrm[$i],$CurrVal);
1835
$x = $this->meth_Misc_ToStr($CurrVal);
1836
if (strlen($x)>$Loc->OpePrm[$i]) {
1837
$this->f_Xml_Max($x,$Loc->OpePrm[$i],$Loc->OpeEnd);
1841
$x = $this->meth_Misc_ToStr($CurrVal);
1842
if (strlen($x)>$Loc->OpePrm[$i]) {
1843
if ($Loc->OpeUtf8) {
1844
$CurrVal = mb_substr($x,0,$Loc->OpePrm[$i],'UTF-8').$Loc->OpeEnd;
1846
$CurrVal = substr($x,0,$Loc->OpePrm[$i]).$Loc->OpeEnd;
1850
case 5: $CurrVal = ('0'+$CurrVal) % $Loc->OpePrm[$i]; break;
1851
case 6: $CurrVal = ('0'+$CurrVal) + $Loc->OpePrm[$i]; break;
1852
case 7: $CurrVal = ('0'+$CurrVal) * $Loc->OpePrm[$i]; break;
1853
case 8: $CurrVal = ('0'+$CurrVal) / $Loc->OpePrm[$i]; break;
1856
$CurrVal = (in_array($this->meth_Misc_ToStr($CurrVal),$Loc->OpeMOK)) ? ' ' : '';
1858
$CurrVal = (in_array($this->meth_Misc_ToStr($CurrVal),$Loc->OpeMKO)) ? '' : ' ';
1861
if ($this->meth_Misc_ToStr($CurrVal)==='') {
1862
if ($Loc->MagnetId===0) $Loc->MagnetId = $Loc->MSave;
1864
if ($Loc->MagnetId!==0) {
1865
$Loc->MSave = $Loc->MagnetId;
1871
case 12: if ($this->meth_Misc_ToStr($CurrVal)===$Loc->OpePrm[$i]) $CurrVal = ''; break;
1872
case 13: $CurrVal = str_replace('*',$CurrVal,$Loc->OpePrm[$i]); break;
1873
case 14: $CurrVal = self::f_Loc_AttBoolean($CurrVal, $Loc->PrmLst['atttrue'], $Loc->AttName); break;
1874
case 15: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_UPPER, 'UTF-8') : strtoupper($CurrVal); break;
1875
case 16: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_LOWER, 'UTF-8') : strtolower($CurrVal); break;
1876
case 17: $CurrVal = ucfirst($CurrVal); break;
1877
case 18: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_TITLE, 'UTF-8') : ucwords(strtolower($CurrVal)); break;
1882
// String conversion or format
1883
if ($Loc->ConvMode===1) { // Usual string conversion
1884
$CurrVal = $this->meth_Misc_ToStr($CurrVal);
1885
if ($Loc->ConvStr) $this->meth_Conv_Str($CurrVal,$Loc->ConvBr);
1886
} elseif ($Loc->ConvMode===0) { // Format
1887
$CurrVal = $this->meth_Misc_Format($CurrVal,$Loc->PrmLst);
1888
} elseif ($Loc->ConvMode===2) { // Special string conversion
1889
$CurrVal = $this->meth_Misc_ToStr($CurrVal);
1890
if ($Loc->ConvStr) $this->meth_Conv_Str($CurrVal,$Loc->ConvBr);
1891
if ($Loc->ConvEsc) $CurrVal = str_replace('\'','\'\'',$CurrVal);
1896
$pos = strpos($CurrVal,$check);
1897
if ($pos!==false) $CurrVal = substr_replace($CurrVal,$nbsp,$pos,1);
1898
} while ($pos!==false);
1901
$CurrVal = addslashes($CurrVal); // apply to ('), ("), (\) and (null)
1902
$CurrVal = str_replace(array("\n","\r","\t"),array('\n','\r','\t'),$CurrVal);
1904
if ($Loc->ConvUrl) $CurrVal = urlencode($CurrVal);
1905
if ($Loc->ConvUtf8) $CurrVal = iconv('ISO-8859-1', 'UTF-8', $CurrVal);
1908
// if/then/else process, there may be several if/then
1909
if ($Loc->PrmIfNbr) {
1912
while ($i!==false) {
1913
if ($Loc->PrmIfVar[$i]) $Loc->PrmIfVar[$i] = $this->meth_Merge_AutoVar($Loc->PrmIf[$i],true);
1914
$x = str_replace($this->_ChrVal,$CurrVal,$Loc->PrmIf[$i]);
1915
if ($this->f_Misc_CheckCondition($x)) {
1916
if (isset($Loc->PrmThen[$i])) {
1917
if ($Loc->PrmThenVar[$i]) $Loc->PrmThenVar[$i] = $this->meth_Merge_AutoVar($Loc->PrmThen[$i],true);
1918
$z = $Loc->PrmThen[$i];
1923
if ($i>$Loc->PrmIfNbr) {
1924
if (isset($Loc->PrmLst['else'])) {
1925
if ($Loc->PrmElseVar) $Loc->PrmElseVar = $this->meth_Merge_AutoVar($Loc->PrmLst['else'],true);
1926
$z =$Loc->PrmLst['else'];
1934
$CurrVal = str_replace($this->_ChrOpen,$this->_ChrProtect,$CurrVal); // TBS protection
1935
$ConvProtect = false;
1937
$CurrVal = str_replace($this->_ChrVal,$CurrVal,$z);
1941
$IsTpl = false; // Indicates is $CurrVal is a sub-template
1943
if (isset($Loc->PrmLst['file'])) {
1944
$x = $Loc->PrmLst['file'];
1945
if ($x===true) $x = $CurrVal;
1946
$this->meth_Merge_AutoVar($x,false);
1947
$x = trim(str_replace($this->_ChrVal,$CurrVal,$x));
1950
if ($this->f_Misc_GetFile($CurrVal, $x, $this->_LastFile, $this->IncludePath)) {
1951
$this->meth_Locator_PartAndRename($CurrVal, $Loc->PrmLst);
1954
if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'the file \''.$x.'\' given by parameter file is not found or not readable.',true);
1956
$ConvProtect = false;
1960
if (isset($Loc->PrmLst['script'])) {// Include external PHP script
1961
$x = $Loc->PrmLst['script'];
1962
if ($this->ScriptsAllowed) {
1963
if ($x===true) $x = $CurrVal;
1964
$this->meth_Merge_AutoVar($x,false);
1965
$x = trim(str_replace($this->_ChrVal,$CurrVal,$x));
1966
if (basename($x) == basename($this->_LastFile)) {
1967
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);
1971
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);
1975
$this->_Subscript = $x;
1976
$this->CurrPrm = &$Loc->PrmLst;
1977
$sub = isset($Loc->PrmLst['subtpl']);
1978
if ($sub) $this->meth_Misc_ChangeMode(true,$Loc,$CurrVal);
1979
if ($this->meth_Misc_RunSubscript($CurrVal,$Loc->PrmLst)===false) {
1980
if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'the file \''.$x.'\' given by parameter script is not found or not readable.',true);
1982
if ($sub) $this->meth_Misc_ChangeMode(false,$Loc,$CurrVal);
1983
$this->meth_Locator_PartAndRename($CurrVal, $Loc->PrmLst);
1985
unset($this->CurrPrm);
1986
$ConvProtect = false;
1990
if (isset($Loc->PrmLst['att'])) {
1991
$this->f_Xml_AttFind($Txt,$Loc,true,$this->AttDelim);
1992
if (isset($Loc->PrmLst['atttrue'])) {
1993
$CurrVal = self::f_Loc_AttBoolean($CurrVal, $Loc->PrmLst['atttrue'], $Loc->AttName);
1994
$Loc->PrmLst['magnet'] = '#';
1998
// Case when it's an empty string
1999
if ($CurrVal==='') {
2001
if ($Loc->MagnetId===false) {
2002
if (isset($Loc->PrmLst['.'])) {
2003
$Loc->MagnetId = -1;
2004
} elseif (isset($Loc->PrmLst['ifempty'])) {
2005
$Loc->MagnetId = -2;
2006
} elseif (isset($Loc->PrmLst['magnet'])) {
2008
$Loc->PosBeg0 = $Loc->PosBeg;
2009
$Loc->PosEnd0 = $Loc->PosEnd;
2010
if ($Loc->PrmLst['magnet']==='#') {
2011
if (!isset($Loc->AttBeg)) {
2012
$Loc->PrmLst['att'] = '.';
2013
// no moving because att info would be modified and thus become wrong regarding to the eventually cached source
2014
$this->f_Xml_AttFind($Txt,$Loc,false,$this->AttDelim);
2016
if (isset($Loc->AttBeg)) {
2017
$Loc->MagnetId = -3;
2019
$this->meth_Misc_Alert($Loc,'parameter \'magnet=#\' cannot be processed because the corresponding attribute is not found.',true);
2021
} elseif (isset($Loc->PrmLst['mtype'])) {
2022
switch ($Loc->PrmLst['mtype']) {
2023
case 'm+m': $Loc->MagnetId = 2; break;
2024
case 'm*': $Loc->MagnetId = 3; break;
2025
case '*m': $Loc->MagnetId = 4; break;
2028
} elseif (isset($Loc->PrmLst['attadd'])) {
2029
// In order to delete extra space
2030
$Loc->PosBeg0 = $Loc->PosBeg;
2031
$Loc->PosEnd0 = $Loc->PosEnd;
2038
switch ($Loc->MagnetId) {
2040
case -1: $CurrVal = ' '; break; // Enables to avoid null cells in HTML tables
2041
case -2: $CurrVal = $Loc->PrmLst['ifempty']; break;
2044
$Loc->Enlarged = true;
2045
$Loc->PosBeg = ($Txt[$Loc->AttBeg-1]===' ') ? $Loc->AttBeg-1 : $Loc->AttBeg;
2046
$Loc->PosEnd = $Loc->AttEnd;
2049
$Loc->Enlarged = true;
2050
$this->f_Loc_EnlargeToTag($Txt,$Loc,$Loc->PrmLst['magnet'],false);
2053
$Loc->Enlarged = true;
2054
$CurrVal = $this->f_Loc_EnlargeToTag($Txt,$Loc,$Loc->PrmLst['magnet'],true);
2057
$Loc->Enlarged = true;
2058
$Loc2 = $this->f_Xml_FindTag($Txt,$Loc->PrmLst['magnet'],true,$Loc->PosBeg,false,false,false);
2059
if ($Loc2!==false) {
2060
$Loc->PosBeg = $Loc2->PosBeg;
2061
if ($Loc->PosEnd<$Loc2->PosEnd) $Loc->PosEnd = $Loc2->PosEnd;
2065
$Loc->Enlarged = true;
2066
$Loc2 = $this->f_Xml_FindTag($Txt,$Loc->PrmLst['magnet'],true,$Loc->PosBeg,true,false,false);
2067
if ($Loc2!==false) $Loc->PosEnd = $Loc2->PosEnd;
2070
$Loc->Enlarged = true;
2071
if (substr($Txt,$Loc->PosBeg-1,1)==' ') $Loc->PosBeg--;
2074
$NewEnd = $Loc->PosBeg; // Useful when mtype='m+m'
2077
if ($ConvProtect) $CurrVal = str_replace($this->_ChrOpen,$this->_ChrProtect,$CurrVal); // TBS protection
2078
$NewEnd = $Loc->PosBeg + ($IsTpl ? 0 : strlen($CurrVal));
2082
$Txt = substr_replace($Txt,$CurrVal,$Loc->PosBeg,$Loc->PosEnd-$Loc->PosBeg+1);
2084
$Loc->PosNext = $NewEnd;
2085
return $NewEnd; // Return the new end position of the field
2090
* Return the first block locator just after the PosBeg position
2092
* @param integer $Mode
2093
* 1 : Merge_Auto => doesn't save $Loc->BlockSrc, save the bounds of TBS Def tags instead, return also fields
2094
* 2 : FindBlockLst or GetBlockSource => save $Loc->BlockSrc without TBS Def tags
2095
* 3 : GetBlockSource => save $Loc->BlockSrc with TBS Def tags
2097
function meth_Locator_FindBlockNext(&$Txt,$BlockName,$PosBeg,$ChrSub,$Mode,&$P1,&$FieldBefore) {
2100
$FirstField = false;
2101
// Search for the first tag with parameter "block"
2102
while ($SearchDef && ($Loc = $this->meth_Locator_FindTbs($Txt,$BlockName,$PosBeg,$ChrSub))) {
2103
if (isset($Loc->PrmLst['block'])) {
2104
if (isset($Loc->PrmLst['p1'])) {
2105
if ($P1) return false;
2108
$Block = $Loc->PrmLst['block'];
2110
} elseif ($Mode===1) {
2112
} elseif ($FirstField===false) {
2115
$PosBeg = $Loc->PosEnd;
2119
if ($FirstField!==false) $FieldBefore = true;
2123
$Loc->PosDefBeg = -1;
2125
if ($Block==='begin') { // Block definied using begin/end
2127
if (($FirstField!==false) && ($FirstField->PosEnd<$Loc->PosBeg)) $FieldBefore = true;
2130
while ($Loc2 = $this->meth_Locator_FindTbs($Txt,$BlockName,$PosBeg,$ChrSub)) {
2131
if (isset($Loc2->PrmLst['block'])) {
2132
switch ($Loc2->PrmLst['block']) {
2133
case 'end': $Opened--; break;
2134
case 'begin': $Opened++; break;
2138
$Loc->PosBeg2 = $Loc2->PosBeg;
2139
$Loc->PosEnd2 = $Loc2->PosEnd;
2142
$Loc->BlockSrc = substr($Txt,$Loc->PosEnd+1,$Loc2->PosBeg-$Loc->PosEnd-1);
2144
$Loc->BlockSrc = substr($Txt,$Loc->PosBeg,$Loc2->PosEnd-$Loc->PosBeg+1);
2146
$Loc->PosEnd = $Loc2->PosEnd;
2148
$Loc->BlockFound = true;
2152
$PosBeg = $Loc2->PosEnd;
2155
return $this->meth_Misc_Alert($Loc,'a least one tag with parameter \'block=end\' is missing.',false,'in block\'s definition');
2160
$Loc->PosBeg2 = false;
2162
$beg = $Loc->PosBeg;
2163
$end = $Loc->PosEnd;
2164
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');
2165
if ($Loc->SubOk || ($Mode===3)) {
2166
$Loc->BlockSrc = substr($Txt,$Loc->PosBeg,$Loc->PosEnd-$Loc->PosBeg+1);
2167
$Loc->PosDefBeg = $beg - $Loc->PosBeg;
2168
$Loc->PosDefEnd = $end - $Loc->PosBeg;
2170
$Loc->BlockSrc = substr($Txt,$Loc->PosBeg,$beg-$Loc->PosBeg).substr($Txt,$end+1,$Loc->PosEnd-$end);
2174
$Loc->BlockFound = true;
2175
if (($FirstField!==false) && ($FirstField->PosEnd<$Loc->PosBeg)) $FieldBefore = true;
2176
return $Loc; // methods return by ref by default
2180
function meth_Locator_PartAndRename(&$CurrVal, &$PrmLst) {
2183
if (isset($PrmLst['store'])) {
2184
$storename = (isset($PrmLst['storename'])) ? $PrmLst['storename'] : 'default';
2185
if (!isset($this->TplStore[$storename])) $this->TplStore[$storename] = '';
2186
$this->TplStore[$storename] .= $this->f_Xml_GetPart($CurrVal, $PrmLst['store'], false);
2190
if (isset($PrmLst['getpart'])) {
2191
$part = $PrmLst['getpart'];
2192
} elseif (isset($PrmLst['getbody'])) {
2193
$part = $PrmLst['getbody'];
2198
$CurrVal = $this->f_Xml_GetPart($CurrVal, $part, true);
2201
// Rename or delete TBS tags names
2202
if (isset($PrmLst['rename'])) {
2204
$Replace = $PrmLst['rename'];
2206
if (is_string($Replace)) $Replace = explode(',',$Replace);
2207
foreach ($Replace as $x) {
2208
if (is_string($x)) $x = explode('=', $x);
2216
$this->meth_Merge_Block($CurrVal, $old, $s, $q, false, false, false);
2218
$old = $this->_ChrOpen.$old;
2219
$old = array($old.'.', $old.' ', $old.';', $old.$this->_ChrClose);
2220
$new = $this->_ChrOpen.$new;
2221
$new = array($new.'.', $new.' ', $new.';', $new.$this->_ChrClose);
2222
$CurrVal = str_replace($old,$new,$CurrVal);
2233
* Retrieve the list of all sections and their finition for a given block name.
2235
* @param string $Txt
2236
* @param string $BlockName
2237
* @param integer $Pos
2238
* @param string|false $SpePrm The parameter's name for Special section (used for navigation bar), or false if none.
2242
function meth_Locator_FindBlockLst(&$Txt,$BlockName,$Pos,$SpePrm) {
2243
// Return a locator object covering all block definitions, even if there is no block definition found.
2245
$LocR = new clsTbsLocator;
2247
$LocR->FieldOutside = false;
2248
$LocR->FOStop = false;
2249
$LocR->BDefLst = array();
2251
$LocR->NoData = false;
2252
$LocR->Special = false;
2253
$LocR->HeaderFound = false;
2254
$LocR->FooterFound = false;
2255
$LocR->SerialEmpty = false;
2256
$LocR->GrpBreak = false; // Only for plug-ins
2258
$LocR->BoundFound = false;
2259
$LocR->CheckNext = false;
2260
$LocR->CheckPrev = false;
2262
$LocR->WhenFound = false;
2263
$LocR->WhenDefault = false;
2265
$LocR->SectionNbr = 0; // Normal sections
2266
$LocR->SectionLst = array(); // 1 to SectionNbr
2269
$ParentLst = array();
2274
if ($BlockName==='') {
2277
$Loc = $this->meth_Locator_FindBlockNext($Txt,$BlockName,$Pos,'.',2,$LocR->P1,$LocR->FieldOutside);
2282
if ($Pid>0) { // parentgrp mode => disconnect $Txt from the source
2283
$Parent = &$ParentLst[$Pid];
2285
$Txt = &$Parent->Txt;
2286
if ($LocR->BlockFound) {
2287
// Redefine the Header block
2288
$Parent->Src = substr($Src,0,$LocR->PosBeg);
2289
// Add a Footer block
2290
$BDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,substr($Src,$LocR->PosEnd+1),$Parent->Prm,true);
2291
$this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'F',$Parent->Fld,'parentgrp');
2293
// Now go down to previous level
2294
$Pos = $Parent->Pos;
2295
$LocR->PosBeg = $Parent->Beg;
2296
$LocR->PosEnd = $Parent->End;
2297
$LocR->BlockFound = true;
2299
unset($ParentLst[$Pid]);
2306
$Pos = $Loc->PosEnd;
2308
// Define the block limits
2309
if ($LocR->BlockFound) {
2310
if ( $LocR->PosBeg > $Loc->PosBeg ) $LocR->PosBeg = $Loc->PosBeg;
2311
if ( $LocR->PosEnd < $Loc->PosEnd ) $LocR->PosEnd = $Loc->PosEnd;
2313
$LocR->BlockFound = true;
2314
$LocR->PosBeg = $Loc->PosBeg;
2315
$LocR->PosEnd = $Loc->PosEnd;
2318
// Merge block parameters
2319
if (count($Loc->PrmLst)>0) $LocR->PrmLst = array_merge($LocR->PrmLst,$Loc->PrmLst);
2321
// Force dynamic parameter to be cachable
2322
if ($Loc->PosDefBeg>=0) {
2323
$dynprm = array('when','headergrp','footergrp','parentgrp');
2324
foreach($dynprm as $dp) {
2326
if ((isset($Loc->PrmLst[$dp])) && (strpos($Loc->PrmLst[$dp],$this->_ChrOpen.$BlockName)!==false)) {
2329
$len = $Loc->PosDefEnd - $Loc->PosDefBeg + 1;
2330
$x = substr($Loc->BlockSrc,$Loc->PosDefBeg,$len);
2332
$x = str_replace($Loc->PrmLst[$dp],'',$x);
2334
if ($n>0) $Loc->BlockSrc = substr_replace($Loc->BlockSrc,$x,$Loc->PosDefBeg,$len);
2337
// Save the block and cache its tags
2338
$IsParentGrp = isset($Loc->PrmLst['parentgrp']);
2339
$BDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Loc->BlockSrc,$Loc->PrmLst,!$IsParentGrp);
2343
$lst = array('firstingrp'=>1, 'lastingrp'=>2, 'singleingrp'=>3); // 1=prev, 2=next, 3=1+2=prev+next
2344
foreach ($lst as $prm => $chk) {
2345
if (isset($Loc->PrmLst[$prm])) {
2351
// Add the text in the list of blocks
2352
if (isset($Loc->PrmLst['nodata'])) { // Nodata section
2353
$LocR->NoData = $BDef;
2354
} elseif (($SpePrm!==false) && isset($Loc->PrmLst[$SpePrm])) { // Special section (used for navigation bar)
2355
$LocR->Special = $BDef;
2356
} elseif (isset($Loc->PrmLst['when'])) {
2357
if ($LocR->WhenFound===false) {
2358
$LocR->WhenFound = true;
2359
$LocR->WhenSeveral = false;
2361
$LocR->WhenLst = array();
2363
$this->meth_Merge_AutoVar($Loc->PrmLst['when'],false);
2364
$BDef->WhenCond = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Loc->PrmLst['when'],array(),true);
2365
$BDef->WhenBeforeNS = ($LocR->SectionNbr===0); // position of the When section relativley to the Normal Section
2366
$i = ++$LocR->WhenNbr;
2367
$LocR->WhenLst[$i] = $BDef;
2368
if (isset($Loc->PrmLst['several'])) $LocR->WhenSeveral = true;
2369
} elseif (isset($Loc->PrmLst['default'])) {
2370
$LocR->WhenDefault = $BDef;
2371
$LocR->WhenDefaultBeforeNS = ($LocR->SectionNbr===0);
2372
} elseif (isset($Loc->PrmLst['headergrp'])) {
2373
$this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'H',$Loc->PrmLst['headergrp'],'headergrp');
2374
} elseif (isset($Loc->PrmLst['footergrp'])) {
2375
$this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'F',$Loc->PrmLst['footergrp'],'footergrp');
2376
} elseif (isset($Loc->PrmLst['splittergrp'])) {
2377
$this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'S',$Loc->PrmLst['splittergrp'],'splittergrp');
2378
} elseif ($IsParentGrp) {
2379
$this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'H',$Loc->PrmLst['parentgrp'],'parentgrp');
2380
$BDef->Fld = $Loc->PrmLst['parentgrp'];
2383
$BDef->Beg = $LocR->PosBeg;
2384
$BDef->End = $LocR->PosEnd;
2386
$ParentLst[$Pid] = $BDef;
2388
$Pos = $Loc->PosDefBeg + 1;
2389
$LocR->BlockFound = false;
2390
$LocR->PosBeg = false;
2391
$LocR->PosEnd = false;
2392
} elseif (isset($Loc->PrmLst['serial'])) {
2393
// Section with serial subsections
2394
$SrSrc = &$BDef->Src;
2395
// Search the empty item
2396
if ($LocR->SerialEmpty===false) {
2397
$SrName = $BlockName.'_0';
2399
$SrLoc = $this->meth_Locator_FindBlockNext($SrSrc,$SrName,0,'.',2,$x,$x);
2400
if ($SrLoc!==false) {
2401
$LocR->SerialEmpty = $SrLoc->BlockSrc;
2402
$SrSrc = substr_replace($SrSrc,'',$SrLoc->PosBeg,$SrLoc->PosEnd-$SrLoc->PosBeg+1);
2405
$SrName = $BlockName.'_1';
2407
$SrLoc = $this->meth_Locator_FindBlockNext($SrSrc,$SrName,0,'.',2,$x,$x);
2408
if ($SrLoc!==false) {
2411
// Save previous subsection
2412
$SrBDef = $this->meth_Locator_SectionNewBDef($LocR,$SrName,$SrLoc->BlockSrc,$SrLoc->PrmLst,true);
2413
$SrBDef->SrBeg = $SrLoc->PosBeg;
2414
$SrBDef->SrLen = $SrLoc->PosEnd - $SrLoc->PosBeg + 1;
2415
$SrBDef->SrTxt = false;
2416
$BDef->SrBDefLst[$SrId] = $SrBDef;
2418
$BDef->SrBDefOrdered[$SrId] = $SrBDef;
2420
while (($i>1) && ($SrBDef->SrBeg<$BDef->SrBDefOrdered[$SrId-1]->SrBeg)) {
2421
$BDef->SrBDefOrdered[$i] = $BDef->SrBDefOrdered[$i-1];
2422
$BDef->SrBDefOrdered[$i-1] = $SrBDef;
2425
// Search next subsection
2427
$SrName = $BlockName.'_'.$SrId;
2429
$SrLoc = $this->meth_Locator_FindBlockNext($SrSrc,$SrName,0,'.',2,$x,$x);
2430
} while ($SrLoc!==false);
2431
$BDef->SrBDefNbr = $SrId-1;
2432
$BDef->IsSerial = true;
2433
$i = ++$LocR->SectionNbr;
2434
$LocR->SectionLst[$i] = $BDef;
2436
} elseif (isset($Loc->PrmLst['parallel'])) {
2437
$BlockLst = $this->meth_Locator_FindParallel($Txt, $Loc->PosBeg, $Loc->PosEnd, $Loc->PrmLst['parallel']);
2440
foreach ($BlockLst as $i => $Blk) {
2441
if ($Blk['IsRef']) {
2444
$PrBDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Blk['Src'],array(),true);
2446
$PrBDef->PosBeg = $Blk['PosBeg'];
2447
$PrBDef->PosEnd = $Blk['PosEnd'];
2448
$i = ++$LocR->SectionNbr;
2449
$LocR->SectionLst[$i] = $PrBDef;
2451
$LocR->PosBeg = $BlockLst[0]['PosBeg'];
2452
$LocR->PosEnd = $BlockLst[$LocR->SectionNbr-1]['PosEnd'];
2454
} elseif ($BoundPrm !== false) {
2455
$BDef->BoundExpr = $this->meth_Locator_MakeBDefFromField($LocR,$BlockName,$Loc->PrmLst[$BoundPrm],$BoundPrm);
2456
$BDef->ValCurr = null;
2457
$BDef->ValNext = null;
2458
$BDef->CheckPrev = (($BoundChk & 1) != 0); // bitwise check
2459
if ($BDef->CheckPrev) {
2460
$LocR->CheckPrev = true;
2461
$LocR->ValPrev = null;
2463
$BDef->CheckNext = (($BoundChk & 2) != 0); // bitwise check
2464
if ($BDef->CheckNext) {
2465
$LocR->CheckNext = true;
2466
$LocR->ValNext = null;
2468
if (!$LocR->BoundFound) {
2469
$LocR->BoundFound = true;
2470
$LocR->BoundLst = array();
2472
$LocR->BoundSingleNb = 0;
2474
if ($BoundChk == 3) {
2475
// Insert the singleingrp before all other types
2476
array_splice($LocR->BoundLst, $LocR->BoundSingleNb, 0, array($BDef));
2477
$LocR->BoundSingleNb++;
2479
// Insert other types at the end
2480
$LocR->BoundLst[] = $BDef;
2485
$i = ++$LocR->SectionNbr;
2486
$LocR->SectionLst[$i] = $BDef;
2491
} while ($Loc!==false);
2493
if ($LocR->WhenFound && ($LocR->SectionNbr===0)) {
2494
// Add a blank section if When is used without a normal section
2495
$BDef = $this->meth_Locator_SectionNewBDef($LocR,$BlockName,'',array(),false);
2496
$LocR->SectionNbr = 1;
2497
$LocR->SectionLst[1] = &$BDef;
2500
return $LocR; // methods return by ref by default
2504
function meth_Locator_FindParallel(&$Txt, $ZoneBeg, $ZoneEnd, $ConfId) {
2506
// Define configurations
2507
global $_TBS_ParallelLst;
2509
if ( ($ConfId=='tbs:table') && (!isset($_TBS_ParallelLst['tbs:table'])) ) {
2510
$_TBS_ParallelLst['tbs:table'] = array(
2511
'parent' => 'table',
2512
'ignore' => array('!--', 'caption', 'thead', 'tbody', 'tfoot'),
2514
'rows' => array('tr', 'colgroup'),
2515
'cells' => array('td'=>'colspan', 'th'=>'colspan', 'col'=>'span'),
2519
if (!isset($_TBS_ParallelLst[$ConfId])) return $this->meth_Misc_Alert("Parallel", "The configuration '$ConfId' is not found.");
2521
$conf = $_TBS_ParallelLst[$ConfId];
2523
$Parent = $conf['parent'];
2525
// Search parent bounds
2526
$par_o = self::f_Xml_FindTag($Txt,$Parent,true ,$ZoneBeg,false,1,false);
2527
if ($par_o===false) return $this->meth_Misc_Alert("Parallel", "The opening tag '$Parent' is not found.");
2529
$par_c = self::f_Xml_FindTag($Txt,$Parent,false,$ZoneBeg,true,-1,false);
2530
if ($par_c===false) return $this->meth_Misc_Alert("Parallel", "The closing tag '$Parent' is not found.");
2532
$SrcPOffset = $par_o->PosEnd + 1;
2533
$SrcP = substr($Txt, $SrcPOffset, $par_c->PosBeg - $SrcPOffset);
2535
// temporary variables
2544
$Loc = new clsTbsLocator;
2554
// Loop on entities inside the parent entity
2557
$mode_column = true;
2562
// Search for the next Row Opening tag
2563
while (self::f_Xml_GetNextEntityName($SrcP, $PosR, $tagR, $pRO, $p)) {
2565
$pROe = strpos($SrcP, '>', $p) + 1;
2566
$singleR = ($SrcP[$pROe-2] === '/');
2568
// If the tag is not a closing, a self-closing and has a name
2571
if (in_array($tagR, $conf['ignore'])) {
2572
// This tag must be ignored
2574
} elseif (isset($conf['cols'][$tagR])) {
2575
// Column definition that must be merged as a cell
2576
if ($mode_column === false) return $this->meth_Misc_Alert("Parallel", "There is a column definition ($tagR) after a row (".$Rows[$RowIdx-1]['tag'].").");
2577
if (isset($RowType['_column'])) {
2578
$RowType['_column']++;
2580
$RowType['_column'] = 1;
2582
$att = $conf['cols'][$tagR];
2583
$this->meth_Locator_FindParallelCol($SrcP, $PosR, $tagR, $pRO, $p, $SrcPOffset, $RowIdx, $ZoneBeg, $ZoneEnd, $att, $Loc, $Cells, $ColNum, $IsRef, $RefCellB, $RefCellE, $RefRow);
2585
} elseif (!$singleR) {
2587
// Search the Row Closing tag
2588
$locRE = self::f_Xml_FindTag($SrcP, $tagR, false, $pROe, true, -1, false);
2589
if ($locRE===false) return $this->meth_Misc_Alert("Parallel", "The row closing tag is not found. (tagR=$tagR, p=$p, pROe=$pROe)");
2592
$SrcR = substr($SrcP, $pROe, $locRE->PosBeg - $pROe);
2593
$SrcROffset = $SrcPOffset + $pROe;
2595
if (in_array($tagR, $conf['rows'])) {
2597
if ( $mode_column && isset($RowType['_column']) ) {
2598
$Rows[$RowIdx] = array('tag'=>'_column', 'cells' => $Cells, 'isref' => $IsRef, 'count' => $RowType['_column']);
2602
$mode_column = false;
2604
if (isset($RowType[$tagR])) {
2607
$RowType[$tagR] = 1;
2610
// Now we've got the row entity, we search for cell entities
2616
// Loop on Cell Opening tags
2617
while (self::f_Xml_GetNextEntityName($SrcR, $PosC, $tagC, $pCO, $p)) {
2618
if (isset($conf['cells'][$tagC]) ) {
2619
$att = $conf['cells'][$tagC];
2620
$this->meth_Locator_FindParallelCol($SrcR, $PosC, $tagC, $pCO, $p, $SrcROffset, $RowIdx, $ZoneBeg, $ZoneEnd, $att, $Loc, $Cells, $ColNum, $IsRef, $RefCellB, $RefCellE, $RefRow);
2626
$Rows[$RowIdx] = array('tag'=>$tagR, 'cells' => $Cells, 'isref' => $IsRef, 'count' => $RowType[$tagR]);
2631
$PosR = $locRE->PosEnd;
2644
$rMax = count($Rows) -1;
2645
foreach ($Rows as $r=>$Row) {
2646
$Cells = $Row['cells'];
2647
if (isset($Cells[$RefCellB]) && $Cells[$RefCellB]['IsBegin']) {
2648
if ( isset($Cells[$RefCellE]) && $Cells[$RefCellE]['IsEnd'] ) {
2649
$PosBeg = $Cells[$RefCellB]['PosBeg'];
2650
$PosEnd = $Cells[$RefCellE]['PosEnd'];
2651
$Blocks[$r] = array(
2652
'PosBeg' => $PosBeg,
2653
'PosEnd' => $PosEnd,
2654
'IsRef' => $Row['isref'],
2655
'Src' => substr($Txt, $PosBeg, $PosEnd - $PosBeg + 1),
2658
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)");
2661
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)");
2669
function meth_Locator_FindParallelCol($SrcR, &$PosC, $tagC, $pCO, $p, $SrcROffset, $RowIdx, $ZoneBeg, $ZoneEnd, &$att, &$Loc, &$Cells, &$ColNum, &$IsRef, &$RefCellB, &$RefCellE, &$RefRow) {
2674
$Loc->PrmLst = array();
2675
self::f_Loc_PrmRead($SrcR,$p,true,'\'"','<','>',$Loc,$pCOe,true);
2677
$singleC = ($SrcR[$pCOe-1] === '/');
2681
// Find the Cell Closing tag
2682
$locCE = self::f_Xml_FindTag($SrcR, $tagC, false, $pCOe, true, -1, false);
2683
if ($locCE===false) return $this->meth_Misc_Alert("Parallel", "The cell closing tag is not found. (pCOe=$pCOe)");
2684
$pCEe = $locCE->PosEnd;
2687
// Check the cell of reference
2688
$Width = (isset($Loc->PrmLst[$att])) ? intval($Loc->PrmLst[$att]) : 1;
2689
$ColNumE = $ColNum + $Width -1; // Ending Cell
2690
$PosBeg = $SrcROffset + $pCO;
2691
$PosEnd = $SrcROffset + $pCEe;
2693
if ( ($PosBeg <= $ZoneBeg) && ($ZoneBeg <= $PosEnd) && ($RefRow===false) ) {
2695
$RefCellB = $ColNum;
2699
if ( ($PosBeg <= $ZoneEnd) && ($ZoneEnd <= $PosEnd) ) {
2700
$RefCellE = $ColNum;
2706
//'_tagR' => $tagR, '_tagC' => $tagC, '_att' => $att, '_OnZone' => $OnZone, '_PrmLst' => $Loc->PrmLst, '_Offset' => $SrcROffset, '_Src' => substr($SrcR, $pCO, $locCE->PosEnd - $pCO + 1),
2707
'PosBeg' => $PosBeg,
2708
'PosEnd' => $PosEnd,
2709
'ColNum' => $ColNum,
2714
$Cells[$ColNum] = $Cell;
2716
// add a virtual column to say if its a ending
2717
if (!isset($Cells[$ColNumE])) $Cells[$ColNumE] = array('IsBegin' => false);
2719
$Cells[$ColNumE]['IsEnd'] = true;
2720
$Cells[$ColNumE]['PosEnd'] = $Cells[$ColNum]['PosEnd'];
2727
function meth_Merge_Block(&$Txt,$BlockLst,&$SrcId,&$Query,$SpePrm,$SpeRecNum,$QryPrms=false) {
2729
$BlockSave = $this->_CurrBlock;
2730
$this->_CurrBlock = $BlockLst;
2732
// Get source type and info
2733
$Src = new clsTbsDataSource;
2734
if (!$Src->DataPrepare($SrcId,$this)) {
2735
$this->_CurrBlock = $BlockSave;
2739
if (is_string($BlockLst)) $BlockLst = explode(',', $BlockLst);
2740
$BlockNbr = count($BlockLst);
2745
$ReturnData = false;
2748
while ($BlockId<$BlockNbr) {
2750
$RecSpe = 0; // Row with a special block's definition (used for the navigation bar)
2752
$this->_CurrBlock = trim($BlockLst[$BlockId]);
2753
if ($this->_CurrBlock==='*') {
2755
if ($Src->RecSaved===false) $Src->RecSaving = true;
2756
$this->_CurrBlock = '';
2760
$LocR = $this->meth_Locator_FindBlockLst($Txt,$this->_CurrBlock,0,$SpePrm);
2762
if ($LocR->BlockFound) {
2766
if ($LocR->Special!==false) $RecSpe = $SpeRecNum;
2768
if ($Src->OnDataPrm = isset($LocR->PrmLst['ondata'])) {
2769
$Src->OnDataPrmRef = $LocR->PrmLst['ondata'];
2770
if (isset($Src->OnDataPrmDone[$Src->OnDataPrmRef])) {
2771
$Src->OnDataPrm = false;
2774
if ($this->meth_Misc_UserFctCheck($Src->OnDataPrmRef,'f',$ErrMsg,$ErrMsg,true)) {
2775
$Src->OnDataOk = true;
2777
$LocR->FullName = $this->_CurrBlock;
2778
$Src->OnDataPrm = $this->meth_Misc_Alert($LocR,'(parameter ondata) '.$ErrMsg,false,'block');
2784
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
2785
if ($Src->RecSaved===false) $Src->RecSaving = true;
2786
} elseif (is_string($Query)) {
2787
$Src->RecSaved = false;
2788
unset($QueryZ); $QueryZ = ''.$Query;
2792
if (isset($LocR->PrmLst[$x])) {
2793
$QueryZ = str_replace('%p'.$i.'%',$LocR->PrmLst[$x],$QueryZ);
2798
} while ($i!==false);
2801
} elseif (($Src->RecSaved===false) && ($BlockNbr-$BlockId>1)) {
2802
$Src->RecSaving = true;
2809
// Open the recordset
2811
if ((!$LocR->BlockFound) && (!$LocR->FieldOutside)) {
2812
// Special case: return data without any block to merge
2814
if ($ReturnData && (!$Src->RecSaved)) {
2815
if ($Src->DataOpen($QueryZ,$QryPrms)) {
2816
do {$Src->DataFetch();} while ($Src->CurrRec!==false);
2821
$QueryOk = $Src->DataOpen($QueryZ,$QryPrms);
2823
if ($WasP1) { $WasP1 = false;} else {$LocR->FieldOutside = false;} // prevent from infinit loop
2830
if ($Src->Type===2) { // Special for Text merge
2831
if ($LocR->BlockFound) {
2832
$Txt = substr_replace($Txt,$Src->RecSet,$LocR->PosBeg,$LocR->PosEnd-$LocR->PosBeg+1);
2833
$Src->DataFetch(); // store data, may be needed for multiple blocks
2835
$Src->CurrRec = false;
2837
$Src->DataAlert('can\'t merge the block with a text value because the block definition is not found.');
2839
} elseif ($LocR->BlockFound===false) {
2840
$Src->DataFetch(); // Merge first record only
2841
} elseif (isset($LocR->PrmLst['parallel'])) {
2842
$this->meth_Merge_BlockParallel($Txt,$LocR,$Src);
2844
$this->meth_Merge_BlockSections($Txt,$LocR,$Src,$RecSpe);
2846
$Src->DataClose(); // Close the resource
2850
$NbrRecTot += $Src->RecNum;
2853
if ($LocR->FieldOutside) {
2855
$this->meth_Merge_FieldOutside($Txt,$Src->CurrRec,$Src->RecNum,$LocR->FOStop);
2862
$this->_CurrBlock = $BlockSave;
2864
return $Src->RecSet;
2867
return ($Nothing) ? false : $NbrRecTot;
2872
function meth_Merge_BlockParallel(&$Txt,&$LocR,&$Src) {
2878
$BlockRes = array();
2879
for ($i=1 ; $i<=$LocR->SectionNbr ; $i++) {
2881
// Add txt source between the BDefs
2882
$BlockRes[$i] = substr($Txt, $LocR->SectionLst[$i-1]->PosEnd + 1, $LocR->SectionLst[$i]->PosBeg - $LocR->SectionLst[$i-1]->PosEnd -1);
2888
while($Src->CurrRec!==false) {
2889
// Merge the current record with all sections
2890
for ($i=1 ; $i<=$LocR->SectionNbr ; $i++) {
2891
$SecDef = &$LocR->SectionLst[$i];
2892
$SecSrc = $this->meth_Merge_SectionNormal($SecDef,$Src);
2893
$BlockRes[$i] .= $SecSrc;
2899
$BlockRes = implode('', $BlockRes);
2900
$Txt = substr_replace($Txt,$BlockRes,$LocR->PosBeg,$LocR->PosEnd-$LocR->PosBeg+1);
2904
function meth_Merge_BlockSections(&$Txt,&$LocR,&$Src,&$RecSpe) {
2908
$SecOk = ($LocR->SectionNbr>0);
2910
$BlockRes = ''; // The result of the chained merged blocks
2915
if ($LocR->HeaderFound || $LocR->FooterFound) {
2918
if ($LocR->FooterFound) {
2919
$Src->PrevSave = true; // $Src->PrevRec will be saved then
2922
if ($LocR->CheckPrev) $Src->PrevSave = true;
2923
if ($LocR->CheckNext) $Src->NextSave = true;
2926
if ($this->_PlugIns_Ok) {
2927
if (isset($this->_piBeforeMergeBlock)) {
2928
$ArgLst = array(&$Txt,&$LocR->PosBeg,&$LocR->PosEnd,$LocR->PrmLst,&$Src,&$LocR);
2929
$this->meth_Plugin_RunAll($this->_piBeforeMergeBlock,$ArgLst);
2931
if (isset($this->_piOnMergeSection)) {
2932
$ArgLst = array(&$BlockRes,&$SecSrc);
2935
if ($GrpFound && isset($this->_piOnMergeGroup)) {
2936
$ArgLst2 = array(0,0,&$Src,&$LocR);
2944
while($Src->CurrRec!==false) {
2946
// Headers and Footers
2949
$brk_src = ''; // concatenated source to insert as of breaked groups (header and footer)
2950
if ($LocR->FooterFound) {
2952
for ($i=$LocR->FooterNbr;$i>=1;$i--) {
2953
$GrpDef = &$LocR->FooterDef[$i];
2954
$x = $this->meth_Merge_SectionNormal($GrpDef->FDef,$Src); // value of the group expression for the current record
2955
if ($Src->RecNum===1) {
2956
// no footer break on first record
2957
$GrpDef->PrevValue = $x;
2960
// default state for breaked group
2961
if ($GrpDef->AddLastGrp) {
2962
$brk_i = &$brk; // cascading breakings
2964
unset($brk_i); $brk_i = false; // independent breaking
2966
if (!$brk_i) $brk_i = !($GrpDef->PrevValue===$x);
2970
if ($piOMG) {$ArgLst2[0]=&$Src->PrevRec; $ArgLst2[1]=&$GrpDef; $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup,$ArgLst2);}
2971
if ($ok!==false) $brk_src = $this->meth_Merge_SectionNormal($GrpDef,$Src->PrevRec).$brk_src;
2972
$GrpDef->PrevValue = $x;
2977
if ($LocR->HeaderFound) {
2978
// Check if the current record breaks any header group
2979
$brk = ($Src->RecNum===1); // there is always a header break on first record
2980
for ($i=1;$i<=$LocR->HeaderNbr;$i++) {
2981
$GrpDef = &$LocR->HeaderDef[$i];
2982
$x = $this->meth_Merge_SectionNormal($GrpDef->FDef,$Src); // value of the group expression for the current record
2983
if (!$brk) $brk = !($GrpDef->PrevValue===$x); // cascading breakings
2986
if ($piOMG) {$ArgLst2[0]=&$Src; $ArgLst2[1]=&$GrpDef; $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup,$ArgLst2);}
2987
if ($ok!==false) $brk_src .= $this->meth_Merge_SectionNormal($GrpDef,$Src);
2988
$GrpDef->PrevValue = $x;
2991
$brk_any = ($brk_any || $brk);
2995
$BlockRes .= $this->meth_Merge_SectionSerial($SecDef,$SrId,$LocR);
2998
$BlockRes .= $brk_src;
3000
} // end of header and footer
3002
// Increment Section
3003
if (($IsSerial===false) && $SecOk) {
3005
if ($SecId>$LocR->SectionNbr) $SecId = 1;
3006
$SecDef = &$LocR->SectionLst[$SecId];
3007
$IsSerial = $SecDef->IsSerial;
3010
$SrNbr = $SecDef->SrBDefNbr;
3014
// Serial Mode Activation
3015
if ($IsSerial) { // Serial Merge
3017
$SrBDef = &$SecDef->SrBDefLst[$SrId];
3018
$SrBDef->SrTxt = $this->meth_Merge_SectionNormal($SrBDef,$Src);
3019
if ($SrId>=$SrNbr) {
3020
$SecSrc = $this->meth_Merge_SectionSerial($SecDef,$SrId,$LocR);
3021
$BlockRes .= $SecSrc;
3024
} else { // Classic merge
3026
// There is some normal sections
3027
if ($Src->RecNum===$RecSpe) {
3028
$SecDef = &$LocR->Special;
3029
} elseif ($LocR->BoundFound) {
3031
for ($i = 0 ; $i < $LocR->BoundNb ; $i++) {
3032
// all bounds must be tested in order to update next and prev values, but only the first found must be kept
3033
if ($this->meth_Merge_CheckBounds($LocR->BoundLst[$i],$Src)) {
3034
if ($first) $SecDef = &$LocR->BoundLst[$i];
3039
$SecSrc = $this->meth_Merge_SectionNormal($SecDef,$Src);
3041
// No normal section
3044
// Conditional blocks
3045
if ($LocR->WhenFound) {
3050
$WhenBDef = &$LocR->WhenLst[$i];
3051
$cond = $this->meth_Merge_SectionNormal($WhenBDef->WhenCond,$Src); // conditional expression for the current record
3052
if ($this->f_Misc_CheckCondition($cond)) {
3053
$x_when = $this->meth_Merge_SectionNormal($WhenBDef,$Src);
3054
$SecSrc = ($WhenBDef->WhenBeforeNS) ? $x_when.$SecSrc : $SecSrc.$x_when;
3056
if ($LocR->WhenSeveral===false) $continue = false;
3059
if ($i>$LocR->WhenNbr) $continue = false;
3060
} while ($continue);
3061
if (($found===false) && ($LocR->WhenDefault!==false)) {
3062
$x_when = $this->meth_Merge_SectionNormal($LocR->WhenDefault,$Src);
3063
$SecSrc = ($LocR->WhenDefaultBeforeNS) ? $x_when.$SecSrc : $SecSrc.$x_when;
3066
if ($piOMS) $this->meth_PlugIn_RunAll($this->_piOnMergeSection,$ArgLst);
3067
$BlockRes .= $SecSrc;
3073
} //--> while($CurrRec!==false) {
3075
// At this point, all data has been fetched.
3077
// Source to add after the last record
3080
// Serial: merge the extra the sub-blocks
3081
if ($IsSerial) $SecSrc .= $this->meth_Merge_SectionSerial($SecDef,$SrId,$LocR);
3083
// Add all footers after the last record
3084
if ($LocR->FooterFound) {
3085
if ($Src->RecNum>0) {
3086
for ($i=1;$i<=$LocR->FooterNbr;$i++) {
3087
$GrpDef = &$LocR->FooterDef[$i];
3088
if ($GrpDef->AddLastGrp) {
3090
if ($piOMG) {$ArgLst2[0]=&$Src->PrevRec; $ArgLst2[1]=&$GrpDef; $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup,$ArgLst2);}
3091
if ($ok!==false) $SecSrc .= $this->meth_Merge_SectionNormal($GrpDef,$Src->PrevRec);
3098
if ($Src->RecNum===0) {
3099
if ($LocR->NoData!==false) {
3100
$SecSrc = $LocR->NoData->Src;
3101
} elseif(isset($LocR->PrmLst['bmagnet'])) {
3102
$this->f_Loc_EnlargeToTag($Txt,$LocR,$LocR->PrmLst['bmagnet'],false);
3107
if ($piOMS && ($SecSrc!=='')) $this->meth_PlugIn_RunAll($this->_piOnMergeSection,$ArgLst);
3109
$BlockRes .= $SecSrc;
3112
if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterMergeBlock)) {
3113
$ArgLst = array(&$BlockRes,&$Src,&$LocR);
3114
$this->meth_PlugIn_RunAll($this->_piAfterMergeBlock,$ArgLst);
3118
$Txt = substr_replace($Txt,$BlockRes,$LocR->PosBeg,$LocR->PosEnd-$LocR->PosBeg+1);
3119
if ($LocR->P1) $LocR->FOStop = $LocR->PosBeg + strlen($BlockRes) -1;
3123
function meth_Merge_AutoVar(&$Txt,$ConvStr,$Id='var') {
3124
// Merge automatic fields with VarRef
3126
$Pref = &$this->VarPrefix;
3127
$PrefL = strlen($Pref);
3128
$PrefOk = ($PrefL>0);
3130
if ($ConvStr===false) {
3131
$Charset = $this->Charset;
3132
$this->Charset = false;
3135
// Then we scann all fields in the model
3138
while ($Loc = $this->meth_Locator_FindTbs($Txt,$Id,$Pos,'.')) {
3139
if ($Loc->SubNbr==0) $Loc->SubLst[0]=''; // In order to force error message
3140
if ($Loc->SubLst[0]==='') {
3141
$Pos = $this->meth_Merge_AutoSpe($Txt,$Loc);
3142
} elseif ($Loc->SubLst[0][0]==='~') {
3143
if (!isset($ObjOk)) $ObjOk = (is_object($this->ObjectRef) || is_array($this->ObjectRef));
3145
$Loc->SubLst[0] = substr($Loc->SubLst[0],1);
3146
$Pos = $this->meth_Locator_Replace($Txt,$Loc,$this->ObjectRef,0);
3147
} elseif (isset($Loc->PrmLst['noerr'])) {
3148
$Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false);
3150
$this->meth_Misc_Alert($Loc,'property ObjectRef is neither an object nor an array. Its type is \''.gettype($this->ObjectRef).'\'.',true);
3151
$Pos = $Loc->PosEnd + 1;
3153
} elseif ($PrefOk && (substr($Loc->SubLst[0],0,$PrefL)!==$Pref)) {
3154
if (isset($Loc->PrmLst['noerr'])) {
3155
$Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false);
3157
$this->meth_Misc_Alert($Loc,'does not match the allowed prefix.',true);
3158
$Pos = $Loc->PosEnd + 1;
3160
} elseif ( isset($this->VarRef) && isset($this->VarRef[$Loc->SubLst[0]])) {
3161
$Pos = $this->meth_Locator_Replace($Txt,$Loc, $this->VarRef[$Loc->SubLst[0]], 1);
3162
} elseif ( is_null($this->VarRef) && isset($GLOBALS[$Loc->SubLst[0]]) ) {
3163
$Pos = $this->meth_Locator_Replace($Txt,$Loc, $GLOBALS[$Loc->SubLst[0]], 1);
3165
if (isset($Loc->PrmLst['noerr'])) {
3166
$Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false);
3168
$Pos = $Loc->PosEnd + 1;
3169
$msg = (is_null($this->VarRef)) ? 'VarRef seems refers to $GLOBALS' : 'VarRef seems refers to a custom array of values';
3170
$this->meth_Misc_Alert($Loc,'the key \''.$Loc->SubLst[0].'\' does not exist or is not set in VarRef. ('.$msg.')',true);
3175
if ($ConvStr===false) $this->Charset = $Charset;
3177
return false; // Useful for properties PrmIfVar & PrmThenVar
3181
function meth_Merge_AutoSpe(&$Txt,&$Loc) {
3182
// Merge Special Var Fields ([var..*])
3186
if (isset($Loc->SubLst[1])) {
3187
switch ($Loc->SubLst[1]) {
3188
case 'now': $x = time(); break;
3189
case 'version': $x = $this->Version; break;
3190
case 'script_name': $x = basename(((isset($_SERVER)) ? $_SERVER['PHP_SELF'] : $GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'] )); break;
3191
case 'template_name': $x = $this->_LastFile; break;
3192
case 'template_date': $x = ''; if ($this->f_Misc_GetFile($x,$this->_LastFile,'',array(),false)) $x = $x['mtime']; break;
3193
case 'template_path': $x = dirname($this->_LastFile).'/'; break;
3194
case 'name': $x = 'TinyButStrong'; break;
3195
case 'logo': $x = '**TinyButStrong**'; break;
3196
case 'charset': $x = $this->Charset; break;
3197
case 'error_msg': $this->_ErrMsgName = $Loc->FullName; return $Loc->PosEnd; break;
3198
case '': $ErrMsg = 'it doesn\'t have any keyword.'; break;
3200
if ($Loc->SubNbr==2) {
3202
$x = implode(',',array_keys($this->TplVars)); // list of all template variables
3204
if (isset($this->TplVars[$Loc->SubLst[2]])) {
3206
$x = &$this->TplVars[$Loc->SubLst[2]];
3208
$ErrMsg = 'property TplVars doesn\'t have any item named \''.$Loc->SubLst[2].'\'.';
3213
if ($Loc->SubNbr==2) {
3215
$x = implode('',$this->TplStore); // concatenation of all stores
3217
if (isset($this->TplStore[$Loc->SubLst[2]])) {
3219
$x = &$this->TplStore[$Loc->SubLst[2]];
3221
$ErrMsg = 'Store named \''.$Loc->SubLst[2].'\' is not defined yet.';
3224
if (!isset($Loc->PrmLst['strconv'])) {$Loc->PrmLst['strconv'] = 'no'; $Loc->PrmLst['protect'] = 'no';}
3226
case 'cst': $x = @constant($Loc->SubLst[2]); break;
3228
$x = 'TinyButStrong version '.$this->Version.' for PHP 5';
3229
$x .= "\r\nInstalled plug-ins: ".count($this->_PlugIns);
3230
foreach (array_keys($this->_PlugIns) as $pi) {
3231
$o = &$this->_PlugIns[$pi];
3232
$x .= "\r\n- plug-in [".(isset($o->Name) ? $o->Name : $pi ).'] version '.(isset($o->Version) ? $o->Version : '?' );
3238
$x = ob_get_contents();
3240
$x = self::f_Xml_GetPart($x, '(style)+body', false);
3241
if (!isset($Loc->PrmLst['strconv'])) {$Loc->PrmLst['strconv'] = 'no'; $Loc->PrmLst['protect'] = 'no';}
3244
$IsSupported = false;
3245
if (isset($this->_piOnSpecialVar)) {
3247
$ArgLst = array(substr($Loc->SubName,1),&$IsSupported ,&$x, &$Loc->PrmLst,&$Txt,&$Loc->PosBeg,&$Loc->PosEnd,&$Loc);
3248
$this->meth_PlugIn_RunAll($this->_piOnSpecialVar,$ArgLst);
3250
if (!$IsSupported) $ErrMsg = '\''.$Loc->SubLst[1].'\' is an unsupported keyword.';
3253
$ErrMsg = 'it doesn\'t have any subname.';
3255
if ($ErrMsg!==false) {
3256
$this->meth_Misc_Alert($Loc,$ErrMsg);
3259
if ($Loc->PosBeg===false) {
3260
return $Loc->PosEnd;
3262
return $this->meth_Locator_Replace($Txt,$Loc,$x,$SubStart);
3266
function meth_Merge_FieldOutside(&$Txt, &$CurrRec, $RecNum, $PosMax) {
3268
$SubStart = ($CurrRec===false) ? false : 0;
3270
$Loc = $this->meth_Locator_FindTbs($Txt,$this->_CurrBlock,$Pos,'.');
3272
if (($PosMax!==false) && ($Loc->PosEnd>$PosMax)) return;
3273
if ($Loc->SubName==='#') {
3274
$NewEnd = $this->meth_Locator_Replace($Txt,$Loc,$RecNum,false);
3276
$NewEnd = $this->meth_Locator_Replace($Txt,$Loc,$CurrRec,$SubStart);
3278
if ($PosMax!==false) $PosMax += $NewEnd - $Loc->PosEnd;
3281
} while ($Loc!==false);
3285
* Check the values of previous and next record for expression.
3289
function meth_Merge_CheckBounds($BDef,$Src) {
3291
// Retrieve values considering that a new record is fetched
3292
// The order is important
3293
if ($BDef->CheckPrev) {
3294
$BDef->ValPrev = $BDef->ValCurr;
3296
if ($BDef->CheckNext) {
3297
if (is_null($BDef->ValNext)) {
3298
// ValNext is not set at this point for the very first record
3299
$BDef->ValCurr = $this->meth_Merge_SectionNormal($BDef->BoundExpr,$Src);
3301
$BDef->ValCurr = $BDef->ValNext;
3303
if ($Src->NextRec->CurrRec === false) {
3307
$BDef->ValNext = $this->meth_Merge_SectionNormal($BDef->BoundExpr,$Src->NextRec); // merge with next record
3308
$diff_next = ($BDef->ValCurr !== $BDef->ValNext);
3311
$BDef->ValCurr = $this->meth_Merge_SectionNormal($BDef->BoundExpr,$Src); // merge with current record
3315
$result = false; // this state must never happen
3316
if ($BDef->CheckPrev) {
3317
$diff_prev = ($BDef->ValCurr !== $BDef->ValPrev);
3318
if ($BDef->CheckNext) {
3319
$result = $diff_prev && $diff_next;
3321
$result = $diff_prev;
3323
} elseif ($BDef->CheckNext) {
3324
$result = $diff_next;
3331
function meth_Merge_SectionNormal(&$BDef,&$Src) {
3334
$LocLst = &$BDef->LocLst;
3335
$iMax = $BDef->LocNbr;
3336
$PosMax = strlen($Txt);
3338
if ($Src===false) { // Erase all fields
3343
for ($i=$iMax;$i>0;$i--) {
3344
if ($LocLst[$i]->PosBeg<$PosMax) {
3345
$this->meth_Locator_Replace($Txt,$LocLst[$i],$x,false);
3346
if ($LocLst[$i]->Enlarged) {
3347
$PosMax = $LocLst[$i]->PosBeg;
3348
$LocLst[$i]->PosBeg = $LocLst[$i]->PosBeg0;
3349
$LocLst[$i]->PosEnd = $LocLst[$i]->PosEnd0;
3350
$LocLst[$i]->Enlarged = false;
3355
// Uncached locators
3357
$BlockName = &$BDef->Name;
3359
while ($Loc = $this->meth_Locator_FindTbs($Txt,$BlockName,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false);
3365
for ($i=$iMax;$i>0;$i--) {
3366
if ($LocLst[$i]->PosBeg<$PosMax) {
3367
if ($LocLst[$i]->IsRecInfo) {
3368
if ($LocLst[$i]->RecInfo==='#') {
3369
$this->meth_Locator_Replace($Txt,$LocLst[$i],$Src->RecNum,false);
3371
$this->meth_Locator_Replace($Txt,$LocLst[$i],$Src->RecKey,false);
3374
$this->meth_Locator_Replace($Txt,$LocLst[$i],$Src->CurrRec,0);
3376
if ($LocLst[$i]->Enlarged) {
3377
$PosMax = $LocLst[$i]->PosBeg;
3378
$LocLst[$i]->PosBeg = $LocLst[$i]->PosBeg0;
3379
$LocLst[$i]->PosEnd = $LocLst[$i]->PosEnd0;
3380
$LocLst[$i]->Enlarged = false;
3385
// Unchached locators
3387
$BlockName = &$BDef->Name;
3388
foreach ($Src->CurrRec as $key => $val) {
3390
$Name = $BlockName.'.'.$key;
3391
while ($Loc = $this->meth_Locator_FindTbs($Txt,$Name,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$val,0);
3394
$Name = $BlockName.'.#';
3395
while ($Loc = $this->meth_Locator_FindTbs($Txt,$Name,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$Src->RecNum,0);
3397
$Name = $BlockName.'.$';
3398
while ($Loc = $this->meth_Locator_FindTbs($Txt,$Name,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$Src->RecKey,0);
3403
// Automatic sub-blocks
3404
if (isset($BDef->AutoSub)) {
3405
for ($i=1;$i<=$BDef->AutoSub;$i++) {
3406
$name = $BDef->Name.'_sub'.$i;
3408
$col = $BDef->Prm['sub'.$i];
3409
if ($col===true) $col = '';
3410
$col_opt = (substr($col,0,1)==='(') && (substr($col,-1,1)===')');
3411
if ($col_opt) $col = substr($col,1,strlen($col)-2);
3413
// $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'
3414
$data = &$Src->CurrRec;
3415
} elseif (is_object($Src->CurrRec)) {
3416
$data = &$Src->CurrRec->$col;
3418
if (array_key_exists($col, $Src->CurrRec)) {
3419
$data = &$Src->CurrRec[$col];
3421
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.')');
3422
unset($data); $data = array();
3425
if (is_string($data)) {
3426
$data = explode(',',$data);
3427
} elseif (is_null($data) || ($data===false)) {
3430
$this->meth_Merge_Block($Txt, $name, $data, $query, false, 0, false);
3438
function meth_Merge_SectionSerial(&$BDef,&$SrId,&$LocR) {
3441
$SrBDefOrdered = &$BDef->SrBDefOrdered;
3442
$Empty = &$LocR->SerialEmpty;
3446
for ($i=$BDef->SrBDefNbr;$i>0;$i--) {
3447
$SrBDef = &$SrBDefOrdered[$i];
3448
if ($SrBDef->SrTxt===false) { // Subsection not merged with a record
3449
if ($Empty===false) {
3450
$SrBDef->SrTxt = $this->meth_Merge_SectionNormal($SrBDef,$F);
3452
$SrBDef->SrTxt = $Empty;
3455
$Txt = substr_replace($Txt,$SrBDef->SrTxt,$SrBDef->SrBeg,$SrBDef->SrLen);
3456
$SrBDef->SrTxt = false;
3465
* Merge [onload] or [onshow] fields and blocks
3467
function meth_Merge_AutoOn(&$Txt,$Name,$TplVar,$MergeVar) {
3469
$GrpDisplayed = array();
3470
$GrpExclusive = array();
3472
$FieldBefore = false;
3475
while ($LocA=$this->meth_Locator_FindBlockNext($Txt,$Name,$Pos,'_',1,$P1,$FieldBefore)) {
3477
if ($LocA->BlockFound) {
3479
if (!isset($GrpDisplayed[$LocA->SubName])) {
3480
$GrpDisplayed[$LocA->SubName] = false;
3481
$GrpExclusive[$LocA->SubName] = ($LocA->SubName!=='');
3483
$Displayed = &$GrpDisplayed[$LocA->SubName];
3484
$Exclusive = &$GrpExclusive[$LocA->SubName];
3488
if ($Displayed && $Exclusive) {
3491
if (isset($LocA->PrmLst['when'])) {
3492
if (isset($LocA->PrmLst['several'])) $Exclusive=false;
3493
$x = $LocA->PrmLst['when'];
3494
$this->meth_Merge_AutoVar($x,false);
3495
if ($this->f_Misc_CheckCondition($x)) {
3501
} elseif(isset($LocA->PrmLst['default'])) {
3508
$Exclusive = true; // No more block displayed for the group after
3514
if ($LocA->PosBeg2!==false) $Txt = substr_replace($Txt, '', $LocA->PosBeg2, $LocA->PosEnd2 - $LocA->PosBeg2 + 1);
3515
$Txt = substr_replace($Txt,'',$LocA->PosBeg,$LocA->PosEnd-$LocA->PosBeg+1);
3516
$Pos = $LocA->PosBeg;
3518
$FldPos = $LocA->PosBeg;
3519
$FldLen = $LocA->PosEnd - $LocA->PosBeg + 1;
3520
if ($LocA->PosBeg2===false) {
3521
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');
3523
$LocA->PosEnd = $LocA->PosEnd2;
3527
if (isset($LocA->PrmLst['parallel'])) {
3528
// may return false if error
3529
$parallel = $this->meth_Locator_FindParallel($Txt, $LocA->PosBeg, $LocA->PosEnd, $LocA->PrmLst['parallel']);
3530
if ($parallel===false) {
3531
$Txt = substr_replace($Txt,'',$FldPos,$FldLen);
3533
// delete in reverse order
3534
for ($r = count($parallel)-1 ; $r >= 0 ; $r--) {
3536
$Txt = substr_replace($Txt,'',$p['PosBeg'],$p['PosEnd']-$p['PosBeg']+1);
3540
$Txt = substr_replace($Txt,'',$LocA->PosBeg,$LocA->PosEnd-$LocA->PosBeg+1);
3542
$Pos = $LocA->PosBeg;
3544
// Merge the block as if it was a field
3546
$this->meth_Locator_Replace($Txt,$LocA,$x,false);
3547
$Pos = $LocA->PosNext;
3551
} else { // Field (the Loc has no subname at this point)
3553
// Check for Template Var
3555
if (isset($LocA->PrmLst['tplvars']) || isset($LocA->PrmLst['tplfrms'])) {
3557
foreach ($LocA->PrmLst as $Key => $Val) {
3559
$this->TplVars[$Key] = $Val;
3560
} elseif ($Scan=='f') {
3561
self::f_Misc_FormatSave($Val,$Key);
3562
} elseif ($Key==='tplvars') {
3564
} elseif ($Key==='tplfrms') {
3572
$this->meth_Locator_Replace($Txt,$LocA,$x,false);
3573
$Pos = $LocA->PosNext; // continue at the start so embedded fields can be merged
3579
if ($MergeVar) $this->meth_Merge_AutoVar($Txt,true,$Name); // merge other fields (must have subnames)
3581
foreach ($this->Assigned as $n=>$a) {
3582
if (isset($a['auto']) && ($a['auto']===$Name)) {
3584
$this->meth_Misc_Assign($n,$x,false);
3590
// Prepare the strconv parameter
3591
function meth_Conv_Prepare(&$Loc, $StrConv) {
3592
$x = strtolower($StrConv);
3593
$x = '+'.str_replace(' ','',$x).'+';
3594
if (strpos($x,'+esc+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvEsc = true; }
3595
if (strpos($x,'+wsp+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvWS = true; }
3596
if (strpos($x,'+js+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvJS = true; }
3597
if (strpos($x,'+url+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvUrl = true; }
3598
if (strpos($x,'+utf8+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvUtf8 = true; }
3599
if (strpos($x,'+no+')!==false) $Loc->ConvStr = false;
3600
if (strpos($x,'+yes+')!==false) $Loc->ConvStr = true;
3601
if (strpos($x,'+nobr+')!==false) {$Loc->ConvStr = true; $Loc->ConvBr = false; }
3604
// Convert a string with charset or custom function
3605
function meth_Conv_Str(&$Txt,$ConvBr=true) {
3606
if ($this->Charset==='') { // Html by default
3607
$Txt = htmlspecialchars($Txt, ENT_COMPAT); // ENT_COMPAT is no more the default value since PHP 8.1
3608
if ($ConvBr) $Txt = nl2br($Txt);
3609
} elseif ($this->_CharsetFct) {
3610
$Txt = call_user_func($this->Charset, $Txt,$ConvBr);
3612
$Txt = htmlspecialchars($Txt, ENT_COMPAT, $this->Charset);
3613
if ($ConvBr) $Txt = nl2br($Txt);
3617
// Standard alert message provided by TinyButStrong, return False is the message is cancelled.
3618
function meth_Misc_Alert($Src,$Msg,$NoErrMsg=false,$SrcType=false) {
3620
if ($this->NoErr || (PHP_SAPI==='cli') ) {
3621
$t = array('','','','','');
3623
$t = array('<br /><b>','</b>','<em>','</em>','<br />');
3624
$Msg = htmlentities($Msg);
3626
if (!is_string($Src)) {
3627
if ($SrcType===false) $SrcType='in field';
3628
if (isset($Src->PrmLst['tbstype'])) {
3629
$Msg = 'Column \''.$Src->SubName.'\' is expected but missing in the current record.';
3630
$Src = 'Parameter \''.$Src->PrmLst['tbstype'].'='.$Src->SubName.'\'';
3633
$Src = $SrcType.' '.$this->_ChrOpen.$Src->FullName.'...'.$this->_ChrClose;
3636
$x = $t[0].'TinyButStrong Error'.$t[1].' '.$Src.': '.$Msg;
3637
if ($NoErrMsg) $x = $x.' '.$t[2].'This message can be cancelled using parameter \'noerr\'.'.$t[3];
3640
$this->ErrMsg .= $x;
3642
if (PHP_SAPI!=='cli') {
3643
$x = str_replace($this->_ChrOpen,$this->_ChrProtect,$x);
3650
function meth_Misc_Assign($Name,&$ArgLst,$CallingMeth) {
3651
// $ArgLst must be by reference in order to have its inner items by reference too.
3653
if (!isset($this->Assigned[$Name])) {
3654
if ($CallingMeth===false) return true;
3655
return $this->meth_Misc_Alert('with '.$CallingMeth.'() method','key \''.$Name.'\' is not defined in property Assigned.');
3658
$a = &$this->Assigned[$Name];
3659
$meth = (isset($a['type'])) ? $a['type'] : 'MergeBlock';
3660
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.'.');
3663
for ($i=0;$i<$n;$i++) {
3664
if (isset($a[$i])) $ArgLst[$i] = &$a[$i];
3667
if ($CallingMeth===false) {
3668
if (in_array(strtolower($meth),array('mergeblock','mergefield'))) {
3669
call_user_func_array(array(&$this,$meth), $ArgLst);
3671
return $this->meth_Misc_Alert('', 'The assigned field \''.$Name.'\'. cannot be merged because its type \''.$a[0].'\' is not supported.');
3674
if (!isset($a['merged'])) $a['merged'] = 0;
3679
function meth_Misc_IsMainTpl() {
3680
return ($this->_Mode==0);
3683
function meth_Misc_ChangeMode($Init,&$Loc,&$CurrVal) {
3685
// Save contents configuration
3686
$Loc->SaveSrc = &$this->Source;
3687
$Loc->SaveMode = $this->_Mode;
3688
$Loc->SaveVarRef = &$this->VarRef;
3689
unset($this->Source); $this->Source = '';
3690
$this->_Mode++; // Mode>0 means subtemplate mode
3691
if ($this->OldSubTpl) {
3692
ob_start(); // Start buffuring output
3693
$Loc->SaveRender = $this->Render;
3695
$this->Render = TBS_OUTPUT;
3697
// Restore contents configuration
3698
if ($this->OldSubTpl) {
3699
$CurrVal = ob_get_contents();
3701
$this->Render = $Loc->SaveRender;
3703
$CurrVal = $this->Source;
3705
$this->Source = &$Loc->SaveSrc;
3706
$this->_Mode = $Loc->SaveMode;
3707
$this->VarRef = &$Loc->SaveVarRef;
3711
function meth_Misc_UserFctCheck(&$FctInfo,$FctCat,&$FctObj,&$ErrMsg,$FctCheck=false) {
3713
$FctId = $FctCat.':'.$FctInfo;
3714
if (isset($this->_UserFctLst[$FctId])) {
3715
$FctInfo = $this->_UserFctLst[$FctId];
3719
// Check and put in cache
3721
$IsData = ($FctCat!=='f');
3723
if ($FctStr[0]==='~') {
3724
$ObjRef = &$this->ObjectRef;
3725
$Lst = explode('.',substr($FctStr,1));
3726
$iMax = count($Lst) - 1;
3730
$Suff = $Lst[$iMax];
3733
// Reading sub items
3734
for ($i=0;$i<=$iMax;$i++) {
3736
if (is_object($ObjRef)) {
3737
$form = $this->f_Misc_ParseFctForm($x);
3739
if ($i === $iMax0) {
3740
// last item is supposed to be a function's name, without parenthesis
3741
if ( method_exists($ObjRef,$n) || (method_exists($ObjRef, '__call'))) {
3742
// Ok, continue. If $form['as_fct'] is true, then it will produce an error when try to call function $x
3744
$ErrMsg = 'Expression \''.$FctStr.'\' is invalid because \''.$n.'\' is not a method in the class \''.get_class($ObjRef).'\'.';
3747
} elseif ( method_exists($ObjRef,$n) || ($form['as_fct'] && method_exists($ObjRef, 'x__call')) ) {
3748
$f = array(&$ObjRef,$n);
3750
$ObjRef = call_user_func_array($f,$form['args']);
3751
} elseif (isset($ObjRef->$n)) {
3752
$ObjRef = &$ObjRef->$n;
3754
$ErrMsg = 'Expression \''.$FctStr.'\' is invalid because sub-item \''.$n.'\' is neither a method nor a property in the class \''.get_class($ObjRef).'\'.';
3757
} elseif (($i<$iMax0) && is_array($ObjRef)) {
3758
if (isset($ObjRef[$x])) {
3759
$ObjRef = &$ObjRef[$x];
3761
$ErrMsg = 'Expression \''.$FctStr.'\' is invalid because sub-item \''.$x.'\' is not a existing key in the array.';
3765
$ErrMsg = 'Expression \''.$FctStr.'\' is invalid because '.(($i===0)?'property ObjectRef':'sub-item \''.$x.'\'').' is not an object'.(($i<$iMax)?' or an array.':'.');
3769
// Referencing last item
3771
$FctInfo = array('open'=>'','fetch'=>'','close'=>'');
3772
foreach ($FctInfo as $act=>$x) {
3773
$FctName = $Suff.'_'.$act;
3774
if (method_exists($ObjRef,$FctName)) {
3775
$FctInfo[$act] = array(&$ObjRef,$FctName);
3777
$ErrMsg = 'Expression \''.$FctStr.'\' is invalid because method '.$FctName.' is not found.';
3781
$FctInfo['type'] = 4;
3782
if (isset($this->RecheckObj) && $this->RecheckObj) $Save = false;
3784
$FctInfo = array(&$ObjRef,$x);
3786
} elseif ($IsData) {
3788
$IsObj = ($FctCat==='o');
3790
if ($IsObj && method_exists($FctObj,'tbsdb_open') && (!method_exists($FctObj,'+'))) { // '+' avoid a bug in PHP 5
3792
if (!method_exists($FctObj,'tbsdb_fetch')) {
3793
$ErrMsg = 'the expected method \'tbsdb_fetch\' is not found for the class '.$Cls.'.';
3796
if (!method_exists($FctObj,'tbsdb_close')) {
3797
$ErrMsg = 'the expected method \'tbsdb_close\' is not found for the class '.$Cls.'.';
3800
$FctInfo = array('type'=>5);
3804
if ($FctCat==='r') { // Resource
3805
$x = strtolower($FctStr);
3806
$x = str_replace('-','_',$x);
3811
if (($x[$i]==='_') || (($x[$i]>='a') && ($x[$i]<='z')) || (($x[$i]>='0') && ($x[$i]<='9'))) {
3822
$FctInfo = array('open'=>'','fetch'=>'','close'=>'');
3823
foreach ($FctInfo as $act=>$x) {
3824
$FctName = 'tbsdb_'.$Key.'_'.$act;
3825
if (function_exists($FctName)) {
3826
$FctInfo[$act] = $FctName;
3829
if ($act==='open') { // Try simplified key
3830
$p = strpos($Key,'_');
3832
$Key2 = substr($Key,0,$p);
3833
$FctName2 = 'tbsdb_'.$Key2.'_'.$act;
3834
if (function_exists($FctName2)) {
3837
$FctInfo[$act] = $FctName2;
3842
$ErrMsg = 'Data source Id \''.$FctStr.'\' is unsupported because function \''.$FctName.'\' is not found.';
3848
$FctInfo['type'] = 3;
3853
if ( $FctCheck && ($this->FctPrefix!=='') && (strncmp($this->FctPrefix,$FctStr,strlen($this->FctPrefix))!==0) ) {
3854
$ErrMsg = 'user function \''.$FctStr.'\' does not match the allowed prefix.'; return false;
3855
} else if (!function_exists($FctStr)) {
3856
$x = explode('.',$FctStr);
3858
if (class_exists($x[0])) {
3861
$ErrMsg = 'user function \''.$FctStr.'\' is not correct because \''.$x[0].'\' is not a class name.'; return false;
3864
$ErrMsg = 'user function \''.$FctStr.'\' is not found.'; return false;
3869
if ($Save) $this->_UserFctLst[$FctId] = $FctInfo;
3874
function meth_Misc_RunSubscript(&$CurrVal,$CurrPrm) {
3875
// Run a subscript without any local variable damage
3876
return @include($this->_Subscript);
3879
function meth_Misc_Charset($Charset) {
3880
if ($Charset==='+') return;
3881
$this->_CharsetFct = false;
3882
if (is_string($Charset)) {
3883
if (($Charset!=='') && ($Charset[0]==='=')) {
3885
$Charset = substr($Charset,1);
3886
if ($this->meth_Misc_UserFctCheck($Charset,'f',$ErrMsg,$ErrMsg,false)) {
3887
$this->_CharsetFct = true;
3889
$this->meth_Misc_Alert('with charset option',$ErrMsg);
3893
} elseif (is_array($Charset)) {
3894
$this->_CharsetFct = true;
3895
} elseif ($Charset===false) {
3896
$this->Protect = false;
3898
$this->meth_Misc_Alert('with charset option','the option value is not a string nor an array.');
3901
$this->Charset = $Charset;
3904
function meth_PlugIn_RunAll(&$FctBank,&$ArgLst) {
3906
foreach ($FctBank as $FctInfo) {
3907
$Ok = call_user_func_array($FctInfo,$ArgLst);
3908
if (!is_null($Ok)) $OkAll = ($OkAll && $Ok);
3913
function meth_PlugIn_Install($PlugInId,$ArgLst,$Auto) {
3915
$ErrMsg = 'with plug-in \''.$PlugInId.'\'';
3917
if (class_exists($PlugInId)) {
3918
// Create an instance
3920
$PiRef = new $PlugInId;
3921
$PiRef->TBS = &$this; // public $TBS property is madatory since PHP 8.2
3922
if (!method_exists($PiRef,'OnInstall')) return $this->meth_Misc_Alert($ErrMsg,'OnInstall() method is not found.');
3923
$FctRef = array(&$PiRef,'OnInstall');
3925
$FctRef = 'tbspi_'.$PlugInId.'_OnInstall';
3926
if(function_exists($FctRef)) {
3930
return $this->meth_Misc_Alert($ErrMsg,'no class named \''.$PlugInId.'\' is found, and no function named \''.$FctRef.'\' is found.');
3934
$this->_PlugIns[$PlugInId] = &$PiRef;
3936
$EventLst = call_user_func_array($FctRef,$ArgLst);
3937
if (is_string($EventLst)) $EventLst = explode(',',$EventLst);
3938
if (!is_array($EventLst)) return $this->meth_Misc_Alert($ErrMsg,'OnInstall() method does not return an array.');
3940
// Add activated methods
3941
foreach ($EventLst as $Event) {
3942
$Event = trim($Event);
3943
if (!$this->meth_PlugIn_SetEvent($PlugInId, $Event)) return false;
3950
function meth_PlugIn_SetEvent($PlugInId, $Event, $NewRef='') {
3951
// 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.
3953
// Check the event's name
3954
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.');
3956
$PropName = '_pi'.$Event;
3958
if ($NewRef===false) {
3959
// Disable the event
3960
if (!isset($this->$PropName)) return false;
3961
$PropRef = &$this->$PropName;
3962
unset($PropRef[$PlugInId]);
3966
// Prepare the reference to be called
3967
$PiRef = &$this->_PlugIns[$PlugInId];
3968
if (is_object($PiRef)) {
3969
if ($NewRef==='') $NewRef = $Event;
3970
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.');
3971
$FctRef = array(&$PiRef, $NewRef);
3973
$FctRef = ($NewRef==='') ? 'tbspi_'.$PlugInId.'_'.$Event : $NewRef;
3974
if (!function_exists($FctRef)) return $this->meth_Misc_Alert('with plug-in \''.$PlugInId.'\'','The expected function \''.$FctRef.'\' is not found.');
3977
// Save information into the corresponding property
3978
if (!isset($this->$PropName)) $this->$PropName = array();
3979
$PropRef = &$this->$PropName;
3980
$PropRef[$PlugInId] = $FctRef;
3982
// Flags saying if a plugin is installed
3984
case 'OnCommand': break;
3985
case 'OnSpecialVar': break;
3986
case 'OnOperation': break;
3987
case 'OnFormat': $this->_piOnFrm_Ok = true; break;
3988
default: $this->_PlugIns_Ok = true; break;
3996
* Convert any value to a string without specific formating.
3998
static function meth_Misc_ToStr($Value) {
3999
if (is_string($Value)) {
4001
} elseif(is_object($Value)) {
4002
if (method_exists($Value,'__toString')) {
4003
return $Value->__toString();
4004
} elseif (is_a($Value, 'DateTime')) {
4005
// ISO date-time format
4006
return $Value->format('c');
4009
return @(string)$Value; // (string) is faster than strval() and settype()
4013
* Return the formated representation of a Date/Time or numeric variable using a 'VB like' format syntax instead of the PHP syntax.
4015
function meth_Misc_Format(&$Value,&$PrmLst) {
4017
$FrmStr = $PrmLst['frm'];
4018
$CheckNumeric = true;
4019
if (is_string($Value)) $Value = trim($Value);
4021
if ($FrmStr==='') return '';
4022
$Frm = self::f_Misc_FormatSave($FrmStr);
4024
// Manage Multi format strings
4025
if ($Frm['type']=='multi') {
4027
// Found the format according to the value (positive|negative|zero|null)
4029
if (is_numeric($Value)) {
4031
if (is_string($Value)) $Value = 0.0 + $Value;
4034
} elseif ($Value<0) {
4036
if ($Frm['abs']) $Value = abs($Value);
4042
$CheckNumeric = false;
4045
$Value = $this->meth_Misc_ToStr($Value);
4051
$t = strtotime($Value); // We look if it's a date
4052
if (($t===-1) || ($t===false)) {
4053
// Date not recognized
4055
} elseif ($t===943916400) {
4056
// Date to zero in some softwares
4066
// Retrieve the correct simple format
4067
if ($FrmStr==='') return '';
4068
$Frm = self::f_Misc_FormatSave($FrmStr);
4072
switch ($Frm['type']) {
4075
if ($CheckNumeric) {
4076
if (is_numeric($Value)) {
4077
if (is_string($Value)) $Value = 0.0 + $Value;
4079
return $this->meth_Misc_ToStr($Value);
4082
if ($Frm['PerCent']) $Value = $Value * 100;
4083
$Value = number_format($Value,$Frm['DecNbr'],$Frm['DecSep'],$Frm['ThsSep']);
4084
if ($Frm['Pad']!==false) $Value = str_pad($Value, $Frm['Pad'], '0', STR_PAD_LEFT);
4085
if ($Frm['ThsRpl']!==false) $Value = str_replace($Frm['ThsSep'], $Frm['ThsRpl'], $Value);
4086
$Value = substr_replace($Frm['Str'],$Value,$Frm['Pos'],$Frm['Len']);
4091
return $this->meth_Misc_DateFormat($Value, $Frm);
4094
return $Frm['string'];
4100
function meth_Misc_DateFormat(&$Value, $Frm) {
4102
if (is_object($Value)) {
4103
$Value = $this->meth_Misc_ToStr($Value);
4106
if ($Value==='') return '';
4108
// Note : DateTime object is supported since PHP 5.2
4109
// So we could simplify this function using only DateTime instead of timestamp.
4111
// Now we try to get the timestamp
4112
if (is_string($Value)) {
4113
// Any string value is assumed to be a formated date.
4114
// If you whant a string value to be a considered to a a time stamp, then use prefixe '@' accordding to the
4115
$x = strtotime($Value);
4116
// In case of error return false (return -1 for PHP < 5.1.0)
4117
if (($x===false) || ($x===-1)) {
4118
if (!is_numeric($Value)) {
4119
// At this point the value is not recognized as a date
4120
// Special fix for PHP 32-bit and date > '2038-01-19 03:14:07' => strtotime() failes
4121
if (PHP_INT_SIZE === 4) { // 32-bit
4123
$date = new DateTime($Value);
4124
return $date->format($Frm['str_us']);
4125
// 'locale' cannot be supported in this case because strftime() has to equilavent with DateTime
4126
} catch (Exception $e) {
4127
// We take an arbitrary value in order to avoid formating error
4128
$Value = 0; // '1970-01-01'
4129
// echo $e->getMessage();
4132
// We take an arbirtary value in order to avoid formating error
4133
$Value = 0; // '1970-01-01'
4140
if (!is_numeric($Value)) {
4141
// It’s not a timestamp, thus we return the non formated value
4142
return $this->meth_Misc_ToStr($Value);
4146
if ($Frm['loc'] || isset($PrmLst['locale'])) {
4147
$x = strftime($Frm['str_loc'],$Value);
4148
$this->meth_Conv_Str($x,false); // may have accent
4151
return date($Frm['str_us'],$Value);
4157
* Apply combo parameters.
4158
* @param array $PrmLst The existing list of combo
4159
* @param object|false $Loc The current locator, of false if called from an combo definition
4161
static function meth_Misc_ApplyPrmCombo(&$PrmLst, $Loc) {
4163
if (isset($PrmLst['combo'])) {
4165
$name_lst = explode(',', $PrmLst['combo']);
4166
$DefLst = &$GLOBALS['_TBS_PrmCombo'];
4168
foreach ($name_lst as $name) {
4169
if (isset($DefLst[$name])) {
4170
$ap = $DefLst[$name];
4171
if (isset($PrmLst['ope']) && isset($ap['ope'])) {
4172
$PrmLst['ope'] .= ',' . $ap['ope']; // ope will be processed fifo
4175
if ($Loc !== false) {
4176
if ( isset($ap['if']) && is_array($ap['if']) ) {
4177
foreach($ap['if'] as $v) {
4178
self::f_Loc_PrmIfThen($Loc, true, $v, false);
4182
if ( isset($ap['then']) && is_array($ap['then'])) {
4183
foreach($ap['then'] as $v) {
4184
self::f_Loc_PrmIfThen($Loc, false, $v, false);
4189
$PrmLst = array_merge($ap, $PrmLst);
4191
$this->meth_Misc_Alert("with parameter 'combo'", "Combo '". $a. "' is not yet set.");
4195
$PrmLst['_combo'] = $PrmLst['combo']; // for debug
4196
unset($PrmLst['combo']); // for security
4202
* Simply update an array with another array.
4203
* It works for both indexed or associativ arrays.
4204
* NULL value will be deleted from the target array.
4206
* @param array $array The target array to be updated.
4207
* @param mixed $numerical True if the keys ar numerical. Use special keyword 'frm' for TBS formats, and 'prm' for a set of parameters.
4208
* @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.
4209
* @param mixed $d To be used when $v is a single not null value. Will apply the key $v with value $d.
4211
static function f_Misc_UpdateArray(&$array, $numerical, $v, $d) {
4212
if (!is_array($v)) {
4220
foreach ($v as $p=>$a) {
4221
if ($numerical===true) { // numerical keys
4222
if (is_string($p)) {
4223
// syntax: item => true/false
4224
$i = array_search($p, $array, true);
4226
if (!is_null($a)) $array[] = $p;
4228
if (is_null($a)) array_splice($array, $i, 1);
4231
// syntax: i => item
4232
$i = array_search($a, $array, true);
4233
if ($i==false) $array[] = $a;
4235
} else { // string keys
4238
} elseif ($numerical==='frm') {
4239
self::f_Misc_FormatSave($a, $p);
4241
if ($numerical==='prm') {
4242
// apply existing combo on the new combo, so that all combo are translated into basic parameters
4243
if ( isset($a['if']) && (!is_array($a['if'])) ) {
4244
$a['if'] = array($a['if']);
4246
if ( isset($a['then']) && (!is_array($a['then'])) ) {
4247
$a['then'] = array($a['then']);
4249
self::meth_Misc_ApplyPrmCombo($a, false);
4257
static function f_Misc_FormatSave(&$FrmStr,$Alias='') {
4259
$FormatLst = &$GLOBALS['_TBS_FormatLst'];
4261
if (isset($FormatLst[$FrmStr])) {
4262
if ($Alias!='') $FormatLst[$Alias] = &$FormatLst[$FrmStr];
4263
return $FormatLst[$FrmStr];
4266
if (strpos($FrmStr,'|')!==false) {
4269
$Frm = explode('|',$FrmStr); // syntax: Postive|Negative|Zero|Null
4270
$FrmNbr = count($Frm);
4271
$Frm['abs'] = ($FrmNbr>1);
4272
if ($FrmNbr<3) $Frm[2] = &$Frm[0]; // zero
4273
if ($FrmNbr<4) $Frm[3] = ''; // null
4274
$Frm['type'] = 'multi';
4275
$FormatLst[$FrmStr] = $Frm;
4277
} elseif (($nPosEnd = strrpos($FrmStr,'0'))!==false) {
4286
if (substr($FrmStr,$nPosEnd+1,1)==='.') {
4291
$nPos = $nPosEnd - 1;
4292
while (($nPos>=0) && ($FrmStr[$nPos]==='0')) {
4295
if (($nPos>=1) && ($FrmStr[$nPos-1]==='0')) {
4296
$nDecSep = $FrmStr[$nPos];
4297
$nDecNbr = $nPosEnd - $nPos;
4303
// Thousand separator
4306
if (($nDecOk) && ($nPos>=5)) {
4307
if ((substr($FrmStr,$nPos-3,3)==='000') && ($FrmStr[$nPos-4]!=='0')) {
4308
$p = strrpos(substr($FrmStr,0,$nPos-4), '0');
4311
$x = substr($FrmStr, $p+1, $len);
4313
// for compatibility for number_format() with PHP < 5.4.0
4314
$nThsSep = ($nDecSep=='*') ? '.' : '*';
4325
if ($nDecOk) $nPos--;
4326
while (($nPos>=0) && ($FrmStr[$nPos]==='0')) {
4330
$nLen = $nPosEnd-$nPos;
4331
if ( ($nThsSep==='') && ($nLen>($nDecNbr+$nPadZ+1)) ) $nPad = $nLen - $nPadZ;
4334
$nPerCent = (strpos($FrmStr,'%')===false) ? false : true;
4336
$FormatLst[$FrmStr] = array('type'=>'num','Str'=>$FrmStr,'Pos'=>($nPos+1),'Len'=>$nLen,'ThsSep'=>$nThsSep,'ThsRpl'=>$nThsRpl,'DecSep'=>$nDecSep,'DecNbr'=>$nDecNbr,'PerCent'=>$nPerCent,'Pad'=>$nPad);
4346
$i = strpos($FrmStr,'(locale)');
4347
$Locale = ($i!==false);
4348
if ($Locale) $x = substr_replace($x,'',$i,8);
4351
for ($i=0;$i<$iEnd;$i++) {
4354
// We are in a string part
4356
if (substr($x,$i+1,1)==='"') {
4357
$FrmPHP .= '\\"'; // protected char
4364
$FrmPHP .= '\\'.$x[$i]; // protected char
4372
if (strcasecmp(substr($x,$i,2),'hh' )===0) { $FrmPHP .= 'H'; $FrmLOC .= '%H'; $i += 1;}
4373
elseif (strcasecmp(substr($x,$i,2),'hm' )===0) { $FrmPHP .= 'h'; $FrmLOC .= '%I'; $i += 1;} // for compatibility
4374
elseif (strcasecmp(substr($x,$i,1),'h' )===0) { $FrmPHP .= 'G'; $FrmLOC .= '%H';}
4375
elseif (strcasecmp(substr($x,$i,2),'rr' )===0) { $FrmPHP .= 'h'; $FrmLOC .= '%I'; $i += 1;}
4376
elseif (strcasecmp(substr($x,$i,1),'r' )===0) { $FrmPHP .= 'g'; $FrmLOC .= '%I';}
4377
elseif (strcasecmp(substr($x,$i,4),'ampm')===0) { $FrmPHP .= substr($x,$i,1); $FrmLOC .= '%p'; $i += 3;} // $Fmp = 'A' or 'a'
4378
elseif (strcasecmp(substr($x,$i,2),'nn' )===0) { $FrmPHP .= 'i'; $FrmLOC .= '%M'; $i += 1;}
4379
elseif (strcasecmp(substr($x,$i,2),'ss' )===0) { $FrmPHP .= 's'; $FrmLOC .= '%S'; $i += 1;}
4380
elseif (strcasecmp(substr($x,$i,2),'xx' )===0) { $FrmPHP .= 'S'; $FrmLOC .= '' ; $i += 1;}
4381
elseif (strcasecmp(substr($x,$i,4),'yyyy')===0) { $FrmPHP .= 'Y'; $FrmLOC .= '%Y'; $i += 3;}
4382
elseif (strcasecmp(substr($x,$i,2),'yy' )===0) { $FrmPHP .= 'y'; $FrmLOC .= '%y'; $i += 1;}
4383
elseif (strcasecmp(substr($x,$i,4),'mmmm')===0) { $FrmPHP .= 'F'; $FrmLOC .= '%B'; $i += 3;}
4384
elseif (strcasecmp(substr($x,$i,3),'mmm' )===0) { $FrmPHP .= 'M'; $FrmLOC .= '%b'; $i += 2;}
4385
elseif (strcasecmp(substr($x,$i,2),'mm' )===0) { $FrmPHP .= 'm'; $FrmLOC .= '%m'; $i += 1;}
4386
elseif (strcasecmp(substr($x,$i,1),'m' )===0) { $FrmPHP .= 'n'; $FrmLOC .= '%m';}
4387
elseif (strcasecmp(substr($x,$i,4),'wwww')===0) { $FrmPHP .= 'l'; $FrmLOC .= '%A'; $i += 3;}
4388
elseif (strcasecmp(substr($x,$i,3),'www' )===0) { $FrmPHP .= 'D'; $FrmLOC .= '%a'; $i += 2;}
4389
elseif (strcasecmp(substr($x,$i,1),'w' )===0) { $FrmPHP .= 'w'; $FrmLOC .= '%u';}
4390
elseif (strcasecmp(substr($x,$i,4),'dddd')===0) { $FrmPHP .= 'l'; $FrmLOC .= '%A'; $i += 3;}
4391
elseif (strcasecmp(substr($x,$i,3),'ddd' )===0) { $FrmPHP .= 'D'; $FrmLOC .= '%a'; $i += 2;}
4392
elseif (strcasecmp(substr($x,$i,2),'dd' )===0) { $FrmPHP .= 'd'; $FrmLOC .= '%d'; $i += 1;}
4393
elseif (strcasecmp(substr($x,$i,1),'d' )===0) { $FrmPHP .= 'j'; $FrmLOC .= '%d';}
4395
$FrmPHP .= '\\'.$x[$i]; // protected char
4396
$FrmLOC .= $x[$i]; // protected char
4405
$FormatLst[$FrmStr] = array('type'=>'date','str_us'=>$FrmPHP,'str_loc'=>$FrmLOC,'loc'=>$Locale);
4407
$FormatLst[$FrmStr] = array('type'=>'else','string'=>$FrmStr);
4412
if ($Alias!='') $FormatLst[$Alias] = &$FormatLst[$FrmStr];
4414
return $FormatLst[$FrmStr];
4418
static function f_Misc_ConvSpe(&$Loc) {
4419
if ($Loc->ConvMode!==2) {
4421
$Loc->ConvEsc = false;
4422
$Loc->ConvWS = false;
4423
$Loc->ConvJS = false;
4424
$Loc->ConvUrl = false;
4425
$Loc->ConvUtf8 = false;
4430
* Return the information if parsing a form which can be either a property of a function.
4431
* @param string $Str The form. Example : 'my_func(aaa,bbb)'
4432
* @return array Information about the form. Example : array('name' => 'my_func', 'as_fct' => true, 'args' => array('aaa', 'bbb'),)
4433
* name: the name of the function of the property.
4434
* as_fct: true if the form is as a function
4435
* args: arguments of the function, or empty array if it's a property
4437
static function f_Misc_ParseFctForm($Str) {
4438
$info = array('name' => $Str, 'as_fct' => false, 'args' => array());
4439
if (substr($Str,-1,1)===')') {
4440
$pos = strpos($Str,'(');
4442
$info['args'] = explode(',',substr($Str,$pos+1,strlen($Str)-$pos-2));
4443
$info['name'] = substr($Str,0,$pos);
4444
$info['as_fct'] = true;
4451
* Check if a string condition is true.
4452
* @param string $Str The condition to check.
4453
* @return boolean True if the condition if checked.
4455
static function f_Misc_CheckCondition($Str) {
4456
// Check if an expression like "exrp1=expr2" is true or false.
4458
// Bluid $StrZ, wich is the same as $Str but with 'z' for each character that is protected with "'".
4459
// This will help to search for operators outside protected strings.
4461
$Max = strlen($Str)-1;
4462
$p = strpos($Str,'\'');
4463
if ($Esc=($p!==false)) {
4465
for ($p=$p+1;$p<=$Max;$p++) {
4466
if ($StrZ[$p]==='\'') {
4474
// Find operator and position
4477
$p = strpos($StrZ,$Ope);
4480
$p = strpos($StrZ,$Ope);
4481
if ($p===false) return false;
4482
if (($p>0) && ($StrZ[$p-1]==='-')) {
4483
$Ope = '-+'; $p--; $Len=2;
4484
} elseif (($p<$Max) && ($StrZ[$p+1]==='-')) {
4485
$Ope = '+-'; $Len=2;
4493
$Ope = '!='; $p--; $Len=2;
4494
} elseif ($x==='~') {
4495
$Ope = '~='; $p--; $Len=2;
4496
} elseif ($p<$Max) {
4500
} elseif (($x==='+') && ($y==='-')) {
4501
$Ope = '+=-'; $p--; $Len=3;
4502
} elseif (($x==='-') && ($y==='+')) {
4503
$Ope = '-=+'; $p--; $Len=3;
4511
$Val1 = trim(substr($Str,0,$p));
4512
$Val2 = trim(substr($Str,$p+$Len));
4514
$NoDelim1 = self::f_Misc_DelDelimiter($Val1,'\'');
4515
$NoDelim2 = self::f_Misc_DelDelimiter($Val2,'\'');
4517
$NoDelim1 = $NoDelim2 = false;
4522
return (strcasecmp($Val1,$Val2)==0);
4523
} elseif ($Ope==='!=') {
4524
return (strcasecmp($Val1,$Val2)!=0);
4525
} elseif ($Ope==='~=') {
4526
return (preg_match($Val2,$Val1)>0);
4528
// If a value has no string delimiter, we assume it is supposed to be a numerical comparison.
4529
if ($NoDelim1 && ($Val1 === '') ) $Val1 = '0';
4530
if ($NoDelim2 && ($Val2 === '') ) $Val2 ='0';
4531
// PHP makes a numerical comparison when each item is independently either a numeric value or a numeric string. Otherwise it makes a string comparison.
4532
// So we let PHP doing the comparison on its onw way.
4534
return ($Val1 > $Val2);
4535
} elseif ($Ope==='-+') {
4536
return ($Val1 < $Val2);
4537
} elseif ($Ope==='+=-') {
4538
return ($Val1 >= $Val2);
4539
} elseif ($Ope==='-=+') {
4540
return ($Val1<=$Val2);
4549
* Delete the string delimiters that surrounds the string, if any. But not inside (no need).
4550
* @param string $Txt The string to modifiy.
4551
* @param string $Delim The character that can delimit the string.
4552
* @return boolean True if the given string was not delimited with $Delim.
4554
static function f_Misc_DelDelimiter(&$Txt,$Delim) {
4555
// Delete the string delimiters
4556
$len = strlen($Txt);
4557
if (($len>1) && ($Txt[0]===$Delim)) {
4558
if ($Txt[$len-1]===$Delim) $Txt = substr($Txt,1,$len-2);
4565
static function f_Misc_GetFile(&$Res, &$File, $LastFile='', $IncludePath=false, $Contents=true) {
4566
// Load the content of a file into the text variable.
4569
$fd = self::f_Misc_TryFile($File, false);
4571
if (is_array($IncludePath)) {
4572
foreach ($IncludePath as $d) {
4573
$fd = self::f_Misc_TryFile($File, $d);
4574
if ($fd!==false) break;
4577
if (($fd===false) && ($LastFile!='')) $fd = self::f_Misc_TryFile($File, dirname($LastFile));
4578
if ($fd===false) return false;
4584
if (isset($fs['size'])) {
4585
if ($fs['size']>0) $Res = fread($fd,$fs['size']);
4587
while (!feof($fd)) $Res .= fread($fd,4096);
4600
* Try to open the file for reading.
4601
* @param string $File The file name.
4602
* @param string|bolean $Dir A The directory where to search, of false to omit directory.
4603
* @return ressource Return the file pointer, of false on error. Note that urgument $File will be updated to the file with directory.
4605
static function f_Misc_TryFile(&$File, $Dir) {
4606
if ($Dir==='') return false;
4607
$FileSearch = ($Dir===false) ? $File : $Dir.'/'.$File;
4608
// 'rb' if binary for some OS. fopen() uses include_path and search on the __FILE__ directory while file_exists() doesn't.
4609
$f = @fopen($FileSearch, 'r', true);
4610
if ($f!==false) $File = $FileSearch;
4615
* Read TBS or XML tags, starting to the begining of the tag.
4617
static function f_Loc_PrmRead(&$Txt,$Pos,$XmlTag,$DelimChrs,$BegStr,$EndStr,&$Loc,&$PosEnd,$WithPos=false) {
4619
$BegLen = strlen($BegStr);
4620
$BegChr = $BegStr[0];
4621
$BegIs1 = ($BegLen===1);
4627
$SubName = $Loc->SubOk;
4629
$Status = 0; // 0: name not started, 1: name started, 2: name ended, 3: equal found, 4: value started
4634
// Variables for checking the loop
4635
$PosEnd = strpos($Txt,$EndStr,$Pos);
4636
if ($PosEnd===false) return;
4637
$Continue = ($Pos<$PosEnd);
4643
if ($DelimIdx) { // Reading in the string
4645
if ($Chr===$DelimChr) { // Quote found
4646
if ($Chr===$Txt[$Pos+1]) { // Double Quote => the string continue with un-double the quote
4648
} else { // Simple Quote => end of string
4653
} else { // Reading outside the string
4657
// Analyzing parameters
4659
if (($Chr===' ') || ($Chr==="\r") || ($Chr==="\n")) {
4661
if ($SubName && ($XmlTag===false)) {
4662
// Accept spaces in TBS subname.
4667
} elseif ($XmlTag && ($Status===4)) {
4668
self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos);
4671
} elseif (($XmlTag===false) && ($Chr===';')) {
4672
self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos);
4674
} elseif ($Status===4) {
4676
} elseif ($Status===3) {
4681
} elseif ($Status===2) {
4684
} elseif ($XmlTag) {
4685
self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos);
4695
} elseif ($Status===1) {
4709
$DelimIdx = strpos($DelimChrs,$Chr);
4710
if ($DelimIdx===false) {
4711
if ($Chr===$BegChr) {
4714
} elseif(substr($Txt,$Pos,$BegLen)===$BegStr) {
4719
$DelimChr = $DelimChrs[$DelimIdx];
4726
if ($Chr===$BegChr) {
4729
} elseif(substr($Txt,$Pos,$BegLen)===$BegStr) {
4740
// We check if it's the end
4741
if ($Pos===$PosEnd) {
4744
} elseif ($DelimIdx===false) {
4752
$PosEnd = strpos($Txt,$EndStr,$PosEnd+1);
4753
if ($PosEnd===false) return;
4755
if ($XmlTag && ($Txt[$Pos-1]==='/')) $Pos--; // In case last attribute is stuck to "/>"
4756
self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos);
4762
$PosEnd = $PosEnd + (strlen($EndStr)-1);
4766
static function f_Loc_PrmCompute(&$Txt,&$Loc,&$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos) {
4772
$x = substr($Txt,$PosName,$Pos-$PosName);
4774
$x = substr($Txt,$PosName,$PosNend-$PosName);
4776
if ($XmlTag) $x = strtolower($x);
4778
$Loc->SubName = trim($x);
4782
$v = trim(substr($Txt,$PosVal,$Pos-$PosVal));
4783
if ($DelimCnt===1) { // Delete quotes inside the value
4784
if ($v[0]===$DelimChr) {
4786
if ($v[$len-1]===$DelimChr) {
4787
$v = substr($v,1,$len-2);
4788
$v = str_replace($DelimChr.$DelimChr,$DelimChr,$v);
4796
self::f_Loc_PrmIfThen($Loc, true, $v, true);
4797
} elseif ($x==='then') {
4798
self::f_Loc_PrmIfThen($Loc, false, $v, true);
4800
$Loc->PrmLst[$x] = $v;
4801
if ($WithPos) $Loc->PrmPos[$x] = array($PosName,$PosNend,$PosVal,$Pos,$DelimChr,$DelimCnt);
4809
* Add a new parameter 'if or 'then' to the locator.
4811
* @param object $Loc The locator.
4812
* @param boolean $IsIf Concerned parameter. True means 'if', false means 'then'.
4813
* @param string $Val The value of the parameter.
4814
* @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.
4817
static function f_Loc_PrmIfThen(&$Loc, $IsIf, $Val, $Ordered) {
4818
$nb_if = &$Loc->PrmIfNbr;
4819
if ($nb_if===false) {
4821
$Loc->PrmIf = array();
4822
$Loc->PrmIfVar = array();
4823
$Loc->PrmThen = array();
4824
$Loc->PrmThenVar = array();
4825
$Loc->PrmElseVar = true;
4829
$Loc->PrmIf[$nb_if] = $Val;
4830
$Loc->PrmIfVar[$nb_if] = true;
4834
if ($nb_then===false) $nb_then = 1; // Only the first 'then' can be placed before its 'if'. This is for compatibility.
4836
$nb_then = count($Loc->PrmThen) + 1;
4838
$Loc->PrmThen[$nb_then] = $Val;
4839
$Loc->PrmThenVar[$nb_then] = true;
4844
This function enables to enlarge the pos limits of the Locator.
4845
If the search result is not correct, $PosBeg must not change its value, and $PosEnd must be False.
4846
This is because of the calling function.
4848
static function f_Loc_EnlargeToStr(&$Txt,&$Loc,$StrBeg,$StrEnd) {
4850
// Search for the begining string
4851
$Pos = $Loc->PosBeg;
4854
$Pos = strrpos(substr($Txt,0,$Pos),$StrBeg[0]);
4856
if (substr($Txt,$Pos,strlen($StrBeg))===$StrBeg) $Ok = true;
4858
} while ( (!$Ok) && ($Pos!==false) );
4861
$PosEnd = strpos($Txt,$StrEnd,$Loc->PosEnd + 1);
4862
if ($PosEnd===false) {
4865
$Loc->PosBeg = $Pos;
4866
$Loc->PosEnd = $PosEnd + strlen($StrEnd) - 1;
4874
static function f_Loc_EnlargeToTag(&$Txt,&$Loc,$TagStr,$RetInnerSrc) {
4875
//Modify $Loc, return false if tags not found, returns the inner source of tag if $RetInnerSrc=true
4877
$AliasLst = &$GLOBALS['_TBS_BlockAlias'];
4886
while ($TagStr!=='') {
4888
$p = strpos($TagStr, '+');
4893
$t = substr($TagStr,0,$p);
4894
$TagStr = substr($TagStr,$p+1);
4896
// Check parentheses, relative position and single tag
4899
$e = strlen($t) - 1; // pos of last char
4900
if (($e>1) && ($t[0]==='(') && ($t[$e]===')')) {
4901
if ($Ref===0) $Ref = $i;
4902
if ($Ref===$i) $LevelStop++;
4903
$t = substr($t,1,$e-1);
4905
if (($e>=0) && ($t[$e]==='/')) $t = substr($t,0,$e); // for compatibilty
4908
} while ($e!==false);
4909
// Check for multiples
4910
$p = strpos($t, '*');
4912
$n = intval(substr($t, 0, $p));
4913
$t = substr($t, $p + 1);
4914
$n = max($n ,1); // prevent for error: minimum valu is 1
4915
$TagStr = str_repeat($t . '+', $n-1) . $TagStr;
4918
if (($t==='.') && ($Ref===0)) $Ref = $i;
4919
// Take of the (!) prefix
4921
if (($t!=='') && ($t[0]==='!')) {
4927
if (isset($AliasLst[$t])) {
4928
$a = $AliasLst[$t]; // a string or a function
4929
if (is_string($a)) {
4930
if ($i>999) return false; // prevent from circular alias
4931
$TagStr = $b . $a . (($TagStr==='') ? '' : '+') . $TagStr;
4936
$TagLst[$i] = $t; // with prefix ! if specified
4938
$TagBnd[$i] = ($b==='');
4945
// Find tags that embeds the locator
4946
if ($LevelStop===0) $LevelStop = 1;
4948
// First tag of reference
4949
if ($TagLst[$Ref] === '.') {
4950
$TagO = new clsTbsLocator;
4951
$TagO->PosBeg = $Loc->PosBeg;
4952
$TagO->PosEnd = $Loc->PosEnd;
4953
$PosBeg = $Loc->PosBeg;
4954
$PosEnd = $Loc->PosEnd;
4956
$TagO = self::f_Loc_Enlarge_Find($Txt,$TagLst[$Ref],$TagFct[$Ref],$Loc->PosBeg-1,false,$LevelStop);
4957
if ($TagO===false) return false;
4958
$PosBeg = $TagO->PosBeg;
4959
$LevelStop += -$TagO->RightLevel; // RightLevel=1 only if the tag is single and embeds $Loc, otherwise it is 0
4961
$TagC = self::f_Loc_Enlarge_Find($Txt,$TagLst[$Ref],$TagFct[$Ref],$Loc->PosEnd+1,true,-$LevelStop);
4962
if ($TagC==false) return false;
4963
$PosEnd = $TagC->PosEnd;
4964
$InnerLim = $TagC->PosBeg;
4965
if ((!$TagBnd[$Ref]) && ($TagMax==0)) {
4966
$PosBeg = $TagO->PosEnd + 1;
4967
$PosEnd = $TagC->PosBeg - 1;
4970
$PosEnd = $TagO->PosEnd;
4971
$InnerLim = $PosEnd + 1;
4978
if ($Loc->PosBeg>$TagO->PosEnd) $RetVal .= substr($Txt,$TagO->PosEnd+1,min($Loc->PosBeg,$InnerLim)-$TagO->PosEnd-1);
4979
if ($Loc->PosEnd<$InnerLim) $RetVal .= substr($Txt,max($Loc->PosEnd,$TagO->PosEnd)+1,$InnerLim-max($Loc->PosEnd,$TagO->PosEnd)-1);
4982
// Other tags forward
4984
for ($i=$Ref+1;$i<=$TagMax;$i++) {
4986
if (($x!=='') && ($TagC!==false)) {
4987
$level = ($TagBnd[$i]) ? 0 : 1;
4988
$TagC = self::f_Loc_Enlarge_Find($Txt,$x,$TagFct[$i],$PosEnd+1,true,$level);
4989
if ($TagC!==false) {
4990
$PosEnd = ($TagBnd[$i]) ? $TagC->PosEnd : $TagC->PosBeg -1 ;
4995
// Other tags backward
4997
for ($i=$Ref-1;$i>=0;$i--) {
4999
if (($x!=='') && ($TagO!==false)) {
5000
$level = ($TagBnd[$i]) ? 0 : -1;
5001
$TagO = self::f_Loc_Enlarge_Find($Txt,$x,$TagFct[$i],$PosBeg-1,false,$level);
5002
if ($TagO!==false) {
5003
$PosBeg = ($TagBnd[$i]) ? $TagO->PosBeg : $TagO->PosEnd + 1;
5008
$Loc->PosBeg = $PosBeg;
5009
$Loc->PosEnd = $PosEnd;
5014
static function f_Loc_Enlarge_Find($Txt, $Tag, $Fct, $Pos, $Forward, $LevelStop) {
5016
return self::f_Xml_FindTag($Txt,$Tag,(!$Forward),$Pos,$Forward,$LevelStop,false);
5018
$p = call_user_func_array($Fct,array($Tag,$Txt,$Pos,$Forward,$LevelStop));
5022
return (object) array('PosBeg'=>$p, 'PosEnd'=>$p, 'RightLevel'=> 0); // it's a trick
5028
* Return the expected value for a boolean attribute
5030
static function f_Loc_AttBoolean($CurrVal, $AttTrue, $AttName) {
5032
if ($AttTrue===true) {
5033
if (self::meth_Misc_ToStr($CurrVal)==='') {
5038
} elseif (self::meth_Misc_ToStr($CurrVal)===$AttTrue) {
5047
* Affects the positions of a list of locators regarding to a specific moving locator.
5049
static function f_Loc_Moving(&$LocM, &$LocLst) {
5050
foreach ($LocLst as &$Loc) {
5051
if ($Loc !== $LocM) {
5052
if ($Loc->PosBeg >= $LocM->InsPos) {
5053
$Loc->PosBeg += $LocM->InsLen;
5054
$Loc->PosEnd += $LocM->InsLen;
5056
if ($Loc->PosBeg > $LocM->DelPos) {
5057
$Loc->PosBeg -= $LocM->DelLen;
5058
$Loc->PosEnd -= $LocM->DelLen;
5066
* Sort the locators in the list. Apply the bubble algorithm.
5067
* Deleted locators maked with DelMe.
5068
* @param array $LocLst An array of locators.
5069
* @param boolean $DelEmbd True to deleted locators that embded other ones.
5070
* @param boolean $iFirst Index of the first item.
5071
* @return integer Return the number of met embedding locators.
5073
static function f_Loc_Sort(&$LocLst, $DelEmbd, $iFirst = 0) {
5075
$iLast = $iFirst + count($LocLst) - 1;
5078
for ($i = $iLast ; $i>=$iFirst ; $i--) {
5080
$d = (isset($Loc->DelMe) && $Loc->DelMe);
5083
for ($j=$i+1; $j<=$iLast ; $j++) {
5084
// If DelMe, then the loc will be put at the end and deleted
5085
$jb = $LocLst[$j]->PosBeg;
5086
if ($d || ($b > $jb)) {
5087
$LocLst[$j-1] = $LocLst[$j];
5089
} elseif ($e > $jb) {
5093
$j--; // replay the current position
5095
$j = $iLast; // quit the loop
5098
$j = $iLast; // quit the loop
5102
unset($LocLst[$iLast]);
5111
* Prepare all informations to move a locator according to parameter "att".
5113
* @param false|true|array $MoveLocLst true to simple move the loc, or an array of loc to rearrange the list after the move.
5114
* Note: rearrange doest not work with PHP4.
5116
static function f_Xml_AttFind(&$Txt,&$Loc,$MoveLocLst=false,$AttDelim=false,$LocLst=false) {
5117
// att=div#class ; att=((div))#class ; att=+((div))#class
5119
$Att = $Loc->PrmLst['att'];
5120
unset($Loc->PrmLst['att']); // prevent from processing the field twice
5121
$Loc->PrmLst['att;'] = $Att; // for debug
5123
$p = strrpos($Att,'#');
5127
$TagLst = substr($Att,0,$p);
5128
$Att = substr($Att,$p+1);
5131
$Forward = (substr($TagLst,0,1)==='+');
5132
if ($Forward) $TagLst = substr($TagLst,1);
5133
$TagLst = explode('+',$TagLst);
5135
$iMax = count($TagLst)-1;
5138
foreach ($TagLst as $i=>$Tag) {
5140
while ((strlen($Tag)>1) && (substr($Tag,0,1)==='(') && (substr($Tag,-1,1)===')')) {
5141
if ($LevelStop===false) $LevelStop = 0;
5143
$Tag = trim(substr($Tag,1,strlen($Tag)-2));
5145
if ($i==$iMax) $WithPrm = true;
5146
$Pos = ($Forward) ? $LocO->PosEnd+1 : $LocO->PosBeg-1;
5148
$LocO = self::f_Xml_FindTag($Txt,$Tag,true,$Pos,$Forward,$LevelStop,$WithPrm,$WithPrm);
5149
if ($LocO===false) return false;
5152
$Loc->AttForward = $Forward;
5153
$Loc->AttTagBeg = $LocO->PosBeg;
5154
$Loc->AttTagEnd = $LocO->PosEnd;
5155
$Loc->AttDelimChr = false;
5158
// this indicates that the TBS field is supposed to be inside an attribute's value
5159
foreach ($LocO->PrmPos as $a=>$p ) {
5160
if ( ($p[0]<$Loc->PosBeg) && ($Loc->PosEnd<$p[3]) ) $Att = $a;
5162
if ($Att==='.') return false;
5164
$Loc->AttName = $Att;
5166
$AttLC = strtolower($Att);
5167
if (isset($LocO->PrmLst[$AttLC])) {
5168
// The attribute is existing
5169
$p = $LocO->PrmPos[$AttLC];
5170
$Loc->AttBeg = $p[0];
5171
$p[3]--; while ($Txt[$p[3]]===' ') $p[3]--; // external end of the attribute, may has an extra spaces
5172
$Loc->AttEnd = $p[3];
5173
$Loc->AttDelimCnt = $p[5];
5174
$Loc->AttDelimChr = $p[4];
5175
if (($p[1]>$p[0]) && ($p[2]>$p[1])) {
5176
//$Loc->AttNameEnd = $p[1];
5177
$Loc->AttValBeg = $p[2];
5178
} else { // attribute without value
5179
//$Loc->AttNameEnd = $p[3];
5180
$Loc->AttValBeg = false;
5183
// The attribute is not yet existing
5184
$Loc->AttDelimCnt = 0;
5185
$Loc->AttBeg = false;
5188
// Search for a delimitor
5189
if (($Loc->AttDelimCnt==0) && (isset($LocO->PrmPos))) {
5190
foreach ($LocO->PrmPos as $p) {
5191
if ($p[5]>0) $Loc->AttDelimChr = $p[4];
5195
if ($MoveLocLst) return self::f_Xml_AttMove($Txt,$Loc,$AttDelim,$MoveLocLst);
5202
* Move a locator in the source from its original location to the attribute location.
5203
* The new locator string is only '[]', no need to copy the full source since all parameters are saved in $Loc.*
5205
* @param false|true|array $MoveLocLst If the function is called from the caching process, then this value is an array.
5207
static function f_Xml_AttMove(&$Txt, &$Loc, $AttDelim, &$MoveLocLst) {
5209
if ($AttDelim===false) $AttDelim = $Loc->AttDelimChr;
5210
if ($AttDelim===false) $AttDelim = '"';
5212
$DelPos = $Loc->PosBeg;
5213
$DelLen = $Loc->PosEnd - $Loc->PosBeg + 1;
5214
$Txt = substr_replace($Txt,'',$DelPos,$DelLen); // delete the current locator
5215
if ($Loc->AttForward) {
5216
$Loc->AttTagBeg += -$DelLen;
5217
$Loc->AttTagEnd += -$DelLen;
5218
} elseif ($Loc->PosBeg<$Loc->AttTagEnd) {
5219
$Loc->AttTagEnd += -$DelLen;
5223
if ($Loc->AttBeg===false) {
5224
$InsPos = $Loc->AttTagEnd;
5225
if ($Txt[$InsPos-1]==='/') $InsPos--;
5226
if ($Txt[$InsPos-1]===' ') $InsPos--;
5227
$Ins1 = ' '.$Loc->AttName.'='.$AttDelim;
5229
$Loc->AttBeg = $InsPos + 1;
5230
$Loc->AttValBeg = $InsPos + strlen($Ins1) - 1;
5232
if ($Loc->PosEnd<$Loc->AttBeg) $Loc->AttBeg += -$DelLen;
5233
if ($Loc->PosEnd<$Loc->AttEnd) $Loc->AttEnd += -$DelLen;
5234
if ($Loc->AttValBeg===false) {
5235
$InsPos = $Loc->AttEnd+1;
5236
$Ins1 = '='.$AttDelim;
5238
$Loc->AttValBeg = $InsPos+1;
5239
} elseif (isset($Loc->PrmLst['attadd'])) {
5240
$InsPos = $Loc->AttEnd;
5244
// value already existing
5245
if ($Loc->PosEnd<$Loc->AttValBeg) $Loc->AttValBeg += -$DelLen;
5246
$PosBeg = $Loc->AttValBeg;
5247
$PosEnd = $Loc->AttEnd;
5248
if ($Loc->AttDelimCnt>0) {$PosBeg++; $PosEnd--;}
5252
if ($InsPos===false) {
5255
$InsTxt = $Ins1.'[]'.$Ins2;
5256
$InsLen = strlen($InsTxt);
5257
$PosBeg = $InsPos + strlen($Ins1);
5258
$PosEnd = $PosBeg + 1;
5259
$Txt = substr_replace($Txt,$InsTxt,$InsPos,0);
5260
$Loc->AttEnd = $InsPos + $InsLen - 1;
5261
$Loc->AttTagEnd += $InsLen;
5264
$Loc->PosBeg = $PosBeg;
5265
$Loc->PosEnd = $PosEnd;
5268
if (is_array($MoveLocLst)) {
5269
$Loc->InsPos = $InsPos;
5270
$Loc->InsLen = $InsLen;
5271
$Loc->DelPos = $DelPos;
5272
if ($Loc->InsPos < $Loc->DelPos) $Loc->DelPos += $InsLen;
5273
$Loc->DelLen = $DelLen;
5274
self::f_Loc_Moving($Loc, $MoveLocLst);
5281
static function f_Xml_Max(&$Txt,&$Nbr,$MaxEnd) {
5282
// Limit the number of HTML chars
5284
$pMax = strlen($Txt)-1;
5292
if ($Txt[$p]===';') {
5297
if ($Txt[$p]==='&') {
5303
if (($n>=$Nbr) || ($p>=$pMax)) {
5310
if (($n>=$Nbr) && ($p<$pMax)) $Txt = substr($Txt,0,$p).$MaxEnd;
5314
static function f_Xml_GetPart(&$Txt, $TagLst, $AllIfNothing=false) {
5315
// Returns parts of the XML/HTML content, default is BODY.
5317
if (($TagLst===true) || ($TagLst==='')) $TagLst = 'body';
5321
$TagLst = explode('+',$TagLst);
5323
// Build a clean list of tags
5324
foreach ($TagLst as $i=>$t) {
5325
if ((substr($t,0,1)=='(') && (substr($t,-1,1)==')')) {
5326
$t = substr($t,1,strlen($t)-2);
5331
$TagLst[$i] = array('t'=>$t, 'k'=>$Keep, 'b'=>-1, 'e'=>-1, 's'=>false);
5334
$PosOut = strlen($Txt);
5337
// Optimized search for all tag types
5340
// Search next positions of each tag type
5341
$TagMin = false; // idx of the tag at first position
5342
$PosMin = $PosOut; // pos of the tag at first position
5343
foreach ($TagLst as $i=>$Tag) {
5344
if ($Tag['b']<$Pos) {
5345
$Loc = self::f_Xml_FindTag($Txt,$Tag['t'],true,$Pos,true,false,false);
5347
$Tag['b'] = $PosOut; // tag not found, no more search on this tag
5349
$Tag['b'] = $Loc->PosBeg;
5350
$Tag['e'] = $Loc->PosEnd;
5351
$Tag['s'] = (substr($Txt,$Loc->PosEnd-1,1)==='/'); // true if it's a single tag
5353
$TagLst[$i] = $Tag; // update
5355
if ($Tag['b']<$PosMin) {
5357
$PosMin = $Tag['b'];
5361
// Add the part of tag types
5362
if ($TagMin!==false) {
5363
$Tag = &$TagLst[$TagMin];
5367
if ($Tag['k']) $x .= substr($Txt,$Tag['b'] ,$Tag['e'] - $Tag['b'] + 1);
5369
// search the closing tag
5370
$Loc = self::f_Xml_FindTag($Txt,$Tag['t'],false,$Pos,true,false,false);
5372
$Tag['b'] = $PosOut; // closing tag not found, no more search on this tag
5376
$x .= substr($Txt,$Tag['b'] ,$Loc->PosEnd - $Tag['b'] + 1);
5378
$x .= substr($Txt,$Tag['e']+1,$Loc->PosBeg - $Tag['e'] - 1);
5380
$Pos = $Loc->PosEnd + 1;
5385
} while ($TagMin!==false);
5387
if ($AllIfNothing && $nothing) return $Txt;
5393
* Find the start position of an XML tag. Used by OpenTBS.
5394
* $Case=false can be useful for HTML.
5395
* $Tag='' should work and found the start of the first opening tag of any name.
5396
* $Tag='/' should work and found the start of the first closing tag of any name.
5397
* Encapsulation levels are not featured yet.
5399
static function f_Xml_FindTagStart(&$Txt,$Tag,$Opening,$PosBeg,$Forward,$Case=true) {
5401
if ($Txt==='') return false;
5403
$x = '<'.(($Opening) ? '' : '/').$Tag;
5406
$p = $PosBeg - (($Forward) ? 1 : -1);
5410
if ($Forward) $p = strpos($Txt,$x,$p+1); else $p = strrpos(substr($Txt,0,$p+1),$x);
5411
if ($p===false) return false;
5412
$z = substr($Txt,$p+$xl,1);
5413
} while ( ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') && ($Tag!=='/') && ($Tag!=='') );
5416
if ($Forward) $p = stripos($Txt,$x,$p+1); else $p = strripos(substr($Txt,0,$p+1),$x);
5417
if ($p===false) return false;
5418
$z = substr($Txt,$p+$xl,1);
5419
} while ( ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') && ($Tag!=='/') && ($Tag!=='') );
5427
* This function is a smart solution to find an XML tag.
5428
* It allows to ignore full opening/closing couple of tags that could be inserted before the searched tag.
5429
* It allows also to pass a number of encapsulations.
5430
* To ignore encapsulation and opengin/closing just set $LevelStop=false.
5431
* $Opening is used only when $LevelStop=false.
5433
static function f_Xml_FindTag(&$Txt,$Tag,$Opening,$PosBeg,$Forward,$LevelStop,$WithPrm,$WithPos=false) {
5435
if ($Tag==='_') { // New line
5436
$p = self::f_Xml_FindNewLine($Txt,$PosBeg,$Forward,($LevelStop!==0));
5437
$Loc = new clsTbsLocator;
5438
$Loc->PosBeg = ($Forward) ? $PosBeg : $p;
5439
$Loc->PosEnd = ($Forward) ? $p : $PosBeg;
5440
$Loc->RightLevel = 0;
5444
$Pos = $PosBeg + (($Forward) ? -1 : +1);
5445
$TagIsOpening = false;
5446
$TagClosing = '/'.$Tag;
5450
$TagL = strlen($Tag);
5451
$TagClosingL = strlen($TagClosing);
5456
// Look for the next tag def
5458
$Pos = strpos($Txt,'<',$Pos+1);
5463
$Pos = strrpos(substr($Txt,0,$Pos - 1),'<'); // strrpos() syntax compatible with PHP 4
5469
// Check the name of the tag
5470
if (strcasecmp(substr($Txt,$Pos+1,$TagL),$Tag)==0) {
5471
// It's an opening tag
5472
$PosX = $Pos + 1 + $TagL; // The next char
5474
$TagIsOpening = true;
5475
} elseif (strcasecmp(substr($Txt,$Pos+1,$TagClosingL),$TagClosing)==0) {
5476
// It's a closing tag
5477
$PosX = $Pos + 1 + $TagClosingL; // The next char
5479
$TagIsOpening = false;
5483
// Check the next char
5485
if (($x===' ') || ($x==="\r") || ($x==="\n") || ($x==='>') || ($x==='/') || ($Tag==='/') || ($Tag==='')) {
5486
// Check the encapsulation count
5487
if ($LevelStop===false) { // No encapsulation check
5488
if ($TagIsOpening!==$Opening) $TagOk = false;
5489
} else { // Count the number of level
5490
if ($TagIsOpening) {
5491
$PosEnd = strpos($Txt,'>',$PosX);
5492
if ($PosEnd!==false) {
5493
if ($Txt[$PosEnd-1]==='/') {
5494
if (($Pos<$PosBeg) && ($PosEnd>$PosBeg)) {$RightLevel=1; $LevelNum++;}
5502
// Check if it's the expected level
5503
if ($LevelNum!=$LevelStop) {
5514
} while (($Pos!==false) && ($TagOk===false));
5516
// Search for the end of the tag
5518
$Loc = new clsTbsLocator;
5520
self::f_Loc_PrmRead($Txt,$PosX,true,'\'"','<','>',$Loc,$PosEnd,$WithPos);
5521
} elseif ($PosEnd===false) {
5522
$PosEnd = strpos($Txt,'>',$PosX);
5523
if ($PosEnd===false) {
5531
$Loc->PosBeg = $Pos;
5532
$Loc->PosEnd = $PosEnd;
5533
$Loc->RightLevel = $RightLevel;
5541
static function f_Xml_FindNewLine(&$Txt,$PosBeg,$Forward,$IsRef) {
5547
$Sup = strlen($Txt)-1;
5555
if ($Inf>$Sup) return max($Sup,0);
5557
if (($x==="\r") || ($x==="\n")) {
5558
$x2 = ($x==="\n") ? "\r" : "\n";
5560
if (($Inf<$Sup) && ($Txt[$p+$Inc]===$x2)) $p += $Inc; // Newline char can have two chars.
5561
if ($Forward) return $p; // Forward => return pos including newline char.
5562
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.
5569
static function f_Xml_GetNextEntityName($Txt, $Pos, &$tag, &$PosBeg, &$p) {
5572
$PosBeg : position of the tag
5573
$p : position where the read has stop
5574
$z : first char after the name
5578
$PosBeg = strpos($Txt, '<', $Pos);
5580
if ($PosBeg===false) return false;
5582
// Read the name of the tag
5588
if ($go = ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') ) {