-
-
Save florianbrinkmann/b76a81a6a2af7f2d69b0d4f6ed1edb13 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Main plugin code. | |
* | |
* @package FlorianBrinkmann\Copier | |
*/ | |
namespace FlorianBrinkmann\Copier; | |
use Symfony\Component\Filesystem\Exception\IOExceptionInterface; | |
use Symfony\Component\Filesystem\Filesystem; | |
use Symfony\Component\Finder\Finder; | |
/** | |
* Class Plugin | |
* | |
* @package FlorianBrinkmann\Copier | |
*/ | |
class Plugin { | |
/** | |
* Absolute path to the WordPress install. | |
* | |
* @var string | |
*/ | |
private $abspath = ''; | |
/** | |
* Absolute path where we want to copy the files to. | |
* | |
* @var string | |
*/ | |
private $dest = ''; | |
/** | |
* Name of destination directory. | |
* | |
* @var string | |
*/ | |
private $dest_dir_name = ''; | |
/** | |
* Array of default directories to exclude. | |
* | |
* @var array | |
*/ | |
private $default_exclude = []; | |
/** | |
* Array of additional to exclude. | |
* | |
* @var array | |
*/ | |
private $additional_exclude = []; | |
/** | |
* Path to status file. | |
* | |
* @var string | |
*/ | |
private $status_file; | |
/** | |
* Current file index. | |
* | |
* @var string | |
*/ | |
private $current_file_index = ''; | |
/** | |
* Current path. | |
* | |
* @var string | |
*/ | |
private $current_path = ''; | |
/** | |
* Tables of WordPress install. | |
* | |
* @var array | |
*/ | |
private $tables = []; | |
/** | |
* The current step of the process. | |
* | |
* @var string | |
*/ | |
private $step = ''; | |
/** | |
* Time limit in seconds. Default 30. | |
* | |
* @var int | |
*/ | |
private $time_limit = 30; | |
/** | |
* Unix timestamp of init event. | |
* | |
* @var int | |
*/ | |
private $timer; | |
/** | |
* Symfony filesystem object. | |
* | |
* @var Filesystem | |
*/ | |
private $filesystem; | |
/** | |
* List of directories and files. | |
* | |
* @var array | |
*/ | |
private $files_list; | |
public function init() { | |
// Set timer. | |
$this->timer = time(); | |
// Set filesystem property. | |
$this->filesystem = new Filesystem(); | |
$this->step = 'file-list-creation'; | |
// Set dest folder for copying. | |
$dest_dir_name = uniqid( 'uaas-copy-' ); | |
// Check if folder exists. If so, change name and check again. | |
// @todo: Only check for a limited time. | |
while ( $this->filesystem->exists( "$this->abspath/$dest_dir_name" ) ) { | |
$dest_dir_name = uniqid( 'uaas-copy-' ); | |
} | |
$this->dest_dir_name = $dest_dir_name; | |
$this->dest = trailingslashit( "$this->abspath{$dest_dir_name}" ); | |
// Create file list for cloning. | |
$this->default_exclude = [ 'wp-content/uploads', $this->dest_dir_name ]; | |
$this->create_file_list(); | |
// Create dest folder. | |
$this->filesystem->mkdir( $this->dest, 0755 ); | |
// Create status file. | |
$this->status_file = "{$this->dest}uaas-status.txt"; | |
$this->filesystem->dumpFile( $this->status_file, serialize( $this ) ); | |
// We have the files in the files_list property now, so we can clone! | |
$this->step = 'copying-files'; | |
$this->copy_files(); | |
} | |
/** | |
* Continue copying. | |
*/ | |
public function continue_copying() { | |
// Check for step. | |
if ( $this->step !== 'copying-files' ) { | |
return false; | |
} | |
// Set timer. | |
$this->timer = time(); | |
// Update file list. | |
$this->create_file_list(); | |
$this->copy_files(); | |
} | |
/** | |
* Create a list of the files that we want to copy. | |
*/ | |
private function create_file_list() { | |
$finder = new Finder(); | |
// @todo: check for modified uploads destination. | |
// @todo: ignore other directories like cache, backups, … | |
$exclude = array_merge( $this->default_exclude, $this->additional_exclude ); | |
$finder->files()->in( $this->abspath )->exclude( $exclude )->notPath( '/.*\/node_modules\/.*/' ); | |
if ( ! $finder->hasResults() ) { | |
// @todo: Add error, nothing found. | |
return; | |
} | |
$this->files_list = $finder; | |
} | |
/** | |
* Copy the files. | |
*/ | |
private function copy_files() { | |
// Loop the files list. | |
$already_processed = true; | |
foreach ( $this->files_list as $index => $file ) { | |
// Check if we already had that file. | |
if ( $this->current_file_index === '' ) { | |
$already_processed = false; | |
} else if ( $this->current_file_index === $index ) { | |
$already_processed = false; | |
} | |
if ( $already_processed ) { | |
continue; | |
} | |
$tmp = str_replace( '\\', '/', $file->getRelativePath() ); | |
// Check if current path is not empty. | |
if ( $this->current_path !== '' ) { | |
// Now check if the path of prev file | |
// does not exist in path of current file. | |
if ( strpos( $tmp, $this->current_path ) !== 0 ) { | |
$pushed_to_array = false; | |
// Add prev path to exclude in conditional cases. | |
// Add it if is wp-admin folder or a direct subfolder. | |
if ( strpos( $this->current_path, 'wp-admin' ) === 0 && substr_count( $this->current_path, '/' ) <= 1 ) { | |
array_push( $this->additional_exclude, untrailingslashit( $this->current_path ) ); | |
$pushed_to_array = true; | |
} | |
// Add it if is wp-content folder or up to two levels deeper. | |
if ( strpos( $this->current_path, 'wp-content' ) === 0 ) { | |
// Get first three directories of paths. | |
// https://stackoverflow.com/a/1935929/7774451 | |
$path_parts = explode( '/', $this->current_path, 4 ); | |
if ( isset ( $path_parts[3] ) ) { | |
unset( $path_parts[3] ); | |
} | |
$tmp_path_parts = explode( '/', $tmp, 4 ); | |
if ( isset ( $tmp_path_parts[3] ) ) { | |
unset( $tmp_path_parts[3] ); | |
} | |
// If both arrays would be the same, that means we are deeper than three subdirs. | |
// CHECK WHY THAT DOES NOT WORK FOR ANTISPAM-BEE AND ANTISPAM-BEE-3-0 | |
if ( $path_parts !== $tmp_path_parts ) { | |
// Push the path from $path_parts. | |
array_push( $this->additional_exclude, implode( '/', $path_parts ) ); | |
$pushed_to_array = true; | |
} | |
} | |
// Remove entries from exclude that are covered by more general rules. | |
if ( $pushed_to_array ) { | |
$filtered = array_filter( $this->additional_exclude, function( $var ) { | |
// Check if $var is equal with current_path. | |
if ( $var === untrailingslashit( $this->current_path ) ) { | |
return true; | |
} | |
// If $var contains $this->current_path, remove it. | |
if ( strpos( $var, untrailingslashit( $this->current_path ) ) === 0 ) { | |
return false; | |
} | |
return true; | |
} ); | |
$this->additional_exclude = $filtered; | |
} | |
} | |
} | |
$this->current_path = str_replace( '\\', '/', $file->getRelativePath() ); | |
$absolute_file_path = str_replace( '\\', '/', $file->getRealPath() ); | |
// Create dest path for file. | |
$dest_file_path = str_replace( $this->abspath, $this->dest, $absolute_file_path ); | |
// Copy the file. | |
try { | |
$this->filesystem->copy( $absolute_file_path, $dest_file_path ); | |
} catch ( IOExceptionInterface $exception ) { | |
// @todo: make something with the error. | |
} | |
// Check if we are near the self-defined script timeout limit. | |
if ( time() - $this->timer >= $this->time_limit - 1 ) { | |
$this->current_file_index = $index; | |
// Store the current object in a file. | |
$this->filesystem->dumpFile( $this->status_file, serialize( $this ) ); | |
// Add a new cron event in the near future to continue the copying. | |
wp_schedule_single_event( time(), 'flobn_uaas_continue_copying', [ $this->status_file ] ); | |
// Exit. | |
exit(); | |
} | |
} | |
error_log( 'Finished file copying' ); | |
} | |
public function set_abspath( string $abspath ) { | |
// Replace backslash with slash. | |
$abspath = str_replace( '\\', '/', $abspath ); | |
$this->abspath = trailingslashit( $abspath ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment