diff --git a/composer.json b/composer.json
index dda9b87..5214cf0 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "tinybutstrong/tinybutstrong",
- "description": "Template Engine for Pro and Beginners ",
+ "description": "Template Engine for Pro and Beginners",
"type": "library",
"keywords": ["templating"],
"homepage": "http://www.tinybutstrong.com",
diff --git a/tbs_class.php b/tbs_class.php
index 3581e16..f939288 100644
--- a/tbs_class.php
+++ b/tbs_class.php
@@ -1,6 +1,5 @@
TinyButStrong Error (PHP Version Check) : Your PHP version is '.PHP_VERSION.' while TinyButStrong needs PHP version 5.0 or higher. You should try with TinyButStrong Edition for PHP 4.';
-/* COMPAT#1 */
+if (version_compare(PHP_VERSION, '5.0') < 0) {
+ echo '
TinyButStrong Error (PHP Version Check) : Your PHP version is ' . PHP_VERSION . ' while TinyButStrong needs PHP version 5.0 or higher. You should try with TinyButStrong Edition for PHP 4.';
+}
// Render flags
define('TBS_NOTHING', 0);
@@ -27,4788 +27,5576 @@
define('TBS_INSTALL', -1);
define('TBS_ISINSTALLED', -3);
-// *********************************************
-
-class clsTbsLocator {
- public $PosBeg = false;
- public $PosEnd = false;
- public $Enlarged = false;
- public $FullName = false;
- public $SubName = '';
- public $SubOk = false;
- public $SubLst = array();
- public $SubNbr = 0;
- public $PrmLst = array();
- public $PrmIfNbr = false;
- public $MagnetId = false;
- public $BlockFound = false;
- public $FirstMerge = true;
- public $ConvProtect = true;
- public $ConvStr = true;
- public $ConvMode = 1; // Normal
- public $ConvBr = true;
-}
-
-// *********************************************
-
-class clsTbsDataSource {
-
-public $Type = false;
-public $SubType = 0;
-public $SrcId = false;
-public $Query = '';
-public $RecSet = false;
-public $RecKey = '';
-public $RecNum = 0;
-public $RecNumInit = 0;
-public $RecSaving = false;
-public $RecSaved = false;
-public $RecBuffer = false;
-public $CurrRec = false;
-public $TBS = false;
-public $OnDataOk = false;
-public $OnDataPrm = false;
-public $OnDataPrmDone = array();
-public $OnDataPi = false;
-
-public function DataAlert($Msg) {
- if (is_array($this->TBS->_CurrBlock)) {
- return $this->TBS->meth_Misc_Alert('when merging block "'.implode(',',$this->TBS->_CurrBlock).'"',$Msg);
- } else {
- return $this->TBS->meth_Misc_Alert('when merging block '.$this->TBS->_ChrOpen.$this->TBS->_CurrBlock.$this->TBS->_ChrClose,$Msg);
- }
-}
-
-public function DataPrepare(&$SrcId,&$TBS) {
-
- $this->SrcId = &$SrcId;
- $this->TBS = &$TBS;
- $FctInfo = false;
- $FctObj = false;
-
- if (is_array($SrcId)) {
- $this->Type = 0;
- } elseif (is_resource($SrcId)) {
-
- $Key = get_resource_type($SrcId);
- switch ($Key) {
- case 'mysql link' : $this->Type = 6; break;
- case 'mysql link persistent' : $this->Type = 6; break;
- case 'mysql result' : $this->Type = 6; $this->SubType = 1; break;
- case 'pgsql link' : $this->Type = 7; break;
- case 'pgsql link persistent' : $this->Type = 7; break;
- case 'pgsql result' : $this->Type = 7; $this->SubType = 1; break;
- case 'sqlite database' : $this->Type = 8; break;
- case 'sqlite database (persistent)' : $this->Type = 8; break;
- case 'sqlite result' : $this->Type = 8; $this->SubType = 1; break;
- default :
- $FctInfo = $Key;
- $FctCat = 'r';
- }
-
- } elseif (is_string($SrcId)) {
-
- switch (strtolower($SrcId)) {
- case 'array' : $this->Type = 0; $this->SubType = 1; break;
- case 'clear' : $this->Type = 0; $this->SubType = 3; break;
- case 'mysql' : $this->Type = 6; $this->SubType = 2; break;
- case 'text' : $this->Type = 2; break;
- case 'num' : $this->Type = 1; break;
- default :
- $FctInfo = $SrcId;
- $FctCat = 'k';
- }
-
- } elseif ($SrcId instanceof Iterator) {
- $this->Type = 9; $this->SubType = 1;
- } elseif ($SrcId instanceof ArrayObject) {
- $this->Type = 9; $this->SubType = 2;
- } elseif ($SrcId instanceof IteratorAggregate) {
- $this->Type = 9; $this->SubType = 3;
- } elseif ($SrcId instanceof MySQLi) {
- $this->Type = 10;
- } elseif ($SrcId instanceof PDO) {
- $this->Type = 11;
- } elseif ($SrcId instanceof Zend_Db_Adapter_Abstract) {
- $this->Type = 12;
- } elseif ($SrcId instanceof SQLite3) {
- $this->Type = 13; $this->SubType = 1;
- } elseif ($SrcId instanceof SQLite3Stmt) {
- $this->Type = 13; $this->SubType = 2;
- } elseif ($SrcId instanceof SQLite3Result) {
- $this->Type = 13; $this->SubType = 3;
- } elseif (is_object($SrcId)) {
- $FctInfo = get_class($SrcId);
- $FctCat = 'o';
- $FctObj = &$SrcId;
- $this->SrcId = &$SrcId;
- } elseif ($SrcId===false) {
- $this->DataAlert('the specified source is set to FALSE. Maybe your connection has failed.');
- } else {
- $this->DataAlert('unsupported variable type : \''.gettype($SrcId).'\'.');
- }
-
- if ($FctInfo!==false) {
- $ErrMsg = false;
- if ($TBS->meth_Misc_UserFctCheck($FctInfo,$FctCat,$FctObj,$ErrMsg,false)) {
- $this->Type = $FctInfo['type'];
- if ($this->Type!==5) {
- if ($this->Type===4) {
- $this->FctPrm = array(false,0);
- $this->SrcId = &$FctInfo['open'][0];
- }
- $this->FctOpen = &$FctInfo['open'];
- $this->FctFetch = &$FctInfo['fetch'];
- $this->FctClose = &$FctInfo['close'];
- }
- } else {
- $this->Type = $this->DataAlert($ErrMsg);
- }
- }
-
- return ($this->Type!==false);
-
-}
-
-public function DataOpen(&$Query,$QryPrms=false) {
-
- // Init values
- unset($this->CurrRec); $this->CurrRec = true;
- if ($this->RecSaved) {
- $this->FirstRec = true;
- unset($this->RecKey); $this->RecKey = '';
- $this->RecNum = $this->RecNumInit;
- if ($this->OnDataOk) $this->OnDataArgs[1] = &$this->CurrRec;
- return true;
- }
- unset($this->RecSet); $this->RecSet = false;
- $this->RecNumInit = 0;
- $this->RecNum = 0;
-
- if (isset($this->TBS->_piOnData)) {
- $this->OnDataPi = true;
- $this->OnDataPiRef = &$this->TBS->_piOnData;
- $this->OnDataOk = true;
- }
- if ($this->OnDataOk) {
- $this->OnDataArgs = array();
- $this->OnDataArgs[0] = &$this->TBS->_CurrBlock;
- $this->OnDataArgs[1] = &$this->CurrRec;
- $this->OnDataArgs[2] = &$this->RecNum;
- $this->OnDataArgs[3] = &$this->TBS;
- }
-
- switch ($this->Type) {
- case 0: // Array
- if (($this->SubType===1) && (is_string($Query))) $this->SubType = 2;
- if ($this->SubType===0) {
- $this->RecSet = &$this->SrcId; /* COMPAT#2 */
- } elseif ($this->SubType===1) {
- if (is_array($Query)) {
- $this->RecSet = &$Query; /* COMPAT#3 */
- } else {
- $this->DataAlert('type \''.gettype($Query).'\' not supported for the Query Parameter going with \'array\' Source Type.');
- }
- } elseif ($this->SubType===2) {
- // TBS query string for array and objects, syntax: "var[item1][item2]->item3[item4]..."
- $x = trim($Query);
- $z = chr(0);
- $x = str_replace(array(']->','][','->','['),$z,$x);
- if (substr($x,strlen($x)-1,1)===']') $x = substr($x,0,strlen($x)-1);
- $ItemLst = explode($z,$x);
- $ItemNbr = count($ItemLst);
- $Item0 = &$ItemLst[0];
- // Check first item
- if ($Item0[0]==='~') {
- $Item0 = substr($Item0,1);
- if ($this->TBS->ObjectRef!==false) {
- $Var = &$this->TBS->ObjectRef;
- $i = 0;
- } else {
- $i = $this->DataAlert('invalid query \''.$Query.'\' because property ObjectRef is not set.');
- }
- } else {
- if (isset($this->TBS->VarRef[$Item0])) {
- $Var = &$this->TBS->VarRef[$Item0]; /* COMPAT#4 */
- $i = 1;
- } else {
- $i = $this->DataAlert('invalid query \''.$Query.'\' because VarRef item \''.$Item0.'\' is not found.');
- }
- }
- // Check sub-items
- $Empty = false;
- while (($i!==false) && ($i<$ItemNbr) && ($Empty===false)) {
- $x = $ItemLst[$i];
- if (is_array($Var)) {
- if (isset($Var[$x])) {
- $Var = &$Var[$x];
- } else {
- $Empty = true;
- }
- } elseif (is_object($Var)) {
- $ArgLst = $this->TBS->f_Misc_CheckArgLst($x);
- if (method_exists($Var,$x)) {
- $f = array(&$Var,$x); unset($Var);
- $Var = call_user_func_array($f,$ArgLst);
- } elseif (property_exists(get_class($Var),$x)) {
- if (isset($Var->$x)) $Var = &$Var->$x;
- } elseif (isset($Var->$x)) {
- $Var = $Var->$x; // useful for overloaded property
- } else {
- $Empty = true;
- }
- } else {
- $i = $this->DataAlert('invalid query \''.$Query.'\' because item \''.$ItemLst[$i].'\' is neither an Array nor an Object. Its type is \''.gettype($Var).'\'.');
- }
- if ($i!==false) $i++;
- }
- // Assign data
- if ($i!==false) {
- if ($Empty) {
- $this->RecSet = array();
- } else {
- $this->RecSet = &$Var;
- }
- }
- } elseif ($this->SubType===3) { // Clear
- $this->RecSet = array();
- }
- // First record
- if ($this->RecSet!==false) {
- $this->RecNbr = $this->RecNumInit + count($this->RecSet);
- $this->FirstRec = true;
- $this->RecSaved = true;
- $this->RecSaving = false;
- }
- break;
- case 6: // MySQL
- switch ($this->SubType) {
- case 0: $this->RecSet = @mysql_query($Query,$this->SrcId); break;
- case 1: $this->RecSet = $this->SrcId; break;
- case 2: $this->RecSet = @mysql_query($Query); break;
- }
- if ($this->RecSet===false) $this->DataAlert('MySql error message when opening the query: '.mysql_error());
- break;
- case 1: // Num
- $this->RecSet = true;
- $this->NumMin = 1;
- $this->NumMax = 1;
- $this->NumStep = 1;
- if (is_array($Query)) {
- if (isset($Query['min'])) $this->NumMin = $Query['min'];
- if (isset($Query['step'])) $this->NumStep = $Query['step'];
- if (isset($Query['max'])) {
- $this->NumMax = $Query['max'];
- } else {
- $this->RecSet = $this->DataAlert('the \'num\' source is an array that has no value for the \'max\' key.');
- }
- if ($this->NumStep==0) $this->RecSet = $this->DataAlert('the \'num\' source is an array that has a step value set to zero.');
- } else {
- $this->NumMax = ceil($Query);
- }
- if ($this->RecSet) {
- if ($this->NumStep>0) {
- $this->NumVal = $this->NumMin;
- } else {
- $this->NumVal = $this->NumMax;
- }
- }
- break;
- case 2: // Text
- if (is_string($Query)) {
- $this->RecSet = &$Query;
- } else {
- $this->RecSet = $this->TBS->meth_Misc_ToStr($Query);
- }
- break;
- case 3: // Custom function
- $FctOpen = $this->FctOpen;
- $this->RecSet = $FctOpen($this->SrcId,$Query,$QryPrms);
- if ($this->RecSet===false) $this->DataAlert('function '.$FctOpen.'() has failed to open query {'.$Query.'}');
- break;
- case 4: // Custom method from ObjectRef
- $this->RecSet = call_user_func_array($this->FctOpen,array(&$this->SrcId,&$Query,&$QryPrms));
- if ($this->RecSet===false) $this->DataAlert('method '.get_class($this->FctOpen[0]).'::'.$this->FctOpen[1].'() has failed to open query {'.$Query.'}');
- break;
- case 5: // Custom method of object
- $this->RecSet = $this->SrcId->tbsdb_open($this->SrcId,$Query,$QryPrms);
- if ($this->RecSet===false) $this->DataAlert('method '.get_class($this->SrcId).'::tbsdb_open() has failed to open query {'.$Query.'}');
- break;
- case 7: // PostgreSQL
- switch ($this->SubType) {
- case 0: $this->RecSet = @pg_query($this->SrcId,$Query); break;
- case 1: $this->RecSet = $this->SrcId; break;
- }
- if ($this->RecSet===false) $this->DataAlert('PostgreSQL error message when opening the query: '.pg_last_error($this->SrcId));
- break;
- case 8: // SQLite
- switch ($this->SubType) {
- case 0: $this->RecSet = @sqlite_query($this->SrcId,$Query); break;
- case 1: $this->RecSet = $this->SrcId; break;
- }
- if ($this->RecSet===false) $this->DataAlert('SQLite error message when opening the query:'.sqlite_error_string(sqlite_last_error($this->SrcId)));
- break;
- case 9: // Iterator
- if ($this->SubType==1) {
- $this->RecSet = $this->SrcId;
- } else { // 2 or 3
- $this->RecSet = $this->SrcId->getIterator();
- }
- $this->RecSet->rewind();
- break;
- case 10: // MySQLi
- $this->RecSet = $this->SrcId->query($Query);
- if ($this->RecSet===false) $this->DataAlert('MySQLi error message when opening the query:'.$this->SrcId->error);
- break;
- case 11: // PDO
- $this->RecSet = $this->SrcId->prepare($Query);
- if ($this->RecSet===false) {
- $ok = false;
- } else {
- if (!is_array($QryPrms)) $QryPrms = array();
- $ok = $this->RecSet->execute($QryPrms);
- }
- if (!$ok) {
- $err = $this->SrcId->errorInfo();
- $this->DataAlert('PDO error message when opening the query:'.$err[2]);
- }
- break;
- case 12: // Zend_DB_Adapter
- try {
- if (!is_array($QryPrms)) $QryPrms = array();
- $this->RecSet = $this->SrcId->query($Query, $QryPrms);
- } catch (Exception $e) {
- $this->DataAlert('Zend_DB_Adapter error message when opening the query: '.$e->getMessage());
- }
- break;
- case 13: // SQLite3
- try {
- if ($this->SubType==3) {
- $this->RecSet = $this->SrcId;
- } elseif (($this->SubType==1) && (!is_array($QryPrms))) {
- // SQL statement without parameters
- $this->RecSet = $this->SrcId->query($Query);
- } else {
- if ($this->SubType==2) {
- $stmt = $this->SrcId;
- $prms = $Query;
- } else {
- // SQL statement with parameters
- $stmt = $this->SrcId->prepare($Query);
- $prms = $QryPrms;
- }
- // bind parameters
- if (is_array($prms)) {
- foreach ($prms as $p => $v) {
- if (is_numeric($p)) {
- $p = $p + 1;
- }
- if (is_array($v)) {
- $stmt->bindValue($p, $v[0], $v[1]);
- } else {
- $stmt->bindValue($p, $v);
- }
- }
- }
- $this->RecSet = $stmt->execute();
- }
- } catch (Exception $e) {
- $this->DataAlert('SQLite3 error message when opening the query: '.$e->getMessage());
- }
- break;
- }
-
- if (($this->Type===0) || ($this->Type===9)) {
- unset($this->RecKey); $this->RecKey = '';
- } else {
- if ($this->RecSaving) {
- unset($this->RecBuffer); $this->RecBuffer = array();
- }
- $this->RecKey = &$this->RecNum; // Not array: RecKey = RecNum
- }
-
- return ($this->RecSet!==false);
-
-}
-
-public function DataFetch() {
-
- if ($this->RecSaved) {
- if ($this->RecNum<$this->RecNbr) {
- if ($this->FirstRec) {
- if ($this->SubType===2) { // From string
- reset($this->RecSet);
- $this->RecKey = key($this->RecSet);
- $this->CurrRec = &$this->RecSet[$this->RecKey];
- } else {
- $this->CurrRec = reset($this->RecSet);
- $this->RecKey = key($this->RecSet);
- }
- $this->FirstRec = false;
- } else {
- if ($this->SubType===2) { // From string
- next($this->RecSet);
- $this->RecKey = key($this->RecSet);
- $this->CurrRec = &$this->RecSet[$this->RecKey];
- } else {
- $this->CurrRec = next($this->RecSet);
- $this->RecKey = key($this->RecSet);
- }
- }
- if ((!is_array($this->CurrRec)) && (!is_object($this->CurrRec))) $this->CurrRec = array('key'=>$this->RecKey, 'val'=>$this->CurrRec);
- $this->RecNum++;
- if ($this->OnDataOk) {
- $this->OnDataArgs[1] = &$this->CurrRec; // Reference has changed if ($this->SubType===2)
- if ($this->OnDataPrm) call_user_func_array($this->OnDataPrmRef,$this->OnDataArgs);
- if ($this->OnDataPi) $this->TBS->meth_PlugIn_RunAll($this->OnDataPiRef,$this->OnDataArgs);
- if ($this->SubType!==2) $this->RecSet[$this->RecKey] = $this->CurrRec; // save modifications because array reading is done without reference :(
- }
- } else {
- unset($this->CurrRec); $this->CurrRec = false;
- }
- return;
- }
-
- switch ($this->Type) {
- case 6: // MySQL
- $this->CurrRec = mysql_fetch_assoc($this->RecSet);
- break;
- case 1: // Num
- if (($this->NumVal>=$this->NumMin) && ($this->NumVal<=$this->NumMax)) {
- $this->CurrRec = array('val'=>$this->NumVal);
- $this->NumVal += $this->NumStep;
- } else {
- $this->CurrRec = false;
- }
- break;
- case 2: // Text
- if ($this->RecNum===0) {
- if ($this->RecSet==='') {
- $this->CurrRec = false;
- } else {
- $this->CurrRec = &$this->RecSet;
- }
- } else {
- $this->CurrRec = false;
- }
- break;
- case 3: // Custom function
- $FctFetch = $this->FctFetch;
- $this->CurrRec = $FctFetch($this->RecSet,$this->RecNum+1);
- break;
- case 4: // Custom method from ObjectRef
- $this->FctPrm[0] = &$this->RecSet; $this->FctPrm[1] = $this->RecNum+1;
- $this->CurrRec = call_user_func_array($this->FctFetch,$this->FctPrm);
- break;
- case 5: // Custom method of object
- $this->CurrRec = $this->SrcId->tbsdb_fetch($this->RecSet,$this->RecNum+1);
- break;
- case 7: // PostgreSQL
- $this->CurrRec = pg_fetch_assoc($this->RecSet); /* COMPAT#5 */
- break;
- case 8: // SQLite
- $this->CurrRec = sqlite_fetch_array($this->RecSet,SQLITE_ASSOC);
- break;
- case 9: // Iterator
- if ($this->RecSet->valid()) {
- $this->CurrRec = $this->RecSet->current();
- $this->RecKey = $this->RecSet->key();
- $this->RecSet->next();
- } else {
- $this->CurrRec = false;
- }
- break;
- case 10: // MySQLi
- $this->CurrRec = $this->RecSet->fetch_assoc();
- if (is_null($this->CurrRec)) $this->CurrRec = false;
- break;
- case 11: // PDO
- $this->CurrRec = $this->RecSet->fetch(PDO::FETCH_ASSOC);
- break;
- case 12: // Zend_DB_Adapater
- $this->CurrRec = $this->RecSet->fetch(Zend_Db::FETCH_ASSOC);
- break;
- case 13: // SQLite3
- $this->CurrRec = $this->RecSet->fetchArray(SQLITE3_ASSOC);
- break;
- }
-
- // Set the row count
- if ($this->CurrRec!==false) {
- $this->RecNum++;
- if ($this->OnDataOk) {
- if ($this->OnDataPrm) call_user_func_array($this->OnDataPrmRef,$this->OnDataArgs);
- if ($this->OnDataPi) $this->TBS->meth_PlugIn_RunAll($this->OnDataPiRef,$this->OnDataArgs);
- }
- if ($this->RecSaving) $this->RecBuffer[$this->RecKey] = $this->CurrRec;
- }
-
-}
-
-public function DataClose() {
- $this->OnDataOk = false;
- $this->OnDataPrm = false;
- $this->OnDataPi = false;
- if ($this->RecSaved) return;
- switch ($this->Type) {
- case 6: mysql_free_result($this->RecSet); break;
- case 3: $FctClose=$this->FctClose; $FctClose($this->RecSet); break;
- case 4: call_user_func_array($this->FctClose,array(&$this->RecSet)); break;
- case 5: $this->SrcId->tbsdb_close($this->RecSet); break;
- case 7: pg_free_result($this->RecSet); break;
- case 10: $this->RecSet->free(); break; // MySQLi
- case 13: // SQLite3
- if ($this->SubType!=3) {
- $this->RecSet->finalize();
- }
- break;
- //case 11: $this->RecSet->closeCursor(); break; // PDO
- }
- if ($this->RecSaving) {
- $this->RecSet = &$this->RecBuffer;
- $this->RecNbr = $this->RecNumInit + count($this->RecSet);
- $this->RecSaving = false;
- $this->RecSaved = true;
- }
-}
-
-}
-
-// *********************************************
-
-class clsTinyButStrong {
-
-// Public properties
-public $Source = '';
-public $Render = 3;
-public $TplVars = array();
-public $ObjectRef = false;
-public $NoErr = false;
-public $Assigned = array();
-public $ExtendedMethods = array();
-public $ErrCount = 0;
-// Undocumented (can change at any version)
-public $Version = '3.10.1';
-public $Charset = '';
-public $TurboBlock = true;
-public $VarPrefix = '';
-public $VarRef = null;
-public $FctPrefix = '';
-public $Protect = true;
-public $ErrMsg = '';
-public $AttDelim = false;
-public $MethodsAllowed = false;
-public $OnLoad = true;
-public $OnShow = true;
-public $IncludePath = array();
-public $TplStore = array();
-public $OldSubTpl = false;
-// Private
-public $_ErrMsgName = '';
-public $_LastFile = '';
-public $_CharsetFct = false;
-public $_Mode = 0;
-public $_CurrBlock = '';
-public $_ChrOpen = '[';
-public $_ChrClose = ']';
-public $_ChrVal = '[val]';
-public $_ChrProtect = '[';
-public $_PlugIns = array();
-public $_PlugIns_Ok = false;
-public $_piOnFrm_Ok = false;
-
-function __construct($Options=null,$VarPrefix='',$FctPrefix='') {
-
- // Compatibility
- if (is_string($Options)) {
- $Chrs = $Options;
- $Options = array('var_prefix'=>$VarPrefix, 'fct_prefix'=>$FctPrefix);
- if ($Chrs!=='') {
- $Err = true;
- $Len = strlen($Chrs);
- if ($Len===2) { // For compatibility
- $Options['chr_open'] = $Chrs[0];
- $Options['chr_close'] = $Chrs[1];
- $Err = false;
- } else {
- $Pos = strpos($Chrs,',');
- if (($Pos!==false) && ($Pos>0) && ($Pos<$Len-1)) {
- $Options['chr_open'] = substr($Chrs,0,$Pos);
- $Options['chr_close'] = substr($Chrs,$Pos+1);
- $Err = false;
- }
- }
- if ($Err) $this->meth_Misc_Alert('with clsTinyButStrong() function','value \''.$Chrs.'\' is a bad tag delimitor definition.');
- }
- }
-
- // Set options
- $this->VarRef =& $GLOBALS;
- if (is_array($Options)) $this->SetOption($Options);
-
- // Links to global variables (cannot be converted to static yet because of compatibility)
- global $_TBS_FormatLst, $_TBS_UserFctLst, $_TBS_BlockAlias, $_TBS_AutoInstallPlugIns, $_TBS_ParallelLst;
- if (!isset($_TBS_FormatLst)) $_TBS_FormatLst = array();
- if (!isset($_TBS_UserFctLst)) $_TBS_UserFctLst = array();
- if (!isset($_TBS_BlockAlias)) $_TBS_BlockAlias = array();
- if (!isset($_TBS_ParallelLst)) $_TBS_ParallelLst = array();
- $this->_UserFctLst = &$_TBS_UserFctLst;
-
- // Auto-installing plug-ins
- if (isset($_TBS_AutoInstallPlugIns)) foreach ($_TBS_AutoInstallPlugIns as $pi) $this->PlugIn(TBS_INSTALL,$pi);
-
-}
-
-function __call($meth, $args) {
- if (isset($this->ExtendedMethods[$meth])) {
- if ( is_array($this->ExtendedMethods[$meth]) || is_string($this->ExtendedMethods[$meth]) ) {
- return call_user_func_array($this->ExtendedMethods[$meth], $args);
- } else {
- return call_user_func_array(array(&$this->ExtendedMethods[$meth], $meth), $args);
- }
- } else {
- $this->meth_Misc_Alert('Method not found','\''.$meth.'\' is neither a native nor an extended method of TinyButStrong.');
- }
-}
-
-function SetOption($o, $v=false, $d=false) {
- if (!is_array($o)) $o = array($o=>$v);
- if (isset($o['var_prefix'])) $this->VarPrefix = $o['var_prefix'];
- if (isset($o['fct_prefix'])) $this->FctPrefix = $o['fct_prefix'];
- if (isset($o['noerr'])) $this->NoErr = $o['noerr'];
- if (isset($o['old_subtemplate'])) $this->OldSubTpl = $o['old_subtemplate'];
- if (isset($o['auto_merge'])) {
- $this->OnLoad = $o['auto_merge'];
- $this->OnShow = $o['auto_merge'];
- }
- if (isset($o['onload'])) $this->OnLoad = $o['onload'];
- if (isset($o['onshow'])) $this->OnShow = $o['onshow'];
- if (isset($o['att_delim'])) $this->AttDelim = $o['att_delim'];
- if (isset($o['protect'])) $this->Protect = $o['protect'];
- if (isset($o['turbo_block'])) $this->TurboBlock = $o['turbo_block'];
- if (isset($o['charset'])) $this->meth_Misc_Charset($o['charset']);
-
- $UpdateChr = false;
- if (isset($o['chr_open'])) {
- $this->_ChrOpen = $o['chr_open'];
- $UpdateChr = true;
- }
- if (isset($o['chr_close'])) {
- $this->_ChrClose = $o['chr_close'];
- $UpdateChr = true;
- }
- if ($UpdateChr) {
- $this->_ChrVal = $this->_ChrOpen.'val'.$this->_ChrClose;
- $this->_ChrProtect = ''.ord($this->_ChrOpen[0]).';'.substr($this->_ChrOpen,1);
- }
- if (array_key_exists('tpl_frms',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_FormatLst'], 'frm', $o['tpl_frms'], $d);
- if (array_key_exists('block_alias',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_BlockAlias'], false, $o['block_alias'], $d);
- if (array_key_exists('parallel_conf',$o)) self::f_Misc_UpdateArray($GLOBALS['_TBS_ParallelLst'], false, $o['parallel_conf'], $d);
- if (array_key_exists('include_path',$o)) self::f_Misc_UpdateArray($this->IncludePath, true, $o['include_path'], $d);
- if (isset($o['render'])) $this->Render = $o['render'];
- if (isset($o['methods_allowed'])) $this->MethodsAllowed = $o['methods_allowed'];
-}
-
-function GetOption($o) {
- if ($o==='all') {
- $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');
- $r = array();
- foreach ($x as $o) $r[$o] = $this->GetOption($o);
- return $r;
- }
- if ($o==='var_prefix') return $this->VarPrefix;
- if ($o==='fct_prefix') return $this->FctPrefix;
- if ($o==='noerr') return $this->NoErr;
- if ($o==='auto_merge') return ($this->OnLoad && $this->OnShow);
- if ($o==='onload') return $this->OnLoad;
- if ($o==='onshow') return $this->OnShow;
- if ($o==='att_delim') return $this->AttDelim;
- if ($o==='protect') return $this->Protect;
- if ($o==='turbo_block') return $this->TurboBlock;
- if ($o==='charset') return $this->Charset;
- if ($o==='chr_open') return $this->_ChrOpen;
- if ($o==='chr_close') return $this->_ChrClose;
- if ($o==='tpl_frms') {
- // simplify the list of formats
- $x = array();
- foreach ($GLOBALS['_TBS_FormatLst'] as $s=>$i) $x[$s] = $i['Str'];
- return $x;
- }
- if ($o==='include_path') return $this->IncludePath;
- if ($o==='render') return $this->Render;
- if ($o==='methods_allowed') return $this->MethodsAllowed;
- if ($o==='parallel_conf') return $GLOBALS['_TBS_ParallelLst'];
- if ($o==='block_alias') return $GLOBALS['_TBS_BlockAlias'];
- return $this->meth_Misc_Alert('with GetOption() method','option \''.$o.'\' is not supported.');;
-}
-
-public function ResetVarRef($ToGlobal) {
- if ($ToGlobal) {
- $this->VarRef = &$GLOBALS;
- } else {
- $x = array();
- $this->VarRef = &$x;
- }
-}
-
-// Public methods
-public function LoadTemplate($File,$Charset='') {
- if ($File==='') {
- $this->meth_Misc_Charset($Charset);
- return true;
- }
- $Ok = true;
- if ($this->_PlugIns_Ok) {
- if (isset($this->_piBeforeLoadTemplate) || isset($this->_piAfterLoadTemplate)) {
- // Plug-ins
- $ArgLst = func_get_args();
- $ArgLst[0] = &$File;
- $ArgLst[1] = &$Charset;
- if (isset($this->_piBeforeLoadTemplate)) $Ok = $this->meth_PlugIn_RunAll($this->_piBeforeLoadTemplate,$ArgLst);
- }
- }
- // Load the file
- if ($Ok!==false) {
- if (!is_null($File)) {
- $x = '';
- 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.');
- if ($Charset==='+') {
- $this->Source .= $x;
- } else {
- $this->Source = $x;
- }
- }
- if ($this->meth_Misc_IsMainTpl()) {
- if (!is_null($File)) $this->_LastFile = $File;
- if ($Charset!=='+') $this->TplVars = array();
- $this->meth_Misc_Charset($Charset);
- }
- // Automatic fields and blocks
- if ($this->OnLoad) $this->meth_Merge_AutoOn($this->Source,'onload',true,true);
- }
- // Plug-ins
- if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterLoadTemplate)) $Ok = $this->meth_PlugIn_RunAll($this->_piAfterLoadTemplate,$ArgLst);
- return $Ok;
-}
-
-public function GetBlockSource($BlockName,$AsArray=false,$DefTags=true,$ReplaceWith=false) {
- $RetVal = array();
- $Nbr = 0;
- $Pos = 0;
- $FieldOutside = false;
- $P1 = false;
- $Mode = ($DefTags) ? 3 : 2;
- $PosBeg1 = 0;
- while ($Loc = $this->meth_Locator_FindBlockNext($this->Source,$BlockName,$Pos,'.',$Mode,$P1,$FieldOutside)) {
- $Nbr++;
- $Sep = '';
- if ($Nbr==1) {
- $PosBeg1 = $Loc->PosBeg;
- } elseif (!$AsArray) {
- $Sep = substr($this->Source,$PosSep,$Loc->PosBeg-$PosSep); // part of the source between sections
- }
- $RetVal[$Nbr] = $Sep.$Loc->BlockSrc;
- $Pos = $Loc->PosEnd;
- $PosSep = $Loc->PosEnd+1;
- $P1 = false;
- }
- if ($Nbr==0) return false;
- if (!$AsArray) {
- if ($DefTags) {
- // Return the true part of the template
- $RetVal = substr($this->Source,$PosBeg1,$Pos-$PosBeg1+1);
- } else {
- // Return the concatenated section without def tags
- $RetVal = implode('', $RetVal);
- }
- }
- if ($ReplaceWith!==false) $this->Source = substr($this->Source,0,$PosBeg1).$ReplaceWith.substr($this->Source,$Pos+1);
- return $RetVal;
-}
-
-public function MergeBlock($BlockLst,$SrcId='assigned',$Query='',$QryPrms=false) {
-
- if ($SrcId==='assigned') {
- $Arg = array($BlockLst,&$SrcId,&$Query,&$QryPrms);
- if (!$this->meth_Misc_Assign($BlockLst, $Arg, 'MergeBlock')) return 0;
- $BlockLst = $Arg[0]; $SrcId = &$Arg[1]; $Query = &$Arg[2];
- }
-
- if (is_string($BlockLst)) $BlockLst = explode(',',$BlockLst);
-
- if ($SrcId==='cond') {
- $Nbr = 0;
- foreach ($BlockLst as $Block) {
- $Block = trim($Block);
- if ($Block!=='') $Nbr += $this->meth_Merge_AutoOn($this->Source,$Block,true,true);
- }
- return $Nbr;
- } else {
- return $this->meth_Merge_Block($this->Source,$BlockLst,$SrcId,$Query,false,0,$QryPrms);
- }
-
-}
-
-public function MergeField($NameLst,$Value='assigned',$IsUserFct=false,$DefaultPrm=false) {
-
- $FctCheck = $IsUserFct;
- if ($PlugIn = isset($this->_piOnMergeField)) $ArgPi = array('','',&$Value,0,&$this->Source,0,0);
- $SubStart = 0;
- $Ok = true;
- $Prm = is_array($DefaultPrm);
-
- if ( ($Value==='assigned') && ($NameLst!=='var') && ($NameLst!=='onshow') && ($NameLst!=='onload') ) {
- $Arg = array($NameLst,&$Value,&$IsUserFct,&$DefaultPrm);
- if (!$this->meth_Misc_Assign($NameLst, $Arg, 'MergeField')) return false;
- $NameLst = $Arg[0]; $Value = &$Arg[1]; $IsUserFct = &$Arg[2]; $DefaultPrm = &$Arg[3];
- }
-
- $NameLst = explode(',',$NameLst);
-
- foreach ($NameLst as $Name) {
- $Name = trim($Name);
- $Cont = false;
- switch ($Name) {
- case '': $Cont=true;break;
- case 'onload': $this->meth_Merge_AutoOn($this->Source,'onload',true,true);$Cont=true;break;
- case 'onshow': $this->meth_Merge_AutoOn($this->Source,'onshow',true,true);$Cont=true;break;
- case 'var': $this->meth_Merge_AutoVar($this->Source,true);$Cont=true;break;
- }
- if ($Cont) continue;
- if ($PlugIn) $ArgPi[0] = $Name;
- $PosBeg = 0;
- // Initilize the user function (only once)
- if ($FctCheck) {
- $FctInfo = $Value;
- $ErrMsg = false;
- if (!$this->meth_Misc_UserFctCheck($FctInfo,'f',$ErrMsg,$ErrMsg,false)) return $this->meth_Misc_Alert('with MergeField() method',$ErrMsg);
- $FctArg = array('','');
- $SubStart = false;
- $FctCheck = false;
- }
- while ($Loc = $this->meth_Locator_FindTbs($this->Source,$Name,$PosBeg,'.')) {
- if ($Prm) $Loc->PrmLst = array_merge($DefaultPrm,$Loc->PrmLst);
- // Apply user function
- if ($IsUserFct) {
- $FctArg[0] = &$Loc->SubName; $FctArg[1] = &$Loc->PrmLst;
- $Value = call_user_func_array($FctInfo,$FctArg);
- }
- // Plug-ins
- if ($PlugIn) {
- $ArgPi[1] = $Loc->SubName; $ArgPi[3] = &$Loc->PrmLst; $ArgPi[5] = &$Loc->PosBeg; $ArgPi[6] = &$Loc->PosEnd;
- $Ok = $this->meth_PlugIn_RunAll($this->_piOnMergeField,$ArgPi);
- }
- // Merge the field
- if ($Ok) {
- $PosBeg = $this->meth_Locator_Replace($this->Source,$Loc,$Value,$SubStart);
- } else {
- $PosBeg = $Loc->PosEnd;
- }
- }
- }
-}
-
-public function Show($Render=false) {
- $Ok = true;
- if ($Render===false) $Render = $this->Render;
- if ($this->_PlugIns_Ok) {
- if (isset($this->_piBeforeShow) || isset($this->_piAfterShow)) {
- // Plug-ins
- $ArgLst = func_get_args();
- $ArgLst[0] = &$Render;
- if (isset($this->_piBeforeShow)) $Ok = $this->meth_PlugIn_RunAll($this->_piBeforeShow,$ArgLst);
- }
- }
- if ($Ok!==false) {
- if ($this->OnShow) $this->meth_Merge_AutoOn($this->Source,'onshow',true,true);
- $this->meth_Merge_AutoVar($this->Source,true);
- }
- if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterShow)) $this->meth_PlugIn_RunAll($this->_piAfterShow,$ArgLst);
- if ($this->_ErrMsgName!=='') $this->MergeField($this->_ErrMsgName, $this->ErrMsg);
- if ($this->meth_Misc_IsMainTpl()) {
- if (($Render & TBS_OUTPUT)==TBS_OUTPUT) echo $this->Source;
- if (($Render & TBS_EXIT)==TBS_EXIT) exit;
- } elseif ($this->OldSubTpl) {
- if (($Render & TBS_OUTPUT)==TBS_OUTPUT) echo $this->Source;
- }
- return $Ok;
-}
-
-public function PlugIn($Prm1,$Prm2=0) {
-
- if (is_numeric($Prm1)) {
- switch ($Prm1) {
- case TBS_INSTALL: // Try to install the plug-in
- $PlugInId = $Prm2;
- if (isset($this->_PlugIns[$PlugInId])) {
- return $this->meth_Misc_Alert('with PlugIn() method','plug-in \''.$PlugInId.'\' is already installed.');
- } else {
- $ArgLst = func_get_args();
- array_shift($ArgLst); array_shift($ArgLst);
- return $this->meth_PlugIn_Install($PlugInId,$ArgLst,false);
- }
- case TBS_ISINSTALLED: // Check if the plug-in is installed
- return isset($this->_PlugIns[$Prm2]);
- case -4: // Deactivate special plug-ins
- $this->_PlugIns_Ok_save = $this->_PlugIns_Ok;
- $this->_PlugIns_Ok = false;
- return true;
- case -5: // Deactivate OnFormat
- $this->_piOnFrm_Ok_save = $this->_piOnFrm_Ok;
- $this->_piOnFrm_Ok = false;
- return true;
- case -10: // Restore
- if (isset($this->_PlugIns_Ok_save)) $this->_PlugIns_Ok = $this->_PlugIns_Ok_save;
- if (isset($this->_piOnFrm_Ok_save)) $this->_piOnFrm_Ok = $this->_piOnFrm_Ok_save;
- return true;
- }
-
- } elseif (is_string($Prm1)) {
- // Plug-in's command
- $p = strpos($Prm1,'.');
- if ($p===false) {
- $PlugInId = $Prm1;
- } else {
- $PlugInId = substr($Prm1,0,$p); // direct command
- }
- if (!isset($this->_PlugIns[$PlugInId])) {
- if (!$this->meth_PlugIn_Install($PlugInId,array(),true)) return false;
- }
- 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.');
- $ArgLst = func_get_args();
- if ($p===false) array_shift($ArgLst);
- $Ok = call_user_func_array($this->_piOnCommand[$PlugInId],$ArgLst);
- if (is_null($Ok)) $Ok = true;
- return $Ok;
- }
- return $this->meth_Misc_Alert('with PlugIn() method','\''.$Prm1.'\' is an invalid plug-in key, the type of the value is \''.gettype($Prm1).'\'.');
-
-}
-
-// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
-
-function meth_Locator_FindTbs(&$Txt,$Name,$Pos,$ChrSub) {
-// Find a TBS Locator
-
- $PosEnd = false;
- $PosMax = strlen($Txt) -1;
- $Start = $this->_ChrOpen.$Name;
-
- do {
- // Search for the opening char
- if ($Pos>$PosMax) return false;
- $Pos = strpos($Txt,$Start,$Pos);
-
- // If found => next chars are analyzed
- if ($Pos===false) {
- return false;
- } else {
- $Loc = new clsTbsLocator;
- $ReadPrm = false;
- $PosX = $Pos + strlen($Start);
- $x = $Txt[$PosX];
-
- if ($x===$this->_ChrClose) {
- $PosEnd = $PosX;
- } elseif ($x===$ChrSub) {
- $Loc->SubOk = true; // it is no longer the false value
- $ReadPrm = true;
- $PosX++;
- } elseif (strpos(';',$x)!==false) {
- $ReadPrm = true;
- $PosX++;
- } else {
- $Pos++;
- }
-
- $Loc->PosBeg = $Pos;
- if ($ReadPrm) {
- self::f_Loc_PrmRead($Txt,$PosX,false,'\'',$this->_ChrOpen,$this->_ChrClose,$Loc,$PosEnd);
- if ($PosEnd===false) {
- $this->meth_Misc_Alert('','can\'t found the end of the tag \''.substr($Txt,$Pos,$PosX-$Pos+10).'...\'.');
- $Pos++;
- }
- }
-
- }
-
- } while ($PosEnd===false);
-
- $Loc->PosEnd = $PosEnd;
- if ($Loc->SubOk) {
- $Loc->FullName = $Name.'.'.$Loc->SubName;
- $Loc->SubLst = explode('.',$Loc->SubName);
- $Loc->SubNbr = count($Loc->SubLst);
- } else {
- $Loc->FullName = $Name;
- }
- if ( $ReadPrm && ( isset($Loc->PrmLst['enlarge']) || isset($Loc->PrmLst['comm']) ) ) {
- $Loc->PosBeg0 = $Loc->PosBeg;
- $Loc->PosEnd0 = $Loc->PosEnd;
- $enlarge = (isset($Loc->PrmLst['enlarge'])) ? $Loc->PrmLst['enlarge'] : $Loc->PrmLst['comm'];
- if (($enlarge===true) || ($enlarge==='')) {
- $Loc->Enlarged = self::f_Loc_EnlargeToStr($Txt,$Loc,'');
- } else {
- $Loc->Enlarged = self::f_Loc_EnlargeToTag($Txt,$Loc,$enlarge,false);
- }
- }
-
- return $Loc;
-
-}
-
-function &meth_Locator_SectionNewBDef(&$LocR,$BlockName,$Txt,$PrmLst,$Cache) {
-
- $Chk = true;
- $LocLst = array();
- $Pos = 0;
- $Sort = false;
-
- if ($this->_PlugIns_Ok && isset($this->_piOnCacheField)) {
- $pi = true;
- $ArgLst = array(0=>$BlockName, 1=>false, 2=>&$Txt, 3=>array('att'=>true), 4=>&$LocLst, 5=>&$Pos);
- } else {
- $pi = false;
- }
-
- // Cache TBS locators
- $Cache = ($Cache && $this->TurboBlock);
- if ($Cache) {
-
- $Chk = false;
- while ($Loc = $this->meth_Locator_FindTbs($Txt,$BlockName,$Pos,'.')) {
-
- $LocNbr = 1 + count($LocLst);
- $LocLst[$LocNbr] = &$Loc;
-
- // Next search position : always ("original PosBeg" + 1).
- // Must be done here because loc can be moved by the plug-in.
- if ($Loc->Enlarged) {
- // Enlarged
- $Pos = $Loc->PosBeg0 + 1;
- $Loc->Enlarged = false;
- } else {
- // Normal
- $Pos = $Loc->PosBeg + 1;
- }
-
- // Note: the plug-in may move, delete and add one or several locs.
- // Move : backward or forward (will be sorted)
- // Delete : add property DelMe=true
- // Add : at the end of $LocLst (will be sorted)
- if ($pi) {
- $ArgLst[1] = &$Loc;
- $this->meth_Plugin_RunAll($this->_piOnCacheField,$ArgLst);
- }
-
- if (($Loc->SubName==='#') || ($Loc->SubName==='$')) {
- $Loc->IsRecInfo = true;
- $Loc->RecInfo = $Loc->SubName;
- $Loc->SubName = '';
- } else {
- $Loc->IsRecInfo = false;
- }
-
- // Process parameter att for new added locators.
- $NewNbr = count($LocLst);
- for ($i=$LocNbr;$i<=$NewNbr;$i++) {
- $li = &$LocLst[$i];
- if (isset($li->PrmLst['att'])) {
- $LocSrc = substr($Txt,$li->PosBeg,$li->PosEnd-$li->PosBeg+1); // for error message
- if ($this->f_Xml_AttFind($Txt,$li,$LocLst,$this->AttDelim)) {
- if (isset($Loc->PrmLst['atttrue'])) {
- $li->PrmLst['magnet'] = '#';
- $li->PrmLst['ope'] = (isset($li->PrmLst['ope'])) ? $li->PrmLst['ope'].',attbool' : 'attbool';
- }
- if ($i==$LocNbr) {
- $Pos = $Loc->DelPos;
- }
- } else {
- $this->meth_Misc_Alert('','TBS is not able to merge the field '.$LocSrc.' because the entity targeted by parameter \'att\' cannot be found.');
- }
- }
- }
-
- unset($Loc);
-
- }
-
- // Re-order loc
- $e = self::f_Loc_Sort($LocLst, true, 1);
- $Chk = ($e > 0);
-
- }
-
- // Create the object
- $o = (object) null;
- $o->Prm = $PrmLst;
- $o->LocLst = $LocLst;
- $o->LocNbr = count($LocLst);
- $o->Name = $BlockName;
- $o->Src = $Txt;
- $o->Chk = $Chk;
- $o->IsSerial = false;
- $o->AutoSub = false;
- $i = 1;
- while (isset($PrmLst['sub'.$i])) {
- $o->AutoSub = $i;
- $i++;
- }
-
- $LocR->BDefLst[] = &$o; // Can be usefull for plug-in
- return $o;
-
-}
-
-function meth_Locator_SectionAddGrp(&$LocR,$BlockName,&$BDef,$Type,$Field,$Prm) {
-
- $BDef->PrevValue = false;
- $BDef->Type = $Type;
-
- // Save sub items in a structure near to Locator.
- $Field0 = $Field;
- if (strpos($Field,$this->_ChrOpen)===false) $Field = $this->_ChrOpen.$BlockName.'.'.$Field.';tbstype='.$Prm.$this->_ChrClose; // tbstype is an internal parameter for catching errors
- $BDef->FDef = &$this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Field,array(),true);
- if ($BDef->FDef->LocNbr==0) $this->meth_Misc_Alert('Parameter '.$Prm,'The value \''.$Field0.'\' is unvalide for this parameter.');
-
- if ($Type==='H') {
- if ($LocR->HeaderFound===false) {
- $LocR->HeaderFound = true;
- $LocR->HeaderNbr = 0;
- $LocR->HeaderDef = array(); // 1 to HeaderNbr
- }
- $i = ++$LocR->HeaderNbr;
- $LocR->HeaderDef[$i] = &$BDef;
- } else {
- if ($LocR->FooterFound===false) {
- $LocR->FooterFound = true;
- $LocR->FooterNbr = 0;
- $LocR->FooterDef = array(); // 1 to FooterNbr
- }
- $BDef->AddLastGrp = ($Type==='F');
- $i = ++$LocR->FooterNbr;
- $LocR->FooterDef[$i] = &$BDef;
- }
-
-}
-
-function meth_Locator_Replace(&$Txt,&$Loc,&$Value,$SubStart) {
-// This function enables to merge a locator with a text and returns the position just after the replaced block
-// This position can be useful because we don't know in advance how $Value will be replaced.
-
- // Found the value if there is a subname
- if (($SubStart!==false) && $Loc->SubOk) {
- for ($i=$SubStart;$i<$Loc->SubNbr;$i++) {
- $x = $Loc->SubLst[$i]; // &$Loc... brings an error with Event Example, I don't know why.
- if (is_array($Value)) {
- if (isset($Value[$x])) {
- $Value = &$Value[$x];
- } elseif (array_key_exists($x,$Value)) {// can happens when value is NULL
- $Value = &$Value[$x];
- } else {
- if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'item \''.$x.'\' is not an existing key in the array.',true);
- unset($Value); $Value = ''; break;
- }
- } elseif (is_object($Value)) {
- $ArgLst = $this->f_Misc_CheckArgLst($x);
- if (method_exists($Value,$x)) {
- if ($this->MethodsAllowed || !in_array(strtok($Loc->FullName,'.'),array('onload','onshow','var')) ) {
- $x = call_user_func_array(array(&$Value,$x),$ArgLst);
- } else {
- if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'\''.$x.'\' is a method and the current TBS settings do not allow to call methods on automatic fields.',true);
- $x = '';
- }
- } elseif (property_exists($Value,$x)) {
- $x = &$Value->$x;
- } elseif (isset($Value->$x)) {
- $x = $Value->$x; // useful for overloaded property
- } else {
- if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'item '.$x.'\' is neither a method nor a property in the class \''.get_class($Value).'\'.',true);
- unset($Value); $Value = ''; break;
- }
- $Value = &$x; unset($x); $x = '';
- } else {
- 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);
- unset($Value); $Value = ''; break;
- }
- }
- }
-
- $CurrVal = $Value; // Unlink
-
- if (isset($Loc->PrmLst['onformat'])) {
- if ($Loc->FirstMerge) {
- $Loc->OnFrmInfo = $Loc->PrmLst['onformat'];
- $Loc->OnFrmArg = array($Loc->FullName,'',&$Loc->PrmLst,&$this);
- $ErrMsg = false;
- if (!$this->meth_Misc_UserFctCheck($Loc->OnFrmInfo,'f',$ErrMsg,$ErrMsg,true)) {
- unset($Loc->PrmLst['onformat']);
- if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'(parameter onformat) '.$ErrMsg);
- $Loc->OnFrmInfo = 'pi'; // Execute the function pi() just to avoid extra error messages
- }
- } else {
- $Loc->OnFrmArg[3] = &$this; // bugs.php.net/51174
- }
- $Loc->OnFrmArg[1] = &$CurrVal;
- if (isset($Loc->PrmLst['subtpl'])) {
- $this->meth_Misc_ChangeMode(true,$Loc,$CurrVal);
- call_user_func_array($Loc->OnFrmInfo,$Loc->OnFrmArg);
- $this->meth_Misc_ChangeMode(false,$Loc,$CurrVal);
- $Loc->ConvProtect = false;
- $Loc->ConvStr = false;
- } else {
- call_user_func_array($Loc->OnFrmInfo,$Loc->OnFrmArg);
- }
- }
-
- if ($Loc->FirstMerge) {
- if (isset($Loc->PrmLst['frm'])) {
- $Loc->ConvMode = 0; // Frm
- $Loc->ConvProtect = false;
- } else {
- // Analyze parameter 'strconv'
- if (isset($Loc->PrmLst['strconv'])) {
- $this->meth_Conv_Prepare($Loc, $Loc->PrmLst['strconv']);
- } elseif (isset($Loc->PrmLst['htmlconv'])) { // compatibility
- $this->meth_Conv_Prepare($Loc, $Loc->PrmLst['htmlconv']);
- } else {
- if ($this->Charset===false) $Loc->ConvStr = false; // No conversion
- }
- // Analyze parameter 'protect'
- if (isset($Loc->PrmLst['protect'])) {
- $x = strtolower($Loc->PrmLst['protect']);
- if ($x==='no') {
- $Loc->ConvProtect = false;
- } elseif ($x==='yes') {
- $Loc->ConvProtect = true;
- }
- } elseif ($this->Protect===false) {
- $Loc->ConvProtect = false;
- }
- }
- if ($Loc->Ope = isset($Loc->PrmLst['ope'])) {
- $OpeLst = explode(',',$Loc->PrmLst['ope']);
- $Loc->OpeAct = array();
- $Loc->OpeArg = array();
- $Loc->OpeUtf8 = false;
- foreach ($OpeLst as $i=>$ope) {
- if ($ope==='list') {
- $Loc->OpeAct[$i] = 1;
- $Loc->OpePrm[$i] = (isset($Loc->PrmLst['valsep'])) ? $Loc->PrmLst['valsep'] : ',';
- if (($Loc->ConvMode===1) && $Loc->ConvStr) $Loc->ConvMode = -1; // special mode for item list conversion
- } elseif ($ope==='minv') {
- $Loc->OpeAct[$i] = 11;
- $Loc->MSave = $Loc->MagnetId;
- } elseif ($ope==='attbool') { // this operation key is set when a loc is cached with paremeter atttrue
- $Loc->OpeAct[$i] = 14;
- } elseif ($ope==='utf8') { $Loc->OpeUtf8 = true;
- } elseif ($ope==='upper') { $Loc->OpeAct[$i] = 15;
- } elseif ($ope==='lower') { $Loc->OpeAct[$i] = 16;
- } elseif ($ope==='upper1') { $Loc->OpeAct[$i] = 17;
- } elseif ($ope==='upperw') { $Loc->OpeAct[$i] = 18;
- } else {
- $x = substr($ope,0,4);
- if ($x==='max:') {
- $Loc->OpeAct[$i] = (isset($Loc->PrmLst['maxhtml'])) ? 2 : 3;
- if (isset($Loc->PrmLst['maxutf8'])) $Loc->OpeUtf8 = true;
- $Loc->OpePrm[$i] = intval(trim(substr($ope,4)));
- $Loc->OpeEnd = (isset($Loc->PrmLst['maxend'])) ? $Loc->PrmLst['maxend'] : '...';
- if ($Loc->OpePrm[$i]<=0) $Loc->Ope = false;
- } elseif ($x==='mod:') {$Loc->OpeAct[$i] = 5; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4));
- } elseif ($x==='add:') {$Loc->OpeAct[$i] = 6; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4));
- } elseif ($x==='mul:') {$Loc->OpeAct[$i] = 7; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4));
- } elseif ($x==='div:') {$Loc->OpeAct[$i] = 8; $Loc->OpePrm[$i] = '0'+trim(substr($ope,4));
- } elseif ($x==='mok:') {$Loc->OpeAct[$i] = 9; $Loc->OpeMOK[] = trim(substr($ope,4)); $Loc->MSave = $Loc->MagnetId;
- } elseif ($x==='mko:') {$Loc->OpeAct[$i] =10; $Loc->OpeMKO[] = trim(substr($ope,4)); $Loc->MSave = $Loc->MagnetId;
- } elseif ($x==='nif:') {$Loc->OpeAct[$i] =12; $Loc->OpePrm[$i] = trim(substr($ope,4));
- } elseif ($x==='msk:') {$Loc->OpeAct[$i] =13; $Loc->OpePrm[$i] = trim(substr($ope,4));
- } elseif (isset($this->_piOnOperation)) {
- $Loc->OpeAct[$i] = 0;
- $Loc->OpePrm[$i] = $ope;
- $Loc->OpeArg[$i] = array($Loc->FullName,&$CurrVal,&$Loc->PrmLst,&$Txt,$Loc->PosBeg,$Loc->PosEnd,&$Loc);
- $Loc->PrmLst['_ope'] = $Loc->PrmLst['ope'];
- } elseif (!isset($Loc->PrmLst['noerr'])) {
- $this->meth_Misc_Alert($Loc,'parameter ope doesn\'t support value \''.$ope.'\'.',true);
- }
- }
- }
- }
- $Loc->FirstMerge = false;
- }
- $ConvProtect = $Loc->ConvProtect;
-
- // Plug-in OnFormat
- if ($this->_piOnFrm_Ok) {
- if (isset($Loc->OnFrmArgPi)) {
- $Loc->OnFrmArgPi[1] = &$CurrVal;
- $Loc->OnFrmArgPi[3] = &$this; // bugs.php.net/51174
- } else {
- $Loc->OnFrmArgPi = array($Loc->FullName,&$CurrVal,&$Loc->PrmLst,&$this);
- }
- $this->meth_PlugIn_RunAll($this->_piOnFormat,$Loc->OnFrmArgPi);
- }
-
- // Operation
- if ($Loc->Ope) {
- foreach ($Loc->OpeAct as $i=>$ope) {
- switch ($ope) {
- case 0:
- $Loc->PrmLst['ope'] = $Loc->OpePrm[$i]; // for compatibility
- $OpeArg = &$Loc->OpeArg[$i];
- $OpeArg[1] = &$CurrVal; $OpeArg[3] = &$Txt;
- if (!$this->meth_PlugIn_RunAll($this->_piOnOperation,$OpeArg)) return $Loc->PosBeg;
- break;
- case 1:
- if ($Loc->ConvMode===-1) {
- if (is_array($CurrVal)) {
- foreach ($CurrVal as $k=>$v) {
- $v = $this->meth_Misc_ToStr($v);
- $this->meth_Conv_Str($v,$Loc->ConvBr);
- $CurrVal[$k] = $v;
- }
- $CurrVal = implode($Loc->OpePrm[$i],$CurrVal);
- } else {
- $CurrVal = $this->meth_Misc_ToStr($CurrVal);
- $this->meth_Conv_Str($CurrVal,$Loc->ConvBr);
- }
- } else {
- if (is_array($CurrVal)) $CurrVal = implode($Loc->OpePrm[$i],$CurrVal);
- }
- break;
- case 2:
- $x = $this->meth_Misc_ToStr($CurrVal);
- if (strlen($x)>$Loc->OpePrm[$i]) {
- $this->f_Xml_Max($x,$Loc->OpePrm[$i],$Loc->OpeEnd);
- }
- break;
- case 3:
- $x = $this->meth_Misc_ToStr($CurrVal);
- if (strlen($x)>$Loc->OpePrm[$i]) {
- if ($Loc->OpeUtf8) {
- $CurrVal = mb_substr($x,0,$Loc->OpePrm[$i],'UTF-8').$Loc->OpeEnd;
- } else {
- $CurrVal = substr($x,0,$Loc->OpePrm[$i]).$Loc->OpeEnd;
- }
- }
- break;
- case 5: $CurrVal = ('0'+$CurrVal) % $Loc->OpePrm[$i]; break;
- case 6: $CurrVal = ('0'+$CurrVal) + $Loc->OpePrm[$i]; break;
- case 7: $CurrVal = ('0'+$CurrVal) * $Loc->OpePrm[$i]; break;
- case 8: $CurrVal = ('0'+$CurrVal) / $Loc->OpePrm[$i]; break;
- case 9; case 10:
- if ($ope===9) {
- $CurrVal = (in_array($this->meth_Misc_ToStr($CurrVal),$Loc->OpeMOK)) ? ' ' : '';
- } else {
- $CurrVal = (in_array($this->meth_Misc_ToStr($CurrVal),$Loc->OpeMKO)) ? '' : ' ';
- } // no break here
- case 11:
- if ($this->meth_Misc_ToStr($CurrVal)==='') {
- if ($Loc->MagnetId===0) $Loc->MagnetId = $Loc->MSave;
- } else {
- if ($Loc->MagnetId!==0) {
- $Loc->MSave = $Loc->MagnetId;
- $Loc->MagnetId = 0;
- }
- $CurrVal = '';
- }
- break;
- case 12: if ($this->meth_Misc_ToStr($CurrVal)===$Loc->OpePrm[$i]) $CurrVal = ''; break;
- case 13: $CurrVal = str_replace('*',$CurrVal,$Loc->OpePrm[$i]); break;
- case 14: $CurrVal = self::f_Loc_AttBoolean($CurrVal, $Loc->PrmLst['atttrue'], $Loc->AttName); break;
- case 15: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_UPPER, 'UTF-8') : strtoupper($CurrVal); break;
- case 16: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_LOWER, 'UTF-8') : strtolower($CurrVal); break;
- case 17: $CurrVal = ucfirst($CurrVal); break;
- case 18: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_TITLE, 'UTF-8') : ucwords(strtolower($CurrVal)); break;
- }
- }
- }
-
- // String conversion or format
- if ($Loc->ConvMode===1) { // Usual string conversion
- $CurrVal = $this->meth_Misc_ToStr($CurrVal);
- if ($Loc->ConvStr) $this->meth_Conv_Str($CurrVal,$Loc->ConvBr);
- } elseif ($Loc->ConvMode===0) { // Format
- $CurrVal = $this->meth_Misc_Format($CurrVal,$Loc->PrmLst);
- } elseif ($Loc->ConvMode===2) { // Special string conversion
- $CurrVal = $this->meth_Misc_ToStr($CurrVal);
- if ($Loc->ConvStr) $this->meth_Conv_Str($CurrVal,$Loc->ConvBr);
- if ($Loc->ConvEsc) $CurrVal = str_replace('\'','\'\'',$CurrVal);
- if ($Loc->ConvWS) {
- $check = ' ';
- $nbsp = ' ';
- do {
- $pos = strpos($CurrVal,$check);
- if ($pos!==false) $CurrVal = substr_replace($CurrVal,$nbsp,$pos,1);
- } while ($pos!==false);
- }
- if ($Loc->ConvJS) {
- $CurrVal = addslashes($CurrVal); // apply to ('), ("), (\) and (null)
- $CurrVal = str_replace(array("\n","\r","\t"),array('\n','\r','\t'),$CurrVal);
- }
- if ($Loc->ConvUrl) $CurrVal = urlencode($CurrVal);
- if ($Loc->ConvUtf8) $CurrVal = utf8_encode($CurrVal);
- }
-
- // if/then/else process, there may be several if/then
- if ($Loc->PrmIfNbr) {
- $z = false;
- $i = 1;
- while ($i!==false) {
- if ($Loc->PrmIfVar[$i]) $Loc->PrmIfVar[$i] = $this->meth_Merge_AutoVar($Loc->PrmIf[$i],true);
- $x = str_replace($this->_ChrVal,$CurrVal,$Loc->PrmIf[$i]);
- if ($this->f_Misc_CheckCondition($x)) {
- if (isset($Loc->PrmThen[$i])) {
- if ($Loc->PrmThenVar[$i]) $Loc->PrmThenVar[$i] = $this->meth_Merge_AutoVar($Loc->PrmThen[$i],true);
- $z = $Loc->PrmThen[$i];
- }
- $i = false;
- } else {
- $i++;
- if ($i>$Loc->PrmIfNbr) {
- if (isset($Loc->PrmLst['else'])) {
- if ($Loc->PrmElseVar) $Loc->PrmElseVar = $this->meth_Merge_AutoVar($Loc->PrmLst['else'],true);
- $z =$Loc->PrmLst['else'];
- }
- $i = false;
- }
- }
- }
- if ($z!==false) {
- if ($ConvProtect) {
- $CurrVal = str_replace($this->_ChrOpen,$this->_ChrProtect,$CurrVal); // TBS protection
- $ConvProtect = false;
- }
- $CurrVal = str_replace($this->_ChrVal,$CurrVal,$z);
- }
- }
-
- if (isset($Loc->PrmLst['file'])) {
- $x = $Loc->PrmLst['file'];
- if ($x===true) $x = $CurrVal;
- $this->meth_Merge_AutoVar($x,false);
- $x = trim(str_replace($this->_ChrVal,$CurrVal,$x));
- $CurrVal = '';
- if ($x!=='') {
- if ($this->f_Misc_GetFile($CurrVal, $x, $this->_LastFile, $this->IncludePath)) {
- $this->meth_Locator_PartAndRename($CurrVal, $Loc->PrmLst);
- } else {
- if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'the file \''.$x.'\' given by parameter file is not found or not readable.',true);
- }
- $ConvProtect = false;
- }
- }
-
- if (isset($Loc->PrmLst['script'])) {// Include external PHP script
- $x = $Loc->PrmLst['script'];
- if ($x===true) $x = $CurrVal;
- $this->meth_Merge_AutoVar($x,false);
- $x = trim(str_replace($this->_ChrVal,$CurrVal,$x));
- if ($x!=='') {
- $this->_Subscript = $x;
- $this->CurrPrm = &$Loc->PrmLst;
- $sub = isset($Loc->PrmLst['subtpl']);
- if ($sub) $this->meth_Misc_ChangeMode(true,$Loc,$CurrVal);
- if ($this->meth_Misc_RunSubscript($CurrVal,$Loc->PrmLst)===false) {
- if (!isset($Loc->PrmLst['noerr'])) $this->meth_Misc_Alert($Loc,'the file \''.$x.'\' given by parameter script is not found or not readable.',true);
- }
- if ($sub) $this->meth_Misc_ChangeMode(false,$Loc,$CurrVal);
- $this->meth_Locator_PartAndRename($CurrVal, $Loc->PrmLst);
- unset($this->CurrPrm);
- $ConvProtect = false;
- }
- }
-
- if (isset($Loc->PrmLst['att'])) {
- $this->f_Xml_AttFind($Txt,$Loc,true,$this->AttDelim);
- if (isset($Loc->PrmLst['atttrue'])) {
- $CurrVal = self::f_Loc_AttBoolean($CurrVal, $Loc->PrmLst['atttrue'], $Loc->AttName);
- $Loc->PrmLst['magnet'] = '#';
- }
- }
-
- // Case when it's an empty string
- if ($CurrVal==='') {
-
- if ($Loc->MagnetId===false) {
- if (isset($Loc->PrmLst['.'])) {
- $Loc->MagnetId = -1;
- } elseif (isset($Loc->PrmLst['ifempty'])) {
- $Loc->MagnetId = -2;
- } elseif (isset($Loc->PrmLst['magnet'])) {
- $Loc->MagnetId = 1;
- $Loc->PosBeg0 = $Loc->PosBeg;
- $Loc->PosEnd0 = $Loc->PosEnd;
- if ($Loc->PrmLst['magnet']==='#') {
- if (!isset($Loc->AttBeg)) {
- $Loc->PrmLst['att'] = '.';
- $this->f_Xml_AttFind($Txt,$Loc,true,$this->AttDelim);
- }
- if (isset($Loc->AttBeg)) {
- $Loc->MagnetId = -3;
- } else {
- $this->meth_Misc_Alert($Loc,'parameter \'magnet=#\' cannot be processed because the corresponding attribute is not found.',true);
- }
- } elseif (isset($Loc->PrmLst['mtype'])) {
- switch ($Loc->PrmLst['mtype']) {
- case 'm+m': $Loc->MagnetId = 2; break;
- case 'm*': $Loc->MagnetId = 3; break;
- case '*m': $Loc->MagnetId = 4; break;
- }
- }
- } elseif (isset($Loc->PrmLst['attadd'])) {
- // In order to delete extra space
- $Loc->PosBeg0 = $Loc->PosBeg;
- $Loc->PosEnd0 = $Loc->PosEnd;
- $Loc->MagnetId = 5;
- } else {
- $Loc->MagnetId = 0;
- }
- }
-
- switch ($Loc->MagnetId) {
- case 0: break;
- case -1: $CurrVal = ' '; break; // Enables to avoid null cells in HTML tables
- case -2: $CurrVal = $Loc->PrmLst['ifempty']; break;
- case -3: $Loc->Enlarged = true; $Loc->PosBeg = $Loc->AttBegM; $Loc->PosEnd = $Loc->AttEnd; break;
- case 1:
- $Loc->Enlarged = true;
- $this->f_Loc_EnlargeToTag($Txt,$Loc,$Loc->PrmLst['magnet'],false);
- break;
- case 2:
- $Loc->Enlarged = true;
- $CurrVal = $this->f_Loc_EnlargeToTag($Txt,$Loc,$Loc->PrmLst['magnet'],true);
- break;
- case 3:
- $Loc->Enlarged = true;
- $Loc2 = $this->f_Xml_FindTag($Txt,$Loc->PrmLst['magnet'],true,$Loc->PosBeg,false,false,false);
- if ($Loc2!==false) {
- $Loc->PosBeg = $Loc2->PosBeg;
- if ($Loc->PosEnd<$Loc2->PosEnd) $Loc->PosEnd = $Loc2->PosEnd;
- }
- break;
- case 4:
- $Loc->Enlarged = true;
- $Loc2 = $this->f_Xml_FindTag($Txt,$Loc->PrmLst['magnet'],true,$Loc->PosBeg,true,false,false);
- if ($Loc2!==false) $Loc->PosEnd = $Loc2->PosEnd;
- break;
- case 5:
- $Loc->Enlarged = true;
- if (substr($Txt,$Loc->PosBeg-1,1)==' ') $Loc->PosBeg--;
- break;
- }
- $NewEnd = $Loc->PosBeg; // Useful when mtype='m+m'
- } else {
-
- if ($ConvProtect) $CurrVal = str_replace($this->_ChrOpen,$this->_ChrProtect,$CurrVal); // TBS protection
- $NewEnd = $Loc->PosBeg + strlen($CurrVal);
-
- }
-
- $Txt = substr_replace($Txt,$CurrVal,$Loc->PosBeg,$Loc->PosEnd-$Loc->PosBeg+1);
- return $NewEnd; // Return the new end position of the field
-
-}
-
-function meth_Locator_FindBlockNext(&$Txt,$BlockName,$PosBeg,$ChrSub,$Mode,&$P1,&$FieldBefore) {
-// Return the first block locator just after the PosBeg position
-// Mode = 1 : Merge_Auto => doesn't save $Loc->BlockSrc, save the bounds of TBS Def tags instead, return also fields
-// Mode = 2 : FindBlockLst or GetBlockSource => save $Loc->BlockSrc without TBS Def tags
-// Mode = 3 : GetBlockSource => save $Loc->BlockSrc with TBS Def tags
-
- $SearchDef = true;
- $FirstField = false;
- // Search for the first tag with parameter "block"
- while ($SearchDef && ($Loc = $this->meth_Locator_FindTbs($Txt,$BlockName,$PosBeg,$ChrSub))) {
- if (isset($Loc->PrmLst['block'])) {
- if (isset($Loc->PrmLst['p1'])) {
- if ($P1) return false;
- $P1 = true;
- }
- $Block = $Loc->PrmLst['block'];
- $SearchDef = false;
- } elseif ($Mode===1) {
- return $Loc;
- } elseif ($FirstField===false) {
- $FirstField = $Loc;
- }
- $PosBeg = $Loc->PosEnd;
- }
-
- if ($SearchDef) {
- if ($FirstField!==false) $FieldBefore = true;
- return false;
- }
-
- $Loc->PosDefBeg = -1;
-
- if ($Block==='begin') { // Block definied using begin/end
-
- if (($FirstField!==false) && ($FirstField->PosEnd<$Loc->PosBeg)) $FieldBefore = true;
-
- $Opened = 1;
- while ($Loc2 = $this->meth_Locator_FindTbs($Txt,$BlockName,$PosBeg,$ChrSub)) {
- if (isset($Loc2->PrmLst['block'])) {
- switch ($Loc2->PrmLst['block']) {
- case 'end': $Opened--; break;
- case 'begin': $Opened++; break;
- }
- if ($Opened==0) {
- if ($Mode===1) {
- $Loc->PosBeg2 = $Loc2->PosBeg;
- $Loc->PosEnd2 = $Loc2->PosEnd;
- } else {
- if ($Mode===2) {
- $Loc->BlockSrc = substr($Txt,$Loc->PosEnd+1,$Loc2->PosBeg-$Loc->PosEnd-1);
- } else {
- $Loc->BlockSrc = substr($Txt,$Loc->PosBeg,$Loc2->PosEnd-$Loc->PosBeg+1);
- }
- $Loc->PosEnd = $Loc2->PosEnd;
- }
- $Loc->BlockFound = true;
- return $Loc;
- }
- }
- $PosBeg = $Loc2->PosEnd;
- }
-
- return $this->meth_Misc_Alert($Loc,'a least one tag with parameter \'block=end\' is missing.',false,'in block\'s definition');
-
- }
-
- if ($Mode===1) {
- $Loc->PosBeg2 = false;
- } else {
- $beg = $Loc->PosBeg;
- $end = $Loc->PosEnd;
- 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');
- if ($Loc->SubOk || ($Mode===3)) {
- $Loc->BlockSrc = substr($Txt,$Loc->PosBeg,$Loc->PosEnd-$Loc->PosBeg+1);
- $Loc->PosDefBeg = $beg - $Loc->PosBeg;
- $Loc->PosDefEnd = $end - $Loc->PosBeg;
- } else {
- $Loc->BlockSrc = substr($Txt,$Loc->PosBeg,$beg-$Loc->PosBeg).substr($Txt,$end+1,$Loc->PosEnd-$end);
- }
- }
-
- $Loc->BlockFound = true;
- if (($FirstField!==false) && ($FirstField->PosEnd<$Loc->PosBeg)) $FieldBefore = true;
- return $Loc; // methods return by ref by default
-
-}
-
-function meth_Locator_PartAndRename(&$CurrVal, &$PrmLst) {
-
- // Store part
- if (isset($PrmLst['store'])) {
- $storename = (isset($PrmLst['storename'])) ? $PrmLst['storename'] : 'default';
- if (!isset($this->TplStore[$storename])) $this->TplStore[$storename] = '';
- $this->TplStore[$storename] .= $this->f_Xml_GetPart($CurrVal, $PrmLst['store'], false);
- }
-
- // Get part
- if (isset($PrmLst['getpart'])) {
- $part = $PrmLst['getpart'];
- } elseif (isset($PrmLst['getbody'])) {
- $part = $PrmLst['getbody'];
- } else {
- $part = false;
- }
- if ($part!=false) {
- $CurrVal = $this->f_Xml_GetPart($CurrVal, $part, true);
- }
-
- // Rename or delete TBS tags names
- if (isset($PrmLst['rename'])) {
-
- $Replace = $PrmLst['rename'];
-
- if (is_string($Replace)) $Replace = explode(',',$Replace);
- foreach ($Replace as $x) {
- if (is_string($x)) $x = explode('=', $x);
- if (count($x)==2) {
- $old = trim($x[0]);
- $new = trim($x[1]);
- if ($old!=='') {
- if ($new==='') {
- $q = false;
- $s = 'clear';
- $this->meth_Merge_Block($CurrVal, $old, $s, $q, false, false, false);
- } else {
- $old = $this->_ChrOpen.$old;
- $old = array($old.'.', $old.' ', $old.';', $old.$this->_ChrClose);
- $new = $this->_ChrOpen.$new;
- $new = array($new.'.', $new.' ', $new.';', $new.$this->_ChrClose);
- $CurrVal = str_replace($old,$new,$CurrVal);
- }
- }
- }
- }
-
- }
-
-}
-
-function meth_Locator_FindBlockLst(&$Txt,$BlockName,$Pos,$SpePrm) {
-// Return a locator object covering all block definitions, even if there is no block definition found.
-
- $LocR = new clsTbsLocator;
- $LocR->P1 = false;
- $LocR->FieldOutside = false;
- $LocR->FOStop = false;
- $LocR->BDefLst = array();
-
- $LocR->NoData = false;
- $LocR->Special = false;
- $LocR->HeaderFound = false;
- $LocR->FooterFound = false;
- $LocR->SerialEmpty = false;
- $LocR->GrpBreak = false; // Only for plug-ins
-
- $LocR->WhenFound = false;
- $LocR->WhenDefault = false;
-
- $LocR->SectionNbr = 0; // Normal sections
- $LocR->SectionLst = array(); // 1 to SectionNbr
-
- $BDef = false;
- $ParentLst = array();
- $Pid = 0;
-
- do {
-
- if ($BlockName==='') {
- $Loc = false;
- } else {
- $Loc = $this->meth_Locator_FindBlockNext($Txt,$BlockName,$Pos,'.',2,$LocR->P1,$LocR->FieldOutside);
- }
-
- if ($Loc===false) {
-
- if ($Pid>0) { // parentgrp mode => disconnect $Txt from the source
- $Parent = &$ParentLst[$Pid];
- $Src = $Txt;
- $Txt = &$Parent->Txt;
- if ($LocR->BlockFound) {
- // Redefine the Header block
- $Parent->Src = substr($Src,0,$LocR->PosBeg);
- // Add a Footer block
- $BDef = &$this->meth_Locator_SectionNewBDef($LocR,$BlockName,substr($Src,$LocR->PosEnd+1),$Parent->Prm,true);
- $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'F',$Parent->Fld,'parentgrp');
- }
- // Now go down to previous level
- $Pos = $Parent->Pos;
- $LocR->PosBeg = $Parent->Beg;
- $LocR->PosEnd = $Parent->End;
- $LocR->BlockFound = true;
- unset($Parent);
- unset($ParentLst[$Pid]);
- $Pid--;
- $Loc = true;
- }
-
- } else {
-
- $Pos = $Loc->PosEnd;
-
- // Define the block limits
- if ($LocR->BlockFound) {
- if ( $LocR->PosBeg > $Loc->PosBeg ) $LocR->PosBeg = $Loc->PosBeg;
- if ( $LocR->PosEnd < $Loc->PosEnd ) $LocR->PosEnd = $Loc->PosEnd;
- } else {
- $LocR->BlockFound = true;
- $LocR->PosBeg = $Loc->PosBeg;
- $LocR->PosEnd = $Loc->PosEnd;
- }
-
- // Merge block parameters
- if (count($Loc->PrmLst)>0) $LocR->PrmLst = array_merge($LocR->PrmLst,$Loc->PrmLst);
-
- // Force dynamic parameter to be cachable
- if ($Loc->PosDefBeg>=0) {
- $dynprm = array('when','headergrp','footergrp','parentgrp');
- foreach($dynprm as $dp) {
- $n = 0;
- if ((isset($Loc->PrmLst[$dp])) && (strpos($Loc->PrmLst[$dp],$this->_ChrOpen.$BlockName)!==false)) {
- $n++;
- if ($n==1) {
- $len = $Loc->PosDefEnd - $Loc->PosDefBeg + 1;
- $x = substr($Loc->BlockSrc,$Loc->PosDefBeg,$len);
- }
- $x = str_replace($Loc->PrmLst[$dp],'',$x);
- }
- if ($n>0) $Loc->BlockSrc = substr_replace($Loc->BlockSrc,$x,$Loc->PosDefBeg,$len);
- }
- }
- // Save the block and cache its tags
- $IsParentGrp = isset($Loc->PrmLst['parentgrp']);
- $BDef = &$this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Loc->BlockSrc,$Loc->PrmLst,!$IsParentGrp);
-
- // Add the text in the list of blocks
- if (isset($Loc->PrmLst['nodata'])) { // Nodata section
- $LocR->NoData = &$BDef;
- } elseif (($SpePrm!==false) && isset($Loc->PrmLst[$SpePrm])) { // Special section (used for navigation bar)
- $LocR->Special = &$BDef;
- } elseif (isset($Loc->PrmLst['when'])) {
- if ($LocR->WhenFound===false) {
- $LocR->WhenFound = true;
- $LocR->WhenSeveral = false;
- $LocR->WhenNbr = 0;
- $LocR->WhenLst = array();
- }
- $this->meth_Merge_AutoVar($Loc->PrmLst['when'],false);
- $BDef->WhenCond = &$this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Loc->PrmLst['when'],array(),true);
- $BDef->WhenBeforeNS = ($LocR->SectionNbr===0);
- $i = ++$LocR->WhenNbr;
- $LocR->WhenLst[$i] = &$BDef;
- if (isset($Loc->PrmLst['several'])) $LocR->WhenSeveral = true;
- } elseif (isset($Loc->PrmLst['default'])) {
- $LocR->WhenDefault = &$BDef;
- $LocR->WhenDefaultBeforeNS = ($LocR->SectionNbr===0);
- } elseif (isset($Loc->PrmLst['headergrp'])) {
- $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'H',$Loc->PrmLst['headergrp'],'headergrp');
- } elseif (isset($Loc->PrmLst['footergrp'])) {
- $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'F',$Loc->PrmLst['footergrp'],'footergrp');
- } elseif (isset($Loc->PrmLst['splittergrp'])) {
- $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'S',$Loc->PrmLst['splittergrp'],'splittergrp');
- } elseif ($IsParentGrp) {
- $this->meth_Locator_SectionAddGrp($LocR,$BlockName,$BDef,'H',$Loc->PrmLst['parentgrp'],'parentgrp');
- $BDef->Fld = $Loc->PrmLst['parentgrp'];
- $BDef->Txt = &$Txt;
- $BDef->Pos = $Pos;
- $BDef->Beg = $LocR->PosBeg;
- $BDef->End = $LocR->PosEnd;
- $Pid++;
- $ParentLst[$Pid] = &$BDef;
- $Txt = &$BDef->Src;
- $Pos = $Loc->PosDefBeg + 1;
- $LocR->BlockFound = false;
- $LocR->PosBeg = false;
- $LocR->PosEnd = false;
- } elseif (isset($Loc->PrmLst['serial'])) {
- // Section with serial subsections
- $SrSrc = &$BDef->Src;
- // Search the empty item
- if ($LocR->SerialEmpty===false) {
- $SrName = $BlockName.'_0';
- $x = false;
- $SrLoc = $this->meth_Locator_FindBlockNext($SrSrc,$SrName,0,'.',2,$x,$x);
- if ($SrLoc!==false) {
- $LocR->SerialEmpty = $SrLoc->BlockSrc;
- $SrSrc = substr_replace($SrSrc,'',$SrLoc->PosBeg,$SrLoc->PosEnd-$SrLoc->PosBeg+1);
- }
- }
- $SrName = $BlockName.'_1';
- $x = false;
- $SrLoc = $this->meth_Locator_FindBlockNext($SrSrc,$SrName,0,'.',2,$x,$x);
- if ($SrLoc!==false) {
- $SrId = 1;
- do {
- // Save previous subsection
- $SrBDef = &$this->meth_Locator_SectionNewBDef($LocR,$SrName,$SrLoc->BlockSrc,$SrLoc->PrmLst,true);
- $SrBDef->SrBeg = $SrLoc->PosBeg;
- $SrBDef->SrLen = $SrLoc->PosEnd - $SrLoc->PosBeg + 1;
- $SrBDef->SrTxt = false;
- $BDef->SrBDefLst[$SrId] = &$SrBDef;
- // Put in order
- $BDef->SrBDefOrdered[$SrId] = &$SrBDef;
- $i = $SrId;
- while (($i>1) && ($SrBDef->SrBeg<$BDef->SrBDefOrdered[$SrId-1]->SrBeg)) {
- $BDef->SrBDefOrdered[$i] = &$BDef->SrBDefOrdered[$i-1];
- $BDef->SrBDefOrdered[$i-1] = &$SrBDef;
- $i--;
- }
- // Search next subsection
- $SrId++;
- $SrName = $BlockName.'_'.$SrId;
- $x = false;
- $SrLoc = $this->meth_Locator_FindBlockNext($SrSrc,$SrName,0,'.',2,$x,$x);
- } while ($SrLoc!==false);
- $BDef->SrBDefNbr = $SrId-1;
- $BDef->IsSerial = true;
- $i = ++$LocR->SectionNbr;
- $LocR->SectionLst[$i] = &$BDef;
- }
- } elseif (isset($Loc->PrmLst['parallel'])) {
- $BlockLst = $this->meth_Locator_FindParallel($Txt, $Loc->PosBeg, $Loc->PosEnd, $Loc->PrmLst['parallel']);
- if ($BlockLst) {
- // Store BDefs
- foreach ($BlockLst as $i => $Blk) {
- if ($Blk['IsRef']) {
- $PrBDef = &$BDef;
- } else {
- $PrBDef = &$this->meth_Locator_SectionNewBDef($LocR,$BlockName,$Blk['Src'],array(),true);
- }
- $PrBDef->PosBeg = $Blk['PosBeg'];
- $PrBDef->PosEnd = $Blk['PosEnd'];
- $i = ++$LocR->SectionNbr;
- $LocR->SectionLst[$i] = &$PrBDef;
- }
- $LocR->PosBeg = $BlockLst[0]['PosBeg'];
- $LocR->PosEnd = $BlockLst[$LocR->SectionNbr-1]['PosEnd'];
- }
- } else {
- // Normal section
- $i = ++$LocR->SectionNbr;
- $LocR->SectionLst[$i] = &$BDef;
- }
-
- }
-
- } while ($Loc!==false);
-
- if ($LocR->WhenFound && ($LocR->SectionNbr===0)) {
- // Add a blank section if When is used without a normal section
- $BDef = &$this->meth_Locator_SectionNewBDef($LocR,$BlockName,'',array(),false);
- $LocR->SectionNbr = 1;
- $LocR->SectionLst[1] = &$BDef;
- }
-
- return $LocR; // methods return by ref by default
-
-}
-
-function meth_Locator_FindParallel(&$Txt, $ZoneBeg, $ZoneEnd, $ConfId) {
-
- // Define configurations
- global $_TBS_ParallelLst;
-
- if ( ($ConfId=='tbs:table') && (!isset($_TBS_ParallelLst['tbs:table'])) ) {
- $_TBS_ParallelLst['tbs:table'] = array(
- 'parent' => 'table',
- 'ignore' => array('!--', 'caption', 'thead', 'tbody', 'tfoot'),
- 'cols' => array(),
- 'rows' => array('tr', 'colgroup'),
- 'cells' => array('td'=>'colspan', 'th'=>'colspan', 'col'=>'span'),
- );
- }
-
- if (!isset($_TBS_ParallelLst[$ConfId])) return $this->meth_Misc_Alert("Parallel", "The configuration '$ConfId' is not found.");
-
- $conf = $_TBS_ParallelLst[$ConfId];
-
- $Parent = $conf['parent'];
-
- // Search parent bounds
- $par_o = self::f_Xml_FindTag($Txt,$Parent,true ,$ZoneBeg,false,1,false);
- if ($par_o===false) return $this->meth_Misc_Alert("Parallel", "The opening tag '$Parent' is not found.");
-
- $par_c = self::f_Xml_FindTag($Txt,$Parent,false,$ZoneBeg,true,-1,false);
- if ($par_c===false) return $this->meth_Misc_Alert("Parallel", "The closing tag '$Parent' is not found.");
-
- $SrcPOffset = $par_o->PosEnd + 1;
- $SrcP = substr($Txt, $SrcPOffset, $par_c->PosBeg - $SrcPOffset);
-
- // temporary variables
- $tagR = '';
- $tagC = '';
- $z = '';
- $pRO = false;
- $pROe = false;
- $pCO = false;
- $pCOe = false;
- $p = false;
- $Loc = new clsTbsLocator;
-
- $Rows = array();
- $RowIdx = 0;
- $RefRow = false;
- $RefCellB= false;
- $RefCellE = false;
-
- $RowType = array();
-
- // Loop on entities inside the parent entity
- $PosR = 0;
-
- $mode_column = true;
- $Cells = array();
- $ColNum = 1;
- $IsRef = false;
-
- // Search for the next Row Opening tag
- while (self::f_Xml_GetNextEntityName($SrcP, $PosR, $tagR, $pRO, $p)) {
-
- $pROe = strpos($SrcP, '>', $p) + 1;
- $singleR = ($SrcP[$pROe-2] === '/');
-
- // If the tag is not a closing, a self-closing and has a name
- if ($tagR!=='') {
-
- if (in_array($tagR, $conf['ignore'])) {
- // This tag must be ignored
- $PosR = $p;
- } elseif (isset($conf['cols'][$tagR])) {
- // Column definition that must be merged as a cell
- if ($mode_column === false) return $this->meth_Misc_Alert("Parallel", "There is a column definition ($tagR) after a row (".$Rows[$RowIdx-1]['tag'].").");
- if (isset($RowType['_column'])) {
- $RowType['_column']++;
- } else {
- $RowType['_column'] = 1;
- }
- $att = $conf['cols'][$tagR];
- $this->meth_Locator_FindParallelCol($SrcP, $PosR, $tagR, $pRO, $p, $SrcPOffset, $RowIdx, $ZoneBeg, $ZoneEnd, $att, $Loc, $Cells, $ColNum, $IsRef, $RefCellB, $RefCellE, $RefRow);
-
- } elseif (!$singleR) {
-
- // Search the Row Closing tag
- $locRE = self::f_Xml_FindTag($SrcP, $tagR, false, $pROe, true, -1, false);
- if ($locRE===false) return $this->meth_Misc_Alert("Parallel", "The row closing tag is not found. (tagR=$tagR, p=$p, pROe=$pROe)");
-
- // Inner source
- $SrcR = substr($SrcP, $pROe, $locRE->PosBeg - $pROe);
- $SrcROffset = $SrcPOffset + $pROe;
-
- if (in_array($tagR, $conf['rows'])) {
-
- if ( $mode_column && isset($RowType['_column']) ) {
- $Rows[$RowIdx] = array('tag'=>'_column', 'cells' => $Cells, 'isref' => $IsRef, 'count' => $RowType['_column']);
- $RowIdx++;
- }
-
- $mode_column = false;
-
- if (isset($RowType[$tagR])) {
- $RowType[$tagR]++;
- } else {
- $RowType[$tagR] = 1;
- }
-
- // Now we've got the row entity, we search for cell entities
- $Cells = array();
- $ColNum = 1;
- $PosC = 0;
- $IsRef = false;
-
- // Loop on Cell Opening tags
- while (self::f_Xml_GetNextEntityName($SrcR, $PosC, $tagC, $pCO, $p)) {
- if (isset($conf['cells'][$tagC]) ) {
- $att = $conf['cells'][$tagC];
- $this->meth_Locator_FindParallelCol($SrcR, $PosC, $tagC, $pCO, $p, $SrcROffset, $RowIdx, $ZoneBeg, $ZoneEnd, $att, $Loc, $Cells, $ColNum, $IsRef, $RefCellB, $RefCellE, $RefRow);
- } else {
- $PosC = $p;
- }
- }
-
- $Rows[$RowIdx] = array('tag'=>$tagR, 'cells' => $Cells, 'isref' => $IsRef, 'count' => $RowType[$tagR]);
- $RowIdx++;
-
- }
-
- $PosR = $locRE->PosEnd;
-
- } else {
- $PosR = $pROe;
- }
- } else {
- $PosR = $pROe;
- }
- }
-
- //return $Rows;
-
- $Blocks = array();
- $rMax = count($Rows) -1;
- foreach ($Rows as $r=>$Row) {
- $Cells = $Row['cells'];
- if (isset($Cells[$RefCellB]) && $Cells[$RefCellB]['IsBegin']) {
- if ( isset($Cells[$RefCellE]) && $Cells[$RefCellE]['IsEnd'] ) {
- $PosBeg = $Cells[$RefCellB]['PosBeg'];
- $PosEnd = $Cells[$RefCellE]['PosEnd'];
- $Blocks[$r] = array(
- 'PosBeg' => $PosBeg,
- 'PosEnd' => $PosEnd,
- 'IsRef' => $Row['isref'],
- 'Src' => substr($Txt, $PosBeg, $PosEnd - $PosBeg + 1),
- );
- } else {
- 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)");
- }
- } else {
- 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)");
- }
- }
-
- return $Blocks;
-
-}
-
-function meth_Locator_FindParallelCol($SrcR, &$PosC, $tagC, $pCO, $p, $SrcROffset, $RowIdx, $ZoneBeg, $ZoneEnd, &$att, &$Loc, &$Cells, &$ColNum, &$IsRef, &$RefCellB, &$RefCellE, &$RefRow) {
-
- $pCOe = false;
-
- // Read parameters
- $Loc->PrmLst = array();
- self::f_Loc_PrmRead($SrcR,$p,true,'\'"','<','>',$Loc,$pCOe,true);
-
- $singleC = ($SrcR[$pCOe-1] === '/');
- if ($singleC) {
- $pCEe = $pCOe;
- } else {
- // Find the Cell Closing tag
- $locCE = self::f_Xml_FindTag($SrcR, $tagC, false, $pCOe, true, -1, false);
- if ($locCE===false) return $this->meth_Misc_Alert("Parallel", "The cell closing tag is not found. (pCOe=$pCOe)");
- $pCEe = $locCE->PosEnd;
- }
-
- // Check the cell of reference
- $Width = (isset($Loc->PrmLst[$att])) ? intval($Loc->PrmLst[$att]) : 1;
- $ColNumE = $ColNum + $Width -1; // Ending Cell
- $PosBeg = $SrcROffset + $pCO;
- $PosEnd = $SrcROffset + $pCEe;
- $OnZone = false;
- if ( ($PosBeg <= $ZoneBeg) && ($ZoneBeg <= $PosEnd) && ($RefRow===false) ) {
- $RefRow = $RowIdx;
- $RefCellB = $ColNum;
- $OnZone = true;
- $IsRef = true;
- }
- if ( ($PosBeg <= $ZoneEnd) && ($ZoneEnd <= $PosEnd) ) {
- $RefCellE = $ColNum;
- $OnZone = true;
- }
-
- // Save info
- $Cell = array(
- //'_tagR' => $tagR, '_tagC' => $tagC, '_att' => $att, '_OnZone' => $OnZone, '_PrmLst' => $Loc->PrmLst, '_Offset' => $SrcROffset, '_Src' => substr($SrcR, $pCO, $locCE->PosEnd - $pCO + 1),
- 'PosBeg' => $PosBeg,
- 'PosEnd' => $PosEnd,
- 'ColNum' => $ColNum,
- 'Width' => $Width,
- 'IsBegin' => true,
- 'IsEnd' => false,
- );
- $Cells[$ColNum] = $Cell;
-
- // add a virtual column to say if its a ending
- if (!isset($Cells[$ColNumE])) $Cells[$ColNumE] = array('IsBegin' => false);
-
- $Cells[$ColNumE]['IsEnd'] = true;
- $Cells[$ColNumE]['PosEnd'] = $Cells[$ColNum]['PosEnd'];
-
- $PosC = $pCEe;
- $ColNum += $Width;
-
-}
-
-function meth_Merge_Block(&$Txt,$BlockLst,&$SrcId,&$Query,$SpePrm,$SpeRecNum,$QryPrms=false) {
-
- $BlockSave = $this->_CurrBlock;
- $this->_CurrBlock = $BlockLst;
-
- // Get source type and info
- $Src = new clsTbsDataSource;
- if (!$Src->DataPrepare($SrcId,$this)) {
- $this->_CurrBlock = $BlockSave;
- return 0;
- }
-
- if (is_string($BlockLst)) $BlockLst = explode(',', $BlockLst);
- $BlockNbr = count($BlockLst);
- $BlockId = 0;
- $WasP1 = false;
- $NbrRecTot = 0;
- $QueryZ = &$Query;
- $ReturnData = false;
-
- while ($BlockId<$BlockNbr) {
-
- $RecSpe = 0; // Row with a special block's definition (used for the navigation bar)
- $QueryOk = true;
- $this->_CurrBlock = trim($BlockLst[$BlockId]);
- if ($this->_CurrBlock==='*') {
- $ReturnData = true;
- if ($Src->RecSaved===false) $Src->RecSaving = true;
- $this->_CurrBlock = '';
- }
-
- // Search the block
- $LocR = $this->meth_Locator_FindBlockLst($Txt,$this->_CurrBlock,0,$SpePrm);
-
- if ($LocR->BlockFound) {
-
- if ($LocR->Special!==false) $RecSpe = $SpeRecNum;
- // OnData
- if ($Src->OnDataPrm = isset($LocR->PrmLst['ondata'])) {
- $Src->OnDataPrmRef = $LocR->PrmLst['ondata'];
- if (isset($Src->OnDataPrmDone[$Src->OnDataPrmRef])) {
- $Src->OnDataPrm = false;
- } else {
- $ErrMsg = false;
- if ($this->meth_Misc_UserFctCheck($Src->OnDataPrmRef,'f',$ErrMsg,$ErrMsg,true)) {
- $Src->OnDataOk = true;
- } else {
- $LocR->FullName = $this->_CurrBlock;
- $Src->OnDataPrm = $this->meth_Misc_Alert($LocR,'(parameter ondata) '.$ErrMsg,false,'block');
- }
- }
- }
- // Dynamic query
- if ($LocR->P1) {
- 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
- if ($Src->RecSaved===false) $Src->RecSaving = true;
- } elseif (is_string($Query)) {
- $Src->RecSaved = false;
- unset($QueryZ); $QueryZ = ''.$Query;
- $i = 1;
- do {
- $x = 'p'.$i;
- if (isset($LocR->PrmLst[$x])) {
- $QueryZ = str_replace('%p'.$i.'%',$LocR->PrmLst[$x],$QueryZ);
- $i++;
- } else {
- $i = false;
- }
- } while ($i!==false);
- }
- $WasP1 = true;
- } elseif (($Src->RecSaved===false) && ($BlockNbr-$BlockId>1)) {
- $Src->RecSaving = true;
- }
- } elseif ($WasP1) {
- $QueryOk = false;
- $WasP1 = false;
- }
-
- // Open the recordset
- if ($QueryOk) {
- if ((!$LocR->BlockFound) && (!$LocR->FieldOutside)) {
- // Special case: return data without any block to merge
- $QueryOk = false;
- if ($ReturnData && (!$Src->RecSaved)) {
- if ($Src->DataOpen($QueryZ,$QryPrms)) {
- do {$Src->DataFetch();} while ($Src->CurrRec!==false);
- $Src->DataClose();
- }
- }
- } else {
- $QueryOk = $Src->DataOpen($QueryZ,$QryPrms);
- if (!$QueryOk) {
- if ($WasP1) { $WasP1 = false;} else {$LocR->FieldOutside = false;} // prevent from infinit loop
- }
- }
- }
-
- // Merge sections
- if ($QueryOk) {
- if ($Src->Type===2) { // Special for Text merge
- if ($LocR->BlockFound) {
- $Txt = substr_replace($Txt,$Src->RecSet,$LocR->PosBeg,$LocR->PosEnd-$LocR->PosBeg+1);
- $Src->DataFetch(); // store data, may be needed for multiple blocks
- $Src->RecNum = 1;
- $Src->CurrRec = false;
- } else {
- $Src->DataAlert('can\'t merge the block with a text value because the block definition is not found.');
- }
- } elseif ($LocR->BlockFound===false) {
- $Src->DataFetch(); // Merge first record only
- } elseif (isset($LocR->PrmLst['parallel'])) {
- $this->meth_Merge_BlockParallel($Txt,$LocR,$Src);
- } else {
- $this->meth_Merge_BlockSections($Txt,$LocR,$Src,$RecSpe);
- }
- $Src->DataClose(); // Close the resource
- }
-
- if (!$WasP1) {
- $NbrRecTot += $Src->RecNum;
- $BlockId++;
- }
- if ($LocR->FieldOutside) $this->meth_Merge_FieldOutside($Txt,$Src->CurrRec,$Src->RecNum,$LocR->FOStop);
-
- }
-
- // End of the merge
- unset($LocR);
- $this->_CurrBlock = $BlockSave;
- if ($ReturnData) {
- return $Src->RecSet;
- } else {
- unset($Src);
- return $NbrRecTot;
- }
-
-}
-
-function meth_Merge_BlockParallel(&$Txt,&$LocR,&$Src) {
-
- // Main loop
- $Src->DataFetch();
-
- $FirstRec = true;
-
- // Prepare sources
- $BlockRes = array();
- for ($i=1 ; $i<=$LocR->SectionNbr ; $i++) {
- if ($i>1) {
- // Add txt source between the BDefs
- $BlockRes[$i] = substr($Txt, $LocR->SectionLst[$i-1]->PosEnd + 1, $LocR->SectionLst[$i]->PosBeg - $LocR->SectionLst[$i-1]->PosEnd -1);
- } else {
- $BlockRes[$i] = '';
- }
- }
-
- while($Src->CurrRec!==false) {
- // Merge the current record with all sections
- for ($i=1 ; $i<=$LocR->SectionNbr ; $i++) {
- $SecDef = &$LocR->SectionLst[$i];
- $SecSrc = $this->meth_Merge_SectionNormal($SecDef,$Src);
- $BlockRes[$i] .= $SecSrc;
- }
- // Next row
- $Src->DataFetch();
- }
-
- $BlockRes = implode('', $BlockRes);
- $Txt = substr_replace($Txt,$BlockRes,$LocR->PosBeg,$LocR->PosEnd-$LocR->PosBeg+1);
-
-}
-
-function meth_Merge_BlockSections(&$Txt,&$LocR,&$Src,&$RecSpe) {
-
- // Initialise
- $SecId = 0;
- $SecOk = ($LocR->SectionNbr>0);
- $SecSrc = '';
- $BlockRes = ''; // The result of the chained merged blocks
- $IsSerial = false;
- $SrId = 0;
- $SrNbr = 0;
- $GrpFound = false;
- if ($LocR->HeaderFound || $LocR->FooterFound) {
- $GrpFound = true;
- $piOMG = false;
- if ($LocR->FooterFound) $Src->PrevRec = (object) null;
- }
- // Plug-ins
- $piOMS = false;
- if ($this->_PlugIns_Ok) {
- if (isset($this->_piBeforeMergeBlock)) {
- $ArgLst = array(&$Txt,&$LocR->PosBeg,&$LocR->PosEnd,$LocR->PrmLst,&$Src,&$LocR);
- $this->meth_Plugin_RunAll($this->_piBeforeMergeBlock,$ArgLst);
- }
- if (isset($this->_piOnMergeSection)) {
- $ArgLst = array(&$BlockRes,&$SecSrc);
- $piOMS = true;
- }
- if ($GrpFound && isset($this->_piOnMergeGroup)) {
- $ArgLst2 = array(0,0,&$Src,&$LocR);
- $piOMG = true;
- }
- }
-
- // Main loop
- $Src->DataFetch();
-
- while($Src->CurrRec!==false) {
-
- // Headers and Footers
- if ($GrpFound) {
- $brk_any = false;
- $brk_src = '';
- if ($LocR->FooterFound) {
- $brk = false;
- for ($i=$LocR->FooterNbr;$i>=1;$i--) {
- $GrpDef = &$LocR->FooterDef[$i];
- $x = $this->meth_Merge_SectionNormal($GrpDef->FDef,$Src);
- if ($Src->RecNum===1) {
- $GrpDef->PrevValue = $x;
- $brk_i = false;
- } else {
- if ($GrpDef->AddLastGrp) {
- $brk_i = &$brk;
- } else {
- unset($brk_i); $brk_i = false;
- }
- if (!$brk_i) $brk_i = !($GrpDef->PrevValue===$x);
- if ($brk_i) {
- $brk_any = true;
- $ok = true;
- if ($piOMG) {$ArgLst2[0]=&$Src->PrevRec; $ArgLst2[1]=&$GrpDef; $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup,$ArgLst2);}
- if ($ok!==false) $brk_src = $this->meth_Merge_SectionNormal($GrpDef,$Src->PrevRec).$brk_src;
- $GrpDef->PrevValue = $x;
- }
- }
- }
- $Src->PrevRec->CurrRec = $Src->CurrRec;
- $Src->PrevRec->RecNum = $Src->RecNum;
- $Src->PrevRec->RecKey = $Src->RecKey;
- }
- if ($LocR->HeaderFound) {
- $brk = ($Src->RecNum===1);
- for ($i=1;$i<=$LocR->HeaderNbr;$i++) {
- $GrpDef = &$LocR->HeaderDef[$i];
- $x = $this->meth_Merge_SectionNormal($GrpDef->FDef,$Src);
- if (!$brk) $brk = !($GrpDef->PrevValue===$x);
- if ($brk) {
- $ok = true;
- if ($piOMG) {$ArgLst2[0]=&$Src; $ArgLst2[1]=&$GrpDef; $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup,$ArgLst2);}
- if ($ok!==false) $brk_src .= $this->meth_Merge_SectionNormal($GrpDef,$Src);
- $GrpDef->PrevValue = $x;
- }
- }
- $brk_any = ($brk_any || $brk);
- }
- if ($brk_any) {
- if ($IsSerial) {
- $BlockRes .= $this->meth_Merge_SectionSerial($SecDef,$SrId,$LocR);
- $IsSerial = false;
- }
- $BlockRes .= $brk_src;
- }
- } // end of header and footer
-
- // Increment Section
- if (($IsSerial===false) && $SecOk) {
- $SecId++;
- if ($SecId>$LocR->SectionNbr) $SecId = 1;
- $SecDef = &$LocR->SectionLst[$SecId];
- $IsSerial = $SecDef->IsSerial;
- if ($IsSerial) {
- $SrId = 0;
- $SrNbr = $SecDef->SrBDefNbr;
- }
- }
-
- // Serial Mode Activation
- if ($IsSerial) { // Serial Merge
- $SrId++;
- $SrBDef = &$SecDef->SrBDefLst[$SrId];
- $SrBDef->SrTxt = $this->meth_Merge_SectionNormal($SrBDef,$Src);
- if ($SrId>=$SrNbr) {
- $SecSrc = $this->meth_Merge_SectionSerial($SecDef,$SrId,$LocR);
- $BlockRes .= $SecSrc;
- $IsSerial = false;
- }
- } else { // Classic merge
- if ($SecOk) {
- if ($Src->RecNum===$RecSpe) $SecDef = &$LocR->Special;
- $SecSrc = $this->meth_Merge_SectionNormal($SecDef,$Src);
- } else {
- $SecSrc = '';
- }
- if ($LocR->WhenFound) { // With conditional blocks
- $found = false;
- $continue = true;
- $i = 1;
- do {
- $WhenBDef = &$LocR->WhenLst[$i];
- $cond = $this->meth_Merge_SectionNormal($WhenBDef->WhenCond,$Src);
- if ($this->f_Misc_CheckCondition($cond)) {
- $x_when = $this->meth_Merge_SectionNormal($WhenBDef,$Src);
- if ($WhenBDef->WhenBeforeNS) {$SecSrc = $x_when.$SecSrc;} else {$SecSrc = $SecSrc.$x_when;}
- $found = true;
- if ($LocR->WhenSeveral===false) $continue = false;
- }
- $i++;
- if ($i>$LocR->WhenNbr) $continue = false;
- } while ($continue);
- if (($found===false) && ($LocR->WhenDefault!==false)) {
- $x_when = $this->meth_Merge_SectionNormal($LocR->WhenDefault,$Src);
- if ($LocR->WhenDefaultBeforeNS) {$SecSrc = $x_when.$SecSrc;} else {$SecSrc = $SecSrc.$x_when;}
- }
- }
- if ($piOMS) $this->meth_PlugIn_RunAll($this->_piOnMergeSection,$ArgLst);
- $BlockRes .= $SecSrc;
- }
-
- // Next row
- $Src->DataFetch();
-
- } //--> while($CurrRec!==false) {
-
- $SecSrc = '';
-
- // Serial: merge the extra the sub-blocks
- if ($IsSerial) $SecSrc .= $this->meth_Merge_SectionSerial($SecDef,$SrId,$LocR);
-
- // Footer
- if ($LocR->FooterFound) {
- if ($Src->RecNum>0) {
- for ($i=1;$i<=$LocR->FooterNbr;$i++) {
- $GrpDef = &$LocR->FooterDef[$i];
- if ($GrpDef->AddLastGrp) {
- $ok = true;
- if ($piOMG) {$ArgLst2[0]=&$Src->PrevRec; $ArgLst2[1]=&$GrpDef; $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup,$ArgLst2);}
- if ($ok!==false) $SecSrc .= $this->meth_Merge_SectionNormal($GrpDef,$Src->PrevRec);
- }
- }
- }
- }
-
- // NoData
- if ($Src->RecNum===0) {
- if ($LocR->NoData!==false) {
- $SecSrc = $LocR->NoData->Src;
- } elseif(isset($LocR->PrmLst['bmagnet'])) {
- $this->f_Loc_EnlargeToTag($Txt,$LocR,$LocR->PrmLst['bmagnet'],false);
- }
- }
-
- // Plug-ins
- if ($piOMS && ($SecSrc!=='')) $this->meth_PlugIn_RunAll($this->_piOnMergeSection,$ArgLst);
-
- $BlockRes .= $SecSrc;
-
- // Plug-ins
- if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterMergeBlock)) {
- $ArgLst = array(&$BlockRes,&$Src,&$LocR);
- $this->meth_PlugIn_RunAll($this->_piAfterMergeBlock,$ArgLst);
- }
-
- // Merge the result
- $Txt = substr_replace($Txt,$BlockRes,$LocR->PosBeg,$LocR->PosEnd-$LocR->PosBeg+1);
- if ($LocR->P1) $LocR->FOStop = $LocR->PosBeg + strlen($BlockRes) -1;
-
-}
-
-function meth_Merge_AutoVar(&$Txt,$ConvStr,$Id='var') {
-// Merge automatic fields with VarRef
-
- $Pref = &$this->VarPrefix;
- $PrefL = strlen($Pref);
- $PrefOk = ($PrefL>0);
-
- if ($ConvStr===false) {
- $Charset = $this->Charset;
- $this->Charset = false;
- }
-
- // Then we scann all fields in the model
- $x = '';
- $Pos = 0;
- while ($Loc = $this->meth_Locator_FindTbs($Txt,$Id,$Pos,'.')) {
- if ($Loc->SubNbr==0) $Loc->SubLst[0]=''; // In order to force error message
- if ($Loc->SubLst[0]==='') {
- $Pos = $this->meth_Merge_AutoSpe($Txt,$Loc);
- } elseif ($Loc->SubLst[0][0]==='~') {
- if (!isset($ObjOk)) $ObjOk = (is_object($this->ObjectRef) || is_array($this->ObjectRef));
- if ($ObjOk) {
- $Loc->SubLst[0] = substr($Loc->SubLst[0],1);
- $Pos = $this->meth_Locator_Replace($Txt,$Loc,$this->ObjectRef,0);
- } elseif (isset($Loc->PrmLst['noerr'])) {
- $Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false);
- } else {
- $this->meth_Misc_Alert($Loc,'property ObjectRef is neither an object nor an array. Its type is \''.gettype($this->ObjectRef).'\'.',true);
- $Pos = $Loc->PosEnd + 1;
- }
- } elseif ($PrefOk && (substr($Loc->SubLst[0],0,$PrefL)!==$Pref)) {
- if (isset($Loc->PrmLst['noerr'])) {
- $Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false);
- } else {
- $this->meth_Misc_Alert($Loc,'does not match the allowed prefix.',true);
- $Pos = $Loc->PosEnd + 1;
- }
- } elseif (isset($this->VarRef[$Loc->SubLst[0]])) {
- $Pos = $this->meth_Locator_Replace($Txt,$Loc,$this->VarRef[$Loc->SubLst[0]],1);
- } else {
- if (isset($Loc->PrmLst['noerr'])) {
- $Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false);
- } else {
- $Pos = $Loc->PosEnd + 1;
- $msg = (isset($this->VarRef['GLOBALS'])) ? 'VarRef seems refers to $GLOBALS' : 'VarRef seems refers to a custom array of values';
- $this->meth_Misc_Alert($Loc,'the key \''.$Loc->SubLst[0].'\' does not exist or is not set in VarRef. ('.$msg.')',true);
- }
- }
- }
-
- if ($ConvStr===false) $this->Charset = $Charset;
-
- return false; // Useful for properties PrmIfVar & PrmThenVar
-
-}
-
-function meth_Merge_AutoSpe(&$Txt,&$Loc) {
-// Merge Special Var Fields ([var..*])
-
- $ErrMsg = false;
- $SubStart = false;
- if (isset($Loc->SubLst[1])) {
- switch ($Loc->SubLst[1]) {
- case 'now': $x = time(); break;
- case 'version': $x = $this->Version; break;
- case 'script_name': $x = basename(((isset($_SERVER)) ? $_SERVER['PHP_SELF'] : $GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'] )); break;
- case 'template_name': $x = $this->_LastFile; break;
- case 'template_date': $x = ''; if ($this->f_Misc_GetFile($x,$this->_LastFile,'',array(),false)) $x = $x['mtime']; break;
- case 'template_path': $x = dirname($this->_LastFile).'/'; break;
- case 'name': $x = 'TinyButStrong'; break;
- case 'logo': $x = '**TinyButStrong**'; break;
- case 'charset': $x = $this->Charset; break;
- case 'error_msg': $this->_ErrMsgName = $Loc->FullName; return $Loc->PosEnd; break;
- case '': $ErrMsg = 'it doesn\'t have any keyword.'; break;
- case 'tplvars':
- if ($Loc->SubNbr==2) {
- $SubStart = 2;
- $x = implode(',',array_keys($this->TplVars)); // list of all template variables
- } else {
- if (isset($this->TplVars[$Loc->SubLst[2]])) {
- $SubStart = 3;
- $x = &$this->TplVars[$Loc->SubLst[2]];
- } else {
- $ErrMsg = 'property TplVars doesn\'t have any item named \''.$Loc->SubLst[2].'\'.';
- }
- }
- break;
- case 'store':
- if ($Loc->SubNbr==2) {
- $SubStart = 2;
- $x = implode('',$this->TplStore); // concatenation of all stores
- } else {
- if (isset($this->TplStore[$Loc->SubLst[2]])) {
- $SubStart = 3;
- $x = &$this->TplStore[$Loc->SubLst[2]];
- } else {
- $ErrMsg = 'Store named \''.$Loc->SubLst[2].'\' is not defined yet.';
- }
- }
- if (!isset($Loc->PrmLst['strconv'])) {$Loc->PrmLst['strconv'] = 'no'; $Loc->PrmLst['protect'] = 'no';}
- break;
- case 'cst': $x = @constant($Loc->SubLst[2]); break;
- case 'tbs_info':
- $x = 'TinyButStrong version '.$this->Version.' for PHP 5';
- $x .= "\r\nInstalled plug-ins: ".count($this->_PlugIns);
- foreach (array_keys($this->_PlugIns) as $pi) {
- $o = &$this->_PlugIns[$pi];
- $x .= "\r\n- plug-in [".(isset($o->Name) ? $o->Name : $pi ).'] version '.(isset($o->Version) ? $o->Version : '?' );
- }
- break;
- case 'php_info':
- ob_start();
- phpinfo();
- $x = ob_get_contents();
- ob_end_clean();
- $x = self::f_Xml_GetPart($x, '(style)+body', false);
- if (!isset($Loc->PrmLst['strconv'])) {$Loc->PrmLst['strconv'] = 'no'; $Loc->PrmLst['protect'] = 'no';}
- break;
- default:
- $IsSupported = false;
- if (isset($this->_piOnSpecialVar)) {
- $x = '';
- $ArgLst = array(substr($Loc->SubName,1),&$IsSupported ,&$x, &$Loc->PrmLst,&$Txt,&$Loc->PosBeg,&$Loc->PosEnd,&$Loc);
- $this->meth_PlugIn_RunAll($this->_piOnSpecialVar,$ArgLst);
- }
- if (!$IsSupported) $ErrMsg = '\''.$Loc->SubLst[1].'\' is an unsupported keyword.';
- }
- } else {
- $ErrMsg = 'it doesn\'t have any subname.';
- }
- if ($ErrMsg!==false) {
- $this->meth_Misc_Alert($Loc,$ErrMsg);
- $x = '';
- }
- if ($Loc->PosBeg===false) {
- return $Loc->PosEnd;
- } else {
- return $this->meth_Locator_Replace($Txt,$Loc,$x,$SubStart);
- }
-}
-
-function meth_Merge_FieldOutside(&$Txt, &$CurrRec, $RecNum, $PosMax) {
- $Pos = 0;
- $SubStart = ($CurrRec===false) ? false : 0;
- do {
- $Loc = $this->meth_Locator_FindTbs($Txt,$this->_CurrBlock,$Pos,'.');
- if ($Loc!==false) {
- if (($PosMax!==false) && ($Loc->PosEnd>$PosMax)) return;
- if ($Loc->SubName==='#') {
- $NewEnd = $this->meth_Locator_Replace($Txt,$Loc,$RecNum,false);
- } else {
- $NewEnd = $this->meth_Locator_Replace($Txt,$Loc,$CurrRec,$SubStart);
- }
- if ($PosMax!==false) $PosMax += $NewEnd - $Loc->PosEnd;
- $Pos = $NewEnd;
- }
- } while ($Loc!==false);
-}
-
-function meth_Merge_SectionNormal(&$BDef,&$Src) {
-
- $Txt = $BDef->Src;
- $LocLst = &$BDef->LocLst;
- $iMax = $BDef->LocNbr;
- $PosMax = strlen($Txt);
-
- if ($Src===false) { // Erase all fields
-
- $x = '';
-
- // Chached locators
- for ($i=$iMax;$i>0;$i--) {
- if ($LocLst[$i]->PosBeg<$PosMax) {
- $this->meth_Locator_Replace($Txt,$LocLst[$i],$x,false);
- if ($LocLst[$i]->Enlarged) {
- $PosMax = $LocLst[$i]->PosBeg;
- $LocLst[$i]->PosBeg = $LocLst[$i]->PosBeg0;
- $LocLst[$i]->PosEnd = $LocLst[$i]->PosEnd0;
- $LocLst[$i]->Enlarged = false;
- }
- }
- }
-
- // Uncached locators
- if ($BDef->Chk) {
- $BlockName = &$BDef->Name;
- $Pos = 0;
- while ($Loc = $this->meth_Locator_FindTbs($Txt,$BlockName,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$x,false);
- }
-
- } else {
-
- // Cached locators
- for ($i=$iMax;$i>0;$i--) {
- if ($LocLst[$i]->PosBeg<$PosMax) {
- if ($LocLst[$i]->IsRecInfo) {
- if ($LocLst[$i]->RecInfo==='#') {
- $this->meth_Locator_Replace($Txt,$LocLst[$i],$Src->RecNum,false);
- } else {
- $this->meth_Locator_Replace($Txt,$LocLst[$i],$Src->RecKey,false);
- }
- } else {
- $this->meth_Locator_Replace($Txt,$LocLst[$i],$Src->CurrRec,0);
- }
- if ($LocLst[$i]->Enlarged) {
- $PosMax = $LocLst[$i]->PosBeg;
- $LocLst[$i]->PosBeg = $LocLst[$i]->PosBeg0;
- $LocLst[$i]->PosEnd = $LocLst[$i]->PosEnd0;
- $LocLst[$i]->Enlarged = false;
- }
- }
- }
-
- // Unchached locators
- if ($BDef->Chk) {
- $BlockName = &$BDef->Name;
- foreach ($Src->CurrRec as $key => $val) {
- $Pos = 0;
- $Name = $BlockName.'.'.$key;
- while ($Loc = $this->meth_Locator_FindTbs($Txt,$Name,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$val,0);
- }
- $Pos = 0;
- $Name = $BlockName.'.#';
- while ($Loc = $this->meth_Locator_FindTbs($Txt,$Name,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$Src->RecNum,0);
- $Pos = 0;
- $Name = $BlockName.'.$';
- while ($Loc = $this->meth_Locator_FindTbs($Txt,$Name,$Pos,'.')) $Pos = $this->meth_Locator_Replace($Txt,$Loc,$Src->RecKey,0);
- }
-
- }
-
- // Automatic sub-blocks
- if (isset($BDef->AutoSub)) {
- for ($i=1;$i<=$BDef->AutoSub;$i++) {
- $name = $BDef->Name.'_sub'.$i;
- $query = '';
- $col = $BDef->Prm['sub'.$i];
- if ($col===true) $col = '';
- $col_opt = (substr($col,0,1)==='(') && (substr($col,-1,1)===')');
- if ($col_opt) $col = substr($col,1,strlen($col)-2);
- if ($col==='') {
- // $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'
- $data = &$Src->CurrRec;
- } elseif (is_object($Src->CurrRec)) {
- $data = &$Src->CurrRec->$col;
- } else {
- if (array_key_exists($col, $Src->CurrRec)) {
- $data = &$Src->CurrRec[$col];
- } else {
- 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.')');
- unset($data); $data = array();
- }
- }
- if (is_string($data)) {
- $data = explode(',',$data);
- } elseif (is_null($data) || ($data===false)) {
- $data = array();
- }
- $this->meth_Merge_Block($Txt, $name, $data, $query, false, 0, false);
- }
- }
-
- return $Txt;
-
-}
-
-function meth_Merge_SectionSerial(&$BDef,&$SrId,&$LocR) {
-
- $Txt = $BDef->Src;
- $SrBDefOrdered = &$BDef->SrBDefOrdered;
- $Empty = &$LocR->SerialEmpty;
-
- // All Items
- $F = false;
- for ($i=$BDef->SrBDefNbr;$i>0;$i--) {
- $SrBDef = &$SrBDefOrdered[$i];
- if ($SrBDef->SrTxt===false) { // Subsection not merged with a record
- if ($Empty===false) {
- $SrBDef->SrTxt = $this->meth_Merge_SectionNormal($SrBDef,$F);
- } else {
- $SrBDef->SrTxt = $Empty;
- }
- }
- $Txt = substr_replace($Txt,$SrBDef->SrTxt,$SrBDef->SrBeg,$SrBDef->SrLen);
- $SrBDef->SrTxt = false;
- }
-
- $SrId = 0;
- return $Txt;
-
-}
-
-function meth_Merge_AutoOn(&$Txt,$Name,$TplVar,$MergeVar) {
-// Merge [onload] or [onshow] fields and blocks
-
- $GrpDisplayed = array();
- $GrpExclusive = array();
- $P1 = false;
- $FieldBefore = false;
- $Pos = 0;
-
- while ($LocA=$this->meth_Locator_FindBlockNext($Txt,$Name,$Pos,'_',1,$P1,$FieldBefore)) {
-
- if ($LocA->BlockFound) {
-
- if (!isset($GrpDisplayed[$LocA->SubName])) {
- $GrpDisplayed[$LocA->SubName] = false;
- $GrpExclusive[$LocA->SubName] = ($LocA->SubName!=='');
- }
- $Displayed = &$GrpDisplayed[$LocA->SubName];
- $Exclusive = &$GrpExclusive[$LocA->SubName];
-
- $DelBlock = false;
- $DelField = false;
- if ($Displayed && $Exclusive) {
- $DelBlock = true;
- } else {
- if (isset($LocA->PrmLst['when'])) {
- if (isset($LocA->PrmLst['several'])) $Exclusive=false;
- $x = $LocA->PrmLst['when'];
- $this->meth_Merge_AutoVar($x,false);
- if ($this->f_Misc_CheckCondition($x)) {
- $DelField = true;
- $Displayed = true;
- } else {
- $DelBlock = true;
- }
- } elseif(isset($LocA->PrmLst['default'])) {
- if ($Displayed) {
- $DelBlock = true;
- } else {
- $Displayed = true;
- $DelField = true;
- }
- $Exclusive = true; // No more block displayed for the group after
- }
- }
-
- // Del parts
- if ($DelField) {
- if ($LocA->PosBeg2!==false) $Txt = substr_replace($Txt,'',$LocA->PosBeg2,$LocA->PosEnd2-$LocA->PosBeg2+1);
- $Txt = substr_replace($Txt,'',$LocA->PosBeg,$LocA->PosEnd-$LocA->PosBeg+1);
- $Pos = $LocA->PosBeg;
- } else {
- $FldPos = $LocA->PosBeg;
- $FldLen = $LocA->PosEnd - $LocA->PosBeg + 1;
- if ($LocA->PosBeg2===false) {
- 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');
- } else {
- $LocA->PosEnd = $LocA->PosEnd2;
- }
- if ($DelBlock) {
- $parallel = false;
- if (isset($LocA->PrmLst['parallel'])) {
- // may return false if error
- $parallel = $this->meth_Locator_FindParallel($Txt, $LocA->PosBeg, $LocA->PosEnd, $LocA->PrmLst['parallel']);
- if ($parallel===false) {
- $Txt = substr_replace($Txt,'',$FldPos,$FldLen);
- } else {
- // delete in reverse order
- for ($r = count($parallel)-1 ; $r >= 0 ; $r--) {
- $p = $parallel[$r];
- $Txt = substr_replace($Txt,'',$p['PosBeg'],$p['PosEnd']-$p['PosBeg']+1);
- }
- }
- } else {
- $Txt = substr_replace($Txt,'',$LocA->PosBeg,$LocA->PosEnd-$LocA->PosBeg+1);
- }
- } else {
- // Merge the block as if it was a field
- $x = '';
- $this->meth_Locator_Replace($Txt,$LocA,$x,false);
- }
- $Pos = $LocA->PosBeg;
- }
-
- } else { // Field (has no subname at this point)
-
- // Check for Template Var
- if ($TplVar) {
- if (isset($LocA->PrmLst['tplvars']) || isset($LocA->PrmLst['tplfrms'])) {
- $Scan = '';
- foreach ($LocA->PrmLst as $Key => $Val) {
- if ($Scan=='v') {
- $this->TplVars[$Key] = $Val;
- } elseif ($Scan=='f') {
- self::f_Misc_FormatSave($Val,$Key);
- } elseif ($Key==='tplvars') {
- $Scan = 'v';
- } elseif ($Key==='tplfrms') {
- $Scan = 'f';
- }
- }
- }
- }
-
- $x = '';
- $Pos = $this->meth_Locator_Replace($Txt,$LocA,$x,false);
- $Pos = $LocA->PosBeg;
-
- }
-
- }
-
- if ($MergeVar) $this->meth_Merge_AutoVar($this->Source,true,$Name); // merge other fields (must have subnames)
-
- foreach ($this->Assigned as $n=>$a) {
- if (isset($a['auto']) && ($a['auto']===$Name)) {
- $x = array();
- $this->meth_Misc_Assign($n,$x,false);
- }
- }
-
-}
-
-// Prepare the strconv parameter
-function meth_Conv_Prepare(&$Loc, $StrConv) {
- $x = strtolower($StrConv);
- $x = '+'.str_replace(' ','',$x).'+';
- if (strpos($x,'+esc+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvEsc = true; }
- if (strpos($x,'+wsp+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvWS = true; }
- if (strpos($x,'+js+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvJS = true; }
- if (strpos($x,'+url+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvUrl = true; }
- if (strpos($x,'+utf8+')!==false) {$this->f_Misc_ConvSpe($Loc); $Loc->ConvStr = false; $Loc->ConvUtf8 = true; }
- if (strpos($x,'+no+')!==false) $Loc->ConvStr = false;
- if (strpos($x,'+yes+')!==false) $Loc->ConvStr = true;
- if (strpos($x,'+nobr+')!==false) {$Loc->ConvStr = true; $Loc->ConvBr = false; }
-}
-
-// Convert a string with charset or custom function
-function meth_Conv_Str(&$Txt,$ConvBr=true) {
- if ($this->Charset==='') { // Html by default
- $Txt = htmlspecialchars($Txt);
- if ($ConvBr) $Txt = nl2br($Txt);
- } elseif ($this->_CharsetFct) {
- $Txt = call_user_func($this->Charset,$Txt,$ConvBr);
- } else {
- $Txt = htmlspecialchars($Txt,ENT_COMPAT,$this->Charset);
- if ($ConvBr) $Txt = nl2br($Txt);
- }
-}
-
-// Standard alert message provided by TinyButStrong, return False is the message is cancelled.
-function meth_Misc_Alert($Src,$Msg,$NoErrMsg=false,$SrcType=false) {
- $this->ErrCount++;
- if ($this->NoErr || (PHP_SAPI==='cli') ) {
- $t = array('','','','','');
- } else {
- $t = array('
','','','','
');
- $Msg = htmlentities($Msg);
- }
- if (!is_string($Src)) {
- if ($SrcType===false) $SrcType='in field';
- if (isset($Src->PrmLst['tbstype'])) {
- $Msg = 'Column \''.$Src->SubName.'\' is expected but missing in the current record.';
- $Src = 'Parameter \''.$Src->PrmLst['tbstype'].'='.$Src->SubName.'\'';
- $NoErrMsg = false;
- } else {
- $Src = $SrcType.' '.$this->_ChrOpen.$Src->FullName.'...'.$this->_ChrClose;
- }
- }
- $x = $t[0].'TinyButStrong Error'.$t[1].' '.$Src.': '.$Msg;
- if ($NoErrMsg) $x = $x.' '.$t[2].'This message can be cancelled using parameter \'noerr\'.'.$t[3];
- $x = $x.$t[4]."\n";
- if ($this->NoErr) {
- $this->ErrMsg .= $x;
- } else {
- if (PHP_SAPI!=='cli') {
- $x = str_replace($this->_ChrOpen,$this->_ChrProtect,$x);
- }
- echo $x;
- }
- return false;
-}
-
-function meth_Misc_Assign($Name,&$ArgLst,$CallingMeth) {
-// $ArgLst must be by reference in order to have its inner items by reference too.
-
- if (!isset($this->Assigned[$Name])) {
- if ($CallingMeth===false) return true;
- return $this->meth_Misc_Alert('with '.$CallingMeth.'() method','key \''.$Name.'\' is not defined in property Assigned.');
- }
-
- $a = &$this->Assigned[$Name];
- $meth = (isset($a['type'])) ? $a['type'] : 'MergeBlock';
- 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.'.');
-
- $n = count($a);
- for ($i=0;$i<$n;$i++) {
- if (isset($a[$i])) $ArgLst[$i] = &$a[$i];
- }
-
- if ($CallingMeth===false) {
- if (in_array(strtolower($meth),array('mergeblock','mergefield'))) {
- call_user_func_array(array(&$this,$meth), $ArgLst);
- } else {
- return $this->meth_Misc_Alert('The assigned field \''.$Name.'\'. cannot be merged because its type \''.$a[0].'\' is not supported.');
- }
- }
- if (!isset($a['merged'])) $a['merged'] = 0;
- $a['merged']++;
- return true;
-}
-
-function meth_Misc_IsMainTpl() {
- return ($this->_Mode==0);
-}
-
-function meth_Misc_ChangeMode($Init,&$Loc,&$CurrVal) {
- if ($Init) {
- // Save contents configuration
- $Loc->SaveSrc = &$this->Source;
- $Loc->SaveMode = $this->_Mode;
- $Loc->SaveVarRef = &$this->VarRef;
- unset($this->Source); $this->Source = '';
- $this->_Mode++; // Mode>0 means subtemplate mode
- if ($this->OldSubTpl) {
- ob_start(); // Start buffuring output
- $Loc->SaveRender = $this->Render;
- }
- $this->Render = TBS_OUTPUT;
- } else {
- // Restore contents configuration
- if ($this->OldSubTpl) {
- $CurrVal = ob_get_contents();
- ob_end_clean();
- $this->Render = $Loc->SaveRender;
- } else {
- $CurrVal = $this->Source;
- }
- $this->Source = &$Loc->SaveSrc;
- $this->_Mode = $Loc->SaveMode;
- $this->VarRef = &$Loc->SaveVarRef;
- }
-}
-
-function meth_Misc_UserFctCheck(&$FctInfo,$FctCat,&$FctObj,&$ErrMsg,$FctCheck=false) {
-
- $FctId = $FctCat.':'.$FctInfo;
- if (isset($this->_UserFctLst[$FctId])) {
- $FctInfo = $this->_UserFctLst[$FctId];
- return true;
- }
-
- // Check and put in cache
- $FctStr = $FctInfo;
- $IsData = ($FctCat!=='f');
- $Save = true;
- if ($FctStr[0]==='~') {
- $ObjRef = &$this->ObjectRef;
- $Lst = explode('.',substr($FctStr,1));
- $iMax = count($Lst) - 1;
- $Suff = 'tbsdb';
- $iMax0 = $iMax;
- if ($IsData) {
- $Suff = $Lst[$iMax];
- $iMax--;
- }
- // Reading sub items
- for ($i=0;$i<=$iMax;$i++) {
- $x = &$Lst[$i];
- if (is_object($ObjRef)) {
- $ArgLst = $this->f_Misc_CheckArgLst($x);
- if (method_exists($ObjRef,$x)) {
- if ($i<$iMax) {
- $f = array(&$ObjRef,$x); unset($ObjRef);
- $ObjRef = call_user_func_array($f,$ArgLst);
- }
- } elseif ($i===$iMax0) {
- $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because \''.$x.'\' is not a method in the class \''.get_class($ObjRef).'\'.';
- return false;
- } elseif (isset($ObjRef->$x)) {
- $ObjRef = &$ObjRef->$x;
- } else {
- $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because sub-item \''.$x.'\' is neither a method nor a property in the class \''.get_class($ObjRef).'\'.';
- return false;
- }
- } elseif (($i<$iMax0) && is_array($ObjRef)) {
- if (isset($ObjRef[$x])) {
- $ObjRef = &$ObjRef[$x];
- } else {
- $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because sub-item \''.$x.'\' is not a existing key in the array.';
- return false;
- }
- } else {
- $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because '.(($i===0)?'property ObjectRef':'sub-item \''.$x.'\'').' is not an object'.(($i<$iMax)?' or an array.':'.');
- return false;
- }
- }
- // Referencing last item
- if ($IsData) {
- $FctInfo = array('open'=>'','fetch'=>'','close'=>'');
- foreach ($FctInfo as $act=>$x) {
- $FctName = $Suff.'_'.$act;
- if (method_exists($ObjRef,$FctName)) {
- $FctInfo[$act] = array(&$ObjRef,$FctName);
- } else {
- $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because method '.$FctName.' is not found.';
- return false;
- }
- }
- $FctInfo['type'] = 4;
- if (isset($this->RecheckObj) && $this->RecheckObj) $Save = false;
- } else {
- $FctInfo = array(&$ObjRef,$x);
- }
- } elseif ($IsData) {
-
- $IsObj = ($FctCat==='o');
-
- if ($IsObj && method_exists($FctObj,'tbsdb_open') && (!method_exists($FctObj,'+'))) { // '+' avoid a bug in PHP 5
-
- if (!method_exists($FctObj,'tbsdb_fetch')) {
- $ErrMsg = 'the expected method \'tbsdb_fetch\' is not found for the class '.$Cls.'.';
- return false;
- }
- if (!method_exists($FctObj,'tbsdb_close')) {
- $ErrMsg = 'the expected method \'tbsdb_close\' is not found for the class '.$Cls.'.';
- return false;
- }
- $FctInfo = array('type'=>5);
-
- } else {
-
- if ($FctCat==='r') { // Resource
- $x = strtolower($FctStr);
- $x = str_replace('-','_',$x);
- $Key = '';
- $i = 0;
- $iMax = strlen($x);
- while ($i<$iMax) {
- if (($x[$i]==='_') || (($x[$i]>='a') && ($x[$i]<='z')) || (($x[$i]>='0') && ($x[$i]<='9'))) {
- $Key .= $x[$i];
- $i++;
- } else {
- $i = $iMax;
- }
- }
- } else {
- $Key = $FctStr;
- }
-
- $FctInfo = array('open'=>'','fetch'=>'','close'=>'');
- foreach ($FctInfo as $act=>$x) {
- $FctName = 'tbsdb_'.$Key.'_'.$act;
- if (function_exists($FctName)) {
- $FctInfo[$act] = $FctName;
- } else {
- $err = true;
- if ($act==='open') { // Try simplified key
- $p = strpos($Key,'_');
- if ($p!==false) {
- $Key2 = substr($Key,0,$p);
- $FctName2 = 'tbsdb_'.$Key2.'_'.$act;
- if (function_exists($FctName2)) {
- $err = false;
- $Key = $Key2;
- $FctInfo[$act] = $FctName2;
- }
- }
- }
- if ($err) {
- $ErrMsg = 'Data source Id \''.$FctStr.'\' is unsupported because function \''.$FctName.'\' is not found.';
- return false;
- }
- }
- }
-
- $FctInfo['type'] = 3;
-
- }
-
- } else {
- if ( $FctCheck && ($this->FctPrefix!=='') && (strncmp($this->FctPrefix,$FctStr,strlen($this->FctPrefix))!==0) ) {
- $ErrMsg = 'user function \''.$FctStr.'\' does not match the allowed prefix.'; return false;
- } else if (!function_exists($FctStr)) {
- $x = explode('.',$FctStr);
- if (count($x)==2) {
- if (class_exists($x[0])) {
- $FctInfo = $x;
- } else {
- $ErrMsg = 'user function \''.$FctStr.'\' is not correct because \''.$x[0].'\' is not a class name.'; return false;
- }
- } else {
- $ErrMsg = 'user function \''.$FctStr.'\' is not found.'; return false;
- }
- }
- }
-
- if ($Save) $this->_UserFctLst[$FctId] = $FctInfo;
- return true;
-
-}
-
-function meth_Misc_RunSubscript(&$CurrVal,$CurrPrm) {
-// Run a subscript without any local variable damage
- return @include($this->_Subscript);
-}
-
-function meth_Misc_Charset($Charset) {
- if ($Charset==='+') return;
- $this->_CharsetFct = false;
- if (is_string($Charset)) {
- if (($Charset!=='') && ($Charset[0]==='=')) {
- $ErrMsg = false;
- $Charset = substr($Charset,1);
- if ($this->meth_Misc_UserFctCheck($Charset,'f',$ErrMsg,$ErrMsg,false)) {
- $this->_CharsetFct = true;
- } else {
- $this->meth_Misc_Alert('with charset option',$ErrMsg);
- $Charset = '';
- }
- }
- } elseif (is_array($Charset)) {
- $this->_CharsetFct = true;
- } elseif ($Charset===false) {
- $this->Protect = false;
- } else {
- $this->meth_Misc_Alert('with charset option','the option value is not a string nor an array.');
- $Charset = '';
- }
- $this->Charset = $Charset;
-}
-
-function meth_PlugIn_RunAll(&$FctBank,&$ArgLst) {
- $OkAll = true;
- foreach ($FctBank as $FctInfo) {
- $Ok = call_user_func_array($FctInfo,$ArgLst);
- if (!is_null($Ok)) $OkAll = ($OkAll && $Ok);
- }
- return $OkAll;
-}
-
-function meth_PlugIn_Install($PlugInId,$ArgLst,$Auto) {
-
- $ErrMsg = 'with plug-in \''.$PlugInId.'\'';
-
- if (class_exists($PlugInId)) {
- // Create an instance
- $IsObj = true;
- $PiRef = new $PlugInId;
- $PiRef->TBS = &$this;
- if (!method_exists($PiRef,'OnInstall')) return $this->meth_Misc_Alert($ErrMsg,'OnInstall() method is not found.');
- $FctRef = array(&$PiRef,'OnInstall');
- } else {
- $FctRef = 'tbspi_'.$PlugInId.'_OnInstall';
- if(function_exists($FctRef)) {
- $IsObj = false;
- $PiRef = true;
- } else {
- return $this->meth_Misc_Alert($ErrMsg,'no class named \''.$PlugInId.'\' is found, and no function named \''.$FctRef.'\' is found.');
- }
- }
-
- $this->_PlugIns[$PlugInId] = &$PiRef;
-
- $EventLst = call_user_func_array($FctRef,$ArgLst);
- if (is_string($EventLst)) $EventLst = explode(',',$EventLst);
- if (!is_array($EventLst)) return $this->meth_Misc_Alert($ErrMsg,'OnInstall() method does not return an array.');
-
- // Add activated methods
- foreach ($EventLst as $Event) {
- $Event = trim($Event);
- if (!$this->meth_PlugIn_SetEvent($PlugInId, $Event)) return false;
- }
-
- return true;
-
-}
-
-function meth_PlugIn_SetEvent($PlugInId, $Event, $NewRef='') {
-// 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.
-
- // Check the event's name
- 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.');
-
- $PropName = '_pi'.$Event;
-
- if ($NewRef===false) {
- // Disable the event
- if (!isset($this->$PropName)) return false;
- $PropRef = &$this->$PropName;
- unset($PropRef[$PlugInId]);
- return true;
- }
-
- // Prepare the reference to be called
- $PiRef = &$this->_PlugIns[$PlugInId];
- if (is_object($PiRef)) {
- if ($NewRef==='') $NewRef = $Event;
- 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.');
- $FctRef = array(&$PiRef, $NewRef);
- } else {
- $FctRef = ($NewRef==='') ? 'tbspi_'.$PlugInId.'_'.$Event : $NewRef;
- if (!function_exists($FctRef)) return $this->meth_Misc_Alert('with plug-in \''.$PlugInId.'\'','The expected function \''.$FctRef.'\' is not found.');
- }
-
- // Save information into the corresponding property
- if (!isset($this->$PropName)) $this->$PropName = array();
- $PropRef = &$this->$PropName;
- $PropRef[$PlugInId] = $FctRef;
-
- // Flags saying if a plugin is installed
- switch ($Event) {
- case 'OnCommand': break;
- case 'OnSpecialVar': break;
- case 'OnOperation': break;
- case 'OnFormat': $this->_piOnFrm_Ok = true; break;
- default: $this->_PlugIns_Ok = true; break;
- }
-
- return true;
-
-}
-
-static function meth_Misc_ToStr($Value) {
- if (is_string($Value)) {
- return $Value;
- } elseif(is_object($Value)) {
- if (method_exists($Value,'__toString')) {
- return $Value->__toString();
- } elseif (is_a($Value, 'DateTime')) {
- return $Value->format('c');
- }
- }
- return @(string)$Value; // (string) is faster than strval() and settype()
-}
-
-function meth_Misc_Format(&$Value,&$PrmLst) {
-// This function return the formated representation of a Date/Time or numeric variable using a 'VB like' format syntax instead of the PHP syntax.
-
- $FrmStr = $PrmLst['frm'];
- $CheckNumeric = true;
- if (is_string($Value)) $Value = trim($Value);
-
- if ($FrmStr==='') return '';
- $Frm = self::f_Misc_FormatSave($FrmStr);
-
- // Manage Multi format strings
- if ($Frm['type']=='multi') {
-
- // Select the format
- if (is_numeric($Value)) {
- if (is_string($Value)) $Value = 0.0 + $Value;
- if ($Value>0) {
- $FrmStr = &$Frm[0];
- } elseif ($Value<0) {
- $FrmStr = &$Frm[1];
- if ($Frm['abs']) $Value = abs($Value);
- } else { // zero
- $FrmStr = &$Frm[2];
- $Minus = '';
- }
- $CheckNumeric = false;
- } else {
- $Value = $this->meth_Misc_ToStr($Value);
- if ($Value==='') {
- return $Frm[3]; // Null value
- } else {
- $t = strtotime($Value); // We look if it's a date
- if (($t===-1) || ($t===false)) { // Date not recognized
- return $Frm[1];
- } elseif ($t===943916400) { // Date to zero
- return $Frm[2];
- } else { // It's a date
- $Value = $t;
- $FrmStr = &$Frm[0];
- }
- }
- }
-
- // Retrieve the correct simple format
- if ($FrmStr==='') return '';
- $Frm = self::f_Misc_FormatSave($FrmStr);
-
- }
-
- switch ($Frm['type']) {
- case 'num' :
- // NUMERIC
- if ($CheckNumeric) {
- if (is_numeric($Value)) {
- if (is_string($Value)) $Value = 0.0 + $Value;
- } else {
- return $this->meth_Misc_ToStr($Value);
- }
- }
- if ($Frm['PerCent']) $Value = $Value * 100;
- $Value = number_format($Value,$Frm['DecNbr'],$Frm['DecSep'],$Frm['ThsSep']);
- if ($Frm['Pad']!==false) $Value = str_pad($Value, $Frm['Pad'], '0', STR_PAD_LEFT);
- if ($Frm['ThsRpl']!==false) $Value = str_replace($Frm['ThsSep'], $Frm['ThsRpl'], $Value);
- $Value = substr_replace($Frm['Str'],$Value,$Frm['Pos'],$Frm['Len']);
- return $Value;
- break;
- case 'date' :
- // DATE
- if (is_object($Value)) {
- $Value = $this->meth_Misc_ToStr($Value);
- }
- if (is_string($Value)) {
- if ($Value==='') return '';
- $x = strtotime($Value);
- if (($x===-1) || ($x===false)) {
- if (!is_numeric($Value)) $Value = 0;
- } else {
- $Value = &$x;
- }
- } else {
- if (!is_numeric($Value)) return $this->meth_Misc_ToStr($Value);
- }
- if ($Frm['loc'] || isset($PrmLst['locale'])) {
- $x = strftime($Frm['str_loc'],$Value);
- $this->meth_Conv_Str($x,false); // may have accent
- return $x;
- } else {
- return date($Frm['str_us'],$Value);
- }
- break;
- default:
- return $Frm['string'];
- break;
- }
-
-}
-
-// Simply update an array
-static function f_Misc_UpdateArray(&$array, $numerical, $v, $d) {
- if (!is_array($v)) {
- if (is_null($v)) {
- $array = array();
- return;
- } else {
- $v = array($v=>$d);
- }
- }
- foreach ($v as $p=>$a) {
- if ($numerical===true) { // numerical keys
- if (is_string($p)) {
- // syntax: item => true/false
- $i = array_search($p, $array, true);
- if ($i===false) {
- if (!is_null($a)) $array[] = $p;
- } else {
- if (is_null($a)) array_splice($array, $i, 1);
- }
- } else {
- // syntax: i => item
- $i = array_search($a, $array, true);
- if ($i==false) $array[] = $a;
- }
- } else { // string keys
- if (is_null($a)) {
- unset($array[$p]);
- } elseif ($numerical==='frm') {
- self::f_Misc_FormatSave($a, $p);
- } else {
- $array[$p] = $a;
- }
- }
- }
-}
-
-static function f_Misc_FormatSave(&$FrmStr,$Alias='') {
-
- $FormatLst = &$GLOBALS['_TBS_FormatLst'];
-
- if (isset($FormatLst[$FrmStr])) {
- if ($Alias!='') $FormatLst[$Alias] = &$FormatLst[$FrmStr];
- return $FormatLst[$FrmStr];
- }
-
- if (strpos($FrmStr,'|')!==false) {
-
- // Multi format
- $Frm = explode('|',$FrmStr); // syntax: Postive|Negative|Zero|Null
- $FrmNbr = count($Frm);
- $Frm['abs'] = ($FrmNbr>1);
- if ($FrmNbr<3) $Frm[2] = &$Frm[0]; // zero
- if ($FrmNbr<4) $Frm[3] = ''; // null
- $Frm['type'] = 'multi';
- $FormatLst[$FrmStr] = $Frm;
-
- } elseif (($nPosEnd = strrpos($FrmStr,'0'))!==false) {
-
- // Numeric format
- $nDecSep = '.';
- $nDecNbr = 0;
- $nDecOk = true;
- $nPad = false;
- $nPadZ = 0;
-
- if (substr($FrmStr,$nPosEnd+1,1)==='.') {
- $nPosEnd++;
- $nPos = $nPosEnd;
- $nPadZ = 1;
- } else {
- $nPos = $nPosEnd - 1;
- while (($nPos>=0) && ($FrmStr[$nPos]==='0')) {
- $nPos--;
- }
- if (($nPos>=1) && ($FrmStr[$nPos-1]==='0')) {
- $nDecSep = $FrmStr[$nPos];
- $nDecNbr = $nPosEnd - $nPos;
- } else {
- $nDecOk = false;
- }
- }
-
- // Thousand separator
- $nThsSep = '';
- $nThsRpl = false;
- if (($nDecOk) && ($nPos>=5)) {
- if ((substr($FrmStr,$nPos-3,3)==='000') && ($FrmStr[$nPos-4]!=='0')) {
- $p = strrpos(substr($FrmStr,0,$nPos-4), '0');
- if ($p!==false) {
- $len = $nPos-4-$p;
- $x = substr($FrmStr, $p+1, $len);
- if ($len>1) {
- // for compatibility for number_format() with PHP < 5.4.0
- $nThsSep = ($nDecSep=='*') ? '.' : '*';
- $nThsRpl = $x;
- } else {
- $nThsSep = $x;
- }
- $nPos = $p+1;
- }
- }
- }
-
- // Pass next zero
- if ($nDecOk) $nPos--;
- while (($nPos>=0) && ($FrmStr[$nPos]==='0')) {
- $nPos--;
- }
-
- $nLen = $nPosEnd-$nPos;
- if ( ($nThsSep==='') && ($nLen>($nDecNbr+$nPadZ+1)) ) $nPad = $nLen - $nPadZ;
-
- // Percent
- $nPerCent = (strpos($FrmStr,'%')===false) ? false : true;
-
- $FormatLst[$FrmStr] = array('type'=>'num','Str'=>$FrmStr,'Pos'=>($nPos+1),'Len'=>$nLen,'ThsSep'=>$nThsSep,'ThsRpl'=>$nThsRpl,'DecSep'=>$nDecSep,'DecNbr'=>$nDecNbr,'PerCent'=>$nPerCent,'Pad'=>$nPad);
-
- } else {
-
- // Date format
- $x = $FrmStr;
- $FrmPHP = '';
- $FrmLOC = '';
- $StrIn = false;
- $Cnt = 0;
- $i = strpos($FrmStr,'(locale)');
- $Locale = ($i!==false);
- if ($Locale) $x = substr_replace($x,'',$i,8);
-
- $iEnd = strlen($x);
- for ($i=0;$i<$iEnd;$i++) {
-
- if ($StrIn) {
- // We are in a string part
- if ($x[$i]==='"') {
- if (substr($x,$i+1,1)==='"') {
- $FrmPHP .= '\\"'; // protected char
- $FrmLOC .= $x[$i];
- $i++;
- } else {
- $StrIn = false;
- }
- } else {
- $FrmPHP .= '\\'.$x[$i]; // protected char
- $FrmLOC .= $x[$i];
- }
- } else {
- if ($x[$i]==='"') {
- $StrIn = true;
- } else {
- $Cnt++;
- if (strcasecmp(substr($x,$i,2),'hh' )===0) { $FrmPHP .= 'H'; $FrmLOC .= '%H'; $i += 1;}
- elseif (strcasecmp(substr($x,$i,2),'hm' )===0) { $FrmPHP .= 'h'; $FrmLOC .= '%I'; $i += 1;} // for compatibility
- elseif (strcasecmp(substr($x,$i,1),'h' )===0) { $FrmPHP .= 'G'; $FrmLOC .= '%H';}
- elseif (strcasecmp(substr($x,$i,2),'rr' )===0) { $FrmPHP .= 'h'; $FrmLOC .= '%I'; $i += 1;}
- elseif (strcasecmp(substr($x,$i,1),'r' )===0) { $FrmPHP .= 'g'; $FrmLOC .= '%I';}
- elseif (strcasecmp(substr($x,$i,4),'ampm')===0) { $FrmPHP .= substr($x,$i,1); $FrmLOC .= '%p'; $i += 3;} // $Fmp = 'A' or 'a'
- elseif (strcasecmp(substr($x,$i,2),'nn' )===0) { $FrmPHP .= 'i'; $FrmLOC .= '%M'; $i += 1;}
- elseif (strcasecmp(substr($x,$i,2),'ss' )===0) { $FrmPHP .= 's'; $FrmLOC .= '%S'; $i += 1;}
- elseif (strcasecmp(substr($x,$i,2),'xx' )===0) { $FrmPHP .= 'S'; $FrmLOC .= '' ; $i += 1;}
- elseif (strcasecmp(substr($x,$i,4),'yyyy')===0) { $FrmPHP .= 'Y'; $FrmLOC .= '%Y'; $i += 3;}
- elseif (strcasecmp(substr($x,$i,2),'yy' )===0) { $FrmPHP .= 'y'; $FrmLOC .= '%y'; $i += 1;}
- elseif (strcasecmp(substr($x,$i,4),'mmmm')===0) { $FrmPHP .= 'F'; $FrmLOC .= '%B'; $i += 3;}
- elseif (strcasecmp(substr($x,$i,3),'mmm' )===0) { $FrmPHP .= 'M'; $FrmLOC .= '%b'; $i += 2;}
- elseif (strcasecmp(substr($x,$i,2),'mm' )===0) { $FrmPHP .= 'm'; $FrmLOC .= '%m'; $i += 1;}
- elseif (strcasecmp(substr($x,$i,1),'m' )===0) { $FrmPHP .= 'n'; $FrmLOC .= '%m';}
- elseif (strcasecmp(substr($x,$i,4),'wwww')===0) { $FrmPHP .= 'l'; $FrmLOC .= '%A'; $i += 3;}
- elseif (strcasecmp(substr($x,$i,3),'www' )===0) { $FrmPHP .= 'D'; $FrmLOC .= '%a'; $i += 2;}
- elseif (strcasecmp(substr($x,$i,1),'w' )===0) { $FrmPHP .= 'w'; $FrmLOC .= '%u';}
- elseif (strcasecmp(substr($x,$i,4),'dddd')===0) { $FrmPHP .= 'l'; $FrmLOC .= '%A'; $i += 3;}
- elseif (strcasecmp(substr($x,$i,3),'ddd' )===0) { $FrmPHP .= 'D'; $FrmLOC .= '%a'; $i += 2;}
- elseif (strcasecmp(substr($x,$i,2),'dd' )===0) { $FrmPHP .= 'd'; $FrmLOC .= '%d'; $i += 1;}
- elseif (strcasecmp(substr($x,$i,1),'d' )===0) { $FrmPHP .= 'j'; $FrmLOC .= '%d';}
- else {
- $FrmPHP .= '\\'.$x[$i]; // protected char
- $FrmLOC .= $x[$i]; // protected char
- $Cnt--;
- }
- }
- }
-
- }
-
- if ($Cnt>0) {
- $FormatLst[$FrmStr] = array('type'=>'date','str_us'=>$FrmPHP,'str_loc'=>$FrmLOC,'loc'=>$Locale);
- } else {
- $FormatLst[$FrmStr] = array('type'=>'else','string'=>$FrmStr);
- }
-
- }
-
- if ($Alias!='') $FormatLst[$Alias] = &$FormatLst[$FrmStr];
-
- return $FormatLst[$FrmStr];
-
-}
-
-static function f_Misc_ConvSpe(&$Loc) {
- if ($Loc->ConvMode!==2) {
- $Loc->ConvMode = 2;
- $Loc->ConvEsc = false;
- $Loc->ConvWS = false;
- $Loc->ConvJS = false;
- $Loc->ConvUrl = false;
- $Loc->ConvUtf8 = false;
- }
-}
-
-static function f_Misc_CheckArgLst(&$Str) {
- $ArgLst = array();
- if (substr($Str,-1,1)===')') {
- $pos = strpos($Str,'(');
- if ($pos!==false) {
- $ArgLst = explode(',',substr($Str,$pos+1,strlen($Str)-$pos-2));
- $Str = substr($Str,0,$pos);
- }
- }
- return $ArgLst;
-}
-
-static function f_Misc_CheckCondition($Str) {
-// Check if an expression like "exrp1=expr2" is true or false.
-
- $StrZ = $Str; // same string but without protected data
- $Max = strlen($Str)-1;
- $p = strpos($Str,'\'');
- if ($Esc=($p!==false)) {
- $In = true;
- for ($p=$p+1;$p<=$Max;$p++) {
- if ($StrZ[$p]==='\'') {
- $In = !$In;
- } elseif ($In) {
- $StrZ[$p] = 'z';
- }
- }
- }
-
- // Find operator and position
- $Ope = '=';
- $Len = 1;
- $p = strpos($StrZ,$Ope);
- if ($p===false) {
- $Ope = '+';
- $p = strpos($StrZ,$Ope);
- if ($p===false) return false;
- if (($p>0) && ($StrZ[$p-1]==='-')) {
- $Ope = '-+'; $p--; $Len=2;
- } elseif (($p<$Max) && ($StrZ[$p+1]==='-')) {
- $Ope = '+-'; $Len=2;
- } else {
- return false;
- }
- } else {
- if ($p>0) {
- $x = $StrZ[$p-1];
- if ($x==='!') {
- $Ope = '!='; $p--; $Len=2;
- } elseif ($x==='~') {
- $Ope = '~='; $p--; $Len=2;
- } elseif ($p<$Max) {
- $y = $StrZ[$p+1];
- if ($y==='=') {
- $Len=2;
- } elseif (($x==='+') && ($y==='-')) {
- $Ope = '+=-'; $p--; $Len=3;
- } elseif (($x==='-') && ($y==='+')) {
- $Ope = '-=+'; $p--; $Len=3;
- }
- } else {
- }
- }
- }
-
- // Read values
- $Val1 = trim(substr($Str,0,$p));
- $Val2 = trim(substr($Str,$p+$Len));
- if ($Esc) {
- $Nude1 = self::f_Misc_DelDelimiter($Val1,'\'');
- $Nude2 = self::f_Misc_DelDelimiter($Val2,'\'');
- } else {
- $Nude1 = $Nude2 = false;
- }
-
- // Compare values
- if ($Ope==='=') {
- return (strcasecmp($Val1,$Val2)==0);
- } elseif ($Ope==='!=') {
- return (strcasecmp($Val1,$Val2)!=0);
- } elseif ($Ope==='~=') {
- return (preg_match($Val2,$Val1)>0);
- } else {
- if ($Nude1) $Val1='0'+$Val1;
- if ($Nude2) $Val2='0'+$Val2;
- if ($Ope==='+-') {
- return ($Val1>$Val2);
- } elseif ($Ope==='-+') {
- return ($Val1 < $Val2);
- } elseif ($Ope==='+=-') {
- return ($Val1 >= $Val2);
- } elseif ($Ope==='-=+') {
- return ($Val1<=$Val2);
- } else {
- return false;
- }
- }
-
-}
-
-static function f_Misc_DelDelimiter(&$Txt,$Delim) {
-// Delete the string delimiters
- $len = strlen($Txt);
- if (($len>1) && ($Txt[0]===$Delim)) {
- if ($Txt[$len-1]===$Delim) $Txt = substr($Txt,1,$len-2);
- return false;
- } else {
- return true;
- }
-}
-
-static function f_Misc_GetFile(&$Res, &$File, $LastFile='', $IncludePath=false, $Contents=true) {
-// Load the content of a file into the text variable.
-
- $Res = '';
- $fd = self::f_Misc_TryFile($File, false);
- if ($fd===false) {
- if (is_array($IncludePath)) {
- foreach ($IncludePath as $d) {
- $fd = self::f_Misc_TryFile($File, $d);
- if ($fd!==false) break;
- }
- }
- if (($fd===false) && ($LastFile!='')) $fd = self::f_Misc_TryFile($File, dirname($LastFile));
- if ($fd===false) return false;
- }
-
- $fs = fstat($fd);
- if ($Contents) {
- // Return contents
- if (isset($fs['size'])) {
- if ($fs['size']>0) $Res = fread($fd,$fs['size']);
- } else {
- while (!feof($fd)) $Res .= fread($fd,4096);
- }
- } else {
- // Return stats
- $Res = $fs;
- }
-
- fclose($fd);
- return true;
-
-}
-
-static function f_Misc_TryFile(&$File, $Dir) {
- if ($Dir==='') return false;
- $FileSearch = ($Dir===false) ? $File : $Dir.'/'.$File;
- // 'rb' if binary for some OS. fopen() uses include_path and search on the __FILE__ directory while file_exists() doesn't.
- $f = @fopen($FileSearch, 'r', true);
- if ($f!==false) $File = $FileSearch;
- return $f;
-}
-
-static function f_Loc_PrmRead(&$Txt,$Pos,$XmlTag,$DelimChrs,$BegStr,$EndStr,&$Loc,&$PosEnd,$WithPos=false) {
-
- $BegLen = strlen($BegStr);
- $BegChr = $BegStr[0];
- $BegIs1 = ($BegLen===1);
-
- $DelimIdx = false;
- $DelimCnt = 0;
- $DelimChr = '';
- $BegCnt = 0;
- $SubName = $Loc->SubOk;
-
- $Status = 0; // 0: name not started, 1: name started, 2: name ended, 3: equal found, 4: value started
- $PosName = 0;
- $PosNend = 0;
- $PosVal = 0;
-
- // Variables for checking the loop
- $PosEnd = strpos($Txt,$EndStr,$Pos);
- if ($PosEnd===false) return;
- $Continue = ($Pos<$PosEnd);
-
- while ($Continue) {
-
- $Chr = $Txt[$Pos];
-
- if ($DelimIdx) { // Reading in the string
-
- if ($Chr===$DelimChr) { // Quote found
- if ($Chr===$Txt[$Pos+1]) { // Double Quote => the string continue with un-double the quote
- $Pos++;
- } else { // Simple Quote => end of string
- $DelimIdx = false;
- }
- }
-
- } else { // Reading outside the string
-
- if ($BegCnt===0) {
-
- // Analyzing parameters
- $CheckChr = false;
- if (($Chr===' ') || ($Chr==="\r") || ($Chr==="\n")) {
- if ($Status===1) {
- if ($SubName && ($XmlTag===false)) {
- // Accept spaces in TBS subname.
- } else {
- $Status = 2;
- $PosNend = $Pos;
- }
- } elseif ($XmlTag && ($Status===4)) {
- self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos);
- $Status = 0;
- }
- } elseif (($XmlTag===false) && ($Chr===';')) {
- self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos);
- $Status = 0;
- } elseif ($Status===4) {
- $CheckChr = true;
- } elseif ($Status===3) {
- $Status = 4;
- $DelimCnt = 0;
- $PosVal = $Pos;
- $CheckChr = true;
- } elseif ($Status===2) {
- if ($Chr==='=') {
- $Status = 3;
- } elseif ($XmlTag) {
- self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos);
- $Status = 1;
- $PosName = $Pos;
- $CheckChr = true;
- } else {
- $Status = 4;
- $DelimCnt = 0;
- $PosVal = $Pos;
- $CheckChr = true;
- }
- } elseif ($Status===1) {
- if ($Chr==='=') {
- $Status = 3;
- $PosNend = $Pos;
- } else {
- $CheckChr = true;
- }
- } else {
- $Status = 1;
- $PosName = $Pos;
- $CheckChr = true;
- }
-
- if ($CheckChr) {
- $DelimIdx = strpos($DelimChrs,$Chr);
- if ($DelimIdx===false) {
- if ($Chr===$BegChr) {
- if ($BegIs1) {
- $BegCnt++;
- } elseif(substr($Txt,$Pos,$BegLen)===$BegStr) {
- $BegCnt++;
- }
- }
- } else {
- $DelimChr = $DelimChrs[$DelimIdx];
- $DelimCnt++;
- $DelimIdx = true;
- }
- }
-
- } else {
- if ($Chr===$BegChr) {
- if ($BegIs1) {
- $BegCnt++;
- } elseif(substr($Txt,$Pos,$BegLen)===$BegStr) {
- $BegCnt++;
- }
- }
- }
-
- }
-
- // Next char
- $Pos++;
-
- // We check if it's the end
- if ($Pos===$PosEnd) {
- if ($XmlTag) {
- $Continue = false;
- } elseif ($DelimIdx===false) {
- if ($BegCnt>0) {
- $BegCnt--;
- } else {
- $Continue = false;
- }
- }
- if ($Continue) {
- $PosEnd = strpos($Txt,$EndStr,$PosEnd+1);
- if ($PosEnd===false) return;
- } else {
- if ($XmlTag && ($Txt[$Pos-1]==='/')) $Pos--; // In case last attribute is stuck to "/>"
- self::f_Loc_PrmCompute($Txt,$Loc,$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos);
- }
- }
-
- }
-
- $PosEnd = $PosEnd + (strlen($EndStr)-1);
-
-}
-
-static function f_Loc_PrmCompute(&$Txt,&$Loc,&$SubName,$Status,$XmlTag,$DelimChr,$DelimCnt,$PosName,$PosNend,$PosVal,$Pos,$WithPos) {
-
- if ($Status===0) {
- $SubName = false;
- } else {
- if ($Status===1) {
- $x = substr($Txt,$PosName,$Pos-$PosName);
- } else {
- $x = substr($Txt,$PosName,$PosNend-$PosName);
- }
- if ($XmlTag) $x = strtolower($x);
- if ($SubName) {
- $Loc->SubName = trim($x);
- $SubName = false;
- } else {
- if ($Status===4) {
- $v = trim(substr($Txt,$PosVal,$Pos-$PosVal));
- if ($DelimCnt===1) { // Delete quotes inside the value
- if ($v[0]===$DelimChr) {
- $len = strlen($v);
- if ($v[$len-1]===$DelimChr) {
- $v = substr($v,1,$len-2);
- $v = str_replace($DelimChr.$DelimChr,$DelimChr,$v);
- }
- }
- }
- } else {
- $v = true;
- }
- if ($x==='if') {
- self::f_Loc_PrmIfThen($Loc,true,$v);
- } elseif ($x==='then') {
- self::f_Loc_PrmIfThen($Loc,false,$v);
- } else {
- $Loc->PrmLst[$x] = $v;
- if ($WithPos) $Loc->PrmPos[$x] = array($PosName,$PosNend,$PosVal,$Pos,$DelimChr,$DelimCnt);
- }
- }
- }
-
-}
-
-static function f_Loc_PrmIfThen(&$Loc,$IsIf,$Val) {
- $nbr = &$Loc->PrmIfNbr;
- if ($nbr===false) {
- $nbr = 0;
- $Loc->PrmIf = array();
- $Loc->PrmIfVar = array();
- $Loc->PrmThen = array();
- $Loc->PrmThenVar = array();
- $Loc->PrmElseVar = true;
- }
- if ($IsIf) {
- $nbr++;
- $Loc->PrmIf[$nbr] = $Val;
- $Loc->PrmIfVar[$nbr] = true;
- } else {
- $nbr2 = $nbr;
- if ($nbr2===false) $nbr2 = 1; // Only the first 'then' can be placed before its 'if'. This is for compatibility.
- $Loc->PrmThen[$nbr2] = $Val;
- $Loc->PrmThenVar[$nbr2] = true;
- }
-}
-
-static function f_Loc_EnlargeToStr(&$Txt,&$Loc,$StrBeg,$StrEnd) {
-/*
-This function enables to enlarge the pos limits of the Locator.
-If the search result is not correct, $PosBeg must not change its value, and $PosEnd must be False.
-This is because of the calling function.
-*/
-
- // Search for the begining string
- $Pos = $Loc->PosBeg;
- $Ok = false;
- do {
- $Pos = strrpos(substr($Txt,0,$Pos),$StrBeg[0]);
- if ($Pos!==false) {
- if (substr($Txt,$Pos,strlen($StrBeg))===$StrBeg) $Ok = true;
- }
- } while ( (!$Ok) && ($Pos!==false) );
-
- if ($Ok) {
- $PosEnd = strpos($Txt,$StrEnd,$Loc->PosEnd + 1);
- if ($PosEnd===false) {
- $Ok = false;
- } else {
- $Loc->PosBeg = $Pos;
- $Loc->PosEnd = $PosEnd + strlen($StrEnd) - 1;
- }
- }
-
- return $Ok;
-
-}
-
-static function f_Loc_EnlargeToTag(&$Txt,&$Loc,$TagStr,$RetInnerSrc) {
-//Modify $Loc, return false if tags not found, returns the inner source of tag if $RetInnerSrc=true
-
- $AliasLst = &$GLOBALS['_TBS_BlockAlias'];
-
- // Analyze string
- $Ref = 0;
- $LevelStop = 0;
- $i = 0;
- $TagFct = array();
- $TagLst = array();
- $TagBnd = array();
- while ($TagStr!=='') {
- // get next tag
- $p = strpos($TagStr, '+');
- if ($p===false) {
- $t = $TagStr;
- $TagStr = '';
- } else {
- $t = substr($TagStr,0,$p);
- $TagStr = substr($TagStr,$p+1);
- }
- // Check parentheses, relative position and single tag
- do {
- $t = trim($t);
- $e = strlen($t) - 1; // pos of last char
- if (($e>1) && ($t[0]==='(') && ($t[$e]===')')) {
- if ($Ref===0) $Ref = $i;
- if ($Ref===$i) $LevelStop++;
- $t = substr($t,1,$e-1);
- } else {
- if (($e>=0) && ($t[$e]==='/')) $t = substr($t,0,$e); // for compatibilty
- $e = false;
- }
- } while ($e!==false);
- // Check for multiples
- $p = strpos($t, '*');
- if ($p!==false) {
- $n = intval(substr($t, 0, $p));
- $t = substr($t, $p + 1);
- $n = max($n ,1); // prevent for error: minimum valu is 1
- $TagStr = str_repeat($t . '+', $n-1) . $TagStr;
- }
- // Reference
- if (($t==='.') && ($Ref===0)) $Ref = $i;
- // Take of the (!) prefix
- $b = '';
- if (($t!=='') && ($t[0]==='!')) {
- $t = substr($t, 1);
- $b = '!';
- }
- // Alias
- $a = false;
- if (isset($AliasLst[$t])) {
- $a = $AliasLst[$t]; // a string or a function
- if (is_string($a)) {
- if ($i>999) return false; // prevent from circular alias
- $TagStr = $b . $a . (($TagStr==='') ? '' : '+') . $TagStr;
- $t = false;
- }
- }
- if ($t!==false) {
- $TagLst[$i] = $t; // with prefix ! if specified
- $TagFct[$i] = $a;
- $TagBnd[$i] = ($b==='');
- $i++;
- }
- }
-
- $TagMax = $i-1;
-
- // Find tags that embeds the locator
- if ($LevelStop===0) $LevelStop = 1;
-
- // First tag of reference
- if ($TagLst[$Ref] === '.') {
- $TagO = new clsTbsLocator;
- $TagO->PosBeg = $Loc->PosBeg;
- $TagO->PosEnd = $Loc->PosEnd;
- $PosBeg = $Loc->PosBeg;
- $PosEnd = $Loc->PosEnd;
- } else {
- $TagO = self::f_Loc_Enlarge_Find($Txt,$TagLst[$Ref],$TagFct[$Ref],$Loc->PosBeg-1,false,$LevelStop);
- if ($TagO===false) return false;
- $PosBeg = $TagO->PosBeg;
- $LevelStop += -$TagO->RightLevel; // RightLevel=1 only if the tag is single and embeds $Loc, otherwise it is 0
- if ($LevelStop>0) {
- $TagC = self::f_Loc_Enlarge_Find($Txt,$TagLst[$Ref],$TagFct[$Ref],$Loc->PosEnd+1,true,-$LevelStop);
- if ($TagC==false) return false;
- $PosEnd = $TagC->PosEnd;
- $InnerLim = $TagC->PosBeg;
- if ((!$TagBnd[$Ref]) && ($TagMax==0)) {
- $PosBeg = $TagO->PosEnd + 1;
- $PosEnd = $TagC->PosBeg - 1;
- }
- } else {
- $PosEnd = $TagO->PosEnd;
- $InnerLim = $PosEnd + 1;
- }
- }
-
- $RetVal = true;
- if ($RetInnerSrc) {
- $RetVal = '';
- if ($Loc->PosBeg>$TagO->PosEnd) $RetVal .= substr($Txt,$TagO->PosEnd+1,min($Loc->PosBeg,$InnerLim)-$TagO->PosEnd-1);
- if ($Loc->PosEnd<$InnerLim) $RetVal .= substr($Txt,max($Loc->PosEnd,$TagO->PosEnd)+1,$InnerLim-max($Loc->PosEnd,$TagO->PosEnd)-1);
- }
-
- // Other tags forward
- $TagC = true;
- for ($i=$Ref+1;$i<=$TagMax;$i++) {
- $x = $TagLst[$i];
- if (($x!=='') && ($TagC!==false)) {
- $level = ($TagBnd[$i]) ? 0 : 1;
- $TagC = self::f_Loc_Enlarge_Find($Txt,$x,$TagFct[$i],$PosEnd+1,true,$level);
- if ($TagC!==false) {
- $PosEnd = ($TagBnd[$i]) ? $TagC->PosEnd : $TagC->PosBeg -1 ;
- }
- }
- }
-
- // Other tags backward
- $TagO = true;
- for ($i=$Ref-1;$i>=0;$i--) {
- $x = $TagLst[$i];
- if (($x!=='') && ($TagO!==false)) {
- $level = ($TagBnd[$i]) ? 0 : -1;
- $TagO = self::f_Loc_Enlarge_Find($Txt,$x,$TagFct[$i],$PosBeg-1,false,$level);
- if ($TagO!==false) {
- $PosBeg = ($TagBnd[$i]) ? $TagO->PosBeg : $TagO->PosEnd + 1;
- }
- }
- }
-
- $Loc->PosBeg = $PosBeg;
- $Loc->PosEnd = $PosEnd;
- return $RetVal;
-
-}
-
-static function f_Loc_Enlarge_Find($Txt, $Tag, $Fct, $Pos, $Forward, $LevelStop) {
- if ($Fct===false) {
- return self::f_Xml_FindTag($Txt,$Tag,(!$Forward),$Pos,$Forward,$LevelStop,false);
- } else {
- $p = call_user_func_array($Fct,array($Tag,$Txt,$Pos,$Forward,$LevelStop));
- if ($p===false) {
- return false;
- } else {
- return (object) array('PosBeg'=>$p, 'PosEnd'=>$p, 'RightLevel'=> 0); // it's a trick
- }
- }
-}
-
-static function f_Loc_AttBoolean($CurrVal, $AttTrue, $AttName) {
-
-// Return the good value for a boolean attribute
- if ($AttTrue===true) {
- if (self::meth_Misc_ToStr($CurrVal)==='') {
- return '';
- } else {
- return $AttName;
- }
- } elseif (self::meth_Misc_ToStr($CurrVal)===$AttTrue) {
- return $AttName;
- } else {
- return '';
- }
-}
-
-/**
- * Affects the positions of a list of locators regarding to a specific moving locator.
- */
-static function f_Loc_Moving(&$LocM, &$LocLst) {
- foreach ($LocLst as &$Loc) {
- if ($Loc !== $LocM) {
- if ($Loc->PosBeg >= $LocM->InsPos) {
- $Loc->PosBeg += $LocM->InsLen;
- $Loc->PosEnd += $LocM->InsLen;
- }
- if ($Loc->PosBeg > $LocM->DelPos) {
- $Loc->PosBeg -= $LocM->DelLen;
- $Loc->PosEnd -= $LocM->DelLen;
- }
- }
- }
- return true;
-}
-
-/**
- * Sort the locators in the list. Apply the bubble algorithm.
- * Deleted locators maked with DelMe.
- * @param array $LocLst An array of locators.
- * @param boolean $DelEmbd True to deleted locators that embded other ones.
- * @param boolean $iFirst Index of the first item.
- * @return integer Return the number of met embedding locators.
- */
-static function f_Loc_Sort(&$LocLst, $DelEmbd, $iFirst = 0) {
-
- $iLast = $iFirst + count($LocLst) - 1;
- $embd = 0;
-
- for ($i = $iLast ; $i>=$iFirst ; $i--) {
- $Loc = $LocLst[$i];
- $d = (isset($Loc->DelMe) && $Loc->DelMe);
- $b = $Loc->PosBeg;
- $e = $Loc->PosEnd;
- for ($j=$i+1; $j<=$iLast ; $j++) {
- // If DelMe, then the loc will be put at the end and deleted
- $jb = $LocLst[$j]->PosBeg;
- if ($d || ($b > $jb)) {
- $LocLst[$j-1] = $LocLst[$j];
- $LocLst[$j] = $Loc;
- } elseif ($e > $jb) {
- $embd++;
- if ($DelEmbd) {
- $d = true;
- $j--; // replay the current position
- } else {
- $j = $iLast; // quit the loop
- }
- } else {
- $j = $iLast; // quit the loop
- }
- }
- if ($d) {
- unset($LocLst[$iLast]);
- $iLast--;
- }
- }
-
- return $embd;
-}
-
-/**
- * Prepare all informations to move a locator according to parameter "att".
- * @param mixed $MoveLocLst true to simple move the loc, or an array of loc to rearrange the list after the move.
- * Note: rearrange doest not work with PHP4.
- */
-static function f_Xml_AttFind(&$Txt,&$Loc,$MoveLocLst=false,$AttDelim=false,$LocLst=false) {
-// att=div#class ; att=((div))#class ; att=+((div))#class
-
- $Att = $Loc->PrmLst['att'];
- unset($Loc->PrmLst['att']); // prevent from processing the field twice
- $Loc->PrmLst['att;'] = $Att; // for debug
-
- $p = strrpos($Att,'#');
- if ($p===false) {
- $TagLst = '';
- } else {
- $TagLst = substr($Att,0,$p);
- $Att = substr($Att,$p+1);
- }
-
- $Forward = (substr($TagLst,0,1)==='+');
- if ($Forward) $TagLst = substr($TagLst,1);
- $TagLst = explode('+',$TagLst);
-
- $iMax = count($TagLst)-1;
- $WithPrm = false;
- $LocO = &$Loc;
- foreach ($TagLst as $i=>$Tag) {
- $LevelStop = false;
- while ((strlen($Tag)>1) && (substr($Tag,0,1)==='(') && (substr($Tag,-1,1)===')')) {
- if ($LevelStop===false) $LevelStop = 0;
- $LevelStop++;
- $Tag = trim(substr($Tag,1,strlen($Tag)-2));
- }
- if ($i==$iMax) $WithPrm = true;
- $Pos = ($Forward) ? $LocO->PosEnd+1 : $LocO->PosBeg-1;
- unset($LocO);
- $LocO = self::f_Xml_FindTag($Txt,$Tag,true,$Pos,$Forward,$LevelStop,$WithPrm,$WithPrm);
- if ($LocO===false) return false;
- }
-
- $Loc->AttForward = $Forward;
- $Loc->AttTagBeg = $LocO->PosBeg;
- $Loc->AttTagEnd = $LocO->PosEnd;
- $Loc->AttDelimChr = false;
-
- if ($Att==='.') {
- // this indicates that the TBS field is supposed to be inside an attribute's value
- foreach ($LocO->PrmPos as $a=>$p ) {
- if ( ($p[0]<$Loc->PosBeg) && ($Loc->PosEnd<$p[3]) ) $Att = $a;
- }
- if ($Att==='.') return false;
- }
- $Loc->AttName = $Att;
-
- $AttLC = strtolower($Att);
- if (isset($LocO->PrmLst[$AttLC])) {
- // The attribute is existing
- $p = $LocO->PrmPos[$AttLC];
- $Loc->AttBeg = $p[0];
- $p[3]--; while ($Txt[$p[3]]===' ') $p[3]--; // external end of the attribute, may has an extra spaces
- $Loc->AttEnd = $p[3];
- $Loc->AttDelimCnt = $p[5];
- $Loc->AttDelimChr = $p[4];
- if (($p[1]>$p[0]) && ($p[2]>$p[1])) {
- //$Loc->AttNameEnd = $p[1];
- $Loc->AttValBeg = $p[2];
- } else { // attribute without value
- //$Loc->AttNameEnd = $p[3];
- $Loc->AttValBeg = false;
- }
- } else {
- // The attribute is not yet existing
- $Loc->AttDelimCnt = 0;
- $Loc->AttBeg = false;
- }
-
- // Search for a delimitor
- if (($Loc->AttDelimCnt==0) && (isset($LocO->PrmPos))) {
- foreach ($LocO->PrmPos as $p) {
- if ($p[5]>0) $Loc->AttDelimChr = $p[4];
- }
- }
-
- if ($MoveLocLst) return self::f_Xml_AttMove($Txt,$Loc,$AttDelim,$MoveLocLst);
-
- return true;
-
-}
-
-static function f_Xml_AttMove(&$Txt, &$Loc, $AttDelim, &$MoveLocLst) {
-
- if ($AttDelim===false) $AttDelim = $Loc->AttDelimChr;
- if ($AttDelim===false) $AttDelim = '"';
-
- $DelPos = $Loc->PosBeg;
- $DelLen = $Loc->PosEnd - $Loc->PosBeg + 1;
- $Txt = substr_replace($Txt,'',$DelPos,$DelLen); // delete the current locator
- if ($Loc->AttForward) {
- $Loc->AttTagBeg += -$DelLen;
- $Loc->AttTagEnd += -$DelLen;
- } elseif ($Loc->PosBeg<$Loc->AttTagEnd) {
- $Loc->AttTagEnd += -$DelLen;
- }
-
- $InsPos = false;
- if ($Loc->AttBeg===false) {
- $InsPos = $Loc->AttTagEnd;
- if ($Txt[$InsPos-1]==='/') $InsPos--;
- if ($Txt[$InsPos-1]===' ') $InsPos--;
- $Ins1 = ' '.$Loc->AttName.'='.$AttDelim;
- $Ins2 = $AttDelim;
- $Loc->AttBeg = $InsPos + 1;
- $Loc->AttValBeg = $InsPos + strlen($Ins1) - 1;
- } else {
- if ($Loc->PosEnd<$Loc->AttBeg) $Loc->AttBeg += -$DelLen;
- if ($Loc->PosEnd<$Loc->AttEnd) $Loc->AttEnd += -$DelLen;
- if ($Loc->AttValBeg===false) {
- $InsPos = $Loc->AttEnd+1;
- $Ins1 = '='.$AttDelim;
- $Ins2 = $AttDelim;
- $Loc->AttValBeg = $InsPos+1;
- } elseif (isset($Loc->PrmLst['attadd'])) {
- $InsPos = $Loc->AttEnd;
- $Ins1 = ' ';
- $Ins2 = '';
- } else {
- // value already existing
- if ($Loc->PosEnd<$Loc->AttValBeg) $Loc->AttValBeg += -$DelLen;
- $PosBeg = $Loc->AttValBeg;
- $PosEnd = $Loc->AttEnd;
- if ($Loc->AttDelimCnt>0) {$PosBeg++; $PosEnd--;}
- }
- }
-
- if ($InsPos===false) {
- $InsLen = 0;
- } else {
- $InsTxt = $Ins1.'[]'.$Ins2;
- $InsLen = strlen($InsTxt);
- $PosBeg = $InsPos + strlen($Ins1);
- $PosEnd = $PosBeg + 1;
- $Txt = substr_replace($Txt,$InsTxt,$InsPos,0);
- $Loc->AttEnd = $InsPos + $InsLen - 1;
- $Loc->AttTagEnd += $InsLen;
- }
-
- $Loc->PrevPosBeg = $Loc->PosBeg;
- $Loc->PrevPosEnd = $Loc->PosEnd;
- $Loc->PosBeg = $PosBeg;
- $Loc->PosEnd = $PosEnd;
- $Loc->AttBegM = ($Txt[$Loc->AttBeg-1]===' ') ? $Loc->AttBeg-1 : $Loc->AttBeg; // for magnet=#
-
- // for CacheField
- if (is_array($MoveLocLst)) {
- $Loc->InsPos = $InsPos;
- $Loc->InsLen = $InsLen;
- $Loc->DelPos = $DelPos;
- if ($Loc->InsPos < $Loc->DelPos) $Loc->DelPos += $InsLen;
- $Loc->DelLen = $DelLen;
- self::f_Loc_Moving($Loc, $MoveLocLst);
- }
-
- return true;
-
-}
-
-static function f_Xml_Max(&$Txt,&$Nbr,$MaxEnd) {
-// Limit the number of HTML chars
-
- $pMax = strlen($Txt)-1;
- $p=0;
- $n=0;
- $in = false;
- $ok = true;
-
- while ($ok) {
- if ($in) {
- if ($Txt[$p]===';') {
- $in = false;
- $n++;
- }
- } else {
- if ($Txt[$p]==='&') {
- $in = true;
- } else {
- $n++;
- }
- }
- if (($n>=$Nbr) || ($p>=$pMax)) {
- $ok = false;
- } else {
- $p++;
- }
- }
-
- if (($n>=$Nbr) && ($p<$pMax)) $Txt = substr($Txt,0,$p).$MaxEnd;
-
-}
-
-static function f_Xml_GetPart(&$Txt, $TagLst, $AllIfNothing=false) {
-// Returns parts of the XML/HTML content, default is BODY.
-
- if (($TagLst===true) || ($TagLst==='')) $TagLst = 'body';
-
- $x = '';
- $nothing = true;
- $TagLst = explode('+',$TagLst);
-
- // Build a clean list of tags
- foreach ($TagLst as $i=>$t) {
- if ((substr($t,0,1)=='(') && (substr($t,-1,1)==')')) {
- $t = substr($t,1,strlen($t)-2);
- $Keep = true;
- } else {
- $Keep = false;
- }
- $TagLst[$i] = array('t'=>$t, 'k'=>$Keep, 'b'=>-1, 'e'=>-1, 's'=>false);
- }
-
- $PosOut = strlen($Txt);
- $Pos = 0;
-
- // Optimized search for all tag types
- do {
-
- // Search next positions of each tag type
- $TagMin = false; // idx of the tag at first position
- $PosMin = $PosOut; // pos of the tag at first position
- foreach ($TagLst as $i=>$Tag) {
- if ($Tag['b']<$Pos) {
- $Loc = self::f_Xml_FindTag($Txt,$Tag['t'],true,$Pos,true,false,false);
- if ($Loc===false) {
- $Tag['b'] = $PosOut; // tag not found, no more search on this tag
- } else {
- $Tag['b'] = $Loc->PosBeg;
- $Tag['e'] = $Loc->PosEnd;
- $Tag['s'] = (substr($Txt,$Loc->PosEnd-1,1)==='/'); // true if it's a single tag
- }
- $TagLst[$i] = $Tag; // update
- }
- if ($Tag['b']<$PosMin) {
- $TagMin = $i;
- $PosMin = $Tag['b'];
- }
- }
-
- // Add the part of tag types
- if ($TagMin!==false) {
- $Tag = &$TagLst[$TagMin];
- $Pos = $Tag['e']+1;
- if ($Tag['s']) {
- // single tag
- if ($Tag['k']) $x .= substr($Txt,$Tag['b'] ,$Tag['e'] - $Tag['b'] + 1);
- } else {
- // search the closing tag
- $Loc = self::f_Xml_FindTag($Txt,$Tag['t'],false,$Pos,true,false,false);
- if ($Loc===false) {
- $Tag['b'] = $PosOut; // closing tag not found, no more search on this tag
- } else {
- $nothing = false;
- if ($Tag['k']) {
- $x .= substr($Txt,$Tag['b'] ,$Loc->PosEnd - $Tag['b'] + 1);
- } else {
- $x .= substr($Txt,$Tag['e']+1,$Loc->PosBeg - $Tag['e'] - 1);
- }
- $Pos = $Loc->PosEnd + 1;
- }
- }
- }
-
- } while ($TagMin!==false);
-
- if ($AllIfNothing && $nothing) return $Txt;
- return $x;
-
-}
-
-/**
- * Find the start of an XML tag. Used by OpenTBS.
- * $Case=false can be useful for HTML.
- * $Tag='' should work and found the start of the first tag.
- * $Tag='/' should work and found the start of the first closing tag.
- * Encapsulation levels are not featured yet.
- */
-static function f_Xml_FindTagStart(&$Txt,$Tag,$Opening,$PosBeg,$Forward,$Case=true) {
-
- if ($Txt==='') return false;
-
- $x = '<'.(($Opening) ? '' : '/').$Tag;
- $xl = strlen($x);
-
- $p = $PosBeg - (($Forward) ? 1 : -1);
-
- if ($Case) {
- do {
- if ($Forward) $p = strpos($Txt,$x,$p+1); else $p = strrpos(substr($Txt,0,$p+1),$x);
- if ($p===false) return false;
- /* COMPAT#6 */
- $z = substr($Txt,$p+$xl,1);
- } while ( ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') && ($Tag!=='/') && ($Tag!=='') );
- } else {
- do {
- if ($Forward) $p = stripos($Txt,$x,$p+1); else $p = strripos(substr($Txt,0,$p+1),$x);
- if ($p===false) return false;
- /* COMPAT#7 */
- $z = substr($Txt,$p+$xl,1);
- } while ( ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') && ($Tag!=='/') && ($Tag!=='') );
- }
-
- return $p;
-
-}
-
-/**
- * This function is a smart solution to find an XML tag.
- * It allows to ignore full opening/closing couple of tags that could be inserted before the searched tag.
- * It allows also to pass a number of encapsulations.
- * To ignore encapsulation and opengin/closing just set $LevelStop=false.
- * $Opening is used only when $LevelStop=false.
- */
-static function f_Xml_FindTag(&$Txt,$Tag,$Opening,$PosBeg,$Forward,$LevelStop,$WithPrm,$WithPos=false) {
-
- if ($Tag==='_') { // New line
- $p = self::f_Xml_FindNewLine($Txt,$PosBeg,$Forward,($LevelStop!==0));
- $Loc = new clsTbsLocator;
- $Loc->PosBeg = ($Forward) ? $PosBeg : $p;
- $Loc->PosEnd = ($Forward) ? $p : $PosBeg;
- $Loc->RightLevel = 0;
- return $Loc;
- }
-
- $Pos = $PosBeg + (($Forward) ? -1 : +1);
- $TagIsOpening = false;
- $TagClosing = '/'.$Tag;
- $LevelNum = 0;
- $TagOk = false;
- $PosEnd = false;
- $TagL = strlen($Tag);
- $TagClosingL = strlen($TagClosing);
- $RightLevel = 0;
-
- do {
-
- // Look for the next tag def
- if ($Forward) {
- $Pos = strpos($Txt,'<',$Pos+1);
- } else {
- if ($Pos<=0) {
- $Pos = false;
- } else {
- $Pos = strrpos(substr($Txt,0,$Pos - 1),'<'); // strrpos() syntax compatible with PHP 4
- }
- }
-
- if ($Pos!==false) {
-
- // Check the name of the tag
- if (strcasecmp(substr($Txt,$Pos+1,$TagL),$Tag)==0) {
- // It's an opening tag
- $PosX = $Pos + 1 + $TagL; // The next char
- $TagOk = true;
- $TagIsOpening = true;
- } elseif (strcasecmp(substr($Txt,$Pos+1,$TagClosingL),$TagClosing)==0) {
- // It's a closing tag
- $PosX = $Pos + 1 + $TagClosingL; // The next char
- $TagOk = true;
- $TagIsOpening = false;
- }
-
- if ($TagOk) {
- // Check the next char
- $x = $Txt[$PosX];
- if (($x===' ') || ($x==="\r") || ($x==="\n") || ($x==='>') || ($x==='/') || ($Tag==='/') || ($Tag==='')) {
- // Check the encapsulation count
- if ($LevelStop===false) { // No encapsulation check
- if ($TagIsOpening!==$Opening) $TagOk = false;
- } else { // Count the number of level
- if ($TagIsOpening) {
- $PosEnd = strpos($Txt,'>',$PosX);
- if ($PosEnd!==false) {
- if ($Txt[$PosEnd-1]==='/') {
- if (($Pos<$PosBeg) && ($PosEnd>$PosBeg)) {$RightLevel=1; $LevelNum++;}
- } else {
- $LevelNum++;
- }
- }
- } else {
- $LevelNum--;
- }
- // Check if it's the expected level
- if ($LevelNum!=$LevelStop) {
- $TagOk = false;
- $PosEnd = false;
- }
- }
- } else {
- $TagOk = false;
- }
- } //--> if ($TagOk)
-
- }
- } while (($Pos!==false) && ($TagOk===false));
-
- // Search for the end of the tag
- if ($TagOk) {
- $Loc = new clsTbsLocator;
- if ($WithPrm) {
- self::f_Loc_PrmRead($Txt,$PosX,true,'\'"','<','>',$Loc,$PosEnd,$WithPos);
- } elseif ($PosEnd===false) {
- $PosEnd = strpos($Txt,'>',$PosX);
- if ($PosEnd===false) {
- $TagOk = false;
- }
- }
- }
-
- // Result
- if ($TagOk) {
- $Loc->PosBeg = $Pos;
- $Loc->PosEnd = $PosEnd;
- $Loc->RightLevel = $RightLevel;
- return $Loc;
- } else {
- return false;
- }
-
-}
-
-static function f_Xml_FindNewLine(&$Txt,$PosBeg,$Forward,$IsRef) {
-
- $p = $PosBeg;
- if ($Forward) {
- $Inc = 1;
- $Inf = &$p;
- $Sup = strlen($Txt)-1;
- } else {
- $Inc = -1;
- $Inf = 0;
- $Sup = &$p;
- }
-
- do {
- if ($Inf>$Sup) return max($Sup,0);
- $x = $Txt[$p];
- if (($x==="\r") || ($x==="\n")) {
- $x2 = ($x==="\n") ? "\r" : "\n";
- $p0 = $p;
- if (($Inf<$Sup) && ($Txt[$p+$Inc]===$x2)) $p += $Inc; // Newline char can have two chars.
- if ($Forward) return $p; // Forward => return pos including newline char.
- 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.
- }
- $p += $Inc;
- } while (true);
-
+class clsTbsLocator
+{
+ public $PosBeg = false;
+ public $PosEnd = false;
+ public $Enlarged = false;
+ public $FullName = false;
+ public $SubName = '';
+ public $SubOk = false;
+ public $SubLst = [];
+ public $SubNbr = 0;
+ public $PrmLst = [];
+ public $PrmIfNbr = false;
+ public $MagnetId = false;
+ public $BlockFound = false;
+ public $FirstMerge = true;
+ public $ConvProtect = true;
+ public $ConvStr = true;
+ public $ConvMode = 1; // Normal
+ public $ConvBr = true;
}
-static function f_Xml_GetNextEntityName($Txt, $Pos, &$tag, &$PosBeg, &$p) {
-/*
- $tag : tag name
- $PosBeg : position of the tag
- $p : position where the read has stop
- $z : first char after the name
-*/
-
- $tag = '';
- $PosBeg = strpos($Txt, '<', $Pos);
-
- if ($PosBeg===false) return false;
-
- // Read the name of the tag
- $go = true;
- $p = $PosBeg;
- while ($go) {
- $p++;
- $z = $Txt[$p];
- if ($go = ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') ) {
- $tag .= $z;
- }
- }
-
- return true;
-
+class clsTbsDataSource
+{
+ public $Type = false;
+ public $SubType = 0;
+ public $SrcId = false;
+ public $Query = '';
+ public $RecSet = false;
+ public $RecKey = '';
+ public $RecNum = 0;
+ public $RecNumInit = 0;
+ public $RecSaving = false;
+ public $RecSaved = false;
+ public $RecBuffer = false;
+ public $CurrRec = false;
+ public $TBS = false;
+ public $OnDataOk = false;
+ public $OnDataPrm = false;
+ public $OnDataPrmDone = [];
+ public $OnDataPi = false;
+
+ public function DataAlert($Msg)
+ {
+ if (is_array($this->TBS->_CurrBlock)) {
+ return $this->TBS->meth_Misc_Alert('when merging block "'.implode(',', $this->TBS->_CurrBlock).'"', $Msg);
+ } else {
+ return $this->TBS->meth_Misc_Alert('when merging block '.$this->TBS->_ChrOpen.$this->TBS->_CurrBlock.$this->TBS->_ChrClose, $Msg);
+ }
+ }
+
+ public function DataPrepare(&$SrcId, &$TBS)
+ {
+ $this->SrcId = &$SrcId;
+ $this->TBS = &$TBS;
+ $FctInfo = false;
+ $FctObj = false;
+
+ if (is_array($SrcId)) {
+ $this->Type = 0;
+ } elseif (is_resource($SrcId)) {
+ $Key = get_resource_type($SrcId);
+ switch ($Key) {
+ case 'mysql link':
+ $this->Type = 6;
+ break;
+ case 'mysql link persistent':
+ $this->Type = 6;
+ break;
+ case 'mysql result':
+ $this->Type = 6;
+ $this->SubType = 1;
+ break;
+ case 'pgsql link':
+ $this->Type = 7;
+ break;
+ case 'pgsql link persistent':
+ $this->Type = 7;
+ break;
+ case 'pgsql result':
+ $this->Type = 7;
+ $this->SubType = 1;
+ break;
+ case 'sqlite database':
+ $this->Type = 8;
+ break;
+ case 'sqlite database (persistent)':
+ $this->Type = 8;
+ break;
+ case 'sqlite result':
+ $this->Type = 8;
+ $this->SubType = 1;
+ break;
+ default:
+ $FctInfo = $Key;
+ $FctCat = 'r';
+ }
+ } elseif (is_string($SrcId)) {
+ switch (strtolower($SrcId)) {
+ case 'array':
+ $this->Type = 0;
+ $this->SubType = 1;
+ break;
+ case 'clear':
+ $this->Type = 0;
+ $this->SubType = 3;
+ break;
+ case 'mysql':
+ $this->Type = 6;
+ $this->SubType = 2;
+ break;
+ case 'text':
+ $this->Type = 2;
+ break;
+ case 'num':
+ $this->Type = 1;
+ break;
+ default:
+ $FctInfo = $SrcId;
+ $FctCat = 'k';
+ }
+ } elseif ($SrcId instanceof Iterator) {
+ $this->Type = 9;
+ $this->SubType = 1;
+ } elseif ($SrcId instanceof ArrayObject) {
+ $this->Type = 9;
+ $this->SubType = 2;
+ } elseif ($SrcId instanceof IteratorAggregate) {
+ $this->Type = 9;
+ $this->SubType = 3;
+ } elseif ($SrcId instanceof MySQLi) {
+ $this->Type = 10;
+ } elseif ($SrcId instanceof PDO) {
+ $this->Type = 11;
+ } elseif ($SrcId instanceof Zend_Db_Adapter_Abstract) {
+ $this->Type = 12;
+ } elseif ($SrcId instanceof SQLite3) {
+ $this->Type = 13;
+ $this->SubType = 1;
+ } elseif ($SrcId instanceof SQLite3Stmt) {
+ $this->Type = 13;
+ $this->SubType = 2;
+ } elseif ($SrcId instanceof SQLite3Result) {
+ $this->Type = 13;
+ $this->SubType = 3;
+ } elseif (is_object($SrcId)) {
+ $FctInfo = get_class($SrcId);
+ $FctCat = 'o';
+ $FctObj = &$SrcId;
+ $this->SrcId = &$SrcId;
+ } elseif ($SrcId===false) {
+ $this->DataAlert('the specified source is set to FALSE. Maybe your connection has failed.');
+ } else {
+ $this->DataAlert('unsupported variable type : \''.gettype($SrcId).'\'.');
+ }
+
+ if ($FctInfo !== false) {
+ $ErrMsg = false;
+ if ($TBS->meth_Misc_UserFctCheck($FctInfo, $FctCat, $FctObj, $ErrMsg, false)) {
+ $this->Type = $FctInfo['type'];
+ if ($this->Type !== 5) {
+ if ($this->Type === 4) {
+ $this->FctPrm = [false, 0];
+ $this->SrcId = &$FctInfo['open'][0];
+ }
+ $this->FctOpen = &$FctInfo['open'];
+ $this->FctFetch = &$FctInfo['fetch'];
+ $this->FctClose = &$FctInfo['close'];
+ }
+ } else {
+ $this->Type = $this->DataAlert($ErrMsg);
+ }
+ }
+
+ return ($this->Type!==false);
+ }
+
+ public function DataOpen(&$Query, $QryPrms=false)
+ {
+
+ // Init values
+ unset($this->CurrRec);
+ $this->CurrRec = true;
+ if ($this->RecSaved) {
+ $this->FirstRec = true;
+ unset($this->RecKey);
+ $this->RecKey = '';
+ $this->RecNum = $this->RecNumInit;
+ if ($this->OnDataOk) {
+ $this->OnDataArgs[1] = &$this->CurrRec;
+ }
+ return true;
+ }
+ unset($this->RecSet);
+ $this->RecSet = false;
+ $this->RecNumInit = 0;
+ $this->RecNum = 0;
+
+ if (isset($this->TBS->_piOnData)) {
+ $this->OnDataPi = true;
+ $this->OnDataPiRef = &$this->TBS->_piOnData;
+ $this->OnDataOk = true;
+ }
+ if ($this->OnDataOk) {
+ $this->OnDataArgs = [];
+ $this->OnDataArgs[0] = &$this->TBS->_CurrBlock;
+ $this->OnDataArgs[1] = &$this->CurrRec;
+ $this->OnDataArgs[2] = &$this->RecNum;
+ $this->OnDataArgs[3] = &$this->TBS;
+ }
+
+ switch ($this->Type) {
+ case 0: // Array
+ if (($this->SubType===1) && (is_string($Query))) {
+ $this->SubType = 2;
+ }
+ if ($this->SubType===0) {
+ $this->RecSet = &$this->SrcId; /* COMPAT#2 */
+ } elseif ($this->SubType===1) {
+ if (is_array($Query)) {
+ $this->RecSet = &$Query; /* COMPAT#3 */
+ } else {
+ $this->DataAlert('type \''.gettype($Query).'\' not supported for the Query Parameter going with \'array\' Source Type.');
+ }
+ } elseif ($this->SubType===2) {
+ // TBS query string for array and objects, syntax: "var[item1][item2]->item3[item4]..."
+ $x = trim($Query);
+ $z = chr(0);
+ $x = str_replace([']->','][','->','['], $z, $x);
+ if (substr($x, strlen($x)-1, 1)===']') {
+ $x = substr($x, 0, strlen($x)-1);
+ }
+ $ItemLst = explode($z, $x);
+ $ItemNbr = count($ItemLst);
+ $Item0 = &$ItemLst[0];
+ // Check first item
+ if ($Item0[0]==='~') {
+ $Item0 = substr($Item0, 1);
+ if ($this->TBS->ObjectRef!==false) {
+ $Var = &$this->TBS->ObjectRef;
+ $i = 0;
+ } else {
+ $i = $this->DataAlert('invalid query \''.$Query.'\' because property ObjectRef is not set.');
+ }
+ } else {
+ if (isset($this->TBS->VarRef[$Item0])) {
+ $Var = &$this->TBS->VarRef[$Item0]; /* COMPAT#4 */
+ $i = 1;
+ } else {
+ $i = $this->DataAlert('invalid query \''.$Query.'\' because VarRef item \''.$Item0.'\' is not found.');
+ }
+ }
+ // Check sub-items
+ $Empty = false;
+ while (($i!==false) && ($i<$ItemNbr) && ($Empty===false)) {
+ $x = $ItemLst[$i];
+ if (is_array($Var)) {
+ if (isset($Var[$x])) {
+ $Var = &$Var[$x];
+ } else {
+ $Empty = true;
+ }
+ } elseif (is_object($Var)) {
+ $ArgLst = $this->TBS->f_Misc_CheckArgLst($x);
+ if (method_exists($Var, $x)) {
+ $f = [&$Var,$x];
+ unset($Var);
+ $Var = call_user_func_array($f, $ArgLst);
+ } elseif (property_exists(get_class($Var), $x)) {
+ if (isset($Var->$x)) {
+ $Var = &$Var->$x;
+ }
+ } elseif (isset($Var->$x)) {
+ $Var = $Var->$x; // useful for overloaded property
+ } else {
+ $Empty = true;
+ }
+ } else {
+ $i = $this->DataAlert('invalid query \''.$Query.'\' because item \''.$ItemLst[$i].'\' is neither an Array nor an Object. Its type is \''.gettype($Var).'\'.');
+ }
+ if ($i!==false) {
+ $i++;
+ }
+ }
+ // Assign data
+ if ($i!==false) {
+ if ($Empty) {
+ $this->RecSet = [];
+ } else {
+ $this->RecSet = &$Var;
+ }
+ }
+ } elseif ($this->SubType===3) { // Clear
+ $this->RecSet = [];
+ }
+ // First record
+ if ($this->RecSet!==false) {
+ $this->RecNbr = $this->RecNumInit + count($this->RecSet);
+ $this->FirstRec = true;
+ $this->RecSaved = true;
+ $this->RecSaving = false;
+ }
+ break;
+ case 6: // MySQL
+ switch ($this->SubType) {
+ case 0: $this->RecSet = @mysql_query($Query, $this->SrcId); break;
+ case 1: $this->RecSet = $this->SrcId; break;
+ case 2: $this->RecSet = @mysql_query($Query); break;
+ }
+ if ($this->RecSet===false) {
+ $this->DataAlert('MySql error message when opening the query: '.mysql_error());
+ }
+ break;
+ case 1: // Num
+ $this->RecSet = true;
+ $this->NumMin = 1;
+ $this->NumMax = 1;
+ $this->NumStep = 1;
+ if (is_array($Query)) {
+ if (isset($Query['min'])) {
+ $this->NumMin = $Query['min'];
+ }
+ if (isset($Query['step'])) {
+ $this->NumStep = $Query['step'];
+ }
+ if (isset($Query['max'])) {
+ $this->NumMax = $Query['max'];
+ } else {
+ $this->RecSet = $this->DataAlert('the \'num\' source is an array that has no value for the \'max\' key.');
+ }
+ if ($this->NumStep==0) {
+ $this->RecSet = $this->DataAlert('the \'num\' source is an array that has a step value set to zero.');
+ }
+ } else {
+ $this->NumMax = ceil($Query);
+ }
+ if ($this->RecSet) {
+ if ($this->NumStep>0) {
+ $this->NumVal = $this->NumMin;
+ } else {
+ $this->NumVal = $this->NumMax;
+ }
+ }
+ break;
+ case 2: // Text
+ if (is_string($Query)) {
+ $this->RecSet = &$Query;
+ } else {
+ $this->RecSet = $this->TBS->meth_Misc_ToStr($Query);
+ }
+ break;
+ case 3: // Custom function
+ $FctOpen = $this->FctOpen;
+ $this->RecSet = $FctOpen($this->SrcId, $Query, $QryPrms);
+ if ($this->RecSet===false) {
+ $this->DataAlert('function '.$FctOpen.'() has failed to open query {'.$Query.'}');
+ }
+ break;
+ case 4: // Custom method from ObjectRef
+ $this->RecSet = call_user_func_array($this->FctOpen, [&$this->SrcId,&$Query,&$QryPrms]);
+ if ($this->RecSet===false) {
+ $this->DataAlert('method '.get_class($this->FctOpen[0]).'::'.$this->FctOpen[1].'() has failed to open query {'.$Query.'}');
+ }
+ break;
+ case 5: // Custom method of object
+ $this->RecSet = $this->SrcId->tbsdb_open($this->SrcId, $Query, $QryPrms);
+ if ($this->RecSet===false) {
+ $this->DataAlert('method '.get_class($this->SrcId).'::tbsdb_open() has failed to open query {'.$Query.'}');
+ }
+ break;
+ case 7: // PostgreSQL
+ switch ($this->SubType) {
+ case 0: $this->RecSet = @pg_query($this->SrcId, $Query); break;
+ case 1: $this->RecSet = $this->SrcId; break;
+ }
+ if ($this->RecSet===false) {
+ $this->DataAlert('PostgreSQL error message when opening the query: '.pg_last_error($this->SrcId));
+ }
+ break;
+ case 8: // SQLite
+ switch ($this->SubType) {
+ case 0: $this->RecSet = @sqlite_query($this->SrcId, $Query); break;
+ case 1: $this->RecSet = $this->SrcId; break;
+ }
+ if ($this->RecSet===false) {
+ $this->DataAlert('SQLite error message when opening the query:'.sqlite_error_string(sqlite_last_error($this->SrcId)));
+ }
+ break;
+ case 9: // Iterator
+ if ($this->SubType==1) {
+ $this->RecSet = $this->SrcId;
+ } else { // 2 or 3
+ $this->RecSet = $this->SrcId->getIterator();
+ }
+ $this->RecSet->rewind();
+ break;
+ case 10: // MySQLi
+ $this->RecSet = $this->SrcId->query($Query);
+ if ($this->RecSet===false) {
+ $this->DataAlert('MySQLi error message when opening the query:'.$this->SrcId->error);
+ }
+ break;
+ case 11: // PDO
+ $this->RecSet = $this->SrcId->prepare($Query);
+ if ($this->RecSet===false) {
+ $ok = false;
+ } else {
+ if (!is_array($QryPrms)) {
+ $QryPrms = [];
+ }
+ $ok = $this->RecSet->execute($QryPrms);
+ }
+ if (!$ok) {
+ $err = $this->SrcId->errorInfo();
+ $this->DataAlert('PDO error message when opening the query:'.$err[2]);
+ }
+ break;
+ case 12: // Zend_DB_Adapter
+ try {
+ if (!is_array($QryPrms)) {
+ $QryPrms = [];
+ }
+ $this->RecSet = $this->SrcId->query($Query, $QryPrms);
+ } catch (Exception $e) {
+ $this->DataAlert('Zend_DB_Adapter error message when opening the query: '.$e->getMessage());
+ }
+ break;
+ case 13: // SQLite3
+ try {
+ if ($this->SubType==3) {
+ $this->RecSet = $this->SrcId;
+ } elseif (($this->SubType==1) && (!is_array($QryPrms))) {
+ // SQL statement without parameters
+ $this->RecSet = $this->SrcId->query($Query);
+ } else {
+ if ($this->SubType==2) {
+ $stmt = $this->SrcId;
+ $prms = $Query;
+ } else {
+ // SQL statement with parameters
+ $stmt = $this->SrcId->prepare($Query);
+ $prms = $QryPrms;
+ }
+ // bind parameters
+ if (is_array($prms)) {
+ foreach ($prms as $p => $v) {
+ if (is_numeric($p)) {
+ $p = $p + 1;
+ }
+ if (is_array($v)) {
+ $stmt->bindValue($p, $v[0], $v[1]);
+ } else {
+ $stmt->bindValue($p, $v);
+ }
+ }
+ }
+ $this->RecSet = $stmt->execute();
+ }
+ } catch (Exception $e) {
+ $this->DataAlert('SQLite3 error message when opening the query: '.$e->getMessage());
+ }
+ break;
+ }
+
+ if (($this->Type===0) || ($this->Type===9)) {
+ unset($this->RecKey);
+ $this->RecKey = '';
+ } else {
+ if ($this->RecSaving) {
+ unset($this->RecBuffer);
+ $this->RecBuffer = [];
+ }
+ $this->RecKey = &$this->RecNum; // Not array: RecKey = RecNum
+ }
+
+ return ($this->RecSet!==false);
+ }
+
+ public function DataFetch()
+ {
+ if ($this->RecSaved) {
+ if ($this->RecNum<$this->RecNbr) {
+ if ($this->FirstRec) {
+ if ($this->SubType===2) { // From string
+ reset($this->RecSet);
+ $this->RecKey = key($this->RecSet);
+ $this->CurrRec = &$this->RecSet[$this->RecKey];
+ } else {
+ $this->CurrRec = reset($this->RecSet);
+ $this->RecKey = key($this->RecSet);
+ }
+ $this->FirstRec = false;
+ } else {
+ if ($this->SubType===2) { // From string
+ next($this->RecSet);
+ $this->RecKey = key($this->RecSet);
+ $this->CurrRec = &$this->RecSet[$this->RecKey];
+ } else {
+ $this->CurrRec = next($this->RecSet);
+ $this->RecKey = key($this->RecSet);
+ }
+ }
+ if ((!is_array($this->CurrRec)) && (!is_object($this->CurrRec))) {
+ $this->CurrRec = ['key'=>$this->RecKey, 'val'=>$this->CurrRec];
+ }
+ $this->RecNum++;
+ if ($this->OnDataOk) {
+ $this->OnDataArgs[1] = &$this->CurrRec; // Reference has changed if ($this->SubType===2)
+ if ($this->OnDataPrm) {
+ call_user_func_array($this->OnDataPrmRef, $this->OnDataArgs);
+ }
+ if ($this->OnDataPi) {
+ $this->TBS->meth_PlugIn_RunAll($this->OnDataPiRef, $this->OnDataArgs);
+ }
+ if ($this->SubType!==2) {
+ $this->RecSet[$this->RecKey] = $this->CurrRec;
+ } // save modifications because array reading is done without reference :(
+ }
+ } else {
+ unset($this->CurrRec);
+ $this->CurrRec = false;
+ }
+ return;
+ }
+
+ switch ($this->Type) {
+ case 6: // MySQL
+ $this->CurrRec = mysql_fetch_assoc($this->RecSet);
+ break;
+ case 1: // Num
+ if (($this->NumVal>=$this->NumMin) && ($this->NumVal<=$this->NumMax)) {
+ $this->CurrRec = ['val'=>$this->NumVal];
+ $this->NumVal += $this->NumStep;
+ } else {
+ $this->CurrRec = false;
+ }
+ break;
+ case 2: // Text
+ if ($this->RecNum===0) {
+ if ($this->RecSet==='') {
+ $this->CurrRec = false;
+ } else {
+ $this->CurrRec = &$this->RecSet;
+ }
+ } else {
+ $this->CurrRec = false;
+ }
+ break;
+ case 3: // Custom function
+ $FctFetch = $this->FctFetch;
+ $this->CurrRec = $FctFetch($this->RecSet, $this->RecNum+1);
+ break;
+ case 4: // Custom method from ObjectRef
+ $this->FctPrm[0] = &$this->RecSet; $this->FctPrm[1] = $this->RecNum+1;
+ $this->CurrRec = call_user_func_array($this->FctFetch, $this->FctPrm);
+ break;
+ case 5: // Custom method of object
+ $this->CurrRec = $this->SrcId->tbsdb_fetch($this->RecSet, $this->RecNum+1);
+ break;
+ case 7: // PostgreSQL
+ $this->CurrRec = pg_fetch_assoc($this->RecSet); /* COMPAT#5 */
+ break;
+ case 8: // SQLite
+ $this->CurrRec = sqlite_fetch_array($this->RecSet, SQLITE_ASSOC);
+ break;
+ case 9: // Iterator
+ if ($this->RecSet->valid()) {
+ $this->CurrRec = $this->RecSet->current();
+ $this->RecKey = $this->RecSet->key();
+ $this->RecSet->next();
+ } else {
+ $this->CurrRec = false;
+ }
+ break;
+ case 10: // MySQLi
+ $this->CurrRec = $this->RecSet->fetch_assoc();
+ if (is_null($this->CurrRec)) {
+ $this->CurrRec = false;
+ }
+ break;
+ case 11: // PDO
+ $this->CurrRec = $this->RecSet->fetch(PDO::FETCH_ASSOC);
+ break;
+ case 12: // Zend_DB_Adapater
+ $this->CurrRec = $this->RecSet->fetch(Zend_Db::FETCH_ASSOC);
+ break;
+ case 13: // SQLite3
+ $this->CurrRec = $this->RecSet->fetchArray(SQLITE3_ASSOC);
+ break;
+ }
+
+ // Set the row count
+ if ($this->CurrRec!==false) {
+ $this->RecNum++;
+ if ($this->OnDataOk) {
+ if ($this->OnDataPrm) {
+ call_user_func_array($this->OnDataPrmRef, $this->OnDataArgs);
+ }
+ if ($this->OnDataPi) {
+ $this->TBS->meth_PlugIn_RunAll($this->OnDataPiRef, $this->OnDataArgs);
+ }
+ }
+ if ($this->RecSaving) {
+ $this->RecBuffer[$this->RecKey] = $this->CurrRec;
+ }
+ }
+ }
+
+ public function DataClose()
+ {
+ $this->OnDataOk = false;
+ $this->OnDataPrm = false;
+ $this->OnDataPi = false;
+ if ($this->RecSaved) {
+ return;
+ }
+ switch ($this->Type) {
+ case 6:
+ mysql_free_result($this->RecSet);
+ break;
+ case 3:
+ $FctClose = $this->FctClose;
+ $FctClose($this->RecSet);
+ break;
+ case 4:
+ call_user_func_array($this->FctClose, [&$this->RecSet]);
+ break;
+ case 5:
+ $this->SrcId->tbsdb_close($this->RecSet);
+ break;
+ case 7:
+ pg_free_result($this->RecSet);
+ break;
+ case 10:
+ $this->RecSet->free();
+ break; // MySQLi
+ case 13: // SQLite3
+ if ($this->SubType != 3) {
+ $this->RecSet->finalize();
+ }
+ break;
+ //case 11: $this->RecSet->closeCursor(); break; // PDO
+ }
+ if ($this->RecSaving) {
+ $this->RecSet = &$this->RecBuffer;
+ $this->RecNbr = $this->RecNumInit + count($this->RecSet);
+ $this->RecSaving = false;
+ $this->RecSaved = true;
+ }
+ }
}
+class clsTinyButStrong
+{
+ // Public properties
+ public $Source = '';
+ public $Render = 3;
+ public $TplVars = [];
+ public $ObjectRef = false;
+ public $NoErr = false;
+ public $Assigned = [];
+ public $ExtendedMethods = [];
+ public $ErrCount = 0;
+ // Undocumented (can change at any version)
+ public $Version = '3.10.1';
+ public $Charset = '';
+ public $TurboBlock = true;
+ public $VarPrefix = '';
+ public $VarRef = null;
+ public $FctPrefix = '';
+ public $Protect = true;
+ public $ErrMsg = '';
+ public $AttDelim = false;
+ public $MethodsAllowed = false;
+ public $OnLoad = true;
+ public $OnShow = true;
+ public $IncludePath = [];
+ public $TplStore = [];
+ public $OldSubTpl = false;
+ // Private
+ public $_ErrMsgName = '';
+ public $_LastFile = '';
+ public $_CharsetFct = false;
+ public $_Mode = 0;
+ public $_CurrBlock = '';
+ public $_ChrOpen = '[';
+ public $_ChrClose = ']';
+ public $_ChrVal = '[val]';
+ public $_ChrProtect = '[';
+ public $_PlugIns = [];
+ public $_PlugIns_Ok = false;
+ public $_piOnFrm_Ok = false;
+
+ public function __construct($Options=null, $VarPrefix='', $FctPrefix='')
+ {
+ // Compatibility
+ if (is_string($Options)) {
+ $Chrs = $Options;
+ $Options = ['var_prefix'=>$VarPrefix, 'fct_prefix'=>$FctPrefix];
+ if ($Chrs!=='') {
+ $Err = true;
+ $Len = strlen($Chrs);
+ if ($Len===2) { // For compatibility
+ $Options['chr_open'] = $Chrs[0];
+ $Options['chr_close'] = $Chrs[1];
+ $Err = false;
+ } else {
+ $Pos = strpos($Chrs, ',');
+ if (($Pos!==false) && ($Pos>0) && ($Pos<$Len-1)) {
+ $Options['chr_open'] = substr($Chrs, 0, $Pos);
+ $Options['chr_close'] = substr($Chrs, $Pos+1);
+ $Err = false;
+ }
+ }
+ if ($Err) {
+ $this->meth_Misc_Alert('with clsTinyButStrong() function', 'value \''.$Chrs.'\' is a bad tag delimitor definition.');
+ }
+ }
+ }
+
+ // Set options
+ $this->VarRef =& $GLOBALS;
+ if (is_array($Options)) {
+ $this->SetOption($Options);
+ }
+
+ // Links to global variables (cannot be converted to static yet because of compatibility)
+ global $_TBS_FormatLst, $_TBS_UserFctLst, $_TBS_BlockAlias, $_TBS_AutoInstallPlugIns, $_TBS_ParallelLst;
+ if (!isset($_TBS_FormatLst)) {
+ $_TBS_FormatLst = [];
+ }
+ if (!isset($_TBS_UserFctLst)) {
+ $_TBS_UserFctLst = [];
+ }
+ if (!isset($_TBS_BlockAlias)) {
+ $_TBS_BlockAlias = [];
+ }
+ if (!isset($_TBS_ParallelLst)) {
+ $_TBS_ParallelLst = [];
+ }
+ $this->_UserFctLst = &$_TBS_UserFctLst;
+
+ // Auto-installing plug-ins
+ if (isset($_TBS_AutoInstallPlugIns)) {
+ foreach ($_TBS_AutoInstallPlugIns as $pi) {
+ $this->PlugIn(TBS_INSTALL, $pi);
+ }
+ }
+ }
+
+ public function __call($meth, $args)
+ {
+ if (isset($this->ExtendedMethods[$meth])) {
+ if (is_array($this->ExtendedMethods[$meth]) || is_string($this->ExtendedMethods[$meth])) {
+ return call_user_func_array($this->ExtendedMethods[$meth], $args);
+ } else {
+ return call_user_func_array([&$this->ExtendedMethods[$meth], $meth], $args);
+ }
+ } else {
+ $this->meth_Misc_Alert('Method not found', '\''.$meth.'\' is neither a native nor an extended method of TinyButStrong.');
+ }
+ }
+
+ public function SetOption($o, $v=false, $d=false)
+ {
+ if (!is_array($o)) {
+ $o = [$o=>$v];
+ }
+ if (isset($o['var_prefix'])) {
+ $this->VarPrefix = $o['var_prefix'];
+ }
+ if (isset($o['fct_prefix'])) {
+ $this->FctPrefix = $o['fct_prefix'];
+ }
+ if (isset($o['noerr'])) {
+ $this->NoErr = $o['noerr'];
+ }
+ if (isset($o['old_subtemplate'])) {
+ $this->OldSubTpl = $o['old_subtemplate'];
+ }
+ if (isset($o['auto_merge'])) {
+ $this->OnLoad = $o['auto_merge'];
+ $this->OnShow = $o['auto_merge'];
+ }
+ if (isset($o['onload'])) {
+ $this->OnLoad = $o['onload'];
+ }
+ if (isset($o['onshow'])) {
+ $this->OnShow = $o['onshow'];
+ }
+ if (isset($o['att_delim'])) {
+ $this->AttDelim = $o['att_delim'];
+ }
+ if (isset($o['protect'])) {
+ $this->Protect = $o['protect'];
+ }
+ if (isset($o['turbo_block'])) {
+ $this->TurboBlock = $o['turbo_block'];
+ }
+ if (isset($o['charset'])) {
+ $this->meth_Misc_Charset($o['charset']);
+ }
+
+ $UpdateChr = false;
+ if (isset($o['chr_open'])) {
+ $this->_ChrOpen = $o['chr_open'];
+ $UpdateChr = true;
+ }
+ if (isset($o['chr_close'])) {
+ $this->_ChrClose = $o['chr_close'];
+ $UpdateChr = true;
+ }
+ if ($UpdateChr) {
+ $this->_ChrVal = $this->_ChrOpen.'val'.$this->_ChrClose;
+ $this->_ChrProtect = ''.ord($this->_ChrOpen[0]).';'.substr($this->_ChrOpen, 1);
+ }
+ if (array_key_exists('tpl_frms', $o)) {
+ self::f_Misc_UpdateArray($GLOBALS['_TBS_FormatLst'], 'frm', $o['tpl_frms'], $d);
+ }
+ if (array_key_exists('block_alias', $o)) {
+ self::f_Misc_UpdateArray($GLOBALS['_TBS_BlockAlias'], false, $o['block_alias'], $d);
+ }
+ if (array_key_exists('parallel_conf', $o)) {
+ self::f_Misc_UpdateArray($GLOBALS['_TBS_ParallelLst'], false, $o['parallel_conf'], $d);
+ }
+ if (array_key_exists('include_path', $o)) {
+ self::f_Misc_UpdateArray($this->IncludePath, true, $o['include_path'], $d);
+ }
+ if (isset($o['render'])) {
+ $this->Render = $o['render'];
+ }
+ if (isset($o['methods_allowed'])) {
+ $this->MethodsAllowed = $o['methods_allowed'];
+ }
+ }
+
+ public function GetOption($o)
+ {
+ if ($o==='all') {
+ $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');
+ $r = [];
+ foreach ($x as $o) {
+ $r[$o] = $this->GetOption($o);
+ }
+ return $r;
+ }
+ if ($o==='var_prefix') {
+ return $this->VarPrefix;
+ }
+ if ($o==='fct_prefix') {
+ return $this->FctPrefix;
+ }
+ if ($o==='noerr') {
+ return $this->NoErr;
+ }
+ if ($o==='auto_merge') {
+ return ($this->OnLoad && $this->OnShow);
+ }
+ if ($o==='onload') {
+ return $this->OnLoad;
+ }
+ if ($o==='onshow') {
+ return $this->OnShow;
+ }
+ if ($o==='att_delim') {
+ return $this->AttDelim;
+ }
+ if ($o==='protect') {
+ return $this->Protect;
+ }
+ if ($o==='turbo_block') {
+ return $this->TurboBlock;
+ }
+ if ($o==='charset') {
+ return $this->Charset;
+ }
+ if ($o==='chr_open') {
+ return $this->_ChrOpen;
+ }
+ if ($o==='chr_close') {
+ return $this->_ChrClose;
+ }
+ if ($o==='tpl_frms') {
+ // simplify the list of formats
+ $x = [];
+ foreach ($GLOBALS['_TBS_FormatLst'] as $s=>$i) {
+ $x[$s] = $i['Str'];
+ }
+ return $x;
+ }
+ if ($o==='include_path') {
+ return $this->IncludePath;
+ }
+ if ($o==='render') {
+ return $this->Render;
+ }
+ if ($o==='methods_allowed') {
+ return $this->MethodsAllowed;
+ }
+ if ($o==='parallel_conf') {
+ return $GLOBALS['_TBS_ParallelLst'];
+ }
+ if ($o==='block_alias') {
+ return $GLOBALS['_TBS_BlockAlias'];
+ }
+ return $this->meth_Misc_Alert('with GetOption() method', 'option \''.$o.'\' is not supported.');
+ }
+
+ public function ResetVarRef($ToGlobal)
+ {
+ if ($ToGlobal) {
+ $this->VarRef = &$GLOBALS;
+ } else {
+ $x = [];
+ $this->VarRef = &$x;
+ }
+ }
+
+ // Public methods
+ public function LoadTemplate($File, $Charset='')
+ {
+ if ($File==='') {
+ $this->meth_Misc_Charset($Charset);
+ return true;
+ }
+ $Ok = true;
+ if ($this->_PlugIns_Ok) {
+ if (isset($this->_piBeforeLoadTemplate) || isset($this->_piAfterLoadTemplate)) {
+ // Plug-ins
+ $ArgLst = func_get_args();
+ $ArgLst[0] = &$File;
+ $ArgLst[1] = &$Charset;
+ if (isset($this->_piBeforeLoadTemplate)) {
+ $Ok = $this->meth_PlugIn_RunAll($this->_piBeforeLoadTemplate, $ArgLst);
+ }
+ }
+ }
+ // Load the file
+ if ($Ok!==false) {
+ if (!is_null($File)) {
+ $x = '';
+ 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.');
+ }
+ if ($Charset==='+') {
+ $this->Source .= $x;
+ } else {
+ $this->Source = $x;
+ }
+ }
+ if ($this->meth_Misc_IsMainTpl()) {
+ if (!is_null($File)) {
+ $this->_LastFile = $File;
+ }
+ if ($Charset!=='+') {
+ $this->TplVars = [];
+ }
+ $this->meth_Misc_Charset($Charset);
+ }
+ // Automatic fields and blocks
+ if ($this->OnLoad) {
+ $this->meth_Merge_AutoOn($this->Source, 'onload', true, true);
+ }
+ }
+ // Plug-ins
+ if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterLoadTemplate)) {
+ $Ok = $this->meth_PlugIn_RunAll($this->_piAfterLoadTemplate, $ArgLst);
+ }
+ return $Ok;
+ }
+
+ public function GetBlockSource($BlockName, $AsArray=false, $DefTags=true, $ReplaceWith=false)
+ {
+ $RetVal = [];
+ $Nbr = 0;
+ $Pos = 0;
+ $FieldOutside = false;
+ $P1 = false;
+ $Mode = ($DefTags) ? 3 : 2;
+ $PosBeg1 = 0;
+ while ($Loc = $this->meth_Locator_FindBlockNext($this->Source, $BlockName, $Pos, '.', $Mode, $P1, $FieldOutside)) {
+ $Nbr++;
+ $Sep = '';
+ if ($Nbr==1) {
+ $PosBeg1 = $Loc->PosBeg;
+ } elseif (!$AsArray) {
+ $Sep = substr($this->Source, $PosSep, $Loc->PosBeg-$PosSep); // part of the source between sections
+ }
+ $RetVal[$Nbr] = $Sep.$Loc->BlockSrc;
+ $Pos = $Loc->PosEnd;
+ $PosSep = $Loc->PosEnd+1;
+ $P1 = false;
+ }
+ if ($Nbr==0) {
+ return false;
+ }
+ if (!$AsArray) {
+ if ($DefTags) {
+ // Return the true part of the template
+ $RetVal = substr($this->Source, $PosBeg1, $Pos-$PosBeg1+1);
+ } else {
+ // Return the concatenated section without def tags
+ $RetVal = implode('', $RetVal);
+ }
+ }
+ if ($ReplaceWith!==false) {
+ $this->Source = substr($this->Source, 0, $PosBeg1).$ReplaceWith.substr($this->Source, $Pos+1);
+ }
+ return $RetVal;
+ }
+
+ public function MergeBlock($BlockLst, $SrcId='assigned', $Query='', $QryPrms=false)
+ {
+ if ($SrcId==='assigned') {
+ $Arg = [$BlockLst,&$SrcId,&$Query,&$QryPrms];
+ if (!$this->meth_Misc_Assign($BlockLst, $Arg, 'MergeBlock')) {
+ return 0;
+ }
+ $BlockLst = $Arg[0];
+ $SrcId = &$Arg[1];
+ $Query = &$Arg[2];
+ }
+
+ if (is_string($BlockLst)) {
+ $BlockLst = explode(',', $BlockLst);
+ }
+
+ if ($SrcId==='cond') {
+ $Nbr = 0;
+ foreach ($BlockLst as $Block) {
+ $Block = trim($Block);
+ if ($Block!=='') {
+ $Nbr += $this->meth_Merge_AutoOn($this->Source, $Block, true, true);
+ }
+ }
+ return $Nbr;
+ } else {
+ return $this->meth_Merge_Block($this->Source, $BlockLst, $SrcId, $Query, false, 0, $QryPrms);
+ }
+ }
+
+ public function MergeField($NameLst, $Value='assigned', $IsUserFct=false, $DefaultPrm=false)
+ {
+ $FctCheck = $IsUserFct;
+ if ($PlugIn = isset($this->_piOnMergeField)) {
+ $ArgPi = ['','',&$Value,0,&$this->Source,0,0];
+ }
+ $SubStart = 0;
+ $Ok = true;
+ $Prm = is_array($DefaultPrm);
+
+ if (($Value==='assigned') && ($NameLst!=='var') && ($NameLst!=='onshow') && ($NameLst!=='onload')) {
+ $Arg = [$NameLst,&$Value,&$IsUserFct,&$DefaultPrm];
+ if (!$this->meth_Misc_Assign($NameLst, $Arg, 'MergeField')) {
+ return false;
+ }
+ $NameLst = $Arg[0];
+ $Value = &$Arg[1];
+ $IsUserFct = &$Arg[2];
+ $DefaultPrm = &$Arg[3];
+ }
+
+ $NameLst = explode(',', $NameLst);
+
+ foreach ($NameLst as $Name) {
+ $Name = trim($Name);
+ $Cont = false;
+ switch ($Name) {
+ case '': $Cont=true;break;
+ case 'onload': $this->meth_Merge_AutoOn($this->Source, 'onload', true, true);$Cont=true;break;
+ case 'onshow': $this->meth_Merge_AutoOn($this->Source, 'onshow', true, true);$Cont=true;break;
+ case 'var': $this->meth_Merge_AutoVar($this->Source, true);$Cont=true;break;
+ }
+ if ($Cont) {
+ continue;
+ }
+ if ($PlugIn) {
+ $ArgPi[0] = $Name;
+ }
+ $PosBeg = 0;
+ // Initilize the user function (only once)
+ if ($FctCheck) {
+ $FctInfo = $Value;
+ $ErrMsg = false;
+ if (!$this->meth_Misc_UserFctCheck($FctInfo, 'f', $ErrMsg, $ErrMsg, false)) {
+ return $this->meth_Misc_Alert('with MergeField() method', $ErrMsg);
+ }
+ $FctArg = ['',''];
+ $SubStart = false;
+ $FctCheck = false;
+ }
+ while ($Loc = $this->meth_Locator_FindTbs($this->Source, $Name, $PosBeg, '.')) {
+ if ($Prm) {
+ $Loc->PrmLst = array_merge($DefaultPrm, $Loc->PrmLst);
+ }
+ // Apply user function
+ if ($IsUserFct) {
+ $FctArg[0] = &$Loc->SubName;
+ $FctArg[1] = &$Loc->PrmLst;
+ $Value = call_user_func_array($FctInfo, $FctArg);
+ }
+ // Plug-ins
+ if ($PlugIn) {
+ $ArgPi[1] = $Loc->SubName;
+ $ArgPi[3] = &$Loc->PrmLst;
+ $ArgPi[5] = &$Loc->PosBeg;
+ $ArgPi[6] = &$Loc->PosEnd;
+ $Ok = $this->meth_PlugIn_RunAll($this->_piOnMergeField, $ArgPi);
+ }
+ // Merge the field
+ if ($Ok) {
+ $PosBeg = $this->meth_Locator_Replace($this->Source, $Loc, $Value, $SubStart);
+ } else {
+ $PosBeg = $Loc->PosEnd;
+ }
+ }
+ }
+ }
+
+ public function Show($Render=false)
+ {
+ $Ok = true;
+ if ($Render===false) {
+ $Render = $this->Render;
+ }
+ if ($this->_PlugIns_Ok) {
+ if (isset($this->_piBeforeShow) || isset($this->_piAfterShow)) {
+ // Plug-ins
+ $ArgLst = func_get_args();
+ $ArgLst[0] = &$Render;
+ if (isset($this->_piBeforeShow)) {
+ $Ok = $this->meth_PlugIn_RunAll($this->_piBeforeShow, $ArgLst);
+ }
+ }
+ }
+ if ($Ok!==false) {
+ if ($this->OnShow) {
+ $this->meth_Merge_AutoOn($this->Source, 'onshow', true, true);
+ }
+ $this->meth_Merge_AutoVar($this->Source, true);
+ }
+ if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterShow)) {
+ $this->meth_PlugIn_RunAll($this->_piAfterShow, $ArgLst);
+ }
+ if ($this->_ErrMsgName!=='') {
+ $this->MergeField($this->_ErrMsgName, $this->ErrMsg);
+ }
+ if ($this->meth_Misc_IsMainTpl()) {
+ if (($Render & TBS_OUTPUT)==TBS_OUTPUT) {
+ echo $this->Source;
+ }
+ if (($Render & TBS_EXIT)==TBS_EXIT) {
+ exit;
+ }
+ } elseif ($this->OldSubTpl) {
+ if (($Render & TBS_OUTPUT)==TBS_OUTPUT) {
+ echo $this->Source;
+ }
+ }
+ return $Ok;
+ }
+
+ public function PlugIn($Prm1, $Prm2=0)
+ {
+ if (is_numeric($Prm1)) {
+ switch ($Prm1) {
+ case TBS_INSTALL: // Try to install the plug-in
+ $PlugInId = $Prm2;
+ if (isset($this->_PlugIns[$PlugInId])) {
+ return $this->meth_Misc_Alert('with PlugIn() method', 'plug-in \''.$PlugInId.'\' is already installed.');
+ } else {
+ $ArgLst = func_get_args();
+ array_shift($ArgLst);
+ array_shift($ArgLst);
+ return $this->meth_PlugIn_Install($PlugInId, $ArgLst, false);
+ }
+ // no break
+ case TBS_ISINSTALLED: // Check if the plug-in is installed
+ return isset($this->_PlugIns[$Prm2]);
+ case -4: // Deactivate special plug-ins
+ $this->_PlugIns_Ok_save = $this->_PlugIns_Ok;
+ $this->_PlugIns_Ok = false;
+ return true;
+ case -5: // Deactivate OnFormat
+ $this->_piOnFrm_Ok_save = $this->_piOnFrm_Ok;
+ $this->_piOnFrm_Ok = false;
+ return true;
+ case -10: // Restore
+ if (isset($this->_PlugIns_Ok_save)) {
+ $this->_PlugIns_Ok = $this->_PlugIns_Ok_save;
+ }
+ if (isset($this->_piOnFrm_Ok_save)) {
+ $this->_piOnFrm_Ok = $this->_piOnFrm_Ok_save;
+ }
+ return true;
+ }
+ } elseif (is_string($Prm1)) {
+ // Plug-in's command
+ $p = strpos($Prm1, '.');
+ if ($p===false) {
+ $PlugInId = $Prm1;
+ } else {
+ $PlugInId = substr($Prm1, 0, $p); // direct command
+ }
+ if (!isset($this->_PlugIns[$PlugInId])) {
+ if (!$this->meth_PlugIn_Install($PlugInId, [], true)) {
+ return false;
+ }
+ }
+ 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.');
+ }
+ $ArgLst = func_get_args();
+ if ($p===false) {
+ array_shift($ArgLst);
+ }
+ $Ok = call_user_func_array($this->_piOnCommand[$PlugInId], $ArgLst);
+ if (is_null($Ok)) {
+ $Ok = true;
+ }
+ return $Ok;
+ }
+ return $this->meth_Misc_Alert('with PlugIn() method', '\''.$Prm1.'\' is an invalid plug-in key, the type of the value is \''.gettype($Prm1).'\'.');
+ }
+
+ public function meth_Locator_FindTbs(&$Txt, $Name, $Pos, $ChrSub)
+ {
+ // Find a TBS Locator
+
+ $PosEnd = false;
+ $PosMax = strlen($Txt) -1;
+ $Start = $this->_ChrOpen.$Name;
+
+ do {
+ // Search for the opening char
+ if ($Pos>$PosMax) {
+ return false;
+ }
+ $Pos = strpos($Txt, $Start, $Pos);
+
+ // If found => next chars are analyzed
+ if ($Pos===false) {
+ return false;
+ } else {
+ $Loc = new clsTbsLocator;
+ $ReadPrm = false;
+ $PosX = $Pos + strlen($Start);
+ $x = $Txt[$PosX];
+
+ if ($x===$this->_ChrClose) {
+ $PosEnd = $PosX;
+ } elseif ($x===$ChrSub) {
+ $Loc->SubOk = true; // it is no longer the false value
+ $ReadPrm = true;
+ $PosX++;
+ } elseif (strpos(';', $x)!==false) {
+ $ReadPrm = true;
+ $PosX++;
+ } else {
+ $Pos++;
+ }
+
+ $Loc->PosBeg = $Pos;
+ if ($ReadPrm) {
+ self::f_Loc_PrmRead($Txt, $PosX, false, '\'', $this->_ChrOpen, $this->_ChrClose, $Loc, $PosEnd);
+ if ($PosEnd===false) {
+ $this->meth_Misc_Alert('', 'can\'t found the end of the tag \''.substr($Txt, $Pos, $PosX-$Pos+10).'...\'.');
+ $Pos++;
+ }
+ }
+ }
+ } while ($PosEnd===false);
+
+ $Loc->PosEnd = $PosEnd;
+ if ($Loc->SubOk) {
+ $Loc->FullName = $Name.'.'.$Loc->SubName;
+ $Loc->SubLst = explode('.', $Loc->SubName);
+ $Loc->SubNbr = count($Loc->SubLst);
+ } else {
+ $Loc->FullName = $Name;
+ }
+ if ($ReadPrm && (isset($Loc->PrmLst['enlarge']) || isset($Loc->PrmLst['comm']))) {
+ $Loc->PosBeg0 = $Loc->PosBeg;
+ $Loc->PosEnd0 = $Loc->PosEnd;
+ $enlarge = (isset($Loc->PrmLst['enlarge'])) ? $Loc->PrmLst['enlarge'] : $Loc->PrmLst['comm'];
+ if (($enlarge===true) || ($enlarge==='')) {
+ $Loc->Enlarged = self::f_Loc_EnlargeToStr($Txt, $Loc, '');
+ } else {
+ $Loc->Enlarged = self::f_Loc_EnlargeToTag($Txt, $Loc, $enlarge, false);
+ }
+ }
+
+ return $Loc;
+ }
+
+ public function &meth_Locator_SectionNewBDef(&$LocR, $BlockName, $Txt, $PrmLst, $Cache)
+ {
+ $Chk = true;
+ $LocLst = [];
+ $Pos = 0;
+ $Sort = false;
+
+ if ($this->_PlugIns_Ok && isset($this->_piOnCacheField)) {
+ $pi = true;
+ $ArgLst = [0=>$BlockName, 1=>false, 2=>&$Txt, 3=> ['att'=>true], 4=>&$LocLst, 5=>&$Pos];
+ } else {
+ $pi = false;
+ }
+
+ // Cache TBS locators
+ $Cache = ($Cache && $this->TurboBlock);
+ if ($Cache) {
+ $Chk = false;
+ while ($Loc = $this->meth_Locator_FindTbs($Txt, $BlockName, $Pos, '.')) {
+ $LocNbr = 1 + count($LocLst);
+ $LocLst[$LocNbr] = &$Loc;
+
+ // Next search position : always ("original PosBeg" + 1).
+ // Must be done here because loc can be moved by the plug-in.
+ if ($Loc->Enlarged) {
+ // Enlarged
+ $Pos = $Loc->PosBeg0 + 1;
+ $Loc->Enlarged = false;
+ } else {
+ // Normal
+ $Pos = $Loc->PosBeg + 1;
+ }
+
+ // Note: the plug-in may move, delete and add one or several locs.
+ // Move : backward or forward (will be sorted)
+ // Delete : add property DelMe=true
+ // Add : at the end of $LocLst (will be sorted)
+ if ($pi) {
+ $ArgLst[1] = &$Loc;
+ $this->meth_Plugin_RunAll($this->_piOnCacheField, $ArgLst);
+ }
+
+ if (($Loc->SubName==='#') || ($Loc->SubName==='$')) {
+ $Loc->IsRecInfo = true;
+ $Loc->RecInfo = $Loc->SubName;
+ $Loc->SubName = '';
+ } else {
+ $Loc->IsRecInfo = false;
+ }
+
+ // Process parameter att for new added locators.
+ $NewNbr = count($LocLst);
+ for ($i=$LocNbr;$i<=$NewNbr;$i++) {
+ $li = &$LocLst[$i];
+ if (isset($li->PrmLst['att'])) {
+ $LocSrc = substr($Txt, $li->PosBeg, $li->PosEnd-$li->PosBeg+1); // for error message
+ if ($this->f_Xml_AttFind($Txt, $li, $LocLst, $this->AttDelim)) {
+ if (isset($Loc->PrmLst['atttrue'])) {
+ $li->PrmLst['magnet'] = '#';
+ $li->PrmLst['ope'] = (isset($li->PrmLst['ope'])) ? $li->PrmLst['ope'].',attbool' : 'attbool';
+ }
+ if ($i==$LocNbr) {
+ $Pos = $Loc->DelPos;
+ }
+ } else {
+ $this->meth_Misc_Alert('', 'TBS is not able to merge the field '.$LocSrc.' because the entity targeted by parameter \'att\' cannot be found.');
+ }
+ }
+ }
+
+ unset($Loc);
+ }
+
+ // Re-order loc
+ $e = self::f_Loc_Sort($LocLst, true, 1);
+ $Chk = ($e > 0);
+ }
+
+ // Create the object
+ $o = (object) null;
+ $o->Prm = $PrmLst;
+ $o->LocLst = $LocLst;
+ $o->LocNbr = count($LocLst);
+ $o->Name = $BlockName;
+ $o->Src = $Txt;
+ $o->Chk = $Chk;
+ $o->IsSerial = false;
+ $o->AutoSub = false;
+ $i = 1;
+ while (isset($PrmLst['sub'.$i])) {
+ $o->AutoSub = $i;
+ $i++;
+ }
+
+ $LocR->BDefLst[] = &$o; // Can be usefull for plug-in
+ return $o;
+ }
+
+ public function meth_Locator_SectionAddGrp(&$LocR, $BlockName, &$BDef, $Type, $Field, $Prm)
+ {
+ $BDef->PrevValue = false;
+ $BDef->Type = $Type;
+
+ // Save sub items in a structure near to Locator.
+ $Field0 = $Field;
+ if (strpos($Field, $this->_ChrOpen)===false) {
+ $Field = $this->_ChrOpen.$BlockName.'.'.$Field.';tbstype='.$Prm.$this->_ChrClose;
+ } // tbstype is an internal parameter for catching errors
+ $BDef->FDef = &$this->meth_Locator_SectionNewBDef($LocR, $BlockName, $Field, [], true);
+ if ($BDef->FDef->LocNbr==0) {
+ $this->meth_Misc_Alert('Parameter '.$Prm, 'The value \''.$Field0.'\' is unvalide for this parameter.');
+ }
+
+ if ($Type==='H') {
+ if ($LocR->HeaderFound===false) {
+ $LocR->HeaderFound = true;
+ $LocR->HeaderNbr = 0;
+ $LocR->HeaderDef = []; // 1 to HeaderNbr
+ }
+ $i = ++$LocR->HeaderNbr;
+ $LocR->HeaderDef[$i] = &$BDef;
+ } else {
+ if ($LocR->FooterFound===false) {
+ $LocR->FooterFound = true;
+ $LocR->FooterNbr = 0;
+ $LocR->FooterDef = []; // 1 to FooterNbr
+ }
+ $BDef->AddLastGrp = ($Type==='F');
+ $i = ++$LocR->FooterNbr;
+ $LocR->FooterDef[$i] = &$BDef;
+ }
+ }
+
+ public function meth_Locator_Replace(&$Txt, &$Loc, &$Value, $SubStart)
+ {
+ // This function enables to merge a locator with a text and returns the position just after the replaced block
+ // This position can be useful because we don't know in advance how $Value will be replaced.
+
+ // Found the value if there is a subname
+ if (($SubStart!==false) && $Loc->SubOk) {
+ for ($i=$SubStart;$i<$Loc->SubNbr;$i++) {
+ $x = $Loc->SubLst[$i]; // &$Loc... brings an error with Event Example, I don't know why.
+ if (is_array($Value)) {
+ if (isset($Value[$x])) {
+ $Value = &$Value[$x];
+ } elseif (array_key_exists($x, $Value)) {// can happens when value is NULL
+ $Value = &$Value[$x];
+ } else {
+ if (!isset($Loc->PrmLst['noerr'])) {
+ $this->meth_Misc_Alert($Loc, 'item \''.$x.'\' is not an existing key in the array.', true);
+ }
+ unset($Value);
+ $Value = '';
+ break;
+ }
+ } elseif (is_object($Value)) {
+ $ArgLst = $this->f_Misc_CheckArgLst($x);
+ if (method_exists($Value, $x)) {
+ if ($this->MethodsAllowed || !in_array(strtok($Loc->FullName, '.'), ['onload','onshow','var'])) {
+ $x = call_user_func_array([&$Value,$x], $ArgLst);
+ } else {
+ if (!isset($Loc->PrmLst['noerr'])) {
+ $this->meth_Misc_Alert($Loc, '\''.$x.'\' is a method and the current TBS settings do not allow to call methods on automatic fields.', true);
+ }
+ $x = '';
+ }
+ } elseif (property_exists($Value, $x)) {
+ $x = &$Value->$x;
+ } elseif (isset($Value->$x)) {
+ $x = $Value->$x; // useful for overloaded property
+ } else {
+ if (!isset($Loc->PrmLst['noerr'])) {
+ $this->meth_Misc_Alert($Loc, 'item '.$x.'\' is neither a method nor a property in the class \''.get_class($Value).'\'.', true);
+ }
+ unset($Value);
+ $Value = '';
+ break;
+ }
+ $Value = &$x;
+ unset($x);
+ $x = '';
+ } else {
+ 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);
+ }
+ unset($Value);
+ $Value = '';
+ break;
+ }
+ }
+ }
+
+ $CurrVal = $Value; // Unlink
+
+ if (isset($Loc->PrmLst['onformat'])) {
+ if ($Loc->FirstMerge) {
+ $Loc->OnFrmInfo = $Loc->PrmLst['onformat'];
+ $Loc->OnFrmArg = [$Loc->FullName,'',&$Loc->PrmLst,&$this];
+ $ErrMsg = false;
+ if (!$this->meth_Misc_UserFctCheck($Loc->OnFrmInfo, 'f', $ErrMsg, $ErrMsg, true)) {
+ unset($Loc->PrmLst['onformat']);
+ if (!isset($Loc->PrmLst['noerr'])) {
+ $this->meth_Misc_Alert($Loc, '(parameter onformat) '.$ErrMsg);
+ }
+ $Loc->OnFrmInfo = 'pi'; // Execute the function pi() just to avoid extra error messages
+ }
+ } else {
+ $Loc->OnFrmArg[3] = &$this; // bugs.php.net/51174
+ }
+ $Loc->OnFrmArg[1] = &$CurrVal;
+ if (isset($Loc->PrmLst['subtpl'])) {
+ $this->meth_Misc_ChangeMode(true, $Loc, $CurrVal);
+ call_user_func_array($Loc->OnFrmInfo, $Loc->OnFrmArg);
+ $this->meth_Misc_ChangeMode(false, $Loc, $CurrVal);
+ $Loc->ConvProtect = false;
+ $Loc->ConvStr = false;
+ } else {
+ call_user_func_array($Loc->OnFrmInfo, $Loc->OnFrmArg);
+ }
+ }
+
+ if ($Loc->FirstMerge) {
+ if (isset($Loc->PrmLst['frm'])) {
+ $Loc->ConvMode = 0; // Frm
+ $Loc->ConvProtect = false;
+ } else {
+ // Analyze parameter 'strconv'
+ if (isset($Loc->PrmLst['strconv'])) {
+ $this->meth_Conv_Prepare($Loc, $Loc->PrmLst['strconv']);
+ } elseif (isset($Loc->PrmLst['htmlconv'])) { // compatibility
+ $this->meth_Conv_Prepare($Loc, $Loc->PrmLst['htmlconv']);
+ } else {
+ if ($this->Charset===false) {
+ $Loc->ConvStr = false;
+ } // No conversion
+ }
+ // Analyze parameter 'protect'
+ if (isset($Loc->PrmLst['protect'])) {
+ $x = strtolower($Loc->PrmLst['protect']);
+ if ($x==='no') {
+ $Loc->ConvProtect = false;
+ } elseif ($x==='yes') {
+ $Loc->ConvProtect = true;
+ }
+ } elseif ($this->Protect===false) {
+ $Loc->ConvProtect = false;
+ }
+ }
+ if ($Loc->Ope = isset($Loc->PrmLst['ope'])) {
+ $OpeLst = explode(',', $Loc->PrmLst['ope']);
+ $Loc->OpeAct = [];
+ $Loc->OpeArg = [];
+ $Loc->OpeUtf8 = false;
+ foreach ($OpeLst as $i=>$ope) {
+ if ($ope==='list') {
+ $Loc->OpeAct[$i] = 1;
+ $Loc->OpePrm[$i] = (isset($Loc->PrmLst['valsep'])) ? $Loc->PrmLst['valsep'] : ',';
+ if (($Loc->ConvMode===1) && $Loc->ConvStr) {
+ $Loc->ConvMode = -1;
+ } // special mode for item list conversion
+ } elseif ($ope==='minv') {
+ $Loc->OpeAct[$i] = 11;
+ $Loc->MSave = $Loc->MagnetId;
+ } elseif ($ope==='attbool') { // this operation key is set when a loc is cached with paremeter atttrue
+ $Loc->OpeAct[$i] = 14;
+ } elseif ($ope==='utf8') {
+ $Loc->OpeUtf8 = true;
+ } elseif ($ope==='upper') {
+ $Loc->OpeAct[$i] = 15;
+ } elseif ($ope==='lower') {
+ $Loc->OpeAct[$i] = 16;
+ } elseif ($ope==='upper1') {
+ $Loc->OpeAct[$i] = 17;
+ } elseif ($ope==='upperw') {
+ $Loc->OpeAct[$i] = 18;
+ } else {
+ $x = substr($ope, 0, 4);
+ if ($x==='max:') {
+ $Loc->OpeAct[$i] = (isset($Loc->PrmLst['maxhtml'])) ? 2 : 3;
+ if (isset($Loc->PrmLst['maxutf8'])) {
+ $Loc->OpeUtf8 = true;
+ }
+ $Loc->OpePrm[$i] = intval(trim(substr($ope, 4)));
+ $Loc->OpeEnd = (isset($Loc->PrmLst['maxend'])) ? $Loc->PrmLst['maxend'] : '...';
+ if ($Loc->OpePrm[$i]<=0) {
+ $Loc->Ope = false;
+ }
+ } elseif ($x==='mod:') {
+ $Loc->OpeAct[$i] = 5;
+ $Loc->OpePrm[$i] = '0'+trim(substr($ope, 4));
+ } elseif ($x==='add:') {
+ $Loc->OpeAct[$i] = 6;
+ $Loc->OpePrm[$i] = '0'+trim(substr($ope, 4));
+ } elseif ($x==='mul:') {
+ $Loc->OpeAct[$i] = 7;
+ $Loc->OpePrm[$i] = '0'+trim(substr($ope, 4));
+ } elseif ($x==='div:') {
+ $Loc->OpeAct[$i] = 8;
+ $Loc->OpePrm[$i] = '0'+trim(substr($ope, 4));
+ } elseif ($x==='mok:') {
+ $Loc->OpeAct[$i] = 9;
+ $Loc->OpeMOK[] = trim(substr($ope, 4));
+ $Loc->MSave = $Loc->MagnetId;
+ } elseif ($x==='mko:') {
+ $Loc->OpeAct[$i] =10;
+ $Loc->OpeMKO[] = trim(substr($ope, 4));
+ $Loc->MSave = $Loc->MagnetId;
+ } elseif ($x==='nif:') {
+ $Loc->OpeAct[$i] =12;
+ $Loc->OpePrm[$i] = trim(substr($ope, 4));
+ } elseif ($x==='msk:') {
+ $Loc->OpeAct[$i] =13;
+ $Loc->OpePrm[$i] = trim(substr($ope, 4));
+ } elseif (isset($this->_piOnOperation)) {
+ $Loc->OpeAct[$i] = 0;
+ $Loc->OpePrm[$i] = $ope;
+ $Loc->OpeArg[$i] = [$Loc->FullName,&$CurrVal,&$Loc->PrmLst,&$Txt,$Loc->PosBeg,$Loc->PosEnd,&$Loc];
+ $Loc->PrmLst['_ope'] = $Loc->PrmLst['ope'];
+ } elseif (!isset($Loc->PrmLst['noerr'])) {
+ $this->meth_Misc_Alert($Loc, 'parameter ope doesn\'t support value \''.$ope.'\'.', true);
+ }
+ }
+ }
+ }
+ $Loc->FirstMerge = false;
+ }
+ $ConvProtect = $Loc->ConvProtect;
+
+ // Plug-in OnFormat
+ if ($this->_piOnFrm_Ok) {
+ if (isset($Loc->OnFrmArgPi)) {
+ $Loc->OnFrmArgPi[1] = &$CurrVal;
+ $Loc->OnFrmArgPi[3] = &$this; // bugs.php.net/51174
+ } else {
+ $Loc->OnFrmArgPi = [$Loc->FullName,&$CurrVal,&$Loc->PrmLst,&$this];
+ }
+ $this->meth_PlugIn_RunAll($this->_piOnFormat, $Loc->OnFrmArgPi);
+ }
+
+ // Operation
+ if ($Loc->Ope) {
+ foreach ($Loc->OpeAct as $i=>$ope) {
+ switch ($ope) {
+ case 0:
+ $Loc->PrmLst['ope'] = $Loc->OpePrm[$i]; // for compatibility
+ $OpeArg = &$Loc->OpeArg[$i];
+ $OpeArg[1] = &$CurrVal; $OpeArg[3] = &$Txt;
+ if (!$this->meth_PlugIn_RunAll($this->_piOnOperation, $OpeArg)) {
+ return $Loc->PosBeg;
+ }
+ break;
+ case 1:
+ if ($Loc->ConvMode===-1) {
+ if (is_array($CurrVal)) {
+ foreach ($CurrVal as $k=>$v) {
+ $v = $this->meth_Misc_ToStr($v);
+ $this->meth_Conv_Str($v, $Loc->ConvBr);
+ $CurrVal[$k] = $v;
+ }
+ $CurrVal = implode($Loc->OpePrm[$i], $CurrVal);
+ } else {
+ $CurrVal = $this->meth_Misc_ToStr($CurrVal);
+ $this->meth_Conv_Str($CurrVal, $Loc->ConvBr);
+ }
+ } else {
+ if (is_array($CurrVal)) {
+ $CurrVal = implode($Loc->OpePrm[$i], $CurrVal);
+ }
+ }
+ break;
+ case 2:
+ $x = $this->meth_Misc_ToStr($CurrVal);
+ if (strlen($x)>$Loc->OpePrm[$i]) {
+ $this->f_Xml_Max($x, $Loc->OpePrm[$i], $Loc->OpeEnd);
+ }
+ break;
+ case 3:
+ $x = $this->meth_Misc_ToStr($CurrVal);
+ if (strlen($x)>$Loc->OpePrm[$i]) {
+ if ($Loc->OpeUtf8) {
+ $CurrVal = mb_substr($x, 0, $Loc->OpePrm[$i], 'UTF-8').$Loc->OpeEnd;
+ } else {
+ $CurrVal = substr($x, 0, $Loc->OpePrm[$i]).$Loc->OpeEnd;
+ }
+ }
+ break;
+ case 5: $CurrVal = ('0'+$CurrVal) % $Loc->OpePrm[$i]; break;
+ case 6: $CurrVal = ('0'+$CurrVal) + $Loc->OpePrm[$i]; break;
+ case 7: $CurrVal = ('0'+$CurrVal) * $Loc->OpePrm[$i]; break;
+ case 8: $CurrVal = ('0'+$CurrVal) / $Loc->OpePrm[$i]; break;
+ case 9: case 10:
+ if ($ope===9) {
+ $CurrVal = (in_array($this->meth_Misc_ToStr($CurrVal), $Loc->OpeMOK)) ? ' ' : '';
+ } else {
+ $CurrVal = (in_array($this->meth_Misc_ToStr($CurrVal), $Loc->OpeMKO)) ? '' : ' ';
+ }
+ // no break here
+ case 11:
+ if ($this->meth_Misc_ToStr($CurrVal)==='') {
+ if ($Loc->MagnetId===0) {
+ $Loc->MagnetId = $Loc->MSave;
+ }
+ } else {
+ if ($Loc->MagnetId!==0) {
+ $Loc->MSave = $Loc->MagnetId;
+ $Loc->MagnetId = 0;
+ }
+ $CurrVal = '';
+ }
+ break;
+ case 12: if ($this->meth_Misc_ToStr($CurrVal)===$Loc->OpePrm[$i]) {
+ $CurrVal = '';
+ } break;
+ case 13: $CurrVal = str_replace('*', $CurrVal, $Loc->OpePrm[$i]); break;
+ case 14: $CurrVal = self::f_Loc_AttBoolean($CurrVal, $Loc->PrmLst['atttrue'], $Loc->AttName); break;
+ case 15: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_UPPER, 'UTF-8') : strtoupper($CurrVal); break;
+ case 16: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_LOWER, 'UTF-8') : strtolower($CurrVal); break;
+ case 17: $CurrVal = ucfirst($CurrVal); break;
+ case 18: $CurrVal = ($Loc->OpeUtf8) ? mb_convert_case($CurrVal, MB_CASE_TITLE, 'UTF-8') : ucwords(strtolower($CurrVal)); break;
+ }
+ }
+ }
+
+ // String conversion or format
+ if ($Loc->ConvMode===1) { // Usual string conversion
+ $CurrVal = $this->meth_Misc_ToStr($CurrVal);
+ if ($Loc->ConvStr) {
+ $this->meth_Conv_Str($CurrVal, $Loc->ConvBr);
+ }
+ } elseif ($Loc->ConvMode===0) { // Format
+ $CurrVal = $this->meth_Misc_Format($CurrVal, $Loc->PrmLst);
+ } elseif ($Loc->ConvMode===2) { // Special string conversion
+ $CurrVal = $this->meth_Misc_ToStr($CurrVal);
+ if ($Loc->ConvStr) {
+ $this->meth_Conv_Str($CurrVal, $Loc->ConvBr);
+ }
+ if ($Loc->ConvEsc) {
+ $CurrVal = str_replace('\'', '\'\'', $CurrVal);
+ }
+ if ($Loc->ConvWS) {
+ $check = ' ';
+ $nbsp = ' ';
+ do {
+ $pos = strpos($CurrVal, $check);
+ if ($pos!==false) {
+ $CurrVal = substr_replace($CurrVal, $nbsp, $pos, 1);
+ }
+ } while ($pos!==false);
+ }
+ if ($Loc->ConvJS) {
+ $CurrVal = addslashes($CurrVal); // apply to ('), ("), (\) and (null)
+ $CurrVal = str_replace(["\n","\r","\t"], ['\n','\r','\t'], $CurrVal);
+ }
+ if ($Loc->ConvUrl) {
+ $CurrVal = urlencode($CurrVal);
+ }
+ if ($Loc->ConvUtf8) {
+ $CurrVal = utf8_encode($CurrVal);
+ }
+ }
+
+ // if/then/else process, there may be several if/then
+ if ($Loc->PrmIfNbr) {
+ $z = false;
+ $i = 1;
+ while ($i!==false) {
+ if ($Loc->PrmIfVar[$i]) {
+ $Loc->PrmIfVar[$i] = $this->meth_Merge_AutoVar($Loc->PrmIf[$i], true);
+ }
+ $x = str_replace($this->_ChrVal, $CurrVal, $Loc->PrmIf[$i]);
+ if ($this->f_Misc_CheckCondition($x)) {
+ if (isset($Loc->PrmThen[$i])) {
+ if ($Loc->PrmThenVar[$i]) {
+ $Loc->PrmThenVar[$i] = $this->meth_Merge_AutoVar($Loc->PrmThen[$i], true);
+ }
+ $z = $Loc->PrmThen[$i];
+ }
+ $i = false;
+ } else {
+ $i++;
+ if ($i>$Loc->PrmIfNbr) {
+ if (isset($Loc->PrmLst['else'])) {
+ if ($Loc->PrmElseVar) {
+ $Loc->PrmElseVar = $this->meth_Merge_AutoVar($Loc->PrmLst['else'], true);
+ }
+ $z =$Loc->PrmLst['else'];
+ }
+ $i = false;
+ }
+ }
+ }
+ if ($z!==false) {
+ if ($ConvProtect) {
+ $CurrVal = str_replace($this->_ChrOpen, $this->_ChrProtect, $CurrVal); // TBS protection
+ $ConvProtect = false;
+ }
+ $CurrVal = str_replace($this->_ChrVal, $CurrVal, $z);
+ }
+ }
+
+ if (isset($Loc->PrmLst['file'])) {
+ $x = $Loc->PrmLst['file'];
+ if ($x===true) {
+ $x = $CurrVal;
+ }
+ $this->meth_Merge_AutoVar($x, false);
+ $x = trim(str_replace($this->_ChrVal, $CurrVal, $x));
+ $CurrVal = '';
+ if ($x!=='') {
+ if ($this->f_Misc_GetFile($CurrVal, $x, $this->_LastFile, $this->IncludePath)) {
+ $this->meth_Locator_PartAndRename($CurrVal, $Loc->PrmLst);
+ } else {
+ if (!isset($Loc->PrmLst['noerr'])) {
+ $this->meth_Misc_Alert($Loc, 'the file \''.$x.'\' given by parameter file is not found or not readable.', true);
+ }
+ }
+ $ConvProtect = false;
+ }
+ }
+
+ if (isset($Loc->PrmLst['script'])) {// Include external PHP script
+ $x = $Loc->PrmLst['script'];
+ if ($x===true) {
+ $x = $CurrVal;
+ }
+ $this->meth_Merge_AutoVar($x, false);
+ $x = trim(str_replace($this->_ChrVal, $CurrVal, $x));
+ if ($x!=='') {
+ $this->_Subscript = $x;
+ $this->CurrPrm = &$Loc->PrmLst;
+ $sub = isset($Loc->PrmLst['subtpl']);
+ if ($sub) {
+ $this->meth_Misc_ChangeMode(true, $Loc, $CurrVal);
+ }
+ if ($this->meth_Misc_RunSubscript($CurrVal, $Loc->PrmLst)===false) {
+ if (!isset($Loc->PrmLst['noerr'])) {
+ $this->meth_Misc_Alert($Loc, 'the file \''.$x.'\' given by parameter script is not found or not readable.', true);
+ }
+ }
+ if ($sub) {
+ $this->meth_Misc_ChangeMode(false, $Loc, $CurrVal);
+ }
+ $this->meth_Locator_PartAndRename($CurrVal, $Loc->PrmLst);
+ unset($this->CurrPrm);
+ $ConvProtect = false;
+ }
+ }
+
+ if (isset($Loc->PrmLst['att'])) {
+ $this->f_Xml_AttFind($Txt, $Loc, true, $this->AttDelim);
+ if (isset($Loc->PrmLst['atttrue'])) {
+ $CurrVal = self::f_Loc_AttBoolean($CurrVal, $Loc->PrmLst['atttrue'], $Loc->AttName);
+ $Loc->PrmLst['magnet'] = '#';
+ }
+ }
+
+ // Case when it's an empty string
+ if ($CurrVal==='') {
+ if ($Loc->MagnetId===false) {
+ if (isset($Loc->PrmLst['.'])) {
+ $Loc->MagnetId = -1;
+ } elseif (isset($Loc->PrmLst['ifempty'])) {
+ $Loc->MagnetId = -2;
+ } elseif (isset($Loc->PrmLst['magnet'])) {
+ $Loc->MagnetId = 1;
+ $Loc->PosBeg0 = $Loc->PosBeg;
+ $Loc->PosEnd0 = $Loc->PosEnd;
+ if ($Loc->PrmLst['magnet']==='#') {
+ if (!isset($Loc->AttBeg)) {
+ $Loc->PrmLst['att'] = '.';
+ $this->f_Xml_AttFind($Txt, $Loc, true, $this->AttDelim);
+ }
+ if (isset($Loc->AttBeg)) {
+ $Loc->MagnetId = -3;
+ } else {
+ $this->meth_Misc_Alert($Loc, 'parameter \'magnet=#\' cannot be processed because the corresponding attribute is not found.', true);
+ }
+ } elseif (isset($Loc->PrmLst['mtype'])) {
+ switch ($Loc->PrmLst['mtype']) {
+ case 'm+m': $Loc->MagnetId = 2; break;
+ case 'm*': $Loc->MagnetId = 3; break;
+ case '*m': $Loc->MagnetId = 4; break;
+ }
+ }
+ } elseif (isset($Loc->PrmLst['attadd'])) {
+ // In order to delete extra space
+ $Loc->PosBeg0 = $Loc->PosBeg;
+ $Loc->PosEnd0 = $Loc->PosEnd;
+ $Loc->MagnetId = 5;
+ } else {
+ $Loc->MagnetId = 0;
+ }
+ }
+
+ switch ($Loc->MagnetId) {
+ case 0: break;
+ case -1: $CurrVal = ' '; break; // Enables to avoid null cells in HTML tables
+ case -2: $CurrVal = $Loc->PrmLst['ifempty']; break;
+ case -3: $Loc->Enlarged = true; $Loc->PosBeg = $Loc->AttBegM; $Loc->PosEnd = $Loc->AttEnd; break;
+ case 1:
+ $Loc->Enlarged = true;
+ $this->f_Loc_EnlargeToTag($Txt, $Loc, $Loc->PrmLst['magnet'], false);
+ break;
+ case 2:
+ $Loc->Enlarged = true;
+ $CurrVal = $this->f_Loc_EnlargeToTag($Txt, $Loc, $Loc->PrmLst['magnet'], true);
+ break;
+ case 3:
+ $Loc->Enlarged = true;
+ $Loc2 = $this->f_Xml_FindTag($Txt, $Loc->PrmLst['magnet'], true, $Loc->PosBeg, false, false, false);
+ if ($Loc2!==false) {
+ $Loc->PosBeg = $Loc2->PosBeg;
+ if ($Loc->PosEnd<$Loc2->PosEnd) {
+ $Loc->PosEnd = $Loc2->PosEnd;
+ }
+ }
+ break;
+ case 4:
+ $Loc->Enlarged = true;
+ $Loc2 = $this->f_Xml_FindTag($Txt, $Loc->PrmLst['magnet'], true, $Loc->PosBeg, true, false, false);
+ if ($Loc2!==false) {
+ $Loc->PosEnd = $Loc2->PosEnd;
+ }
+ break;
+ case 5:
+ $Loc->Enlarged = true;
+ if (substr($Txt, $Loc->PosBeg-1, 1)==' ') {
+ $Loc->PosBeg--;
+ }
+ break;
+ }
+ $NewEnd = $Loc->PosBeg; // Useful when mtype='m+m'
+ } else {
+ if ($ConvProtect) {
+ $CurrVal = str_replace($this->_ChrOpen, $this->_ChrProtect, $CurrVal);
+ } // TBS protection
+ $NewEnd = $Loc->PosBeg + strlen($CurrVal);
+ }
+
+ $Txt = substr_replace($Txt, $CurrVal, $Loc->PosBeg, $Loc->PosEnd-$Loc->PosBeg+1);
+ return $NewEnd; // Return the new end position of the field
+ }
+
+ public function meth_Locator_FindBlockNext(&$Txt, $BlockName, $PosBeg, $ChrSub, $Mode, &$P1, &$FieldBefore)
+ {
+ // Return the first block locator just after the PosBeg position
+ // Mode = 1 : Merge_Auto => doesn't save $Loc->BlockSrc, save the bounds of TBS Def tags instead, return also fields
+ // Mode = 2 : FindBlockLst or GetBlockSource => save $Loc->BlockSrc without TBS Def tags
+ // Mode = 3 : GetBlockSource => save $Loc->BlockSrc with TBS Def tags
+
+ $SearchDef = true;
+ $FirstField = false;
+ // Search for the first tag with parameter "block"
+ while ($SearchDef && ($Loc = $this->meth_Locator_FindTbs($Txt, $BlockName, $PosBeg, $ChrSub))) {
+ if (isset($Loc->PrmLst['block'])) {
+ if (isset($Loc->PrmLst['p1'])) {
+ if ($P1) {
+ return false;
+ }
+ $P1 = true;
+ }
+ $Block = $Loc->PrmLst['block'];
+ $SearchDef = false;
+ } elseif ($Mode===1) {
+ return $Loc;
+ } elseif ($FirstField===false) {
+ $FirstField = $Loc;
+ }
+ $PosBeg = $Loc->PosEnd;
+ }
+
+ if ($SearchDef) {
+ if ($FirstField!==false) {
+ $FieldBefore = true;
+ }
+ return false;
+ }
+
+ $Loc->PosDefBeg = -1;
+
+ if ($Block==='begin') { // Block definied using begin/end
+
+ if (($FirstField!==false) && ($FirstField->PosEnd<$Loc->PosBeg)) {
+ $FieldBefore = true;
+ }
+
+ $Opened = 1;
+ while ($Loc2 = $this->meth_Locator_FindTbs($Txt, $BlockName, $PosBeg, $ChrSub)) {
+ if (isset($Loc2->PrmLst['block'])) {
+ switch ($Loc2->PrmLst['block']) {
+ case 'end': $Opened--; break;
+ case 'begin': $Opened++; break;
+ }
+ if ($Opened==0) {
+ if ($Mode===1) {
+ $Loc->PosBeg2 = $Loc2->PosBeg;
+ $Loc->PosEnd2 = $Loc2->PosEnd;
+ } else {
+ if ($Mode===2) {
+ $Loc->BlockSrc = substr($Txt, $Loc->PosEnd+1, $Loc2->PosBeg-$Loc->PosEnd-1);
+ } else {
+ $Loc->BlockSrc = substr($Txt, $Loc->PosBeg, $Loc2->PosEnd-$Loc->PosBeg+1);
+ }
+ $Loc->PosEnd = $Loc2->PosEnd;
+ }
+ $Loc->BlockFound = true;
+ return $Loc;
+ }
+ }
+ $PosBeg = $Loc2->PosEnd;
+ }
+
+ return $this->meth_Misc_Alert($Loc, 'a least one tag with parameter \'block=end\' is missing.', false, 'in block\'s definition');
+ }
+
+ if ($Mode===1) {
+ $Loc->PosBeg2 = false;
+ } else {
+ $beg = $Loc->PosBeg;
+ $end = $Loc->PosEnd;
+ 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');
+ }
+ if ($Loc->SubOk || ($Mode===3)) {
+ $Loc->BlockSrc = substr($Txt, $Loc->PosBeg, $Loc->PosEnd-$Loc->PosBeg+1);
+ $Loc->PosDefBeg = $beg - $Loc->PosBeg;
+ $Loc->PosDefEnd = $end - $Loc->PosBeg;
+ } else {
+ $Loc->BlockSrc = substr($Txt, $Loc->PosBeg, $beg-$Loc->PosBeg).substr($Txt, $end+1, $Loc->PosEnd-$end);
+ }
+ }
+
+ $Loc->BlockFound = true;
+ if (($FirstField!==false) && ($FirstField->PosEnd<$Loc->PosBeg)) {
+ $FieldBefore = true;
+ }
+ return $Loc; // methods return by ref by default
+ }
+
+ public function meth_Locator_PartAndRename(&$CurrVal, &$PrmLst)
+ {
+ // Store part
+ if (isset($PrmLst['store'])) {
+ $storename = (isset($PrmLst['storename'])) ? $PrmLst['storename'] : 'default';
+ if (!isset($this->TplStore[$storename])) {
+ $this->TplStore[$storename] = '';
+ }
+ $this->TplStore[$storename] .= $this->f_Xml_GetPart($CurrVal, $PrmLst['store'], false);
+ }
+
+ // Get part
+ if (isset($PrmLst['getpart'])) {
+ $part = $PrmLst['getpart'];
+ } elseif (isset($PrmLst['getbody'])) {
+ $part = $PrmLst['getbody'];
+ } else {
+ $part = false;
+ }
+ if ($part!=false) {
+ $CurrVal = $this->f_Xml_GetPart($CurrVal, $part, true);
+ }
+
+ // Rename or delete TBS tags names
+ if (isset($PrmLst['rename'])) {
+ $Replace = $PrmLst['rename'];
+
+ if (is_string($Replace)) {
+ $Replace = explode(',', $Replace);
+ }
+ foreach ($Replace as $x) {
+ if (is_string($x)) {
+ $x = explode('=', $x);
+ }
+ if (count($x)==2) {
+ $old = trim($x[0]);
+ $new = trim($x[1]);
+ if ($old!=='') {
+ if ($new==='') {
+ $q = false;
+ $s = 'clear';
+ $this->meth_Merge_Block($CurrVal, $old, $s, $q, false, false, false);
+ } else {
+ $old = $this->_ChrOpen.$old;
+ $old = [$old.'.', $old.' ', $old.';', $old.$this->_ChrClose];
+ $new = $this->_ChrOpen.$new;
+ $new = [$new.'.', $new.' ', $new.';', $new.$this->_ChrClose];
+ $CurrVal = str_replace($old, $new, $CurrVal);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public function meth_Locator_FindBlockLst(&$Txt, $BlockName, $Pos, $SpePrm)
+ {
+ // Return a locator object covering all block definitions, even if there is no block definition found.
+
+ $LocR = new clsTbsLocator;
+ $LocR->P1 = false;
+ $LocR->FieldOutside = false;
+ $LocR->FOStop = false;
+ $LocR->BDefLst = [];
+
+ $LocR->NoData = false;
+ $LocR->Special = false;
+ $LocR->HeaderFound = false;
+ $LocR->FooterFound = false;
+ $LocR->SerialEmpty = false;
+ $LocR->GrpBreak = false; // Only for plug-ins
+
+ $LocR->WhenFound = false;
+ $LocR->WhenDefault = false;
+
+ $LocR->SectionNbr = 0; // Normal sections
+ $LocR->SectionLst = []; // 1 to SectionNbr
+
+ $BDef = false;
+ $ParentLst = [];
+ $Pid = 0;
+
+ do {
+ if ($BlockName==='') {
+ $Loc = false;
+ } else {
+ $Loc = $this->meth_Locator_FindBlockNext($Txt, $BlockName, $Pos, '.', 2, $LocR->P1, $LocR->FieldOutside);
+ }
+
+ if ($Loc===false) {
+ if ($Pid>0) { // parentgrp mode => disconnect $Txt from the source
+ $Parent = &$ParentLst[$Pid];
+ $Src = $Txt;
+ $Txt = &$Parent->Txt;
+ if ($LocR->BlockFound) {
+ // Redefine the Header block
+ $Parent->Src = substr($Src, 0, $LocR->PosBeg);
+ // Add a Footer block
+ $BDef = &$this->meth_Locator_SectionNewBDef($LocR, $BlockName, substr($Src, $LocR->PosEnd+1), $Parent->Prm, true);
+ $this->meth_Locator_SectionAddGrp($LocR, $BlockName, $BDef, 'F', $Parent->Fld, 'parentgrp');
+ }
+ // Now go down to previous level
+ $Pos = $Parent->Pos;
+ $LocR->PosBeg = $Parent->Beg;
+ $LocR->PosEnd = $Parent->End;
+ $LocR->BlockFound = true;
+ unset($Parent);
+ unset($ParentLst[$Pid]);
+ $Pid--;
+ $Loc = true;
+ }
+ } else {
+ $Pos = $Loc->PosEnd;
+
+ // Define the block limits
+ if ($LocR->BlockFound) {
+ if ($LocR->PosBeg > $Loc->PosBeg) {
+ $LocR->PosBeg = $Loc->PosBeg;
+ }
+ if ($LocR->PosEnd < $Loc->PosEnd) {
+ $LocR->PosEnd = $Loc->PosEnd;
+ }
+ } else {
+ $LocR->BlockFound = true;
+ $LocR->PosBeg = $Loc->PosBeg;
+ $LocR->PosEnd = $Loc->PosEnd;
+ }
+
+ // Merge block parameters
+ if (count($Loc->PrmLst)>0) {
+ $LocR->PrmLst = array_merge($LocR->PrmLst, $Loc->PrmLst);
+ }
+
+ // Force dynamic parameter to be cachable
+ if ($Loc->PosDefBeg>=0) {
+ $dynprm = ['when','headergrp','footergrp','parentgrp'];
+ foreach ($dynprm as $dp) {
+ $n = 0;
+ if ((isset($Loc->PrmLst[$dp])) && (strpos($Loc->PrmLst[$dp], $this->_ChrOpen.$BlockName)!==false)) {
+ $n++;
+ if ($n==1) {
+ $len = $Loc->PosDefEnd - $Loc->PosDefBeg + 1;
+ $x = substr($Loc->BlockSrc, $Loc->PosDefBeg, $len);
+ }
+ $x = str_replace($Loc->PrmLst[$dp], '', $x);
+ }
+ if ($n>0) {
+ $Loc->BlockSrc = substr_replace($Loc->BlockSrc, $x, $Loc->PosDefBeg, $len);
+ }
+ }
+ }
+ // Save the block and cache its tags
+ $IsParentGrp = isset($Loc->PrmLst['parentgrp']);
+ $BDef = &$this->meth_Locator_SectionNewBDef($LocR, $BlockName, $Loc->BlockSrc, $Loc->PrmLst, !$IsParentGrp);
+
+ // Add the text in the list of blocks
+ if (isset($Loc->PrmLst['nodata'])) { // Nodata section
+ $LocR->NoData = &$BDef;
+ } elseif (($SpePrm!==false) && isset($Loc->PrmLst[$SpePrm])) { // Special section (used for navigation bar)
+ $LocR->Special = &$BDef;
+ } elseif (isset($Loc->PrmLst['when'])) {
+ if ($LocR->WhenFound===false) {
+ $LocR->WhenFound = true;
+ $LocR->WhenSeveral = false;
+ $LocR->WhenNbr = 0;
+ $LocR->WhenLst = [];
+ }
+ $this->meth_Merge_AutoVar($Loc->PrmLst['when'], false);
+ $BDef->WhenCond = &$this->meth_Locator_SectionNewBDef($LocR, $BlockName, $Loc->PrmLst['when'], [], true);
+ $BDef->WhenBeforeNS = ($LocR->SectionNbr===0);
+ $i = ++$LocR->WhenNbr;
+ $LocR->WhenLst[$i] = &$BDef;
+ if (isset($Loc->PrmLst['several'])) {
+ $LocR->WhenSeveral = true;
+ }
+ } elseif (isset($Loc->PrmLst['default'])) {
+ $LocR->WhenDefault = &$BDef;
+ $LocR->WhenDefaultBeforeNS = ($LocR->SectionNbr===0);
+ } elseif (isset($Loc->PrmLst['headergrp'])) {
+ $this->meth_Locator_SectionAddGrp($LocR, $BlockName, $BDef, 'H', $Loc->PrmLst['headergrp'], 'headergrp');
+ } elseif (isset($Loc->PrmLst['footergrp'])) {
+ $this->meth_Locator_SectionAddGrp($LocR, $BlockName, $BDef, 'F', $Loc->PrmLst['footergrp'], 'footergrp');
+ } elseif (isset($Loc->PrmLst['splittergrp'])) {
+ $this->meth_Locator_SectionAddGrp($LocR, $BlockName, $BDef, 'S', $Loc->PrmLst['splittergrp'], 'splittergrp');
+ } elseif ($IsParentGrp) {
+ $this->meth_Locator_SectionAddGrp($LocR, $BlockName, $BDef, 'H', $Loc->PrmLst['parentgrp'], 'parentgrp');
+ $BDef->Fld = $Loc->PrmLst['parentgrp'];
+ $BDef->Txt = &$Txt;
+ $BDef->Pos = $Pos;
+ $BDef->Beg = $LocR->PosBeg;
+ $BDef->End = $LocR->PosEnd;
+ $Pid++;
+ $ParentLst[$Pid] = &$BDef;
+ $Txt = &$BDef->Src;
+ $Pos = $Loc->PosDefBeg + 1;
+ $LocR->BlockFound = false;
+ $LocR->PosBeg = false;
+ $LocR->PosEnd = false;
+ } elseif (isset($Loc->PrmLst['serial'])) {
+ // Section with serial subsections
+ $SrSrc = &$BDef->Src;
+ // Search the empty item
+ if ($LocR->SerialEmpty===false) {
+ $SrName = $BlockName.'_0';
+ $x = false;
+ $SrLoc = $this->meth_Locator_FindBlockNext($SrSrc, $SrName, 0, '.', 2, $x, $x);
+ if ($SrLoc!==false) {
+ $LocR->SerialEmpty = $SrLoc->BlockSrc;
+ $SrSrc = substr_replace($SrSrc, '', $SrLoc->PosBeg, $SrLoc->PosEnd-$SrLoc->PosBeg+1);
+ }
+ }
+ $SrName = $BlockName.'_1';
+ $x = false;
+ $SrLoc = $this->meth_Locator_FindBlockNext($SrSrc, $SrName, 0, '.', 2, $x, $x);
+ if ($SrLoc!==false) {
+ $SrId = 1;
+ do {
+ // Save previous subsection
+ $SrBDef = &$this->meth_Locator_SectionNewBDef($LocR, $SrName, $SrLoc->BlockSrc, $SrLoc->PrmLst, true);
+ $SrBDef->SrBeg = $SrLoc->PosBeg;
+ $SrBDef->SrLen = $SrLoc->PosEnd - $SrLoc->PosBeg + 1;
+ $SrBDef->SrTxt = false;
+ $BDef->SrBDefLst[$SrId] = &$SrBDef;
+ // Put in order
+ $BDef->SrBDefOrdered[$SrId] = &$SrBDef;
+ $i = $SrId;
+ while (($i>1) && ($SrBDef->SrBeg<$BDef->SrBDefOrdered[$SrId-1]->SrBeg)) {
+ $BDef->SrBDefOrdered[$i] = &$BDef->SrBDefOrdered[$i-1];
+ $BDef->SrBDefOrdered[$i-1] = &$SrBDef;
+ $i--;
+ }
+ // Search next subsection
+ $SrId++;
+ $SrName = $BlockName.'_'.$SrId;
+ $x = false;
+ $SrLoc = $this->meth_Locator_FindBlockNext($SrSrc, $SrName, 0, '.', 2, $x, $x);
+ } while ($SrLoc!==false);
+ $BDef->SrBDefNbr = $SrId-1;
+ $BDef->IsSerial = true;
+ $i = ++$LocR->SectionNbr;
+ $LocR->SectionLst[$i] = &$BDef;
+ }
+ } elseif (isset($Loc->PrmLst['parallel'])) {
+ $BlockLst = $this->meth_Locator_FindParallel($Txt, $Loc->PosBeg, $Loc->PosEnd, $Loc->PrmLst['parallel']);
+ if ($BlockLst) {
+ // Store BDefs
+ foreach ($BlockLst as $i => $Blk) {
+ if ($Blk['IsRef']) {
+ $PrBDef = &$BDef;
+ } else {
+ $PrBDef = &$this->meth_Locator_SectionNewBDef($LocR, $BlockName, $Blk['Src'], [], true);
+ }
+ $PrBDef->PosBeg = $Blk['PosBeg'];
+ $PrBDef->PosEnd = $Blk['PosEnd'];
+ $i = ++$LocR->SectionNbr;
+ $LocR->SectionLst[$i] = &$PrBDef;
+ }
+ $LocR->PosBeg = $BlockLst[0]['PosBeg'];
+ $LocR->PosEnd = $BlockLst[$LocR->SectionNbr-1]['PosEnd'];
+ }
+ } else {
+ // Normal section
+ $i = ++$LocR->SectionNbr;
+ $LocR->SectionLst[$i] = &$BDef;
+ }
+ }
+ } while ($Loc!==false);
+
+ if ($LocR->WhenFound && ($LocR->SectionNbr===0)) {
+ // Add a blank section if When is used without a normal section
+ $BDef = &$this->meth_Locator_SectionNewBDef($LocR, $BlockName, '', [], false);
+ $LocR->SectionNbr = 1;
+ $LocR->SectionLst[1] = &$BDef;
+ }
+
+ return $LocR; // methods return by ref by default
+ }
+
+ public function meth_Locator_FindParallel(&$Txt, $ZoneBeg, $ZoneEnd, $ConfId)
+ {
+ // Define configurations
+ global $_TBS_ParallelLst;
+
+ if (($ConfId=='tbs:table') && (!isset($_TBS_ParallelLst['tbs:table']))) {
+ $_TBS_ParallelLst['tbs:table'] = [
+ 'parent' => 'table',
+ 'ignore' => ['!--', 'caption', 'thead', 'tbody', 'tfoot'],
+ 'cols' => [],
+ 'rows' => ['tr', 'colgroup'],
+ 'cells' => ['td'=>'colspan', 'th'=>'colspan', 'col'=>'span'],
+ ];
+ }
+
+ if (!isset($_TBS_ParallelLst[$ConfId])) {
+ return $this->meth_Misc_Alert("Parallel", "The configuration '$ConfId' is not found.");
+ }
+
+ $conf = $_TBS_ParallelLst[$ConfId];
+
+ $Parent = $conf['parent'];
+
+ // Search parent bounds
+ $par_o = self::f_Xml_FindTag($Txt, $Parent, true, $ZoneBeg, false, 1, false);
+ if ($par_o===false) {
+ return $this->meth_Misc_Alert("Parallel", "The opening tag '$Parent' is not found.");
+ }
+
+ $par_c = self::f_Xml_FindTag($Txt, $Parent, false, $ZoneBeg, true, -1, false);
+ if ($par_c===false) {
+ return $this->meth_Misc_Alert("Parallel", "The closing tag '$Parent' is not found.");
+ }
+
+ $SrcPOffset = $par_o->PosEnd + 1;
+ $SrcP = substr($Txt, $SrcPOffset, $par_c->PosBeg - $SrcPOffset);
+
+ // temporary variables
+ $tagR = '';
+ $tagC = '';
+ $z = '';
+ $pRO = false;
+ $pROe = false;
+ $pCO = false;
+ $pCOe = false;
+ $p = false;
+ $Loc = new clsTbsLocator;
+
+ $Rows = [];
+ $RowIdx = 0;
+ $RefRow = false;
+ $RefCellB= false;
+ $RefCellE = false;
+
+ $RowType = [];
+
+ // Loop on entities inside the parent entity
+ $PosR = 0;
+
+ $mode_column = true;
+ $Cells = [];
+ $ColNum = 1;
+ $IsRef = false;
+
+ // Search for the next Row Opening tag
+ while (self::f_Xml_GetNextEntityName($SrcP, $PosR, $tagR, $pRO, $p)) {
+ $pROe = strpos($SrcP, '>', $p) + 1;
+ $singleR = ($SrcP[$pROe-2] === '/');
+
+ // If the tag is not a closing, a self-closing and has a name
+ if ($tagR!=='') {
+ if (in_array($tagR, $conf['ignore'])) {
+ // This tag must be ignored
+ $PosR = $p;
+ } elseif (isset($conf['cols'][$tagR])) {
+ // Column definition that must be merged as a cell
+ if ($mode_column === false) {
+ return $this->meth_Misc_Alert("Parallel", "There is a column definition ($tagR) after a row (".$Rows[$RowIdx-1]['tag'].").");
+ }
+ if (isset($RowType['_column'])) {
+ $RowType['_column']++;
+ } else {
+ $RowType['_column'] = 1;
+ }
+ $att = $conf['cols'][$tagR];
+ $this->meth_Locator_FindParallelCol($SrcP, $PosR, $tagR, $pRO, $p, $SrcPOffset, $RowIdx, $ZoneBeg, $ZoneEnd, $att, $Loc, $Cells, $ColNum, $IsRef, $RefCellB, $RefCellE, $RefRow);
+ } elseif (!$singleR) {
+
+ // Search the Row Closing tag
+ $locRE = self::f_Xml_FindTag($SrcP, $tagR, false, $pROe, true, -1, false);
+ if ($locRE===false) {
+ return $this->meth_Misc_Alert("Parallel", "The row closing tag is not found. (tagR=$tagR, p=$p, pROe=$pROe)");
+ }
+
+ // Inner source
+ $SrcR = substr($SrcP, $pROe, $locRE->PosBeg - $pROe);
+ $SrcROffset = $SrcPOffset + $pROe;
+
+ if (in_array($tagR, $conf['rows'])) {
+ if ($mode_column && isset($RowType['_column'])) {
+ $Rows[$RowIdx] = ['tag'=>'_column', 'cells' => $Cells, 'isref' => $IsRef, 'count' => $RowType['_column']];
+ $RowIdx++;
+ }
+
+ $mode_column = false;
+
+ if (isset($RowType[$tagR])) {
+ $RowType[$tagR]++;
+ } else {
+ $RowType[$tagR] = 1;
+ }
+
+ // Now we've got the row entity, we search for cell entities
+ $Cells = [];
+ $ColNum = 1;
+ $PosC = 0;
+ $IsRef = false;
+
+ // Loop on Cell Opening tags
+ while (self::f_Xml_GetNextEntityName($SrcR, $PosC, $tagC, $pCO, $p)) {
+ if (isset($conf['cells'][$tagC])) {
+ $att = $conf['cells'][$tagC];
+ $this->meth_Locator_FindParallelCol($SrcR, $PosC, $tagC, $pCO, $p, $SrcROffset, $RowIdx, $ZoneBeg, $ZoneEnd, $att, $Loc, $Cells, $ColNum, $IsRef, $RefCellB, $RefCellE, $RefRow);
+ } else {
+ $PosC = $p;
+ }
+ }
+
+ $Rows[$RowIdx] = ['tag'=>$tagR, 'cells' => $Cells, 'isref' => $IsRef, 'count' => $RowType[$tagR]];
+ $RowIdx++;
+ }
+
+ $PosR = $locRE->PosEnd;
+ } else {
+ $PosR = $pROe;
+ }
+ } else {
+ $PosR = $pROe;
+ }
+ }
+
+ $Blocks = [];
+ $rMax = count($Rows) -1;
+ foreach ($Rows as $r=>$Row) {
+ $Cells = $Row['cells'];
+ if (isset($Cells[$RefCellB]) && $Cells[$RefCellB]['IsBegin']) {
+ if (isset($Cells[$RefCellE]) && $Cells[$RefCellE]['IsEnd']) {
+ $PosBeg = $Cells[$RefCellB]['PosBeg'];
+ $PosEnd = $Cells[$RefCellE]['PosEnd'];
+ $Blocks[$r] = [
+ 'PosBeg' => $PosBeg,
+ 'PosEnd' => $PosEnd,
+ 'IsRef' => $Row['isref'],
+ 'Src' => substr($Txt, $PosBeg, $PosEnd - $PosBeg + 1),
+ ];
+ } else {
+ 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)");
+ }
+ } else {
+ 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)");
+ }
+ }
+
+ return $Blocks;
+ }
+
+ public function meth_Locator_FindParallelCol($SrcR, &$PosC, $tagC, $pCO, $p, $SrcROffset, $RowIdx, $ZoneBeg, $ZoneEnd, &$att, &$Loc, &$Cells, &$ColNum, &$IsRef, &$RefCellB, &$RefCellE, &$RefRow)
+ {
+ $pCOe = false;
+
+ // Read parameters
+ $Loc->PrmLst = [];
+ self::f_Loc_PrmRead($SrcR, $p, true, '\'"', '<', '>', $Loc, $pCOe, true);
+
+ $singleC = ($SrcR[$pCOe-1] === '/');
+ if ($singleC) {
+ $pCEe = $pCOe;
+ } else {
+ // Find the Cell Closing tag
+ $locCE = self::f_Xml_FindTag($SrcR, $tagC, false, $pCOe, true, -1, false);
+ if ($locCE===false) {
+ return $this->meth_Misc_Alert("Parallel", "The cell closing tag is not found. (pCOe=$pCOe)");
+ }
+ $pCEe = $locCE->PosEnd;
+ }
+
+ // Check the cell of reference
+ $Width = (isset($Loc->PrmLst[$att])) ? intval($Loc->PrmLst[$att]) : 1;
+ $ColNumE = $ColNum + $Width -1; // Ending Cell
+ $PosBeg = $SrcROffset + $pCO;
+ $PosEnd = $SrcROffset + $pCEe;
+ $OnZone = false;
+ if (($PosBeg <= $ZoneBeg) && ($ZoneBeg <= $PosEnd) && ($RefRow===false)) {
+ $RefRow = $RowIdx;
+ $RefCellB = $ColNum;
+ $OnZone = true;
+ $IsRef = true;
+ }
+ if (($PosBeg <= $ZoneEnd) && ($ZoneEnd <= $PosEnd)) {
+ $RefCellE = $ColNum;
+ $OnZone = true;
+ }
+
+ // Save info
+ $Cell = [
+ //'_tagR' => $tagR, '_tagC' => $tagC, '_att' => $att, '_OnZone' => $OnZone, '_PrmLst' => $Loc->PrmLst, '_Offset' => $SrcROffset, '_Src' => substr($SrcR, $pCO, $locCE->PosEnd - $pCO + 1),
+ 'PosBeg' => $PosBeg,
+ 'PosEnd' => $PosEnd,
+ 'ColNum' => $ColNum,
+ 'Width' => $Width,
+ 'IsBegin' => true,
+ 'IsEnd' => false,
+ ];
+ $Cells[$ColNum] = $Cell;
+
+ // add a virtual column to say if its a ending
+ if (!isset($Cells[$ColNumE])) {
+ $Cells[$ColNumE] = ['IsBegin' => false];
+ }
+
+ $Cells[$ColNumE]['IsEnd'] = true;
+ $Cells[$ColNumE]['PosEnd'] = $Cells[$ColNum]['PosEnd'];
+
+ $PosC = $pCEe;
+ $ColNum += $Width;
+ }
+
+ public function meth_Merge_Block(&$Txt, $BlockLst, &$SrcId, &$Query, $SpePrm, $SpeRecNum, $QryPrms=false)
+ {
+ $BlockSave = $this->_CurrBlock;
+ $this->_CurrBlock = $BlockLst;
+
+ // Get source type and info
+ $Src = new clsTbsDataSource;
+ if (!$Src->DataPrepare($SrcId, $this)) {
+ $this->_CurrBlock = $BlockSave;
+ return 0;
+ }
+
+ if (is_string($BlockLst)) {
+ $BlockLst = explode(',', $BlockLst);
+ }
+ $BlockNbr = count($BlockLst);
+ $BlockId = 0;
+ $WasP1 = false;
+ $NbrRecTot = 0;
+ $QueryZ = &$Query;
+ $ReturnData = false;
+
+ while ($BlockId<$BlockNbr) {
+ $RecSpe = 0; // Row with a special block's definition (used for the navigation bar)
+ $QueryOk = true;
+ $this->_CurrBlock = trim($BlockLst[$BlockId]);
+ if ($this->_CurrBlock==='*') {
+ $ReturnData = true;
+ if ($Src->RecSaved===false) {
+ $Src->RecSaving = true;
+ }
+ $this->_CurrBlock = '';
+ }
+
+ // Search the block
+ $LocR = $this->meth_Locator_FindBlockLst($Txt, $this->_CurrBlock, 0, $SpePrm);
+
+ if ($LocR->BlockFound) {
+ if ($LocR->Special!==false) {
+ $RecSpe = $SpeRecNum;
+ }
+ // OnData
+ if ($Src->OnDataPrm = isset($LocR->PrmLst['ondata'])) {
+ $Src->OnDataPrmRef = $LocR->PrmLst['ondata'];
+ if (isset($Src->OnDataPrmDone[$Src->OnDataPrmRef])) {
+ $Src->OnDataPrm = false;
+ } else {
+ $ErrMsg = false;
+ if ($this->meth_Misc_UserFctCheck($Src->OnDataPrmRef, 'f', $ErrMsg, $ErrMsg, true)) {
+ $Src->OnDataOk = true;
+ } else {
+ $LocR->FullName = $this->_CurrBlock;
+ $Src->OnDataPrm = $this->meth_Misc_Alert($LocR, '(parameter ondata) '.$ErrMsg, false, 'block');
+ }
+ }
+ }
+ // Dynamic query
+ if ($LocR->P1) {
+ 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
+ if ($Src->RecSaved===false) {
+ $Src->RecSaving = true;
+ }
+ } elseif (is_string($Query)) {
+ $Src->RecSaved = false;
+ unset($QueryZ);
+ $QueryZ = ''.$Query;
+ $i = 1;
+ do {
+ $x = 'p'.$i;
+ if (isset($LocR->PrmLst[$x])) {
+ $QueryZ = str_replace('%p'.$i.'%', $LocR->PrmLst[$x], $QueryZ);
+ $i++;
+ } else {
+ $i = false;
+ }
+ } while ($i!==false);
+ }
+ $WasP1 = true;
+ } elseif (($Src->RecSaved===false) && ($BlockNbr-$BlockId>1)) {
+ $Src->RecSaving = true;
+ }
+ } elseif ($WasP1) {
+ $QueryOk = false;
+ $WasP1 = false;
+ }
+
+ // Open the recordset
+ if ($QueryOk) {
+ if ((!$LocR->BlockFound) && (!$LocR->FieldOutside)) {
+ // Special case: return data without any block to merge
+ $QueryOk = false;
+ if ($ReturnData && (!$Src->RecSaved)) {
+ if ($Src->DataOpen($QueryZ, $QryPrms)) {
+ do {
+ $Src->DataFetch();
+ } while ($Src->CurrRec!==false);
+ $Src->DataClose();
+ }
+ }
+ } else {
+ $QueryOk = $Src->DataOpen($QueryZ, $QryPrms);
+ if (!$QueryOk) {
+ if ($WasP1) {
+ $WasP1 = false;
+ } else {
+ $LocR->FieldOutside = false;
+ } // prevent from infinit loop
+ }
+ }
+ }
+
+ // Merge sections
+ if ($QueryOk) {
+ if ($Src->Type===2) { // Special for Text merge
+ if ($LocR->BlockFound) {
+ $Txt = substr_replace($Txt, $Src->RecSet, $LocR->PosBeg, $LocR->PosEnd-$LocR->PosBeg+1);
+ $Src->DataFetch(); // store data, may be needed for multiple blocks
+ $Src->RecNum = 1;
+ $Src->CurrRec = false;
+ } else {
+ $Src->DataAlert('can\'t merge the block with a text value because the block definition is not found.');
+ }
+ } elseif ($LocR->BlockFound===false) {
+ $Src->DataFetch(); // Merge first record only
+ } elseif (isset($LocR->PrmLst['parallel'])) {
+ $this->meth_Merge_BlockParallel($Txt, $LocR, $Src);
+ } else {
+ $this->meth_Merge_BlockSections($Txt, $LocR, $Src, $RecSpe);
+ }
+ $Src->DataClose(); // Close the resource
+ }
+
+ if (!$WasP1) {
+ $NbrRecTot += $Src->RecNum;
+ $BlockId++;
+ }
+ if ($LocR->FieldOutside) {
+ $this->meth_Merge_FieldOutside($Txt, $Src->CurrRec, $Src->RecNum, $LocR->FOStop);
+ }
+ }
+
+ // End of the merge
+ unset($LocR);
+ $this->_CurrBlock = $BlockSave;
+ if ($ReturnData) {
+ return $Src->RecSet;
+ } else {
+ unset($Src);
+ return $NbrRecTot;
+ }
+ }
+
+ public function meth_Merge_BlockParallel(&$Txt, &$LocR, &$Src)
+ {
+ // Main loop
+ $Src->DataFetch();
+
+ $FirstRec = true;
+
+ // Prepare sources
+ $BlockRes = [];
+ for ($i=1 ; $i<=$LocR->SectionNbr ; $i++) {
+ if ($i>1) {
+ // Add txt source between the BDefs
+ $BlockRes[$i] = substr($Txt, $LocR->SectionLst[$i-1]->PosEnd + 1, $LocR->SectionLst[$i]->PosBeg - $LocR->SectionLst[$i-1]->PosEnd -1);
+ } else {
+ $BlockRes[$i] = '';
+ }
+ }
+
+ while ($Src->CurrRec!==false) {
+ // Merge the current record with all sections
+ for ($i=1 ; $i<=$LocR->SectionNbr ; $i++) {
+ $SecDef = &$LocR->SectionLst[$i];
+ $SecSrc = $this->meth_Merge_SectionNormal($SecDef, $Src);
+ $BlockRes[$i] .= $SecSrc;
+ }
+ // Next row
+ $Src->DataFetch();
+ }
+
+ $BlockRes = implode('', $BlockRes);
+ $Txt = substr_replace($Txt, $BlockRes, $LocR->PosBeg, $LocR->PosEnd-$LocR->PosBeg+1);
+ }
+
+ public function meth_Merge_BlockSections(&$Txt, &$LocR, &$Src, &$RecSpe)
+ {
+ // Initialise
+ $SecId = 0;
+ $SecOk = ($LocR->SectionNbr>0);
+ $SecSrc = '';
+ $BlockRes = ''; // The result of the chained merged blocks
+ $IsSerial = false;
+ $SrId = 0;
+ $SrNbr = 0;
+ $GrpFound = false;
+ if ($LocR->HeaderFound || $LocR->FooterFound) {
+ $GrpFound = true;
+ $piOMG = false;
+ if ($LocR->FooterFound) {
+ $Src->PrevRec = (object) null;
+ }
+ }
+ // Plug-ins
+ $piOMS = false;
+ if ($this->_PlugIns_Ok) {
+ if (isset($this->_piBeforeMergeBlock)) {
+ $ArgLst = [&$Txt,&$LocR->PosBeg,&$LocR->PosEnd,$LocR->PrmLst,&$Src,&$LocR];
+ $this->meth_Plugin_RunAll($this->_piBeforeMergeBlock, $ArgLst);
+ }
+ if (isset($this->_piOnMergeSection)) {
+ $ArgLst = [&$BlockRes,&$SecSrc];
+ $piOMS = true;
+ }
+ if ($GrpFound && isset($this->_piOnMergeGroup)) {
+ $ArgLst2 = [0,0,&$Src,&$LocR];
+ $piOMG = true;
+ }
+ }
+
+ // Main loop
+ $Src->DataFetch();
+
+ while ($Src->CurrRec!==false) {
+
+ // Headers and Footers
+ if ($GrpFound) {
+ $brk_any = false;
+ $brk_src = '';
+ if ($LocR->FooterFound) {
+ $brk = false;
+ for ($i=$LocR->FooterNbr;$i>=1;$i--) {
+ $GrpDef = &$LocR->FooterDef[$i];
+ $x = $this->meth_Merge_SectionNormal($GrpDef->FDef, $Src);
+ if ($Src->RecNum===1) {
+ $GrpDef->PrevValue = $x;
+ $brk_i = false;
+ } else {
+ if ($GrpDef->AddLastGrp) {
+ $brk_i = &$brk;
+ } else {
+ unset($brk_i);
+ $brk_i = false;
+ }
+ if (!$brk_i) {
+ $brk_i = !($GrpDef->PrevValue===$x);
+ }
+ if ($brk_i) {
+ $brk_any = true;
+ $ok = true;
+ if ($piOMG) {
+ $ArgLst2[0]=&$Src->PrevRec;
+ $ArgLst2[1]=&$GrpDef;
+ $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup, $ArgLst2);
+ }
+ if ($ok!==false) {
+ $brk_src = $this->meth_Merge_SectionNormal($GrpDef, $Src->PrevRec).$brk_src;
+ }
+ $GrpDef->PrevValue = $x;
+ }
+ }
+ }
+ $Src->PrevRec->CurrRec = $Src->CurrRec;
+ $Src->PrevRec->RecNum = $Src->RecNum;
+ $Src->PrevRec->RecKey = $Src->RecKey;
+ }
+ if ($LocR->HeaderFound) {
+ $brk = ($Src->RecNum===1);
+ for ($i=1;$i<=$LocR->HeaderNbr;$i++) {
+ $GrpDef = &$LocR->HeaderDef[$i];
+ $x = $this->meth_Merge_SectionNormal($GrpDef->FDef, $Src);
+ if (!$brk) {
+ $brk = !($GrpDef->PrevValue===$x);
+ }
+ if ($brk) {
+ $ok = true;
+ if ($piOMG) {
+ $ArgLst2[0]=&$Src;
+ $ArgLst2[1]=&$GrpDef;
+ $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup, $ArgLst2);
+ }
+ if ($ok!==false) {
+ $brk_src .= $this->meth_Merge_SectionNormal($GrpDef, $Src);
+ }
+ $GrpDef->PrevValue = $x;
+ }
+ }
+ $brk_any = ($brk_any || $brk);
+ }
+ if ($brk_any) {
+ if ($IsSerial) {
+ $BlockRes .= $this->meth_Merge_SectionSerial($SecDef, $SrId, $LocR);
+ $IsSerial = false;
+ }
+ $BlockRes .= $brk_src;
+ }
+ } // end of header and footer
+
+ // Increment Section
+ if (($IsSerial===false) && $SecOk) {
+ $SecId++;
+ if ($SecId>$LocR->SectionNbr) {
+ $SecId = 1;
+ }
+ $SecDef = &$LocR->SectionLst[$SecId];
+ $IsSerial = $SecDef->IsSerial;
+ if ($IsSerial) {
+ $SrId = 0;
+ $SrNbr = $SecDef->SrBDefNbr;
+ }
+ }
+
+ // Serial Mode Activation
+ if ($IsSerial) { // Serial Merge
+ $SrId++;
+ $SrBDef = &$SecDef->SrBDefLst[$SrId];
+ $SrBDef->SrTxt = $this->meth_Merge_SectionNormal($SrBDef, $Src);
+ if ($SrId>=$SrNbr) {
+ $SecSrc = $this->meth_Merge_SectionSerial($SecDef, $SrId, $LocR);
+ $BlockRes .= $SecSrc;
+ $IsSerial = false;
+ }
+ } else { // Classic merge
+ if ($SecOk) {
+ if ($Src->RecNum===$RecSpe) {
+ $SecDef = &$LocR->Special;
+ }
+ $SecSrc = $this->meth_Merge_SectionNormal($SecDef, $Src);
+ } else {
+ $SecSrc = '';
+ }
+ if ($LocR->WhenFound) { // With conditional blocks
+ $found = false;
+ $continue = true;
+ $i = 1;
+ do {
+ $WhenBDef = &$LocR->WhenLst[$i];
+ $cond = $this->meth_Merge_SectionNormal($WhenBDef->WhenCond, $Src);
+ if ($this->f_Misc_CheckCondition($cond)) {
+ $x_when = $this->meth_Merge_SectionNormal($WhenBDef, $Src);
+ if ($WhenBDef->WhenBeforeNS) {
+ $SecSrc = $x_when.$SecSrc;
+ } else {
+ $SecSrc = $SecSrc.$x_when;
+ }
+ $found = true;
+ if ($LocR->WhenSeveral===false) {
+ $continue = false;
+ }
+ }
+ $i++;
+ if ($i>$LocR->WhenNbr) {
+ $continue = false;
+ }
+ } while ($continue);
+ if (($found===false) && ($LocR->WhenDefault!==false)) {
+ $x_when = $this->meth_Merge_SectionNormal($LocR->WhenDefault, $Src);
+ if ($LocR->WhenDefaultBeforeNS) {
+ $SecSrc = $x_when.$SecSrc;
+ } else {
+ $SecSrc = $SecSrc.$x_when;
+ }
+ }
+ }
+ if ($piOMS) {
+ $this->meth_PlugIn_RunAll($this->_piOnMergeSection, $ArgLst);
+ }
+ $BlockRes .= $SecSrc;
+ }
+
+ // Next row
+ $Src->DataFetch();
+ } //--> while($CurrRec!==false) {
+
+ $SecSrc = '';
+
+ // Serial: merge the extra the sub-blocks
+ if ($IsSerial) {
+ $SecSrc .= $this->meth_Merge_SectionSerial($SecDef, $SrId, $LocR);
+ }
+
+ // Footer
+ if ($LocR->FooterFound) {
+ if ($Src->RecNum>0) {
+ for ($i=1;$i<=$LocR->FooterNbr;$i++) {
+ $GrpDef = &$LocR->FooterDef[$i];
+ if ($GrpDef->AddLastGrp) {
+ $ok = true;
+ if ($piOMG) {
+ $ArgLst2[0]=&$Src->PrevRec;
+ $ArgLst2[1]=&$GrpDef;
+ $ok = $this->meth_PlugIn_RunAll($this->_piOnMergeGroup, $ArgLst2);
+ }
+ if ($ok!==false) {
+ $SecSrc .= $this->meth_Merge_SectionNormal($GrpDef, $Src->PrevRec);
+ }
+ }
+ }
+ }
+ }
+
+ // NoData
+ if ($Src->RecNum===0) {
+ if ($LocR->NoData!==false) {
+ $SecSrc = $LocR->NoData->Src;
+ } elseif (isset($LocR->PrmLst['bmagnet'])) {
+ $this->f_Loc_EnlargeToTag($Txt, $LocR, $LocR->PrmLst['bmagnet'], false);
+ }
+ }
+
+ // Plug-ins
+ if ($piOMS && ($SecSrc!=='')) {
+ $this->meth_PlugIn_RunAll($this->_piOnMergeSection, $ArgLst);
+ }
+
+ $BlockRes .= $SecSrc;
+
+ // Plug-ins
+ if ($this->_PlugIns_Ok && isset($ArgLst) && isset($this->_piAfterMergeBlock)) {
+ $ArgLst = [&$BlockRes,&$Src,&$LocR];
+ $this->meth_PlugIn_RunAll($this->_piAfterMergeBlock, $ArgLst);
+ }
+
+ // Merge the result
+ $Txt = substr_replace($Txt, $BlockRes, $LocR->PosBeg, $LocR->PosEnd-$LocR->PosBeg+1);
+ if ($LocR->P1) {
+ $LocR->FOStop = $LocR->PosBeg + strlen($BlockRes) -1;
+ }
+ }
+
+ public function meth_Merge_AutoVar(&$Txt, $ConvStr, $Id='var')
+ {
+ // Merge automatic fields with VarRef
+
+ $Pref = &$this->VarPrefix;
+ $PrefL = strlen($Pref);
+ $PrefOk = ($PrefL>0);
+
+ if ($ConvStr===false) {
+ $Charset = $this->Charset;
+ $this->Charset = false;
+ }
+
+ // Then we scann all fields in the model
+ $x = '';
+ $Pos = 0;
+ while ($Loc = $this->meth_Locator_FindTbs($Txt, $Id, $Pos, '.')) {
+ if ($Loc->SubNbr==0) {
+ $Loc->SubLst[0]='';
+ } // In order to force error message
+ if ($Loc->SubLst[0]==='') {
+ $Pos = $this->meth_Merge_AutoSpe($Txt, $Loc);
+ } elseif ($Loc->SubLst[0][0]==='~') {
+ if (!isset($ObjOk)) {
+ $ObjOk = (is_object($this->ObjectRef) || is_array($this->ObjectRef));
+ }
+ if ($ObjOk) {
+ $Loc->SubLst[0] = substr($Loc->SubLst[0], 1);
+ $Pos = $this->meth_Locator_Replace($Txt, $Loc, $this->ObjectRef, 0);
+ } elseif (isset($Loc->PrmLst['noerr'])) {
+ $Pos = $this->meth_Locator_Replace($Txt, $Loc, $x, false);
+ } else {
+ $this->meth_Misc_Alert($Loc, 'property ObjectRef is neither an object nor an array. Its type is \''.gettype($this->ObjectRef).'\'.', true);
+ $Pos = $Loc->PosEnd + 1;
+ }
+ } elseif ($PrefOk && (substr($Loc->SubLst[0], 0, $PrefL)!==$Pref)) {
+ if (isset($Loc->PrmLst['noerr'])) {
+ $Pos = $this->meth_Locator_Replace($Txt, $Loc, $x, false);
+ } else {
+ $this->meth_Misc_Alert($Loc, 'does not match the allowed prefix.', true);
+ $Pos = $Loc->PosEnd + 1;
+ }
+ } elseif (isset($this->VarRef[$Loc->SubLst[0]])) {
+ $Pos = $this->meth_Locator_Replace($Txt, $Loc, $this->VarRef[$Loc->SubLst[0]], 1);
+ } else {
+ if (isset($Loc->PrmLst['noerr'])) {
+ $Pos = $this->meth_Locator_Replace($Txt, $Loc, $x, false);
+ } else {
+ $Pos = $Loc->PosEnd + 1;
+ $msg = (isset($this->VarRef['GLOBALS'])) ? 'VarRef seems refers to $GLOBALS' : 'VarRef seems refers to a custom array of values';
+ $this->meth_Misc_Alert($Loc, 'the key \''.$Loc->SubLst[0].'\' does not exist or is not set in VarRef. ('.$msg.')', true);
+ }
+ }
+ }
+
+ if ($ConvStr===false) {
+ $this->Charset = $Charset;
+ }
+
+ return false; // Useful for properties PrmIfVar & PrmThenVar
+ }
+
+ public function meth_Merge_AutoSpe(&$Txt, &$Loc)
+ {
+ // Merge Special Var Fields ([var..*])
+
+ $ErrMsg = false;
+ $SubStart = false;
+ if (isset($Loc->SubLst[1])) {
+ switch ($Loc->SubLst[1]) {
+ case 'now': $x = time(); break;
+ case 'version': $x = $this->Version; break;
+ case 'script_name': $x = basename(((isset($_SERVER)) ? $_SERVER['PHP_SELF'] : $GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'])); break;
+ case 'template_name': $x = $this->_LastFile; break;
+ case 'template_date': $x = ''; if ($this->f_Misc_GetFile($x, $this->_LastFile, '', [], false)) {
+ $x = $x['mtime'];
+ } break;
+ case 'template_path': $x = dirname($this->_LastFile).'/'; break;
+ case 'name': $x = 'TinyButStrong'; break;
+ case 'logo': $x = '**TinyButStrong**'; break;
+ case 'charset': $x = $this->Charset; break;
+ case 'error_msg': $this->_ErrMsgName = $Loc->FullName; return $Loc->PosEnd; break;
+ case '': $ErrMsg = 'it doesn\'t have any keyword.'; break;
+ case 'tplvars':
+ if ($Loc->SubNbr==2) {
+ $SubStart = 2;
+ $x = implode(',', array_keys($this->TplVars)); // list of all template variables
+ } else {
+ if (isset($this->TplVars[$Loc->SubLst[2]])) {
+ $SubStart = 3;
+ $x = &$this->TplVars[$Loc->SubLst[2]];
+ } else {
+ $ErrMsg = 'property TplVars doesn\'t have any item named \''.$Loc->SubLst[2].'\'.';
+ }
+ }
+ break;
+ case 'store':
+ if ($Loc->SubNbr==2) {
+ $SubStart = 2;
+ $x = implode('', $this->TplStore); // concatenation of all stores
+ } else {
+ if (isset($this->TplStore[$Loc->SubLst[2]])) {
+ $SubStart = 3;
+ $x = &$this->TplStore[$Loc->SubLst[2]];
+ } else {
+ $ErrMsg = 'Store named \''.$Loc->SubLst[2].'\' is not defined yet.';
+ }
+ }
+ if (!isset($Loc->PrmLst['strconv'])) {
+ $Loc->PrmLst['strconv'] = 'no';
+ $Loc->PrmLst['protect'] = 'no';
+ }
+ break;
+ case 'cst': $x = @constant($Loc->SubLst[2]); break;
+ case 'tbs_info':
+ $x = 'TinyButStrong version '.$this->Version.' for PHP 5';
+ $x .= "\r\nInstalled plug-ins: ".count($this->_PlugIns);
+ foreach (array_keys($this->_PlugIns) as $pi) {
+ $o = &$this->_PlugIns[$pi];
+ $x .= "\r\n- plug-in [".(isset($o->Name) ? $o->Name : $pi).'] version '.(isset($o->Version) ? $o->Version : '?');
+ }
+ break;
+ case 'php_info':
+ ob_start();
+ phpinfo();
+ $x = ob_get_contents();
+ ob_end_clean();
+ $x = self::f_Xml_GetPart($x, '(style)+body', false);
+ if (!isset($Loc->PrmLst['strconv'])) {
+ $Loc->PrmLst['strconv'] = 'no';
+ $Loc->PrmLst['protect'] = 'no';
+ }
+ break;
+ default:
+ $IsSupported = false;
+ if (isset($this->_piOnSpecialVar)) {
+ $x = '';
+ $ArgLst = [substr($Loc->SubName, 1),&$IsSupported ,&$x, &$Loc->PrmLst,&$Txt,&$Loc->PosBeg,&$Loc->PosEnd,&$Loc];
+ $this->meth_PlugIn_RunAll($this->_piOnSpecialVar, $ArgLst);
+ }
+ if (!$IsSupported) {
+ $ErrMsg = '\''.$Loc->SubLst[1].'\' is an unsupported keyword.';
+ }
+ }
+ } else {
+ $ErrMsg = 'it doesn\'t have any subname.';
+ }
+ if ($ErrMsg!==false) {
+ $this->meth_Misc_Alert($Loc, $ErrMsg);
+ $x = '';
+ }
+ if ($Loc->PosBeg===false) {
+ return $Loc->PosEnd;
+ } else {
+ return $this->meth_Locator_Replace($Txt, $Loc, $x, $SubStart);
+ }
+ }
+
+ public function meth_Merge_FieldOutside(&$Txt, &$CurrRec, $RecNum, $PosMax)
+ {
+ $Pos = 0;
+ $SubStart = ($CurrRec===false) ? false : 0;
+ do {
+ $Loc = $this->meth_Locator_FindTbs($Txt, $this->_CurrBlock, $Pos, '.');
+ if ($Loc!==false) {
+ if (($PosMax!==false) && ($Loc->PosEnd>$PosMax)) {
+ return;
+ }
+ if ($Loc->SubName==='#') {
+ $NewEnd = $this->meth_Locator_Replace($Txt, $Loc, $RecNum, false);
+ } else {
+ $NewEnd = $this->meth_Locator_Replace($Txt, $Loc, $CurrRec, $SubStart);
+ }
+ if ($PosMax!==false) {
+ $PosMax += $NewEnd - $Loc->PosEnd;
+ }
+ $Pos = $NewEnd;
+ }
+ } while ($Loc!==false);
+ }
+
+ public function meth_Merge_SectionNormal(&$BDef, &$Src)
+ {
+ $Txt = $BDef->Src;
+ $LocLst = &$BDef->LocLst;
+ $iMax = $BDef->LocNbr;
+ $PosMax = strlen($Txt);
+
+ if ($Src===false) { // Erase all fields
+
+ $x = '';
+
+ // Chached locators
+ for ($i=$iMax;$i>0;$i--) {
+ if ($LocLst[$i]->PosBeg<$PosMax) {
+ $this->meth_Locator_Replace($Txt, $LocLst[$i], $x, false);
+ if ($LocLst[$i]->Enlarged) {
+ $PosMax = $LocLst[$i]->PosBeg;
+ $LocLst[$i]->PosBeg = $LocLst[$i]->PosBeg0;
+ $LocLst[$i]->PosEnd = $LocLst[$i]->PosEnd0;
+ $LocLst[$i]->Enlarged = false;
+ }
+ }
+ }
+
+ // Uncached locators
+ if ($BDef->Chk) {
+ $BlockName = &$BDef->Name;
+ $Pos = 0;
+ while ($Loc = $this->meth_Locator_FindTbs($Txt, $BlockName, $Pos, '.')) {
+ $Pos = $this->meth_Locator_Replace($Txt, $Loc, $x, false);
+ }
+ }
+ } else {
+
+ // Cached locators
+ for ($i=$iMax;$i>0;$i--) {
+ if ($LocLst[$i]->PosBeg<$PosMax) {
+ if ($LocLst[$i]->IsRecInfo) {
+ if ($LocLst[$i]->RecInfo==='#') {
+ $this->meth_Locator_Replace($Txt, $LocLst[$i], $Src->RecNum, false);
+ } else {
+ $this->meth_Locator_Replace($Txt, $LocLst[$i], $Src->RecKey, false);
+ }
+ } else {
+ $this->meth_Locator_Replace($Txt, $LocLst[$i], $Src->CurrRec, 0);
+ }
+ if ($LocLst[$i]->Enlarged) {
+ $PosMax = $LocLst[$i]->PosBeg;
+ $LocLst[$i]->PosBeg = $LocLst[$i]->PosBeg0;
+ $LocLst[$i]->PosEnd = $LocLst[$i]->PosEnd0;
+ $LocLst[$i]->Enlarged = false;
+ }
+ }
+ }
+
+ // Unchached locators
+ if ($BDef->Chk) {
+ $BlockName = &$BDef->Name;
+ foreach ($Src->CurrRec as $key => $val) {
+ $Pos = 0;
+ $Name = $BlockName.'.'.$key;
+ while ($Loc = $this->meth_Locator_FindTbs($Txt, $Name, $Pos, '.')) {
+ $Pos = $this->meth_Locator_Replace($Txt, $Loc, $val, 0);
+ }
+ }
+ $Pos = 0;
+ $Name = $BlockName.'.#';
+ while ($Loc = $this->meth_Locator_FindTbs($Txt, $Name, $Pos, '.')) {
+ $Pos = $this->meth_Locator_Replace($Txt, $Loc, $Src->RecNum, 0);
+ }
+ $Pos = 0;
+ $Name = $BlockName.'.$';
+ while ($Loc = $this->meth_Locator_FindTbs($Txt, $Name, $Pos, '.')) {
+ $Pos = $this->meth_Locator_Replace($Txt, $Loc, $Src->RecKey, 0);
+ }
+ }
+ }
+
+ // Automatic sub-blocks
+ if (isset($BDef->AutoSub)) {
+ for ($i=1;$i<=$BDef->AutoSub;$i++) {
+ $name = $BDef->Name.'_sub'.$i;
+ $query = '';
+ $col = $BDef->Prm['sub'.$i];
+ if ($col===true) {
+ $col = '';
+ }
+ $col_opt = (substr($col, 0, 1)==='(') && (substr($col, -1, 1)===')');
+ if ($col_opt) {
+ $col = substr($col, 1, strlen($col)-2);
+ }
+ if ($col==='') {
+ // $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'
+ $data = &$Src->CurrRec;
+ } elseif (is_object($Src->CurrRec)) {
+ $data = &$Src->CurrRec->$col;
+ } else {
+ if (array_key_exists($col, $Src->CurrRec)) {
+ $data = &$Src->CurrRec[$col];
+ } else {
+ 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.')');
+ }
+ unset($data);
+ $data = [];
+ }
+ }
+ if (is_string($data)) {
+ $data = explode(',', $data);
+ } elseif (is_null($data) || ($data===false)) {
+ $data = [];
+ }
+ $this->meth_Merge_Block($Txt, $name, $data, $query, false, 0, false);
+ }
+ }
+
+ return $Txt;
+ }
+
+ public function meth_Merge_SectionSerial(&$BDef, &$SrId, &$LocR)
+ {
+ $Txt = $BDef->Src;
+ $SrBDefOrdered = &$BDef->SrBDefOrdered;
+ $Empty = &$LocR->SerialEmpty;
+
+ // All Items
+ $F = false;
+ for ($i=$BDef->SrBDefNbr;$i>0;$i--) {
+ $SrBDef = &$SrBDefOrdered[$i];
+ if ($SrBDef->SrTxt===false) { // Subsection not merged with a record
+ if ($Empty===false) {
+ $SrBDef->SrTxt = $this->meth_Merge_SectionNormal($SrBDef, $F);
+ } else {
+ $SrBDef->SrTxt = $Empty;
+ }
+ }
+ $Txt = substr_replace($Txt, $SrBDef->SrTxt, $SrBDef->SrBeg, $SrBDef->SrLen);
+ $SrBDef->SrTxt = false;
+ }
+
+ $SrId = 0;
+ return $Txt;
+ }
+
+ public function meth_Merge_AutoOn(&$Txt, $Name, $TplVar, $MergeVar)
+ {
+ // Merge [onload] or [onshow] fields and blocks
+
+ $GrpDisplayed = [];
+ $GrpExclusive = [];
+ $P1 = false;
+ $FieldBefore = false;
+ $Pos = 0;
+
+ while ($LocA=$this->meth_Locator_FindBlockNext($Txt, $Name, $Pos, '_', 1, $P1, $FieldBefore)) {
+ if ($LocA->BlockFound) {
+ if (!isset($GrpDisplayed[$LocA->SubName])) {
+ $GrpDisplayed[$LocA->SubName] = false;
+ $GrpExclusive[$LocA->SubName] = ($LocA->SubName!=='');
+ }
+ $Displayed = &$GrpDisplayed[$LocA->SubName];
+ $Exclusive = &$GrpExclusive[$LocA->SubName];
+
+ $DelBlock = false;
+ $DelField = false;
+ if ($Displayed && $Exclusive) {
+ $DelBlock = true;
+ } else {
+ if (isset($LocA->PrmLst['when'])) {
+ if (isset($LocA->PrmLst['several'])) {
+ $Exclusive=false;
+ }
+ $x = $LocA->PrmLst['when'];
+ $this->meth_Merge_AutoVar($x, false);
+ if ($this->f_Misc_CheckCondition($x)) {
+ $DelField = true;
+ $Displayed = true;
+ } else {
+ $DelBlock = true;
+ }
+ } elseif (isset($LocA->PrmLst['default'])) {
+ if ($Displayed) {
+ $DelBlock = true;
+ } else {
+ $Displayed = true;
+ $DelField = true;
+ }
+ $Exclusive = true; // No more block displayed for the group after
+ }
+ }
+
+ // Del parts
+ if ($DelField) {
+ if ($LocA->PosBeg2!==false) {
+ $Txt = substr_replace($Txt, '', $LocA->PosBeg2, $LocA->PosEnd2-$LocA->PosBeg2+1);
+ }
+ $Txt = substr_replace($Txt, '', $LocA->PosBeg, $LocA->PosEnd-$LocA->PosBeg+1);
+ $Pos = $LocA->PosBeg;
+ } else {
+ $FldPos = $LocA->PosBeg;
+ $FldLen = $LocA->PosEnd - $LocA->PosBeg + 1;
+ if ($LocA->PosBeg2===false) {
+ 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');
+ }
+ } else {
+ $LocA->PosEnd = $LocA->PosEnd2;
+ }
+ if ($DelBlock) {
+ $parallel = false;
+ if (isset($LocA->PrmLst['parallel'])) {
+ // may return false if error
+ $parallel = $this->meth_Locator_FindParallel($Txt, $LocA->PosBeg, $LocA->PosEnd, $LocA->PrmLst['parallel']);
+ if ($parallel===false) {
+ $Txt = substr_replace($Txt, '', $FldPos, $FldLen);
+ } else {
+ // delete in reverse order
+ for ($r = count($parallel)-1 ; $r >= 0 ; $r--) {
+ $p = $parallel[$r];
+ $Txt = substr_replace($Txt, '', $p['PosBeg'], $p['PosEnd']-$p['PosBeg']+1);
+ }
+ }
+ } else {
+ $Txt = substr_replace($Txt, '', $LocA->PosBeg, $LocA->PosEnd-$LocA->PosBeg+1);
+ }
+ } else {
+ // Merge the block as if it was a field
+ $x = '';
+ $this->meth_Locator_Replace($Txt, $LocA, $x, false);
+ }
+ $Pos = $LocA->PosBeg;
+ }
+ } else { // Field (has no subname at this point)
+
+ // Check for Template Var
+ if ($TplVar) {
+ if (isset($LocA->PrmLst['tplvars']) || isset($LocA->PrmLst['tplfrms'])) {
+ $Scan = '';
+ foreach ($LocA->PrmLst as $Key => $Val) {
+ if ($Scan=='v') {
+ $this->TplVars[$Key] = $Val;
+ } elseif ($Scan=='f') {
+ self::f_Misc_FormatSave($Val, $Key);
+ } elseif ($Key==='tplvars') {
+ $Scan = 'v';
+ } elseif ($Key==='tplfrms') {
+ $Scan = 'f';
+ }
+ }
+ }
+ }
+
+ $x = '';
+ $Pos = $this->meth_Locator_Replace($Txt, $LocA, $x, false);
+ $Pos = $LocA->PosBeg;
+ }
+ }
+
+ if ($MergeVar) {
+ $this->meth_Merge_AutoVar($this->Source, true, $Name);
+ } // merge other fields (must have subnames)
+
+ foreach ($this->Assigned as $n=>$a) {
+ if (isset($a['auto']) && ($a['auto']===$Name)) {
+ $x = [];
+ $this->meth_Misc_Assign($n, $x, false);
+ }
+ }
+ }
+
+ // Prepare the strconv parameter
+ public function meth_Conv_Prepare(&$Loc, $StrConv)
+ {
+ $x = strtolower($StrConv);
+ $x = '+'.str_replace(' ', '', $x).'+';
+ if (strpos($x, '+esc+')!==false) {
+ $this->f_Misc_ConvSpe($Loc);
+ $Loc->ConvStr = false;
+ $Loc->ConvEsc = true;
+ }
+ if (strpos($x, '+wsp+')!==false) {
+ $this->f_Misc_ConvSpe($Loc);
+ $Loc->ConvWS = true;
+ }
+ if (strpos($x, '+js+')!==false) {
+ $this->f_Misc_ConvSpe($Loc);
+ $Loc->ConvStr = false;
+ $Loc->ConvJS = true;
+ }
+ if (strpos($x, '+url+')!==false) {
+ $this->f_Misc_ConvSpe($Loc);
+ $Loc->ConvStr = false;
+ $Loc->ConvUrl = true;
+ }
+ if (strpos($x, '+utf8+')!==false) {
+ $this->f_Misc_ConvSpe($Loc);
+ $Loc->ConvStr = false;
+ $Loc->ConvUtf8 = true;
+ }
+ if (strpos($x, '+no+')!==false) {
+ $Loc->ConvStr = false;
+ }
+ if (strpos($x, '+yes+')!==false) {
+ $Loc->ConvStr = true;
+ }
+ if (strpos($x, '+nobr+')!==false) {
+ $Loc->ConvStr = true;
+ $Loc->ConvBr = false;
+ }
+ }
+
+ // Convert a string with charset or custom function
+ public function meth_Conv_Str(&$Txt, $ConvBr=true)
+ {
+ if ($this->Charset==='') { // Html by default
+ $Txt = htmlspecialchars($Txt);
+ if ($ConvBr) {
+ $Txt = nl2br($Txt);
+ }
+ } elseif ($this->_CharsetFct) {
+ $Txt = call_user_func($this->Charset, $Txt, $ConvBr);
+ } else {
+ $Txt = htmlspecialchars($Txt, ENT_COMPAT, $this->Charset);
+ if ($ConvBr) {
+ $Txt = nl2br($Txt);
+ }
+ }
+ }
+
+ // Standard alert message provided by TinyButStrong, return False is the message is cancelled.
+ public function meth_Misc_Alert($Src, $Msg, $NoErrMsg=false, $SrcType=false)
+ {
+ $this->ErrCount++;
+ if ($this->NoErr || (PHP_SAPI==='cli')) {
+ $t = ['','','','',''];
+ } else {
+ $t = ['
','','','','
'];
+ $Msg = htmlentities($Msg);
+ }
+ if (!is_string($Src)) {
+ if ($SrcType===false) {
+ $SrcType='in field';
+ }
+ if (isset($Src->PrmLst['tbstype'])) {
+ $Msg = 'Column \''.$Src->SubName.'\' is expected but missing in the current record.';
+ $Src = 'Parameter \''.$Src->PrmLst['tbstype'].'='.$Src->SubName.'\'';
+ $NoErrMsg = false;
+ } else {
+ $Src = $SrcType.' '.$this->_ChrOpen.$Src->FullName.'...'.$this->_ChrClose;
+ }
+ }
+ $x = $t[0].'TinyButStrong Error'.$t[1].' '.$Src.': '.$Msg;
+ if ($NoErrMsg) {
+ $x = $x.' '.$t[2].'This message can be cancelled using parameter \'noerr\'.'.$t[3];
+ }
+ $x = $x.$t[4]."\n";
+ if ($this->NoErr) {
+ $this->ErrMsg .= $x;
+ } else {
+ if (PHP_SAPI!=='cli') {
+ $x = str_replace($this->_ChrOpen, $this->_ChrProtect, $x);
+ }
+ echo $x;
+ }
+ return false;
+ }
+
+ public function meth_Misc_Assign($Name, &$ArgLst, $CallingMeth)
+ {
+ // $ArgLst must be by reference in order to have its inner items by reference too.
+
+ if (!isset($this->Assigned[$Name])) {
+ if ($CallingMeth===false) {
+ return true;
+ }
+ return $this->meth_Misc_Alert('with '.$CallingMeth.'() method', 'key \''.$Name.'\' is not defined in property Assigned.');
+ }
+
+ $a = &$this->Assigned[$Name];
+ $meth = (isset($a['type'])) ? $a['type'] : 'MergeBlock';
+ 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.'.');
+ }
+
+ $n = count($a);
+ for ($i=0;$i<$n;$i++) {
+ if (isset($a[$i])) {
+ $ArgLst[$i] = &$a[$i];
+ }
+ }
+
+ if ($CallingMeth===false) {
+ if (in_array(strtolower($meth), ['mergeblock','mergefield'])) {
+ call_user_func_array([&$this,$meth], $ArgLst);
+ } else {
+ return $this->meth_Misc_Alert('The assigned field \''.$Name.'\'. cannot be merged because its type \''.$a[0].'\' is not supported.');
+ }
+ }
+ if (!isset($a['merged'])) {
+ $a['merged'] = 0;
+ }
+ $a['merged']++;
+ return true;
+ }
+
+ public function meth_Misc_IsMainTpl()
+ {
+ return ($this->_Mode==0);
+ }
+
+ public function meth_Misc_ChangeMode($Init, &$Loc, &$CurrVal)
+ {
+ if ($Init) {
+ // Save contents configuration
+ $Loc->SaveSrc = &$this->Source;
+ $Loc->SaveMode = $this->_Mode;
+ $Loc->SaveVarRef = &$this->VarRef;
+ unset($this->Source);
+ $this->Source = '';
+ $this->_Mode++; // Mode>0 means subtemplate mode
+ if ($this->OldSubTpl) {
+ ob_start(); // Start buffuring output
+ $Loc->SaveRender = $this->Render;
+ }
+ $this->Render = TBS_OUTPUT;
+ } else {
+ // Restore contents configuration
+ if ($this->OldSubTpl) {
+ $CurrVal = ob_get_contents();
+ ob_end_clean();
+ $this->Render = $Loc->SaveRender;
+ } else {
+ $CurrVal = $this->Source;
+ }
+ $this->Source = &$Loc->SaveSrc;
+ $this->_Mode = $Loc->SaveMode;
+ $this->VarRef = &$Loc->SaveVarRef;
+ }
+ }
+
+ public function meth_Misc_UserFctCheck(&$FctInfo, $FctCat, &$FctObj, &$ErrMsg, $FctCheck=false)
+ {
+ $FctId = $FctCat.':'.$FctInfo;
+ if (isset($this->_UserFctLst[$FctId])) {
+ $FctInfo = $this->_UserFctLst[$FctId];
+ return true;
+ }
+
+ // Check and put in cache
+ $FctStr = $FctInfo;
+ $IsData = ($FctCat!=='f');
+ $Save = true;
+ if ($FctStr[0]==='~') {
+ $ObjRef = &$this->ObjectRef;
+ $Lst = explode('.', substr($FctStr, 1));
+ $iMax = count($Lst) - 1;
+ $Suff = 'tbsdb';
+ $iMax0 = $iMax;
+ if ($IsData) {
+ $Suff = $Lst[$iMax];
+ $iMax--;
+ }
+ // Reading sub items
+ for ($i=0;$i<=$iMax;$i++) {
+ $x = &$Lst[$i];
+ if (is_object($ObjRef)) {
+ $ArgLst = $this->f_Misc_CheckArgLst($x);
+ if (method_exists($ObjRef, $x)) {
+ if ($i<$iMax) {
+ $f = [&$ObjRef,$x];
+ unset($ObjRef);
+ $ObjRef = call_user_func_array($f, $ArgLst);
+ }
+ } elseif ($i===$iMax0) {
+ $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because \''.$x.'\' is not a method in the class \''.get_class($ObjRef).'\'.';
+ return false;
+ } elseif (isset($ObjRef->$x)) {
+ $ObjRef = &$ObjRef->$x;
+ } else {
+ $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because sub-item \''.$x.'\' is neither a method nor a property in the class \''.get_class($ObjRef).'\'.';
+ return false;
+ }
+ } elseif (($i<$iMax0) && is_array($ObjRef)) {
+ if (isset($ObjRef[$x])) {
+ $ObjRef = &$ObjRef[$x];
+ } else {
+ $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because sub-item \''.$x.'\' is not a existing key in the array.';
+ return false;
+ }
+ } else {
+ $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because '.(($i===0)?'property ObjectRef':'sub-item \''.$x.'\'').' is not an object'.(($i<$iMax)?' or an array.':'.');
+ return false;
+ }
+ }
+ // Referencing last item
+ if ($IsData) {
+ $FctInfo = ['open'=>'','fetch'=>'','close'=>''];
+ foreach ($FctInfo as $act=>$x) {
+ $FctName = $Suff.'_'.$act;
+ if (method_exists($ObjRef, $FctName)) {
+ $FctInfo[$act] = [&$ObjRef,$FctName];
+ } else {
+ $ErrMsg = 'Expression \''.$FctStr.'\' is invalid because method '.$FctName.' is not found.';
+ return false;
+ }
+ }
+ $FctInfo['type'] = 4;
+ if (isset($this->RecheckObj) && $this->RecheckObj) {
+ $Save = false;
+ }
+ } else {
+ $FctInfo = [&$ObjRef,$x];
+ }
+ } elseif ($IsData) {
+ $IsObj = ($FctCat==='o');
+
+ if ($IsObj && method_exists($FctObj, 'tbsdb_open') && (!method_exists($FctObj, '+'))) { // '+' avoid a bug in PHP 5
+
+ if (!method_exists($FctObj, 'tbsdb_fetch')) {
+ $ErrMsg = 'the expected method \'tbsdb_fetch\' is not found for the class '.$Cls.'.';
+ return false;
+ }
+ if (!method_exists($FctObj, 'tbsdb_close')) {
+ $ErrMsg = 'the expected method \'tbsdb_close\' is not found for the class '.$Cls.'.';
+ return false;
+ }
+ $FctInfo = ['type'=>5];
+ } else {
+ if ($FctCat==='r') { // Resource
+ $x = strtolower($FctStr);
+ $x = str_replace('-', '_', $x);
+ $Key = '';
+ $i = 0;
+ $iMax = strlen($x);
+ while ($i<$iMax) {
+ if (($x[$i]==='_') || (($x[$i]>='a') && ($x[$i]<='z')) || (($x[$i]>='0') && ($x[$i]<='9'))) {
+ $Key .= $x[$i];
+ $i++;
+ } else {
+ $i = $iMax;
+ }
+ }
+ } else {
+ $Key = $FctStr;
+ }
+
+ $FctInfo = ['open'=>'','fetch'=>'','close'=>''];
+ foreach ($FctInfo as $act=>$x) {
+ $FctName = 'tbsdb_'.$Key.'_'.$act;
+ if (function_exists($FctName)) {
+ $FctInfo[$act] = $FctName;
+ } else {
+ $err = true;
+ if ($act==='open') { // Try simplified key
+ $p = strpos($Key, '_');
+ if ($p!==false) {
+ $Key2 = substr($Key, 0, $p);
+ $FctName2 = 'tbsdb_'.$Key2.'_'.$act;
+ if (function_exists($FctName2)) {
+ $err = false;
+ $Key = $Key2;
+ $FctInfo[$act] = $FctName2;
+ }
+ }
+ }
+ if ($err) {
+ $ErrMsg = 'Data source Id \''.$FctStr.'\' is unsupported because function \''.$FctName.'\' is not found.';
+ return false;
+ }
+ }
+ }
+
+ $FctInfo['type'] = 3;
+ }
+ } else {
+ if ($FctCheck && ($this->FctPrefix!=='') && (strncmp($this->FctPrefix, $FctStr, strlen($this->FctPrefix))!==0)) {
+ $ErrMsg = 'user function \''.$FctStr.'\' does not match the allowed prefix.';
+ return false;
+ } elseif (!function_exists($FctStr)) {
+ $x = explode('.', $FctStr);
+ if (count($x)==2) {
+ if (class_exists($x[0])) {
+ $FctInfo = $x;
+ } else {
+ $ErrMsg = 'user function \''.$FctStr.'\' is not correct because \''.$x[0].'\' is not a class name.';
+ return false;
+ }
+ } else {
+ $ErrMsg = 'user function \''.$FctStr.'\' is not found.';
+ return false;
+ }
+ }
+ }
+
+ if ($Save) {
+ $this->_UserFctLst[$FctId] = $FctInfo;
+ }
+ return true;
+ }
+
+ public function meth_Misc_RunSubscript(&$CurrVal, $CurrPrm)
+ {
+ // Run a subscript without any local variable damage
+ return @include($this->_Subscript);
+ }
+
+ public function meth_Misc_Charset($Charset)
+ {
+ if ($Charset === '+') {
+ return;
+ }
+ $this->_CharsetFct = false;
+ if (is_string($Charset)) {
+ if (($Charset!=='') && ($Charset[0]==='=')) {
+ $ErrMsg = false;
+ $Charset = substr($Charset, 1);
+ if ($this->meth_Misc_UserFctCheck($Charset, 'f', $ErrMsg, $ErrMsg, false)) {
+ $this->_CharsetFct = true;
+ } else {
+ $this->meth_Misc_Alert('with charset option', $ErrMsg);
+ $Charset = '';
+ }
+ }
+ } elseif (is_array($Charset)) {
+ $this->_CharsetFct = true;
+ } elseif ($Charset===false) {
+ $this->Protect = false;
+ } else {
+ $this->meth_Misc_Alert('with charset option', 'the option value is not a string nor an array.');
+ $Charset = '';
+ }
+ $this->Charset = $Charset;
+ }
+
+ public function meth_PlugIn_RunAll(&$FctBank, &$ArgLst)
+ {
+ $OkAll = true;
+ foreach ($FctBank as $FctInfo) {
+ $Ok = call_user_func_array($FctInfo, $ArgLst);
+ if (!is_null($Ok)) {
+ $OkAll = ($OkAll && $Ok);
+ }
+ }
+ return $OkAll;
+ }
+
+ public function meth_PlugIn_Install($PlugInId, $ArgLst, $Auto)
+ {
+ $ErrMsg = 'with plug-in \''.$PlugInId.'\'';
+
+ if (class_exists($PlugInId)) {
+ // Create an instance
+ $IsObj = true;
+ $PiRef = new $PlugInId;
+ $PiRef->TBS = &$this;
+ if (!method_exists($PiRef, 'OnInstall')) {
+ return $this->meth_Misc_Alert($ErrMsg, 'OnInstall() method is not found.');
+ }
+ $FctRef = [&$PiRef,'OnInstall'];
+ } else {
+ $FctRef = 'tbspi_'.$PlugInId.'_OnInstall';
+ if (function_exists($FctRef)) {
+ $IsObj = false;
+ $PiRef = true;
+ } else {
+ return $this->meth_Misc_Alert($ErrMsg, 'no class named \''.$PlugInId.'\' is found, and no function named \''.$FctRef.'\' is found.');
+ }
+ }
+
+ $this->_PlugIns[$PlugInId] = &$PiRef;
+
+ $EventLst = call_user_func_array($FctRef, $ArgLst);
+ if (is_string($EventLst)) {
+ $EventLst = explode(',', $EventLst);
+ }
+ if (!is_array($EventLst)) {
+ return $this->meth_Misc_Alert($ErrMsg, 'OnInstall() method does not return an array.');
+ }
+
+ // Add activated methods
+ foreach ($EventLst as $Event) {
+ $Event = trim($Event);
+ if (!$this->meth_PlugIn_SetEvent($PlugInId, $Event)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function meth_PlugIn_SetEvent($PlugInId, $Event, $NewRef='')
+ {
+ // 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.
+
+ // Check the event's name
+ 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.');
+ }
+
+ $PropName = '_pi'.$Event;
+
+ if ($NewRef===false) {
+ // Disable the event
+ if (!isset($this->$PropName)) {
+ return false;
+ }
+ $PropRef = &$this->$PropName;
+ unset($PropRef[$PlugInId]);
+ return true;
+ }
+
+ // Prepare the reference to be called
+ $PiRef = &$this->_PlugIns[$PlugInId];
+ if (is_object($PiRef)) {
+ if ($NewRef==='') {
+ $NewRef = $Event;
+ }
+ 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.');
+ }
+ $FctRef = [&$PiRef, $NewRef];
+ } else {
+ $FctRef = ($NewRef==='') ? 'tbspi_'.$PlugInId.'_'.$Event : $NewRef;
+ if (!function_exists($FctRef)) {
+ return $this->meth_Misc_Alert('with plug-in \''.$PlugInId.'\'', 'The expected function \''.$FctRef.'\' is not found.');
+ }
+ }
+
+ // Save information into the corresponding property
+ if (!isset($this->$PropName)) {
+ $this->$PropName = [];
+ }
+ $PropRef = &$this->$PropName;
+ $PropRef[$PlugInId] = $FctRef;
+
+ // Flags saying if a plugin is installed
+ switch ($Event) {
+ case 'OnCommand': break;
+ case 'OnSpecialVar': break;
+ case 'OnOperation': break;
+ case 'OnFormat': $this->_piOnFrm_Ok = true; break;
+ default: $this->_PlugIns_Ok = true; break;
+ }
+
+ return true;
+ }
+
+ public static function meth_Misc_ToStr($Value)
+ {
+ if (is_string($Value)) {
+ return $Value;
+ } elseif (is_object($Value)) {
+ if (method_exists($Value, '__toString')) {
+ return $Value->__toString();
+ } elseif (is_a($Value, 'DateTime')) {
+ return $Value->format('c');
+ }
+ }
+ return @(string)$Value; // (string) is faster than strval() and settype()
+ }
+
+ public function meth_Misc_Format(&$Value, &$PrmLst)
+ {
+ // This function return the formated representation of a Date/Time or numeric variable using a 'VB like' format syntax instead of the PHP syntax.
+
+ $FrmStr = $PrmLst['frm'];
+ $CheckNumeric = true;
+ if (is_string($Value)) {
+ $Value = trim($Value);
+ }
+
+ if ($FrmStr==='') {
+ return '';
+ }
+ $Frm = self::f_Misc_FormatSave($FrmStr);
+
+ // Manage Multi format strings
+ if ($Frm['type']=='multi') {
+
+ // Select the format
+ if (is_numeric($Value)) {
+ if (is_string($Value)) {
+ $Value = 0.0 + $Value;
+ }
+ if ($Value>0) {
+ $FrmStr = &$Frm[0];
+ } elseif ($Value<0) {
+ $FrmStr = &$Frm[1];
+ if ($Frm['abs']) {
+ $Value = abs($Value);
+ }
+ } else { // zero
+ $FrmStr = &$Frm[2];
+ $Minus = '';
+ }
+ $CheckNumeric = false;
+ } else {
+ $Value = $this->meth_Misc_ToStr($Value);
+ if ($Value==='') {
+ return $Frm[3]; // Null value
+ } else {
+ $t = strtotime($Value); // We look if it's a date
+ if (($t===-1) || ($t===false)) { // Date not recognized
+ return $Frm[1];
+ } elseif ($t===943916400) { // Date to zero
+ return $Frm[2];
+ } else { // It's a date
+ $Value = $t;
+ $FrmStr = &$Frm[0];
+ }
+ }
+ }
+
+ // Retrieve the correct simple format
+ if ($FrmStr==='') {
+ return '';
+ }
+ $Frm = self::f_Misc_FormatSave($FrmStr);
+ }
+
+ switch ($Frm['type']) {
+ case 'num':
+ // NUMERIC
+ if ($CheckNumeric) {
+ if (is_numeric($Value)) {
+ if (is_string($Value)) {
+ $Value = 0.0 + $Value;
+ }
+ } else {
+ return $this->meth_Misc_ToStr($Value);
+ }
+ }
+ if ($Frm['PerCent']) {
+ $Value = $Value * 100;
+ }
+ $Value = number_format($Value, $Frm['DecNbr'], $Frm['DecSep'], $Frm['ThsSep']);
+ if ($Frm['Pad']!==false) {
+ $Value = str_pad($Value, $Frm['Pad'], '0', STR_PAD_LEFT);
+ }
+ if ($Frm['ThsRpl']!==false) {
+ $Value = str_replace($Frm['ThsSep'], $Frm['ThsRpl'], $Value);
+ }
+ $Value = substr_replace($Frm['Str'], $Value, $Frm['Pos'], $Frm['Len']);
+ return $Value;
+ break;
+ case 'date':
+ // DATE
+ if (is_object($Value)) {
+ $Value = $this->meth_Misc_ToStr($Value);
+ }
+ if (is_string($Value)) {
+ if ($Value==='') {
+ return '';
+ }
+ $x = strtotime($Value);
+ if (($x===-1) || ($x===false)) {
+ if (!is_numeric($Value)) {
+ $Value = 0;
+ }
+ } else {
+ $Value = &$x;
+ }
+ } else {
+ if (!is_numeric($Value)) {
+ return $this->meth_Misc_ToStr($Value);
+ }
+ }
+ if ($Frm['loc'] || isset($PrmLst['locale'])) {
+ $x = strftime($Frm['str_loc'], $Value);
+ $this->meth_Conv_Str($x, false); // may have accent
+ return $x;
+ } else {
+ return date($Frm['str_us'], $Value);
+ }
+ break;
+ default:
+ return $Frm['string'];
+ break;
+ }
+ }
+
+ // Simply update an array
+ public static function f_Misc_UpdateArray(&$array, $numerical, $v, $d)
+ {
+ if (!is_array($v)) {
+ if (is_null($v)) {
+ $array = [];
+ return;
+ } else {
+ $v = [$v=>$d];
+ }
+ }
+ foreach ($v as $p=>$a) {
+ if ($numerical===true) { // numerical keys
+ if (is_string($p)) {
+ // syntax: item => true/false
+ $i = array_search($p, $array, true);
+ if ($i===false) {
+ if (!is_null($a)) {
+ $array[] = $p;
+ }
+ } else {
+ if (is_null($a)) {
+ array_splice($array, $i, 1);
+ }
+ }
+ } else {
+ // syntax: i => item
+ $i = array_search($a, $array, true);
+ if ($i==false) {
+ $array[] = $a;
+ }
+ }
+ } else { // string keys
+ if (is_null($a)) {
+ unset($array[$p]);
+ } elseif ($numerical==='frm') {
+ self::f_Misc_FormatSave($a, $p);
+ } else {
+ $array[$p] = $a;
+ }
+ }
+ }
+ }
+
+ public static function f_Misc_FormatSave(&$FrmStr, $Alias='')
+ {
+ $FormatLst = &$GLOBALS['_TBS_FormatLst'];
+
+ if (isset($FormatLst[$FrmStr])) {
+ if ($Alias!='') {
+ $FormatLst[$Alias] = &$FormatLst[$FrmStr];
+ }
+ return $FormatLst[$FrmStr];
+ }
+
+ if (strpos($FrmStr, '|')!==false) {
+
+ // Multi format
+ $Frm = explode('|', $FrmStr); // syntax: Postive|Negative|Zero|Null
+ $FrmNbr = count($Frm);
+ $Frm['abs'] = ($FrmNbr>1);
+ if ($FrmNbr<3) {
+ $Frm[2] = &$Frm[0];
+ } // zero
+ if ($FrmNbr<4) {
+ $Frm[3] = '';
+ } // null
+ $Frm['type'] = 'multi';
+ $FormatLst[$FrmStr] = $Frm;
+ } elseif (($nPosEnd = strrpos($FrmStr, '0'))!==false) {
+
+ // Numeric format
+ $nDecSep = '.';
+ $nDecNbr = 0;
+ $nDecOk = true;
+ $nPad = false;
+ $nPadZ = 0;
+
+ if (substr($FrmStr, $nPosEnd+1, 1)==='.') {
+ $nPosEnd++;
+ $nPos = $nPosEnd;
+ $nPadZ = 1;
+ } else {
+ $nPos = $nPosEnd - 1;
+ while (($nPos>=0) && ($FrmStr[$nPos]==='0')) {
+ $nPos--;
+ }
+ if (($nPos>=1) && ($FrmStr[$nPos-1]==='0')) {
+ $nDecSep = $FrmStr[$nPos];
+ $nDecNbr = $nPosEnd - $nPos;
+ } else {
+ $nDecOk = false;
+ }
+ }
+
+ // Thousand separator
+ $nThsSep = '';
+ $nThsRpl = false;
+ if (($nDecOk) && ($nPos>=5)) {
+ if ((substr($FrmStr, $nPos-3, 3)==='000') && ($FrmStr[$nPos-4]!=='0')) {
+ $p = strrpos(substr($FrmStr, 0, $nPos-4), '0');
+ if ($p!==false) {
+ $len = $nPos-4-$p;
+ $x = substr($FrmStr, $p+1, $len);
+ if ($len>1) {
+ // for compatibility for number_format() with PHP < 5.4.0
+ $nThsSep = ($nDecSep=='*') ? '.' : '*';
+ $nThsRpl = $x;
+ } else {
+ $nThsSep = $x;
+ }
+ $nPos = $p+1;
+ }
+ }
+ }
+
+ // Pass next zero
+ if ($nDecOk) {
+ $nPos--;
+ }
+ while (($nPos>=0) && ($FrmStr[$nPos]==='0')) {
+ $nPos--;
+ }
+
+ $nLen = $nPosEnd-$nPos;
+ if (($nThsSep==='') && ($nLen>($nDecNbr+$nPadZ+1))) {
+ $nPad = $nLen - $nPadZ;
+ }
+
+ // Percent
+ $nPerCent = (strpos($FrmStr, '%')===false) ? false : true;
+
+ $FormatLst[$FrmStr] = ['type'=>'num','Str'=>$FrmStr,'Pos'=>($nPos+1),'Len'=>$nLen,'ThsSep'=>$nThsSep,'ThsRpl'=>$nThsRpl,'DecSep'=>$nDecSep,'DecNbr'=>$nDecNbr,'PerCent'=>$nPerCent,'Pad'=>$nPad];
+ } else {
+ // Date format
+ $x = $FrmStr;
+ $FrmPHP = '';
+ $FrmLOC = '';
+ $StrIn = false;
+ $Cnt = 0;
+ $i = strpos($FrmStr, '(locale)');
+ $Locale = ($i!==false);
+ if ($Locale) {
+ $x = substr_replace($x, '', $i, 8);
+ }
+
+ $iEnd = strlen($x);
+ for ($i=0;$i<$iEnd;$i++) {
+ if ($StrIn) {
+ // We are in a string part
+ if ($x[$i]==='"') {
+ if (substr($x, $i+1, 1)==='"') {
+ $FrmPHP .= '\\"'; // protected char
+ $FrmLOC .= $x[$i];
+ $i++;
+ } else {
+ $StrIn = false;
+ }
+ } else {
+ $FrmPHP .= '\\'.$x[$i]; // protected char
+ $FrmLOC .= $x[$i];
+ }
+ } else {
+ if ($x[$i]==='"') {
+ $StrIn = true;
+ } else {
+ $Cnt++;
+ if (strcasecmp(substr($x, $i, 2), 'hh')===0) {
+ $FrmPHP .= 'H';
+ $FrmLOC .= '%H';
+ $i += 1;
+ } elseif (strcasecmp(substr($x, $i, 2), 'hm')===0) {
+ $FrmPHP .= 'h';
+ $FrmLOC .= '%I';
+ $i += 1;
+ } // for compatibility
+ elseif (strcasecmp(substr($x, $i, 1), 'h')===0) {
+ $FrmPHP .= 'G';
+ $FrmLOC .= '%H';
+ } elseif (strcasecmp(substr($x, $i, 2), 'rr')===0) {
+ $FrmPHP .= 'h';
+ $FrmLOC .= '%I';
+ $i += 1;
+ } elseif (strcasecmp(substr($x, $i, 1), 'r')===0) {
+ $FrmPHP .= 'g';
+ $FrmLOC .= '%I';
+ } elseif (strcasecmp(substr($x, $i, 4), 'ampm')===0) {
+ $FrmPHP .= substr($x, $i, 1);
+ $FrmLOC .= '%p';
+ $i += 3;
+ } // $Fmp = 'A' or 'a'
+ elseif (strcasecmp(substr($x, $i, 2), 'nn')===0) {
+ $FrmPHP .= 'i';
+ $FrmLOC .= '%M';
+ $i += 1;
+ } elseif (strcasecmp(substr($x, $i, 2), 'ss')===0) {
+ $FrmPHP .= 's';
+ $FrmLOC .= '%S';
+ $i += 1;
+ } elseif (strcasecmp(substr($x, $i, 2), 'xx')===0) {
+ $FrmPHP .= 'S';
+ $FrmLOC .= '' ;
+ $i += 1;
+ } elseif (strcasecmp(substr($x, $i, 4), 'yyyy')===0) {
+ $FrmPHP .= 'Y';
+ $FrmLOC .= '%Y';
+ $i += 3;
+ } elseif (strcasecmp(substr($x, $i, 2), 'yy')===0) {
+ $FrmPHP .= 'y';
+ $FrmLOC .= '%y';
+ $i += 1;
+ } elseif (strcasecmp(substr($x, $i, 4), 'mmmm')===0) {
+ $FrmPHP .= 'F';
+ $FrmLOC .= '%B';
+ $i += 3;
+ } elseif (strcasecmp(substr($x, $i, 3), 'mmm')===0) {
+ $FrmPHP .= 'M';
+ $FrmLOC .= '%b';
+ $i += 2;
+ } elseif (strcasecmp(substr($x, $i, 2), 'mm')===0) {
+ $FrmPHP .= 'm';
+ $FrmLOC .= '%m';
+ $i += 1;
+ } elseif (strcasecmp(substr($x, $i, 1), 'm')===0) {
+ $FrmPHP .= 'n';
+ $FrmLOC .= '%m';
+ } elseif (strcasecmp(substr($x, $i, 4), 'wwww')===0) {
+ $FrmPHP .= 'l';
+ $FrmLOC .= '%A';
+ $i += 3;
+ } elseif (strcasecmp(substr($x, $i, 3), 'www')===0) {
+ $FrmPHP .= 'D';
+ $FrmLOC .= '%a';
+ $i += 2;
+ } elseif (strcasecmp(substr($x, $i, 1), 'w')===0) {
+ $FrmPHP .= 'w';
+ $FrmLOC .= '%u';
+ } elseif (strcasecmp(substr($x, $i, 4), 'dddd')===0) {
+ $FrmPHP .= 'l';
+ $FrmLOC .= '%A';
+ $i += 3;
+ } elseif (strcasecmp(substr($x, $i, 3), 'ddd')===0) {
+ $FrmPHP .= 'D';
+ $FrmLOC .= '%a';
+ $i += 2;
+ } elseif (strcasecmp(substr($x, $i, 2), 'dd')===0) {
+ $FrmPHP .= 'd';
+ $FrmLOC .= '%d';
+ $i += 1;
+ } elseif (strcasecmp(substr($x, $i, 1), 'd')===0) {
+ $FrmPHP .= 'j';
+ $FrmLOC .= '%d';
+ } else {
+ $FrmPHP .= '\\'.$x[$i]; // protected char
+ $FrmLOC .= $x[$i]; // protected char
+ $Cnt--;
+ }
+ }
+ }
+ }
+
+ if ($Cnt>0) {
+ $FormatLst[$FrmStr] = ['type'=>'date','str_us'=>$FrmPHP,'str_loc'=>$FrmLOC,'loc'=>$Locale];
+ } else {
+ $FormatLst[$FrmStr] = ['type'=>'else','string'=>$FrmStr];
+ }
+ }
+
+ if ($Alias!='') {
+ $FormatLst[$Alias] = &$FormatLst[$FrmStr];
+ }
+
+ return $FormatLst[$FrmStr];
+ }
+
+ public static function f_Misc_ConvSpe(&$Loc)
+ {
+ if ($Loc->ConvMode!==2) {
+ $Loc->ConvMode = 2;
+ $Loc->ConvEsc = false;
+ $Loc->ConvWS = false;
+ $Loc->ConvJS = false;
+ $Loc->ConvUrl = false;
+ $Loc->ConvUtf8 = false;
+ }
+ }
+
+ public static function f_Misc_CheckArgLst(&$Str)
+ {
+ $ArgLst = [];
+ if (substr($Str, -1, 1)===')') {
+ $pos = strpos($Str, '(');
+ if ($pos!==false) {
+ $ArgLst = explode(',', substr($Str, $pos+1, strlen($Str)-$pos-2));
+ $Str = substr($Str, 0, $pos);
+ }
+ }
+ return $ArgLst;
+ }
+
+ public static function f_Misc_CheckCondition($Str)
+ {
+ // Check if an expression like "exrp1=expr2" is true or false.
+
+ $StrZ = $Str; // same string but without protected data
+ $Max = strlen($Str)-1;
+ $p = strpos($Str, '\'');
+ if ($Esc=($p!==false)) {
+ $In = true;
+ for ($p=$p+1;$p<=$Max;$p++) {
+ if ($StrZ[$p]==='\'') {
+ $In = !$In;
+ } elseif ($In) {
+ $StrZ[$p] = 'z';
+ }
+ }
+ }
+
+ // Find operator and position
+ $Ope = '=';
+ $Len = 1;
+ $p = strpos($StrZ, $Ope);
+ if ($p===false) {
+ $Ope = '+';
+ $p = strpos($StrZ, $Ope);
+ if ($p===false) {
+ return false;
+ }
+ if (($p>0) && ($StrZ[$p-1]==='-')) {
+ $Ope = '-+';
+ $p--;
+ $Len=2;
+ } elseif (($p<$Max) && ($StrZ[$p+1]==='-')) {
+ $Ope = '+-';
+ $Len=2;
+ } else {
+ return false;
+ }
+ } else {
+ if ($p>0) {
+ $x = $StrZ[$p-1];
+ if ($x==='!') {
+ $Ope = '!=';
+ $p--;
+ $Len=2;
+ } elseif ($x==='~') {
+ $Ope = '~=';
+ $p--;
+ $Len=2;
+ } elseif ($p<$Max) {
+ $y = $StrZ[$p+1];
+ if ($y==='=') {
+ $Len=2;
+ } elseif (($x==='+') && ($y==='-')) {
+ $Ope = '+=-';
+ $p--;
+ $Len=3;
+ } elseif (($x==='-') && ($y==='+')) {
+ $Ope = '-=+';
+ $p--;
+ $Len=3;
+ }
+ } else {
+ }
+ }
+ }
+
+ // Read values
+ $Val1 = trim(substr($Str, 0, $p));
+ $Val2 = trim(substr($Str, $p+$Len));
+ if ($Esc) {
+ $Nude1 = self::f_Misc_DelDelimiter($Val1, '\'');
+ $Nude2 = self::f_Misc_DelDelimiter($Val2, '\'');
+ } else {
+ $Nude1 = $Nude2 = false;
+ }
+
+ // Compare values
+ if ($Ope==='=') {
+ return (strcasecmp($Val1, $Val2)==0);
+ } elseif ($Ope==='!=') {
+ return (strcasecmp($Val1, $Val2)!=0);
+ } elseif ($Ope==='~=') {
+ return (preg_match($Val2, $Val1)>0);
+ } else {
+ if ($Nude1) {
+ $Val1='0'+$Val1;
+ }
+ if ($Nude2) {
+ $Val2='0'+$Val2;
+ }
+ if ($Ope==='+-') {
+ return ($Val1>$Val2);
+ } elseif ($Ope==='-+') {
+ return ($Val1 < $Val2);
+ } elseif ($Ope==='+=-') {
+ return ($Val1 >= $Val2);
+ } elseif ($Ope==='-=+') {
+ return ($Val1<=$Val2);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public static function f_Misc_DelDelimiter(&$Txt, $Delim)
+ {
+ // Delete the string delimiters
+ $len = strlen($Txt);
+ if (($len>1) && ($Txt[0]===$Delim)) {
+ if ($Txt[$len-1]===$Delim) {
+ $Txt = substr($Txt, 1, $len-2);
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public static function f_Misc_GetFile(&$Res, &$File, $LastFile='', $IncludePath=false, $Contents=true)
+ {
+ // Load the content of a file into the text variable.
+
+ $Res = '';
+ $fd = self::f_Misc_TryFile($File, false);
+ if ($fd===false) {
+ if (is_array($IncludePath)) {
+ foreach ($IncludePath as $d) {
+ $fd = self::f_Misc_TryFile($File, $d);
+ if ($fd!==false) {
+ break;
+ }
+ }
+ }
+ if (($fd===false) && ($LastFile!='')) {
+ $fd = self::f_Misc_TryFile($File, dirname($LastFile));
+ }
+ if ($fd===false) {
+ return false;
+ }
+ }
+
+ $fs = fstat($fd);
+ if ($Contents) {
+ // Return contents
+ if (isset($fs['size'])) {
+ if ($fs['size']>0) {
+ $Res = fread($fd, $fs['size']);
+ }
+ } else {
+ while (!feof($fd)) {
+ $Res .= fread($fd, 4096);
+ }
+ }
+ } else {
+ // Return stats
+ $Res = $fs;
+ }
+
+ fclose($fd);
+ return true;
+ }
+
+ public static function f_Misc_TryFile(&$File, $Dir)
+ {
+ if ($Dir==='') {
+ return false;
+ }
+ $FileSearch = ($Dir===false) ? $File : $Dir.'/'.$File;
+ // 'rb' if binary for some OS. fopen() uses include_path and search on the __FILE__ directory while file_exists() doesn't.
+ $f = @fopen($FileSearch, 'r', true);
+ if ($f!==false) {
+ $File = $FileSearch;
+ }
+ return $f;
+ }
+
+ public static function f_Loc_PrmRead(&$Txt, $Pos, $XmlTag, $DelimChrs, $BegStr, $EndStr, &$Loc, &$PosEnd, $WithPos=false)
+ {
+ $BegLen = strlen($BegStr);
+ $BegChr = $BegStr[0];
+ $BegIs1 = ($BegLen===1);
+
+ $DelimIdx = false;
+ $DelimCnt = 0;
+ $DelimChr = '';
+ $BegCnt = 0;
+ $SubName = $Loc->SubOk;
+
+ $Status = 0; // 0: name not started, 1: name started, 2: name ended, 3: equal found, 4: value started
+ $PosName = 0;
+ $PosNend = 0;
+ $PosVal = 0;
+
+ // Variables for checking the loop
+ $PosEnd = strpos($Txt, $EndStr, $Pos);
+ if ($PosEnd===false) {
+ return;
+ }
+ $Continue = ($Pos<$PosEnd);
+
+ while ($Continue) {
+ $Chr = $Txt[$Pos];
+
+ if ($DelimIdx) { // Reading in the string
+
+ if ($Chr===$DelimChr) { // Quote found
+ if ($Chr===$Txt[$Pos+1]) { // Double Quote => the string continue with un-double the quote
+ $Pos++;
+ } else { // Simple Quote => end of string
+ $DelimIdx = false;
+ }
+ }
+ } else { // Reading outside the string
+
+ if ($BegCnt===0) {
+
+ // Analyzing parameters
+ $CheckChr = false;
+ if (($Chr===' ') || ($Chr==="\r") || ($Chr==="\n")) {
+ if ($Status===1) {
+ if ($SubName && ($XmlTag===false)) {
+ // Accept spaces in TBS subname.
+ } else {
+ $Status = 2;
+ $PosNend = $Pos;
+ }
+ } elseif ($XmlTag && ($Status===4)) {
+ self::f_Loc_PrmCompute($Txt, $Loc, $SubName, $Status, $XmlTag, $DelimChr, $DelimCnt, $PosName, $PosNend, $PosVal, $Pos, $WithPos);
+ $Status = 0;
+ }
+ } elseif (($XmlTag===false) && ($Chr===';')) {
+ self::f_Loc_PrmCompute($Txt, $Loc, $SubName, $Status, $XmlTag, $DelimChr, $DelimCnt, $PosName, $PosNend, $PosVal, $Pos, $WithPos);
+ $Status = 0;
+ } elseif ($Status===4) {
+ $CheckChr = true;
+ } elseif ($Status===3) {
+ $Status = 4;
+ $DelimCnt = 0;
+ $PosVal = $Pos;
+ $CheckChr = true;
+ } elseif ($Status===2) {
+ if ($Chr==='=') {
+ $Status = 3;
+ } elseif ($XmlTag) {
+ self::f_Loc_PrmCompute($Txt, $Loc, $SubName, $Status, $XmlTag, $DelimChr, $DelimCnt, $PosName, $PosNend, $PosVal, $Pos, $WithPos);
+ $Status = 1;
+ $PosName = $Pos;
+ $CheckChr = true;
+ } else {
+ $Status = 4;
+ $DelimCnt = 0;
+ $PosVal = $Pos;
+ $CheckChr = true;
+ }
+ } elseif ($Status===1) {
+ if ($Chr==='=') {
+ $Status = 3;
+ $PosNend = $Pos;
+ } else {
+ $CheckChr = true;
+ }
+ } else {
+ $Status = 1;
+ $PosName = $Pos;
+ $CheckChr = true;
+ }
+
+ if ($CheckChr) {
+ $DelimIdx = strpos($DelimChrs, $Chr);
+ if ($DelimIdx===false) {
+ if ($Chr===$BegChr) {
+ if ($BegIs1) {
+ $BegCnt++;
+ } elseif (substr($Txt, $Pos, $BegLen)===$BegStr) {
+ $BegCnt++;
+ }
+ }
+ } else {
+ $DelimChr = $DelimChrs[$DelimIdx];
+ $DelimCnt++;
+ $DelimIdx = true;
+ }
+ }
+ } else {
+ if ($Chr===$BegChr) {
+ if ($BegIs1) {
+ $BegCnt++;
+ } elseif (substr($Txt, $Pos, $BegLen)===$BegStr) {
+ $BegCnt++;
+ }
+ }
+ }
+ }
+
+ // Next char
+ $Pos++;
+
+ // We check if it's the end
+ if ($Pos===$PosEnd) {
+ if ($XmlTag) {
+ $Continue = false;
+ } elseif ($DelimIdx===false) {
+ if ($BegCnt>0) {
+ $BegCnt--;
+ } else {
+ $Continue = false;
+ }
+ }
+ if ($Continue) {
+ $PosEnd = strpos($Txt, $EndStr, $PosEnd+1);
+ if ($PosEnd===false) {
+ return;
+ }
+ } else {
+ if ($XmlTag && ($Txt[$Pos-1]==='/')) {
+ $Pos--;
+ } // In case last attribute is stuck to "/>"
+ self::f_Loc_PrmCompute($Txt, $Loc, $SubName, $Status, $XmlTag, $DelimChr, $DelimCnt, $PosName, $PosNend, $PosVal, $Pos, $WithPos);
+ }
+ }
+ }
+
+ $PosEnd = $PosEnd + (strlen($EndStr)-1);
+ }
+
+ public static function f_Loc_PrmCompute(&$Txt, &$Loc, &$SubName, $Status, $XmlTag, $DelimChr, $DelimCnt, $PosName, $PosNend, $PosVal, $Pos, $WithPos)
+ {
+ if ($Status===0) {
+ $SubName = false;
+ } else {
+ if ($Status===1) {
+ $x = substr($Txt, $PosName, $Pos-$PosName);
+ } else {
+ $x = substr($Txt, $PosName, $PosNend-$PosName);
+ }
+ if ($XmlTag) {
+ $x = strtolower($x);
+ }
+ if ($SubName) {
+ $Loc->SubName = trim($x);
+ $SubName = false;
+ } else {
+ if ($Status===4) {
+ $v = trim(substr($Txt, $PosVal, $Pos-$PosVal));
+ if ($DelimCnt===1) { // Delete quotes inside the value
+ if ($v[0]===$DelimChr) {
+ $len = strlen($v);
+ if ($v[$len-1]===$DelimChr) {
+ $v = substr($v, 1, $len-2);
+ $v = str_replace($DelimChr.$DelimChr, $DelimChr, $v);
+ }
+ }
+ }
+ } else {
+ $v = true;
+ }
+ if ($x==='if') {
+ self::f_Loc_PrmIfThen($Loc, true, $v);
+ } elseif ($x==='then') {
+ self::f_Loc_PrmIfThen($Loc, false, $v);
+ } else {
+ $Loc->PrmLst[$x] = $v;
+ if ($WithPos) {
+ $Loc->PrmPos[$x] = [$PosName,$PosNend,$PosVal,$Pos,$DelimChr,$DelimCnt];
+ }
+ }
+ }
+ }
+ }
+
+ public static function f_Loc_PrmIfThen(&$Loc, $IsIf, $Val)
+ {
+ $nbr = &$Loc->PrmIfNbr;
+ if ($nbr===false) {
+ $nbr = 0;
+ $Loc->PrmIf = [];
+ $Loc->PrmIfVar = [];
+ $Loc->PrmThen = [];
+ $Loc->PrmThenVar = [];
+ $Loc->PrmElseVar = true;
+ }
+ if ($IsIf) {
+ $nbr++;
+ $Loc->PrmIf[$nbr] = $Val;
+ $Loc->PrmIfVar[$nbr] = true;
+ } else {
+ $nbr2 = $nbr;
+ if ($nbr2===false) {
+ $nbr2 = 1;
+ } // Only the first 'then' can be placed before its 'if'. This is for compatibility.
+ $Loc->PrmThen[$nbr2] = $Val;
+ $Loc->PrmThenVar[$nbr2] = true;
+ }
+ }
+
+ public static function f_Loc_EnlargeToStr(&$Txt, &$Loc, $StrBeg, $StrEnd)
+ {
+ /*
+ This function enables to enlarge the pos limits of the Locator.
+ If the search result is not correct, $PosBeg must not change its value, and $PosEnd must be False.
+ This is because of the calling function.
+ */
+
+ // Search for the begining string
+ $Pos = $Loc->PosBeg;
+ $Ok = false;
+ do {
+ $Pos = strrpos(substr($Txt, 0, $Pos), $StrBeg[0]);
+ if ($Pos!==false) {
+ if (substr($Txt, $Pos, strlen($StrBeg))===$StrBeg) {
+ $Ok = true;
+ }
+ }
+ } while ((!$Ok) && ($Pos!==false));
+
+ if ($Ok) {
+ $PosEnd = strpos($Txt, $StrEnd, $Loc->PosEnd + 1);
+ if ($PosEnd===false) {
+ $Ok = false;
+ } else {
+ $Loc->PosBeg = $Pos;
+ $Loc->PosEnd = $PosEnd + strlen($StrEnd) - 1;
+ }
+ }
+
+ return $Ok;
+ }
+
+ public static function f_Loc_EnlargeToTag(&$Txt, &$Loc, $TagStr, $RetInnerSrc)
+ {
+ //Modify $Loc, return false if tags not found, returns the inner source of tag if $RetInnerSrc=true
+
+ $AliasLst = &$GLOBALS['_TBS_BlockAlias'];
+
+ // Analyze string
+ $Ref = 0;
+ $LevelStop = 0;
+ $i = 0;
+ $TagFct = [];
+ $TagLst = [];
+ $TagBnd = [];
+ while ($TagStr!=='') {
+ // get next tag
+ $p = strpos($TagStr, '+');
+ if ($p===false) {
+ $t = $TagStr;
+ $TagStr = '';
+ } else {
+ $t = substr($TagStr, 0, $p);
+ $TagStr = substr($TagStr, $p+1);
+ }
+ // Check parentheses, relative position and single tag
+ do {
+ $t = trim($t);
+ $e = strlen($t) - 1; // pos of last char
+ if (($e>1) && ($t[0]==='(') && ($t[$e]===')')) {
+ if ($Ref===0) {
+ $Ref = $i;
+ }
+ if ($Ref===$i) {
+ $LevelStop++;
+ }
+ $t = substr($t, 1, $e-1);
+ } else {
+ if (($e>=0) && ($t[$e]==='/')) {
+ $t = substr($t, 0, $e);
+ } // for compatibilty
+ $e = false;
+ }
+ } while ($e!==false);
+ // Check for multiples
+ $p = strpos($t, '*');
+ if ($p!==false) {
+ $n = intval(substr($t, 0, $p));
+ $t = substr($t, $p + 1);
+ $n = max($n, 1); // prevent for error: minimum valu is 1
+ $TagStr = str_repeat($t . '+', $n-1) . $TagStr;
+ }
+ // Reference
+ if (($t==='.') && ($Ref===0)) {
+ $Ref = $i;
+ }
+ // Take of the (!) prefix
+ $b = '';
+ if (($t!=='') && ($t[0]==='!')) {
+ $t = substr($t, 1);
+ $b = '!';
+ }
+ // Alias
+ $a = false;
+ if (isset($AliasLst[$t])) {
+ $a = $AliasLst[$t]; // a string or a function
+ if (is_string($a)) {
+ if ($i>999) {
+ return false;
+ } // prevent from circular alias
+ $TagStr = $b . $a . (($TagStr==='') ? '' : '+') . $TagStr;
+ $t = false;
+ }
+ }
+ if ($t!==false) {
+ $TagLst[$i] = $t; // with prefix ! if specified
+ $TagFct[$i] = $a;
+ $TagBnd[$i] = ($b==='');
+ $i++;
+ }
+ }
+
+ $TagMax = $i-1;
+
+ // Find tags that embeds the locator
+ if ($LevelStop===0) {
+ $LevelStop = 1;
+ }
+
+ // First tag of reference
+ if ($TagLst[$Ref] === '.') {
+ $TagO = new clsTbsLocator;
+ $TagO->PosBeg = $Loc->PosBeg;
+ $TagO->PosEnd = $Loc->PosEnd;
+ $PosBeg = $Loc->PosBeg;
+ $PosEnd = $Loc->PosEnd;
+ } else {
+ $TagO = self::f_Loc_Enlarge_Find($Txt, $TagLst[$Ref], $TagFct[$Ref], $Loc->PosBeg-1, false, $LevelStop);
+ if ($TagO===false) {
+ return false;
+ }
+ $PosBeg = $TagO->PosBeg;
+ $LevelStop += -$TagO->RightLevel; // RightLevel=1 only if the tag is single and embeds $Loc, otherwise it is 0
+ if ($LevelStop>0) {
+ $TagC = self::f_Loc_Enlarge_Find($Txt, $TagLst[$Ref], $TagFct[$Ref], $Loc->PosEnd+1, true, -$LevelStop);
+ if ($TagC==false) {
+ return false;
+ }
+ $PosEnd = $TagC->PosEnd;
+ $InnerLim = $TagC->PosBeg;
+ if ((!$TagBnd[$Ref]) && ($TagMax==0)) {
+ $PosBeg = $TagO->PosEnd + 1;
+ $PosEnd = $TagC->PosBeg - 1;
+ }
+ } else {
+ $PosEnd = $TagO->PosEnd;
+ $InnerLim = $PosEnd + 1;
+ }
+ }
+
+ $RetVal = true;
+ if ($RetInnerSrc) {
+ $RetVal = '';
+ if ($Loc->PosBeg>$TagO->PosEnd) {
+ $RetVal .= substr($Txt, $TagO->PosEnd+1, min($Loc->PosBeg, $InnerLim)-$TagO->PosEnd-1);
+ }
+ if ($Loc->PosEnd<$InnerLim) {
+ $RetVal .= substr($Txt, max($Loc->PosEnd, $TagO->PosEnd)+1, $InnerLim-max($Loc->PosEnd, $TagO->PosEnd)-1);
+ }
+ }
+
+ // Other tags forward
+ $TagC = true;
+ for ($i=$Ref+1;$i<=$TagMax;$i++) {
+ $x = $TagLst[$i];
+ if (($x!=='') && ($TagC!==false)) {
+ $level = ($TagBnd[$i]) ? 0 : 1;
+ $TagC = self::f_Loc_Enlarge_Find($Txt, $x, $TagFct[$i], $PosEnd+1, true, $level);
+ if ($TagC!==false) {
+ $PosEnd = ($TagBnd[$i]) ? $TagC->PosEnd : $TagC->PosBeg -1 ;
+ }
+ }
+ }
+
+ // Other tags backward
+ $TagO = true;
+ for ($i=$Ref-1;$i>=0;$i--) {
+ $x = $TagLst[$i];
+ if (($x!=='') && ($TagO!==false)) {
+ $level = ($TagBnd[$i]) ? 0 : -1;
+ $TagO = self::f_Loc_Enlarge_Find($Txt, $x, $TagFct[$i], $PosBeg-1, false, $level);
+ if ($TagO!==false) {
+ $PosBeg = ($TagBnd[$i]) ? $TagO->PosBeg : $TagO->PosEnd + 1;
+ }
+ }
+ }
+
+ $Loc->PosBeg = $PosBeg;
+ $Loc->PosEnd = $PosEnd;
+ return $RetVal;
+ }
+
+ public static function f_Loc_Enlarge_Find($Txt, $Tag, $Fct, $Pos, $Forward, $LevelStop)
+ {
+ if ($Fct===false) {
+ return self::f_Xml_FindTag($Txt, $Tag, (!$Forward), $Pos, $Forward, $LevelStop, false);
+ } else {
+ $p = call_user_func_array($Fct, [$Tag,$Txt,$Pos,$Forward,$LevelStop]);
+ if ($p===false) {
+ return false;
+ } else {
+ return (object) ['PosBeg'=>$p, 'PosEnd'=>$p, 'RightLevel'=> 0]; // it's a trick
+ }
+ }
+ }
+
+ public static function f_Loc_AttBoolean($CurrVal, $AttTrue, $AttName)
+ {
+ // Return the good value for a boolean attribute
+ if ($AttTrue===true) {
+ if (self::meth_Misc_ToStr($CurrVal)==='') {
+ return '';
+ } else {
+ return $AttName;
+ }
+ } elseif (self::meth_Misc_ToStr($CurrVal)===$AttTrue) {
+ return $AttName;
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Affects the positions of a list of locators regarding to a specific moving locator.
+ */
+ public static function f_Loc_Moving(&$LocM, &$LocLst)
+ {
+ foreach ($LocLst as &$Loc) {
+ if ($Loc !== $LocM) {
+ if ($Loc->PosBeg >= $LocM->InsPos) {
+ $Loc->PosBeg += $LocM->InsLen;
+ $Loc->PosEnd += $LocM->InsLen;
+ }
+ if ($Loc->PosBeg > $LocM->DelPos) {
+ $Loc->PosBeg -= $LocM->DelLen;
+ $Loc->PosEnd -= $LocM->DelLen;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Sort the locators in the list. Apply the bubble algorithm.
+ * Deleted locators maked with DelMe.
+ * @param array $LocLst An array of locators.
+ * @param boolean $DelEmbd True to deleted locators that embded other ones.
+ * @param boolean $iFirst Index of the first item.
+ * @return integer Return the number of met embedding locators.
+ */
+ public static function f_Loc_Sort(&$LocLst, $DelEmbd, $iFirst = 0)
+ {
+ $iLast = $iFirst + count($LocLst) - 1;
+ $embd = 0;
+
+ for ($i = $iLast ; $i>=$iFirst ; $i--) {
+ $Loc = $LocLst[$i];
+ $d = (isset($Loc->DelMe) && $Loc->DelMe);
+ $b = $Loc->PosBeg;
+ $e = $Loc->PosEnd;
+ for ($j=$i+1; $j<=$iLast ; $j++) {
+ // If DelMe, then the loc will be put at the end and deleted
+ $jb = $LocLst[$j]->PosBeg;
+ if ($d || ($b > $jb)) {
+ $LocLst[$j-1] = $LocLst[$j];
+ $LocLst[$j] = $Loc;
+ } elseif ($e > $jb) {
+ $embd++;
+ if ($DelEmbd) {
+ $d = true;
+ $j--; // replay the current position
+ } else {
+ $j = $iLast; // quit the loop
+ }
+ } else {
+ $j = $iLast; // quit the loop
+ }
+ }
+ if ($d) {
+ unset($LocLst[$iLast]);
+ $iLast--;
+ }
+ }
+
+ return $embd;
+ }
+
+ /**
+ * Prepare all informations to move a locator according to parameter "att".
+ *
+ * @param $Txt
+ * @param $Loc
+ * @param mixed $MoveLocLst true to simple move the loc, or an array of loc to rearrange the list after the move.
+ * Note: rearrange doest not work with PHP4.
+ * @param bool $AttDelim
+ * @param bool $LocLst
+ * @return bool
+ */
+ public static function f_Xml_AttFind(&$Txt, &$Loc, $MoveLocLst=false, $AttDelim=false, $LocLst=false)
+ {
+ // att=div#class ; att=((div))#class ; att=+((div))#class
+
+ $Att = $Loc->PrmLst['att'];
+ unset($Loc->PrmLst['att']); // prevent from processing the field twice
+ $Loc->PrmLst['att;'] = $Att; // for debug
+
+ $p = strrpos($Att, '#');
+ if ($p===false) {
+ $TagLst = '';
+ } else {
+ $TagLst = substr($Att, 0, $p);
+ $Att = substr($Att, $p+1);
+ }
+
+ $Forward = (substr($TagLst, 0, 1)==='+');
+ if ($Forward) {
+ $TagLst = substr($TagLst, 1);
+ }
+ $TagLst = explode('+', $TagLst);
+
+ $iMax = count($TagLst)-1;
+ $WithPrm = false;
+ $LocO = &$Loc;
+ foreach ($TagLst as $i=>$Tag) {
+ $LevelStop = false;
+ while ((strlen($Tag)>1) && (substr($Tag, 0, 1)==='(') && (substr($Tag, -1, 1)===')')) {
+ if ($LevelStop===false) {
+ $LevelStop = 0;
+ }
+ $LevelStop++;
+ $Tag = trim(substr($Tag, 1, strlen($Tag)-2));
+ }
+ if ($i==$iMax) {
+ $WithPrm = true;
+ }
+ $Pos = ($Forward) ? $LocO->PosEnd+1 : $LocO->PosBeg-1;
+ unset($LocO);
+ $LocO = self::f_Xml_FindTag($Txt, $Tag, true, $Pos, $Forward, $LevelStop, $WithPrm, $WithPrm);
+ if ($LocO===false) {
+ return false;
+ }
+ }
+
+ $Loc->AttForward = $Forward;
+ $Loc->AttTagBeg = $LocO->PosBeg;
+ $Loc->AttTagEnd = $LocO->PosEnd;
+ $Loc->AttDelimChr = false;
+
+ if ($Att==='.') {
+ // this indicates that the TBS field is supposed to be inside an attribute's value
+ foreach ($LocO->PrmPos as $a=>$p) {
+ if (($p[0]<$Loc->PosBeg) && ($Loc->PosEnd<$p[3])) {
+ $Att = $a;
+ }
+ }
+ if ($Att==='.') {
+ return false;
+ }
+ }
+ $Loc->AttName = $Att;
+
+ $AttLC = strtolower($Att);
+ if (isset($LocO->PrmLst[$AttLC])) {
+ // The attribute is existing
+ $p = $LocO->PrmPos[$AttLC];
+ $Loc->AttBeg = $p[0];
+ $p[3]--;
+ while ($Txt[$p[3]]===' ') {
+ $p[3]--;
+ } // external end of the attribute, may has an extra spaces
+ $Loc->AttEnd = $p[3];
+ $Loc->AttDelimCnt = $p[5];
+ $Loc->AttDelimChr = $p[4];
+ if (($p[1]>$p[0]) && ($p[2]>$p[1])) {
+ //$Loc->AttNameEnd = $p[1];
+ $Loc->AttValBeg = $p[2];
+ } else { // attribute without value
+ //$Loc->AttNameEnd = $p[3];
+ $Loc->AttValBeg = false;
+ }
+ } else {
+ // The attribute is not yet existing
+ $Loc->AttDelimCnt = 0;
+ $Loc->AttBeg = false;
+ }
+
+ // Search for a delimitor
+ if (($Loc->AttDelimCnt==0) && (isset($LocO->PrmPos))) {
+ foreach ($LocO->PrmPos as $p) {
+ if ($p[5]>0) {
+ $Loc->AttDelimChr = $p[4];
+ }
+ }
+ }
+
+ if ($MoveLocLst) {
+ return self::f_Xml_AttMove($Txt, $Loc, $AttDelim, $MoveLocLst);
+ }
+
+ return true;
+ }
+
+ public static function f_Xml_AttMove(&$Txt, &$Loc, $AttDelim, &$MoveLocLst)
+ {
+ if ($AttDelim===false) {
+ $AttDelim = $Loc->AttDelimChr;
+ }
+ if ($AttDelim===false) {
+ $AttDelim = '"';
+ }
+
+ $DelPos = $Loc->PosBeg;
+ $DelLen = $Loc->PosEnd - $Loc->PosBeg + 1;
+ $Txt = substr_replace($Txt, '', $DelPos, $DelLen); // delete the current locator
+ if ($Loc->AttForward) {
+ $Loc->AttTagBeg += -$DelLen;
+ $Loc->AttTagEnd += -$DelLen;
+ } elseif ($Loc->PosBeg<$Loc->AttTagEnd) {
+ $Loc->AttTagEnd += -$DelLen;
+ }
+
+ $InsPos = false;
+ if ($Loc->AttBeg===false) {
+ $InsPos = $Loc->AttTagEnd;
+ if ($Txt[$InsPos-1]==='/') {
+ $InsPos--;
+ }
+ if ($Txt[$InsPos-1]===' ') {
+ $InsPos--;
+ }
+ $Ins1 = ' '.$Loc->AttName.'='.$AttDelim;
+ $Ins2 = $AttDelim;
+ $Loc->AttBeg = $InsPos + 1;
+ $Loc->AttValBeg = $InsPos + strlen($Ins1) - 1;
+ } else {
+ if ($Loc->PosEnd<$Loc->AttBeg) {
+ $Loc->AttBeg += -$DelLen;
+ }
+ if ($Loc->PosEnd<$Loc->AttEnd) {
+ $Loc->AttEnd += -$DelLen;
+ }
+ if ($Loc->AttValBeg===false) {
+ $InsPos = $Loc->AttEnd+1;
+ $Ins1 = '='.$AttDelim;
+ $Ins2 = $AttDelim;
+ $Loc->AttValBeg = $InsPos+1;
+ } elseif (isset($Loc->PrmLst['attadd'])) {
+ $InsPos = $Loc->AttEnd;
+ $Ins1 = ' ';
+ $Ins2 = '';
+ } else {
+ // value already existing
+ if ($Loc->PosEnd<$Loc->AttValBeg) {
+ $Loc->AttValBeg += -$DelLen;
+ }
+ $PosBeg = $Loc->AttValBeg;
+ $PosEnd = $Loc->AttEnd;
+ if ($Loc->AttDelimCnt>0) {
+ $PosBeg++;
+ $PosEnd--;
+ }
+ }
+ }
+
+ if ($InsPos===false) {
+ $InsLen = 0;
+ } else {
+ $InsTxt = $Ins1.'[]'.$Ins2;
+ $InsLen = strlen($InsTxt);
+ $PosBeg = $InsPos + strlen($Ins1);
+ $PosEnd = $PosBeg + 1;
+ $Txt = substr_replace($Txt, $InsTxt, $InsPos, 0);
+ $Loc->AttEnd = $InsPos + $InsLen - 1;
+ $Loc->AttTagEnd += $InsLen;
+ }
+
+ $Loc->PrevPosBeg = $Loc->PosBeg;
+ $Loc->PrevPosEnd = $Loc->PosEnd;
+ $Loc->PosBeg = $PosBeg;
+ $Loc->PosEnd = $PosEnd;
+ $Loc->AttBegM = ($Txt[$Loc->AttBeg-1]===' ') ? $Loc->AttBeg-1 : $Loc->AttBeg; // for magnet=#
+
+ // for CacheField
+ if (is_array($MoveLocLst)) {
+ $Loc->InsPos = $InsPos;
+ $Loc->InsLen = $InsLen;
+ $Loc->DelPos = $DelPos;
+ if ($Loc->InsPos < $Loc->DelPos) {
+ $Loc->DelPos += $InsLen;
+ }
+ $Loc->DelLen = $DelLen;
+ self::f_Loc_Moving($Loc, $MoveLocLst);
+ }
+
+ return true;
+ }
+
+ public static function f_Xml_Max(&$Txt, &$Nbr, $MaxEnd)
+ {
+ // Limit the number of HTML chars
+
+ $pMax = strlen($Txt)-1;
+ $p=0;
+ $n=0;
+ $in = false;
+ $ok = true;
+
+ while ($ok) {
+ if ($in) {
+ if ($Txt[$p]===';') {
+ $in = false;
+ $n++;
+ }
+ } else {
+ if ($Txt[$p]==='&') {
+ $in = true;
+ } else {
+ $n++;
+ }
+ }
+ if (($n>=$Nbr) || ($p>=$pMax)) {
+ $ok = false;
+ } else {
+ $p++;
+ }
+ }
+
+ if (($n>=$Nbr) && ($p<$pMax)) {
+ $Txt = substr($Txt, 0, $p).$MaxEnd;
+ }
+ }
+
+ public static function f_Xml_GetPart(&$Txt, $TagLst, $AllIfNothing=false)
+ {
+ // Returns parts of the XML/HTML content, default is BODY.
+
+ if (($TagLst===true) || ($TagLst==='')) {
+ $TagLst = 'body';
+ }
+
+ $x = '';
+ $nothing = true;
+ $TagLst = explode('+', $TagLst);
+
+ // Build a clean list of tags
+ foreach ($TagLst as $i=>$t) {
+ if ((substr($t, 0, 1)=='(') && (substr($t, -1, 1)==')')) {
+ $t = substr($t, 1, strlen($t)-2);
+ $Keep = true;
+ } else {
+ $Keep = false;
+ }
+ $TagLst[$i] = ['t'=>$t, 'k'=>$Keep, 'b'=>-1, 'e'=>-1, 's'=>false];
+ }
+
+ $PosOut = strlen($Txt);
+ $Pos = 0;
+
+ // Optimized search for all tag types
+ do {
+ // Search next positions of each tag type
+ $TagMin = false; // idx of the tag at first position
+ $PosMin = $PosOut; // pos of the tag at first position
+ foreach ($TagLst as $i=>$Tag) {
+ if ($Tag['b']<$Pos) {
+ $Loc = self::f_Xml_FindTag($Txt, $Tag['t'], true, $Pos, true, false, false);
+ if ($Loc===false) {
+ $Tag['b'] = $PosOut; // tag not found, no more search on this tag
+ } else {
+ $Tag['b'] = $Loc->PosBeg;
+ $Tag['e'] = $Loc->PosEnd;
+ $Tag['s'] = (substr($Txt, $Loc->PosEnd-1, 1)==='/'); // true if it's a single tag
+ }
+ $TagLst[$i] = $Tag; // update
+ }
+ if ($Tag['b']<$PosMin) {
+ $TagMin = $i;
+ $PosMin = $Tag['b'];
+ }
+ }
+
+ // Add the part of tag types
+ if ($TagMin!==false) {
+ $Tag = &$TagLst[$TagMin];
+ $Pos = $Tag['e']+1;
+ if ($Tag['s']) {
+ // single tag
+ if ($Tag['k']) {
+ $x .= substr($Txt, $Tag['b'], $Tag['e'] - $Tag['b'] + 1);
+ }
+ } else {
+ // search the closing tag
+ $Loc = self::f_Xml_FindTag($Txt, $Tag['t'], false, $Pos, true, false, false);
+ if ($Loc===false) {
+ $Tag['b'] = $PosOut; // closing tag not found, no more search on this tag
+ } else {
+ $nothing = false;
+ if ($Tag['k']) {
+ $x .= substr($Txt, $Tag['b'], $Loc->PosEnd - $Tag['b'] + 1);
+ } else {
+ $x .= substr($Txt, $Tag['e']+1, $Loc->PosBeg - $Tag['e'] - 1);
+ }
+ $Pos = $Loc->PosEnd + 1;
+ }
+ }
+ }
+ } while ($TagMin!==false);
+
+ if ($AllIfNothing && $nothing) {
+ return $Txt;
+ }
+ return $x;
+ }
+
+ /**
+ * Find the start of an XML tag. Used by OpenTBS.
+ * $Case=false can be useful for HTML.
+ * $Tag='' should work and found the start of the first tag.
+ * $Tag='/' should work and found the start of the first closing tag.
+ * Encapsulation levels are not featured yet.
+ */
+ public static function f_Xml_FindTagStart(&$Txt, $Tag, $Opening, $PosBeg, $Forward, $Case=true)
+ {
+ if ($Txt==='') {
+ return false;
+ }
+
+ $x = '<'.(($Opening) ? '' : '/').$Tag;
+ $xl = strlen($x);
+
+ $p = $PosBeg - (($Forward) ? 1 : -1);
+
+ if ($Case) {
+ do {
+ if ($Forward) {
+ $p = strpos($Txt, $x, $p+1);
+ } else {
+ $p = strrpos(substr($Txt, 0, $p+1), $x);
+ }
+ if ($p===false) {
+ return false;
+ }
+ /* COMPAT#6 */
+ $z = substr($Txt, $p+$xl, 1);
+ } while (($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') && ($Tag!=='/') && ($Tag!==''));
+ } else {
+ do {
+ if ($Forward) {
+ $p = stripos($Txt, $x, $p+1);
+ } else {
+ $p = strripos(substr($Txt, 0, $p+1), $x);
+ }
+ if ($p===false) {
+ return false;
+ }
+ /* COMPAT#7 */
+ $z = substr($Txt, $p+$xl, 1);
+ } while (($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/') && ($Tag!=='/') && ($Tag!==''));
+ }
+
+ return $p;
+ }
+
+ /**
+ * This function is a smart solution to find an XML tag.
+ * It allows to ignore full opening/closing couple of tags that could be inserted before the searched tag.
+ * It allows also to pass a number of encapsulations.
+ * To ignore encapsulation and opengin/closing just set $LevelStop=false.
+ * $Opening is used only when $LevelStop=false.
+ */
+ public static function f_Xml_FindTag(&$Txt, $Tag, $Opening, $PosBeg, $Forward, $LevelStop, $WithPrm, $WithPos=false)
+ {
+ if ($Tag==='_') { // New line
+ $p = self::f_Xml_FindNewLine($Txt, $PosBeg, $Forward, ($LevelStop!==0));
+ $Loc = new clsTbsLocator;
+ $Loc->PosBeg = ($Forward) ? $PosBeg : $p;
+ $Loc->PosEnd = ($Forward) ? $p : $PosBeg;
+ $Loc->RightLevel = 0;
+ return $Loc;
+ }
+
+ $Pos = $PosBeg + (($Forward) ? -1 : +1);
+ $TagIsOpening = false;
+ $TagClosing = '/'.$Tag;
+ $LevelNum = 0;
+ $TagOk = false;
+ $PosEnd = false;
+ $TagL = strlen($Tag);
+ $TagClosingL = strlen($TagClosing);
+ $RightLevel = 0;
+
+ do {
+
+ // Look for the next tag def
+ if ($Forward) {
+ $Pos = strpos($Txt, '<', $Pos+1);
+ } else {
+ if ($Pos<=0) {
+ $Pos = false;
+ } else {
+ $Pos = strrpos(substr($Txt, 0, $Pos - 1), '<'); // strrpos() syntax compatible with PHP 4
+ }
+ }
+
+ if ($Pos!==false) {
+
+ // Check the name of the tag
+ if (strcasecmp(substr($Txt, $Pos+1, $TagL), $Tag)==0) {
+ // It's an opening tag
+ $PosX = $Pos + 1 + $TagL; // The next char
+ $TagOk = true;
+ $TagIsOpening = true;
+ } elseif (strcasecmp(substr($Txt, $Pos+1, $TagClosingL), $TagClosing)==0) {
+ // It's a closing tag
+ $PosX = $Pos + 1 + $TagClosingL; // The next char
+ $TagOk = true;
+ $TagIsOpening = false;
+ }
+
+ if ($TagOk) {
+ // Check the next char
+ $x = $Txt[$PosX];
+ if (($x===' ') || ($x==="\r") || ($x==="\n") || ($x==='>') || ($x==='/') || ($Tag==='/') || ($Tag==='')) {
+ // Check the encapsulation count
+ if ($LevelStop===false) { // No encapsulation check
+ if ($TagIsOpening!==$Opening) {
+ $TagOk = false;
+ }
+ } else { // Count the number of level
+ if ($TagIsOpening) {
+ $PosEnd = strpos($Txt, '>', $PosX);
+ if ($PosEnd!==false) {
+ if ($Txt[$PosEnd-1]==='/') {
+ if (($Pos<$PosBeg) && ($PosEnd>$PosBeg)) {
+ $RightLevel=1;
+ $LevelNum++;
+ }
+ } else {
+ $LevelNum++;
+ }
+ }
+ } else {
+ $LevelNum--;
+ }
+ // Check if it's the expected level
+ if ($LevelNum!=$LevelStop) {
+ $TagOk = false;
+ $PosEnd = false;
+ }
+ }
+ } else {
+ $TagOk = false;
+ }
+ } //--> if ($TagOk)
+ }
+ } while (($Pos!==false) && ($TagOk===false));
+
+ // Search for the end of the tag
+ if ($TagOk) {
+ $Loc = new clsTbsLocator;
+ if ($WithPrm) {
+ self::f_Loc_PrmRead($Txt, $PosX, true, '\'"', '<', '>', $Loc, $PosEnd, $WithPos);
+ } elseif ($PosEnd===false) {
+ $PosEnd = strpos($Txt, '>', $PosX);
+ if ($PosEnd===false) {
+ $TagOk = false;
+ }
+ }
+ }
+
+ // Result
+ if ($TagOk) {
+ $Loc->PosBeg = $Pos;
+ $Loc->PosEnd = $PosEnd;
+ $Loc->RightLevel = $RightLevel;
+ return $Loc;
+ } else {
+ return false;
+ }
+ }
+
+ public static function f_Xml_FindNewLine(&$Txt, $PosBeg, $Forward, $IsRef)
+ {
+ $p = $PosBeg;
+ if ($Forward) {
+ $Inc = 1;
+ $Inf = &$p;
+ $Sup = strlen($Txt)-1;
+ } else {
+ $Inc = -1;
+ $Inf = 0;
+ $Sup = &$p;
+ }
+
+ do {
+ if ($Inf>$Sup) {
+ return max($Sup, 0);
+ }
+ $x = $Txt[$p];
+ if (($x==="\r") || ($x==="\n")) {
+ $x2 = ($x==="\n") ? "\r" : "\n";
+ $p0 = $p;
+ if (($Inf<$Sup) && ($Txt[$p+$Inc]===$x2)) {
+ $p += $Inc;
+ } // Newline char can have two chars.
+ if ($Forward) {
+ return $p;
+ } // Forward => return pos including newline char.
+ 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.
+ }
+ $p += $Inc;
+ } while (true);
+ }
+
+ public static function f_Xml_GetNextEntityName($Txt, $Pos, &$tag, &$PosBeg, &$p)
+ {
+ /*
+ $tag : tag name
+ $PosBeg : position of the tag
+ $p : position where the read has stop
+ $z : first char after the name
+ */
+
+ $tag = '';
+ $PosBeg = strpos($Txt, '<', $Pos);
+
+ if ($PosBeg===false) {
+ return false;
+ }
+
+ // Read the name of the tag
+ $go = true;
+ $p = $PosBeg;
+ while ($go) {
+ $p++;
+ $z = $Txt[$p];
+ if ($go = ($z!==' ') && ($z!=="\r") && ($z!=="\n") && ($z!=='>') && ($z!=='/')) {
+ $tag .= $z;
+ }
+ }
+
+ return true;
+ }
}