pquery.cache.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php
  2. /**
  3. * pQuery plugin for cache-control of a list of files.
  4. *
  5. * @package pQuery
  6. */
  7. /**
  8. * pQuery extension class for the 'cache' plugin.
  9. *
  10. * @property string $files The list of files in the cached object.
  11. * @todo Method documentation
  12. */
  13. class pQueryCache extends pQuery {
  14. const CACHE_FOLDER = 'cache/';
  15. const ADMINISTRATION_FILE = 'administration.php';
  16. static $accepts = array('array' => 'get_modification_dates', 'string' => 'make_array');
  17. /**
  18. * @see pQuery::$variable_alias
  19. * @var string|array
  20. */
  21. static $variable_alias = 'files';
  22. /**
  23. * A list of latest known modification timestamps of all files currently in the cache.
  24. *
  25. * @var array
  26. */
  27. static $admin;
  28. /**
  29. * A list of actual modification timestamps of the current file list.
  30. *
  31. * @var array
  32. */
  33. var $modification_dates;
  34. /**
  35. * Reduced script content.
  36. *
  37. * @var string
  38. */
  39. var $content = '';
  40. /**
  41. * Make a single file into an array.
  42. *
  43. * @param string $file The file to put in an array.
  44. */
  45. function make_array($file) {
  46. return $this->get_modification_dates(array($file));
  47. }
  48. /**
  49. *
  50. */
  51. function get_modification_dates($files) {
  52. // Assert existence of all files
  53. foreach( $files as $file )
  54. file_exists($file) || self::error('File "%s" does not exist.', $file);
  55. $timestamps = array_map('filemtime', $files);
  56. $this->modification_dates = array_combine($files, $timestamps);
  57. return $files;
  58. }
  59. /**
  60. *
  61. *
  62. * @returns bool Whether the file list is in the cache and not updated.
  63. */
  64. function admin_updated() {
  65. self::assert_admin_exists();
  66. foreach( $this->modification_dates as $file => $timestamp )
  67. if( !isset(self::$admin[$file]) || self::$admin[$file] !== $timestamp )
  68. return true;
  69. return false;
  70. }
  71. /**
  72. *
  73. */
  74. function concatenate() {
  75. $this->content = implode("\n", array_map('file_get_contents', $this->files));
  76. return $this;
  77. }
  78. /**
  79. *
  80. */
  81. function filename() {
  82. return str_replace('/', '-', implode('-', $this->files));
  83. }
  84. /**
  85. *
  86. */
  87. function output() {
  88. $last_modified = max($this->modification_dates);
  89. header('Last-Modified: '.date('r', $last_modified));
  90. header('Expires: '.date('r', $last_modified + 60 * 60 * 24 * 365));
  91. header('Cache-Control: private');
  92. method_exists($this, 'set_headers') && $this->set_headers();
  93. if( ($admin_updated = $this->admin_updated()) || !isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ) {
  94. $this->save();
  95. } elseif( isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ) {
  96. $if_modified_since = strtotime(preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE']));
  97. if( $if_modified_since >= $last_modified ) {
  98. // Not modified
  99. header((php_sapi_name() == 'CGI' ? 'Status:' : 'HTTP/1.0').' 304 Not Modified');
  100. exit;
  101. }
  102. }
  103. die($this->content);
  104. }
  105. /**
  106. *
  107. */
  108. function save() {
  109. $this->concatenate();
  110. self::assert_cache_folder_exists();
  111. method_exists($this, 'minify') && $this->minify();
  112. file_put_contents(self::CACHE_FOLDER.$this->filename(), $this->content);
  113. self::$admin = array_merge(self::$admin, $this->modification_dates);
  114. self::save_administration();
  115. }
  116. /**
  117. *
  118. */
  119. static function save_administration() {
  120. $handle = fopen(self::CACHE_FOLDER.self::ADMINISTRATION_FILE, 'w');
  121. fwrite($handle, "<?php\n\npQueryCache::\$admin = array(\n");
  122. foreach( self::$admin as $file => $timestamp )
  123. fwrite($handle, "\t'$file' => $timestamp,\n");
  124. fwrite($handle, ");\n\n?>");
  125. fclose($handle);
  126. }
  127. /**
  128. * Assert existence of the administration list by including the administration
  129. * file if it exists, and assigning an empty array otherwise.
  130. */
  131. static function assert_admin_exists() {
  132. if( self::$admin !== null )
  133. return;
  134. $path = self::CACHE_FOLDER.self::ADMINISTRATION_FILE;
  135. if( file_exists($path) )
  136. include_once $path;
  137. else
  138. self::$admin = array();
  139. }
  140. /**
  141. * Assert existence of the cache folder.
  142. */
  143. static function assert_cache_folder_exists() {
  144. is_dir(self::CACHE_FOLDER) || mkdir(self::CACHE_FOLDER, 0777, true);
  145. }
  146. }
  147. /**
  148. * Shortcut constructor for {@link pQueryCache}.
  149. *
  150. * @param array|string $files
  151. * @returns pQueryCache A new cache instance.
  152. */
  153. function _cache($scripts) {
  154. return pQuery::create('cache', $scripts);
  155. }
  156. /*
  157. * Add plugin to pQuery
  158. */
  159. __p::extend('pQueryCache', 'cache');
  160. ?>