Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   1  <?php
   2  /*
   3  @version   v5.21.0  2021-02-27
   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  */
   8  
   9  /*
  10  Setup:
  11  
  12   	 $db = NewADOConnection('text');
  13   	 $db->Connect($array,[$types],[$colnames]);
  14  
  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.
  18  
  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.
  23  
  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.
  27  
  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:
  30  
  31   1. SQL order by's always work for the first column ordered. Subsequent cols are ignored
  32  
  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.
  35  
  36   3. To simplify code, all columns are returned, except when selecting 1 column
  37  
  38   	 $rs = $db->Execute('select col1,col2 from table'); // sql ignored, will generate all cols
  39  
  40  	 We special case handling of 1 column because it is used in filter popups
  41  
  42  	 $rs = $db->Execute('select col1 from table');
  43  	 // sql accepted and processed -- any table name is accepted
  44  
  45  	 $rs = $db->Execute('select distinct col1 from table');
  46  	 // sql accepted and processed
  47  
  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.
  50  
  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.
  54  
  55  4. Group by, having, other clauses are ignored
  56  
  57  5. Expression columns, min(), max() are ignored
  58  
  59  6. All data is readonly. Only SELECTs permitted.
  60  */
  61  
  62  // security - hide paths
  63  if (!defined('ADODB_DIR')) die();
  64  
  65  if (! defined("_ADODB_TEXT_LAYER")) {
  66   define("_ADODB_TEXT_LAYER", 1 );
  67  
  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';
  80  
  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;
  88  
  89  	 var $_rezarray;
  90  	 var $_reznames;
  91  	 var $_reztypes;
  92  
  93  	function RSRecordCount()
  94  	 {
  95  	 	 if (!empty($this->_rezarray)) return sizeof($this->_rezarray);
  96  
  97  	 	 return sizeof($this->_origarray);
  98  	 }
  99  
 100  	function _insertid()
 101  	 {
 102  	 	 	 return false;
 103  	 }
 104  
 105  	function _affectedrows()
 106  	 {
 107  	 	 	 return false;
 108  	 }
 109  
 110  	 	 // returns true or false
 111  	function PConnect(&$array, $types = false, $colnames = false)
 112  	 {
 113  	 	 return $this->Connect($array, $types, $colnames);
 114  	 }
 115  	 	 // returns true or false
 116  	function Connect(&$array, $types = false, $colnames = false)
 117  	 {
 118  	 	 if (is_string($array) and $array === 'iluvphplens') return 'me2';
 119  
 120  	 	 if (!$array) {
 121  	 	 	 $this->_origarray = false;
 122  	 	 	 return true;
 123  	 	 }
 124  	 	 $row = $array[0];
 125  	 	 $cols = sizeof($row);
 126  
 127  
 128  	 	 if ($colnames) $this->_colnames = $colnames;
 129  	 	 else {
 130  	 	 	 $this->_colnames = $array[0];
 131  	 	 	 $this->_skiprow1 = true;
 132  	 	 }
 133  	 	 if (!$types) {
 134  	 	 // probe and guess the type
 135  	 	 	 $types = array();
 136  	 	 	 $firstrow = true;
 137  	 	 	 if ($this->_proberows > sizeof($array)) $max = sizeof($array);
 138  	 	 	 else $max = $this->_proberows;
 139  	 	 	 for ($j=($this->_skiprow1)?1:0;$j < $max; $j++) {
 140  	 	 	 	 $row = $array[$j];
 141  	 	 	 	 if (!$row) break;
 142  	 	 	 	 $i = -1;
 143  	 	 	 	 foreach($row as $v) {
 144  	 	 	 	 	 $i += 1;
 145  	 	 	 	 	 //print " ($i ".$types[$i]. "$v) ";
 146  	 	 	 	 	 $v = trim($v);
 147  	  	 	 	 	 if (!preg_match('/^[+-]{0,1}[0-9\.]+$/',$v)) {
 148  	 	 	 	 	 	 $types[$i] = 'C'; // once C, always C
 149  	 	 	 	 	 	 continue;
 150  	 	 	 	 	 }
 151  	 	 	 	 	 if (isset($types[$i]) && $types[$i]=='C') continue;
 152  	 	 	 	 	 if ($firstrow) {
 153  	 	 	 	 	 // If empty string, we presume is character
 154  	 	 	 	 	 // test for integer for 1st row only
 155  	 	 	 	 	 // after that it is up to testing other rows to prove
 156  	 	 	 	 	 // that it is not an integer
 157  	 	 	 	 	 	 if (strlen($v) == 0) $types[0] = 'C';
 158  	 	 	 	 	 	 if (strpos($v,'.') !== false) $types[0] = 'N';
 159  	 	 	 	 	 	 else  $types[$i] = 'I';
 160  	 	 	 	 	 	 continue;
 161  	 	 	 	 	 }
 162  
 163  	 	 	 	 	 if (strpos($v,'.') !== false) $types[$i] = 'N';
 164  
 165  	 	 	 	 }
 166  	 	 	 	 $firstrow = false;
 167  	 	 	 }
 168  	 	 }
 169  	 	 //print_r($types);
 170  	 	 $this->_origarray = $array;
 171  	 	 $this->_types = $types;
 172  	 	 return true;
 173  	 }
 174  
 175  
 176  
 177  	 // returns queryID or false
 178  	 // We presume that the select statement is on the same table (what else?),
 179  	 // with the only difference being the order by.
 180  	 //You can filter by using $eval and each clause is stored in $arr .eg. $arr[1] == 'name'
 181  	 // also supports SELECT [DISTINCT] COL FROM ... -- only 1 col supported
 182  	function _query($sql,$input_arr,$eval=false)
 183  	 {
 184  	 	 if ($this->_origarray === false) return false;
 185  
 186  	 	 $eval = $this->evalAll;
 187  	 	 $usql = strtoupper(trim($sql));
 188  	 	 $usql = preg_replace("/[\t\n\r]/",' ',$usql);
 189  	 	 $usql = preg_replace('/ *BY/i',' BY',strtoupper($usql));
 190  
 191  	 	 $eregword ='([A-Z_0-9]*)';
 192  	 	 //print "<BR> $sql $eval ";
 193  	 	 if ($eval) {
 194  	 	 	 $i = 0;
 195  	 	 	 foreach($this->_colnames as $n) {
 196  	 	 	 	 $n = strtoupper(trim($n));
 197  	 	 	 	 $eval = str_replace("\$$n","\$arr[$i]",$eval);
 198  
 199  	 	 	 	 $i += 1;
 200  	 	 	 }
 201  
 202  	 	 	 $i = 0;
 203  	 	 	 $eval = "\$rez=($eval);";
 204  	 	 	 //print "<p>Eval string = $eval </p>";
 205  	 	 	 $where_arr = array();
 206  
 207  	 	 	 reset($this->_origarray);
 208  	 	 	 foreach ($this->_origarray as $arr) {
 209  
 210  	 	 	 	 if ($i == 0 && $this->_skiprow1)
 211  	 	 	 	 	 $where_arr[] = $arr;
 212  	 	 	 	 else {
 213  	 	 	 	 	 eval($eval);
 214  	 	 	 	 	 //print " $i: result=$rez arr[0]={$arr[0]} arr[1]={$arr[1]} <BR>\n ";
 215  	 	 	 	 	 if ($rez) $where_arr[] = $arr;
 216  	 	 	 	 }
 217  	 	 	 	 $i += 1;
 218  	 	 	 }
 219  	 	 	 $this->_rezarray = $where_arr;
 220  	 	 }else
 221  	 	 	 $where_arr = $this->_origarray;
 222  
 223  	 	 // THIS PROJECTION CODE ONLY WORKS FOR 1 COLUMN,
 224  	 	 // OTHERWISE IT RETURNS ALL COLUMNS
 225  	 	 if (substr($usql,0,7) == 'SELECT ') {
 226  	 	 	 $at = strpos($usql,' FROM ');
 227  	 	 	 $sel = trim(substr($usql,7,$at-7));
 228  
 229  	 	 	 $distinct = false;
 230  	 	 	 if (substr($sel,0,8) == 'DISTINCT') {
 231  	 	 	 	 $distinct = true;
 232  	 	 	 	 $sel = trim(substr($sel,8,$at));
 233  	 	 	 }
 234  
 235  	 	 	 // $sel holds the selection clause, comma delimited
 236  	 	 	 // currently we only project if one column is involved
 237  	 	 	 // this is to support popups in PHPLens
 238  	 	 	 if (strpos(',',$sel)===false) {
 239  	 	 	 	 $colarr = array();
 240  
 241  	 	 	 	 preg_match("/$eregword/",$sel,$colarr);
 242  	 	 	 	 $col = $colarr[1];
 243  	 	 	 	 $i = 0;
 244  	 	 	 	 $n = '';
 245  	 	 	 	 reset($this->_colnames);
 246  	 	 	 	 foreach ($this->_colnames as $n) {
 247  
 248  	 	 	 	 	 if ($col == strtoupper(trim($n))) break;
 249  	 	 	 	 	 $i += 1;
 250  	 	 	 	 }
 251  
 252  	 	 	 	 if ($n && $col) {
 253  	 	 	 	 	 $distarr = array();
 254  	 	 	 	 	 $projarray = array();
 255  	 	 	 	 	 $projtypes = array($this->_types[$i]);
 256  	 	 	 	 	 $projnames = array($n);
 257  
 258  	 	 	 	 	 foreach ($where_arr as $a) {
 259  	 	 	 	 	 	 if ($i == 0 && $this->_skiprow1) {
 260  	 	 	 	 	 	 	 $projarray[] = array($n);
 261  	 	 	 	 	 	 	 continue;
 262  	 	 	 	 	 	 }
 263  
 264  	 	 	 	 	 	 if ($distinct) {
 265  	 	 	 	 	 	 	 $v = strtoupper($a[$i]);
 266  	 	 	 	 	 	 	 if (! $distarr[$v]) {
 267  	 	 	 	 	 	 	 	 $projarray[] = array($a[$i]);
 268  	 	 	 	 	 	 	 	 $distarr[$v] = 1;
 269  	 	 	 	 	 	 	 }
 270  	 	 	 	 	 	 } else
 271  	 	 	 	 	 	 	 $projarray[] = array($a[$i]);
 272  
 273  	 	 	 	 	 } //foreach
 274  	 	 	 	 	 //print_r($projarray);
 275  	 	 	 	 }
 276  	 	 	 } // check 1 column in projection
 277  	 	 }  // is SELECT
 278  
 279  	 	 if (empty($projarray)) {
 280  	 	 	 $projtypes = $this->_types;
 281  	 	 	 $projarray = $where_arr;
 282  	 	 	 $projnames = $this->_colnames;
 283  	 	 }
 284  	 	 $this->_rezarray = $projarray;
 285  	 	 $this->_reztypes = $projtypes;
 286  	 	 $this->_reznames = $projnames;
 287  
 288  
 289  	 	 $pos = strpos($usql,' ORDER BY ');
 290  	 	 if ($pos === false) return $this;
 291  	 	 $orderby = trim(substr($usql,$pos+10));
 292  
 293  	 	 preg_match("/$eregword/",$orderby,$arr);
 294  	 	 if (sizeof($arr) < 2) return $this; // actually invalid sql
 295  	 	 $col = $arr[1];
 296  	 	 $at = (integer) $col;
 297  	 	 if ($at == 0) {
 298  	 	 	 $i = 0;
 299  	 	 	 reset($projnames);
 300  	 	 	 foreach ($projnames as $n) {
 301  	 	 	 	 if (strtoupper(trim($n)) == $col) {
 302  	 	 	 	 	 $at = $i+1;
 303  	 	 	 	 	 break;
 304  	 	 	 	 }
 305  	 	 	 	 $i += 1;
 306  	 	 	 }
 307  	 	 }
 308  
 309  	 	 if ($at <= 0 || $at > sizeof($projarray[0])) return $this; // cannot find sort column
 310  	 	 $at -= 1;
 311  
 312  	 	 // generate sort array consisting of (sortval1, row index1) (sortval2, row index2)...
 313  	 	 $sorta = array();
 314  	 	 $t = $projtypes[$at];
 315  	 	 $num = ($t == 'I' || $t == 'N');
 316  	 	 for ($i=($this->_skiprow1)?1:0, $max = sizeof($projarray); $i < $max; $i++) {
 317  	 	 	 $row = $projarray[$i];
 318  	 	 	 $val = ($num)?(float)$row[$at]:$row[$at];
 319  	 	 	 $sorta[]=array($val,$i);
 320  	 	 }
 321  
 322  	 	 // check for desc sort
 323  	 	 $orderby = substr($orderby,strlen($col)+1);
 324  	 	 $arr = array();
 325  	 	 preg_match('/([A-Z_0-9]*)/i',$orderby,$arr);
 326  
 327  	 	 if (trim($arr[1]) == 'DESC') $sortf = 'adodb_cmpr';
 328  	 	 else $sortf = 'adodb_cmp';
 329  
 330  	 	 // hasta la sorta babe
 331  	 	 usort($sorta, $sortf);
 332  
 333  	 	 // rearrange original array
 334  	 	 $arr2 = array();
 335  	 	 if ($this->_skiprow1) $arr2[] = $projarray[0];
 336  	 	 foreach($sorta as $v) {
 337  	 	 	 $arr2[] = $projarray[$v[1]];
 338  	 	 }
 339  
 340  	 	 $this->_rezarray = $arr2;
 341  	 	 return $this;
 342  	 }
 343  
 344  	 /*	 Returns: the last error message from previous database operation	 */
 345  	function ErrorMsg()
 346  	 {
 347  	 	 	 return '';
 348  	 }
 349  
 350  	 /*	 Returns: the last error number from previous database operation	 */
 351  	function ErrorNo()
 352  	 {
 353  	 	 return 0;
 354  	 }
 355  
 356  	 // returns true or false
 357  	function _close()
 358  	 {
 359  	 }
 360  
 361  
 362  }
 363  
 364  /*--------------------------------------------------------------------------------------
 365  	  Class Name: Recordset
 366  --------------------------------------------------------------------------------------*/
 367  
 368  
 369  class ADORecordSet_text extends ADORecordSet_array
 370  {
 371  
 372  	 var $databaseType = "text";
 373  
 374  	function __construct( $conn,$mode=false)
 375  	 {
 376  	 	 parent::__construct();
 377  	 	 $this->InitArray($conn->_rezarray,$conn->_reztypes,$conn->_reznames);
 378  	 	 $conn->_rezarray = false;
 379  	 }
 380  
 381  } // class ADORecordSet_text
 382  
 383  
 384  } // defined