{"id":6077,"date":"2021-03-15T19:36:05","date_gmt":"2021-03-15T18:36:05","guid":{"rendered":"https:\/\/florianbrinkmann.com\/en\/?p=6077"},"modified":"2021-03-15T19:36:07","modified_gmt":"2021-03-15T18:36:07","slug":"copy-directory-php","status":"publish","type":"post","link":"https:\/\/florianbrinkmann.com\/en\/copy-directory-php-6077\/","title":{"rendered":"Copying directory with many files in multiple steps via PHP"},"content":{"rendered":"\n<p>I am currently working on a side project, which needs to create a copy of a WordPress installation. Because I do not know the server environment where it is used by the users, I can neither expect that I can run Linux commands, nor that there is a long PHP <code>max_execution_time<\/code>.<\/p>\n\n\n\n<p>So copying needs to be done via PHP and also work when, for example, the maximum execution time for scripts is 30 seconds. 30 seconds are not much for a script that should copy many files. Creating a list of the files and looping it without copying files can already take longer.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Because of that, we need to do the copying in a way where we can save the current state when we are near the time limit, plan the next run, and exit. In the WordPress context, that is possible with WordPress cron events.<\/p>\n\n\n\n<p>I had issues finding a solution how to exclude already copied files from the next process so that they are not looped again.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Symfony Finder component to the rescue<\/h2>\n\n\n\n<p>Relatively fast I came across the&nbsp;<a target=\"_blank\" href=\"https:\/\/symfony.com\/doc\/current\/components\/finder.html\" rel=\"noreferrer noopener\">Finder component of Symfony<\/a>. The component allows finding directories and files by various rules and exclude other directories.<\/p>\n\n\n\n<p>So in a few steps, my solution looks like the following:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>All files in the WordPress folder are searched, except the uploads directory.<\/li><li>Copying starts.<\/li><li>After finishing copying one folder, it is added to an array to be able to ignore it in the next run.<\/li><li>When almost reaching the timeout, the current state is saved to a file, a WP cron event is created and the program is finished.<\/li><li>When the cron event happens, the files from the not-yet processed directories are searched and it starts with step 2 again.<\/li><\/ol>\n\n\n\n<p>My current code for that (not yet finished, but it works and should be enough for inspiration. Besides the Finder component I also use the\u00a0<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/symfony.com\/doc\/current\/components\/filesystem.html\">Filesystem component<\/a>\u00a0\u2014 the\u00a0<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/gist.github.com\/florianbrinkmann\/b76a81a6a2af7f2d69b0d4f6ed1edb13\">code also exists as a Gist<\/a>).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-meta\">&lt;?php<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> * Main plugin code.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> *<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> * <span class=\"hljs-doctag\">@package<\/span> FlorianBrinkmann\\Copier<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">FlorianBrinkmann<\/span>\\<span class=\"hljs-title\">Copier<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Filesystem<\/span>\\<span class=\"hljs-title\">Exception<\/span>\\<span class=\"hljs-title\">IOExceptionInterface<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Filesystem<\/span>\\<span class=\"hljs-title\">Filesystem<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Finder<\/span>\\<span class=\"hljs-title\">Finder<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> * Class Plugin<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> *<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> * <span class=\"hljs-doctag\">@package<\/span> FlorianBrinkmann\\Copier<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Plugin<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Absolute path to the WordPress install.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> string<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $abspath = <span class=\"hljs-string\">''<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Absolute path where we want to copy the files to.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> string<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $dest = <span class=\"hljs-string\">''<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Name of destination directory.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> string<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $dest_dir_name = <span class=\"hljs-string\">''<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Array of default directories to exclude.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> array<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $default_exclude = &#91;];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Array of additional to exclude.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> array<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $additional_exclude = &#91;];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Path to status file.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> string<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $status_file;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Current file index.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> string<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $current_file_index = <span class=\"hljs-string\">''<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Current path.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> string<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $current_path = <span class=\"hljs-string\">''<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Tables of WordPress install.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> array<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $tables = &#91;];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * The current step of the process.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> string<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $step = <span class=\"hljs-string\">''<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Time limit in seconds. Default 30.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> int<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $time_limit = <span class=\"hljs-number\">30<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Unix timestamp of init event.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> int<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $timer;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Symfony filesystem object.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> Filesystem<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $filesystem;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * List of directories and files.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * <span class=\"hljs-doctag\">@var<\/span> array<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> $files_list;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">init<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Set timer.<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;timer = time();\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Set filesystem property.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;filesystem = <span class=\"hljs-keyword\">new<\/span> Filesystem();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;step = <span class=\"hljs-string\">'file-list-creation'<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Set dest folder for copying.<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>\t\t$dest_dir_name = uniqid( <span class=\"hljs-string\">'uaas-copy-'<\/span> );\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Check if folder exists. If so, change name and check again.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ @<span class=\"hljs-doctag\">todo:<\/span> Only check for a limited time.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">while<\/span> ( <span class=\"hljs-keyword\">$this<\/span>-&gt;filesystem-&gt;exists( <span class=\"hljs-string\">\"$this-&gt;abspath\/$dest_dir_name\"<\/span> ) ) {\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t$dest_dir_name = uniqid( <span class=\"hljs-string\">'uaas-copy-'<\/span> );\n<\/span><\/span><span class='shcb-loc'><span>\t\t}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;dest_dir_name = $dest_dir_name;\n<\/span><\/span><span class='shcb-loc'><span>\t\t\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;dest = trailingslashit( <span class=\"hljs-string\">\"$this-&gt;abspath{$dest_dir_name}\"<\/span> );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Create file list for cloning.<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;default_exclude = &#91; <span class=\"hljs-string\">'wp-content\/uploads'<\/span>, <span class=\"hljs-keyword\">$this<\/span>-&gt;dest_dir_name ];\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;create_file_list();\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Create dest folder.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;filesystem-&gt;mkdir( <span class=\"hljs-keyword\">$this<\/span>-&gt;dest, <span class=\"hljs-number\">0755<\/span> );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Create status file.<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;status_file = <span class=\"hljs-string\">\"{$this-&gt;dest}uaas-status.txt\"<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;filesystem-&gt;dumpFile( <span class=\"hljs-keyword\">$this<\/span>-&gt;status_file, serialize( <span class=\"hljs-keyword\">$this<\/span> ) );\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ We have the files in the files_list property now, so we can clone!<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;step = <span class=\"hljs-string\">'copying-files'<\/span>;\n<\/span><\/span><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;copy_files();\n<\/span><\/mark><span class='shcb-loc'><span>\t}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Continue copying.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>\t<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">continue_copying<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Check for step.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">$this<\/span>-&gt;step !== <span class=\"hljs-string\">'copying-files'<\/span> ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">false<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Set timer.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;timer = time();\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Update file list.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;create_file_list();\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;copy_files();\n<\/span><\/mark><mark class='shcb-loc'><span>\t}\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Create a list of the files that we want to copy.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">create_file_list<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t$finder = <span class=\"hljs-keyword\">new<\/span> Finder();\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ @<span class=\"hljs-doctag\">todo:<\/span> check for modified uploads destination.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ @<span class=\"hljs-doctag\">todo:<\/span> ignore other directories like cache, backups, \u2026<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t$exclude = array_merge( <span class=\"hljs-keyword\">$this<\/span>-&gt;default_exclude, <span class=\"hljs-keyword\">$this<\/span>-&gt;additional_exclude );\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t$finder-&gt;files()-&gt;in( <span class=\"hljs-keyword\">$this<\/span>-&gt;abspath )-&gt;exclude( $exclude )-&gt;notPath( <span class=\"hljs-string\">'\/.*\\\/node_modules\\\/.*\/'<\/span> );\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">if<\/span> ( ! $finder-&gt;hasResults() ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t<span class=\"hljs-comment\">\/\/ @<span class=\"hljs-doctag\">todo:<\/span> Add error, nothing found.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t<span class=\"hljs-keyword\">return<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;files_list = $finder;\n<\/span><\/mark><mark class='shcb-loc'><span>\t}\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t * Copy the files.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\t *\/<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>\t<span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">copy_files<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n<\/span><\/mark><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Loop the files list.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t$already_processed = <span class=\"hljs-keyword\">true<\/span>;\n<\/span><\/span><mark class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">foreach<\/span> ( <span class=\"hljs-keyword\">$this<\/span>-&gt;files_list <span class=\"hljs-keyword\">as<\/span> $index =&gt; $file ) {\t\n<\/span><\/mark><span class='shcb-loc'><span>\t\t\t<span class=\"hljs-comment\">\/\/ Check if we already had that file.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">$this<\/span>-&gt;current_file_index === <span class=\"hljs-string\">''<\/span> ) {\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t\t$already_processed = <span class=\"hljs-keyword\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t} <span class=\"hljs-keyword\">else<\/span> <span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">$this<\/span>-&gt;current_file_index === $index ) {\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t\t$already_processed = <span class=\"hljs-keyword\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t<span class=\"hljs-keyword\">if<\/span> ( $already_processed ) {\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-keyword\">continue<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>\t\t\t$tmp = str_replace( <span class=\"hljs-string\">'\\\\'<\/span>, <span class=\"hljs-string\">'\/'<\/span>, $file-&gt;getRelativePath() );\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t<span class=\"hljs-comment\">\/\/ Check if current path is not empty.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">$this<\/span>-&gt;current_path !== <span class=\"hljs-string\">''<\/span> ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-comment\">\/\/ Now check if the path of prev file<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-comment\">\/\/ does not exist in path of current file.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-keyword\">if<\/span> ( strpos( $tmp, <span class=\"hljs-keyword\">$this<\/span>-&gt;current_path ) !== <span class=\"hljs-number\">0<\/span> ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t$pushed_to_array = <span class=\"hljs-keyword\">false<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t<span class=\"hljs-comment\">\/\/ Add prev path to exclude in conditional cases.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t<span class=\"hljs-comment\">\/\/ Add it if is wp-admin folder or a direct subfolder.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t<span class=\"hljs-keyword\">if<\/span> ( strpos( <span class=\"hljs-keyword\">$this<\/span>-&gt;current_path, <span class=\"hljs-string\">'wp-admin'<\/span> ) === <span class=\"hljs-number\">0<\/span> &amp;&amp; substr_count( <span class=\"hljs-keyword\">$this<\/span>-&gt;current_path, <span class=\"hljs-string\">'\/'<\/span> ) &lt;= <span class=\"hljs-number\">1<\/span> ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\tarray_push( <span class=\"hljs-keyword\">$this<\/span>-&gt;additional_exclude, untrailingslashit( <span class=\"hljs-keyword\">$this<\/span>-&gt;current_path ) );\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t$pushed_to_array = <span class=\"hljs-keyword\">true<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t<span class=\"hljs-comment\">\/\/ Add it if is wp-content folder or up to two levels deeper.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t<span class=\"hljs-keyword\">if<\/span> ( strpos( <span class=\"hljs-keyword\">$this<\/span>-&gt;current_path, <span class=\"hljs-string\">'wp-content'<\/span> ) === <span class=\"hljs-number\">0<\/span> ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t<span class=\"hljs-comment\">\/\/ Get first three directories of paths.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t<span class=\"hljs-comment\">\/\/ https:\/\/stackoverflow.com\/a\/1935929\/7774451<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t$path_parts = explode( <span class=\"hljs-string\">'\/'<\/span>, <span class=\"hljs-keyword\">$this<\/span>-&gt;current_path, <span class=\"hljs-number\">4<\/span> );\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">isset<\/span> ( $path_parts&#91;<span class=\"hljs-number\">3<\/span>] ) ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t<span class=\"hljs-keyword\">unset<\/span>( $path_parts&#91;<span class=\"hljs-number\">3<\/span>] );\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t$tmp_path_parts = explode( <span class=\"hljs-string\">'\/'<\/span>, $tmp, <span class=\"hljs-number\">4<\/span> );\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">isset<\/span> ( $tmp_path_parts&#91;<span class=\"hljs-number\">3<\/span>] ) ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t<span class=\"hljs-keyword\">unset<\/span>( $tmp_path_parts&#91;<span class=\"hljs-number\">3<\/span>] );\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t<span class=\"hljs-comment\">\/\/ If both arrays would be the same, that means we are deeper than three subdirs.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t<span class=\"hljs-comment\">\/\/ CHECK WHY THAT DOES NOT WORK FOR ANTISPAM-BEE AND ANTISPAM-BEE-3-0<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t<span class=\"hljs-keyword\">if<\/span> ( $path_parts !== $tmp_path_parts ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t<span class=\"hljs-comment\">\/\/ Push the path from $path_parts.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\tarray_push( <span class=\"hljs-keyword\">$this<\/span>-&gt;additional_exclude, implode( <span class=\"hljs-string\">'\/'<\/span>, $path_parts ) );\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t$pushed_to_array = <span class=\"hljs-keyword\">true<\/span>;\t\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t<span class=\"hljs-comment\">\/\/ Remove entries from exclude that are covered by more general rules.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t<span class=\"hljs-keyword\">if<\/span> ( $pushed_to_array ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t$filtered = array_filter( <span class=\"hljs-keyword\">$this<\/span>-&gt;additional_exclude, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span><span class=\"hljs-params\">( $var )<\/span> <\/span>{\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t<span class=\"hljs-comment\">\/\/ Check if $var is equal with current_path.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t<span class=\"hljs-keyword\">if<\/span> ( $var === untrailingslashit( <span class=\"hljs-keyword\">$this<\/span>-&gt;current_path ) ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">true<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t<span class=\"hljs-comment\">\/\/ If $var contains $this-&gt;current_path, remove it.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t<span class=\"hljs-keyword\">if<\/span> ( strpos( $var, untrailingslashit( <span class=\"hljs-keyword\">$this<\/span>-&gt;current_path ) ) === <span class=\"hljs-number\">0<\/span> ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">false<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">true<\/span>;\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t} );\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;additional_exclude = $filtered;\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t\t}\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t} \n<\/span><\/mark><span class='shcb-loc'><span>\t\t\t}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;current_path = str_replace( <span class=\"hljs-string\">'\\\\'<\/span>, <span class=\"hljs-string\">'\/'<\/span>, $file-&gt;getRelativePath() );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t$absolute_file_path = str_replace( <span class=\"hljs-string\">'\\\\'<\/span>, <span class=\"hljs-string\">'\/'<\/span>, $file-&gt;getRealPath() );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t<span class=\"hljs-comment\">\/\/ Create dest path for file.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t$dest_file_path = str_replace( <span class=\"hljs-keyword\">$this<\/span>-&gt;abspath, <span class=\"hljs-keyword\">$this<\/span>-&gt;dest, $absolute_file_path );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t<span class=\"hljs-comment\">\/\/ Copy the file.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t<span class=\"hljs-keyword\">try<\/span> {\n<\/span><\/span><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;filesystem-&gt;copy( $absolute_file_path, $dest_file_path );\n<\/span><\/mark><span class='shcb-loc'><span>\t\t\t} <span class=\"hljs-keyword\">catch<\/span> ( IOExceptionInterface $exception ) {\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-comment\">\/\/ @<span class=\"hljs-doctag\">todo:<\/span> make something with the error.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t\t<span class=\"hljs-comment\">\/\/ Check if we are near the self-defined script timeout limit.<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>\t\t\t<span class=\"hljs-keyword\">if<\/span> ( time() - <span class=\"hljs-keyword\">$this<\/span>-&gt;timer &gt;= <span class=\"hljs-keyword\">$this<\/span>-&gt;time_limit - <span class=\"hljs-number\">1<\/span> ) {\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;current_file_index = $index;\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-comment\">\/\/ Store the current object in a file.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;filesystem-&gt;dumpFile( <span class=\"hljs-keyword\">$this<\/span>-&gt;status_file, serialize( <span class=\"hljs-keyword\">$this<\/span> ) );\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-comment\">\/\/ Add a new cron event in the near future to continue the copying.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\twp_schedule_single_event( time(), <span class=\"hljs-string\">'flobn_uaas_continue_copying'<\/span>, &#91; <span class=\"hljs-keyword\">$this<\/span>-&gt;status_file ] );\n<\/span><\/mark><mark class='shcb-loc'><span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-comment\">\/\/ Exit.<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t\t<span class=\"hljs-keyword\">exit<\/span>();\n<\/span><\/mark><mark class='shcb-loc'><span>\t\t\t}\n<\/span><\/mark><span class='shcb-loc'><span>\t\t}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t\terror_log( <span class=\"hljs-string\">'Finished file copying'<\/span> );\n<\/span><\/span><span class='shcb-loc'><span>\t}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\t<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">set_abspath<\/span><span class=\"hljs-params\">( string $abspath )<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-comment\">\/\/ Replace backslash with slash.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\t\t$abspath = str_replace( <span class=\"hljs-string\">'\\\\'<\/span>, <span class=\"hljs-string\">'\/'<\/span>, $abspath );\n<\/span><\/span><span class='shcb-loc'><span>\t\t<span class=\"hljs-keyword\">$this<\/span>-&gt;abspath = trailingslashit( $abspath );\n<\/span><\/span><span class='shcb-loc'><span>\t}\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>To start the process, we need to run the following code (the class file needs to be included, too, for example, via the Composer autoloader):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">FlorianBrinkmann<\/span>\\<span class=\"hljs-title\">Copier<\/span>;\n\n<span class=\"hljs-comment\">\/\/ Load Composer autoloader. From https:\/\/github.com\/brightnucleus\/jasper-client\/blob\/master\/tests\/bootstrap.php#L55-L59<\/span>\n$autoloader = dirname( <span class=\"hljs-keyword\">__FILE__<\/span> ) . <span class=\"hljs-string\">'\/vendor\/autoload.php'<\/span>;\n<span class=\"hljs-keyword\">if<\/span> ( is_readable( $autoloader ) ) {\n\t<span class=\"hljs-keyword\">require_once<\/span> $autoloader;\n}\n\n<span class=\"hljs-comment\">\/\/ Create Plugin object.\t<\/span>\n$uaas = <span class=\"hljs-keyword\">new<\/span> Plugin();\n\n<span class=\"hljs-comment\">\/\/ Set abspath property.<\/span>\n$uaas-&gt;set_abspath( ABSPATH );\n\n$uaas-&gt;init();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now the explanation of the most important parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Line 120: <code>timer<\/code> property is set to the current timestamp to later be able to check the script runtime.<\/li><li>Line 128: A directory name for the target folder is generated and it is checked if it already exists.<\/li><li>Lines 141 and 142: The directories to exclude by default are the uploads folder and the target folder. After that, the file list is generated.<\/li><li>Lines 177-192: Running the Symfony Finder in line 183. In addition to our ignore rules and the ones that come from the Finder component by default (version control directories, for example), we also exclude <code>node_modules<\/code> folders. In line 191 the Finder object is stored in the <code>files_list<\/code> property.<\/li><li>Lines 148 and 149: We create a status file in the target directory and save the current object (<code>$this<\/code>) to it.<\/li><li>Line 153: We start copying.<\/li><li>Line 197 following: The method for copying the files. We loop the files list and check if a file was already copied by waiting until the index of the last processed file is there (the files are always in the same order, and the index of the current file is set in line 288). All files before the index can be ignored because they are already processed.<\/li><li>Starting line 212, we begin with the check for fully processed directories. We cannot use all folders, because that would crash the <code>Finder<\/code>. We limit it to direct subdirectories in the <code>wp-admin<\/code> folder and two levels of subdirectories in the <code>wp-content<\/code> folder, so, for example, <code>wp-content\/plugins\/antispam-bee<\/code>.<\/li><li>In lines 252 to 268, we check if <code>$this->additional_exclude<\/code> contains folders that are matched by other entries and remove them. That would be the case for plugin folders after the whole <code>wp-content\/plugins<\/code> directory is processed.<\/li><li>In line 281 the file is copied.<\/li><li>The lines 287 to 289, we check if we are near the timelimit by one second. If that is the case, the currently processed file is saved in <code>current_file_index<\/code> and the current object in our state file. After that, the cron event is planned that executes the <code>flobn_uaas_continue_copying<\/code> hook and gets the path to the status file as a parameter.<\/li><li>Lines 159-172: The function that is hooked to <code>flobn_uaas_continue_copying<\/code> calls the <code>continue_copying()<\/code> method. In it we check it <code>step<\/code> is <code>copying-files<\/code> and if that is true, we set the <code>timer<\/code>, create our file list (in <code>create_file_list()<\/code> also the processed directories are ignored) and continue with copying.<\/li><\/ul>\n\n\n\n<p>The action that is called by the cron event looks like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">add_action( <span class=\"hljs-string\">'flobn_uaas_continue_copying'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span><span class=\"hljs-params\">( $status_file )<\/span> <\/span>{\n\t<span class=\"hljs-comment\">\/\/ Get status file.<\/span>\n\t$status_file_contents = file_get_contents( $status_file );\n\n\t<span class=\"hljs-comment\">\/\/ unserialize object.<\/span>\n\t$uaas = unserialize( $status_file_contents );\n\n\t<span class=\"hljs-comment\">\/\/ Continue with copying.<\/span>\n\t$uaas-&gt;continue_copying();\n} );<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We get the content of the status file, undo serialization of the object and run the <code>continue_copying()<\/code> method.<\/p>\n\n\n\n<p>And that is it, we have a process that even with a short execution time can copy large folders \ud83c\udf89<\/p>\n\n\n\n<p>If copying one file needs a long time, that could of course lead to a timeout error, because the check is done after copying and not in between. But it should be a good starting point.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I am currently working on a side project, which needs to create a copy of a WordPress installation. Because I do not know the server environment where it is used by the users, I can neither expect that I can run Linux commands, nor that there is a long PHP max_execution_time. So copying needs to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wpf_show_in_dewp_planet_feed":false,"flobn_post_versions":"","webmentions_disabled_pings":false,"webmentions_disabled":false,"lazy_load_responsive_images_disabled":false,"footnotes":""},"categories":[115],"tags":[],"class_list":["post-6077","post","type-post","status-publish","format-standard","hentry","category-wordpress-snippets"],"wp-worthy-pixel":{"ignored":false,"public":"b3c75a41684749738b195b0f2702d680","server":"vg07.met.vgwort.de","url":"https:\/\/vg07.met.vgwort.de\/na\/b3c75a41684749738b195b0f2702d680"},"wp-worthy-type":"normal","_links":{"self":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/6077","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/comments?post=6077"}],"version-history":[{"count":2,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/6077\/revisions"}],"predecessor-version":[{"id":6079,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/6077\/revisions\/6079"}],"wp:attachment":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/media?parent=6077"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/categories?post=6077"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/tags?post=6077"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}