<?php
/**
 * This source file is part of the open source project
 * ExpressionEngine (https://expressionengine.com)
 *
 * @link      https://expressionengine.com/
 * @copyright Copyright (c) 2003-2022, Packet Tide, LLC (https://www.packettide.com)
 * @license   https://expressionengine.com/license Licensed under Apache License, Version 2.0
 */

use  ExpressionEngine\Library\Core\LoaderFacade;

/**
 * Loader Class
 *
 * Loads views and files
 */
class EE_Loader
{
    protected $_ci_ob_level;
    protected $_ci_view_paths = array();
    protected $_ci_library_paths = array();
    protected $_ci_model_paths = array();
    protected $_ci_helper_paths = array();
    protected $_base_classes = array(); // Set by the controller class
    protected $_ci_cached_vars = array();
    protected $_ci_classes = array();
    protected $_ci_loaded_files = array();
    protected $_ci_models = array();
    protected $_ci_helpers = array();
    protected $_ci_varmap = array('user_agent' => 'agent');

    protected $facade;

    /**
     * Constructor
     *
     * Sets the path to the view files and gets the initial output buffering level
     */
    public function __construct()
    {
        $this->_ci_ob_level = ob_get_level();
        $this->_ci_library_paths = array(APPPATH, BASEPATH);
        $this->_ci_helper_paths = array(APPPATH, BASEPATH);
        $this->_ci_model_paths = array(APPPATH);
        $this->_ci_view_paths = array(SYSPATH . 'ee/ExpressionEngine/View/' => true);

        $this->set_base_classes();

        log_message('debug', "Loader Class Initialized");
    }

    /**
     * Set the old superglobal facade, which we'll bind all
     * of the singletons on.
     */
    public function setFacade($facade)
    {
        if (isset($this->facade)) {
            throw new \Exception('Cannot override the loader facade.');
        }

        $this->facade = $facade;

        // Assign all the class objects that were instantiated by the
        // bootstrap file (boot.php) to local class variables
        // so that CI can run as one big super object.
        foreach (is_loaded() as $var => $class) {
            $var = ($var == 'loader') ? 'load' : $var;
            $this->facade->set($var, load_class($class, 'core'));
        }
    }

    /**
     * Load EE View
     *
     * This is for limited use inside packages. It loads from EE's main cp
     * theme folder and ignores the package's view folder. The main reason
     * for doing this are layout things, like the glossary. Most developers
     * will not need this. -pk
     *
     * @param	string
     * @param	array 	variables to be loaded into the view
     * @param	bool 	return or not
     * @return	void
     */
    public function ee_view($view, $vars = array(), $return = false)
    {
        $ee_only = array();
        $orig_paths = $this->_ci_view_paths;

        // Regular themes cascade down to the first
        // path (APPPATH.'views'), so we copy them over
        // until we hit a third party or non_cascading path.

        foreach (array_reverse($orig_paths, true) as $path => $cascade) {
            if (strpos($path, PATH_THIRD) !== false or $cascade === false) {
                break;
            }

            $ee_only[$path] = true;
        }

        // Temporarily replace them, load the view, and back again
        $this->_ci_view_paths = array_reverse($ee_only, true);

        $ret = $this->view($view, $vars, $return);

        $this->_ci_view_paths = $orig_paths;

        return $ret;
    }

    /**
     * Class Loader
     *
     * This function lets users load and instantiate classes.
     * It is designed to be called from a user's app controllers.
     *
     * @param	string	the name of the class
     * @param	mixed	the optional parameters
     * @param	string	an optional object name
     * @return	void
     */
    public function library($library = '', $params = null, $object_name = null)
    {
        if (is_array($library)) {
            foreach ($library as $read) {
                $this->library($read);
            }

            return;
        }

        if (strtolower($library) == 'api') {
            $object_name = 'legacy_api';
        }

        // Security is always loaded
        if (strtolower($library) == 'security') {
            return null;
        }

        if (is_array($library)) {
            foreach ($library as $class) {
                $this->library($class, $params);
            }

            return;
        }

        if ($library == '' or isset($this->_base_classes[$library])) {
            return false;
        }

        if (! is_null($params) && ! is_array($params)) {
            $params = null;
        }

        $this->_ci_load_class($library, $params, $object_name);
    }

