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