MDL-58546 libraries: Patch ADOdb for PHP 7.2 compat
[moodle.git] / lib / adodb / drivers / adodb-text.inc.php
1 <?php
2 /*
3 @version   v5.20.9  21-Dec-2016
4 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
5 @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
6    Set tabs to 4.
7 */
9 /*
10 Setup:
12         $db = NewADOConnection('text');
13         $db->Connect($array,[$types],[$colnames]);
15         Parameter $array is the 2 dimensional array of data. The first row can contain the
16         column names. If column names is not defined in first row, you MUST define $colnames,
17         the 3rd parameter.
19         Parameter $types is optional. If defined, it should contain an array matching
20         the number of columns in $array, with each element matching the correct type defined
21         by MetaType: (B,C,I,L,N). If undefined, we will probe for $this->_proberows rows
22         to guess the type. Only C,I and N are recognised.
24         Parameter $colnames is optional. If defined, it is an array that contains the
25         column names of $array. If undefined, we assume the first row of $array holds the
26         column names.
28  The Execute() function will return a recordset. The recordset works like a normal recordset.
29  We have partial support for SQL parsing. We process the SQL using the following rules:
31  1. SQL order by's always work for the first column ordered. Subsequent cols are ignored
33  2. All operations take place on the same table. No joins possible. In fact the FROM clause
34         is ignored! You can use any name for the table.
36  3. To simplify code, all columns are returned, except when selecting 1 column
38         $rs = $db->Execute('select col1,col2 from table'); // sql ignored, will generate all cols
40         We special case handling of 1 column because it is used in filter popups
42         $rs = $db->Execute('select col1 from table');
43         // sql accepted and processed -- any table name is accepted
45         $rs = $db->Execute('select distinct col1 from table');
46         // sql accepted and processed
48 4. Where clauses are ignored, but searching with the 3rd parameter of Execute is permitted.
49    This has to use PHP syntax and we will eval() it. You can even use PHP functions.
51          $rs = $db->Execute('select * from table',false,"\$COL1='abc' and $\COL2=3")
52         // the 3rd param is searched -- make sure that $COL1 is a legal column name
53         // and all column names must be in upper case.
55 4. Group by, having, other clauses are ignored
57 5. Expression columns, min(), max() are ignored
59 6. All data is readonly. Only SELECTs permitted.
60 */
62 // security - hide paths
63 if (!defined('ADODB_DIR')) die();
65 if (! defined("_ADODB_TEXT_LAYER")) {
66  define("_ADODB_TEXT_LAYER", 1 );
68 // for sorting in _query()
69 function adodb_cmp($a, $b) {
70         if ($a[0] == $b[0]) return 0;
71         return ($a[0] < $b[0]) ? -1 : 1;
72 }
73 // for sorting in _query()
74 function adodb_cmpr($a, $b) {
75         if ($a[0] == $b[0]) return 0;
76         return ($a[0] > $b[0]) ? -1 : 1;
77 }
78 class ADODB_text extends ADOConnection {
79         var $databaseType = 'text';
81         var $_origarray; // original data
82         var $_types;
83         var $_proberows = 8;
84         var $_colnames;
85         var $_skiprow1=false;
86         var $readOnly = true;
87         var $hasTransactions = false;
89         var $_rezarray;
90         var $_reznames;
91         var $_reztypes;
93         function __construct()
94         {
95         }
97         function RSRecordCount()
98         {
99                 if (!empty($this->_rezarray)) return sizeof($this->_rezarray);
101                 return sizeof($this->_origarray);
102         }
104         function _insertid()
105         {
106                         return false;
107         }
109         function _affectedrows()
110         {
111                         return false;
112         }
114                 // returns true or false
115         function PConnect(&$array, $types = false, $colnames = false)
116         {
117                 return $this->Connect($array, $types, $colnames);
118         }
119                 // returns true or false
120         function Connect(&$array, $types = false, $colnames = false)
121         {
122                 if (is_string($array) and $array === 'iluvphplens') return 'me2';
124                 if (!$array) {
125                         $this->_origarray = false;
126                         return true;
127                 }
128                 $row = $array[0];
129                 $cols = sizeof($row);
132                 if ($colnames) $this->_colnames = $colnames;
133                 else {
134                         $this->_colnames = $array[0];
135                         $this->_skiprow1 = true;
136                 }
137                 if (!$types) {
138                 // probe and guess the type
139                         $types = array();
140                         $firstrow = true;
141                         if ($this->_proberows > sizeof($array)) $max = sizeof($array);
142                         else $max = $this->_proberows;
143                         for ($j=($this->_skiprow1)?1:0;$j < $max; $j++) {
144                                 $row = $array[$j];
145                                 if (!$row) break;
146                                 $i = -1;
147                                 foreach($row as $v) {
148                                         $i += 1;
149                                         //print " ($i ".$types[$i]. "$v) ";
150                                         $v = trim($v);
151                                         if (!preg_match('/^[+-]{0,1}[0-9\.]+$/',$v)) {
152                                                 $types[$i] = 'C'; // once C, always C
153                                                 continue;
154                                         }
155                                         if (isset($types[$i]) && $types[$i]=='C') continue;
156                                         if ($firstrow) {
157                                         // If empty string, we presume is character
158                                         // test for integer for 1st row only
159                                         // after that it is up to testing other rows to prove
160                                         // that it is not an integer
161                                                 if (strlen($v) == 0) $types[0] = 'C';
162                                                 if (strpos($v,'.') !== false) $types[0] = 'N';
163                                                 else  $types[$i] = 'I';
164                                                 continue;
165                                         }
167                                         if (strpos($v,'.') !== false) $types[$i] = 'N';
169                                 }
170                                 $firstrow = false;
171                         }
172                 }
173                 //print_r($types);
174                 $this->_origarray = $array;
175                 $this->_types = $types;
176                 return true;
177         }
181         // returns queryID or false
182         // We presume that the select statement is on the same table (what else?),
183         // with the only difference being the order by.
184         //You can filter by using $eval and each clause is stored in $arr .eg. $arr[1] == 'name'
185         // also supports SELECT [DISTINCT] COL FROM ... -- only 1 col supported
186         function _query($sql,$input_arr,$eval=false)
187         {
188                 if ($this->_origarray === false) return false;
190                 $eval = $this->evalAll;
191                 $usql = strtoupper(trim($sql));
192                 $usql = preg_replace("/[\t\n\r]/",' ',$usql);
193                 $usql = preg_replace('/ *BY/i',' BY',strtoupper($usql));
195                 $eregword ='([A-Z_0-9]*)';
196                 //print "<BR> $sql $eval ";
197                 if ($eval) {
198                         $i = 0;
199                         foreach($this->_colnames as $n) {
200                                 $n = strtoupper(trim($n));
201                                 $eval = str_replace("\$$n","\$arr[$i]",$eval);
203                                 $i += 1;
204                         }
206                         $i = 0;
207                         $eval = "\$rez=($eval);";
208                         //print "<p>Eval string = $eval </p>";
209                         $where_arr = array();
211                         reset($this->_origarray);
212                         foreach ($this->_origarray as $arr) {
214                                 if ($i == 0 && $this->_skiprow1)
215                                         $where_arr[] = $arr;
216                                 else {
217                                         eval($eval);
218                                         //print " $i: result=$rez arr[0]={$arr[0]} arr[1]={$arr[1]} <BR>\n ";
219                                         if ($rez) $where_arr[] = $arr;
220                                 }
221                                 $i += 1;
222                         }
223                         $this->_rezarray = $where_arr;
224                 }else
225                         $where_arr = $this->_origarray;
227                 // THIS PROJECTION CODE ONLY WORKS FOR 1 COLUMN,
228                 // OTHERWISE IT RETURNS ALL COLUMNS
229                 if (substr($usql,0,7) == 'SELECT ') {
230                         $at = strpos($usql,' FROM ');
231                         $sel = trim(substr($usql,7,$at-7));
233                         $distinct = false;
234                         if (substr($sel,0,8) == 'DISTINCT') {
235                                 $distinct = true;
236                                 $sel = trim(substr($sel,8,$at));
237                         }
239                         // $sel holds the selection clause, comma delimited
240                         // currently we only project if one column is involved
241                         // this is to support popups in PHPLens
242                         if (strpos(',',$sel)===false) {
243                                 $colarr = array();
245                                 preg_match("/$eregword/",$sel,$colarr);
246                                 $col = $colarr[1];
247                                 $i = 0;
248                                 $n = '';
249                                 reset($this->_colnames);
250                                 foreach ($this->_colnames as $n) {
252                                         if ($col == strtoupper(trim($n))) break;
253                                         $i += 1;
254                                 }
256                                 if ($n && $col) {
257                                         $distarr = array();
258                                         $projarray = array();
259                                         $projtypes = array($this->_types[$i]);
260                                         $projnames = array($n);
262                                         reset($where_arr);
263                                         while (list($k_a,$a) = each($where_arr)) {
264                                                 if ($i == 0 && $this->_skiprow1) {
265                                                         $projarray[] = array($n);
266                                                         continue;
267                                                 }
269                                                 if ($distinct) {
270                                                         $v = strtoupper($a[$i]);
271                                                         if (! $distarr[$v]) {
272                                                                 $projarray[] = array($a[$i]);
273                                                                 $distarr[$v] = 1;
274                                                         }
275                                                 } else
276                                                         $projarray[] = array($a[$i]);
278                                         } //foreach
279                                         //print_r($projarray);
280                                 }
281                         } // check 1 column in projection
282                 }  // is SELECT
284                 if (empty($projarray)) {
285                         $projtypes = $this->_types;
286                         $projarray = $where_arr;
287                         $projnames = $this->_colnames;
288                 }
289                 $this->_rezarray = $projarray;
290                 $this->_reztypes = $projtypes;
291                 $this->_reznames = $projnames;
294                 $pos = strpos($usql,' ORDER BY ');
295                 if ($pos === false) return $this;
296                 $orderby = trim(substr($usql,$pos+10));
298                 preg_match("/$eregword/",$orderby,$arr);
299                 if (sizeof($arr) < 2) return $this; // actually invalid sql
300                 $col = $arr[1];
301                 $at = (integer) $col;
302                 if ($at == 0) {
303                         $i = 0;
304                         reset($projnames);
305                         foreach ($projnames as $n) {
306                                 if (strtoupper(trim($n)) == $col) {
307                                         $at = $i+1;
308                                         break;
309                                 }
310                                 $i += 1;
311                         }
312                 }
314                 if ($at <= 0 || $at > sizeof($projarray[0])) return $this; // cannot find sort column
315                 $at -= 1;
317                 // generate sort array consisting of (sortval1, row index1) (sortval2, row index2)...
318                 $sorta = array();
319                 $t = $projtypes[$at];
320                 $num = ($t == 'I' || $t == 'N');
321                 for ($i=($this->_skiprow1)?1:0, $max = sizeof($projarray); $i < $max; $i++) {
322                         $row = $projarray[$i];
323                         $val = ($num)?(float)$row[$at]:$row[$at];
324                         $sorta[]=array($val,$i);
325                 }
327                 // check for desc sort
328                 $orderby = substr($orderby,strlen($col)+1);
329                 $arr == array();
330                 preg_match('/([A-Z_0-9]*)/i',$orderby,$arr);
332                 if (trim($arr[1]) == 'DESC') $sortf = 'adodb_cmpr';
333                 else $sortf = 'adodb_cmp';
335                 // hasta la sorta babe
336                 usort($sorta, $sortf);
338                 // rearrange original array
339                 $arr2 = array();
340                 if ($this->_skiprow1) $arr2[] = $projarray[0];
341                 foreach($sorta as $v) {
342                         $arr2[] = $projarray[$v[1]];
343                 }
345                 $this->_rezarray = $arr2;
346                 return $this;
347         }
349         /*      Returns: the last error message from previous database operation        */
350         function ErrorMsg()
351         {
352                         return '';
353         }
355         /*      Returns: the last error number from previous database operation */
356         function ErrorNo()
357         {
358                 return 0;
359         }
361         // returns true or false
362         function _close()
363         {
364         }
369 /*--------------------------------------------------------------------------------------
370          Class Name: Recordset
371 --------------------------------------------------------------------------------------*/
374 class ADORecordSet_text extends ADORecordSet_array
377         var $databaseType = "text";
379         function __construct(&$conn,$mode=false)
380         {
381                 parent::__construct();
382                 $this->InitArray($conn->_rezarray,$conn->_reztypes,$conn->_reznames);
383                 $conn->_rezarray = false;
384         }
386 } // class ADORecordSet_text
389 } // defined