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 - 2019, 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 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
33 * @license	https://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 * Loader Class
42 *
43 * Loads framework components.
44 *
45 * @package		CodeIgniter
46 * @subpackage	Libraries
47 * @category	Loader
48 * @author		EllisLab Dev Team
49 * @link		https://codeigniter.com/user_guide/libraries/loader.html
50 */
51class CI_Loader {
52
53	// All these are set automatically. Don't mess with them.
54	/**
55	 * Nesting level of the output buffering mechanism
56	 *
57	 * @var	int
58	 */
59	protected $_ci_ob_level;
60
61	/**
62	 * List of paths to load views from
63	 *
64	 * @var	array
65	 */
66	protected $_ci_view_paths =	array(VIEWPATH	=> TRUE);
67
68	/**
69	 * List of paths to load libraries from
70	 *
71	 * @var	array
72	 */
73	protected $_ci_library_paths =	array(APPPATH, BASEPATH);
74
75	/**
76	 * List of paths to load models from
77	 *
78	 * @var	array
79	 */
80	protected $_ci_model_paths =	array(APPPATH);
81
82	/**
83	 * List of paths to load helpers from
84	 *
85	 * @var	array
86	 */
87	protected $_ci_helper_paths =	array(APPPATH, BASEPATH);
88
89	/**
90	 * List of cached variables
91	 *
92	 * @var	array
93	 */
94	protected $_ci_cached_vars =	array();
95
96	/**
97	 * List of loaded classes
98	 *
99	 * @var	array
100	 */
101	protected $_ci_classes =	array();
102
103	/**
104	 * List of loaded models
105	 *
106	 * @var	array
107	 */
108	protected $_ci_models =	array();
109
110	/**
111	 * List of loaded helpers
112	 *
113	 * @var	array
114	 */
115	protected $_ci_helpers =	array();
116
117	/**
118	 * List of class name mappings
119	 *
120	 * @var	array
121	 */
122	protected $_ci_varmap =	array(
123		'unit_test' => 'unit',
124		'user_agent' => 'agent'
125	);
126
127	// --------------------------------------------------------------------
128
129	/**
130	 * Class constructor
131	 *
132	 * Sets component load paths, gets the initial output buffering level.
133	 *
134	 * @return	void
135	 */
136	public function __construct()
137	{
138		$this->_ci_ob_level = ob_get_level();
139		$this->_ci_classes =& is_loaded();
140
141		log_message('info', 'Loader Class Initialized');
142	}
143
144	// --------------------------------------------------------------------
145
146	/**
147	 * Initializer
148	 *
149	 * @todo	Figure out a way to move this to the constructor
150	 *		without breaking *package_path*() methods.
151	 * @uses	CI_Loader::_ci_autoloader()
152	 * @used-by	CI_Controller::__construct()
153	 * @return	void
154	 */
155	public function initialize()
156	{
157		$this->_ci_autoloader();
158	}
159
160	// --------------------------------------------------------------------
161
162	/**
163	 * Is Loaded
164	 *
165	 * A utility method to test if a class is in the self::$_ci_classes array.
166	 *
167	 * @used-by	Mainly used by Form Helper function _get_validation_object().
168	 *
169	 * @param 	string		$class	Class name to check for
170	 * @return 	string|bool	Class object name if loaded or FALSE
171	 */
172	public function is_loaded($class)
173	{
174		return array_search(ucfirst($class), $this->_ci_classes, TRUE);
175	}
176
177	// --------------------------------------------------------------------
178
179	/**
180	 * Library Loader
181	 *
182	 * Loads and instantiates libraries.
183	 * Designed to be called from application controllers.
184	 *
185	 * @param	mixed	$library	Library name
186	 * @param	array	$params		Optional parameters to pass to the library class constructor
187	 * @param	string	$object_name	An optional object name to assign to
188	 * @return	object
189	 */
190	public function library($library, $params = NULL, $object_name = NULL)
191	{
192		if (empty($library))
193		{
194			return $this;
195		}
196		elseif (is_array($library))
197		{
198			foreach ($library as $key => $value)
199			{
200				if (is_int($key))
201				{
202					$this->library($value, $params);
203				}
204				else
205				{
206					$this->library($key, $params, $value);
207				}
208			}
209
210			return $this;
211		}
212
213		if ($params !== NULL && ! is_array($params))
214		{
215			$params = NULL;
216		}
217
218		$this->_ci_load_library($library, $params, $object_name);
219		return $this;
220	}
221
222	// --------------------------------------------------------------------
223
224	/**
225	 * Model Loader
226	 *
227	 * Loads and instantiates models.
228	 *
229	 * @param	mixed	$model		Model name
230	 * @param	string	$name		An optional object name to assign to
231	 * @param	bool	$db_conn	An optional database connection configuration to initialize
232	 * @return	object
233	 */
234	public function model($model, $name = '', $db_conn = FALSE)
235	{
236		if (empty($model))
237		{
238			return $this;
239		}
240		elseif (is_array($model))
241		{
242			foreach ($model as $key => $value)
243			{
244				is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
245			}
246
247			return $this;
248		}
249
250		$path = '';
251
252		// Is the model in a sub-folder? If so, parse out the filename and path.
253		if (($last_slash = strrpos($model, '/')) !== FALSE)
254		{
255			// The path is in front of the last slash
256			$path = substr($model, 0, ++$last_slash);
257
258			// And the model name behind it
259			$model = substr($model, $last_slash);
260		}
261
262		if (empty($name))
263		{
264			$name = $model;
265		}
266
267		if (in_array($name, $this->_ci_models, TRUE))
268		{
269			return $this;
270		}
271
272		$CI =& get_instance();
273		if (isset($CI->$name))
274		{
275			throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name);
276		}
277
278		if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
279		{
280			if ($db_conn === TRUE)
281			{
282				$db_conn = '';
283			}
284
285			$this->database($db_conn, FALSE, TRUE);
286		}
287
288		// Note: All of the code under this condition used to be just:
289		//
290		//       load_class('Model', 'core');
291		//
292		//       However, load_class() instantiates classes
293		//       to cache them for later use and that prevents
294		//       MY_Model from being an abstract class and is
295		//       sub-optimal otherwise anyway.
296		if ( ! class_exists('CI_Model', FALSE))
297		{
298			$app_path = APPPATH.'core'.DIRECTORY_SEPARATOR;
299			if (file_exists($app_path.'Model.php'))
300			{
301				require_once($app_path.'Model.php');
302				if ( ! class_exists('CI_Model', FALSE))
303				{
304					throw new RuntimeException($app_path."Model.php exists, but doesn't declare class CI_Model");
305				}
306
307				log_message('info', 'CI_Model class loaded');
308			}
309			elseif ( ! class_exists('CI_Model', FALSE))
310			{
311				require_once(BASEPATH.'core'.DIRECTORY_SEPARATOR.'Model.php');
312			}
313
314			$class = config_item('subclass_prefix').'Model';
315			if (file_exists($app_path.$class.'.php'))
316			{
317				require_once($app_path.$class.'.php');
318				if ( ! class_exists($class, FALSE))
319				{
320					throw new RuntimeException($app_path.$class.".php exists, but doesn't declare class ".$class);
321				}
322
323				log_message('info', config_item('subclass_prefix').'Model class loaded');
324			}
325		}
326
327		$model = ucfirst($model);
328		if ( ! class_exists($model, FALSE))
329		{
330			foreach ($this->_ci_model_paths as $mod_path)
331			{
332				if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
333				{
334					continue;
335				}
336
337				require_once($mod_path.'models/'.$path.$model.'.php');
338				if ( ! class_exists($model, FALSE))
339				{
340					throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model);
341				}
342
343				break;
344			}
345
346			if ( ! class_exists($model, FALSE))
347			{
348				throw new RuntimeException('Unable to locate the model you have specified: '.$model);
349			}
350		}
351		elseif ( ! is_subclass_of($model, 'CI_Model'))
352		{
353			throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model");
354		}
355
356		$this->_ci_models[] = $name;
357		$model = new $model();
358		$CI->$name = $model;
359		log_message('info', 'Model "'.get_class($model).'" initialized');
360		return $this;
361	}
362
363	// --------------------------------------------------------------------
364
365	/**
366	 * Database Loader
367	 *
368	 * @param	mixed	$params		Database configuration options
369	 * @param	bool	$return 	Whether to return the database object
370	 * @param	bool	$query_builder	Whether to enable Query Builder
371	 *					(overrides the configuration setting)
372	 *
373	 * @return	object|bool	Database object if $return is set to TRUE,
374	 *					FALSE on failure, CI_Loader instance in any other case
375	 */
376	public function database($params = '', $return = FALSE, $query_builder = NULL)
377	{
378		// Grab the super object
379		$CI =& get_instance();
380
381		// Do we even need to load the database class?
382		if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id))
383		{
384			return FALSE;
385		}
386
387		require_once(BASEPATH.'database/DB.php');
388
389		if ($return === TRUE)
390		{
391			return DB($params, $query_builder);
392		}
393
394		// Initialize the db variable. Needed to prevent
395		// reference errors with some configurations
396		$CI->db = '';
397
398		// Load the DB class
399		$CI->db =& DB($params, $query_builder);
400		return $this;
401	}
402
403	// --------------------------------------------------------------------
404
405	/**
406	 * Load the Database Utilities Class
407	 *
408	 * @param	object	$db	Database object
409	 * @param	bool	$return	Whether to return the DB Utilities class object or not
410	 * @return	object
411	 */
412	public function dbutil($db = NULL, $return = FALSE)
413	{
414		$CI =& get_instance();
415
416		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
417		{
418			class_exists('CI_DB', FALSE) OR $this->database();
419			$db =& $CI->db;
420		}
421
422		require_once(BASEPATH.'database/DB_utility.php');
423		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');
424		$class = 'CI_DB_'.$db->dbdriver.'_utility';
425
426		if ($return === TRUE)
427		{
428			return new $class($db);
429		}
430
431		$CI->dbutil = new $class($db);
432		return $this;
433	}
434
435	// --------------------------------------------------------------------
436
437	/**
438	 * Load the Database Forge Class
439	 *
440	 * @param	object	$db	Database object
441	 * @param	bool	$return	Whether to return the DB Forge class object or not
442	 * @return	object
443	 */
444	public function dbforge($db = NULL, $return = FALSE)
445	{
446		$CI =& get_instance();
447		if ( ! is_object($db) OR ! ($db instanceof CI_DB))
448		{
449			class_exists('CI_DB', FALSE) OR $this->database();
450			$db =& $CI->db;
451		}
452
453		require_once(BASEPATH.'database/DB_forge.php');
454		require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php');
455
456		if ( ! empty($db->subdriver))
457		{
458			$driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php';
459			if (file_exists($driver_path))
460			{
461				require_once($driver_path);
462				$class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge';
463			}
464		}
465		else
466		{
467			$class = 'CI_DB_'.$db->dbdriver.'_forge';
468		}
469
470		if ($return === TRUE)
471		{
472			return new $class($db);
473		}
474
475		$CI->dbforge = new $class($db);
476		return $this;
477	}
478
479	// --------------------------------------------------------------------
480
481	/**
482	 * View Loader
483	 *
484	 * Loads "view" files.
485	 *
486	 * @param	string	$view	View name
487	 * @param	array	$vars	An associative array of data
488	 *				to be extracted for use in the view
489	 * @param	bool	$return	Whether to return the view output
490	 *				or leave it to the Output class
491	 * @return	object|string
492	 */
493	public function view($view, $vars = array(), $return = FALSE)
494	{
495		return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_prepare_view_vars($vars), '_ci_return' => $return));
496	}
497
498	// --------------------------------------------------------------------
499
500	/**
501	 * Generic File Loader
502	 *
503	 * @param	string	$path	File path
504	 * @param	bool	$return	Whether to return the file output
505	 * @return	object|string
506	 */
507	public function file($path, $return = FALSE)
508	{
509		return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
510	}
511
512	// --------------------------------------------------------------------
513
514	/**
515	 * Set Variables
516	 *
517	 * Once variables are set they become available within
518	 * the controller class and its "view" files.
519	 *
520	 * @param	array|object|string	$vars
521	 *					An associative array or object containing values
522	 *					to be set, or a value's name if string
523	 * @param 	string	$val	Value to set, only used if $vars is a string
524	 * @return	object
525	 */
526	public function vars($vars, $val = '')
527	{
528		$vars = is_string($vars)
529			? array($vars => $val)
530			: $this->_ci_prepare_view_vars($vars);
531
532		foreach ($vars as $key => $val)
533		{
534			$this->_ci_cached_vars[$key] = $val;
535		}
536
537		return $this;
538	}
539
540	// --------------------------------------------------------------------
541
542	/**
543	 * Clear Cached Variables
544	 *
545	 * Clears the cached variables.
546	 *
547	 * @return	CI_Loader
548	 */
549	public function clear_vars()
550	{
551		$this->_ci_cached_vars = array();
552		return $this;
553	}
554
555	// --------------------------------------------------------------------
556
557	/**
558	 * Get Variable
559	 *
560	 * Check if a variable is set and retrieve it.
561	 *
562	 * @param	string	$key	Variable name
563	 * @return	mixed	The variable or NULL if not found
564	 */
565	public function get_var($key)
566	{
567		return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
568	}
569
570	// --------------------------------------------------------------------
571
572	/**
573	 * Get Variables
574	 *
575	 * Retrieves all loaded variables.
576	 *
577	 * @return	array
578	 */
579	public function get_vars()
580	{
581		return $this->_ci_cached_vars;
582	}
583
584	// --------------------------------------------------------------------
585
586	/**
587	 * Helper Loader
588	 *
589	 * @param	string|string[]	$helpers	Helper name(s)
590	 * @return	object
591	 */
592	public function helper($helpers = array())
593	{
594		is_array($helpers) OR $helpers = array($helpers);
595		foreach ($helpers as &$helper)
596		{
597			$filename = basename($helper);
598			$filepath = ($filename === $helper) ? '' : substr($helper, 0, strlen($helper) - strlen($filename));
599			$filename = strtolower(preg_replace('#(_helper)?(\.php)?$#i', '', $filename)).'_helper';
600			$helper   = $filepath.$filename;
601
602			if (isset($this->_ci_helpers[$helper]))
603			{
604				continue;
605			}
606
607			// Is this a helper extension request?
608			$ext_helper = config_item('subclass_prefix').$filename;
609			$ext_loaded = FALSE;
610			foreach ($this->_ci_helper_paths as $path)
611			{
612				if (file_exists($path.'helpers/'.$ext_helper.'.php'))
613				{
614					include_once($path.'helpers/'.$ext_helper.'.php');
615					$ext_loaded = TRUE;
616				}
617			}
618
619			// If we have loaded extensions - check if the base one is here
620			if ($ext_loaded === TRUE)
621			{
622				$base_helper = BASEPATH.'helpers/'.$helper.'.php';
623				if ( ! file_exists($base_helper))
624				{
625					show_error('Unable to load the requested file: helpers/'.$helper.'.php');
626				}
627
628				include_once($base_helper);
629				$this->_ci_helpers[$helper] = TRUE;
630				log_message('info', 'Helper loaded: '.$helper);
631				continue;
632			}
633
634			// No extensions found ... try loading regular helpers and/or overrides
635			foreach ($this->_ci_helper_paths as $path)
636			{
637				if (file_exists($path.'helpers/'.$helper.'.php'))
638				{
639					include_once($path.'helpers/'.$helper.'.php');
640
641					$this->_ci_helpers[$helper] = TRUE;
642					log_message('info', 'Helper loaded: '.$helper);
643					break;
644				}
645			}
646
647			// unable to load the helper
648			if ( ! isset($this->_ci_helpers[$helper]))
649			{
650				show_error('Unable to load the requested file: helpers/'.$helper.'.php');
651			}
652		}
653
654		return $this;
655	}
656
657	// --------------------------------------------------------------------
658
659	/**
660	 * Load Helpers
661	 *
662	 * An alias for the helper() method in case the developer has
663	 * written the plural form of it.
664	 *
665	 * @uses	CI_Loader::helper()
666	 * @param	string|string[]	$helpers	Helper name(s)
667	 * @return	object
668	 */
669	public function helpers($helpers = array())
670	{
671		return $this->helper($helpers);
672	}
673
674	// --------------------------------------------------------------------
675
676	/**
677	 * Language Loader
678	 *
679	 * Loads language files.
680	 *
681	 * @param	string|string[]	$files	List of language file names to load
682	 * @param	string		Language name
683	 * @return	object
684	 */
685	public function language($files, $lang = '')
686	{
687		get_instance()->lang->load($files, $lang);
688		return $this;
689	}
690
691	// --------------------------------------------------------------------
692
693	/**
694	 * Config Loader
695	 *
696	 * Loads a config file (an alias for CI_Config::load()).
697	 *
698	 * @uses	CI_Config::load()
699	 * @param	string	$file			Configuration file name
700	 * @param	bool	$use_sections		Whether configuration values should be loaded into their own section
701	 * @param	bool	$fail_gracefully	Whether to just return FALSE or display an error message
702	 * @return	bool	TRUE if the file was loaded correctly or FALSE on failure
703	 */
704	public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
705	{
706		return get_instance()->config->load($file, $use_sections, $fail_gracefully);
707	}
708
709	// --------------------------------------------------------------------
710
711	/**
712	 * Driver Loader
713	 *
714	 * Loads a driver library.
715	 *
716	 * @param	string|string[]	$library	Driver name(s)
717	 * @param	array		$params		Optional parameters to pass to the driver
718	 * @param	string		$object_name	An optional object name to assign to
719	 *
720	 * @return	object|bool	Object or FALSE on failure if $library is a string
721	 *				and $object_name is set. CI_Loader instance otherwise.
722	 */
723	public function driver($library, $params = NULL, $object_name = NULL)
724	{
725		if (is_array($library))
726		{
727			foreach ($library as $key => $value)
728			{
729				if (is_int($key))
730				{
731					$this->driver($value, $params);
732				}
733				else
734				{
735					$this->driver($key, $params, $value);
736				}
737			}
738
739			return $this;
740		}
741		elseif (empty($library))
742		{
743			return FALSE;
744		}
745
746		if ( ! class_exists('CI_Driver_Library', FALSE))
747		{
748			// We aren't instantiating an object here, just making the base class available
749			require BASEPATH.'libraries/Driver.php';
750		}
751
752		// We can save the loader some time since Drivers will *always* be in a subfolder,
753		// and typically identically named to the library
754		if ( ! strpos($library, '/'))
755		{
756			$library = ucfirst($library).'/'.$library;
757		}
758
759		return $this->library($library, $params, $object_name);
760	}
761
762	// --------------------------------------------------------------------
763
764	/**
765	 * Add Package Path
766	 *
767	 * Prepends a parent path to the library, model, helper and config
768	 * path arrays.
769	 *
770	 * @see	CI_Loader::$_ci_library_paths
771	 * @see	CI_Loader::$_ci_model_paths
772	 * @see CI_Loader::$_ci_helper_paths
773	 * @see CI_Config::$_config_paths
774	 *
775	 * @param	string	$path		Path to add
776	 * @param 	bool	$view_cascade	(default: TRUE)
777	 * @return	object
778	 */
779	public function add_package_path($path, $view_cascade = TRUE)
780	{
781		$path = rtrim($path, '/').'/';
782
783		array_unshift($this->_ci_library_paths, $path);
784		array_unshift($this->_ci_model_paths, $path);
785		array_unshift($this->_ci_helper_paths, $path);
786
787		$this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;
788
789		// Add config file path
790		$config =& $this->_ci_get_component('config');
791		$config->_config_paths[] = $path;
792
793		return $this;
794	}
795
796	// --------------------------------------------------------------------
797
798	/**
799	 * Get Package Paths
800	 *
801	 * Return a list of all package paths.
802	 *
803	 * @param	bool	$include_base	Whether to include BASEPATH (default: FALSE)
804	 * @return	array
805	 */
806	public function get_package_paths($include_base = FALSE)
807	{
808		return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths;
809	}
810
811	// --------------------------------------------------------------------
812
813	/**
814	 * Remove Package Path
815	 *
816	 * Remove a path from the library, model, helper and/or config
817	 * path arrays if it exists. If no path is provided, the most recently
818	 * added path will be removed removed.
819	 *
820	 * @param	string	$path	Path to remove
821	 * @return	object
822	 */
823	public function remove_package_path($path = '')
824	{
825		$config =& $this->_ci_get_component('config');
826
827		if ($path === '')
828		{
829			array_shift($this->_ci_library_paths);
830			array_shift($this->_ci_model_paths);
831			array_shift($this->_ci_helper_paths);
832			array_shift($this->_ci_view_paths);
833			array_pop($config->_config_paths);
834		}
835		else
836		{
837			$path = rtrim($path, '/').'/';
838			foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)
839			{
840				if (($key = array_search($path, $this->{$var})) !== FALSE)
841				{
842					unset($this->{$var}[$key]);
843				}
844			}
845
846			if (isset($this->_ci_view_paths[$path.'views/']))
847			{
848				unset($this->_ci_view_paths[$path.'views/']);
849			}
850
851			if (($key = array_search($path, $config->_config_paths)) !== FALSE)
852			{
853				unset($config->_config_paths[$key]);
854			}
855		}
856
857		// make sure the application default paths are still in the array
858		$this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
859		$this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
860		$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
861		$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
862		$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
863
864		return $this;
865	}
866
867	// --------------------------------------------------------------------
868
869	/**
870	 * Internal CI Data Loader
871	 *
872	 * Used to load views and files.
873	 *
874	 * Variables are prefixed with _ci_ to avoid symbol collision with
875	 * variables made available to view files.
876	 *
877	 * @used-by	CI_Loader::view()
878	 * @used-by	CI_Loader::file()
879	 * @param	array	$_ci_data	Data to load
880	 * @return	object
881	 */
882	protected function _ci_load($_ci_data)
883	{
884		// Set the default data variables
885		foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
886		{
887			$$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE;
888		}
889
890		$file_exists = FALSE;
891
892		// Set the path to the requested file
893		if (is_string($_ci_path) && $_ci_path !== '')
894		{
895			$_ci_x = explode('/', $_ci_path);
896			$_ci_file = end($_ci_x);
897		}
898		else
899		{
900			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
901			$_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
902
903			foreach ($this->_ci_view_paths as $_ci_view_file => $cascade)
904			{
905				if (file_exists($_ci_view_file.$_ci_file))
906				{
907					$_ci_path = $_ci_view_file.$_ci_file;
908					$file_exists = TRUE;
909					break;
910				}
911
912				if ( ! $cascade)
913				{
914					break;
915				}
916			}
917		}
918
919		if ( ! $file_exists && ! file_exists($_ci_path))
920		{
921			show_error('Unable to load the requested file: '.$_ci_file);
922		}
923
924		// This allows anything loaded using $this->load (views, files, etc.)
925		// to become accessible from within the Controller and Model functions.
926		$_ci_CI =& get_instance();
927		foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
928		{
929			if ( ! isset($this->$_ci_key))
930			{
931				$this->$_ci_key =& $_ci_CI->$_ci_key;
932			}
933		}
934
935		/*
936		 * Extract and cache variables
937		 *
938		 * You can either set variables using the dedicated $this->load->vars()
939		 * function or via the second parameter of this function. We'll merge
940		 * the two types and cache them so that views that are embedded within
941		 * other views can have access to these variables.
942		 */
943		empty($_ci_vars) OR $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
944		extract($this->_ci_cached_vars);
945
946		/*
947		 * Buffer the output
948		 *
949		 * We buffer the output for two reasons:
950		 * 1. Speed. You get a significant speed boost.
951		 * 2. So that the final rendered template can be post-processed by
952		 *	the output class. Why do we need post processing? For one thing,
953		 *	in order to show the elapsed page load time. Unless we can
954		 *	intercept the content right before it's sent to the browser and
955		 *	then stop the timer it won't be accurate.
956		 */
957		ob_start();
958
959		// If the PHP installation does not support short tags we'll
960		// do a little string replacement, changing the short tags
961		// to standard PHP echo statements.
962		if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE)
963		{
964			echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
965		}
966		else
967		{
968			include($_ci_path); // include() vs include_once() allows for multiple views with the same name
969		}
970
971		log_message('info', 'File loaded: '.$_ci_path);
972
973		// Return the file data if requested
974		if ($_ci_return === TRUE)
975		{
976			$buffer = ob_get_contents();
977			@ob_end_clean();
978			return $buffer;
979		}
980
981		/*
982		 * Flush the buffer... or buff the flusher?
983		 *
984		 * In order to permit views to be nested within
985		 * other views, we need to flush the content back out whenever
986		 * we are beyond the first level of output buffering so that
987		 * it can be seen and included properly by the first included
988		 * template and any subsequent ones. Oy!
989		 */
990		if (ob_get_level() > $this->_ci_ob_level + 1)
991		{
992			ob_end_flush();
993		}
994		else
995		{
996			$_ci_CI->output->append_output(ob_get_contents());
997			@ob_end_clean();
998		}
999
1000		return $this;
1001	}
1002
1003	// --------------------------------------------------------------------
1004
1005	/**
1006	 * Internal CI Library Loader
1007	 *
1008	 * @used-by	CI_Loader::library()
1009	 * @uses	CI_Loader::_ci_init_library()
1010	 *
1011	 * @param	string	$class		Class name to load
1012	 * @param	mixed	$params		Optional parameters to pass to the class constructor
1013	 * @param	string	$object_name	Optional object name to assign to
1014	 * @return	void
1015	 */
1016	protected function _ci_load_library($class, $params = NULL, $object_name = NULL)
1017	{
1018		// Get the class name, and while we're at it trim any slashes.
1019		// The directory path can be included as part of the class name,
1020		// but we don't want a leading slash
1021		$class = str_replace('.php', '', trim($class, '/'));
1022
1023		// Was the path included with the class name?
1024		// We look for a slash to determine this
1025		if (($last_slash = strrpos($class, '/')) !== FALSE)
1026		{
1027			// Extract the path
1028			$subdir = substr($class, 0, ++$last_slash);
1029
1030			// Get the filename from the path
1031			$class = substr($class, $last_slash);
1032		}
1033		else
1034		{
1035			$subdir = '';
1036		}
1037
1038		$class = ucfirst($class);
1039
1040		// Is this a stock library? There are a few special conditions if so ...
1041		if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php'))
1042		{
1043			return $this->_ci_load_stock_library($class, $subdir, $params, $object_name);
1044		}
1045
1046		// Safety: Was the class already loaded by a previous call?
1047		if (class_exists($class, FALSE))
1048		{
1049			$property = $object_name;
1050			if (empty($property))
1051			{
1052				$property = strtolower($class);
1053				isset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];
1054			}
1055
1056			$CI =& get_instance();
1057			if (isset($CI->$property))
1058			{
1059				log_message('debug', $class.' class already loaded. Second attempt ignored.');
1060				return;
1061			}
1062
1063			return $this->_ci_init_library($class, '', $params, $object_name);
1064		}
1065
1066		// Let's search for the requested library file and load it.
1067		foreach ($this->_ci_library_paths as $path)
1068		{
1069			// BASEPATH has already been checked for
1070			if ($path === BASEPATH)
1071			{
1072				continue;
1073			}
1074
1075			$filepath = $path.'libraries/'.$subdir.$class.'.php';
1076			// Does the file exist? No? Bummer...
1077			if ( ! file_exists($filepath))
1078			{
1079				continue;
1080			}
1081
1082			include_once($filepath);
1083			return $this->_ci_init_library($class, '', $params, $object_name);
1084		}
1085
1086		// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
1087		if ($subdir === '')
1088		{
1089			return $this->_ci_load_library($class.'/'.$class, $params, $object_name);
1090		}
1091
1092		// If we got this far we were unable to find the requested class.
1093		log_message('error', 'Unable to load the requested class: '.$class);
1094		show_error('Unable to load the requested class: '.$class);
1095	}
1096
1097	// --------------------------------------------------------------------
1098
1099	/**
1100	 * Internal CI Stock Library Loader
1101	 *
1102	 * @used-by	CI_Loader::_ci_load_library()
1103	 * @uses	CI_Loader::_ci_init_library()
1104	 *
1105	 * @param	string	$library_name	Library name to load
1106	 * @param	string	$file_path	Path to the library filename, relative to libraries/
1107	 * @param	mixed	$params		Optional parameters to pass to the class constructor
1108	 * @param	string	$object_name	Optional object name to assign to
1109	 * @return	void
1110	 */
1111	protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name)
1112	{
1113		$prefix = 'CI_';
1114
1115		if (class_exists($prefix.$library_name, FALSE))
1116		{
1117			if (class_exists(config_item('subclass_prefix').$library_name, FALSE))
1118			{
1119				$prefix = config_item('subclass_prefix');
1120			}
1121
1122			$property = $object_name;
1123			if (empty($property))
1124			{
1125				$property = strtolower($library_name);
1126				isset($this->_ci_varmap[$property]) && $property = $this->_ci_varmap[$property];
1127			}
1128
1129			$CI =& get_instance();
1130			if ( ! isset($CI->$property))
1131			{
1132				return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
1133			}
1134
1135			log_message('debug', $library_name.' class already loaded. Second attempt ignored.');
1136			return;
1137		}
1138
1139		$paths = $this->_ci_library_paths;
1140		array_pop($paths); // BASEPATH
1141		array_pop($paths); // APPPATH (needs to be the first path checked)
1142		array_unshift($paths, APPPATH);
1143
1144		foreach ($paths as $path)
1145		{
1146			if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php'))
1147			{
1148				// Override
1149				include_once($path);
1150				if (class_exists($prefix.$library_name, FALSE))
1151				{
1152					return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
1153				}
1154
1155				log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name);
1156			}
1157		}
1158
1159		include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php');
1160
1161		// Check for extensions
1162		$subclass = config_item('subclass_prefix').$library_name;
1163		foreach ($paths as $path)
1164		{
1165			if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php'))
1166			{
1167				include_once($path);
1168				if (class_exists($subclass, FALSE))
1169				{
1170					$prefix = config_item('subclass_prefix');
1171					break;
1172				}
1173
1174				log_message('debug', $path.' exists, but does not declare '.$subclass);
1175			}
1176		}
1177
1178		return $this->_ci_init_library($library_name, $prefix, $params, $object_name);
1179	}
1180
1181	// --------------------------------------------------------------------
1182
1183	/**
1184	 * Internal CI Library Instantiator
1185	 *
1186	 * @used-by	CI_Loader::_ci_load_stock_library()
1187	 * @used-by	CI_Loader::_ci_load_library()
1188	 *
1189	 * @param	string		$class		Class name
1190	 * @param	string		$prefix		Class name prefix
1191	 * @param	array|null|bool	$config		Optional configuration to pass to the class constructor:
1192	 *						FALSE to skip;
1193	 *						NULL to search in config paths;
1194	 *						array containing configuration data
1195	 * @param	string		$object_name	Optional object name to assign to
1196	 * @return	void
1197	 */
1198	protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL)
1199	{
1200		// Is there an associated config file for this class? Note: these should always be lowercase
1201		if ($config === NULL)
1202		{
1203			// Fetch the config paths containing any package paths
1204			$config_component = $this->_ci_get_component('config');
1205
1206			if (is_array($config_component->_config_paths))
1207			{
1208				$found = FALSE;
1209				foreach ($config_component->_config_paths as $path)
1210				{
1211					// We test for both uppercase and lowercase, for servers that
1212					// are case-sensitive with regard to file names. Load global first,
1213					// override with environment next
1214					if (file_exists($path.'config/'.strtolower($class).'.php'))
1215					{
1216						include($path.'config/'.strtolower($class).'.php');
1217						$found = TRUE;
1218					}
1219					elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php'))
1220					{
1221						include($path.'config/'.ucfirst(strtolower($class)).'.php');
1222						$found = TRUE;
1223					}
1224
1225					if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
1226					{
1227						include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
1228						$found = TRUE;
1229					}
1230					elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
1231					{
1232						include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
1233						$found = TRUE;
1234					}
1235
1236					// Break on the first found configuration, thus package
1237					// files are not overridden by default paths
1238					if ($found === TRUE)
1239					{
1240						break;
1241					}
1242				}
1243			}
1244		}
1245
1246		$class_name = $prefix.$class;
1247
1248		// Is the class name valid?
1249		if ( ! class_exists($class_name, FALSE))
1250		{
1251			log_message('error', 'Non-existent class: '.$class_name);
1252			show_error('Non-existent class: '.$class_name);
1253		}
1254
1255		// Set the variable name we will assign the class to
1256		// Was a custom class name supplied? If so we'll use it
1257		if (empty($object_name))
1258		{
1259			$object_name = strtolower($class);
1260			if (isset($this->_ci_varmap[$object_name]))
1261			{
1262				$object_name = $this->_ci_varmap[$object_name];
1263			}
1264		}
1265
1266		// Don't overwrite existing properties
1267		$CI =& get_instance();
1268		if (isset($CI->$object_name))
1269		{
1270			if ($CI->$object_name instanceof $class_name)
1271			{
1272				log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted.");
1273				return;
1274			}
1275
1276			show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance.");
1277		}
1278
1279		// Save the class name and object name
1280		$this->_ci_classes[$object_name] = $class;
1281
1282		// Instantiate the class
1283		$CI->$object_name = isset($config)
1284			? new $class_name($config)
1285			: new $class_name();
1286	}
1287
1288	// --------------------------------------------------------------------
1289
1290	/**
1291	 * CI Autoloader
1292	 *
1293	 * Loads component listed in the config/autoload.php file.
1294	 *
1295	 * @used-by	CI_Loader::initialize()
1296	 * @return	void
1297	 */
1298	protected function _ci_autoloader()
1299	{
1300		if (file_exists(APPPATH.'config/autoload.php'))
1301		{
1302			include(APPPATH.'config/autoload.php');
1303		}
1304
1305		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
1306		{
1307			include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
1308		}
1309
1310		if ( ! isset($autoload))
1311		{
1312			return;
1313		}
1314
1315		// Autoload packages
1316		if (isset($autoload['packages']))
1317		{
1318			foreach ($autoload['packages'] as $package_path)
1319			{
1320				$this->add_package_path($package_path);
1321			}
1322		}
1323
1324		// Load any custom config file
1325		if (count($autoload['config']) > 0)
1326		{
1327			foreach ($autoload['config'] as $val)
1328			{
1329				$this->config($val);
1330			}
1331		}
1332
1333		// Autoload helpers and languages
1334		foreach (array('helper', 'language') as $type)
1335		{
1336			if (isset($autoload[$type]) && count($autoload[$type]) > 0)
1337			{
1338				$this->$type($autoload[$type]);
1339			}
1340		}
1341
1342		// Autoload drivers
1343		if (isset($autoload['drivers']))
1344		{
1345			$this->driver($autoload['drivers']);
1346		}
1347
1348		// Load libraries
1349		if (isset($autoload['libraries']) && count($autoload['libraries']) > 0)
1350		{
1351			// Load the database driver.
1352			if (in_array('database', $autoload['libraries']))
1353			{
1354				$this->database();
1355				$autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
1356			}
1357
1358			// Load all other libraries
1359			$this->library($autoload['libraries']);
1360		}
1361
1362		// Autoload models
1363		if (isset($autoload['model']))
1364		{
1365			$this->model($autoload['model']);
1366		}
1367	}
1368
1369	// --------------------------------------------------------------------
1370
1371	/**
1372	 * Prepare variables for _ci_vars, to be later extract()-ed inside views
1373	 *
1374	 * Converts objects to associative arrays and filters-out internal
1375	 * variable names (i.e. keys prefixed with '_ci_').
1376	 *
1377	 * @param	mixed	$vars
1378	 * @return	array
1379	 */
1380	protected function _ci_prepare_view_vars($vars)
1381	{
1382		if ( ! is_array($vars))
1383		{
1384			$vars = is_object($vars)
1385				? get_object_vars($vars)
1386				: array();
1387		}
1388
1389		foreach (array_keys($vars) as $key)
1390		{
1391			if (strncmp($key, '_ci_', 4) === 0)
1392			{
1393				unset($vars[$key]);
1394			}
1395		}
1396
1397		return $vars;
1398	}
1399
1400	// --------------------------------------------------------------------
1401
1402	/**
1403	 * CI Component getter
1404	 *
1405	 * Get a reference to a specific library or model.
1406	 *
1407	 * @param 	string	$component	Component name
1408	 * @return	bool
1409	 */
1410	protected function &_ci_get_component($component)
1411	{
1412		$CI =& get_instance();
1413		return $CI->$component;
1414	}
1415}
1416