adodb-text.inc.php 9.8 KB

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