    /**
     * Add to the theme cascading
     *
     * Adds a theme to cascade down to. You probably don't
     * need to call this. No really, don't.
     */
    public function add_theme_cascade($theme_path)
    {
        $this->_ci_view_paths = array($theme_path => true) + $this->_ci_view_paths;
    }

    /**
     * Get top of package path
     *
     * We use this to allow package js/css loading, where we need to figure out
     * a theme name. May be renamed in the future, don't use it.
     */
    public function first_package_path()
    {
        reset($this->_ci_view_paths);

        return key($this->_ci_view_paths);
    }

    /**
     * Set _base_classes variable
     *
     * @param 	array
     * @return 	object
     */
    public function set_base_classes()
    {
        $this->_base_classes = is_loaded();

        return $this;
    }

    /**
     * Is Loaded
     *
     * A utility function to test if a class is in the self::$_ci_classes array.
     * This function returns the object name if the class tested for is loaded,
     * and returns FALSE if it isn't.
     *
     * It is mainly used in the form_helper -> _get_validation_object()
     *
     * @param 	string	class being checked for
     * @return 	mixed	class object name on the CI SuperObject or FALSE
     */
    public function is_loaded($class)
    {
        if (isset($this->_ci_classes[$class])) {
            return $this->_ci_classes[$class];
        }

        return false;
    }

    /**
     * Model Loader
     *
     * This function lets users load and instantiate models.
     *
     * @param	string	the name of the class
     * @param	string	name for the model
     * @param	bool	database connection
     * @return	void
     */
    public function model($model, $name = '', $db_conn = false)
    {
        if (is_array($model)) {
            foreach ($model as $babe) {
                $this->model($babe);
            }

            return;
        }

        if ($model == '') {
            return;
        }

        $path = '';

        // Is the model in a sub-folder? If so, parse out the filename and path.
        if (($last_slash = strrpos($model, '/')) !== false) {
            // The path is in front of the last slash
            $path = substr($model, 0, $last_slash + 1);

            // And the model name behind it
            $model = substr($model, $last_slash + 1);
        }

        if ($name == '') {
            $name = $model;
        }

        if (in_array($name, $this->_ci_models, true)) {
            return;
        }

        if ($this->facade->has($name)) {
            show_error('The model name you are loading is the name of a resource that is already being used: ' . $name);
        }

        $model = strtolower($model);

        foreach ($this->_ci_model_paths as $mod_path) {
            if (! file_exists($mod_path . 'models/' . $path . $model . '.php')) {
                continue;
            }

            if ($db_conn !== false and ! class_exists('CI_DB')) {
                if ($db_conn === true) {
                    $db_conn = '';
                }

                $this->database($db_conn, false, true);
            }

            if (! class_exists('EE_Model')) {
                load_class('Model', 'core');
            }

            require_once($mod_path . 'models/' . $path . $model . '.php');

            $model = ucfirst($model);

            $this->facade->set($name, new $model());

            $this->_ci_models[] = $name;

            return;
        }

        // couldn't find the model
        show_error('Unable to locate the model you have specified: ' . $model);
    }

    /**
     * Database Loader
     *
     * @param	string	the DB credentials
     * @param	bool	whether to return the DB object
     * @param	bool	whether to enable active record (this allows us to override the config setting)
     * @return	object
     */
    public function database($params = '', $return = false, $active_record = null)
    {
        // Do we even need to load the database class?
        if (class_exists('CI_DB') and $return == false and $active_record == null and isset(ee()->db) and is_object(ee()->db)) {
            return false;
        }

        $path = (defined('EE_APPPATH')) ? EE_APPPATH : APPPATH;
        require_once($path . 'database/DB.php');

        if ($return === true) {
            return DB($params);
        }

        if ($this->facade->has('db')) {
            return;
        }

        // Load the DB class
        $this->facade->set('db', DB($params));
    }

    /**
     * Load the Utilities Class
     *
     * @return	string
     */
    public function dbutil()
    {
        if ($this->facade->has('dbutil')) {
            return;
        }

        if (! class_exists('CI_DB')) {
            $this->database();
        }

        // for backwards compatibility, load dbforge so we can extend dbutils off it
        // this use is deprecated and strongly discouraged
        $this->dbforge();

        require_once(BASEPATH . 'database/DB_utility.php');
        require_once(BASEPATH . 'database/drivers/' . ee()->db->dbdriver . '/' . ee()->db->dbdriver . '_utility.php');
        $class = 'CI_DB_' . ee()->db->dbdriver . '_utility';

        $this->facade->set('dbutil', new $class());
    }

