Serializer.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. <?php
  2. class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCache
  3. {
  4. /**
  5. * @param HTMLPurifier_Definition $def
  6. * @param HTMLPurifier_Config $config
  7. * @return int|bool
  8. */
  9. public function add($def, $config)
  10. {
  11. if (!$this->checkDefType($def)) {
  12. return;
  13. }
  14. $file = $this->generateFilePath($config);
  15. if (file_exists($file)) {
  16. return false;
  17. }
  18. if (!$this->_prepareDir($config)) {
  19. return false;
  20. }
  21. return $this->_write($file, serialize($def), $config);
  22. }
  23. /**
  24. * @param HTMLPurifier_Definition $def
  25. * @param HTMLPurifier_Config $config
  26. * @return int|bool
  27. */
  28. public function set($def, $config)
  29. {
  30. if (!$this->checkDefType($def)) {
  31. return;
  32. }
  33. $file = $this->generateFilePath($config);
  34. if (!$this->_prepareDir($config)) {
  35. return false;
  36. }
  37. return $this->_write($file, serialize($def), $config);
  38. }
  39. /**
  40. * @param HTMLPurifier_Definition $def
  41. * @param HTMLPurifier_Config $config
  42. * @return int|bool
  43. */
  44. public function replace($def, $config)
  45. {
  46. if (!$this->checkDefType($def)) {
  47. return;
  48. }
  49. $file = $this->generateFilePath($config);
  50. if (!file_exists($file)) {
  51. return false;
  52. }
  53. if (!$this->_prepareDir($config)) {
  54. return false;
  55. }
  56. return $this->_write($file, serialize($def), $config);
  57. }
  58. /**
  59. * @param HTMLPurifier_Config $config
  60. * @return bool|HTMLPurifier_Config
  61. */
  62. public function get($config)
  63. {
  64. $file = $this->generateFilePath($config);
  65. if (!file_exists($file)) {
  66. return false;
  67. }
  68. return unserialize(file_get_contents($file));
  69. }
  70. /**
  71. * @param HTMLPurifier_Config $config
  72. * @return bool
  73. */
  74. public function remove($config)
  75. {
  76. $file = $this->generateFilePath($config);
  77. if (!file_exists($file)) {
  78. return false;
  79. }
  80. return unlink($file);
  81. }
  82. /**
  83. * @param HTMLPurifier_Config $config
  84. * @return bool
  85. */
  86. public function flush($config)
  87. {
  88. if (!$this->_prepareDir($config)) {
  89. return false;
  90. }
  91. $dir = $this->generateDirectoryPath($config);
  92. $dh = opendir($dir);
  93. // Apparently, on some versions of PHP, readdir will return
  94. // an empty string if you pass an invalid argument to readdir.
  95. // So you need this test. See #49.
  96. if (false === $dh) {
  97. return false;
  98. }
  99. while (false !== ($filename = readdir($dh))) {
  100. if (empty($filename)) {
  101. continue;
  102. }
  103. if ($filename[0] === '.') {
  104. continue;
  105. }
  106. unlink($dir . '/' . $filename);
  107. }
  108. closedir($dh);
  109. return true;
  110. }
  111. /**
  112. * @param HTMLPurifier_Config $config
  113. * @return bool
  114. */
  115. public function cleanup($config)
  116. {
  117. if (!$this->_prepareDir($config)) {
  118. return false;
  119. }
  120. $dir = $this->generateDirectoryPath($config);
  121. $dh = opendir($dir);
  122. // See #49 (and above).
  123. if (false === $dh) {
  124. return false;
  125. }
  126. while (false !== ($filename = readdir($dh))) {
  127. if (empty($filename)) {
  128. continue;
  129. }
  130. if ($filename[0] === '.') {
  131. continue;
  132. }
  133. $key = substr($filename, 0, strlen($filename) - 4);
  134. if ($this->isOld($key, $config)) {
  135. unlink($dir . '/' . $filename);
  136. }
  137. }
  138. closedir($dh);
  139. return true;
  140. }
  141. /**
  142. * Generates the file path to the serial file corresponding to
  143. * the configuration and definition name
  144. * @param HTMLPurifier_Config $config
  145. * @return string
  146. * @todo Make protected
  147. */
  148. public function generateFilePath($config)
  149. {
  150. $key = $this->generateKey($config);
  151. return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
  152. }
  153. /**
  154. * Generates the path to the directory contain this cache's serial files
  155. * @param HTMLPurifier_Config $config
  156. * @return string
  157. * @note No trailing slash
  158. * @todo Make protected
  159. */
  160. public function generateDirectoryPath($config)
  161. {
  162. $base = $this->generateBaseDirectoryPath($config);
  163. return $base . '/' . $this->type;
  164. }
  165. /**
  166. * Generates path to base directory that contains all definition type
  167. * serials
  168. * @param HTMLPurifier_Config $config
  169. * @return mixed|string
  170. * @todo Make protected
  171. */
  172. public function generateBaseDirectoryPath($config)
  173. {
  174. $base = $config->get('Cache.SerializerPath');
  175. $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
  176. return $base;
  177. }
  178. /**
  179. * Convenience wrapper function for file_put_contents
  180. * @param string $file File name to write to
  181. * @param string $data Data to write into file
  182. * @param HTMLPurifier_Config $config
  183. * @return int|bool Number of bytes written if success, or false if failure.
  184. */
  185. private function _write($file, $data, $config)
  186. {
  187. $result = file_put_contents($file, $data);
  188. if ($result !== false) {
  189. // set permissions of the new file (no execute)
  190. $chmod = $config->get('Cache.SerializerPermissions');
  191. if ($chmod !== null) {
  192. chmod($file, $chmod & 0666);
  193. }
  194. }
  195. return $result;
  196. }
  197. /**
  198. * Prepares the directory that this type stores the serials in
  199. * @param HTMLPurifier_Config $config
  200. * @return bool True if successful
  201. */
  202. private function _prepareDir($config)
  203. {
  204. $directory = $this->generateDirectoryPath($config);
  205. $chmod = $config->get('Cache.SerializerPermissions');
  206. if ($chmod === null) {
  207. if (!@mkdir($directory) && !is_dir($directory)) {
  208. trigger_error(
  209. 'Could not create directory ' . $directory . '',
  210. E_USER_WARNING
  211. );
  212. return false;
  213. }
  214. return true;
  215. }
  216. if (!is_dir($directory)) {
  217. $base = $this->generateBaseDirectoryPath($config);
  218. if (!is_dir($base)) {
  219. trigger_error(
  220. 'Base directory ' . $base . ' does not exist,
  221. please create or change using %Cache.SerializerPath',
  222. E_USER_WARNING
  223. );
  224. return false;
  225. } elseif (!$this->_testPermissions($base, $chmod)) {
  226. return false;
  227. }
  228. if (!@mkdir($directory, $chmod) && !is_dir($directory)) {
  229. trigger_error(
  230. 'Could not create directory ' . $directory . '',
  231. E_USER_WARNING
  232. );
  233. return false;
  234. }
  235. if (!$this->_testPermissions($directory, $chmod)) {
  236. return false;
  237. }
  238. } elseif (!$this->_testPermissions($directory, $chmod)) {
  239. return false;
  240. }
  241. return true;
  242. }
  243. /**
  244. * Tests permissions on a directory and throws out friendly
  245. * error messages and attempts to chmod it itself if possible
  246. * @param string $dir Directory path
  247. * @param int $chmod Permissions
  248. * @return bool True if directory is writable
  249. */
  250. private function _testPermissions($dir, $chmod)
  251. {
  252. // early abort, if it is writable, everything is hunky-dory
  253. if (is_writable($dir)) {
  254. return true;
  255. }
  256. if (!is_dir($dir)) {
  257. // generally, you'll want to handle this beforehand
  258. // so a more specific error message can be given
  259. trigger_error(
  260. 'Directory ' . $dir . ' does not exist',
  261. E_USER_WARNING
  262. );
  263. return false;
  264. }
  265. if (function_exists('posix_getuid') && $chmod !== null) {
  266. // POSIX system, we can give more specific advice
  267. if (fileowner($dir) === posix_getuid()) {
  268. // we can chmod it ourselves
  269. $chmod = $chmod | 0700;
  270. if (chmod($dir, $chmod)) {
  271. return true;
  272. }
  273. } elseif (filegroup($dir) === posix_getgid()) {
  274. $chmod = $chmod | 0070;
  275. } else {
  276. // PHP's probably running as nobody, so we'll
  277. // need to give global permissions
  278. $chmod = $chmod | 0777;
  279. }
  280. trigger_error(
  281. 'Directory ' . $dir . ' not writable, ' .
  282. 'please chmod to ' . decoct($chmod),
  283. E_USER_WARNING
  284. );
  285. } else {
  286. // generic error message
  287. trigger_error(
  288. 'Directory ' . $dir . ' not writable, ' .
  289. 'please alter file permissions',
  290. E_USER_WARNING
  291. );
  292. }
  293. return false;
  294. }
  295. }
  296. // vim: et sw=4 sts=4