Welcome Guest, Not a member yet? Register   Sign In
CI2.0 - View files in third_party packages
#1

[eluser]JasonS[/eluser]
I found that I required view files in packages. To do this I extended the loader file.

The changes are pretty simple. This extension does the following.

1) Creates a new class variable _ci_view_paths
2) Add's the default APPPATH to the above array.
3) _add_package_path is extended to add the custom path to the _ci_view_paths variable.
4) As usual the _ci_load function does its thing. Before throwing the 'file not found' error we loop through the _ci_view_paths array to check third_party paths.

I would greatly appreciate any feedback on what could be improved.

Changes Marked with a comment. //++

Code:
<?php

class MY_Loader extends CI_Loader {
    
    var $_ci_view_paths = array(); //++
    
    function CI_Loader()
    {    
        $this->_ci_view_path = APPPATH.'views/';
        $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(APPPATH); //++
            
        log_message('debug', "Loader Class Initialized");
    }
    
    function add_package_path($path)
    {
        array_unshift($this->_ci_library_paths, $path);
        array_unshift($this->_ci_model_paths, $path);
        array_unshift($this->_ci_helper_paths, $path);
        array_unshift($this->_ci_view_paths, $path); //++
            
        // Add config file path
        $config =& $this->_ci_get_component('config');
        array_unshift($config->_config_paths, $path);
    }
    
    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];
        }

        // Set the path to the requested file
        if ($_ci_path == '')
        {
            $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
            $_ci_file = ($_ci_ext == '') ? $_ci_view.EXT : $_ci_view;
            $_ci_path = $this->_ci_view_path.$_ci_file;
        }
        else
        {
            $_ci_x = explode('/', $_ci_path);
            $_ci_file = end($_ci_x);
        }
        
        foreach($this->_ci_view_paths as $path) { //++
            if (file_exists($path . 'views/' . $_ci_view . EXT)) { //++
                $_ci_path = $path . 'views/' . $_ci_view . EXT; //++
            } //++
        } //++
        
        if ( ! 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.
        // Only needed when running PHP 5
        
        if ($this->_ci_is_instance())
        {
            $_ci_CI =& get_instance();
            foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
            {
                if ( ! isset($this->$_ci_key))
                {
                    $this->$_ci_key =& $_ci_CI->$_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);
        }
        extract($this->_ci_cached_vars);
                
        /*
         * 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();
                
        // If the PHP installation does not support short tags we'll
        // do a little string replacement, changing the short tags
        // to standard PHP echo statements.
        
        if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
        {
            echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
        }
        else
        {
            include($_ci_path); // include() vs include_once() allows for multiple views with the same name
        }
        
        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
        {
            // PHP 4 requires that we use a global
            global $OUT;
            $OUT->append_output(ob_get_contents());
            @ob_end_clean();
        }
    }
    
}
#2

[eluser]WanWizard[/eluser]
I have made the same change to support views. I added support for controllers as well. So far it works without problems.

The only issue with packages in general that I still have is the lack of namespace.

I use packages to implement modularity without having to resort to HMVC, matchbox, etc.
Works fine, but if you use multiple packages, filenames must be unique across packages, otherwise the wrong file will be loaded.

I'm currently trying to figure out a way to deal with this, using the module folder name as a sort of 'namespace'.
#3

[eluser]Buso[/eluser]
[quote author="WanWizard" date="1279105084"]I have made the same change to support views. I added support for controllers as well. So far it works without problems.

The only issue with packages in general that I still have is the lack of namespace.

I use packages to implement modularity without having to resort to HMVC, matchbox, etc.
Works fine, but if you use multiple packages, filenames must be unique across packages, otherwise the wrong file will be loaded.

I'm currently trying to figure out a way to deal with this, using the module folder name as a sort of 'namespace'.[/quote]
I added support for controllers too but only for one package (my 'common' package, which I use as a fallback for everything)

Using packages as a way of modularize CI isn't slow? If you have 10 active packages, each time you ask for 'somefile.php', it will iterate over an average of 5,5 packages. Then we have to multiply that by the amount of files you need.

I don't know much about optimization anyway, so I may have said something stupid Smile
#4

[eluser]pbreit[/eluser]
I suppose it's conceivable this could be cached. But, yes, I wonder if there's much a performance hit (for what is a cool feature).
#5

[eluser]WanWizard[/eluser]
Still working on that.

The issue with simply iterating over directories is not only performance (which on linux is not too bad, as results of stat calls are cached ), but also the issue of namespace. Two modules can have a library called "test". Which one to load?

At the moment I pass the name of the module as parameter to the various load methods, and add this at the top of the path stack. So providing I have not made a typo, the first file_exists() hits.

First impressions are that packages to nice to introduce multiple directories for libraries, etc. But it is just that. All restrictions you have with a single directory still apply.




Theme © iAndrew 2016 - Forum software by © MyBB