PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.21
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.21
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 3.2.21, at includes/class-wp-optimize-htaccess.php

338 lines 8.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('WPO_VERSION')) 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 readaable.
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 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 * [2] => '# END WordPress'
82 *
83 * @return void
84 */
85 public function read_file() {
86 if (false == $this->is_exists() || false == $this->is_readable()) return;
87
88 $content = file_get_contents($this->_htaccess_file);
89
90 $content = explode("\n", $content);
91
92 $content_tree = array();
93
94 $section = array();
95 $sections = array();
96
97 foreach ($content as $line) {
98 $line = trim($line);
99
100 if (preg_match("/^\<\/(.+)(\s.*)?\>/", $line, $matches)) {
101 $section[] = $line;
102
103 // close section
104 if (!empty($sections)) {
105 $_section = $section;
106 $section = array_pop($sections);
107 $section[] = $_section;
108 } else {
109 $content_tree[] = $section;
110 $section = array();
111 }
112 } elseif (preg_match('/^\<(.+)>/', $line, $matches)) {
113 // open section
114 if (!empty($section)) {
115 $sections[] = $section;
116 }
117
118 $section = array();
119 $section[] = $line;
120 } elseif (!empty($section)) {
121 $section[] = $line;
122 } else {
123 $content_tree[] = $line;
124 }
125 }
126
127 $this->_file_tree = $content_tree;
128 }
129
130 /**
131 * Write current $_file_tree content into .htaccess file.
132 */
133 public function write_file() {
134 $content = implode(PHP_EOL, $this->get_flat_array($this->_file_tree));
135 if ($this->is_writable()) {
136 @file_put_contents($this->_htaccess_file, $content); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Hosts like WP Engine restricts use of `.htaccess` file
137 }
138 }
139
140 /**
141 * Recursive function used to prepare data for output - build flat array from $_file_tree.
142 *
143 * @param array $array
144 * @param string $prefix
145 *
146 * @return array
147 */
148 public function get_flat_array($array, $prefix = '') {
149 $flat_array = array();
150
151 if (!empty($array)) {
152 foreach ($array as $item) {
153 if (is_array($item)) {
154 $item = $this->get_flat_array($item, "\t");
155 $flat_array = array_merge($flat_array, $item);
156 } else {
157 $flat_array[] = $item;
158 }
159 }
160 }
161
162 reset($flat_array);
163 $first = key($flat_array);
164 end($flat_array);
165 $last = key($flat_array);
166
167 foreach ($flat_array as $key => $value) {
168 if ('' != $value && '#' == $value[0]) {
169 // never add prefix for comment lines.
170 $flat_array[$key] = $value;
171 } else {
172 $flat_array[$key] = ($key == $first || $key == $last) ? $value : $prefix . $value;
173 }
174 }
175
176 return $flat_array;
177 }
178
179 /**
180 * Update commented section in array $_file_tree, i.e. section wrapped with comments
181 * # BEGIN WP-Optimize Browser Cache
182 * ...
183 * # END WP-Optimize Browser Cache
184 *
185 * @param array $content
186 * @param string $section
187 */
188 public function update_commented_section($content, $section = 'WP-Optimize Browser Cache') {
189 $section_begin = $this->get_section_begin_comment($section);
190 $section_end = $this->get_section_end_comment($section);
191
192 // add begin-end section comments.
193 array_unshift($content, $section_begin);
194 array_push($content, $section_end);
195
196 $section_index = $this->search_commented_section($section);
197
198 // check if section with cache settings already in the file.
199 if (false === $section_index) {
200 // no section in file then add it to the end of file.
201 $this->_file_tree = array_merge($this->_file_tree, $content);
202 } else {
203 $remove_length = (false === $section_index['end']) ? null : ($section_index['end'] - $section_index['begin'] + 1);
204 array_splice($this->_file_tree, $section_index['begin'], $remove_length, $content);
205 }
206 }
207
208 /**
209 * Removes commented section in $_file_tree, i.e. section wrapped with comments
210 * # BEGIN WP-Optimize Browser Cache
211 * ...
212 * # END WP-Optimize Browser Cache
213 *
214 * @param string $comment
215 *
216 * @return bool
217 */
218 public function remove_commented_section($comment = 'WP-Optimize Browser Cache') {
219 $section_index = $this->search_commented_section($comment);
220 if (false === $section_index) return false;
221
222 $remove_length = (false === $section_index['end']) ? null : ($section_index['end'] - $section_index['begin'] + 1);
223 array_splice($this->_file_tree, $section_index['begin'], $remove_length);
224
225 $this->_file_tree = array_values($this->_file_tree);
226
227 return true;
228 }
229
230 /**
231 * Check if section exists wrapped by comments like
232 *
233 * # BEGIN WP-Optimize Browser Cache
234 * ...
235 * # END WP-Optimize Browser Cache
236 *
237 * @param string $section
238 * @return bool
239 */
240 public function is_commented_section_exists($section = 'WP-Optimize Browser Cache') {
241 $search = $this->search_commented_section($section);
242
243 return (false === $search) ? false : true;
244 }
245
246 /**
247 * Search section in $_file_tree array wrapped by begin and end comments.
248 *
249 * @param string $section
250 * @return array|bool
251 */
252 private function search_commented_section($section) {
253 $section_begin = $this->get_section_begin_comment($section);
254 $section_end = $this->get_section_end_comment($section);
255
256 $section_begin_index = $section_end_index = false;
257
258 $section_begin_normalized = $this->normalize_string($section_begin);
259 $section_end_normalized = $this->normalize_string($section_end);
260
261 foreach ($this->_file_tree as $i => $value) {
262 // if it is subsection then we don't go in deep.
263 if (is_array($value)) continue;
264
265 $value = $this->normalize_string($value);
266
267 if ($value == $section_begin_normalized) $section_begin_index = $i;
268 if ($value == $section_end_normalized) $section_end_index = $i;
269 }
270
271 if (false == $section_begin_index) {
272 return false;
273 } else {
274 return array(
275 'begin' => $section_begin_index,
276 'end' => $section_end_index,
277 );
278 }
279 }
280
281 /**
282 * Generate begin cache section comment.
283 *
284 * @param string $section
285 * @return string
286 */
287 public function get_section_begin_comment($section = 'WP-Optimize Browser Cache') {
288 return '# BEGIN ' . $section;
289 }
290
291 /**
292 * Generate end cache section comment.
293 *
294 * @param string $section
295 * @return string
296 */
297 public function get_section_end_comment($section = 'WP-Optimize Browser Cache') {
298 return '# END ' . $section;
299 }
300
301 /**
302 * Normalize string - make all letters lowercase and remove spaces.
303 *
304 * @param string $string
305 * @return string
306 */
307 private function normalize_string($string) {
308 return strtolower(str_replace(array("\n", "\r", ' '), '', $string));
309 }
310
311 /**
312 * Get the absolute filesystem path to the root of the WordPress installation.
313 * WP_Core function from wp-admin/includes/file.php.
314 *
315 * @since 1.5.0
316 *
317 * @return string Full filesystem path to the root of the WordPress installation
318 */
319 private function get_home_path() {
320 if (function_exists('get_home_path')) {
321 return get_home_path();
322 }
323
324 $home = set_url_scheme(get_option('home'), 'http');
325 $siteurl = set_url_scheme(get_option('siteurl'), 'http');
326 if (!empty($home) && 0 !== strcasecmp($home, $siteurl)) {
327 $wp_path_rel_to_home = str_ireplace($home, '', $siteurl); /* $siteurl - $home */
328 $pos = strripos(str_replace('\\', '/', $_SERVER['SCRIPT_FILENAME']), trailingslashit($wp_path_rel_to_home));
329 $home_path = substr($_SERVER['SCRIPT_FILENAME'], 0, $pos);
330 $home_path = trailingslashit($home_path);
331 } else {
332 $home_path = ABSPATH;
333 }
334
335 return str_replace('\\', '/', $home_path);
336 }
337 }
338