1<?php
2/**
3	@version   v5.21.0-dev  ??-???-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
7	Released under both BSD license and Lesser GPL library license.
8	Whenever there is any discrepancy between the two licenses,
9	the BSD license will take precedence.
10
11	Set tabs to 4 for best viewing.
12
13	Latest version is available at http://adodb.sourceforge.net
14
15	Requires ODBC. Works on Windows and Unix.
16
17	Problems:
18		Where is float/decimal type in pdo_param_type
19		LOB handling for CLOB/BLOB differs significantly
20*/
21
22// security - hide paths
23if (!defined('ADODB_DIR')) die();
24
25
26/*
27enum pdo_param_type {
28PDO::PARAM_NULL, 0
29
30/* int as in long (the php native int type).
31 * If you mark a column as an int, PDO expects get_col to return
32 * a pointer to a long
33PDO::PARAM_INT, 1
34
35/* get_col ptr should point to start of the string buffer
36PDO::PARAM_STR, 2
37
38/* get_col: when len is 0 ptr should point to a php_stream *,
39 * otherwise it should behave like a string. Indicate a NULL field
40 * value by setting the ptr to NULL
41PDO::PARAM_LOB, 3
42
43/* get_col: will expect the ptr to point to a new PDOStatement object handle,
44 * but this isn't wired up yet
45PDO::PARAM_STMT, 4 /* hierarchical result set
46
47/* get_col ptr should point to a zend_bool
48PDO::PARAM_BOOL, 5
49
50
51/* magic flag to denote a parameter as being input/output
52PDO::PARAM_INPUT_OUTPUT = 0x80000000
53};
54*/
55
56function adodb_pdo_type($t)
57{
58	switch($t) {
59	case 2: return 'VARCHAR';
60	case 3: return 'BLOB';
61	default: return 'NUMERIC';
62	}
63}
64
65/*----------------------------------------------------------------------------*/
66
67
68class ADODB_pdo extends ADOConnection {
69	var $databaseType = "pdo";
70	var $dataProvider = "pdo";
71	var $fmtDate = "'Y-m-d'";
72	var $fmtTimeStamp = "'Y-m-d, h:i:sA'";
73	var $replaceQuote = "''"; // string to use to replace quotes
74	var $hasAffectedRows = true;
75	var $_bindInputArray = true;
76	var $_genIDSQL;
77	var $_genSeqSQL = "create table %s (id integer)";
78	var $_dropSeqSQL;
79	var $_autocommit = true;
80	var $_haserrorfunctions = true;
81	var $_lastAffectedRows = 0;
82
83	var $_errormsg = false;
84	var $_errorno = false;
85
86	var $dsnType = '';
87	var $stmt = false;
88	var $_driver;
89
90	function _UpdatePDO()
91	{
92		$d = $this->_driver;
93		$this->fmtDate = $d->fmtDate;
94		$this->fmtTimeStamp = $d->fmtTimeStamp;
95		$this->replaceQuote = $d->replaceQuote;
96		$this->sysDate = $d->sysDate;
97		$this->sysTimeStamp = $d->sysTimeStamp;
98		$this->random = $d->random;
99		$this->concat_operator = $d->concat_operator;
100		$this->nameQuote = $d->nameQuote;
101
102		$this->hasGenID = $d->hasGenID;
103		$this->_genIDSQL = $d->_genIDSQL;
104		$this->_genSeqSQL = $d->_genSeqSQL;
105		$this->_dropSeqSQL = $d->_dropSeqSQL;
106
107		$d->_init($this);
108	}
109
110	function Time()
111	{
112		if (!empty($this->_driver->_hasdual)) {
113			$sql = "select $this->sysTimeStamp from dual";
114		}
115		else {
116			$sql = "select $this->sysTimeStamp";
117		}
118
119		$rs = $this->_Execute($sql);
120		if ($rs && !$rs->EOF) {
121			return $this->UnixTimeStamp(reset($rs->fields));
122		}
123
124		return false;
125	}
126
127	// returns true or false
128	function _connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persist=false)
129	{
130		$at = strpos($argDSN,':');
131		$this->dsnType = substr($argDSN,0,$at);
132
133		if ($argDatabasename) {
134			switch($this->dsnType){
135				case 'sqlsrv':
136					$argDSN .= ';database='.$argDatabasename;
137					break;
138				case 'mssql':
139				case 'mysql':
140				case 'oci':
141				case 'pgsql':
142				case 'sqlite':
143				default:
144					$argDSN .= ';dbname='.$argDatabasename;
145			}
146		}
147		try {
148			$this->_connectionID = new PDO($argDSN, $argUsername, $argPassword);
149		} catch (Exception $e) {
150			$this->_connectionID = false;
151			$this->_errorno = -1;
152			//var_dump($e);
153			$this->_errormsg = 'Connection attempt failed: '.$e->getMessage();
154			return false;
155		}
156
157		if ($this->_connectionID) {
158			switch(ADODB_ASSOC_CASE){
159				case ADODB_ASSOC_CASE_LOWER:
160					$m = PDO::CASE_LOWER;
161					break;
162				case ADODB_ASSOC_CASE_UPPER:
163					$m = PDO::CASE_UPPER;
164					break;
165				default:
166				case ADODB_ASSOC_CASE_NATIVE:
167					$m = PDO::CASE_NATURAL;
168					break;
169			}
170
171			//$this->_connectionID->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_SILENT );
172			$this->_connectionID->setAttribute(PDO::ATTR_CASE,$m);
173
174			$class = 'ADODB_pdo_'.$this->dsnType;
175			//$this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,true);
176			switch($this->dsnType) {
177				case 'mssql':
178				case 'mysql':
179				case 'oci':
180				case 'pgsql':
181				case 'sqlite':
182				case 'sqlsrv':
183					include_once(ADODB_DIR.'/drivers/adodb-pdo_'.$this->dsnType.'.inc.php');
184					break;
185			}
186			if (class_exists($class)) {
187				$this->_driver = new $class();
188			}
189			else {
190				$this->_driver = new ADODB_pdo_base();
191			}
192
193			$this->_driver->_connectionID = $this->_connectionID;
194			$this->_UpdatePDO();
195			return true;
196		}
197		$this->_driver = new ADODB_pdo_base();
198		return false;
199	}
200
201	function Concat()
202	{
203		$args = func_get_args();
204		if(method_exists($this->_driver, 'Concat')) {
205			return call_user_func_array(array($this->_driver, 'Concat'), $args);
206		}
207
208		if (PHP_VERSION >= 5.3) {
209			return call_user_func_array('parent::Concat', $args);
210		}
211		return call_user_func_array(array($this,'parent::Concat'), $args);
212	}
213
214	// returns true or false
215	function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
216	{
217		return $this->_connect($argDSN, $argUsername, $argPassword, $argDatabasename, true);
218	}
219
220	/*------------------------------------------------------------------------------*/
221
222
223	function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0)
224	{
225		$save = $this->_driver->fetchMode;
226		$this->_driver->fetchMode = $this->fetchMode;
227		$this->_driver->debug = $this->debug;
228		$ret = $this->_driver->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
229		$this->_driver->fetchMode = $save;
230		return $ret;
231	}
232
233
234	function ServerInfo()
235	{
236		return $this->_driver->ServerInfo();
237	}
238
239	function MetaTables($ttype=false,$showSchema=false,$mask=false)
240	{
241		return $this->_driver->MetaTables($ttype,$showSchema,$mask);
242	}
243
244	function MetaColumns($table,$normalize=true)
245	{
246		return $this->_driver->MetaColumns($table,$normalize);
247	}
248
249	function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
250	{
251		$obj = $stmt[1];
252		if ($type) {
253			$obj->bindParam($name, $var, $type, $maxLen);
254		}
255		else {
256			$obj->bindParam($name, $var);
257		}
258	}
259
260	function OffsetDate($dayFraction,$date=false)
261	{
262		return $this->_driver->OffsetDate($dayFraction,$date);
263	}
264
265	function ErrorMsg()
266	{
267		if ($this->_errormsg !== false) {
268			return $this->_errormsg;
269		}
270		if (!empty($this->_stmt)) {
271			$arr = $this->_stmt->errorInfo();
272		}
273		else if (!empty($this->_connectionID)) {
274			$arr = $this->_connectionID->errorInfo();
275		}
276		else {
277			return 'No Connection Established';
278		}
279
280		if ($arr) {
281			if (sizeof($arr)<2) {
282				return '';
283			}
284			if ((integer)$arr[0]) {
285				return $arr[2];
286			}
287			else {
288				return '';
289			}
290		}
291		else {
292			return '-1';
293		}
294	}
295
296
297	function ErrorNo()
298	{
299		if ($this->_errorno !== false) {
300			return $this->_errorno;
301		}
302		if (!empty($this->_stmt)) {
303			$err = $this->_stmt->errorCode();
304		}
305		else if (!empty($this->_connectionID)) {
306			$arr = $this->_connectionID->errorInfo();
307			if (isset($arr[0])) {
308				$err = $arr[0];
309			}
310			else {
311				$err = -1;
312			}
313		} else {
314			return 0;
315		}
316
317		if ($err == '00000') {
318			return 0; // allows empty check
319		}
320		return $err;
321	}
322
323	function SetTransactionMode($transaction_mode)
324	{
325		if(method_exists($this->_driver, 'SetTransactionMode')) {
326			return $this->_driver->SetTransactionMode($transaction_mode);
327		}
328
329		return parent::SetTransactionMode($seqname);
330	}
331
332	function BeginTrans()
333	{
334		if(method_exists($this->_driver, 'BeginTrans')) {
335			return $this->_driver->BeginTrans();
336		}
337
338		if (!$this->hasTransactions) {
339			return false;
340		}
341		if ($this->transOff) {
342			return true;
343		}
344		$this->transCnt += 1;
345		$this->_autocommit = false;
346		$this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,false);
347
348		return $this->_connectionID->beginTransaction();
349	}
350
351	function CommitTrans($ok=true)
352	{
353		if(method_exists($this->_driver, 'CommitTrans')) {
354			return $this->_driver->CommitTrans($ok);
355		}
356
357		if (!$this->hasTransactions) {
358			return false;
359		}
360		if ($this->transOff) {
361			return true;
362		}
363		if (!$ok) {
364			return $this->RollbackTrans();
365		}
366		if ($this->transCnt) {
367			$this->transCnt -= 1;
368		}
369		$this->_autocommit = true;
370
371		$ret = $this->_connectionID->commit();
372		$this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,true);
373		return $ret;
374	}
375
376	function RollbackTrans()
377	{
378		if(method_exists($this->_driver, 'RollbackTrans')) {
379			return $this->_driver->RollbackTrans();
380		}
381
382		if (!$this->hasTransactions) {
383			return false;
384		}
385		if ($this->transOff) {
386			return true;
387		}
388		if ($this->transCnt) {
389			$this->transCnt -= 1;
390		}
391		$this->_autocommit = true;
392
393		$ret = $this->_connectionID->rollback();
394		$this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,true);
395		return $ret;
396	}
397
398	function Prepare($sql)
399	{
400		$this->_stmt = $this->_connectionID->prepare($sql);
401		if ($this->_stmt) {
402			return array($sql,$this->_stmt);
403		}
404
405		return false;
406	}
407
408	function PrepareStmt($sql)
409	{
410		$stmt = $this->_connectionID->prepare($sql);
411		if (!$stmt) {
412			return false;
413		}
414		$obj = new ADOPDOStatement($stmt,$this);
415		return $obj;
416	}
417
418	function CreateSequence($seqname='adodbseq',$startID=1)
419	{
420		if(method_exists($this->_driver, 'CreateSequence')) {
421			return $this->_driver->CreateSequence($seqname, $startID);
422		}
423
424		return parent::CreateSequence($seqname, $startID);
425	}
426
427	function DropSequence($seqname='adodbseq')
428	{
429		if(method_exists($this->_driver, 'DropSequence')) {
430			return $this->_driver->DropSequence($seqname);
431		}
432
433		return parent::DropSequence($seqname);
434	}
435
436	function GenID($seqname='adodbseq',$startID=1)
437	{
438		if(method_exists($this->_driver, 'GenID')) {
439			return $this->_driver->GenID($seqname, $startID);
440		}
441
442		return parent::GenID($seqname, $startID);
443	}
444
445
446	/* returns queryID or false */
447	function _query($sql,$inputarr=false)
448	{
449		if (is_array($sql)) {
450			$stmt = $sql[1];
451		} else {
452			$stmt = $this->_connectionID->prepare($sql);
453		}
454		#adodb_backtrace();
455		#var_dump($this->_bindInputArray);
456		if ($stmt) {
457			$this->_driver->debug = $this->debug;
458			if ($inputarr) {
459				$ok = $stmt->execute($inputarr);
460			}
461			else {
462				$ok = $stmt->execute();
463			}
464		}
465
466
467		$this->_errormsg = false;
468		$this->_errorno = false;
469
470		if ($ok) {
471			$this->_stmt = $stmt;
472			return $stmt;
473		}
474
475		if ($stmt) {
476
477			$arr = $stmt->errorinfo();
478			if ((integer)$arr[1]) {
479				$this->_errormsg = $arr[2];
480				$this->_errorno = $arr[1];
481			}
482
483		} else {
484			$this->_errormsg = false;
485			$this->_errorno = false;
486		}
487		return false;
488	}
489
490	// returns true or false
491	function _close()
492	{
493		$this->_stmt = false;
494		return true;
495	}
496
497	function _affectedrows()
498	{
499		return ($this->_stmt) ? $this->_stmt->rowCount() : 0;
500	}
501
502	function _insertid()
503	{
504		return ($this->_connectionID) ? $this->_connectionID->lastInsertId() : 0;
505	}
506}
507
508class ADODB_pdo_base extends ADODB_pdo {
509
510	var $sysDate = "'?'";
511	var $sysTimeStamp = "'?'";
512
513
514	function _init($parentDriver)
515	{
516		$parentDriver->_bindInputArray = true;
517		#$parentDriver->_connectionID->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true);
518	}
519
520	function ServerInfo()
521	{
522		return ADOConnection::ServerInfo();
523	}
524
525	function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0)
526	{
527		$ret = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
528		return $ret;
529	}
530
531	function MetaTables($ttype=false,$showSchema=false,$mask=false)
532	{
533		return false;
534	}
535
536	function MetaColumns($table,$normalize=true)
537	{
538		return false;
539	}
540}
541
542class ADOPDOStatement {
543
544	var $databaseType = "pdo";
545	var $dataProvider = "pdo";
546	var $_stmt;
547	var $_connectionID;
548
549	function __construct($stmt,$connection)
550	{
551		$this->_stmt = $stmt;
552		$this->_connectionID = $connection;
553	}
554
555	function Execute($inputArr=false)
556	{
557		$savestmt = $this->_connectionID->_stmt;
558		$rs = $this->_connectionID->Execute(array(false,$this->_stmt),$inputArr);
559		$this->_connectionID->_stmt = $savestmt;
560		return $rs;
561	}
562
563	function InParameter(&$var,$name,$maxLen=4000,$type=false)
564	{
565
566		if ($type) {
567			$this->_stmt->bindParam($name,$var,$type,$maxLen);
568		}
569		else {
570			$this->_stmt->bindParam($name, $var);
571		}
572	}
573
574	function Affected_Rows()
575	{
576		return ($this->_stmt) ? $this->_stmt->rowCount() : 0;
577	}
578
579	function ErrorMsg()
580	{
581		if ($this->_stmt) {
582			$arr = $this->_stmt->errorInfo();
583		}
584		else {
585			$arr = $this->_connectionID->errorInfo();
586		}
587
588		if (is_array($arr)) {
589			if ((integer) $arr[0] && isset($arr[2])) {
590				return $arr[2];
591			}
592			else {
593				return '';
594			}
595		} else {
596			return '-1';
597		}
598	}
599
600	function NumCols()
601	{
602		return ($this->_stmt) ? $this->_stmt->columnCount() : 0;
603	}
604
605	function ErrorNo()
606	{
607		if ($this->_stmt) {
608			return $this->_stmt->errorCode();
609		}
610		else {
611			return $this->_connectionID->errorInfo();
612		}
613	}
614}
615
616/*--------------------------------------------------------------------------------------
617	Class Name: Recordset
618--------------------------------------------------------------------------------------*/
619
620class ADORecordSet_pdo extends ADORecordSet {
621
622	var $bind = false;
623	var $databaseType = "pdo";
624	var $dataProvider = "pdo";
625
626	function __construct($id,$mode=false)
627	{
628		if ($mode === false) {
629			global $ADODB_FETCH_MODE;
630			$mode = $ADODB_FETCH_MODE;
631		}
632		$this->adodbFetchMode = $mode;
633		switch($mode) {
634		case ADODB_FETCH_NUM: $mode = PDO::FETCH_NUM; break;
635		case ADODB_FETCH_ASSOC:  $mode = PDO::FETCH_ASSOC; break;
636
637		case ADODB_FETCH_BOTH:
638		default: $mode = PDO::FETCH_BOTH; break;
639		}
640		$this->fetchMode = $mode;
641
642		$this->_queryID = $id;
643		parent::__construct($id);
644	}
645
646
647	function Init()
648	{
649		if ($this->_inited) {
650			return;
651		}
652		$this->_inited = true;
653		if ($this->_queryID) {
654			@$this->_initrs();
655		}
656		else {
657			$this->_numOfRows = 0;
658			$this->_numOfFields = 0;
659		}
660		if ($this->_numOfRows != 0 && $this->_currentRow == -1) {
661			$this->_currentRow = 0;
662			if ($this->EOF = ($this->_fetch() === false)) {
663				$this->_numOfRows = 0; // _numOfRows could be -1
664			}
665		} else {
666			$this->EOF = true;
667		}
668	}
669
670	function _initrs()
671	{
672	global $ADODB_COUNTRECS;
673
674		$this->_numOfRows = ($ADODB_COUNTRECS) ? @$this->_queryID->rowCount() : -1;
675		if (!$this->_numOfRows) {
676			$this->_numOfRows = -1;
677		}
678		$this->_numOfFields = $this->_queryID->columnCount();
679	}
680
681	// returns the field object
682	function FetchField($fieldOffset = -1)
683	{
684		$off=$fieldOffset+1; // offsets begin at 1
685
686		$o= new ADOFieldObject();
687		$arr = @$this->_queryID->getColumnMeta($fieldOffset);
688		if (!$arr) {
689			$o->name = 'bad getColumnMeta()';
690			$o->max_length = -1;
691			$o->type = 'VARCHAR';
692			$o->precision = 0;
693	#		$false = false;
694			return $o;
695		}
696		//adodb_pr($arr);
697		$o->name = $arr['name'];
698		if (isset($arr['native_type']) && $arr['native_type'] <> "null") {
699			$o->type = $arr['native_type'];
700		}
701		else {
702			$o->type = adodb_pdo_type($arr['pdo_type']);
703		}
704		$o->max_length = $arr['len'];
705		$o->precision = $arr['precision'];
706
707		switch(ADODB_ASSOC_CASE) {
708			case ADODB_ASSOC_CASE_LOWER:
709				$o->name = strtolower($o->name);
710				break;
711			case ADODB_ASSOC_CASE_UPPER:
712				$o->name = strtoupper($o->name);
713				break;
714		}
715		return $o;
716	}
717
718	function _seek($row)
719	{
720		return false;
721	}
722
723	function _fetch()
724	{
725		if (!$this->_queryID) {
726			return false;
727		}
728
729		$this->fields = $this->_queryID->fetch($this->fetchMode);
730		return !empty($this->fields);
731	}
732
733	function _close()
734	{
735		$this->_queryID = false;
736	}
737
738	function Fields($colname)
739	{
740		if ($this->adodbFetchMode != ADODB_FETCH_NUM) {
741			return @$this->fields[$colname];
742		}
743
744		if (!$this->bind) {
745			$this->bind = array();
746			for ($i=0; $i < $this->_numOfFields; $i++) {
747				$o = $this->FetchField($i);
748				$this->bind[strtoupper($o->name)] = $i;
749			}
750		}
751		return $this->fields[$this->bind[strtoupper($colname)]];
752	}
753
754}
755