    /**
     * Load the Database Forge Class
     *
     * @return	string
     */
    public function dbforge()
    {
        if ($this->facade->has('dbforge')) {
            return;
        }

        if (! class_exists('CI_DB')) {
            $this->database();
        }

        require_once(BASEPATH . 'database/DB_forge.php');
        require_once(BASEPATH . 'database/drivers/' . ee()->db->dbdriver . '/' . ee()->db->dbdriver . '_forge.php');
        $class = 'CI_DB_' . ee()->db->dbdriver . '_forge';

        $this->facade->set('dbforge', new $class());
    }

    /**
     * Load View
     *
     * This function is used to load a "view" file.  It has three parameters:
     *
     * 1. The name of the "view" file to be included.
     * 2. An associative array of data to be extracted for use in the view.
     * 3. TRUE/FALSE - whether to return the data or load it.  In
     * some cases it's advantageous to be able to return data so that
     * a developer can process it in some way.
     *
     * @param	string
     * @param	array
     * @param	bool
     * @return	void
     */
    public function view($view, $vars = array(), $return = false)
    {
        return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
    }

    /**
     * Load File
     *
     * This is a generic file loader
     *
     * @param	string
     * @param	bool
     * @return	string
     */
    public function file($path, $return = false)
    {
        return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
    }

    /**
     * Set Variables
     *
     * Once variables are set they become available within
     * the controller class and its "view" files.
     *
     * @param	array
     * @return	void
     */
    public function vars($vars = array(), $val = '')
    {
        if ($val != '' and is_string($vars)) {
            $vars = array($vars => $val);
        }

        $vars = $this->_ci_object_to_array($vars);

        if (is_array($vars) and count($vars) > 0) {
            foreach ($vars as $key => $val) {
                $this->_ci_cached_vars[$key] = $val;
            }
        }
    }

