PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.9.1
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.9.1
4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Framework / Filesystem / AbstractFilesystemScanner.php
wp-staging / Framework / Filesystem Last commit date
Filters 6 months ago Scanning 5 years ago AbstractFileObject.php 1 year ago AbstractFilesystemScanner.php 2 months ago DebugLogReader.php 2 years ago DirectoryListing.php 5 months ago DiskWriteCheck.php 5 months ago FileObject.php 1 year ago Filesystem.php 6 months ago FilesystemExceptions.php 5 years ago FilesystemScanner.php 1 week ago FilesystemScannerDto.php 1 week ago FilterableDirectoryIterator.php 1 year ago LegacyFileRulesTrait.php 1 week ago LogCleanup.php 5 months ago LogFiles.php 1 year ago MissingFileException.php 3 years ago OPcache.php 5 months ago PartIdentifier.php 8 months ago PathChecker.php 2 years ago PathIdentifier.php 6 months ago Permissions.php 2 days ago WpUploadsFolderSymlinker.php 1 week ago
AbstractFilesystemScanner.php
336 lines
1 <?php
2
3 namespace WPStaging\Framework\Filesystem;
4
5 use Exception;
6 use SplFileInfo;
7 use WPStaging\Framework\Adapter\Directory;
8 use WPStaging\Framework\Traits\EndOfLinePlaceholderTrait;
9 use WPStaging\Framework\Utils\PluginInfo;
10
11 abstract class AbstractFilesystemScanner
12 {
13 use EndOfLinePlaceholderTrait;
14
15 /**
16 * @var string
17 */
18 const PATH_SEPARATOR = '::';
19
20 /** @var Directory */
21 protected $directory;
22
23 /** @var Filesystem */
24 protected $filesystem;
25
26 /** @var PathIdentifier */
27 protected $pathIdentifier;
28
29 /** @var PluginInfo */
30 protected $pluginInfo;
31
32 /**
33 * The parent path which is currently being scanned
34 * Can be either plugins, mu_plugins, themes, uploads or other
35 * Where other means base wp-content directory but skipping plugins, mu_plugins, themes and uploads as they are handle separately
36 * @var string
37 */
38 protected $currentPathScanning = '';
39
40 /**
41 * The root path of the site
42 * @var string
43 */
44 protected $rootPath = '';
45
46 /**
47 * The content path of the site
48 * @var string
49 */
50 protected $contentPath = '';
51
52 /** @var bool */
53 protected $skipFiles = false;
54
55 /** @var bool */
56 protected $skipDirectories = false;
57
58 /** @var array */
59 protected $excludeRules = [];
60
61 /**
62 * @param Directory $directory
63 * @param PathIdentifier $pathIdentifier
64 * @param Filesystem $filesystem
65 * @param PluginInfo $pluginInfo
66 */
67 public function __construct(
68 Directory $directory,
69 PathIdentifier $pathIdentifier,
70 Filesystem $filesystem,
71 PluginInfo $pluginInfo
72 ) {
73 $this->directory = $directory;
74 $this->filesystem = $filesystem;
75 $this->pathIdentifier = $pathIdentifier;
76 $this->pluginInfo = $pluginInfo;
77 $this->rootPath = ABSPATH;
78 $this->contentPath = WP_CONTENT_DIR;
79 }
80
81 /**
82 * @param string $currentPathScanning
83 * @return void
84 */
85 public function setCurrentPathScanning(string $currentPathScanning)
86 {
87 $this->currentPathScanning = $currentPathScanning;
88 }
89
90 /**
91 * @param string $rootPath
92 * @return void
93 */
94 public function setRootPath(string $rootPath)
95 {
96 $this->rootPath = $rootPath;
97 }
98
99 /**
100 * @param string $contentPath
101 * @return void
102 */
103 public function setContentPath(string $contentPath)
104 {
105 $this->contentPath = $contentPath;
106 }
107
108 /**
109 * @return void
110 */
111 public function setOnlyFiles()
112 {
113 $this->skipFiles = false;
114 $this->skipDirectories = true;
115 }
116
117 /**
118 * @return void
119 */
120 public function setOnlyDirectories()
121 {
122 $this->skipFiles = true;
123 $this->skipDirectories = false;
124 }
125
126 public function resetFilesDirectoriesSkipping()
127 {
128 $this->skipFiles = false;
129 $this->skipDirectories = false;
130 }
131
132 /**
133 * @param array $excludeRules
134 * @return void
135 */
136 public function setExcludeRules(array $excludeRules)
137 {
138 $this->excludeRules = $excludeRules;
139 }
140
141 /**
142 * @param string $excludeRule
143 * @return void
144 */
145 public function addExcludeRule(string $excludeRule)
146 {
147 $this->excludeRules[] = $excludeRule;
148 }
149
150 /**
151 * @param string $directory
152 * @param bool $processLinks
153 * @param bool $scanLinkDirectory
154 * @return void
155 */
156 public function preScanPath(string $directory, bool $processLinks = false, bool $scanLinkDirectory = true)
157 {
158 $iterator = (new FilterableDirectoryIterator())
159 ->setDirectory(trailingslashit($directory))
160 ->setRecursive(false)
161 ->setSkipDirectoriesWithIncludeRules()
162 ->setDotSkip()
163 ->setWpRootPath($this->rootPath)
164 ->setExcludePaths($this->excludeRules)
165 ->get();
166
167 /** @var SplFileInfo $item */
168 foreach ($iterator as $item) {
169 if ($item->isLink() && $processLinks) {
170 $this->processLink($item, $scanLinkDirectory);
171 }
172
173 if ($item->isLink()) {
174 continue;
175 }
176
177 if ($item->isFile() && !$this->skipFiles) {
178 $this->processFile($item);
179 }
180
181 if ($item->isFile()) {
182 continue;
183 }
184
185 if ($this->skipDirectories) {
186 continue;
187 }
188
189 if ($item->isDir()) {
190 $this->processDirectory($item);
191 }
192 }
193 }
194
195 /**
196 * @param string $path
197 * @return void
198 * @throws Exception
199 */
200 protected function processPath(string $path)
201 {
202 $path = $this->replacePlaceholdersWithEOLs($path);
203 if (empty($path)) {
204 return;
205 }
206
207 list($path, $linkPath) = $this->resolvePath($path);
208
209 $path = untrailingslashit($this->filesystem->normalizePath($path, true));
210
211 if (!file_exists($path)) {
212 throw new Exception("$path is not a directory. Skipping...");
213 }
214
215 $this->preRecursivePathScanningStep();
216 $this->recursivePathScanning($path, $linkPath);
217 }
218
219 /**
220 * @return void
221 */
222 abstract protected function preRecursivePathScanningStep();
223
224 /**
225 * @param SplFileInfo $fileInfo
226 * @param string $linkPath
227 * @return void
228 */
229 abstract protected function processFile(SplFileInfo $fileInfo, string $linkPath = '');
230
231 /**
232 * @param SplFileInfo $fileInfo
233 * @param ?SplFileInfo $linkInfo
234 * @return void
235 */
236 abstract protected function processDirectory(SplFileInfo $fileInfo, $linkInfo = null);
237
238 /**
239 * @param SplFileInfo $linkInfo
240 * @param bool $scanDirectory
241 * @return void
242 */
243 protected function processLink(SplFileInfo $linkInfo, bool $scanDirectory = true)
244 {
245 // Bail if no link
246 if (!$linkInfo->isLink()) {
247 return;
248 }
249
250 $linkTarget = $linkInfo->getRealPath();
251 $fileInfo = new SplFileInfo($linkTarget);
252 if ($fileInfo->isLink()) {
253 return;
254 }
255
256 if ($fileInfo->isFile()) {
257 $this->processFile($fileInfo, $linkInfo->getPathname());
258 return;
259 }
260
261 if ($fileInfo->isDir() && $scanDirectory) {
262 $this->processDirectory($fileInfo, $linkInfo);
263 return;
264 }
265 }
266
267 /**
268 * Resolve path on non-wp.com sites (sites with no symlinks structure) to [base_directory, path, '']
269 * Resolve path on wp.com sites (sites with symlinks structure) to [base_directory, path, link]
270 * Where base_directory can be either plugins, mu_plugins, themes, uploads or other etc
271 * Where path is the path to scan
272 * Where link is the link to path (empty in case of non-wp.com sites)
273 * @param string $pathToResolve - Path to resolve in format base_directory::path::link or base_directory::path
274 * @return array [string pathToScan, string linkToPath]
275 */
276 protected function resolvePath(string $pathToResolve): array
277 {
278 $linkPath = '';
279 $pathInfos = explode(self::PATH_SEPARATOR, $pathToResolve);
280 // On non-wp.com sites, we don't have link, we only have base directory and path to scan
281 // On wp.com sites, we have base directory, path to scan and link to path, so path info contains 3 elements
282 if (count($pathInfos) > 2) {
283 // link to path
284 $linkPath = $pathInfos[2];
285 }
286
287 // base directory
288 $this->currentPathScanning = $pathInfos[0];
289
290 // path to scan
291 $path = $pathInfos[1];
292
293 return [$path, $linkPath];
294 }
295
296 /**
297 * @param string $path - Path to scan
298 * @param string $link - If original $path is resolved from link, then this is the link
299 * We need it to keep original path after restore
300 * e.g. $link = /var/www/html/wp-content/themes/twentytwenty is a link to /var/www/libs/themes/twentytwenty (a $path)
301 * @return void
302 * @throws FilesystemExceptions
303 */
304 protected function recursivePathScanning(string $path, string $link = '')
305 {
306 $iterator = (new FilterableDirectoryIterator())
307 ->setDirectory(trailingslashit($path))
308 ->setRecursive(false)
309 ->setDotSkip()
310 ->setWpRootPath($this->rootPath)
311 ->get();
312
313 /** @var SplFileInfo $item */
314 foreach ($iterator as $item) {
315 // Always check link first otherwise it may be treated as directory
316 if ($item->isLink()) {
317 continue;
318 }
319
320 $linkPath = '';
321 if (!empty($link)) {
322 $linkPath = trailingslashit($link) . $item->getFilename();
323 }
324
325 if ($item->isDir()) {
326 $this->recursivePathScanning($item->getPathname(), $linkPath);
327 continue;
328 }
329
330 if ($item->isFile()) {
331 $this->processFile($item, $linkPath);
332 }
333 }
334 }
335 }
336