File Manager
<?php
namespace HM\BackUpWordPress;
/**
* Manages both the backup path and site root
*
* Handles calculating & protecting the directory that backups will be stored in
* as well as the directory that is being backed up
*/
class Path {
/**
* The path to the directory that backup files are stored in
*
* @var string $this->path
*/
private $path;
/**
* The path to the directory that will be backed up
*
* @var string $this->root
*/
private $root;
/**
* The path to the directory that backup files are stored in
*
* @var string $this->path
*/
private $custom_path;
/**
* Contains the instantiated Path instance
*
* @var Path $this->instance
*/
private static $instance;
/**
* Protected constructor to prevent creating a new instance of the
* *Singleton* via the `new` operator from outside of this class.
*/
protected function __construct() {}
/**
* Prevent cloning of the instance of the *Singleton* instance.
*/
public function __clone() { throw new \Exception('may not be cloned'); }
/**
* Private unserialize method to prevent unserializing of the *Singleton*
* instance.
*/
public function __wakeup() { throw new \Exception('may not be serialized'); }
/**
* Returns the *Singleton* instance of this class.
*
* @staticvar Path $instance The *Singleton* instances of this class.
*
* @return Path The *Singleton* instance.
*/
public static function get_instance() {
if ( ! ( self::$instance instanceof Path ) ) {
self::$instance = new Path();
}
return self::$instance;
}
/**
* Convenience method for quickly grabbing the path
*/
public static function get_path() {
return self::get_instance()->get_calculated_path();
}
/**
* Convenience method for quickly grabbing the root
*/
public static function get_root() {
return self::get_instance()->get_calculated_root();
}
/**
* Calculate the path to the site "home" directory.
*
* The home directory is the path equivalent to the home_url. That is,
* the path to the true root of the website. In situations where WordPress is
* installed in a subdirectory the home path is different to ABSPATH
*
* @param string $site_path The site_path to use when calculating the home path, defaults to ABSPATH
*/
public static function get_home_path( $site_path = ABSPATH ) {
if ( defined( 'HMBKP_ROOT' ) && HMBKP_ROOT ) {
return wp_normalize_path( HMBKP_ROOT );
}
$home_path = wp_normalize_path( $site_path );
if ( path_in_php_open_basedir( dirname( $site_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( wp_normalize_path( $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) );
$home_path = substr( wp_normalize_path( $_SERVER['SCRIPT_FILENAME'] ), 0, $pos );
$home_path = trailingslashit( $home_path );
}
if ( is_multisite() ) {
$slashed_home = trailingslashit( get_option( 'home' ) );
$base = parse_url( $slashed_home, PHP_URL_PATH );
$document_root_fix = wp_normalize_path( realpath( $_SERVER['DOCUMENT_ROOT'] ) );
$abspath_fix = wp_normalize_path( ABSPATH );
$home_path = strpos( $abspath_fix, $document_root_fix ) === 0 ? $document_root_fix . $base : $home_path;
}
}
return wp_normalize_path( untrailingslashit( $home_path ) );
}
/**
* get the calculated path to the directory where backups will be stored
*/
private function get_calculated_path() {
// Calculate the path if needed
if ( empty( $this->path ) || ! wp_is_writable( $this->path ) ) {
$this->calculate_path();
}
// Ensure the backup directory is protected
$this->protect_path();
return wp_normalize_path( $this->path );
}
/**
* Set the path directly, overriding the default
*
* @param $path
*/
public function set_path( $path ) {
$this->custom_path = $path;
// Re-calculate the backup path
$this->calculate_path();
}
/**
* get the calculated path to the directory that will be backed up
*/
private function get_calculated_root() {
$root = self::get_home_path();
if ( defined( 'HMBKP_ROOT' ) && HMBKP_ROOT ) {
$root = HMBKP_ROOT;
}
if ( $this->root ) {
$root = $this->root;
}
return wp_normalize_path( $root );
}
/**
* Set the root path directly, overriding the default
*
* @param $root
*/
public function set_root( $root ) {
$this->root = $root;
}
public function reset_path() {
$this->path = $this->custom_path = '';
}
/**
* Get the path to the default backup location in wp-content
*/
public function get_default_path() {
return trailingslashit( wp_normalize_path( WP_CONTENT_DIR ) ) . 'backupwordpress-' . substr( HMBKP_SECURE_KEY, 0, 10 ) . '-backups';
}
/**
* Get the path to the fallback backup location in uploads
*/
public function get_fallback_path() {
$upload_dir = wp_upload_dir();
return trailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . 'backupwordpress-' . substr( HMBKP_SECURE_KEY, 0, 10 ) . '-backups';
}
/**
* Get the path to the custom backup location if it's been set
*/
public function get_custom_path() {
if ( $this->custom_path ) {
return $this->custom_path;
}
if ( defined( 'HMBKP_PATH' ) && wp_is_writable( HMBKP_PATH ) ) {
return HMBKP_PATH;
}
return '';
}
/**
* Builds an array containing existing backups folders.
*
* @return array
*/
public function get_existing_paths() {
if ( false === $default = glob( WP_CONTENT_DIR . '/backupwordpress-*-backups', GLOB_ONLYDIR ) ) {
$default = array();
}
$upload_dir = wp_upload_dir();
if ( false === $fallback = glob( $upload_dir['basedir'] . '/backupwordpress-*-backups', GLOB_ONLYDIR ) ) {
$fallback = array();
}
$paths = array_merge( $default, $fallback );
$paths = array_map( 'wp_normalize_path', $paths );
return $paths;
}
/**
* Returns the first existing path if there is one
*
* @return string Backup path if found empty string if not
*/
public function get_existing_path() {
$paths = $this->get_existing_paths();
if ( ! empty( $paths[0] ) ) {
return $paths[0];
}
return '';
}
/**
* Calculate the backup path and create the directory if it doesn't exist.
*
* Tries all possible locations and uses the first one possible.
*
* @return
*/
public function calculate_path() {
$paths = $this->get_possible_paths();
// Loop through possible paths, use the first one that exists/can be created and is writable.
foreach ( $paths as $path ) {
// Also handles fixing perms / directory already exists.
if ( wp_mkdir_p( $path ) && wp_is_writable( $path ) ) {
break;
}
}
/**
* If we managed to create a writable path then use that,
* otherwise just return the unwritable path.
*/
if ( file_exists( $path ) && wp_is_writable( $path ) ) {
$this->path = $path;
} else {
$this->path = reset( $paths );
}
}
public function get_possible_paths() {
$paths = array();
// If we have a custom path then try to use it.
if ( $this->get_custom_path() ) {
$paths[] = $this->get_custom_path();
}
// If there is already a backups directory then try to use that.
if ( $this->get_existing_path() ) {
$paths[] = $this->get_existing_path();
}
// If not then default to a new directory in wp-content.
$paths[] = $this->get_default_path();
// If that didn't work then fallback to a new directory in uploads.
$paths[] = $this->get_fallback_path();
return $paths;
}
/**
* Protect the directory that backups are stored in
*
* - Adds an index.html file in an attempt to disable directory browsing
* - Adds a .httaccess file to deny direct access if on Apache
*
* @param string $reset
*/
public function protect_path( $reset = 'no' ) {
global $is_apache;
// Protect against directory browsing by including an index.html file
$index = $this->path . '/index.html';
if ( 'reset' === $reset && file_exists( $index ) ) {
@unlink( $index );
}
if ( ! file_exists( $index ) && wp_is_writable( $this->path ) ) {
file_put_contents( $index, '' );
}
$htaccess = $this->path . '/.htaccess';
if ( ( 'reset' === $reset ) && file_exists( $htaccess ) ) {
@unlink( $htaccess );
}
// Protect the directory with a .htaccess file on Apache servers
if ( $is_apache && function_exists( 'insert_with_markers' ) && ! file_exists( $htaccess ) && wp_is_writable( $this->path ) ) {
$contents = array();
$contents[] = '# ' . sprintf( __( 'This %s file ensures that other people cannot download your backup files.', 'backupwordpress' ), '.htaccess' );
$contents[] = '';
$contents[] = '<IfModule mod_rewrite.c>';
$contents[] = 'RewriteEngine On';
$contents[] = 'RewriteCond %{QUERY_STRING} !key=' . HMBKP_SECURE_KEY;
$contents[] = 'RewriteRule (.*) - [F]';
$contents[] = '</IfModule>';
$contents[] = '';
file_put_contents( $htaccess, '' );
insert_with_markers( $htaccess, 'BackUpWordPress', $contents );
}
}
/**
* If we have more than one path then move any existing backups to the current path and remove them
*/
public function merge_existing_paths() {
$paths = $this->get_existing_paths();
if ( ( ! empty( $paths ) && $this->get_custom_path() ) || count( $paths ) > 1 ) {
foreach ( $paths as $old_path ) {
$this->move_old_backups( $old_path );
}
}
}
/**
* Move backup files from an existing directory and the new
* location.
*
* @param string $from The path to move the backups from.
*/
public function move_old_backups( $from ) {
if ( ! is_readable( $from ) ) {
return;
}
if ( ! wp_is_writable( Path::get_path() ) ) {
return;
}
// Move any existing backups
if ( $handle = opendir( $from ) ) {
// Loop through the backup directory
while ( false !== ( $file = readdir( $handle ) ) ) {
// Find all zips
if ( 'zip' === pathinfo( $file, PATHINFO_EXTENSION ) ) {
// Try to move them
if ( ! @rename( trailingslashit( $from ) . $file, trailingslashit( Path::get_path() ) . $file ) ) {
// If we can't move them then try to copy them
copy( trailingslashit( $from ) . $file, trailingslashit( Path::get_path() ) . $file );
}
}
}
closedir( $handle );
}
// Delete the old directory if it's inside WP_CONTENT_DIR
if ( false !== strpos( $from, WP_CONTENT_DIR ) && Path::get_path() !== $from ) {
rmdirtree( $from );
}
}
/**
* Clean any temporary / incomplete backups from the backups directory
*/
public function cleanup() {
// Don't cleanup a custom path, who knows what other stuff is there
if ( Path::get_path() === $this->get_custom_path() ) {
return;
}
foreach ( new CleanUpIterator( new \DirectoryIterator( Path::get_path() ) ) as $file ) {
if ( $file->isDot() || ! $file->isReadable() || ! $file->isFile() ) {
continue;
}
@unlink( $file->getPathname() );
}
}
}
class CleanUpIterator extends \FilterIterator {
// Don't match index.html, files with zip extension or status logfiles.
public function accept() {
// Don't remove existing backups
if ( 'zip' === pathinfo( $this->current()->getFilename(), PATHINFO_EXTENSION ) ) {
return false;
}
// Don't remove the index.html file
if ( 'index.html' === $this->current()->getBasename() ) {
return false;
}
// Don't remove the file manifest
if ( '.files' === $this->current()->getBasename() ) {
return false;
}
// Don't cleanup the backup running file
return ! preg_match( '/(.*-running)/', $this->current() );
}
}
File Manager Version 1.0, Coded By Lucas
Email: hehe@yahoo.com