/application/classes/_kohana.php
PHP | 403 lines | 221 code | 64 blank | 118 comment | 43 complexity | 6ce52b765c911f7da47b413014f1c5f9 MD5 | raw file
- <?php defined('SYSPATH') or die('No direct script access.');
- /**
- * A patch to Kohana 3 Core class that automatically merges autoloaded Ko3
- * classes files in one or two (first is common aka 'preloader' and second is route-dependent).
- * Owing that classes can be loaded faster and app performance can be increased.
- * It will likely be more effective on multiple files PHP applications of big overall size.
- *
- * Installation:
- * 1. Copy that file in Kohana 3 APPPATH/classes folder (if you have already patched Kohana_Core,
- * manually merge this patch with your own).
- * 2. Put
- * Kohana::$caching_merged['status'] = TRUE;
- * and other needed settings (see below) anywhere in APPPATH/bootstrap.php (preferably closer to beginning,
- * but before other than Kohana class methods invokation; I put them right after Kohana::init())
- *
- * Use cases:
- * -> If you want to turn off the merged scripts cache, set in bootstrap:
- * Kohana::$caching_merged['status'] = FALSE;
- * -> To set your custom merged cache folder, change
- * Kohana::$caching_merged['dir'] = 'custom-dir-name';
- * -> You can change classes stored in preloader cache by varying threshold class in:
- * Kohana::$caching_merged['threshold_class'] = 'custom-class-name';
- * please insure that 'threshold_class' always is being loaded after succesful
- * potential Request::instance() call. Default set to "Controller", probably better universal choice now.
- * -> You can store all classes in one big preloader without route-dependent parts.
- * simply by setting
- * Kohana::$caching_merged['threshold_class'] = NULL;
- * Actually no evidence present to absolutely prefer only one kind of merging
- * -> To chosse custom preloader filename change
- * Kohana::$caching_merged['preloader_filename'] = 'custom-file-name';
- * -> After any code update or merged cache settings change you should delete the merged scripts files
- * manually or by uploading to your merge cache dir a file of name specified by
- * Kohana::$caching_merged['clear_cache_filename'] = 'custom-filename';
- * Default is 'ccache'
- *
- * @author Alexander Kupreyeu (mailto:alexander.kupreev@gmail.com, http://kupreev.com)
- * @author Kohana Team
- * @link http://github.com/kohana/core/
- *
- * @version 0.2 (not recommended for production use, only testing and bug reporting)
- *
- */
- class Kohana extends Kohana_Core {
-
- /**
- * @var mixed do we compose preloader during this session?
- * TRUE for preloader, FALSE for path-dependent, NULL for init state
- */
- protected static $_is_preloader = NULL;
-
- /**
- * @var bool is merged scripts file (path-dependent) was already loaded
- */
- protected static $_is_merged_loaded = FALSE;
-
- /**
- * @var array Kohana classes to save at shutdown
- */
- protected static $_new_loaded_files = array();
-
- /**
- * @var array settings for caching merged files
- */
- public static $caching_merged = array(
- 'status' => FALSE, // is merge caching on
- 'dir' => 'merged', // directory to store merged files
- 'threshold_class' => 'Controller', // first class that will be cached in route-dependent files
- //(in other words, FIRST class that will NOT be cached in preloader file)
- // or NULL to store all project classes in preloader
- // NB! this class should be loaded after the time when Request::instance() call is possible
- 'preloader_filename' => 'preloader', // name of preloader file
- 'clear_cache_filename' => 'ccache', // if file with such a name is in merged cache dir,
- //all merged files will be deleted
- );
-
- /**
- * Replace native autoload function to allow merged scripts file loading
- *
- * @param string class name
- * @return boolean
- */
- public static function auto_load($class)
- {
- // use merged cache if allowed
- if (Kohana::$caching AND Kohana::$caching_merged['status'])
- {
-
- if ( ! self::$_is_merged_loaded)
- {
- $filepath = Kohana::cache_merged(FALSE);
-
- if ($filepath AND is_file($filepath))
- {
- require $filepath;
-
- self::$_is_merged_loaded = TRUE;
- }
-
- // if class was found, return success
- if (class_exists($class, FALSE))
- {
- return TRUE;
- }
- }
- }
- // ^ class was not loaded so make standard find file procedure
- // and store file in the memory slot
-
- // Transform the class name into a path
- $file = str_replace('_', '/', strtolower($class));
- if ($path = Kohana::find_file('classes', $file))
- {
- // Load the class file
- require $path;
-
- // if merged file cache allowed,
- // store script file path in memory slot
- if (Kohana::$caching AND Kohana::$caching_merged['status'])
- {
- self::$_new_loaded_files[$class] = $path;
- }
-
- // Class has been found
- return TRUE;
- }
- // Class is not in the filesystem
- return FALSE;
- }
-
- /**
- * Native Ko3 shutdown_handler + merged scripts writing
- *
- * @uses Kohana::exception_handler
- * @return void
- */
- public static function shutdown_handler()
- {
- if ( ! Kohana::$_init)
- {
- // Do not execute when not active
- return;
- }
- try
- {
- if (Kohana::$caching === TRUE AND Kohana::$_files_changed === TRUE)
- {
- // Write the file path cache
- Kohana::cache('Kohana::find_file()', Kohana::$_files);
-
- }
-
- // if merged file caching enabled, write cache
- if (Kohana::$caching === TRUE AND Kohana::$caching_merged['status'] === TRUE)
- {
- Kohana::cache_merged();
- }
-
- }
- catch (Exception $e)
- {
- // Pass the exception to the handler
- Kohana::exception_handler($e);
- }
- if (Kohana::$errors AND $error = error_get_last() AND (error_reporting() & $error['type']))
- {
- // If an output buffer exists, clear it
- ob_get_level() and ob_clean();
-
- // Fake an exception for nice debugging
- Kohana::exception_handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
-
- // Shutdown now to avoid a "death loop"
- exit(1);
- }
- }
-
- /**
- * merged scripts file read/write
- *
- * @throws Kohana_Exception
- * @param bool TRUE to save cache, FALSE to load it
- *
- * @return mixed for loading (path or NULL)
- * @return boolean for writing
- */
- public static function cache_merged($save_cache = TRUE)
- {
- $dir = Kohana::$cache_dir.DIRECTORY_SEPARATOR.Kohana::$caching_merged['dir'].DIRECTORY_SEPARATOR;
-
- // init
- if (self::$_is_preloader === NULL)
- {
- // delete cache?
- if (is_file($dir.Kohana::$caching_merged['clear_cache_filename']))
- {
- try
- {
- $iter = new DirectoryIterator($dir);
- foreach ($iter as $finfo)
- {
- $fname = $finfo->getFilename();
- if ($fname != '.' AND $fname != '..')
- {
- unlink($dir.$finfo->getFilename());
- }
- }
-
- } catch (Exception $e) {
- Kohana::exception_handler($e);
- }
- }
-
-
- // what type of merged cache we deal this session
- if (Kohana::$caching_merged['preloader_filename'])
- {
- $file = Kohana::$caching_merged['preloader_filename'].EXT;
-
- // preloader file exist
- if (is_file($dir.$file))
- {
- require $dir.$file;
-
- // if 'threshold_class' not specified, make big all-included merge
- if (Kohana::$caching_merged['threshold_class'])
- {
- self::$_is_preloader = FALSE;
- } else {
- self::$_is_preloader = TRUE;
- }
-
- return NULL;
-
- } else {
- // need to create file, so init preloader creation session
- self::$_is_preloader = TRUE;
-
- return NULL;
- }
- } else {
- // we do not want to merge preloader
- throw new Kohana_Exception('Preloader file name not specified!');
- }
- }
-
-
- if (self::$_is_preloader)
- {
- if ($save_cache === FALSE)
- {
- // we have loaded preloader previousely or it was missing
- // both cases nothing to load
- return NULL;
- }
-
- // compose preloader -- no merged available
- $file = Kohana::$caching_merged['preloader_filename'].EXT;
-
- } else {
-
- $request = Request::factory();
- $file = $request->directory.'.'.$request->controller.'.'.$request->action.EXT;
-
- if ($save_cache === FALSE)
- {
- if (is_file($dir.$file))
- {
- // Return the cache
- return $dir.$file;
- }
- // Cache not found
- return NULL;
- }
- }
-
- // save cache
- try
- {
- if ( ! is_dir($dir))
- {
- // Create the cache directory
- mkdir($dir, 0777, TRUE);
-
- // Set permissions (must be manually set to fix umask issues)
- chmod($dir, 0777);
- }
- if ( ! is_file($dir.$file))
- {
- file_put_contents($dir.$file, Kohana::FILE_SECURITY.PHP_EOL);
-
- chmod($dir.$file, 0666);
- }
-
- if ( ! empty(self::$_new_loaded_files) AND is_writable($dir.$file))
- {
- // classname mark
- $mark = '//----->';
-
- // to prevent concurrent cachefile composing, lock file
- $f_id = fopen($dir.$file, 'rb+');
-
- if ( ! flock($f_id, LOCK_EX))
- {
- fclose($f_id);
- return FALSE;
- }
-
- $contents = fread($f_id, filesize($dir.$file));
-
- $data = array();
- foreach (self::$_new_loaded_files as $key => $filename)
- {
- if (self::$_is_preloader)
- {
- if (Kohana::$caching_merged['threshold_class'] AND $key == Kohana::$caching_merged['threshold_class'])
- {
- break;
- }
- }
-
- // check if class was already stored
- if (stripos($contents, $mark.$key) !== FALSE)
- {
- continue;
- }
-
- $data[$key] = trim(file_get_contents($filename));
-
- if (empty($data[$key]))
- {
- throw new Kohana_Exception('Empty script file :file , nothing to store!',
- array(':file' => Kohana::debug_path(Kohana::$filename)));
- }
-
- // remove UTF BOM mark if presented
- $BOM_utf8 = "\xEF\xBB\xBF";
- $BOM_utf16BE = "\xFE\xFF";
- $BOM_utf16LE = "\xFF\xFE";
- $BOM_utf32BE = "\x00\x00\xFE\xFF";
- $BOM_utf32LE = "\xFF\xFE\x00\x00";
-
- if ($BOM_utf8 == substr($data[$key], 0, 3))
- {
- $data[$key] = substr($data[$key], 3);
- }
- elseif ($BOM_utf16BE == substr($data[$key], 0, 2)
- OR $BOM_utf16LE == substr($data[$key], 0, 2))
- {
- $data[$key] = substr($data[$key], 2);
- }
- elseif ($BOM_utf32BE == substr($data[$key], 0, 4)
- OR $BOM_utf32LE == substr($data[$key], 0, 4))
- {
- $data[$key] = substr($data[$key], 4);
- }
-
- // cut security stuff everywhere
- $data[$key] = str_replace(Kohana::FILE_SECURITY, '', $data[$key]);
-
- // cut leading and trailing php tags if they are presented
- if (strpos($data[$key], '<?php') === 0)
- {
- $data[$key] = substr_replace($data[$key], '', 0, 5);
- }
- elseif (strpos($data[$key], '<?') === 0)
- {
- $data[$key] = substr_replace($data[$key], '', 0, 2);
- }
-
- if ('?>' === substr($data[$key], -2))
- {
- $data[$key] = substr_replace($data[$key], PHP_EOL, -2);
- }
-
- $data[$key] = $mark.$key.PHP_EOL.$data[$key];
- }
-
- $dump = PHP_EOL.implode(PHP_EOL, $data);
-
- $res = (bool) fwrite($f_id, $dump);
-
- fclose($f_id);
-
- // Write the cache
- return $res;
- }
-
- return FALSE;
- }
- catch (Exception $e)
- {
- Kohana::exception_handler($e);
-
- }
- }
- }