PluginProbe ʕ •ᴥ•ʔ
LiteSpeed Cache / 7.6.1
LiteSpeed Cache v7.6.1
trunk 1.0.15 1.9.1.1 2.9.9.2 3.6.4 4.6 5.7.0.1 6.5.4 7.0.0.1 7.0.1 7.1 7.2 7.3 7.3.0.1 7.4 7.5 7.5.0.1 7.6 7.6.1 7.6.2 7.7 7.8 7.8.0.1 7.8.1
litespeed-cache / src / file.cls.php
litespeed-cache / src Last commit date
cdn 7 months ago data_structure 7 months ago activation.cls.php 7 months ago admin-display.cls.php 7 months ago admin-settings.cls.php 7 months ago admin.cls.php 7 months ago api.cls.php 7 months ago avatar.cls.php 7 months ago base.cls.php 7 months ago cdn.cls.php 7 months ago cloud.cls.php 7 months ago conf.cls.php 7 months ago control.cls.php 7 months ago core.cls.php 7 months ago crawler-map.cls.php 7 months ago crawler.cls.php 7 months ago css.cls.php 7 months ago data.cls.php 7 months ago data.upgrade.func.php 7 months ago db-optm.cls.php 7 months ago debug2.cls.php 7 months ago doc.cls.php 7 months ago error.cls.php 7 months ago esi.cls.php 7 months ago file.cls.php 7 months ago gui.cls.php 7 months ago health.cls.php 7 months ago htaccess.cls.php 7 months ago img-optm.cls.php 7 months ago import.cls.php 7 months ago import.preset.cls.php 7 months ago lang.cls.php 7 months ago localization.cls.php 7 months ago media.cls.php 7 months ago metabox.cls.php 7 months ago object-cache-wp.cls.php 7 months ago object-cache.cls.php 7 months ago object.lib.php 7 months ago optimize.cls.php 7 months ago optimizer.cls.php 7 months ago placeholder.cls.php 7 months ago purge.cls.php 7 months ago report.cls.php 7 months ago rest.cls.php 7 months ago root.cls.php 7 months ago router.cls.php 7 months ago str.cls.php 7 months ago tag.cls.php 7 months ago task.cls.php 7 months ago tool.cls.php 7 months ago ucss.cls.php 7 months ago utility.cls.php 7 months ago vary.cls.php 7 months ago vpi.cls.php 7 months ago
file.cls.php
421 lines
1 <?php
2 // phpcs:ignoreFile
3
4 /**
5 * LiteSpeed File Operator Library Class
6 * Append/Replace content to a file
7 *
8 * @since 1.1.0
9 */
10
11 namespace LiteSpeed;
12
13 defined('WPINC') || exit();
14
15 class File {
16
17 const MARKER = 'LiteSpeed Operator';
18
19 /**
20 * Detect if an URL is 404
21 *
22 * @since 3.3
23 */
24 public static function is_404( $url ) {
25 $response = wp_safe_remote_get($url);
26 $code = wp_remote_retrieve_response_code($response);
27 if ($code == 404) {
28 return true;
29 }
30
31 return false;
32 }
33
34 /**
35 * Delete folder
36 *
37 * @since 2.1
38 */
39 public static function rrmdir( $dir ) {
40 $files = array_diff(scandir($dir), array( '.', '..' ));
41
42 foreach ($files as $file) {
43 is_dir("$dir/$file") ? self::rrmdir("$dir/$file") : unlink("$dir/$file");
44 }
45
46 return rmdir($dir);
47 }
48
49 public static function count_lines( $filename ) {
50 if (!file_exists($filename)) {
51 return 0;
52 }
53
54 $file = new \SplFileObject($filename);
55 $file->seek(PHP_INT_MAX);
56 return $file->key() + 1;
57 }
58
59 /**
60 * Read data from file
61 *
62 * @since 1.1.0
63 * @param string $filename
64 * @param int $start_line
65 * @param int $lines
66 */
67 public static function read( $filename, $start_line = null, $lines = null ) {
68 if (!file_exists($filename)) {
69 return '';
70 }
71
72 if (!is_readable($filename)) {
73 return false;
74 }
75
76 if ($start_line !== null) {
77 $res = array();
78 $file = new \SplFileObject($filename);
79 $file->seek($start_line);
80
81 if ($lines === null) {
82 while (!$file->eof()) {
83 $res[] = rtrim($file->current(), "\n");
84 $file->next();
85 }
86 } else {
87 for ($i = 0; $i < $lines; $i++) {
88 if ($file->eof()) {
89 break;
90 }
91 $res[] = rtrim($file->current(), "\n");
92 $file->next();
93 }
94 }
95
96 unset($file);
97 return $res;
98 }
99
100 $content = file_get_contents($filename);
101
102 $content = self::remove_zero_space($content);
103
104 return $content;
105 }
106
107 /**
108 * Append data to file
109 *
110 * @since 1.1.5
111 * @access public
112 * @param string $filename
113 * @param string $data
114 * @param boolean $mkdir
115 * @param boolean $silence Used to avoid WP's functions are used
116 */
117 public static function append( $filename, $data, $mkdir = false, $silence = true ) {
118 return self::save($filename, $data, $mkdir, true, $silence);
119 }
120
121 /**
122 * Save data to file
123 *
124 * @since 1.1.0
125 * @param string $filename
126 * @param string $data
127 * @param boolean $mkdir
128 * @param boolean $append If the content needs to be appended
129 * @param boolean $silence Used to avoid WP's functions are used
130 */
131 public static function save( $filename, $data, $mkdir = false, $append = false, $silence = true ) {
132 if (is_null($filename)) {
133 return $silence ? false : __('Filename is empty!', 'litespeed-cache');
134 }
135
136 $error = false;
137 $folder = dirname($filename);
138
139 // mkdir if folder does not exist
140 if (!file_exists($folder)) {
141 if (!$mkdir) {
142 return $silence ? false : sprintf(__('Folder does not exist: %s', 'litespeed-cache'), $folder);
143 }
144
145 set_error_handler('litespeed_exception_handler');
146
147 try {
148 mkdir($folder, 0755, true);
149 // Create robots.txt file to forbid search engine indexes
150 if (!file_exists(LITESPEED_STATIC_DIR . '/robots.txt')) {
151 file_put_contents(LITESPEED_STATIC_DIR . '/robots.txt', "User-agent: *\nDisallow: /\n");
152 }
153 } catch (\ErrorException $ex) {
154 return $silence ? false : sprintf(__('Can not create folder: %1$s. Error: %2$s', 'litespeed-cache'), $folder, $ex->getMessage());
155 }
156
157 restore_error_handler();
158 }
159
160 if (!file_exists($filename)) {
161 if (!is_writable($folder)) {
162 return $silence ? false : sprintf(__('Folder is not writable: %s.', 'litespeed-cache'), $folder);
163 }
164 set_error_handler('litespeed_exception_handler');
165 try {
166 touch($filename);
167 } catch (\ErrorException $ex) {
168 return $silence ? false : sprintf(__('File %s is not writable.', 'litespeed-cache'), $filename);
169 }
170 restore_error_handler();
171 } elseif (!is_writable($filename)) {
172 return $silence ? false : sprintf(__('File %s is not writable.', 'litespeed-cache'), $filename);
173 }
174
175 $data = self::remove_zero_space($data);
176
177 $ret = file_put_contents($filename, $data, $append ? FILE_APPEND : LOCK_EX);
178 if ($ret === false) {
179 return $silence ? false : sprintf(__('Failed to write to %s.', 'litespeed-cache'), $filename);
180 }
181
182 return true;
183 }
184
185 /**
186 * Remove Unicode zero-width space <200b><200c>
187 *
188 * @since 2.1.2
189 * @since 2.9 changed to public
190 */
191 public static function remove_zero_space( $content ) {
192 if (is_array($content)) {
193 $content = array_map(__CLASS__ . '::remove_zero_space', $content);
194 return $content;
195 }
196
197 // Remove UTF-8 BOM if present
198 if (substr($content, 0, 3) === "\xEF\xBB\xBF") {
199 $content = substr($content, 3);
200 }
201
202 $content = str_replace("\xe2\x80\x8b", '', $content);
203 $content = str_replace("\xe2\x80\x8c", '', $content);
204 $content = str_replace("\xe2\x80\x8d", '', $content);
205
206 return $content;
207 }
208
209 /**
210 * Appends an array of strings into a file (.htaccess ), placing it between
211 * BEGIN and END markers.
212 *
213 * Replaces existing marked info. Retains surrounding
214 * data. Creates file if none exists.
215 *
216 * @param string $filename Filename to alter.
217 * @param string $marker The marker to alter.
218 * @param array|string|false $insertion The new content to insert.
219 * @param bool $prepend Prepend insertion if not exist.
220 * @return bool True on write success, false on failure.
221 */
222 public static function insert_with_markers( $filename, $insertion = false, $marker = false, $prepend = false ) {
223 if (!$marker) {
224 $marker = self::MARKER;
225 }
226
227 if (!$insertion) {
228 $insertion = array();
229 }
230
231 return self::_insert_with_markers($filename, $marker, $insertion, $prepend); // todo: capture exceptions
232 }
233
234 /**
235 * Return wrapped block data with marker
236 *
237 * @param string $insertion
238 * @param string $marker
239 * @return string The block data
240 */
241 public static function wrap_marker_data( $insertion, $marker = false ) {
242 if (!$marker) {
243 $marker = self::MARKER;
244 }
245 $start_marker = "# BEGIN {$marker}";
246 $end_marker = "# END {$marker}";
247
248 $new_data = implode("\n", array_merge(array( $start_marker ), $insertion, array( $end_marker )));
249 return $new_data;
250 }
251
252 /**
253 * Touch block data from file, return with marker
254 *
255 * @param string $filename
256 * @param string $marker
257 * @return string The current block data
258 */
259 public static function touch_marker_data( $filename, $marker = false ) {
260 if (!$marker) {
261 $marker = self::MARKER;
262 }
263
264 $result = self::_extract_from_markers($filename, $marker);
265
266 if (!$result) {
267 return false;
268 }
269
270 $start_marker = "# BEGIN {$marker}";
271 $end_marker = "# END {$marker}";
272 $new_data = implode("\n", array_merge(array( $start_marker ), $result, array( $end_marker )));
273 return $new_data;
274 }
275
276 /**
277 * Extracts strings from between the BEGIN and END markers in the .htaccess file.
278 *
279 * @param string $filename
280 * @param string $marker
281 * @return array An array of strings from a file (.htaccess ) from between BEGIN and END markers.
282 */
283 public static function extract_from_markers( $filename, $marker = false ) {
284 if (!$marker) {
285 $marker = self::MARKER;
286 }
287 return self::_extract_from_markers($filename, $marker);
288 }
289
290 /**
291 * Extracts strings from between the BEGIN and END markers in the .htaccess file.
292 *
293 * @param string $filename
294 * @param string $marker
295 * @return array An array of strings from a file (.htaccess ) from between BEGIN and END markers.
296 */
297 private static function _extract_from_markers( $filename, $marker ) {
298 $result = array();
299
300 if (!file_exists($filename)) {
301 return $result;
302 }
303
304 if ($markerdata = explode("\n", implode('', file($filename)))) {
305 $state = false;
306 foreach ($markerdata as $markerline) {
307 if (strpos($markerline, '# END ' . $marker) !== false) {
308 $state = false;
309 }
310 if ($state) {
311 $result[] = $markerline;
312 }
313 if (strpos($markerline, '# BEGIN ' . $marker) !== false) {
314 $state = true;
315 }
316 }
317 }
318
319 return array_map('trim', $result);
320 }
321
322 /**
323 * Inserts an array of strings into a file (.htaccess ), placing it between BEGIN and END markers.
324 *
325 * Replaces existing marked info. Retains surrounding data. Creates file if none exists.
326 *
327 * NOTE: will throw error if failed
328 *
329 * @since 3.0-
330 * @since 3.0 Throw errors if failed
331 * @access private
332 */
333 private static function _insert_with_markers( $filename, $marker, $insertion, $prepend = false ) {
334 if (!file_exists($filename)) {
335 if (!is_writable(dirname($filename))) {
336 Error::t('W', dirname($filename));
337 }
338
339 set_error_handler('litespeed_exception_handler');
340 try {
341 touch($filename);
342 } catch (\ErrorException $ex) {
343 Error::t('W', $filename);
344 }
345 restore_error_handler();
346 } elseif (!is_writable($filename)) {
347 Error::t('W', $filename);
348 }
349
350 if (!is_array($insertion)) {
351 $insertion = explode("\n", $insertion);
352 }
353
354 $start_marker = "# BEGIN {$marker}";
355 $end_marker = "# END {$marker}";
356
357 $fp = fopen($filename, 'r+');
358 if (!$fp) {
359 Error::t('W', $filename);
360 }
361
362 // Attempt to get a lock. If the filesystem supports locking, this will block until the lock is acquired.
363 flock($fp, LOCK_EX);
364
365 $lines = array();
366 while (!feof($fp)) {
367 $lines[] = rtrim(fgets($fp), "\r\n");
368 }
369
370 // Split out the existing file into the preceding lines, and those that appear after the marker
371 $pre_lines = $post_lines = $existing_lines = array();
372 $found_marker = $found_end_marker = false;
373 foreach ($lines as $line) {
374 if (!$found_marker && false !== strpos($line, $start_marker)) {
375 $found_marker = true;
376 continue;
377 } elseif (!$found_end_marker && false !== strpos($line, $end_marker)) {
378 $found_end_marker = true;
379 continue;
380 }
381
382 if (!$found_marker) {
383 $pre_lines[] = $line;
384 } elseif ($found_marker && $found_end_marker) {
385 $post_lines[] = $line;
386 } else {
387 $existing_lines[] = $line;
388 }
389 }
390
391 // Check to see if there was a change
392 if ($existing_lines === $insertion) {
393 flock($fp, LOCK_UN);
394 fclose($fp);
395
396 return true;
397 }
398
399 // Check if need to prepend data if not exist
400 if ($prepend && !$post_lines) {
401 // Generate the new file data
402 $new_file_data = implode("\n", array_merge(array( $start_marker ), $insertion, array( $end_marker ), $pre_lines));
403 } else {
404 // Generate the new file data
405 $new_file_data = implode("\n", array_merge($pre_lines, array( $start_marker ), $insertion, array( $end_marker ), $post_lines));
406 }
407
408 // Write to the start of the file, and truncate it to that length
409 fseek($fp, 0);
410 $bytes = fwrite($fp, $new_file_data);
411 if ($bytes) {
412 ftruncate($fp, ftell($fp));
413 }
414 fflush($fp);
415 flock($fp, LOCK_UN);
416 fclose($fp);
417
418 return (bool) $bytes;
419 }
420 }
421