pquery.cache.php 4.2 KB

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