PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.5.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.5.0
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 1 year 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 6 months ago FilesystemScannerDto.php 1 year ago FilterableDirectoryIterator.php 1 year 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 5 months ago WpUploadsFolderSymlinker.php 4 years ago
AbstractFilesystemScanner.php
320 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 /** @var bool */
47 protected $skipFiles = false;
48
49 /** @var bool */
50 protected $skipDirectories = false;
51
52 /** @var array */
53 protected $excludeRules = [];
54
55 /**
56 * @param Directory $directory
57 * @param PathIdentifier $pathIdentifier
58 * @param Filesystem $filesystem
59 * @param PluginInfo $pluginInfo
60 */
61 public function __construct(
62 Directory $directory,
63 PathIdentifier $pathIdentifier,
64 Filesystem $filesystem,
65 PluginInfo $pluginInfo
66 ) {
67 $this->directory = $directory;
68 $this->filesystem = $filesystem;
69 $this->pathIdentifier = $pathIdentifier;
70 $this->pluginInfo = $pluginInfo;
71 $this->rootPath = ABSPATH;
72 }
73
74 /**
75 * @param string $currentPathScanning
76 * @return void
77 */
78 public function setCurrentPathScanning(string $currentPathScanning)
79 {
80 $this->currentPathScanning = $currentPathScanning;
81 }
82
83 /**
84 * @param string $rootPath
85 * @return void
86 */
87 public function setRootPath(string $rootPath)
88 {
89 $this->rootPath = $rootPath;
90 }
91
92 /**
93 * @return void
94 */
95 public function setOnlyFiles()
96 {
97 $this->skipFiles = false;
98 $this->skipDirectories = true;
99 }
100
101 /**
102 * @return void
103 */
104 public function setOnlyDirectories()
105 {
106 $this->skipFiles = true;
107 $this->skipDirectories = false;
108 }
109
110 public function resetFilesDirectoriesSkipping()
111 {
112 $this->skipFiles = false;
113 $this->skipDirectories = false;
114 }
115
116 /**
117 * @param array $excludeRules
118 * @return void
119 */
120 public function setExcludeRules(array $excludeRules)
121 {
122 $this->excludeRules = $excludeRules;
123 }
124
125 /**
126 * @param string $excludeRule
127 * @return void
128 */
129 public function addExcludeRule(string $excludeRule)
130 {
131 $this->excludeRules[] = $excludeRule;
132 }
133
134 /**
135 * @param string $directory
136 * @param bool $processLinks
137 * @param bool $scanLinkDirectory
138 * @return void
139 */
140 public function preScanPath(string $directory, bool $processLinks = false, bool $scanLinkDirectory = true)
141 {
142 $iterator = (new FilterableDirectoryIterator())
143 ->setDirectory(trailingslashit($directory))
144 ->setRecursive(false)
145 ->setSkipDirectoriesWithIncludeRules()
146 ->setDotSkip()
147 ->setWpRootPath($this->rootPath)
148 ->setExcludePaths($this->excludeRules)
149 ->get();
150
151 /** @var SplFileInfo $item */
152 foreach ($iterator as $item) {
153 if ($item->isLink() && $processLinks) {
154 $this->processLink($item, $scanLinkDirectory);
155 }
156
157 if ($item->isLink()) {
158 continue;
159 }
160
161 if ($item->isFile() && !$this->skipFiles) {
162 $this->processFile($item);
163 }
164
165 if ($item->isFile()) {
166 continue;
167 }
168
169 if ($this->skipDirectories) {
170 continue;
171 }
172
173 if ($item->isDir()) {
174 $this->processDirectory($item);
175 }
176 }
177 }
178
179 /**
180 * @param string $path
181 * @return void
182 * @throws Exception
183 */
184 protected function processPath(string $path)
185 {
186 $path = $this->replacePlaceholdersWithEOLs($path);
187 if (empty($path)) {
188 return;
189 }
190
191 list($path, $linkPath) = $this->resolvePath($path);
192
193 $path = untrailingslashit($this->filesystem->normalizePath($path, true));
194
195 if (!file_exists($path)) {
196 throw new Exception("$path is not a directory. Skipping...");
197 }
198
199 $this->preRecursivePathScanningStep();
200 $this->recursivePathScanning($path, $linkPath);
201 }
202
203 /**
204 * @return void
205 */
206 abstract protected function preRecursivePathScanningStep();
207
208 /**
209 * @param SplFileInfo $fileInfo
210 * @param string $linkPath
211 * @return void
212 */
213 abstract protected function processFile(SplFileInfo $fileInfo, string $linkPath = '');
214
215 /**
216 * @param SplFileInfo $fileInfo
217 * @param ?SplFileInfo $linkInfo
218 * @return void
219 */
220 abstract protected function processDirectory(SplFileInfo $fileInfo, $linkInfo = null);
221
222 /**
223 * @param SplFileInfo $linkInfo
224 * @param bool $scanDirectory
225 * @return void
226 */
227 protected function processLink(SplFileInfo $linkInfo, bool $scanDirectory = true)
228 {
229 // Bail if no link
230 if (!$linkInfo->isLink()) {
231 return;
232 }
233
234 $linkTarget = $linkInfo->getRealPath();
235 $fileInfo = new SplFileInfo($linkTarget);
236 if ($fileInfo->isLink()) {
237 return;
238 }
239
240 if ($fileInfo->isFile()) {
241 $this->processFile($fileInfo, $linkInfo->getPathname());
242 return;
243 }
244
245 if ($fileInfo->isDir() && $scanDirectory) {
246 $this->processDirectory($fileInfo, $linkInfo);
247 return;
248 }
249 }
250
251 /**
252 * Resolve path on non-wp.com sites (sites with no symlinks structure) to [base_directory, path, '']
253 * Resolve path on wp.com sites (sites with symlinks structure) to [base_directory, path, link]
254 * Where base_directory can be either plugins, mu_plugins, themes, uploads or other etc
255 * Where path is the path to scan
256 * Where link is the link to path (empty in case of non-wp.com sites)
257 * @param string $pathToResolve - Path to resolve in format base_directory::path::link or base_directory::path
258 * @return array [string pathToScan, string linkToPath]
259 */
260 protected function resolvePath(string $pathToResolve): array
261 {
262 $linkPath = '';
263 $pathInfos = explode(self::PATH_SEPARATOR, $pathToResolve);
264 // On non-wp.com sites, we don't have link, we only have base directory and path to scan
265 // On wp.com sites, we have base directory, path to scan and link to path, so path info contains 3 elements
266 if (count($pathInfos) > 2) {
267 // link to path
268 $linkPath = $pathInfos[2];
269 }
270
271 // base directory
272 $this->currentPathScanning = $pathInfos[0];
273
274 // path to scan
275 $path = $pathInfos[1];
276
277 return [$path, $linkPath];
278 }
279
280 /**
281 * @param string $path - Path to scan
282 * @param string $link - If original $path is resolved from link, then this is the link
283 * We need it to keep original path after restore
284 * e.g. $link = /var/www/html/wp-content/themes/twentytwenty is a link to /var/www/libs/themes/twentytwenty (a $path)
285 * @return void
286 * @throws FilesystemExceptions
287 */
288 protected function recursivePathScanning(string $path, string $link = '')
289 {
290 $iterator = (new FilterableDirectoryIterator())
291 ->setDirectory(trailingslashit($path))
292 ->setRecursive(false)
293 ->setDotSkip()
294 ->setWpRootPath($this->rootPath)
295 ->get();
296
297 /** @var SplFileInfo $item */
298 foreach ($iterator as $item) {
299 // Always check link first otherwise it may be treated as directory
300 if ($item->isLink()) {
301 continue;
302 }
303
304 $linkPath = '';
305 if (!empty($link)) {
306 $linkPath = trailingslashit($link) . $item->getFilename();
307 }
308
309 if ($item->isDir()) {
310 $this->recursivePathScanning($item->getPathname(), $linkPath);
311 continue;
312 }
313
314 if ($item->isFile()) {
315 $this->processFile($item, $linkPath);
316 }
317 }
318 }
319 }
320