1<?php
2/**
3 * @package     FrameworkOnFramework
4 * @subpackage  database
5 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
6 * @license     GNU General Public License version 2 or later; see LICENSE.txt
7 * @note        This file has been modified by the Joomla! Project and no longer reflects the original work of its author.
8 *
9 * This file is adapted from the Joomla! Platform. It is used to iterate a database cursor returning FOFTable objects
10 * instead of plain stdClass objects
11 */
12
13// Protect from unauthorized access
14defined('FOF_INCLUDED') or die;
15
16/**
17 * This crazy three line bit is required to convince Joomla! to load JDatabaseInterface which is on the same file as the
18 * abstract JDatabaseDriver class for reasons that beat me. It makes no sense. Furthermore, jimport on Joomla! 3.4
19 * doesn't seem to actually load the file, merely registering the association in the autoloader. Hence the class_exists
20 * in here.
21 */
22jimport('joomla.database.driver');
23jimport('joomla.database.driver.mysqli');
24class_exists('JDatabaseDriver', true);
25
26/**
27 * Joomla! pass-through database driver.
28 */
29class FOFDatabaseDriverJoomla extends FOFDatabase implements FOFDatabaseInterface
30{
31	/** @var FOFDatabase The real database connection object */
32	private $dbo;
33
34	/**
35	 * @var    string  The character(s) used to quote SQL statement names such as table names or field names,
36	 *                 etc.  The child classes should define this as necessary.  If a single character string the
37	 *                 same character is used for both sides of the quoted name, else the first character will be
38	 *                 used for the opening quote and the second for the closing quote.
39	 * @since  11.1
40	 */
41	protected $nameQuote = '';
42
43	/**
44	 * Is this driver supported
45	 *
46	 * @since  11.2
47	 */
48	public static function isSupported()
49	{
50		return true;
51	}
52
53	/**
54	 * Database object constructor
55	 *
56	 * @param    array $options List of options used to configure the connection
57	 */
58	public function __construct($options = array())
59	{
60		// Get best matching Akeeba Backup driver instance
61		$this->dbo = JFactory::getDbo();
62
63		$reflection = new ReflectionClass($this->dbo);
64
65		try
66		{
67			$refProp = $reflection->getProperty('nameQuote');
68			$refProp->setAccessible(true);
69			$this->nameQuote = $refProp->getValue($this->dbo);
70		}
71		catch (Exception $e)
72		{
73			$this->nameQuote = '`';
74		}
75	}
76
77	public function close()
78	{
79		if (method_exists($this->dbo, 'close'))
80		{
81			$this->dbo->close();
82		}
83		elseif (method_exists($this->dbo, 'disconnect'))
84		{
85			$this->dbo->disconnect();
86		}
87	}
88
89	public function disconnect()
90	{
91		$this->close();
92	}
93
94	public function open()
95	{
96		if (method_exists($this->dbo, 'open'))
97		{
98			$this->dbo->open();
99		}
100		elseif (method_exists($this->dbo, 'connect'))
101		{
102			$this->dbo->connect();
103		}
104	}
105
106	public function connect()
107	{
108		$this->open();
109	}
110
111	public function connected()
112	{
113		if (method_exists($this->dbo, 'connected'))
114		{
115			return $this->dbo->connected();
116		}
117
118		return true;
119	}
120
121	public function escape($text, $extra = false)
122	{
123		return $this->dbo->escape($text, $extra);
124	}
125
126	public function execute()
127	{
128		if (method_exists($this->dbo, 'execute'))
129		{
130			return $this->dbo->execute();
131		}
132
133		return $this->dbo->query();
134	}
135
136	public function getAffectedRows()
137	{
138		if (method_exists($this->dbo, 'getAffectedRows'))
139		{
140			return $this->dbo->getAffectedRows();
141		}
142
143		return 0;
144	}
145
146	public function getCollation()
147	{
148		if (method_exists($this->dbo, 'getCollation'))
149		{
150			return $this->dbo->getCollation();
151		}
152
153		return 'utf8_general_ci';
154	}
155
156	public function getConnection()
157	{
158		if (method_exists($this->dbo, 'getConnection'))
159		{
160			return $this->dbo->getConnection();
161		}
162
163		return null;
164	}
165
166	public function getCount()
167	{
168		if (method_exists($this->dbo, 'getCount'))
169		{
170			return $this->dbo->getCount();
171		}
172
173		return 0;
174	}
175
176	public function getDateFormat()
177	{
178		if (method_exists($this->dbo, 'getDateFormat'))
179		{
180			return $this->dbo->getDateFormat();
181		}
182
183		return 'Y-m-d H:i:s';;
184	}
185
186	public function getMinimum()
187	{
188		if (method_exists($this->dbo, 'getMinimum'))
189		{
190			return $this->dbo->getMinimum();
191		}
192
193		return '5.0.40';
194	}
195
196	public function getNullDate()
197	{
198		if (method_exists($this->dbo, 'getNullDate'))
199		{
200			return $this->dbo->getNullDate();
201		}
202
203		return '0000-00-00 00:00:00';
204	}
205
206	public function getNumRows($cursor = null)
207	{
208		if (method_exists($this->dbo, 'getNumRows'))
209		{
210			return $this->dbo->getNumRows($cursor);
211		}
212
213		return 0;
214	}
215
216	public function getQuery($new = false)
217	{
218		if (method_exists($this->dbo, 'getQuery'))
219		{
220			return $this->dbo->getQuery($new);
221		}
222
223		return null;
224	}
225
226	public function getTableColumns($table, $typeOnly = true)
227	{
228		if (method_exists($this->dbo, 'getTableColumns'))
229		{
230			return $this->dbo->getTableColumns($table, $typeOnly);
231		}
232
233		$result = $this->dbo->getTableFields(array($table), $typeOnly);
234
235		return $result[$table];
236	}
237
238	public function getTableKeys($tables)
239	{
240		if (method_exists($this->dbo, 'getTableKeys'))
241		{
242			return $this->dbo->getTableKeys($tables);
243		}
244
245		return array();
246	}
247
248	public function getTableList()
249	{
250		if (method_exists($this->dbo, 'getTableList'))
251		{
252			return $this->dbo->getTableList();
253		}
254
255		return array();
256	}
257
258	public function getVersion()
259	{
260		if (method_exists($this->dbo, 'getVersion'))
261		{
262			return $this->dbo->getVersion();
263		}
264
265		return '5.0.40';
266	}
267
268	public function insertid()
269	{
270		if (method_exists($this->dbo, 'insertid'))
271		{
272			return $this->dbo->insertid();
273		}
274
275		return null;
276	}
277
278	public function insertObject($table, &$object, $key = null)
279	{
280		if (method_exists($this->dbo, 'insertObject'))
281		{
282			return $this->dbo->insertObject($table, $object, $key);
283		}
284
285		return null;
286	}
287
288	public function loadAssoc()
289	{
290		if (method_exists($this->dbo, 'loadAssoc'))
291		{
292			return $this->dbo->loadAssoc();
293		}
294
295		return null;
296	}
297
298	public function loadAssocList($key = null, $column = null)
299	{
300		if (method_exists($this->dbo, 'loadAssocList'))
301		{
302			return $this->dbo->loadAssocList($key, $column);
303		}
304
305		return null;
306	}
307
308	public function loadObject($class = 'stdClass')
309	{
310		if (method_exists($this->dbo, 'loadObject'))
311		{
312			return $this->dbo->loadObject($class);
313		}
314
315		return null;
316	}
317
318	public function loadObjectList($key = '', $class = 'stdClass')
319	{
320		if (method_exists($this->dbo, 'loadObjectList'))
321		{
322			return $this->dbo->loadObjectList($key, $class);
323		}
324
325		return null;
326	}
327
328	public function loadResult()
329	{
330		if (method_exists($this->dbo, 'loadResult'))
331		{
332			return $this->dbo->loadResult();
333		}
334
335		return null;
336	}
337
338	public function loadRow()
339	{
340		if (method_exists($this->dbo, 'loadRow'))
341		{
342			return $this->dbo->loadRow();
343		}
344
345		return null;
346	}
347
348	public function loadRowList($key = null)
349	{
350		if (method_exists($this->dbo, 'loadRowList'))
351		{
352			return $this->dbo->loadRowList($key);
353		}
354
355		return null;
356	}
357
358	public function lockTable($tableName)
359	{
360		if (method_exists($this->dbo, 'lockTable'))
361		{
362			return $this->dbo->lockTable($this);
363		}
364
365		return $this;
366	}
367
368	public function quote($text, $escape = true)
369	{
370		if (method_exists($this->dbo, 'quote'))
371		{
372			return $this->dbo->quote($text, $escape);
373		}
374
375		return $text;
376	}
377
378	public function select($database)
379	{
380		if (method_exists($this->dbo, 'select'))
381		{
382			return $this->dbo->select($database);
383		}
384
385		return false;
386	}
387
388	public function setQuery($query, $offset = 0, $limit = 0)
389	{
390		if (method_exists($this->dbo, 'setQuery'))
391		{
392			return $this->dbo->setQuery($query, $offset, $limit);
393		}
394
395		return false;
396	}
397
398	public function transactionCommit($toSavepoint = false)
399	{
400		if (method_exists($this->dbo, 'transactionCommit'))
401		{
402			$this->dbo->transactionCommit($toSavepoint);
403		}
404	}
405
406	public function transactionRollback($toSavepoint = false)
407	{
408		if (method_exists($this->dbo, 'transactionRollback'))
409		{
410			$this->dbo->transactionRollback($toSavepoint);
411		}
412	}
413
414	public function transactionStart($asSavepoint = false)
415	{
416		if (method_exists($this->dbo, 'transactionStart'))
417		{
418			$this->dbo->transactionStart($asSavepoint);
419		}
420	}
421
422	public function unlockTables()
423	{
424		if (method_exists($this->dbo, 'unlockTables'))
425		{
426			return $this->dbo->unlockTables();
427		}
428
429		return $this;
430	}
431
432	public function updateObject($table, &$object, $key, $nulls = false)
433	{
434		if (method_exists($this->dbo, 'updateObject'))
435		{
436			return $this->dbo->updateObject($table, $object, $key, $nulls);
437		}
438
439		return false;
440	}
441
442	public function getLog()
443	{
444		if (method_exists($this->dbo, 'getLog'))
445		{
446			return $this->dbo->getLog();
447		}
448
449		return array();
450	}
451
452	public function dropTable($table, $ifExists = true)
453	{
454		if (method_exists($this->dbo, 'dropTable'))
455		{
456			return $this->dbo->dropTable($table, $ifExists);
457		}
458
459		return $this;
460	}
461
462	public function getTableCreate($tables)
463	{
464		if (method_exists($this->dbo, 'getTableCreate'))
465		{
466			return $this->dbo->getTableCreate($tables);
467		}
468
469		return array();
470	}
471
472	public function renameTable($oldTable, $newTable, $backup = null, $prefix = null)
473	{
474		if (method_exists($this->dbo, 'renameTable'))
475		{
476			return $this->dbo->renameTable($oldTable, $newTable, $backup, $prefix);
477		}
478
479		return $this;
480	}
481
482	public function setUtf()
483	{
484		if (method_exists($this->dbo, 'setUtf'))
485		{
486			return $this->dbo->setUtf();
487		}
488
489		return false;
490	}
491
492
493	protected function freeResult($cursor = null)
494	{
495		return false;
496	}
497
498	/**
499	 * Method to get an array of values from the <var>$offset</var> field in each row of the result set from
500	 * the database query.
501	 *
502	 * @param   integer  $offset  The row offset to use to build the result array.
503	 *
504	 * @return  mixed    The return value or null if the query failed.
505	 *
506	 * @since   11.1
507	 * @throws  RuntimeException
508	 */
509	public function loadColumn($offset = 0)
510	{
511		if (method_exists($this->dbo, 'loadColumn'))
512		{
513			return $this->dbo->loadColumn($offset);
514		}
515
516		return $this->dbo->loadResultArray($offset);
517	}
518
519	/**
520	 * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection
521	 * risks and reserved word conflicts.
522	 *
523	 * @param   mixed  $name  The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
524	 *                        Each type supports dot-notation name.
525	 * @param   mixed  $as    The AS query part associated to $name. It can be string or array, in latter case it has to be
526	 *                        same length of $name; if is null there will not be any AS part for string or array element.
527	 *
528	 * @return  mixed  The quote wrapped name, same type of $name.
529	 *
530	 * @since   11.1
531	 */
532	public function quoteName($name, $as = null)
533	{
534		if (is_string($name))
535		{
536			$quotedName = $this->quoteNameStr(explode('.', $name));
537
538			$quotedAs = '';
539
540			if (!is_null($as))
541			{
542				settype($as, 'array');
543				$quotedAs .= ' AS ' . $this->quoteNameStr($as);
544			}
545
546			return $quotedName . $quotedAs;
547		}
548		else
549		{
550			$fin = array();
551
552			if (is_null($as))
553			{
554				foreach ($name as $str)
555				{
556					$fin[] = $this->quoteName($str);
557				}
558			}
559			elseif (is_array($name) && (count($name) == count($as)))
560			{
561				$count = count($name);
562
563				for ($i = 0; $i < $count; $i++)
564				{
565					$fin[] = $this->quoteName($name[$i], $as[$i]);
566				}
567			}
568
569			return $fin;
570		}
571	}
572
573	/**
574	 * Quote strings coming from quoteName call.
575	 *
576	 * @param   array  $strArr  Array of strings coming from quoteName dot-explosion.
577	 *
578	 * @return  string  Dot-imploded string of quoted parts.
579	 *
580	 * @since 11.3
581	 */
582	protected function quoteNameStr($strArr)
583	{
584		$parts = array();
585		$q = $this->nameQuote;
586
587		foreach ($strArr as $part)
588		{
589			if (is_null($part))
590			{
591				continue;
592			}
593
594			if (strlen($q) == 1)
595			{
596				$parts[] = $q . $part . $q;
597			}
598			else
599			{
600				$parts[] = $q[0] . $part . $q[1];
601			}
602		}
603
604		return implode('.', $parts);
605	}
606
607	/**
608	 * Gets the error message from the database connection.
609	 *
610	 * @param   boolean  $escaped  True to escape the message string for use in JavaScript.
611	 *
612	 * @return  string  The error message for the most recent query.
613	 *
614	 * @since   11.1
615	 */
616	public function getErrorMsg($escaped = false)
617	{
618		if (method_exists($this->dbo, 'getErrorMsg'))
619		{
620			$errorMessage = $this->dbo->getErrorMsg();
621		}
622		else
623		{
624			$errorMessage = $this->errorMsg;
625		}
626
627		if ($escaped)
628		{
629			return addslashes($errorMessage);
630		}
631
632		return $errorMessage;
633	}
634
635	/**
636	 * Gets the error number from the database connection.
637	 *
638	 * @return      integer  The error number for the most recent query.
639	 *
640	 * @since       11.1
641	 * @deprecated  13.3 (Platform) & 4.0 (CMS)
642	 */
643	public function getErrorNum()
644	{
645		if (method_exists($this->dbo, 'getErrorNum'))
646		{
647			$errorNum = $this->dbo->getErrorNum();
648		}
649		else
650		{
651			$errorNum = $this->getErrorNum;
652		}
653
654		return $errorNum;
655	}
656
657	/**
658	 * Return the most recent error message for the database connector.
659	 *
660	 * @param   boolean  $showSQL  True to display the SQL statement sent to the database as well as the error.
661	 *
662	 * @return  string  The error message for the most recent query.
663	 */
664	public function stderr($showSQL = false)
665	{
666		if (method_exists($this->dbo, 'stderr'))
667		{
668			return $this->dbo->stderr($showSQL);
669		}
670
671		return parent::stderr($showSQL);
672	}
673
674	/**
675	 * Magic method to proxy all calls to the loaded database driver object
676	 */
677	public function __call($name, array $arguments)
678	{
679		if (is_null($this->dbo))
680		{
681			throw new Exception('FOF database driver is not loaded');
682		}
683
684		if (method_exists($this->dbo, $name) || in_array($name, array('q', 'nq', 'qn', 'query')))
685		{
686			switch ($name)
687			{
688				case 'execute':
689					$name = 'query';
690					break;
691
692				case 'q':
693					$name = 'quote';
694					break;
695
696				case 'qn':
697				case 'nq':
698					switch (count($arguments))
699					{
700						case 0 :
701							$result = $this->quoteName();
702							break;
703						case 1 :
704							$result = $this->quoteName($arguments[0]);
705							break;
706						case 2:
707						default:
708							$result = $this->quoteName($arguments[0], $arguments[1]);
709							break;
710					}
711					return $result;
712
713					break;
714			}
715
716			switch (count($arguments))
717			{
718				case 0 :
719					$result = $this->dbo->$name();
720					break;
721				case 1 :
722					$result = $this->dbo->$name($arguments[0]);
723					break;
724				case 2:
725					$result = $this->dbo->$name($arguments[0], $arguments[1]);
726					break;
727				case 3:
728					$result = $this->dbo->$name($arguments[0], $arguments[1], $arguments[2]);
729					break;
730				case 4:
731					$result = $this->dbo->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3]);
732					break;
733				case 5:
734					$result = $this->dbo->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]);
735					break;
736				default:
737					// Resort to using call_user_func_array for many segments
738					$result = call_user_func_array(array($this->dbo, $name), $arguments);
739			}
740
741			if (class_exists('JDatabase') && is_object($result) && ($result instanceof JDatabase))
742			{
743				return $this;
744			}
745
746			return $result;
747		}
748		else
749		{
750			throw new \Exception('Method ' . $name . ' not found in FOFDatabase');
751		}
752	}
753
754	public function __get($name)
755	{
756		if (isset($this->dbo->$name) || property_exists($this->dbo, $name))
757		{
758			return $this->dbo->$name;
759		}
760		else
761		{
762			$this->dbo->$name = null;
763			user_error('Database driver does not support property ' . $name);
764		}
765	}
766
767	public function __set($name, $value)
768	{
769		if (isset($this->dbo->name) || property_exists($this->dbo, $name))
770		{
771			$this->dbo->$name = $value;
772		}
773		else
774		{
775			$this->dbo->$name = null;
776			user_error('Database driver not support property ' . $name);
777		}
778	}
779}
780