[ Index ]

PHP Cross Reference of Moodle 310

title

Body

[close]

/lib/adodb/ -> adodb-lib.inc.php (source)

   1  <?php
   2  // security - hide paths
   3  if (!defined('ADODB_DIR')) die();
   4  
   5  global $ADODB_INCLUDED_LIB;
   6  $ADODB_INCLUDED_LIB = 1;
   7  
   8  /*
   9    @version   v5.20.16  12-Jan-2020
  10    @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  11    @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
  12    Released under both BSD license and Lesser GPL library license.
  13    Whenever there is any discrepancy between the two licenses,
  14    the BSD license will take precedence. See License.txt.
  15    Set tabs to 4 for best viewing.
  16  
  17    Less commonly used functions are placed here to reduce size of adodb.inc.php.
  18  */
  19  
  20  function adodb_strip_order_by($sql)
  21  {
  22      $rez = preg_match('/(\sORDER\s+BY\s(?:[^)](?!LIMIT))*)/is', $sql, $arr);
  23      if ($arr)
  24          if (strpos($arr[1], '(') !== false) {
  25              $at = strpos($sql, $arr[1]);
  26              $cntin = 0;
  27              for ($i=$at, $max=strlen($sql); $i < $max; $i++) {
  28                  $ch = $sql[$i];
  29                  if ($ch == '(') {
  30                      $cntin += 1;
  31                  } elseif($ch == ')') {
  32                      $cntin -= 1;
  33                      if ($cntin < 0) {
  34                          break;
  35                      }
  36                  }
  37              }
  38              $sql = substr($sql,0,$at).substr($sql,$i);
  39          } else {
  40              $sql = str_replace($arr[1], '', $sql);
  41          }
  42      return $sql;
  43  }
  44  
  45  if (false) {
  46      $sql = 'select * from (select a from b order by a(b),b(c) desc)';
  47      $sql = '(select * from abc order by 1)';
  48      die(adodb_strip_order_by($sql));
  49  }
  50  
  51  function adodb_probetypes(&$array,&$types,$probe=8)
  52  {
  53  // probe and guess the type
  54      $types = array();
  55      if ($probe > sizeof($array)) $max = sizeof($array);
  56      else $max = $probe;
  57  
  58  
  59      for ($j=0;$j < $max; $j++) {
  60          $row = $array[$j];
  61          if (!$row) break;
  62          $i = -1;
  63          foreach($row as $v) {
  64              $i += 1;
  65  
  66              if (isset($types[$i]) && $types[$i]=='C') continue;
  67  
  68              //print " ($i ".$types[$i]. "$v) ";
  69              $v = trim($v);
  70  
  71              if (!preg_match('/^[+-]{0,1}[0-9\.]+$/',$v)) {
  72                  $types[$i] = 'C'; // once C, always C
  73  
  74                  continue;
  75              }
  76              if ($j == 0) {
  77              // If empty string, we presume is character
  78              // test for integer for 1st row only
  79              // after that it is up to testing other rows to prove
  80              // that it is not an integer
  81                  if (strlen($v) == 0) $types[$i] = 'C';
  82                  if (strpos($v,'.') !== false) $types[$i] = 'N';
  83                  else  $types[$i] = 'I';
  84                  continue;
  85              }
  86  
  87              if (strpos($v,'.') !== false) $types[$i] = 'N';
  88  
  89          }
  90      }
  91  
  92  }
  93  
  94  function  adodb_transpose(&$arr, &$newarr, &$hdr, &$fobjs)
  95  {
  96      $oldX = sizeof(reset($arr));
  97      $oldY = sizeof($arr);
  98  
  99      if ($hdr) {
 100          $startx = 1;
 101          $hdr = array('Fields');
 102          for ($y = 0; $y < $oldY; $y++) {
 103              $hdr[] = $arr[$y][0];
 104          }
 105      } else
 106          $startx = 0;
 107  
 108      for ($x = $startx; $x < $oldX; $x++) {
 109          if ($fobjs) {
 110              $o = $fobjs[$x];
 111              $newarr[] = array($o->name);
 112          } else
 113              $newarr[] = array();
 114  
 115          for ($y = 0; $y < $oldY; $y++) {
 116              $newarr[$x-$startx][] = $arr[$y][$x];
 117          }
 118      }
 119  }
 120  
 121  // Force key to upper.
 122  // See also http://www.php.net/manual/en/function.array-change-key-case.php
 123  function _array_change_key_case($an_array)
 124  {
 125      if (is_array($an_array)) {
 126          $new_array = array();
 127          foreach($an_array as $key=>$value)
 128              $new_array[strtoupper($key)] = $value;
 129  
 130             return $new_array;
 131     }
 132  
 133      return $an_array;
 134  }
 135  
 136  function _adodb_replace(&$zthis, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc)
 137  {
 138          if (count($fieldArray) == 0) return 0;
 139          $first = true;
 140          $uSet = '';
 141  
 142          if (!is_array($keyCol)) {
 143              $keyCol = array($keyCol);
 144          }
 145          foreach($fieldArray as $k => $v) {
 146              if ($v === null) {
 147                  $v = 'NULL';
 148                  $fieldArray[$k] = $v;
 149              } else if ($autoQuote && /*!is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ strcasecmp($v,$zthis->null2null)!=0) {
 150                  $v = $zthis->qstr($v);
 151                  $fieldArray[$k] = $v;
 152              }
 153              if (in_array($k,$keyCol)) continue; // skip UPDATE if is key
 154  
 155              if ($first) {
 156                  $first = false;
 157                  $uSet = "$k=$v";
 158              } else
 159                  $uSet .= ",$k=$v";
 160          }
 161  
 162          $where = false;
 163          foreach ($keyCol as $v) {
 164              if (isset($fieldArray[$v])) {
 165                  if ($where) $where .= ' and '.$v.'='.$fieldArray[$v];
 166                  else $where = $v.'='.$fieldArray[$v];
 167              }
 168          }
 169  
 170          if ($uSet && $where) {
 171              $update = "UPDATE $table SET $uSet WHERE $where";
 172  
 173              $rs = $zthis->Execute($update);
 174  
 175  
 176              if ($rs) {
 177                  if ($zthis->poorAffectedRows) {
 178                  /*
 179                   The Select count(*) wipes out any errors that the update would have returned.
 180                  http://phplens.com/lens/lensforum/msgs.php?id=5696
 181                  */
 182                      if ($zthis->ErrorNo()<>0) return 0;
 183  
 184                  # affected_rows == 0 if update field values identical to old values
 185                  # for mysql - which is silly.
 186  
 187                      $cnt = $zthis->GetOne("select count(*) from $table where $where");
 188                      if ($cnt > 0) return 1; // record already exists
 189                  } else {
 190                      if (($zthis->Affected_Rows()>0)) return 1;
 191                  }
 192              } else
 193                  return 0;
 194          }
 195  
 196      //    print "<p>Error=".$this->ErrorNo().'<p>';
 197          $first = true;
 198          foreach($fieldArray as $k => $v) {
 199              if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col
 200  
 201              if ($first) {
 202                  $first = false;
 203                  $iCols = "$k";
 204                  $iVals = "$v";
 205              } else {
 206                  $iCols .= ",$k";
 207                  $iVals .= ",$v";
 208              }
 209          }
 210          $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)";
 211          $rs = $zthis->Execute($insert);
 212          return ($rs) ? 2 : 0;
 213  }
 214  
 215  function _adodb_getmenu(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false,
 216              $size=0, $selectAttr='',$compareFields0=true)
 217  {
 218      global $ADODB_FETCH_MODE;
 219  
 220      $s = _adodb_getmenu_select($name, $defstr, $blank1stItem, $multiple, $size, $selectAttr);
 221  
 222      $hasvalue = $zthis->FieldCount() > 1;
 223      if (!$hasvalue) {
 224          $compareFields0 = true;
 225      }
 226  
 227      $value = '';
 228      while(!$zthis->EOF) {
 229          $zval = rtrim(reset($zthis->fields));
 230  
 231          if ($blank1stItem && $zval == "") {
 232              $zthis->MoveNext();
 233              continue;
 234          }
 235  
 236          if ($hasvalue) {
 237              if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC) {
 238                  // Get 2nd field's value regardless of its name
 239                  $zval2 = current(array_slice($zthis->fields, 1, 1));
 240              } else {
 241                  // With NUM or BOTH fetch modes, we have a numeric index
 242                  $zval2 = $zthis->fields[1];
 243              }
 244              $zval2 = trim($zval2);
 245              $value = 'value="' . htmlspecialchars($zval2) . '"';
 246          }
 247  
 248          $s .= _adodb_getmenu_option($defstr, $compareFields0 ? $zval : $zval2, $value, $zval);
 249  
 250          $zthis->MoveNext();
 251      } // while
 252  
 253      return $s ."\n</select>\n";
 254  }
 255  
 256  function _adodb_getmenu_gp(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false,
 257              $size=0, $selectAttr='',$compareFields0=true)
 258  {
 259      global $ADODB_FETCH_MODE;
 260  
 261      $s = _adodb_getmenu_select($name, $defstr, $blank1stItem, $multiple, $size, $selectAttr);
 262  
 263      $hasvalue = $zthis->FieldCount() > 1;
 264      $hasgroup = $zthis->FieldCount() > 2;
 265      if (!$hasvalue) {
 266          $compareFields0 = true;
 267      }
 268  
 269      $value = '';
 270      $optgroup = null;
 271      $firstgroup = true;
 272      while(!$zthis->EOF) {
 273          $zval = rtrim(reset($zthis->fields));
 274          $group = '';
 275  
 276          if ($blank1stItem && $zval=="") {
 277              $zthis->MoveNext();
 278              continue;
 279          }
 280  
 281          if ($hasvalue) {
 282              if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC) {
 283                  // Get 2nd field's value regardless of its name
 284                  $fields = array_slice($zthis->fields, 1);
 285                  $zval2 = current($fields);
 286                  if ($hasgroup) {
 287                      $group = trim(next($fields));
 288                  }
 289              } else {
 290                  // With NUM or BOTH fetch modes, we have a numeric index
 291                  $zval2 = $zthis->fields[1];
 292                  if ($hasgroup) {
 293                      $group = trim($zthis->fields[2]);
 294                  }
 295              }
 296              $zval2 = trim($zval2);
 297              $value = "value='".htmlspecialchars($zval2)."'";
 298          }
 299  
 300          if ($optgroup != $group) {
 301              $optgroup = $group;
 302              if ($firstgroup) {
 303                  $firstgroup = false;
 304              } else {
 305                  $s .="\n</optgroup>";
 306              }
 307              $s .="\n<optgroup label='". htmlspecialchars($group) ."'>";
 308          }
 309  
 310          $s .= _adodb_getmenu_option($defstr, $compareFields0 ? $zval : $zval2, $value, $zval);
 311  
 312          $zthis->MoveNext();
 313      } // while
 314  
 315      // closing last optgroup
 316      if($optgroup != null) {
 317          $s .= "\n</optgroup>";
 318      }
 319      return $s ."\n</select>\n";
 320  }
 321  
 322  /**
 323   * Generate the opening SELECT tag for getmenu functions.
 324   *
 325   * ADOdb internal function, used by _adodb_getmenu() and _adodb_getmenu_gp().
 326   *
 327   * @param string $name
 328   * @param string $defstr
 329   * @param bool   $blank1stItem
 330   * @param bool   $multiple
 331   * @param int    $size
 332   * @param string $selectAttr
 333   *
 334   * @return string HTML
 335   */
 336  function _adodb_getmenu_select($name, $defstr = '', $blank1stItem = true,
 337                                 $multiple = false, $size = 0, $selectAttr = '')
 338  {
 339      if ($multiple || is_array($defstr)) {
 340          if ($size == 0 ) {
 341              $size = 5;
 342          }
 343          $attr = ' multiple size="' . $size . '"';
 344          if (!strpos($name,'[]')) {
 345              $name .= '[]';
 346          }
 347      } elseif ($size) {
 348          $attr = ' size="' . $size . '"';
 349      } else {
 350          $attr = '';
 351      }
 352  
 353      $html = '<select name="' . $name . '"' . $attr . ' ' . $selectAttr . '>';
 354      if ($blank1stItem) {
 355          if (is_string($blank1stItem))  {
 356              $barr = explode(':',$blank1stItem);
 357              if (sizeof($barr) == 1) {
 358                  $barr[] = '';
 359              }
 360              $html .= "\n<option value=\"" . $barr[0] . "\">" . $barr[1] . "</option>";
 361          } else {
 362              $html .= "\n<option></option>";
 363          }
 364      }
 365  
 366      return $html;
 367  }
 368  
 369  /**
 370   * Print the OPTION tags for getmenu functions.
 371   *
 372   * ADOdb internal function, used by _adodb_getmenu() and _adodb_getmenu_gp().
 373   *
 374   * @param string $defstr  Default values
 375   * @param string $compare Value to compare against defaults
 376   * @param string $value   Ready-to-print `value="xxx"` (or empty) string
 377   * @param string $display Display value
 378   *
 379   * @return string HTML
 380   */
 381  function _adodb_getmenu_option($defstr, $compare, $value, $display)
 382  {
 383      if (   is_array($defstr) && in_array($compare, $defstr)
 384          || !is_array($defstr) && strcasecmp($compare, $defstr) == 0
 385      ) {
 386          $selected = ' selected="selected"';
 387      } else {
 388          $selected = '';
 389      }
 390  
 391      return "\n<option $value$selected>" . htmlspecialchars($display) . '</option>';
 392  }
 393  
 394  /*
 395      Count the number of records this sql statement will return by using
 396      query rewriting heuristics...
 397  
 398      Does not work with UNIONs, except with postgresql and oracle.
 399  
 400      Usage:
 401  
 402      $conn->Connect(...);
 403      $cnt = _adodb_getcount($conn, $sql);
 404  
 405  */
 406  function _adodb_getcount(&$zthis, $sql,$inputarr=false,$secs2cache=0)
 407  {
 408      $qryRecs = 0;
 409  
 410       if (!empty($zthis->_nestedSQL) || preg_match("/^\s*SELECT\s+DISTINCT/is", $sql) ||
 411           preg_match('/\s+GROUP\s+BY\s+/is',$sql) ||
 412          preg_match('/\s+UNION\s+/is',$sql)) {
 413  
 414          $rewritesql = adodb_strip_order_by($sql);
 415  
 416          // ok, has SELECT DISTINCT or GROUP BY so see if we can use a table alias
 417          // but this is only supported by oracle and postgresql...
 418          if ($zthis->dataProvider == 'oci8') {
 419              // Allow Oracle hints to be used for query optimization, Chris Wrye
 420              if (preg_match('#/\\*+.*?\\*\\/#', $sql, $hint)) {
 421                  $rewritesql = "SELECT ".$hint[0]." COUNT(*) FROM (".$rewritesql.")";
 422              } else
 423                  $rewritesql = "SELECT COUNT(*) FROM (".$rewritesql.")";
 424  
 425          } else if (strncmp($zthis->databaseType,'postgres',8) == 0
 426              || strncmp($zthis->databaseType,'mysql',5) == 0
 427          || strncmp($zthis->databaseType,'mssql',5) == 0
 428              || strncmp($zthis->dsnType,'sqlsrv',5) == 0
 429              || strncmp($zthis->dsnType,'mssql',5) == 0
 430          ){
 431              $rewritesql = "SELECT COUNT(*) FROM ($rewritesql) _ADODB_ALIAS_";
 432          } else {
 433              $rewritesql = "SELECT COUNT(*) FROM ($rewritesql)";
 434          }
 435      } else {
 436          // now replace SELECT ... FROM with SELECT COUNT(*) FROM
 437          if ( strpos($sql, '_ADODB_COUNT') !== FALSE ) {
 438              $rewritesql = preg_replace('/^\s*?SELECT\s+_ADODB_COUNT(.*)_ADODB_COUNT\s/is','SELECT COUNT(*) ',$sql);
 439          } else {
 440              $rewritesql = preg_replace('/^\s*SELECT\s.*\s+FROM\s/Uis','SELECT COUNT(*) FROM ',$sql);
 441          }
 442          // fix by alexander zhukov, alex#unipack.ru, because count(*) and 'order by' fails
 443          // with mssql, access and postgresql. Also a good speedup optimization - skips sorting!
 444          // also see http://phplens.com/lens/lensforum/msgs.php?id=12752
 445          $rewritesql = adodb_strip_order_by($rewritesql);
 446      }
 447  
 448      if (isset($rewritesql) && $rewritesql != $sql) {
 449          if (preg_match('/\sLIMIT\s+[0-9]+/i',$sql,$limitarr)) $rewritesql .= $limitarr[0];
 450  
 451          if ($secs2cache) {
 452              // we only use half the time of secs2cache because the count can quickly
 453              // become inaccurate if new records are added
 454              $qryRecs = $zthis->CacheGetOne($secs2cache/2,$rewritesql,$inputarr);
 455  
 456          } else {
 457              $qryRecs = $zthis->GetOne($rewritesql,$inputarr);
 458            }
 459          if ($qryRecs !== false) return $qryRecs;
 460      }
 461      //--------------------------------------------
 462      // query rewrite failed - so try slower way...
 463  
 464  
 465      // strip off unneeded ORDER BY if no UNION
 466      if (preg_match('/\s*UNION\s*/is', $sql)) $rewritesql = $sql;
 467      else $rewritesql = $rewritesql = adodb_strip_order_by($sql);
 468  
 469      if (preg_match('/\sLIMIT\s+[0-9]+/i',$sql,$limitarr)) $rewritesql .= $limitarr[0];
 470  
 471      if ($secs2cache) {
 472          $rstest = $zthis->CacheExecute($secs2cache,$rewritesql,$inputarr);
 473          if (!$rstest) $rstest = $zthis->CacheExecute($secs2cache,$sql,$inputarr);
 474      } else {
 475          $rstest = $zthis->Execute($rewritesql,$inputarr);
 476          if (!$rstest) $rstest = $zthis->Execute($sql,$inputarr);
 477      }
 478      if ($rstest) {
 479                $qryRecs = $rstest->RecordCount();
 480          if ($qryRecs == -1) {
 481          global $ADODB_EXTENSION;
 482          // some databases will return -1 on MoveLast() - change to MoveNext()
 483              if ($ADODB_EXTENSION) {
 484                  while(!$rstest->EOF) {
 485                      adodb_movenext($rstest);
 486                  }
 487              } else {
 488                  while(!$rstest->EOF) {
 489                      $rstest->MoveNext();
 490                  }
 491              }
 492              $qryRecs = $rstest->_currentRow;
 493          }
 494          $rstest->Close();
 495          if ($qryRecs == -1) return 0;
 496      }
 497      return $qryRecs;
 498  }
 499  
 500  /*
 501       Code originally from "Cornel G" <conyg@fx.ro>
 502  
 503      This code might not work with SQL that has UNION in it
 504  
 505      Also if you are using CachePageExecute(), there is a strong possibility that
 506      data will get out of synch. use CachePageExecute() only with tables that
 507      rarely change.
 508  */
 509  function _adodb_pageexecute_all_rows(&$zthis, $sql, $nrows, $page,
 510                          $inputarr=false, $secs2cache=0)
 511  {
 512      $atfirstpage = false;
 513      $atlastpage = false;
 514      $lastpageno=1;
 515  
 516      // If an invalid nrows is supplied,
 517      // we assume a default value of 10 rows per page
 518      if (!isset($nrows) || $nrows <= 0) $nrows = 10;
 519  
 520      $qryRecs = false; //count records for no offset
 521  
 522      $qryRecs = _adodb_getcount($zthis,$sql,$inputarr,$secs2cache);
 523      $lastpageno = (int) ceil($qryRecs / $nrows);
 524      $zthis->_maxRecordCount = $qryRecs;
 525  
 526  
 527  
 528      // ***** Here we check whether $page is the last page or
 529      // whether we are trying to retrieve
 530      // a page number greater than the last page number.
 531      if ($page >= $lastpageno) {
 532          $page = $lastpageno;
 533          $atlastpage = true;
 534      }
 535  
 536      // If page number <= 1, then we are at the first page
 537      if (empty($page) || $page <= 1) {
 538          $page = 1;
 539          $atfirstpage = true;
 540      }
 541  
 542      // We get the data we want
 543      $offset = $nrows * ($page-1);
 544      if ($secs2cache > 0)
 545          $rsreturn = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr);
 546      else
 547          $rsreturn = $zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
 548  
 549  
 550      // Before returning the RecordSet, we set the pagination properties we need
 551      if ($rsreturn) {
 552          $rsreturn->_maxRecordCount = $qryRecs;
 553          $rsreturn->rowsPerPage = $nrows;
 554          $rsreturn->AbsolutePage($page);
 555          $rsreturn->AtFirstPage($atfirstpage);
 556          $rsreturn->AtLastPage($atlastpage);
 557          $rsreturn->LastPageNo($lastpageno);
 558      }
 559      return $rsreturn;
 560  }
 561  
 562  // Iván Oliva version
 563  function _adodb_pageexecute_no_last_page(&$zthis, $sql, $nrows, $page, $inputarr=false, $secs2cache=0)
 564  {
 565  
 566      $atfirstpage = false;
 567      $atlastpage = false;
 568  
 569      if (!isset($page) || $page <= 1) {
 570          // If page number <= 1, then we are at the first page
 571          $page = 1;
 572          $atfirstpage = true;
 573      }
 574      if ($nrows <= 0) {
 575          // If an invalid nrows is supplied, we assume a default value of 10 rows per page
 576          $nrows = 10;
 577      }
 578  
 579      $pagecounteroffset = ($page * $nrows) - $nrows;
 580  
 581      // To find out if there are more pages of rows, simply increase the limit or
 582      // nrows by 1 and see if that number of records was returned. If it was,
 583      // then we know there is at least one more page left, otherwise we are on
 584      // the last page. Therefore allow non-Count() paging with single queries
 585      // rather than three queries as was done before.
 586      $test_nrows = $nrows + 1;
 587      if ($secs2cache > 0) {
 588          $rsreturn = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr);
 589      } else {
 590          $rsreturn = $zthis->SelectLimit($sql, $test_nrows, $pagecounteroffset, $inputarr, $secs2cache);
 591      }
 592  
 593      // Now check to see if the number of rows returned was the higher value we asked for or not.
 594      if ( $rsreturn->_numOfRows == $test_nrows ) {
 595          // Still at least 1 more row, so we are not on last page yet...
 596          // Remove the last row from the RS.
 597          $rsreturn->_numOfRows = ( $rsreturn->_numOfRows - 1 );
 598      } elseif ( $rsreturn->_numOfRows == 0 && $page > 1 ) {
 599          // Likely requested a page that doesn't exist, so need to find the last
 600          // page and return it. Revert to original method and loop through pages
 601          // until we find some data...
 602          $pagecounter = $page + 1;
 603          $pagecounteroffset = ($pagecounter * $nrows) - $nrows;
 604  
 605          $rstest = $rsreturn;
 606          if ($rstest) {
 607              while ($rstest && $rstest->EOF && $pagecounter > 0) {
 608                  $atlastpage = true;
 609                  $pagecounter--;
 610                  $pagecounteroffset = $nrows * ($pagecounter - 1);
 611                  $rstest->Close();
 612                  if ($secs2cache>0) {
 613                      $rstest = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr);
 614                  }
 615                  else {
 616                      $rstest = $zthis->SelectLimit($sql, $nrows, $pagecounteroffset, $inputarr, $secs2cache);
 617                  }
 618              }
 619              if ($rstest) $rstest->Close();
 620          }
 621          if ($atlastpage) {
 622              // If we are at the last page or beyond it, we are going to retrieve it
 623              $page = $pagecounter;
 624              if ($page == 1) {
 625                  // We have to do this again in case the last page is the same as
 626                  // the first page, that is, the recordset has only 1 page.
 627                  $atfirstpage = true;
 628              }
 629          }
 630          // We get the data we want
 631          $offset = $nrows * ($page-1);
 632          if ($secs2cache > 0) {
 633              $rsreturn = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr);
 634          }
 635          else {
 636              $rsreturn = $zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
 637          }
 638      } elseif ( $rsreturn->_numOfRows < $test_nrows ) {
 639          // Rows is less than what we asked for, so must be at the last page.
 640          $atlastpage = true;
 641      }
 642  
 643      // Before returning the RecordSet, we set the pagination properties we need
 644      if ($rsreturn) {
 645          $rsreturn->rowsPerPage = $nrows;
 646          $rsreturn->AbsolutePage($page);
 647          $rsreturn->AtFirstPage($atfirstpage);
 648          $rsreturn->AtLastPage($atlastpage);
 649      }
 650      return $rsreturn;
 651  }
 652  
 653  function _adodb_getupdatesql(&$zthis,&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=2)
 654  {
 655      global $ADODB_QUOTE_FIELDNAMES;
 656  
 657          if (!$rs) {
 658              printf(ADODB_BAD_RS,'GetUpdateSQL');
 659              return false;
 660          }
 661  
 662          $fieldUpdatedCount = 0;
 663          $arrFields = _array_change_key_case($arrFields);
 664  
 665          $hasnumeric = isset($rs->fields[0]);
 666          $setFields = '';
 667  
 668          // Loop through all of the fields in the recordset
 669          for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
 670              // Get the field from the recordset
 671              $field = $rs->FetchField($i);
 672  
 673              // If the recordset field is one
 674              // of the fields passed in then process.
 675              $upperfname = strtoupper($field->name);
 676              if (adodb_key_exists($upperfname,$arrFields,$force)) {
 677  
 678                  // If the existing field value in the recordset
 679                  // is different from the value passed in then
 680                  // go ahead and append the field name and new value to
 681                  // the update query.
 682  
 683                  if ($hasnumeric) $val = $rs->fields[$i];
 684                  else if (isset($rs->fields[$upperfname])) $val = $rs->fields[$upperfname];
 685                  else if (isset($rs->fields[$field->name])) $val =  $rs->fields[$field->name];
 686                  else if (isset($rs->fields[strtolower($upperfname)])) $val =  $rs->fields[strtolower($upperfname)];
 687                  else $val = '';
 688  
 689  
 690                  if ($forceUpdate || strcmp($val, $arrFields[$upperfname])) {
 691                      // Set the counter for the number of fields that will be updated.
 692                      $fieldUpdatedCount++;
 693  
 694                      // Based on the datatype of the field
 695                      // Format the value properly for the database
 696                      $type = $rs->MetaType($field->type);
 697  
 698  
 699                      if ($type == 'null') {
 700                          $type = 'C';
 701                      }
 702  
 703                      if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) {
 704                          switch ($ADODB_QUOTE_FIELDNAMES) {
 705                          case 'LOWER':
 706                              $fnameq = $zthis->nameQuote.strtolower($field->name).$zthis->nameQuote;break;
 707                          case 'NATIVE':
 708                              $fnameq = $zthis->nameQuote.$field->name.$zthis->nameQuote;break;
 709                          case 'UPPER':
 710                          default:
 711                              $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;break;
 712                          }
 713                      } else
 714                          $fnameq = $upperfname;
 715  
 716                  //********************************************************//
 717                  if (is_null($arrFields[$upperfname])
 718                      || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
 719                      || $arrFields[$upperfname] === $zthis->null2null
 720                      )
 721                  {
 722                      switch ($force) {
 723  
 724                          //case 0:
 725                          //    //Ignore empty values. This is allready handled in "adodb_key_exists" function.
 726                          //break;
 727  
 728                          case 1:
 729                              //Set null
 730                              $setFields .= $field->name . " = null, ";
 731                          break;
 732  
 733                          case 2:
 734                              //Set empty
 735                              $arrFields[$upperfname] = "";
 736                              $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq);
 737                          break;
 738                          default:
 739                          case 3:
 740                              //Set the value that was given in array, so you can give both null and empty values
 741                              if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) {
 742                                  $setFields .= $field->name . " = null, ";
 743                              } else {
 744                                  $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq);
 745                              }
 746                          break;
 747                      }
 748                  //********************************************************//
 749                  } else {
 750                          //we do this so each driver can customize the sql for
 751                          //DB specific column types.
 752                          //Oracle needs BLOB types to be handled with a returning clause
 753                          //postgres has special needs as well
 754                          $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,
 755                                                            $arrFields, $magicq);
 756                      }
 757                  }
 758              }
 759          }
 760  
 761          // If there were any modified fields then build the rest of the update query.
 762          if ($fieldUpdatedCount > 0 || $forceUpdate) {
 763                      // Get the table name from the existing query.
 764              if (!empty($rs->tableName)) $tableName = $rs->tableName;
 765              else {
 766                  preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName);
 767                  $tableName = $tableName[1];
 768              }
 769              // Get the full where clause excluding the word "WHERE" from
 770              // the existing query.
 771              preg_match('/\sWHERE\s(.*)/is', $rs->sql, $whereClause);
 772  
 773              $discard = false;
 774              // not a good hack, improvements?
 775              if ($whereClause) {
 776              #var_dump($whereClause);
 777                  if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard));
 778                  else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard));
 779                  else if (preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard));
 780                  else preg_match('/\s.*(\) WHERE .*)/is', $whereClause[1], $discard); # see https://sourceforge.net/p/adodb/bugs/37/
 781              } else
 782                  $whereClause = array(false,false);
 783  
 784              if ($discard)
 785                  $whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1]));
 786  
 787              $sql = 'UPDATE '.$tableName.' SET '.substr($setFields, 0, -2);
 788              if (strlen($whereClause[1]) > 0)
 789                  $sql .= ' WHERE '.$whereClause[1];
 790  
 791              return $sql;
 792  
 793          } else {
 794              return false;
 795      }
 796  }
 797  
 798  function adodb_key_exists($key, &$arr,$force=2)
 799  {
 800      if ($force<=0) {
 801          // the following is the old behaviour where null or empty fields are ignored
 802          return (!empty($arr[$key])) || (isset($arr[$key]) && strlen($arr[$key])>0);
 803      }
 804  
 805      if (isset($arr[$key])) return true;
 806      ## null check below
 807      if (ADODB_PHPVER >= 0x4010) return array_key_exists($key,$arr);
 808      return false;
 809  }
 810  
 811  /**
 812   * There is a special case of this function for the oci8 driver.
 813   * The proper way to handle an insert w/ a blob in oracle requires
 814   * a returning clause with bind variables and a descriptor blob.
 815   *
 816   *
 817   */
 818  function _adodb_getinsertsql(&$zthis,&$rs,$arrFields,$magicq=false,$force=2)
 819  {
 820  static $cacheRS = false;
 821  static $cacheSig = 0;
 822  static $cacheCols;
 823      global $ADODB_QUOTE_FIELDNAMES;
 824  
 825      $tableName = '';
 826      $values = '';
 827      $fields = '';
 828      $recordSet = null;
 829      $arrFields = _array_change_key_case($arrFields);
 830      $fieldInsertedCount = 0;
 831  
 832      if (is_string($rs)) {
 833          //ok we have a table name
 834          //try and get the column info ourself.
 835          $tableName = $rs;
 836  
 837          //we need an object for the recordSet
 838          //because we have to call MetaType.
 839          //php can't do a $rsclass::MetaType()
 840          $rsclass = $zthis->rsPrefix.$zthis->databaseType;
 841          $recordSet = new $rsclass(-1,$zthis->fetchMode);
 842          $recordSet->connection = $zthis;
 843  
 844          if (is_string($cacheRS) && $cacheRS == $rs) {
 845              $columns = $cacheCols;
 846          } else {
 847              $columns = $zthis->MetaColumns( $tableName );
 848              $cacheRS = $tableName;
 849              $cacheCols = $columns;
 850          }
 851      } else if (is_subclass_of($rs, 'adorecordset')) {
 852          if (isset($rs->insertSig) && is_integer($cacheRS) && $cacheRS == $rs->insertSig) {
 853              $columns = $cacheCols;
 854          } else {
 855              for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++)
 856                  $columns[] = $rs->FetchField($i);
 857              $cacheRS = $cacheSig;
 858              $cacheCols = $columns;
 859              $rs->insertSig = $cacheSig++;
 860          }
 861          $recordSet = $rs;
 862  
 863      } else {
 864          printf(ADODB_BAD_RS,'GetInsertSQL');
 865          return false;
 866      }
 867  
 868      // Loop through all of the fields in the recordset
 869      foreach( $columns as $field ) {
 870          $upperfname = strtoupper($field->name);
 871          if (adodb_key_exists($upperfname,$arrFields,$force)) {
 872              $bad = false;
 873              if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) {
 874                  switch ($ADODB_QUOTE_FIELDNAMES) {
 875                  case 'LOWER':
 876                      $fnameq = $zthis->nameQuote.strtolower($field->name).$zthis->nameQuote;break;
 877                  case 'NATIVE':
 878                      $fnameq = $zthis->nameQuote.$field->name.$zthis->nameQuote;break;
 879                  case 'UPPER':
 880                  default:
 881                      $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;break;
 882                  }
 883              } else
 884                  $fnameq = $upperfname;
 885  
 886              $type = $recordSet->MetaType($field->type);
 887  
 888              /********************************************************/
 889              if (is_null($arrFields[$upperfname])
 890                  || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
 891                  || $arrFields[$upperfname] === $zthis->null2null
 892                  )
 893                 {
 894                      switch ($force) {
 895  
 896                          case 0: // we must always set null if missing
 897                              $bad = true;
 898                              break;
 899  
 900                          case 1:
 901                              $values  .= "null, ";
 902                          break;
 903  
 904                          case 2:
 905                              //Set empty
 906                              $arrFields[$upperfname] = "";
 907                              $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,$arrFields, $magicq);
 908                          break;
 909  
 910                          default:
 911                          case 3:
 912                              //Set the value that was given in array, so you can give both null and empty values
 913                              if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) {
 914                                  $values  .= "null, ";
 915                              } else {
 916                                  $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields, $magicq);
 917                               }
 918                            break;
 919                       } // switch
 920  
 921              /*********************************************************/
 922              } else {
 923                  //we do this so each driver can customize the sql for
 924                  //DB specific column types.
 925                  //Oracle needs BLOB types to be handled with a returning clause
 926                  //postgres has special needs as well
 927                  $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,
 928                                                 $arrFields, $magicq);
 929              }
 930  
 931              if ($bad) continue;
 932              // Set the counter for the number of fields that will be inserted.
 933              $fieldInsertedCount++;
 934  
 935  
 936              // Get the name of the fields to insert
 937              $fields .= $fnameq . ", ";
 938          }
 939      }
 940  
 941  
 942      // If there were any inserted fields then build the rest of the insert query.
 943      if ($fieldInsertedCount <= 0)  return false;
 944  
 945      // Get the table name from the existing query.
 946      if (!$tableName) {
 947          if (!empty($rs->tableName)) $tableName = $rs->tableName;
 948          else if (preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName))
 949              $tableName = $tableName[1];
 950          else
 951              return false;
 952      }
 953  
 954      // Strip off the comma and space on the end of both the fields
 955      // and their values.
 956      $fields = substr($fields, 0, -2);
 957      $values = substr($values, 0, -2);
 958  
 959      // Append the fields and their values to the insert query.
 960      return 'INSERT INTO '.$tableName.' ( '.$fields.' ) VALUES ( '.$values.' )';
 961  }
 962  
 963  
 964  /**
 965   * This private method is used to help construct
 966   * the update/sql which is generated by GetInsertSQL and GetUpdateSQL.
 967   * It handles the string construction of 1 column -> sql string based on
 968   * the column type.  We want to do 'safe' handling of BLOBs
 969   *
 970   * @param string the type of sql we are trying to create
 971   *                'I' or 'U'.
 972   * @param string column data type from the db::MetaType() method
 973   * @param string the column name
 974   * @param array the column value
 975   *
 976   * @return string
 977   *
 978   */
 979  function _adodb_column_sql_oci8(&$zthis,$action, $type, $fname, $fnameq, $arrFields, $magicq)
 980  {
 981      $sql = '';
 982  
 983      // Based on the datatype of the field
 984      // Format the value properly for the database
 985      switch($type) {
 986      case 'B':
 987          //in order to handle Blobs correctly, we need
 988          //to do some magic for Oracle
 989  
 990          //we need to create a new descriptor to handle
 991          //this properly
 992          if (!empty($zthis->hasReturningInto)) {
 993              if ($action == 'I') {
 994                  $sql = 'empty_blob(), ';
 995              } else {
 996                  $sql = $fnameq. '=empty_blob(), ';
 997              }
 998              //add the variable to the returning clause array
 999              //so the user can build this later in
1000              //case they want to add more to it
1001              $zthis->_returningArray[$fname] = ':xx'.$fname.'xx';
1002          } else if (empty($arrFields[$fname])){
1003              if ($action == 'I') {
1004                  $sql = 'empty_blob(), ';
1005              } else {
1006                  $sql = $fnameq. '=empty_blob(), ';
1007              }
1008          } else {
1009              //this is to maintain compatibility
1010              //with older adodb versions.
1011              $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq,false);
1012          }
1013          break;
1014  
1015      case "X":
1016          //we need to do some more magic here for long variables
1017          //to handle these correctly in oracle.
1018  
1019          //create a safe bind var name
1020          //to avoid conflicts w/ dupes.
1021         if (!empty($zthis->hasReturningInto)) {
1022              if ($action == 'I') {
1023                  $sql = ':xx'.$fname.'xx, ';
1024              } else {
1025                  $sql = $fnameq.'=:xx'.$fname.'xx, ';
1026              }
1027              //add the variable to the returning clause array
1028              //so the user can build this later in
1029              //case they want to add more to it
1030              $zthis->_returningArray[$fname] = ':xx'.$fname.'xx';
1031          } else {
1032              //this is to maintain compatibility
1033              //with older adodb versions.
1034              $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq,false);
1035          }
1036          break;
1037  
1038      default:
1039          $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq,  $arrFields, $magicq,false);
1040          break;
1041      }
1042  
1043      return $sql;
1044  }
1045  
1046  function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq, $recurse=true)
1047  {
1048  
1049      if ($recurse) {
1050          switch($zthis->dataProvider)  {
1051          case 'postgres':
1052              if ($type == 'L') $type = 'C';
1053              break;
1054          case 'oci8':
1055              return _adodb_column_sql_oci8($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq);
1056  
1057          }
1058      }
1059  
1060      switch($type) {
1061          case "C":
1062          case "X":
1063          case 'B':
1064              $val = $zthis->qstr($arrFields[$fname],$magicq);
1065              break;
1066  
1067          case "D":
1068              $val = $zthis->DBDate($arrFields[$fname]);
1069              break;
1070  
1071          case "T":
1072              $val = $zthis->DBTimeStamp($arrFields[$fname]);
1073              break;
1074  
1075          case "N":
1076              $val = $arrFields[$fname];
1077              if (!is_numeric($val)) $val = str_replace(',', '.', (float)$val);
1078              break;
1079  
1080          case "I":
1081          case "R":
1082              $val = $arrFields[$fname];
1083              if (!is_numeric($val)) $val = (integer) $val;
1084              break;
1085  
1086          default:
1087              $val = str_replace(array("'"," ","("),"",$arrFields[$fname]); // basic sql injection defence
1088              if (empty($val)) $val = '0';
1089              break;
1090      }
1091  
1092      if ($action == 'I') return $val . ", ";
1093  
1094  
1095      return $fnameq . "=" . $val  . ", ";
1096  
1097  }
1098  
1099  
1100  
1101  function _adodb_debug_execute(&$zthis, $sql, $inputarr)
1102  {
1103      $ss = '';
1104      if ($inputarr) {
1105          foreach($inputarr as $kk=>$vv) {
1106              if (is_string($vv) && strlen($vv)>64) $vv = substr($vv,0,64).'...';
1107              if (is_null($vv)) $ss .= "($kk=>null) ";
1108              else $ss .= "($kk=>'$vv') ";
1109          }
1110          $ss = "[ $ss ]";
1111      }
1112      $sqlTxt = is_array($sql) ? $sql[0] : $sql;
1113      /*str_replace(', ','##1#__^LF',is_array($sql) ? $sql[0] : $sql);
1114      $sqlTxt = str_replace(',',', ',$sqlTxt);
1115      $sqlTxt = str_replace('##1#__^LF', ', ' ,$sqlTxt);
1116      */
1117      // check if running from browser or command-line
1118      $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
1119  
1120      $dbt = $zthis->databaseType;
1121      if (isset($zthis->dsnType)) $dbt .= '-'.$zthis->dsnType;
1122      if ($inBrowser) {
1123          if ($ss) {
1124              $ss = '<code>'.htmlspecialchars($ss).'</code>';
1125          }
1126          if ($zthis->debug === -1)
1127              ADOConnection::outp( "<br>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<br>\n",false);
1128          else if ($zthis->debug !== -99)
1129              ADOConnection::outp( "<hr>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr>\n",false);
1130      } else {
1131          $ss = "\n   ".$ss;
1132          if ($zthis->debug !== -99)
1133              ADOConnection::outp("-----<hr>\n($dbt): ".$sqlTxt." $ss\n-----<hr>\n",false);
1134      }
1135  
1136      $qID = $zthis->_query($sql,$inputarr);
1137  
1138      /*
1139          Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql
1140          because ErrorNo() calls Execute('SELECT @ERROR'), causing recursion
1141      */
1142      if ($zthis->databaseType == 'mssql') {
1143      // ErrorNo is a slow function call in mssql, and not reliable in PHP 4.0.6
1144  
1145          if($emsg = $zthis->ErrorMsg()) {
1146              if ($err = $zthis->ErrorNo()) {
1147                  if ($zthis->debug === -99)
1148                      ADOConnection::outp( "<hr>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr>\n",false);
1149  
1150                  ADOConnection::outp($err.': '.$emsg);
1151              }
1152          }
1153      } else if (!$qID) {
1154  
1155          if ($zthis->debug === -99)
1156                  if ($inBrowser) ADOConnection::outp( "<hr>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr>\n",false);
1157                  else ADOConnection::outp("-----<hr>\n($dbt): ".$sqlTxt."$ss\n-----<hr>\n",false);
1158  
1159          ADOConnection::outp($zthis->ErrorNo() .': '. $zthis->ErrorMsg());
1160      }
1161  
1162      if ($zthis->debug === 99) _adodb_backtrace(true,9999,2);
1163      return $qID;
1164  }
1165  
1166  # pretty print the debug_backtrace function
1167  function _adodb_backtrace($printOrArr=true,$levels=9999,$skippy=0,$ishtml=null)
1168  {
1169      if (!function_exists('debug_backtrace')) return '';
1170  
1171      if ($ishtml === null) $html =  (isset($_SERVER['HTTP_USER_AGENT']));
1172      else $html = $ishtml;
1173  
1174      $fmt =  ($html) ? "</font><font color=#808080 size=-1> %% line %4d, file: <a href=\"file:/%s\">%s</a></font>" : "%% line %4d, file: %s";
1175  
1176      $MAXSTRLEN = 128;
1177  
1178      $s = ($html) ? '<pre align=left>' : '';
1179  
1180      if (is_array($printOrArr)) $traceArr = $printOrArr;
1181      else $traceArr = debug_backtrace();
1182      array_shift($traceArr);
1183      array_shift($traceArr);
1184      $tabs = sizeof($traceArr)-2;
1185  
1186      foreach ($traceArr as $arr) {
1187          if ($skippy) {$skippy -= 1; continue;}
1188          $levels -= 1;
1189          if ($levels < 0) break;
1190  
1191          $args = array();
1192          for ($i=0; $i < $tabs; $i++) $s .=  ($html) ? ' &nbsp; ' : "\t";
1193          $tabs -= 1;
1194          if ($html) $s .= '<font face="Courier New,Courier">';
1195          if (isset($arr['class'])) $s .= $arr['class'].'.';
1196          if (isset($arr['args']))
1197           foreach($arr['args'] as $v) {
1198              if (is_null($v)) $args[] = 'null';
1199              else if (is_array($v)) $args[] = 'Array['.sizeof($v).']';
1200              else if (is_object($v)) $args[] = 'Object:'.get_class($v);
1201              else if (is_bool($v)) $args[] = $v ? 'true' : 'false';
1202              else {
1203                  $v = (string) @$v;
1204                  $str = htmlspecialchars(str_replace(array("\r","\n"),' ',substr($v,0,$MAXSTRLEN)));
1205                  if (strlen($v) > $MAXSTRLEN) $str .= '...';
1206                  $args[] = $str;
1207              }
1208          }
1209          $s .= $arr['function'].'('.implode(', ',$args).')';
1210  
1211  
1212          $s .= @sprintf($fmt, $arr['line'],$arr['file'],basename($arr['file']));
1213  
1214          $s .= "\n";
1215      }
1216      if ($html) $s .= '</pre>';
1217      if ($printOrArr) print $s;
1218  
1219      return $s;
1220  }
1221  /*
1222  function _adodb_find_from($sql)
1223  {
1224  
1225      $sql = str_replace(array("\n","\r"), ' ', $sql);
1226      $charCount = strlen($sql);
1227  
1228      $inString = false;
1229      $quote = '';
1230      $parentheseCount = 0;
1231      $prevChars = '';
1232      $nextChars = '';
1233  
1234  
1235      for($i = 0; $i < $charCount; $i++) {
1236  
1237          $char = substr($sql,$i,1);
1238          $prevChars = substr($sql,0,$i);
1239          $nextChars = substr($sql,$i+1);
1240  
1241          if((($char == "'" || $char == '"' || $char == '`') && substr($prevChars,-1,1) != '\\') && $inString === false) {
1242              $quote = $char;
1243              $inString = true;
1244          }
1245  
1246          elseif((($char == "'" || $char == '"' || $char == '`') && substr($prevChars,-1,1) != '\\') && $inString === true && $quote == $char) {
1247              $quote = "";
1248              $inString = false;
1249          }
1250  
1251          elseif($char == "(" && $inString === false)
1252              $parentheseCount++;
1253  
1254          elseif($char == ")" && $inString === false && $parentheseCount > 0)
1255              $parentheseCount--;
1256  
1257          elseif($parentheseCount <= 0 && $inString === false && $char == " " && strtoupper(substr($prevChars,-5,5)) == " FROM")
1258              return $i;
1259  
1260      }
1261  }
1262  */


Generated: Wed Jan 22 11:59:49 2025 Cross-referenced by PHPXref 0.7.1