Completed
Push — master ( e162f1...95ce61 )
by Richard
13s
created

_prepareDir()   D

Complexity

Conditions 9
Paths 9

Size

Total Lines 36
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 25
nc 9
nop 1
dl 0
loc 36
rs 4.909
c 0
b 0
f 0
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 null;
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 null;
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
            // TODO: This races
221
            if (is_dir($directory)) return true;
222
            return mkdir($directory);
223
        }
224
        if (!is_dir($directory)) {
225
            $base = $this->generateBaseDirectoryPath($config);
226
            if (!is_dir($base)) {
227
                trigger_error(
228
                    'Base directory ' . $base . ' does not exist,
229
                    please create or change using %Cache.SerializerPath',
230
                    E_USER_WARNING
231
                );
232
                return false;
233
            } elseif (!$this->_testPermissions($base, $chmod)) {
234
                return false;
235
            }
236
            if (!mkdir($directory, $chmod)) {
237
                trigger_error(
238
                    'Could not create directory ' . $directory . '',
239
                    E_USER_WARNING
240
                );
241
                return false;
242
            }
243
            if (!$this->_testPermissions($directory, $chmod)) {
244
                return false;
245
            }
246
        } elseif (!$this->_testPermissions($directory, $chmod)) {
247
            return false;
248
        }
249
        return true;
250
    }
251
252
    /**
253
     * Tests permissions on a directory and throws out friendly
254
     * error messages and attempts to chmod it itself if possible
255
     * @param string $dir Directory path
256
     * @param int $chmod Permissions
257
     * @return bool True if directory is writable
258
     */
259
    private function _testPermissions($dir, $chmod)
260
    {
261
        // early abort, if it is writable, everything is hunky-dory
262
        if (is_writable($dir)) {
263
            return true;
264
        }
265
        if (!is_dir($dir)) {
266
            // generally, you'll want to handle this beforehand
267
            // so a more specific error message can be given
268
            trigger_error(
269
                'Directory ' . $dir . ' does not exist',
270
                E_USER_WARNING
271
            );
272
            return false;
273
        }
274
        if (function_exists('posix_getuid') && $chmod !== null) {
275
            // POSIX system, we can give more specific advice
276
            if (fileowner($dir) === posix_getuid()) {
277
                // we can chmod it ourselves
278
                $chmod = $chmod | 0700;
279
                if (chmod($dir, $chmod)) {
280
                    return true;
281
                }
282
            } elseif (filegroup($dir) === posix_getgid()) {
283
                $chmod = $chmod | 0070;
284
            } else {
285
                // PHP's probably running as nobody, so we'll
286
                // need to give global permissions
287
                $chmod = $chmod | 0777;
288
            }
289
            trigger_error(
290
                'Directory ' . $dir . ' not writable, ' .
291
                'please chmod to ' . decoct($chmod),
292
                E_USER_WARNING
293
            );
294
        } else {
295
            // generic error message
296
            trigger_error(
297
                'Directory ' . $dir . ' not writable, ' .
298
                'please alter file permissions',
299
                E_USER_WARNING
300
            );
301
        }
302
        return false;
303
    }
304
}
305
306
// vim: et sw=4 sts=4
307