PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.0.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.0.0
4.9.3 4.9.2 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 / PathIdentifier.php
wp-staging / Framework / Filesystem Last commit date
Filters 1 year ago Scanning 5 years ago AbstractFileObject.php 1 year ago DebugLogReader.php 2 years ago DirectoryListing.php 2 years ago DiskWriteCheck.php 1 year ago FileObject.php 1 year ago Filesystem.php 1 year ago FilesystemExceptions.php 5 years ago FilterableDirectoryIterator.php 2 years ago LogCleanup.php 2 years ago LogFiles.php 2 years ago MissingFileException.php 3 years ago OPcache.php 2 years ago PartIdentifier.php 1 year ago PathChecker.php 2 years ago PathIdentifier.php 1 year ago Permissions.php 1 year ago WpUploadsFolderSymlinker.php 5 years ago
PathIdentifier.php
302 lines
1 <?php
2
3 namespace WPStaging\Framework\Filesystem;
4
5 use WPStaging\Framework\Adapter\Directory;
6 use WPStaging\Framework\Adapter\DirectoryInterface;
7
8 /**
9 * This class is used to shorten the full file path
10 * to reduce the overall file size of the backup file.
11 *
12 * A file like wp-content/uploads/wp-staging-pro/wp-staging-pro.zip turn into
13 * wpstg_p_/wp-staging-pro/wp-staging.zip
14 *
15 * @todo rename this class to PathShortener
16 */
17
18 class PathIdentifier
19 {
20 /** @var string */
21 const IDENTIFIER_ABSPATH = 'wpstg_a_';
22
23 /** @var string */
24 const IDENTIFIER_WP_CONTENT = 'wpstg_c_';
25
26 /** @var string */
27 const IDENTIFIER_PLUGINS = 'wpstg_p_';
28
29 /** @var string */
30 const IDENTIFIER_THEMES = 'wpstg_t_';
31
32 /** @var string */
33 const IDENTIFIER_MUPLUGINS = 'wpstg_m_';
34
35 /** @var string */
36 const IDENTIFIER_UPLOADS = 'wpstg_u_';
37
38 /** @var string */
39 const IDENTIFIER_LANG = 'wpstg_l_';
40
41 /**
42 * @var string The identifier of the last match.
43 * We will try to match the path/identifier of the next item starting from this one. It's a form of cache,
44 * making it more efficient to transform long lists of similar paths.
45 */
46 protected $lastIdentifier;
47
48 /** @var DirectoryInterface */
49 protected $directory;
50
51 public function __construct(DirectoryInterface $directory)
52 {
53 $this->directory = $directory;
54 }
55
56 /** @var string */
57 public function getBackupDirectory()
58 {
59 return $this->directory->getBackupDirectory();
60 }
61
62 /**
63 * Convert an absolute file path of a file into an abbreviated path.
64 *
65 * E.g.:
66 *
67 * /var/www/single/wp-content/plugins/index.php => wpstg_p_index.php
68 * /var/www/single/wp-content/mu-plugins/index.php => wpstg_m_index.php
69 * /var/www/single/wp-content/uploads/2019/image.png => wpstg_c_uploads/2019/image.png
70 * /var/www/single/wp-content/themes/twentytwentyone/index.php => wpstg_t_twentytwentyone/index.php
71 *
72 * @param string $path /var/www/single/wp-content/plugins/index.php
73 *
74 * @return string wpstg_p_index.php
75 */
76 public function transformPathToIdentifiable($path)
77 {
78 // Start looking from the same placeholder as the last item, unless it was wp-content, which would cause false-positives.
79 if (isset($this->lastIdentifier) && $this->lastIdentifier !== self::IDENTIFIER_WP_CONTENT) {
80 $basePath = $this->getIdentifierPath($this->lastIdentifier);
81
82 // Early bail: This item has the same type as the previous one.
83 if (strpos($path, $basePath) === 0) {
84 return $this->lastIdentifier . substr($path, strlen($basePath));
85 }
86 }
87
88 // Uploads are usually the largest folders, so let's start with them.
89 if (strpos($path, $this->directory->getUploadsDirectory()) === 0) {
90 $this->lastIdentifier = self::IDENTIFIER_UPLOADS;
91
92 return $this->lastIdentifier . substr($path, strlen($this->directory->getUploadsDirectory()));
93 }
94
95 if ($this->directory->getPluginUploadsDirectory() !== $this->directory->getUploadsDirectory()) {
96 if (strpos($path, $this->directory->getPluginUploadsDirectory()) === 0) {
97 $this->lastIdentifier = self::IDENTIFIER_UPLOADS;
98
99 return $this->lastIdentifier . substr($path, strlen($this->directory->getPluginUploadsDirectory()));
100 }
101 }
102
103 if (strpos($path, $this->directory->getPluginsDirectory()) === 0) {
104 $this->lastIdentifier = self::IDENTIFIER_PLUGINS;
105
106 return $this->lastIdentifier . substr($path, strlen($this->directory->getPluginsDirectory()));
107 }
108
109 foreach ($this->directory->getAllThemesDirectories() as $themesDirectory) {
110 if (strpos($path, $themesDirectory) === 0) {
111 $this->lastIdentifier = self::IDENTIFIER_THEMES;
112
113 return $this->lastIdentifier . substr($path, strlen($themesDirectory));
114 }
115 }
116
117 if (strpos($path, $this->directory->getMuPluginsDirectory()) === 0) {
118 $this->lastIdentifier = self::IDENTIFIER_MUPLUGINS;
119
120 return $this->lastIdentifier . substr($path, strlen($this->directory->getMuPluginsDirectory()));
121 }
122
123 if (strpos($path, $this->directory->getLangsDirectory()) === 0) {
124 $this->lastIdentifier = self::IDENTIFIER_LANG;
125
126 return $this->lastIdentifier . substr($path, strlen($this->directory->getLangsDirectory()));
127 }
128
129 if (strpos($path, $this->directory->getWpContentDirectory()) === 0) {
130 $this->lastIdentifier = self::IDENTIFIER_WP_CONTENT;
131
132 return $this->lastIdentifier . substr($path, strlen($this->directory->getWpContentDirectory()));
133 }
134
135 if (strpos($path, $this->directory->getAbspath()) === 0) {
136 $this->lastIdentifier = self::IDENTIFIER_ABSPATH;
137
138 return $this->lastIdentifier . substr($path, strlen($this->directory->getAbspath()));
139 }
140
141 // This should never happen on Backups, as we only scan the folders above explicitly and don't follow links.
142 throw new \RuntimeException("Unknown entity type for path: $path");
143 }
144
145 /**
146 * @param string $path wpstg_p_index.php
147 *
148 * @return string /var/www/single/wp-content/plugins/index.php
149 */
150 public function transformIdentifiableToPath($path)
151 {
152 $identifier = $this->getIdentifierFromPath($path);
153 $pathWithoutIdentifier = $this->getPathWithoutIdentifier($path);
154
155 return $this->getIdentifierPath($identifier) . $pathWithoutIdentifier;
156 }
157
158 /**
159 * @param string $path wpstg_p_index.php
160 *
161 * @return string index.php
162 */
163 public function getPathWithoutIdentifier($path)
164 {
165 return substr($path, 8);
166 }
167
168 /**
169 * @param string $path wpstg_p_index.php
170 *
171 * @return string wpstg_p_
172 */
173 public function getIdentifierFromPath($path)
174 {
175 return substr($path, 0, 8);
176 }
177
178 /**
179 * @return string
180 */
181 public function transformIdentifiableToRelativePath(string $string): string
182 {
183 $key = substr($string, 0, 8);
184 $path = $this->getRelativePath($key);
185 if (!empty($path) && is_string($path)) {
186 return substr_replace($string, $path, 0, 8);
187 }
188
189 return $string;
190 }
191
192 /**
193 * @return string
194 */
195 public function getRelativePath(string $identifier): string
196 {
197 static $cache = [];
198
199 if (!empty($cache) && !empty($identifier) && isset($cache[$identifier])) {
200 return $cache[$identifier];
201 }
202
203 $path = [
204 self::IDENTIFIER_ABSPATH => '',
205 self::IDENTIFIER_WP_CONTENT => 'wp-content/',
206 self::IDENTIFIER_PLUGINS => 'wp-content/plugins/',
207 self::IDENTIFIER_THEMES => 'wp-content/themes/',
208 self::IDENTIFIER_MUPLUGINS => 'wp-content/mu-plugins/',
209 self::IDENTIFIER_UPLOADS => 'wp-content/uploads/',
210 self::IDENTIFIER_LANG => 'wp-content/languages/',
211 ];
212
213 if (!empty($identifier) && isset($path[$identifier])) {
214 $cache[$identifier] = $path[$identifier];
215 return $cache[$identifier];
216 }
217
218 // Add __METHOD__ for debugging in wpstg-restore
219 trigger_error(sprintf('[%s] Could not find a path for the placeholder: %s', __METHOD__, filter_var($identifier, FILTER_SANITIZE_SPECIAL_CHARS)));
220 return $identifier;
221 }
222
223 public function getAbsolutePath(string $identifier): string
224 {
225 return $this->getIdentifierPath($identifier);
226 }
227
228 /**
229 * @return string
230 */
231 public function getIdentifierByPartName(string $key): string
232 {
233 static $cache = [];
234
235 if (!empty($cache) && !empty($key) && !empty($cache[$key])) {
236 return $cache[$key];
237 }
238
239 $list = [
240 PartIdentifier::WP_CONTENT_PART_IDENTIFIER => PathIdentifier::IDENTIFIER_WP_CONTENT,
241 PartIdentifier::PLUGIN_PART_IDENTIFIER => PathIdentifier::IDENTIFIER_PLUGINS,
242 PartIdentifier::THEME_PART_IDENTIFIER => PathIdentifier::IDENTIFIER_THEMES,
243 PartIdentifier::MU_PLUGIN_PART_IDENTIFIER => PathIdentifier::IDENTIFIER_MUPLUGINS,
244 PartIdentifier::UPLOAD_PART_IDENTIFIER => PathIdentifier::IDENTIFIER_UPLOADS,
245 PartIdentifier::LANGUAGE_PART_IDENTIFIER => PathIdentifier::IDENTIFIER_LANG,
246 PartIdentifier::DATABASE_PART_IDENTIFIER => PathIdentifier::IDENTIFIER_UPLOADS,
247 PartIdentifier::WP_ROOT_PART_IDENTIFIER => PathIdentifier::IDENTIFIER_ABSPATH,
248 ];
249
250 if (!empty($key) && !empty($list[$key])) {
251 $cache[$key] = $list[$key];
252 return $cache[$key];
253 }
254
255 return '';
256 }
257
258 /**
259 * @param string $identifier wpstg_p_
260 *
261 * @return string /var/www/single/wp-content/plugins/
262 */
263 protected function getIdentifierPath($identifier)
264 {
265 // It is crucial that generic paths are placed last in this list. Eg: wp-content directory must be last.
266 switch ($identifier) {
267 case self::IDENTIFIER_ABSPATH:
268 return $this->directory->getAbspath();
269 case self::IDENTIFIER_UPLOADS:
270 return $this->directory->getUploadsDirectory();
271 case self::IDENTIFIER_PLUGINS:
272 return $this->directory->getPluginsDirectory();
273 case self::IDENTIFIER_THEMES:
274 return $this->directory->getActiveThemeParentDirectory();
275 case self::IDENTIFIER_MUPLUGINS:
276 return $this->directory->getMuPluginsDirectory();
277 case self::IDENTIFIER_LANG:
278 return $this->directory->getLangsDirectory();
279 case self::IDENTIFIER_WP_CONTENT:
280 return $this->directory->getWpContentDirectory();
281 default:
282 throw new \UnexpectedValueException(sprintf("[%s] Could not find a path for the placeholder: %s", __METHOD__, filter_var($identifier, FILTER_SANITIZE_SPECIAL_CHARS)));
283 }
284 }
285
286 /**
287 * @param string $identifiablePath wpstg_p_db.php
288 *
289 * @return bool
290 */
291 public function hasDropinsFile(string $identifiablePath): bool
292 {
293 if (!(strpos($identifiablePath, self::IDENTIFIER_WP_CONTENT) === 0)) {
294 return false;
295 }
296
297 $dropinsFile = implode('|', PartIdentifier::DROP_IN_FILES);
298
299 return preg_match('@^' . self::IDENTIFIER_WP_CONTENT . '(' . $dropinsFile . ')@', $identifiablePath) ? true : false;
300 }
301 }
302