PageRenderTime 56ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/application/classes/_kohana.php

https://bitbucket.org/seyar/kinda.local
PHP | 403 lines | 221 code | 64 blank | 118 comment | 43 complexity | 6ce52b765c911f7da47b413014f1c5f9 MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * A patch to Kohana 3 Core class that automatically merges autoloaded Ko3
  4. * classes files in one or two (first is common aka 'preloader' and second is route-dependent).
  5. * Owing that classes can be loaded faster and app performance can be increased.
  6. * It will likely be more effective on multiple files PHP applications of big overall size.
  7. *
  8. * Installation:
  9. * 1. Copy that file in Kohana 3 APPPATH/classes folder (if you have already patched Kohana_Core,
  10. * manually merge this patch with your own).
  11. * 2. Put
  12. * Kohana::$caching_merged['status'] = TRUE;
  13. * and other needed settings (see below) anywhere in APPPATH/bootstrap.php (preferably closer to beginning,
  14. * but before other than Kohana class methods invokation; I put them right after Kohana::init())
  15. *
  16. * Use cases:
  17. * -> If you want to turn off the merged scripts cache, set in bootstrap:
  18. * Kohana::$caching_merged['status'] = FALSE;
  19. * -> To set your custom merged cache folder, change
  20. * Kohana::$caching_merged['dir'] = 'custom-dir-name';
  21. * -> You can change classes stored in preloader cache by varying threshold class in:
  22. * Kohana::$caching_merged['threshold_class'] = 'custom-class-name';
  23. * please insure that 'threshold_class' always is being loaded after succesful
  24. * potential Request::instance() call. Default set to "Controller", probably better universal choice now.
  25. * -> You can store all classes in one big preloader without route-dependent parts.
  26. * simply by setting
  27. * Kohana::$caching_merged['threshold_class'] = NULL;
  28. * Actually no evidence present to absolutely prefer only one kind of merging
  29. * -> To chosse custom preloader filename change
  30. * Kohana::$caching_merged['preloader_filename'] = 'custom-file-name';
  31. * -> After any code update or merged cache settings change you should delete the merged scripts files
  32. * manually or by uploading to your merge cache dir a file of name specified by
  33. * Kohana::$caching_merged['clear_cache_filename'] = 'custom-filename';
  34. * Default is 'ccache'
  35. *
  36. * @author Alexander Kupreyeu (mailto:alexander.kupreev@gmail.com, http://kupreev.com)
  37. * @author Kohana Team
  38. * @link http://github.com/kohana/core/
  39. *
  40. * @version 0.2 (not recommended for production use, only testing and bug reporting)
  41. *
  42. */
  43. class Kohana extends Kohana_Core {
  44. /**
  45. * @var mixed do we compose preloader during this session?
  46. * TRUE for preloader, FALSE for path-dependent, NULL for init state
  47. */
  48. protected static $_is_preloader = NULL;
  49. /**
  50. * @var bool is merged scripts file (path-dependent) was already loaded
  51. */
  52. protected static $_is_merged_loaded = FALSE;
  53. /**
  54. * @var array Kohana classes to save at shutdown
  55. */
  56. protected static $_new_loaded_files = array();
  57. /**
  58. * @var array settings for caching merged files
  59. */
  60. public static $caching_merged = array(
  61. 'status' => FALSE, // is merge caching on
  62. 'dir' => 'merged', // directory to store merged files
  63. 'threshold_class' => 'Controller', // first class that will be cached in route-dependent files
  64. //(in other words, FIRST class that will NOT be cached in preloader file)
  65. // or NULL to store all project classes in preloader
  66. // NB! this class should be loaded after the time when Request::instance() call is possible
  67. 'preloader_filename' => 'preloader', // name of preloader file
  68. 'clear_cache_filename' => 'ccache', // if file with such a name is in merged cache dir,
  69. //all merged files will be deleted
  70. );
  71. /**
  72. * Replace native autoload function to allow merged scripts file loading
  73. *
  74. * @param string class name
  75. * @return boolean
  76. */
  77. public static function auto_load($class)
  78. {
  79. // use merged cache if allowed
  80. if (Kohana::$caching AND Kohana::$caching_merged['status'])
  81. {
  82. if ( ! self::$_is_merged_loaded)
  83. {
  84. $filepath = Kohana::cache_merged(FALSE);
  85. if ($filepath AND is_file($filepath))
  86. {
  87. require $filepath;
  88. self::$_is_merged_loaded = TRUE;
  89. }
  90. // if class was found, return success
  91. if (class_exists($class, FALSE))
  92. {
  93. return TRUE;
  94. }
  95. }
  96. }
  97. // ^ class was not loaded so make standard find file procedure
  98. // and store file in the memory slot
  99. // Transform the class name into a path
  100. $file = str_replace('_', '/', strtolower($class));
  101. if ($path = Kohana::find_file('classes', $file))
  102. {
  103. // Load the class file
  104. require $path;
  105. // if merged file cache allowed,
  106. // store script file path in memory slot
  107. if (Kohana::$caching AND Kohana::$caching_merged['status'])
  108. {
  109. self::$_new_loaded_files[$class] = $path;
  110. }
  111. // Class has been found
  112. return TRUE;
  113. }
  114. // Class is not in the filesystem
  115. return FALSE;
  116. }
  117. /**
  118. * Native Ko3 shutdown_handler + merged scripts writing
  119. *
  120. * @uses Kohana::exception_handler
  121. * @return void
  122. */
  123. public static function shutdown_handler()
  124. {
  125. if ( ! Kohana::$_init)
  126. {
  127. // Do not execute when not active
  128. return;
  129. }
  130. try
  131. {
  132. if (Kohana::$caching === TRUE AND Kohana::$_files_changed === TRUE)
  133. {
  134. // Write the file path cache
  135. Kohana::cache('Kohana::find_file()', Kohana::$_files);
  136. }
  137. // if merged file caching enabled, write cache
  138. if (Kohana::$caching === TRUE AND Kohana::$caching_merged['status'] === TRUE)
  139. {
  140. Kohana::cache_merged();
  141. }
  142. }
  143. catch (Exception $e)
  144. {
  145. // Pass the exception to the handler
  146. Kohana::exception_handler($e);
  147. }
  148. if (Kohana::$errors AND $error = error_get_last() AND (error_reporting() & $error['type']))
  149. {
  150. // If an output buffer exists, clear it
  151. ob_get_level() and ob_clean();
  152. // Fake an exception for nice debugging
  153. Kohana::exception_handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
  154. // Shutdown now to avoid a "death loop"
  155. exit(1);
  156. }
  157. }
  158. /**
  159. * merged scripts file read/write
  160. *
  161. * @throws Kohana_Exception
  162. * @param bool TRUE to save cache, FALSE to load it
  163. *
  164. * @return mixed for loading (path or NULL)
  165. * @return boolean for writing
  166. */
  167. public static function cache_merged($save_cache = TRUE)
  168. {
  169. $dir = Kohana::$cache_dir.DIRECTORY_SEPARATOR.Kohana::$caching_merged['dir'].DIRECTORY_SEPARATOR;
  170. // init
  171. if (self::$_is_preloader === NULL)
  172. {
  173. // delete cache?
  174. if (is_file($dir.Kohana::$caching_merged['clear_cache_filename']))
  175. {
  176. try
  177. {
  178. $iter = new DirectoryIterator($dir);
  179. foreach ($iter as $finfo)
  180. {
  181. $fname = $finfo->getFilename();
  182. if ($fname != '.' AND $fname != '..')
  183. {
  184. unlink($dir.$finfo->getFilename());
  185. }
  186. }
  187. } catch (Exception $e) {
  188. Kohana::exception_handler($e);
  189. }
  190. }
  191. // what type of merged cache we deal this session
  192. if (Kohana::$caching_merged['preloader_filename'])
  193. {
  194. $file = Kohana::$caching_merged['preloader_filename'].EXT;
  195. // preloader file exist
  196. if (is_file($dir.$file))
  197. {
  198. require $dir.$file;
  199. // if 'threshold_class' not specified, make big all-included merge
  200. if (Kohana::$caching_merged['threshold_class'])
  201. {
  202. self::$_is_preloader = FALSE;
  203. } else {
  204. self::$_is_preloader = TRUE;
  205. }
  206. return NULL;
  207. } else {
  208. // need to create file, so init preloader creation session
  209. self::$_is_preloader = TRUE;
  210. return NULL;
  211. }
  212. } else {
  213. // we do not want to merge preloader
  214. throw new Kohana_Exception('Preloader file name not specified!');
  215. }
  216. }
  217. if (self::$_is_preloader)
  218. {
  219. if ($save_cache === FALSE)
  220. {
  221. // we have loaded preloader previousely or it was missing
  222. // both cases nothing to load
  223. return NULL;
  224. }
  225. // compose preloader -- no merged available
  226. $file = Kohana::$caching_merged['preloader_filename'].EXT;
  227. } else {
  228. $request = Request::factory();
  229. $file = $request->directory.'.'.$request->controller.'.'.$request->action.EXT;
  230. if ($save_cache === FALSE)
  231. {
  232. if (is_file($dir.$file))
  233. {
  234. // Return the cache
  235. return $dir.$file;
  236. }
  237. // Cache not found
  238. return NULL;
  239. }
  240. }
  241. // save cache
  242. try
  243. {
  244. if ( ! is_dir($dir))
  245. {
  246. // Create the cache directory
  247. mkdir($dir, 0777, TRUE);
  248. // Set permissions (must be manually set to fix umask issues)
  249. chmod($dir, 0777);
  250. }
  251. if ( ! is_file($dir.$file))
  252. {
  253. file_put_contents($dir.$file, Kohana::FILE_SECURITY.PHP_EOL);
  254. chmod($dir.$file, 0666);
  255. }
  256. if ( ! empty(self::$_new_loaded_files) AND is_writable($dir.$file))
  257. {
  258. // classname mark
  259. $mark = '//----->';
  260. // to prevent concurrent cachefile composing, lock file
  261. $f_id = fopen($dir.$file, 'rb+');
  262. if ( ! flock($f_id, LOCK_EX))
  263. {
  264. fclose($f_id);
  265. return FALSE;
  266. }
  267. $contents = fread($f_id, filesize($dir.$file));
  268. $data = array();
  269. foreach (self::$_new_loaded_files as $key => $filename)
  270. {
  271. if (self::$_is_preloader)
  272. {
  273. if (Kohana::$caching_merged['threshold_class'] AND $key == Kohana::$caching_merged['threshold_class'])
  274. {
  275. break;
  276. }
  277. }
  278. // check if class was already stored
  279. if (stripos($contents, $mark.$key) !== FALSE)
  280. {
  281. continue;
  282. }
  283. $data[$key] = trim(file_get_contents($filename));
  284. if (empty($data[$key]))
  285. {
  286. throw new Kohana_Exception('Empty script file :file , nothing to store!',
  287. array(':file' => Kohana::debug_path(Kohana::$filename)));
  288. }
  289. // remove UTF BOM mark if presented
  290. $BOM_utf8 = "\xEF\xBB\xBF";
  291. $BOM_utf16BE = "\xFE\xFF";
  292. $BOM_utf16LE = "\xFF\xFE";
  293. $BOM_utf32BE = "\x00\x00\xFE\xFF";
  294. $BOM_utf32LE = "\xFF\xFE\x00\x00";
  295. if ($BOM_utf8 == substr($data[$key], 0, 3))
  296. {
  297. $data[$key] = substr($data[$key], 3);
  298. }
  299. elseif ($BOM_utf16BE == substr($data[$key], 0, 2)
  300. OR $BOM_utf16LE == substr($data[$key], 0, 2))
  301. {
  302. $data[$key] = substr($data[$key], 2);
  303. }
  304. elseif ($BOM_utf32BE == substr($data[$key], 0, 4)
  305. OR $BOM_utf32LE == substr($data[$key], 0, 4))
  306. {
  307. $data[$key] = substr($data[$key], 4);
  308. }
  309. // cut security stuff everywhere
  310. $data[$key] = str_replace(Kohana::FILE_SECURITY, '', $data[$key]);
  311. // cut leading and trailing php tags if they are presented
  312. if (strpos($data[$key], '<?php') === 0)
  313. {
  314. $data[$key] = substr_replace($data[$key], '', 0, 5);
  315. }
  316. elseif (strpos($data[$key], '<?') === 0)
  317. {
  318. $data[$key] = substr_replace($data[$key], '', 0, 2);
  319. }
  320. if ('?>' === substr($data[$key], -2))
  321. {
  322. $data[$key] = substr_replace($data[$key], PHP_EOL, -2);
  323. }
  324. $data[$key] = $mark.$key.PHP_EOL.$data[$key];
  325. }
  326. $dump = PHP_EOL.implode(PHP_EOL, $data);
  327. $res = (bool) fwrite($f_id, $dump);
  328. fclose($f_id);
  329. // Write the cache
  330. return $res;
  331. }
  332. return FALSE;
  333. }
  334. catch (Exception $e)
  335. {
  336. Kohana::exception_handler($e);
  337. }
  338. }
  339. }