function no_cache() { $this->output->set_header('Last-Modified: ' . gmdate("D, d M Y H:i:s") . ' GMT'); $this->output->set_header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); $this->output->set_header('Pragma: no-cache'); $this->output->set_header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); }
But I’m thinking it would be good to add some function in the class to set the header and then have the no_cache as a config setting or something along those lines…
For that matter, the code between the head elements could be embedded in a view fragment called head and loaded using the part method. Then it might become easier to dynamically control head based on config values as well as code that could conditionally sets values. For example, consider the case where you usually load meta data from language strings for your various modules and also need to load meta data stored with content in a table (e.g., the meta data for a blog article).
I was using the following functions as a ‘helper’ to add things like javascript and css to the header…
I just like that I can use the controller to throw a directory at bundleFiles, which returns an array of paths that are then included in the view..
It makes including stuff like scriptaculous very easy… or it should… one problem is that you’re at the mercy of the filename sort; and so, you have to prepend your js file names with a number so that they load in the right order..
You can do the same with CSS files etc.. probably not as efficient as the class, but it works for me.
/** * bundleFiles * * gather all files in a directory, and return them in an array. * * @access public * @param directory to be scanned * @param whether or not to go beyond first directory level * @param how far down do you want to go? * @return array */
function bundleFiles($dir, $recurse=false, $depth=false) { # Original PHP code by Chirp Internet: www.chirp.com.au # Please acknowledge use of this code by including this header. # array to hold return value $retval = array(); # add trailing slash if missing if(substr($dir, -1) != "/") $dir .= "/"; # open pointer to directory and read list of files $d = @dir($dir) or die("getFileList: Failed opening directory $dir for reading"); while(false !== ($entry = $d->read())) { # skip hidden files if($entry[0] == ".") continue; if($entry == "index.html") continue; if(is_dir("$dir$entry")) { $retval[] = "$dir$entry"; if($recurse && is_readable("$dir$entry/")) { if($depth === false) { $retval = array_merge($retval, getFileList("$dir$entry/", true)); } elseif($depth > 0) { $retval = array_merge($retval, getFileList("$dir$entry/", true, $depth-1)); } } } elseif(is_readable("$dir$entry")) { $retval[] = "$dir$entry"; } } $d->close(); return $retval; }
/** * getJavaScript * * Generates script tags. Takes one parameter, a string or and array that contains * the path(s) to javascript file(s). * * DO NOT INCLUDE A LEADING SLASH!!! * * @access public * @param string or array * @return html string */
function getJavaScript($path, $html = NULL) { // Loop through if array if(is_array($path)){ foreach ($path as $js) { $html .= "<script type='text/javascript' src='" . base_url() . "$js'>\n"; } return $html; }
// Not an array. Include single file. else{ return "<script type='text/javascript' src='" . base_url() . "$path'>"; } }
/** * getCSS * * Generates link tags for CSS. Takes one parameter, a string or and array that contains * the path(s) to external style sheet(s) relative to the base URL/public/css. * * DO NOT INCLUDE A LEADING SLASH!!! * * @access public * @param string or array * @return html string */
/** * getModular * * Loads module view(s). Takes one parameter, a string or an array that contains * the name of the module view file(s) in /system/application/views/modules. * * DO NOT INCLUDE A LEADING SLASH * * * @access public * @param string or array * @return html CodeIgniter View */
function getModular($moduleNames){ // if there are more than one... if(is_array($moduleNames)){ foreach ($moduleNames as $module) { $this->load->view("modules/" . $module); } }
I use a variation of the View library for my own CI subframework and always use parsed templates. You made me realize that parsing support was missing from this public version of the View library. Sorry about that!
How about a $parse config variable instead? That’s how my version of the library handles things. CI’s Parser, which used, also handles non-parsed templates, so by putting the View library into parsing mode, you will get the best of both. That would let you use the single API without having to remember anything extra except to set the config variable to TRUE.
Support for configuration file
The following features can be configured in a config/View.php configuration file, as detailed below.
Support for sending No-Cache headers to browser
The library can send no-cache headers to the browser:
$this->view->no_cache();
(I haven’t yet explored whether it’s possible to send replacement headers to “undo” previously-sent no-cache headers - anybody know?)
Configuration value in config/View.php for specifying cache timeout
$config['cache_timeout'] = 30;
Support for Parsed Templates
Not sure how this one got missed, but the library now supports parsing of template files. As you load each partial or the full view, the Parser library is invoked if in parsing mode.
Method #1 - set “parse” configuration value to TRUE in config/View.php
$config['parse'] = TRUE;
Method #2 - call set_parse_mode() function
$this->view->set_parse_mode(TRUE); // defaults to TRUE, so parameter is optional
I was having a shower and it suddenly occurred to me that I had the cache logic backwards in the latest version of this library. Setting the “cache” flag to TRUE would send the no-cache headers to the browser. Wonder where my head was this morning?
Anyway, I’ve updated the library so that setting “cache” to TRUE or calling set_cache_mode(TRUE) triggers CI’s cache mechanism. An additional configuration option was added to control the timeout of the cache, which defaults to 30 minutes. Disabling the cache setting in this library (which it is by default), sends the no-cache headers to the browser. My thinking here is that the caching of the browser should match the caching on the server, but I’d like your feedback on this. Maybe this is a little too aggressive?
Should the no-cache headers be sent for every view that is not being cached on the server? Normally, the browser would cache images, javascript, etc. by default.
I believe that the view library is an important advance for CI and your efforts are much appreciated! The CI cache is not very flexible at this point. The main problem is that it caches the whole page.
Maybe someone could figure out how to weave these two together into server cache-able ‘layoutable’ views demo code. I’m not suggesting building a huge library all in one, but just a few lines of demo code to show how these could be used together.
@coolfactor
Thank you for your efforts on this class.
For easy usage, would it be an option to add phpdoc style comments to the methods, similar to CI?
I’ve updated the View library to correct the caching logic. The set_cache_mode() method was removed since it was pointless. Replacing it is the no_cache() method that Athfar had suggested. That method does two things:
(1) tells CI not to cache the view (in case it was previously scheduled for caching)
(2) tells the browser not to cache the view
I hope that is the correct behavior. You can always tell *just* CI to cache the view by using:
$this->view->cache(30);
which just calls CI’s Output::cache() method internally, but the parameter is optional and uses the configured “cache_timeout” value if not specified.
Note: the view is cached automatically just by specifying a timeout value in the config.
With the following, the view will be cached automatically for 30 minutes:
$config['cache_timeout'] = 30;
You can override any default caching by calling:
$this->view->cache(15); // only cache for 15 minutes
phpDoc comments
I’ve also added phpDoc-style comments.
Maybe someone could figure out how to weave these two together into server cache-able ‘layoutable’ views demo code. I’m not suggesting building a huge library all in one, but just a few lines of demo code to show how these could be used together.
As much as I respect Coolfactor’s skills and ideas, I think AI’s approach to using a separate Cache library is a good idea from a code reuse point of view—it leads to less code redundance in the framework, standardizes caching code, and should lead to less reading in the docs. This is one area where I hope EllisLabs does a bit a refactoring to the core. The use of cache groups also opens the door to some very fine grained caching approaches that might not seem immediately apparent.