PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta
16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / vendor / wp-php-toolkit / reprint-exporter / src / utils.php
jetpack / vendor / wp-php-toolkit / reprint-exporter / src Last commit date
class-file-tree-producer.php 6 days ago class-hmac-client.php 6 days ago class-hmac-server.php 6 days ago class-http-server.php 6 days ago class-mysql-dump-producer.php 6 days ago class-pdo-polyfill.php 6 days ago class-sqlite-driver-pdo.php 6 days ago class-staged-artifacts.php 6 days ago class-staged-endpoints.php 6 days ago class-staged-push-stream-protocol.php 6 days ago class-wpdb-driver-pdo.php 6 days ago export.php 6 days ago utils.php 6 days ago
utils.php
230 lines
1 <?php
2 /**
3 * Shared utility functions used by both export.php and import.php.
4 *
5 * These helpers live in a namespace so they don't collide with global
6 * functions of the same name declared by third-party plugins or
7 * WordPress drop-ins. The package is loaded via Composer's "files"
8 * autoload, which means every host that pulls in this library (e.g.
9 * wpcomsh on WordPress.com) gets these symbols on every request —
10 * generic names like parse_size() or normalize_path() are guaranteed
11 * to clash sooner or later if they sit in the global namespace.
12 *
13 * The two str_* polyfills at the top stay global on purpose: they
14 * backfill PHP 7.4 built-ins, so callers expect to reach them via
15 * the global namespace without a use-statement.
16 */
17
18 // Polyfill for PHP 7.4 which lacks str_starts_with().
19 namespace {
20 if (!function_exists('str_starts_with')) {
21 function str_starts_with(string $haystack, string $needle): bool {
22 return $needle === '' || strncmp($haystack, $needle, strlen($needle)) === 0;
23 }
24 }
25
26 // Polyfill for PHP 7.4 which lacks str_contains().
27 if (!function_exists('str_contains')) {
28 function str_contains(string $haystack, string $needle): bool {
29 return $needle === '' || strpos($haystack, $needle) !== false;
30 }
31 }
32 }
33
34 namespace WordPress\Reprint\Exporter {
35
36 use InvalidArgumentException;
37 use RuntimeException;
38
39 // Composer's "files" autoload includes this file once per registered
40 // path. In a monorepo where the same package is mirrored into vendor/
41 // (e.g. tests/ pulls in vendor/wp-php-toolkit/reprint-exporter/src/utils.php
42 // AND packages/reprint-exporter/src/utils.php), both copies are loaded.
43 // `return` from inside a bracketed namespace block does not abort the
44 // whole file, so guard the declarations themselves.
45 if (!function_exists(__NAMESPACE__ . '\\build_pdo_dsn')) {
46
47 /**
48 * Builds a PDO DSN string from a WordPress DB_HOST value.
49 *
50 * WordPress's DB_HOST supports several non-standard formats that shared
51 * hosts commonly use:
52 * - "localhost" → standard hostname
53 * - "db.host.com:3307" → hostname with port
54 * - "localhost:/path/sock" → hostname with Unix socket
55 * - "/path/to/mysql.sock" → bare Unix socket path
56 * - "::1" → IPv6 address
57 * - "[::1]" → bracketed IPv6
58 * - "[::1]:3306" → bracketed IPv6 with port
59 * - "[::1]:/path/to/socket" → bracketed IPv6 with Unix socket
60 *
61 * PDO needs these broken out into separate DSN parameters (host, port,
62 * unix_socket), so we parse the value the same way WordPress core does.
63 *
64 * @param string $db_host Raw DB_HOST value.
65 * @param string $db_name Database name.
66 * @return string PDO DSN string.
67 */
68 function build_pdo_dsn(string $db_host, string $db_name): string
69 {
70 $socket = '';
71 $host = $db_host;
72 $port = '';
73
74 if (str_starts_with($db_host, '/') && file_exists($db_host)) {
75 // Bare socket path: "/var/run/mysqld/mysqld.sock"
76 $socket = $db_host;
77 $host = '';
78 } elseif (
79 str_starts_with($db_host, '[') &&
80 ($bracket_end = strpos($db_host, ']')) !== false
81 ) {
82 // Bracketed IPv6: "[::1]", "[::1]:3306", "[::1]:/path/to/socket"
83 $host = substr($db_host, 1, $bracket_end - 1);
84 $after = substr($db_host, $bracket_end + 1);
85 $candidate_socket = str_starts_with($after, ':/') ? substr($after, 1) : '';
86 if ($candidate_socket !== '' && file_exists($candidate_socket)) {
87 $socket = $candidate_socket;
88 } elseif (str_starts_with($after, ':')) {
89 $port = substr($after, 1);
90 }
91 } elseif (($socket_pos = strpos($db_host, ':/')) !== false) {
92 // "host:/path/to/socket" — check before general colon split
93 // to avoid misinterpreting IPv6 addresses as host:port
94 $candidate_socket = substr($db_host, $socket_pos + 1);
95 if (file_exists($candidate_socket)) {
96 $host = substr($db_host, 0, $socket_pos);
97 $socket = $candidate_socket;
98 } elseif (substr_count($db_host, ':') === 1) {
99 // Single colon but not a socket — treat as host:port
100 [$host, $port] = explode(':', $db_host, 2);
101 }
102 } elseif (
103 str_contains($db_host, ':') &&
104 substr_count($db_host, ':') === 1
105 ) {
106 // Exactly one colon: "host:port" — not IPv6
107 [$host, $port] = explode(':', $db_host, 2);
108 }
109 // Otherwise (multiple colons, no socket marker): bare IPv6 like "::1"
110 // — $host stays as the full value.
111
112 if ($socket !== '') {
113 return "mysql:unix_socket={$socket};dbname={$db_name};charset=utf8mb4";
114 }
115
116 $dsn = "mysql:host={$host}";
117 if ($port !== '') {
118 $dsn .= ";port={$port}";
119 }
120 $dsn .= ";dbname={$db_name};charset=utf8mb4";
121 return $dsn;
122 }
123
124 /**
125 * Parse a human-readable size string (e.g. "16M", "1G", "512K") into bytes.
126 * Accepts plain integers as well.
127 */
128 function parse_size(string $value): int
129 {
130 $value = trim($value);
131 if (!preg_match('/^(\d+(?:\.\d+)?)\s*([KMGkmg])?[Bb]?$/', $value, $m)) {
132 throw new InvalidArgumentException(
133 "Invalid size value: '{$value}'. Use a number optionally followed by K, M, or G (e.g. 64M)."
134 );
135 }
136 $num = (float) $m[1];
137 $suffix = strtoupper($m[2] ?? "");
138 switch ($suffix) {
139 case "K":
140 return (int) ($num * 1024);
141 case "M":
142 return (int) ($num * 1024 * 1024);
143 case "G":
144 return (int) ($num * 1024 * 1024 * 1024);
145 default:
146 return (int) $num;
147 }
148 }
149
150 /**
151 * Throws on json_encode failure instead of returning false.
152 *
153 * Do NOT use inside error/shutdown handlers — those need hardcoded fallback strings.
154 */
155 function json_encode_or_throw($value, int $flags = 0): string
156 {
157 $json = json_encode($value, $flags);
158 if ($json === false) {
159 throw new RuntimeException("json_encode failed: " . json_last_error_msg());
160 }
161 return $json;
162 }
163
164 /**
165 * Resolve ".." and "." segments in a path without touching the filesystem.
166 *
167 * Unlike realpath(), this works on paths that don't exist yet.
168 */
169 function normalize_path(string $path): string
170 {
171 $parts = explode("/", $path);
172 $resolved = [];
173 foreach ($parts as $part) {
174 if ($part === "" || $part === ".") {
175 continue;
176 }
177 if ($part === "..") {
178 array_pop($resolved);
179 } else {
180 $resolved[] = $part;
181 }
182 }
183 return "/" . implode("/", $resolved);
184 }
185
186 /**
187 * Returns true when $path is equal to $root or strictly under it.
188 */
189 function path_is_within_root(string $path, string $root): bool
190 {
191 return $path === $root || str_starts_with($path, $root . "/");
192 }
193
194 /**
195 * Validates that a path is a non-empty absolute string without NUL bytes
196 * or dot-segments (. or ..).
197 *
198 * Useful anywhere untrusted or remote paths need to be checked before
199 * use — both the exporter (directory config) and the importer (remote
200 * paths from the server) share this validation.
201 *
202 * @param string $path The path to validate.
203 * @param string $label Human-readable label for error messages (e.g. "directory", "remote path").
204 * @throws InvalidArgumentException When the path fails any check.
205 */
206 function assert_valid_path(string $path, string $label = "path"): void
207 {
208 $path = trim($path);
209 if ($path === "") {
210 throw new InvalidArgumentException("{$label} must be a non-empty string");
211 }
212 if ($path[0] !== "/") {
213 throw new InvalidArgumentException("{$label} must be an absolute path: {$path}");
214 }
215 if (strpos($path, "\0") !== false) {
216 throw new InvalidArgumentException("{$label} must not contain NUL bytes");
217 }
218 foreach (explode("/", $path) as $segment) {
219 if ($segment === "." || $segment === "..") {
220 throw new InvalidArgumentException(
221 "{$label} must not contain dot-segments (. or ..): {$path}"
222 );
223 }
224 }
225 }
226
227 } // !function_exists guard
228
229 }
230