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