PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.3
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.3
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / includes / class-wp-optimize-htaccess.php

class-wp-optimize-htaccess.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.5.3, at includes/class-wp-optimize-htaccess.php

341 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) die('No direct access allowed');
4
5 class WP_Optimize_Htaccess {
6
7 /**
8 * Full path to .htaccess file.
9 *
10 * @var string
11 */
12 private $_htaccess_file;
13
14 /**
15 * Structured content of .htaccess file.
16 *
17 * @var array
18 */
19 private $_file_tree = array();
20
21 /**
22 * WP_Optimize_Htaccess constructor.
23 *
24 * @param string $htaccess_file Full path to .htaccess file.
25 */
26 public function __construct($htaccess_file = '') {
27 $this->_htaccess_file = ('' != $htaccess_file) ? $htaccess_file : $this->get_home_path() . '.htaccess';
28 // read .htaccess content into $_file_tree.
29 $this->read_file();
30 }
31
32 /**
33 * Returns .htaccess filename.
34 *
35 * @return string
36 */
37 public function get_filename() {
38 return $this->_htaccess_file;
39 }
40
41 /**
42 * Checks if .htaccess file exists.
43 *
44 * @return bool
45 */
46 public function is_exists() {
47 return is_file($this->_htaccess_file);
48 }
49
50 /**
51 * Checks if .htaccess file is readable.
52 *
53 * @return bool
54 */
55 public function is_readable() {
56 return is_readable($this->_htaccess_file);
57 }
58
59 /**
60 * Checks if .htaccess file is writable.
61 *
62 * @return bool
63 */
64 public function is_writable() {
65 return wp_is_writable($this->_htaccess_file);
66 }
67
68 /**
69 * Read content of .htaccess file and store it as a tree in $_file_tree variable.
70 * For ex.
71 * [0] => '# BEGIN WordPress',
72 * [1] => [
73 * [0] => '<IfModule mod_rewrite.c>',
74 * [1] => 'RewriteEngine On',
75 * [2] => 'RewriteBase /',
76 * [3] => 'RewriteRule ^index\.php$ - [L]',
77 * [4] => 'RewriteCond %{REQUEST_FILENAME} !-f',
78 * [5] => 'RewriteCond %{REQUEST_FILENAME} !-d',
79 * [6] => 'RewriteRule . /index.php [L]',
80 * [7] => '</IfModule>',
81 * ]
82 * [2] => '# END WordPress'
83 *
84 * @return void
85 */
86 public function read_file() {
87 if (!$this->is_exists() || !$this->is_readable()) return;
88
89 $content = file_get_contents($this->_htaccess_file);
90
91 $content = explode("\n", $content);
92
93 $content_tree = array();
94
95 $section = array();
96 $sections = array();
97
98 foreach ($content as $line) {
99 $line = trim($line);
100
101 if (preg_match("/^\<\/(.+)(\s.*)?\>/", $line, $matches)) {
102 $section[] = $line;
103
104 // close section
105 if (!empty($sections)) {
106 $_section = $section;
107 $section = array_pop($sections);
108 $section[] = $_section;
109 } else {
110 $content_tree[] = $section;
111 $section = array();
112 }
113 } elseif (preg_match('/^\<(.+)>/', $line, $matches)) {
114 // open section
115 if (!empty($section)) {
116 $sections[] = $section;
117 }
118
119 $section = array();
120 $section[] = $line;
121 } elseif (!empty($section)) {
122 $section[] = $line;
123 } else {
124 $content_tree[] = $line;
125 }
126 }
127
128 $this->_file_tree = $content_tree;
129 }
130
131 /**
132 * Write current $_file_tree content into .htaccess file.
133 */
134 public function write_file() {
135 $content = implode(PHP_EOL, $this->get_flat_array($this->_file_tree));
136 if ($this->is_writable()) {
137 @file_put_contents($this->_htaccess_file, $content); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Hosts like WP Engine restricts use of `.htaccess` file
138 }
139 }
140
141 /**
142 * Recursive function used to prepare data for output - build flat array from $_file_tree.
143 *
144 * @param array $array
145 * @param string $prefix
146 *
147 * @return array
148 */
149 public function get_flat_array($array, $prefix = '') {
150 $flat_array = array();
151
152 if (!empty($array)) {
153 foreach ($array as $item) {
154 if (is_array($item)) {
155 $item = $this->get_flat_array($item, "\t");
156 $flat_array = array_merge($flat_array, $item);
157 } else {
158 $flat_array[] = $item;
159 }
160 }
161 }
162
163 reset($flat_array);
164 $first = key($flat_array);
165 end($flat_array);
166 $last = key($flat_array);
167
168 foreach ($flat_array as $key => $value) {
169 if ('' !== $value && '#' === $value[0]) {
170 // never add prefix for comment lines.
171 $flat_array[$key] = $value;
172 } else {
173 $flat_array[$key] = ($key === $first || $key === $last) ? $value : $prefix . $value;
174 }
175 }
176
177 return $flat_array;
178 }
179
180 /**
181 * Update commented section in array $_file_tree, i.e. section wrapped with comments
182 * # BEGIN WP-Optimize Browser Cache
183 * ...
184 * # END WP-Optimize Browser Cache
185 *
186 * @param array $content
187 * @param string $section
188 * @param bool $prepend
189 */
190 public function update_commented_section($content, $section = 'WP-Optimize Browser Cache', $prepend = false) {
191 $section_begin = $this->get_section_begin_comment($section);
192 $section_end = $this->get_section_end_comment($section);
193
194 // add begin-end section comments.
195 array_unshift($content, $section_begin);
196 array_push($content, $section_end);
197
198 $section_index = $this->search_commented_section($section);
199
200 // check if section with cache settings already in the file.
201 if (false === $section_index) {
202 // no section in file then add it to the end of file.
203 $this->_file_tree = $prepend ? array_merge($content, $this->_file_tree) : array_merge($this->_file_tree, $content);
204 } else {
205 $remove_length = (false === $section_index['end']) ? null : ($section_index['end'] - $section_index['begin'] + 1);
206 array_splice($this->_file_tree, $section_index['begin'], $remove_length, $content);
207 }
208 }
209
210 /**
211 * Removes commented section in $_file_tree, i.e. section wrapped with comments
212 * # BEGIN WP-Optimize Browser Cache
213 * ...
214 * # END WP-Optimize Browser Cache
215 *
216 * @param string $comment
217 *
218 * @return bool
219 */
220 public function remove_commented_section($comment = 'WP-Optimize Browser Cache') {
221 $section_index = $this->search_commented_section($comment);
222 if (false === $section_index) return false;
223
224 $remove_length = (false === $section_index['end']) ? null : ($section_index['end'] - $section_index['begin'] + 1);
225 array_splice($this->_file_tree, $section_index['begin'], $remove_length);
226
227 $this->_file_tree = array_values($this->_file_tree);
228
229 return true;
230 }
231
232 /**
233 * Check if section exists wrapped by comments like
234 *
235 * # BEGIN WP-Optimize Browser Cache
236 * ...
237 * # END WP-Optimize Browser Cache
238 *
239 * @param string $section
240 * @return bool
241 */
242 public function is_commented_section_exists($section = 'WP-Optimize Browser Cache') {
243 $search = $this->search_commented_section($section);
244
245 return false !== $search;
246 }
247
248 /**
249 * Search section in $_file_tree array wrapped by begin and end comments.
250 *
251 * @param string $section
252 * @return array|bool
253 */
254 private function search_commented_section($section) {
255 $section_begin = $this->get_section_begin_comment($section);
256 $section_end = $this->get_section_end_comment($section);
257
258 $section_begin_index = $section_end_index = false;
259
260 $section_begin_normalized = $this->normalize_string($section_begin);
261 $section_end_normalized = $this->normalize_string($section_end);
262
263 foreach ($this->_file_tree as $i => $value) {
264 // if it is subsection then we don't go in deep.
265 if (is_array($value)) continue;
266
267 $value = $this->normalize_string($value);
268
269 if ($value === $section_begin_normalized) $section_begin_index = $i;
270 if ($value === $section_end_normalized) $section_end_index = $i;
271 }
272
273 if (false === $section_begin_index) {
274 return false;
275 } else {
276 return array(
277 'begin' => $section_begin_index,
278 'end' => $section_end_index,
279 );
280 }
281 }
282
283 /**
284 * Generate begin cache section comment.
285 *
286 * @param string $section
287 * @return string
288 */
289 public function get_section_begin_comment($section = 'WP-Optimize Browser Cache') {
290 return '# BEGIN ' . $section;
291 }
292
293 /**
294 * Generate end cache section comment.
295 *
296 * @param string $section
297 * @return string
298 */
299 public function get_section_end_comment($section = 'WP-Optimize Browser Cache') {
300 return '# END ' . $section;
301 }
302
303 /**
304 * Normalize string - make all letters lowercase and remove spaces.
305 *
306 * @param string $string
307 * @return string
308 */
309 private function normalize_string($string) {
310 return strtolower(str_replace(array("\n", "\r", ' '), '', $string));
311 }
312
313 /**
314 * Get the absolute filesystem path to the root of the WordPress installation.
315 * WP_Core function from wp-admin/includes/file.php.
316 *
317 * @since 1.5.0
318 *
319 * @return string Full filesystem path to the root of the WordPress installation
320 */
321 private function get_home_path() {
322 if (function_exists('get_home_path')) {
323 return get_home_path();
324 }
325
326 $home = set_url_scheme(get_option('home'), 'http');
327 $siteurl = set_url_scheme(get_option('siteurl'), 'http');
328 if (!empty($home) && 0 !== strcasecmp($home, $siteurl)) {
329 $script_filename = isset($_SERVER['SCRIPT_FILENAME']) ? sanitize_text_field(wp_unslash($_SERVER['SCRIPT_FILENAME'])) : '';
330 $wp_path_rel_to_home = str_ireplace($home, '', $siteurl); /* $siteurl - $home */
331 $pos = strripos(str_replace('\\', '/', $script_filename), trailingslashit($wp_path_rel_to_home));
332 $home_path = substr($script_filename, 0, $pos);
333 $home_path = trailingslashit($home_path);
334 } else {
335 $home_path = ABSPATH;
336 }
337
338 return str_replace('\\', '/', $home_path);
339 }
340 }
341