1<?php
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP
6 *
7 * This content is released under the MIT License (MIT)
8 *
9 * Copyright (c) 2014 - 2018, British Columbia Institute of Technology
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * THE SOFTWARE.
28 *
29 * @package	CodeIgniter
30 * @author	EllisLab Dev Team
31 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
32 * @copyright	Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
33 * @license	http://opensource.org/licenses/MIT	MIT License
34 * @link	https://codeigniter.com
35 * @since	Version 1.0.0
36 * @filesource
37 */
38defined('BASEPATH') OR exit('No direct script access allowed');
39
40/**
41 * CodeIgniter Driver Library Class
42 *
43 * This class enables you to create "Driver" libraries that add runtime ability
44 * to extend the capabilities of a class via additional driver objects
45 *
46 * @package		CodeIgniter
47 * @subpackage	Libraries
48 * @category	Libraries
49 * @author		EllisLab Dev Team
50 * @link
51 */
52class CI_Driver_Library {
53
54	/**
55	 * Array of drivers that are available to use with the driver class
56	 *
57	 * @var array
58	 */
59	protected $valid_drivers = array();
60
61	/**
62	 * Name of the current class - usually the driver class
63	 *
64	 * @var string
65	 */
66	protected $lib_name;
67
68	/**
69	 * Get magic method
70	 *
71	 * The first time a child is used it won't exist, so we instantiate it
72	 * subsequents calls will go straight to the proper child.
73	 *
74	 * @param	string	Child class name
75	 * @return	object	Child class
76	 */
77	public function __get($child)
78	{
79		// Try to load the driver
80		return $this->load_driver($child);
81	}
82
83	/**
84	 * Load driver
85	 *
86	 * Separate load_driver call to support explicit driver load by library or user
87	 *
88	 * @param	string	Driver name (w/o parent prefix)
89	 * @return	object	Child class
90	 */
91	public function load_driver($child)
92	{
93		// Get CodeIgniter instance and subclass prefix
94		$prefix = config_item('subclass_prefix');
95
96		if ( ! isset($this->lib_name))
97		{
98			// Get library name without any prefix
99			$this->lib_name = str_replace(array('CI_', $prefix), '', get_class($this));
100		}
101
102		// The child will be prefixed with the parent lib
103		$child_name = $this->lib_name.'_'.$child;
104
105		// See if requested child is a valid driver
106		if ( ! in_array($child, $this->valid_drivers))
107		{
108			// The requested driver isn't valid!
109			$msg = 'Invalid driver requested: '.$child_name;
110			log_message('error', $msg);
111			show_error($msg);
112		}
113
114		// Get package paths and filename case variations to search
115		$CI = get_instance();
116		$paths = $CI->load->get_package_paths(TRUE);
117
118		// Is there an extension?
119		$class_name = $prefix.$child_name;
120		$found = class_exists($class_name, FALSE);
121		if ( ! $found)
122		{
123			// Check for subclass file
124			foreach ($paths as $path)
125			{
126				// Does the file exist?
127				$file = $path.'libraries/'.$this->lib_name.'/drivers/'.$prefix.$child_name.'.php';
128				if (file_exists($file))
129				{
130					// Yes - require base class from BASEPATH
131					$basepath = BASEPATH.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php';
132					if ( ! file_exists($basepath))
133					{
134						$msg = 'Unable to load the requested class: CI_'.$child_name;
135						log_message('error', $msg);
136						show_error($msg);
137					}
138
139					// Include both sources and mark found
140					include_once($basepath);
141					include_once($file);
142					$found = TRUE;
143					break;
144				}
145			}
146		}
147
148		// Do we need to search for the class?
149		if ( ! $found)
150		{
151			// Use standard class name
152			$class_name = 'CI_'.$child_name;
153			if ( ! class_exists($class_name, FALSE))
154			{
155				// Check package paths
156				foreach ($paths as $path)
157				{
158					// Does the file exist?
159					$file = $path.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php';
160					if (file_exists($file))
161					{
162						// Include source
163						include_once($file);
164						break;
165					}
166				}
167			}
168		}
169
170		// Did we finally find the class?
171		if ( ! class_exists($class_name, FALSE))
172		{
173			if (class_exists($child_name, FALSE))
174			{
175				$class_name = $child_name;
176			}
177			else
178			{
179				$msg = 'Unable to load the requested driver: '.$class_name;
180				log_message('error', $msg);
181				show_error($msg);
182			}
183		}
184
185		// Instantiate, decorate and add child
186		$obj = new $class_name();
187		$obj->decorate($this);
188		$this->$child = $obj;
189		return $this->$child;
190	}
191
192}
193
194// --------------------------------------------------------------------------
195
196/**
197 * CodeIgniter Driver Class
198 *
199 * This class enables you to create drivers for a Library based on the Driver Library.
200 * It handles the drivers' access to the parent library
201 *
202 * @package		CodeIgniter
203 * @subpackage	Libraries
204 * @category	Libraries
205 * @author		EllisLab Dev Team
206 * @link
207 */
208class CI_Driver {
209
210	/**
211	 * Instance of the parent class
212	 *
213	 * @var object
214	 */
215	protected $_parent;
216
217	/**
218	 * List of methods in the parent class
219	 *
220	 * @var array
221	 */
222	protected $_methods = array();
223
224	/**
225	 * List of properties in the parent class
226	 *
227	 * @var array
228	 */
229	protected $_properties = array();
230
231	/**
232	 * Array of methods and properties for the parent class(es)
233	 *
234	 * @static
235	 * @var	array
236	 */
237	protected static $_reflections = array();
238
239	/**
240	 * Decorate
241	 *
242	 * Decorates the child with the parent driver lib's methods and properties
243	 *
244	 * @param	object
245	 * @return	void
246	 */
247	public function decorate($parent)
248	{
249		$this->_parent = $parent;
250
251		// Lock down attributes to what is defined in the class
252		// and speed up references in magic methods
253
254		$class_name = get_class($parent);
255
256		if ( ! isset(self::$_reflections[$class_name]))
257		{
258			$r = new ReflectionObject($parent);
259
260			foreach ($r->getMethods() as $method)
261			{
262				if ($method->isPublic())
263				{
264					$this->_methods[] = $method->getName();
265				}
266			}
267
268			foreach ($r->getProperties() as $prop)
269			{
270				if ($prop->isPublic())
271				{
272					$this->_properties[] = $prop->getName();
273				}
274			}
275
276			self::$_reflections[$class_name] = array($this->_methods, $this->_properties);
277		}
278		else
279		{
280			list($this->_methods, $this->_properties) = self::$_reflections[$class_name];
281		}
282	}
283
284	// --------------------------------------------------------------------
285
286	/**
287	 * __call magic method
288	 *
289	 * Handles access to the parent driver library's methods
290	 *
291	 * @param	string
292	 * @param	array
293	 * @return	mixed
294	 */
295	public function __call($method, $args = array())
296	{
297		if (in_array($method, $this->_methods))
298		{
299			return call_user_func_array(array($this->_parent, $method), $args);
300		}
301
302		throw new BadMethodCallException('No such method: '.$method.'()');
303	}
304
305	// --------------------------------------------------------------------
306
307	/**
308	 * __get magic method
309	 *
310	 * Handles reading of the parent driver library's properties
311	 *
312	 * @param	string
313	 * @return	mixed
314	 */
315	public function __get($var)
316	{
317		if (in_array($var, $this->_properties))
318		{
319			return $this->_parent->$var;
320		}
321	}
322
323	// --------------------------------------------------------------------
324
325	/**
326	 * __set magic method
327	 *
328	 * Handles writing to the parent driver library's properties
329	 *
330	 * @param	string
331	 * @param	array
332	 * @return	mixed
333	 */
334	public function __set($var, $val)
335	{
336		if (in_array($var, $this->_properties))
337		{
338			$this->_parent->$var = $val;
339		}
340	}
341
342}
343