337 lines
8.5 KiB
PHP
337 lines
8.5 KiB
PHP
|
<?php
|
||
|
|
||
|
if (!defined('WPO_VERSION')) die('No direct access allowed');
|
||
|
|
||
|
class WP_Optimize_Htaccess {
|
||
|
|
||
|
/**
|
||
|
* Full path to .htaccess file.
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
private $_htaccess_file = '';
|
||
|
|
||
|
/**
|
||
|
* Structured content of .htaccess file.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private $_file_tree = array();
|
||
|
|
||
|
/**
|
||
|
* WP_Optimize_Htaccess constructor.
|
||
|
*
|
||
|
* @param string $htaccess_file Full path to .htaccess file.
|
||
|
*/
|
||
|
public function __construct($htaccess_file = '') {
|
||
|
$this->_htaccess_file = ('' != $htaccess_file) ? $htaccess_file : $this->get_home_path() . '.htaccess';
|
||
|
// read .htaccess content into $_file_tree.
|
||
|
$this->read_file();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns .htaccess filename.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_filename() {
|
||
|
return $this->_htaccess_file;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if .htaccess file exists.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function is_exists() {
|
||
|
return is_file($this->_htaccess_file);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if .htaccess file is readaable.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function is_readable() {
|
||
|
return is_readable($this->_htaccess_file);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if .htaccess file is writable.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function is_writable() {
|
||
|
return is_writable($this->_htaccess_file);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read content of .htaccess file and store it as a tree in $_file_tree variable.
|
||
|
* For ex.
|
||
|
* [0] => '# BEGIN WordPress',
|
||
|
* [1] => [
|
||
|
* [0] => '<IfModule mod_rewrite.c>',
|
||
|
* [1] => 'RewriteEngine On',
|
||
|
* [2] => 'RewriteBase /',
|
||
|
* [3] => 'RewriteRule ^index\.php$ - [L]',
|
||
|
* [4] => 'RewriteCond %{REQUEST_FILENAME} !-f',
|
||
|
* [5] => 'RewriteCond %{REQUEST_FILENAME} !-d',
|
||
|
* [6] => 'RewriteRule . /index.php [L]',
|
||
|
* [7] => '</IfModule>',
|
||
|
* [2] => '# END WordPress'
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function read_file() {
|
||
|
if (false == $this->is_exists() || false == $this->is_readable()) return;
|
||
|
|
||
|
$content = file_get_contents($this->_htaccess_file);
|
||
|
|
||
|
$content = explode("\n", $content);
|
||
|
|
||
|
$content_tree = array();
|
||
|
|
||
|
$section = array();
|
||
|
$sections = array();
|
||
|
|
||
|
foreach ($content as $line) {
|
||
|
$line = trim($line);
|
||
|
|
||
|
if (preg_match("/^\<\/(.+)(\s.*)?\>/", $line, $matches)) {
|
||
|
$section[] = $line;
|
||
|
|
||
|
// close section
|
||
|
if (!empty($sections)) {
|
||
|
$_section = $section;
|
||
|
$section = array_pop($sections);
|
||
|
$section[] = $_section;
|
||
|
} else {
|
||
|
$content_tree[] = $section;
|
||
|
$section = array();
|
||
|
}
|
||
|
} elseif (preg_match('/^\<(.+)>/', $line, $matches)) {
|
||
|
// open section
|
||
|
if (!empty($section)) {
|
||
|
$sections[] = $section;
|
||
|
}
|
||
|
|
||
|
$section = array();
|
||
|
$section[] = $line;
|
||
|
} elseif (!empty($section)) {
|
||
|
$section[] = $line;
|
||
|
} else {
|
||
|
$content_tree[] = $line;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$this->_file_tree = $content_tree;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write current $_file_tree content into .htaccess file.
|
||
|
*/
|
||
|
public function write_file() {
|
||
|
$content = implode(PHP_EOL, $this->get_flat_array($this->_file_tree));
|
||
|
file_put_contents($this->_htaccess_file, $content);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Recursive function used to prepare data for output - build flat array from $_file_tree.
|
||
|
*
|
||
|
* @param array $array
|
||
|
* @param string $prefix
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function get_flat_array($array, $prefix = '') {
|
||
|
$flat_array = array();
|
||
|
|
||
|
if (!empty($array)) {
|
||
|
foreach ($array as $item) {
|
||
|
if (is_array($item)) {
|
||
|
$item = $this->get_flat_array($item, "\t");
|
||
|
$flat_array = array_merge($flat_array, $item);
|
||
|
} else {
|
||
|
$flat_array[] = $item;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
reset($flat_array);
|
||
|
$first = key($flat_array);
|
||
|
end($flat_array);
|
||
|
$last = key($flat_array);
|
||
|
|
||
|
foreach ($flat_array as $key => $value) {
|
||
|
if ('' != $value && '#' == $value[0]) {
|
||
|
// never add prefix for comment lines.
|
||
|
$flat_array[$key] = $value;
|
||
|
} else {
|
||
|
$flat_array[$key] = ($key == $first || $key == $last) ? $value : $prefix . $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $flat_array;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update commented section in array $_file_tree, i.e. section wrapped with comments
|
||
|
* # BEGIN WP-Optimize Browser Cache
|
||
|
* ...
|
||
|
* # END WP-Optimize Browser Cache
|
||
|
*
|
||
|
* @param array $content
|
||
|
* @param string $section
|
||
|
*/
|
||
|
public function update_commented_section($content, $section = 'WP-Optimize Browser Cache') {
|
||
|
$section_begin = $this->get_section_begin_comment($section);
|
||
|
$section_end = $this->get_section_end_comment($section);
|
||
|
|
||
|
// add begin-end section comments.
|
||
|
array_unshift($content, $section_begin);
|
||
|
array_push($content, $section_end);
|
||
|
|
||
|
$section_index = $this->search_commented_section($section);
|
||
|
|
||
|
// check if section with cache settings already in the file.
|
||
|
if (false === $section_index) {
|
||
|
// no section in file then add it to the end of file.
|
||
|
$this->_file_tree = array_merge($this->_file_tree, $content);
|
||
|
} else {
|
||
|
$remove_length = (false === $section_index['end']) ? null : ($section_index['end'] - $section_index['begin'] + 1);
|
||
|
array_splice($this->_file_tree, $section_index['begin'], $remove_length, $content);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes commented section in $_file_tree, i.e. section wrapped with comments
|
||
|
* # BEGIN WP-Optimize Browser Cache
|
||
|
* ...
|
||
|
* # END WP-Optimize Browser Cache
|
||
|
*
|
||
|
* @param string $comment
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function remove_commented_section($comment = 'WP-Optimize Browser Cache') {
|
||
|
$section_index = $this->search_commented_section($comment);
|
||
|
|
||
|
if (false === $section_index) return false;
|
||
|
|
||
|
$remove_length = (false === $section_index['end']) ? null : ($section_index['end'] - $section_index['begin'] + 1);
|
||
|
array_splice($this->_file_tree, $section_index['begin'], $remove_length);
|
||
|
|
||
|
$this->_file_tree = array_values($this->_file_tree);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if section exists wrapped by comments like
|
||
|
*
|
||
|
* # BEGIN WP-Optimize Browser Cache
|
||
|
* ...
|
||
|
* # END WP-Optimize Browser Cache
|
||
|
*
|
||
|
* @param string $section
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function is_commented_section_exists($section = 'WP-Optimize Browser Cache') {
|
||
|
$search = $this->search_commented_section($section);
|
||
|
|
||
|
return (false === $search) ? false : true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Search section in $_file_tree array wrapped by begin and end comments.
|
||
|
*
|
||
|
* @param string $section
|
||
|
* @return array|bool
|
||
|
*/
|
||
|
private function search_commented_section($section) {
|
||
|
$section_begin = $this->get_section_begin_comment($section);
|
||
|
$section_end = $this->get_section_end_comment($section);
|
||
|
|
||
|
$section_begin_index = $section_end_index = false;
|
||
|
|
||
|
$section_begin_normalized = $this->normalize_string($section_begin);
|
||
|
$section_end_normalized = $this->normalize_string($section_end);
|
||
|
|
||
|
foreach ($this->_file_tree as $i => $value) {
|
||
|
// if it is subsection then we don't go in deep.
|
||
|
if (is_array($value)) continue;
|
||
|
|
||
|
$value = $this->normalize_string($value);
|
||
|
|
||
|
if ($value == $section_begin_normalized) $section_begin_index = $i;
|
||
|
if ($value == $section_end_normalized) $section_end_index = $i;
|
||
|
}
|
||
|
|
||
|
if (false == $section_begin_index) {
|
||
|
return false;
|
||
|
} else {
|
||
|
return array(
|
||
|
'begin' => $section_begin_index,
|
||
|
'end' => $section_end_index,
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Generate begin cache section comment.
|
||
|
*
|
||
|
* @param string $section
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_section_begin_comment($section = 'WP-Optimize Browser Cache') {
|
||
|
return '# BEGIN ' . $section;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Generate end cache section comment.
|
||
|
*
|
||
|
* @param string $section
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_section_end_comment($section = 'WP-Optimize Browser Cache') {
|
||
|
return '# END ' . $section;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Normalize string - make all letters lowercase and remove spaces.
|
||
|
*
|
||
|
* @param string $string
|
||
|
* @return string
|
||
|
*/
|
||
|
private function normalize_string($string) {
|
||
|
return strtolower(str_replace(array("\n", "\r", ' '), '', $string));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the absolute filesystem path to the root of the WordPress installation.
|
||
|
* WP_Core function from wp-admin/includes/file.php.
|
||
|
*
|
||
|
* @since 1.5.0
|
||
|
*
|
||
|
* @return string Full filesystem path to the root of the WordPress installation
|
||
|
*/
|
||
|
private function get_home_path() {
|
||
|
if (function_exists('get_home_path')) {
|
||
|
return get_home_path();
|
||
|
}
|
||
|
|
||
|
$home = set_url_scheme(get_option('home'), 'http');
|
||
|
$siteurl = set_url_scheme(get_option('siteurl'), 'http');
|
||
|
if (!empty($home) && 0 !== strcasecmp($home, $siteurl)) {
|
||
|
$wp_path_rel_to_home = str_ireplace($home, '', $siteurl); /* $siteurl - $home */
|
||
|
$pos = strripos(str_replace('\\', '/', $_SERVER['SCRIPT_FILENAME']), trailingslashit($wp_path_rel_to_home));
|
||
|
$home_path = substr($_SERVER['SCRIPT_FILENAME'], 0, $pos);
|
||
|
$home_path = trailingslashit($home_path);
|
||
|
} else {
|
||
|
$home_path = ABSPATH;
|
||
|
}
|
||
|
|
||
|
return str_replace('\\', '/', $home_path);
|
||
|
}
|
||
|
}
|