    /**
     * Load Helper
     *
     * This function loads the specified helper file.
     *
     * @param	mixed
     * @return	void
     */
    public function helper($helpers = array())
    {
        foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper) {
            if (isset($this->_ci_helpers[$helper])) {
                continue;
            }

            $ext_helper = APPPATH . 'helpers/' . config_item('subclass_prefix') . $helper . '.php';

            // Is this a helper extension request?
            if (file_exists($ext_helper)) {
                $base_helper = BASEPATH . 'helpers/' . $helper . '.php';

                if (! file_exists($base_helper)) {
                    show_error('Unable to load the requested file: helpers/' . $helper . '.php');
                }

                include_once($ext_helper);
                include_once($base_helper);

                $this->_ci_helpers[$helper] = true;
                log_message('debug', 'Helper loaded: ' . $helper);

                continue;
            }

            // Try to load the helper
            foreach ($this->_ci_helper_paths as $path) {
                if (file_exists($path . 'helpers/' . $helper . '.php')) {
                    include_once($path . 'helpers/' . $helper . '.php');

                    $this->_ci_helpers[$helper] = true;
                    log_message('debug', 'Helper loaded: ' . $helper);

                    break;
                }
            }

            // unable to load the helper
            if (! isset($this->_ci_helpers[$helper])) {
                show_error('Unable to load the requested file: helpers/' . $helper . '.php');
            }
        }
    }

    /**
     * Load Helpers
     *
     * This is simply an alias to the above function in case the
     * user has written the plural form of this function.
     *
     * @param	array
     * @return	void
     */
    public function helpers($helpers = array())
    {
        $this->helper($helpers);
    }

    /**
     * Loads a language file
     *
     * @param	array
     * @param	string
     * @return	void
     */
    public function language($file = array(), $lang = '')
    {
        if (! is_array($file)) {
            $file = array($file);
        }

        foreach ($file as $langfile) {
            ee()->lang->load($langfile, $lang);
        }
    }

    /**
     * Loads a config file
     *
     * @param	string
     * @return	void
     */
    public function config($file = '', $use_sections = false, $fail_gracefully = false)
    {
        ee()->config->load($file, $use_sections, $fail_gracefully);
    }

    /**
     * Driver Loader
     *
     * Loads a driver library.
     *
     * @param	string|string[]	$library	Driver name(s)
     * @param	array		$params		Optional parameters to pass to the driver
     * @param	string		$object_name	An optional object name to assign to
     *
     * @return	void|object|bool	Object or FALSE on failure if $library is a string
     *					and $object_name is set. void otherwise.
     */
    public function driver($library = '', $params = null, $object_name = null)
    {
        if (is_array($library)) {
            foreach ($library as $driver) {
                $this->driver($driver);
            }

            return;
        }

        if ($library === '') {
            return false;
        }

        if (! class_exists('EE_Driver_Library', false)) {
            // We aren't instantiating an object here, just making the base class available
            require BASEPATH . 'libraries/Driver.php';
        }

        // We can save the loader some time since Drivers will *always* be in a subfolder,
        // and typically identically named to the library
        if (! strpos($library, '/')) {
            $library = ucfirst($library) . '/' . $library;
        }

        return $this->library($library, $params, $object_name);
    }

    /**
     * Add Package Path
     *
     * Prepends a parent path to the library, model, helper, and config path arrays
     *
     * @param	string
     * @param 	boolean
     * @return	void
     */
    public function add_package_path($path, $view_cascade = true)
    {
        $path = rtrim($path, '/') . '/';

        array_unshift($this->_ci_library_paths, $path);
        array_unshift($this->_ci_model_paths, $path);
        array_unshift($this->_ci_helper_paths, $path);

        $this->_ci_view_paths = array($path . 'views/' => $view_cascade) + $this->_ci_view_paths;

        // Add config file path
        $config = $this->_ci_get_component('config');
        array_unshift($config->_config_paths, $path);
    }

    /**
     * Get Package Paths
     *
     * Return a list of all package paths, by default it will ignore BASEPATH.
     *
     * @param	string
     * @return	void
     */
    public function get_package_paths($include_base = false)
    {
        return $include_base === true ? $this->_ci_library_paths : $this->_ci_model_paths;
    }

    /**
     * Remove Package Path
     *
     * Remove a path from the library, model, and helper path arrays if it exists
     * If no path is provided, the most recently added path is removed.
     *
     * @param	type
     * @return	type
     */
    public function remove_package_path($path = '', $remove_config_path = true)
    {
        $config = $this->_ci_get_component('config');

        if ($path == '') {
            $void = array_shift($this->_ci_model_paths);
            $void = array_shift($this->_ci_helper_paths);
            $void = array_shift($config->_config_paths);
            $removed = array_shift($this->_ci_library_paths);

            // view paths only exist once, so we only remove it if all are gone
            // or it was at the top.
            if (array_search($removed, $this->_ci_library_paths) === false or
                current(array_keys($this->_ci_view_paths)) == $removed . 'views/') {
                $void = array_shift($this->_ci_view_paths);
            }
        } else {
            $path = rtrim($path, '/') . '/';
            foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var) {
                if (($key = array_search($path, $this->{$var})) !== false) {
                    unset($this->{$var}[$key]);
                }
            }

            if (isset($this->_ci_view_paths[$path . 'views/'])) {
                unset($this->_ci_view_paths[$path . 'views/']);
            }

            if (($key = array_search($path, $config->_config_paths)) !== false) {
                unset($config->_config_paths[$key]);
            }
        }

        // make sure the application default paths are still in the array
        $this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
        $this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
        $this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
        $this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH . 'views/' => true));
        $config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
    }

    /**
     * Loader
     *
     * This function is used to load views and files.
     * Variables are prefixed with _ci_ to avoid symbol collision with
     * variables made available to view files
     *
     * @param	array
     * @return	void
     */
    protected function _ci_load($_ci_data)
    {
        // Set the default data variables
        foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val) {
            $$_ci_val = (! isset($_ci_data[$_ci_val])) ? false : $_ci_data[$_ci_val];
        }

        $file_exists = false;

        // Set the path to the requested file
        if ($_ci_path != '') {
            $_ci_x = explode('/', $_ci_path);
            $_ci_file = end($_ci_x);
        } else {
            $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
            $_ci_file = ($_ci_ext == '') ? $_ci_view . '.php' : $_ci_view;

            foreach ($this->_ci_view_paths as $view_file => $cascade) {
                if (file_exists($view_file . $_ci_file)) {
                    $_ci_path = $view_file . $_ci_file;
                    $file_exists = true;

                    break;
                }

                if (! $cascade) {
                    break;
                }
            }
        }

        if (! $file_exists && ! file_exists($_ci_path)) {
            show_error('Unable to load the requested file: ' . $_ci_file);
        }

        // This allows anything loaded using $this->load (views, files, etc.)
        // to become accessible from within the Controller and Model functions.

        foreach (get_object_vars(ee()) as $_ci_key => $_ci_var) {
            if (! isset($this->$_ci_key)) {
                $this->$_ci_key = & ee()->$_ci_key;
            }
        }

        /*
         * Extract and cache variables
         *
         * You can either set variables using the dedicated $this->load_vars()
         * function or via the second parameter of this function. We'll merge
         * the two types and cache them so that views that are embedded within
         * other views can have access to these variables.
         */
        if (is_array($_ci_vars)) {
            $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
        }

        if (is_php('5.4.0')) {
            $use_eval = false;
        } else {
            $use_eval = ((bool) @ini_get('short_open_tag') === false and config_item('rewrite_short_tags') == true);
        }

        /*
         * Buffer the output
         *
         * We buffer the output for two reasons:
         * 1. Speed. You get a significant speed boost.
         * 2. So that the final rendered template can be
         * post-processed by the output class.  Why do we
         * need post processing?  For one thing, in order to
         * show the elapsed page load time.  Unless we
         * can intercept the content right before it's sent to
         * the browser and then stop the timer it won't be accurate.
         */
        ob_start();

        $this->facade->runFileInFacadeScope($_ci_path, $this->_ci_cached_vars, $use_eval);

        log_message('debug', 'File loaded: ' . $_ci_path);

        // Return the file data if requested
        if ($_ci_return === true) {
            $buffer = ob_get_contents();
            @ob_end_clean();

            return $buffer;
        }

        /*
         * Flush the buffer... or buff the flusher?
         *
         * In order to permit views to be nested within
         * other views, we need to flush the content back out whenever
         * we are beyond the first level of output buffering so that
         * it can be seen and included properly by the first included
         * template and any subsequent ones. Oy!
         *
         */
        if (ob_get_level() > $this->_ci_ob_level + 1) {
            ob_end_flush();
        } else {
            ee()->output->append_output(ob_get_contents());
            @ob_end_clean();
        }
    }

    /**
     * Load class
     *
     * This function loads the requested class.
     *
     * @param	string	the item that is being loaded
     * @param	mixed	any additional parameters
     * @param	string	an optional object name
     * @return	void
     */
    protected function _ci_load_class($class, $params = null, $object_name = null)
    {
        // Get the class name, and while we're at it trim any slashes.
        // The directory path can be included as part of the class name,
        // but we don't want a leading slash
        $class = str_replace('.php', '', trim($class, '/'));

        // Was the path included with the class name?
        // We look for a slash to determine this
        $subdir = '';
        if (($last_slash = strrpos($class, '/')) !== false) {
            // Extract the path
            $subdir = substr($class, 0, $last_slash + 1);

            // Get the filename from the path
            $class = substr($class, $last_slash + 1);
        }

        // We'll test for both lowercase and capitalized versions of the file name
        foreach (array(ucfirst($class), strtolower($class)) as $class) {
            $subclass = APPPATH . 'libraries/' . $subdir . config_item('subclass_prefix') . $class . '.php';

            // Is this a class extension request?
            if (file_exists($subclass)) {
                $baseclass = BASEPATH . 'libraries/' . ucfirst($class) . '.php';

                if (! file_exists($baseclass)) {
                    log_message('error', "Unable to load the requested class: " . $class);
                    show_error("Unable to load the requested class: " . $class);
                }

                // Safety:  Was the class already loaded by a previous call?
                if (in_array($subclass, $this->_ci_loaded_files)) {
                    // Before we deem this to be a duplicate request, let's see
                    // if a custom object name is being supplied.  If so, we'll
                    // return a new instance of the object
                    if (! is_null($object_name)) {
                        if (! isset(ee()->$object_name)) {
                            return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
                        }
                    }

                    $is_duplicate = true;
                    log_message('debug', $class . " class already loaded. Second attempt ignored.");

                    return;
                }

                include_once($baseclass);
                include_once($subclass);
                $this->_ci_loaded_files[] = $subclass;

                return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
            }

            // Lets search for the requested library file and load it.
            $is_duplicate = false;
            foreach ($this->_ci_library_paths as $path) {
                $filepath = $path . 'libraries/' . $subdir . $class . '.php';

                // Does the file exist?  No?  Bummer...
                if (! file_exists($filepath)) {
                    continue;
                }

                // Safety:  Was the class already loaded by a previous call?
                if (in_array($filepath, $this->_ci_loaded_files)) {
                    // Before we deem this to be a duplicate request, let's see
                    // if a custom object name is being supplied.  If so, we'll
                    // return a new instance of the object
                    if (! is_null($object_name)) {
                        if (! isset(ee()->$object_name)) {
                            return $this->_ci_init_class($class, '', $params, $object_name);
                        }
                    }

                    $is_duplicate = true;
                    log_message('debug', $class . " class already loaded. Second attempt ignored.");

                    return;
                }

                include_once($filepath);
                $this->_ci_loaded_files[] = $filepath;

                return $this->_ci_init_class($class, '', $params, $object_name);
            }
        } // END FOREACH

        // One last attempt.  Maybe the library is in a subdirectory, but it wasn't specified?
        if ($subdir == '') {
            $path = strtolower($class) . '/' . $class;

            return $this->_ci_load_class($path, $params);
        }

        // If we got this far we were unable to find the requested class.
        // We do not issue errors if the load call failed due to a duplicate request
        if ($is_duplicate == false) {
            log_message('error', "Unable to load the requested class: " . $class);
            show_error("Unable to load the requested class: " . $class);
        }
    }

    /**
     * Instantiates a class
     *
     * @param	string
     * @param	string
     * @param	string	an optional object name
     * @return	null
     */
    protected function _ci_init_class($class, $prefix = '', $config = false, $object_name = null)
    {
        // Is there an associated config file for this class?  Note: these should always be lowercase
        if ($config === null) {
            // We test for both uppercase and lowercase, for servers that
            // are case-sensitive with regard to file names
            $config = ee()->config->loadFile(strtolower($class));
        }

        if ($prefix == '') {
            if (class_exists('EE_' . $class)) {
                $name = 'EE_' . $class;
            } elseif (class_exists(config_item('subclass_prefix') . $class)) {
                $name = config_item('subclass_prefix') . $class;
            } else {
                $name = $class;
            }
        } else {
            $name = $prefix . $class;
        }

        // Is the class name valid?
        if (! class_exists($name)) {
            log_message('error', "Non-existent class: " . $name);
            show_error("Non-existent class: " . $class);
        }

        // Set the variable name we will assign the class to
        // Was a custom class name supplied?  If so we'll use it
        $class = strtolower($class);

        if (is_null($object_name)) {
            $classvar = (! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class];
        } else {
            $classvar = $object_name;
        }

        // Save the class name and object name
        $this->_ci_classes[$class] = $classvar;

        // Instantiate the class
        if (! empty($config)) {
            $this->facade->set($classvar, new $name($config));
        } else {
            $this->facade->set($classvar, new $name());
        }
    }

    /**
     * Object to Array
     *
     * Takes an object as input and converts the class variables to array key/vals
     *
     * @param	object
     * @return	array
     */
    protected function _ci_object_to_array($object)
    {
        return (is_object($object)) ? get_object_vars($object) : $object;
    }

    /**
     * Get a reference to a specific library or model
     *
     * @return	bool
     */
    protected function _ci_get_component($component)
    {
        return ee()->$component;
    }

    /**
     * Prep filename
     *
     * This function preps the name of various items to make loading them more reliable.
     *
     * @param	mixed
     * @return	array
     */
    protected function _ci_prep_filename($filename, $extension)
    {
        if (! is_array($filename)) {
            return array(strtolower(str_replace('.php', '', str_replace($extension, '', $filename)) . $extension));
        } else {
            foreach ($filename as $key => $val) {
                $filename[$key] = strtolower(str_replace('.php', '', str_replace($extension, '', $val)) . $extension);
            }

            return $filename;
        }
    }
}

// EOF
