@wp-playground/wordpress 3.1.1 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs +480 -349
- package/index.cjs.map +1 -1
- package/index.d.ts +1 -1
- package/index.js +659 -522
- package/index.js.map +1 -1
- package/package.json +9 -7
- package/wp-config.d.ts +24 -0
- package/rewrite-wp-config.d.ts +0 -22
package/index.js
CHANGED
|
@@ -1,458 +1,595 @@
|
|
|
1
|
-
import { phpVars as
|
|
2
|
-
import { createMemoizedFetch as P, unzipFile as
|
|
1
|
+
import { phpVars as $, joinPaths as s, dirname as y, basename as b, phpVar as d } from "@php-wasm/util";
|
|
2
|
+
import { createMemoizedFetch as P, unzipFile as p } from "@wp-playground/common";
|
|
3
3
|
import { logger as g } from "@php-wasm/logger";
|
|
4
|
-
import { sandboxedSpawnHandlerFactory as
|
|
5
|
-
const
|
|
4
|
+
import { sandboxedSpawnHandlerFactory as T, PHPRequestHandler as E, PHP as v, setPhpIniEntries as R, writeFiles as f, withPHPIniValues as S } from "@php-wasm/universal";
|
|
5
|
+
const m = `<?php
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
* with specific values.
|
|
10
|
-
*
|
|
11
|
-
* Example:
|
|
12
|
-
*
|
|
13
|
-
* \`\`\`php
|
|
14
|
-
* <?php
|
|
15
|
-
* define('WP_DEBUG', true);
|
|
16
|
-
* // The third define() argument is also supported:
|
|
17
|
-
* define('SAVEQUERIES', false, true);
|
|
18
|
-
*
|
|
19
|
-
* // Expression
|
|
20
|
-
* define(true ? 'WP_DEBUG_LOG' : 'WP_DEBUG_LOG', 123);
|
|
21
|
-
*
|
|
22
|
-
* // Guarded expressions shouldn't be wrapped twice
|
|
23
|
-
* if(!defined(1 ? 'A' : 'B')) {
|
|
24
|
-
* define(1 ? 'A' : 'B', 0);
|
|
25
|
-
* }
|
|
26
|
-
*
|
|
27
|
-
* // More advanced expression
|
|
28
|
-
* define((function() use($x) {
|
|
29
|
-
* return [$x, 'a'];
|
|
30
|
-
* })(), 123);
|
|
31
|
-
* \`\`\`
|
|
32
|
-
*
|
|
33
|
-
* Rewritten with
|
|
34
|
-
*
|
|
35
|
-
* $constants = [
|
|
36
|
-
* 'WP_DEBUG' => false,
|
|
37
|
-
* 'WP_DEBUG_LOG' => true,
|
|
38
|
-
* 'SAVEQUERIES' => true,
|
|
39
|
-
* 'NEW_CONSTANT' => "new constant",
|
|
40
|
-
* ];
|
|
41
|
-
*
|
|
42
|
-
* \`\`\`php
|
|
43
|
-
* <?php
|
|
44
|
-
* define('WP_DEBUG_LOG',true);
|
|
45
|
-
* define('NEW_CONSTANT','new constant');
|
|
46
|
-
* ?><?php
|
|
47
|
-
* define('WP_DEBUG',false);
|
|
48
|
-
* // The third define() argument is also supported:
|
|
49
|
-
* define('SAVEQUERIES',true, true);
|
|
50
|
-
*
|
|
51
|
-
* // Expression
|
|
52
|
-
* if(!defined($const ? 'WP_DEBUG_LOG' : 'WP_DEBUG_LOG')) {
|
|
53
|
-
* define($const ? 'WP_DEBUG_LOG' : 'WP_DEBUG_LOG', 123);
|
|
54
|
-
* }
|
|
55
|
-
*
|
|
56
|
-
* // Guarded expressions shouldn't be wrapped twice
|
|
57
|
-
* if(!defined(1 ? 'A' : 'B')) {
|
|
58
|
-
* define(1 ? 'A' : 'B', 0);
|
|
59
|
-
* }
|
|
60
|
-
*
|
|
61
|
-
* // More advanced expression
|
|
62
|
-
* if(!defined((function() use($x) {
|
|
63
|
-
* return [$x, 'a'];
|
|
64
|
-
* })())) {
|
|
65
|
-
* define((function() use($x) {
|
|
66
|
-
* return [$x, 'a'];
|
|
67
|
-
* })(), 123);
|
|
68
|
-
* }
|
|
69
|
-
* \`\`\`
|
|
8
|
+
* Transforms the "wp-config.php" file.
|
|
70
9
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* @param bool $when_already_defined Optional. What to do if the constant is already defined.
|
|
74
|
-
* Possible values are:
|
|
75
|
-
* 'rewrite' - Rewrite the constant, using the new value.
|
|
76
|
-
* 'skip' - Skip the definition, keeping the existing value.
|
|
77
|
-
* Default: 'rewrite'
|
|
78
|
-
* @return string
|
|
10
|
+
* This parses the "wp-config.php" file contents into a token array and provides
|
|
11
|
+
* methods to modify it and serialize it back to a string with the modifications.
|
|
79
12
|
*/
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
do {
|
|
88
|
-
$buffer = [];
|
|
89
|
-
$name_buffer = [];
|
|
90
|
-
$value_buffer = [];
|
|
91
|
-
$third_arg_buffer = [];
|
|
92
|
-
|
|
93
|
-
// Capture everything until the define call into output.
|
|
94
|
-
// Capturing the define call into a buffer.
|
|
95
|
-
// Example:
|
|
96
|
-
// <?php echo 'a'; define (
|
|
97
|
-
// ^^^^^^^^^^^^^^^^^^^^^^
|
|
98
|
-
// output |buffer
|
|
99
|
-
while ($token = array_pop($tokens)) {
|
|
100
|
-
if (is_array($token) && $token[0] === T_STRING && (strtolower($token[1]) === 'define' || strtolower($token[1]) === 'defined')) {
|
|
101
|
-
$buffer[] = $token;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
$output[] = $token;
|
|
105
|
-
}
|
|
13
|
+
class WP_Config_Transformer {
|
|
14
|
+
/**
|
|
15
|
+
* The tokens of the wp-config.php file.
|
|
16
|
+
*
|
|
17
|
+
* @var array<array|string>
|
|
18
|
+
*/
|
|
19
|
+
private $tokens;
|
|
106
20
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Constructor.
|
|
23
|
+
*
|
|
24
|
+
* @param string $content The contents of the wp-config.php file.
|
|
25
|
+
*/
|
|
26
|
+
public function __construct( string $content ) {
|
|
27
|
+
$this->tokens = token_get_all( $content );
|
|
111
28
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
29
|
+
// Check if the file is a valid PHP file.
|
|
30
|
+
$is_valid_php_file = false;
|
|
31
|
+
foreach ( $this->tokens as $token ) {
|
|
32
|
+
if ( is_array( $token ) && T_OPEN_TAG === $token[0] ) {
|
|
33
|
+
$is_valid_php_file = true;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if ( ! $is_valid_php_file ) {
|
|
38
|
+
throw new Exception( "The 'wp-config.php' file is not a valid PHP file." );
|
|
39
|
+
}
|
|
40
|
+
}
|
|
127
41
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a new config transformer instance from a file.
|
|
44
|
+
*
|
|
45
|
+
* @param string $path The path to the wp-config.php file.
|
|
46
|
+
* @return self The new config transformer instance.
|
|
47
|
+
*/
|
|
48
|
+
public static function from_file( string $path ): self {
|
|
49
|
+
if ( ! is_file( $path ) ) {
|
|
50
|
+
throw new Exception( sprintf( "The '%s' file does not exist.", $path ) );
|
|
51
|
+
}
|
|
52
|
+
return new self( file_get_contents( $path ) );
|
|
53
|
+
}
|
|
141
54
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Get the transformed wp-config.php file contents.
|
|
57
|
+
*
|
|
58
|
+
* @return string The transformed wp-config.php file contents.
|
|
59
|
+
*/
|
|
60
|
+
public function to_string(): string {
|
|
61
|
+
$output = '';
|
|
62
|
+
foreach ( $this->tokens as $token ) {
|
|
63
|
+
$output .= is_array( $token ) ? $token[1] : $token;
|
|
64
|
+
}
|
|
65
|
+
return $output;
|
|
66
|
+
}
|
|
145
67
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Save the transformed wp-config.php file contents to a file.
|
|
70
|
+
*
|
|
71
|
+
* @param string $path The path to the wp-config.php file.
|
|
72
|
+
*/
|
|
73
|
+
public function to_file( string $path ): void {
|
|
74
|
+
$result = file_put_contents( $path, $this->to_string() );
|
|
75
|
+
if ( false === $result ) {
|
|
76
|
+
throw new Exception( sprintf( "Failed to write to the '%s' file.", $path ) );
|
|
77
|
+
}
|
|
78
|
+
}
|
|
155
79
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Check if a constant is defined in the wp-config.php file.
|
|
82
|
+
*
|
|
83
|
+
* @param string $name The name of the constant.
|
|
84
|
+
* @return bool True if the constant is defined, false otherwise.
|
|
85
|
+
*/
|
|
86
|
+
public function constant_exists( string $name ): bool {
|
|
87
|
+
foreach ( $this->tokens as $i => $token ) {
|
|
88
|
+
$is_string_token = is_array( $token ) && T_STRING === $token[0];
|
|
89
|
+
if ( $is_string_token && 'define' === strtolower( $token[1] ) ) {
|
|
90
|
+
$args = $this->collect_function_call_argument_locations( $i );
|
|
91
|
+
$const_name = $this->evaluate_constant_name(
|
|
92
|
+
array_slice( $this->tokens, $args[0][0], $args[0][1] )
|
|
93
|
+
);
|
|
94
|
+
if ( $name === $const_name ) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
174
101
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Define a constant in the wp-config.php file.
|
|
104
|
+
*
|
|
105
|
+
* @param string $name The name of the constant.
|
|
106
|
+
* @param mixed $value The value of the constant.
|
|
107
|
+
*/
|
|
108
|
+
public function define_constant( string $name, $value ): void {
|
|
109
|
+
// Tokenize the new constant value for insertion in the tokens array.
|
|
110
|
+
$definition_tokens = token_get_all(
|
|
111
|
+
sprintf(
|
|
112
|
+
"<?php define( %s, %s );\\n",
|
|
113
|
+
var_export( $name, true ),
|
|
114
|
+
var_export( $value, true )
|
|
115
|
+
)
|
|
116
|
+
);
|
|
178
117
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// ^^^^^^
|
|
182
|
-
$open_parenthesis = 0;
|
|
183
|
-
$is_second_argument = true;
|
|
184
|
-
while ($token = array_pop($tokens)) {
|
|
185
|
-
$buffer[] = $token;
|
|
186
|
-
if ($token === ")" && $open_parenthesis === 0) {
|
|
187
|
-
// Final parenthesis of the define call.
|
|
188
|
-
break;
|
|
189
|
-
} else if ($token === "(" || $token === "[" || $token === "{") {
|
|
190
|
-
++$open_parenthesis;
|
|
191
|
-
} elseif ($token === ")" || $token === "]" || $token === "}") {
|
|
192
|
-
--$open_parenthesis;
|
|
193
|
-
} elseif ($token === "," && $open_parenthesis === 0) {
|
|
194
|
-
// This define call has more than 2 arguments! The third one is the
|
|
195
|
-
// boolean value indicating $is_case_insensitive. Let's continue capturing
|
|
196
|
-
// to $third_arg_buffer.
|
|
197
|
-
$is_second_argument = false;
|
|
198
|
-
}
|
|
199
|
-
if ($is_second_argument) {
|
|
200
|
-
$value_buffer[] = $token;
|
|
201
|
-
} else {
|
|
202
|
-
$third_arg_buffer[] = $token;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
118
|
+
// Full constant definition statement, e.g.: define( 'WP_DEBUG', true );\\n
|
|
119
|
+
$define_tokens = array_slice( $definition_tokens, 1 );
|
|
205
120
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
// ^^^
|
|
209
|
-
while ($token = array_pop($tokens)) {
|
|
210
|
-
$buffer[] = $token;
|
|
211
|
-
if ($token === ";") {
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
121
|
+
// The value of the constant, e.g.: "my-database-name"
|
|
122
|
+
$value_tokens = array_slice( $definition_tokens, 7, -4 );
|
|
215
123
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
$name_token = $token;
|
|
226
|
-
$name_token_index = $k;
|
|
227
|
-
} else {
|
|
228
|
-
$name_is_literal = false;
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
} else if ($token !== "(" && $token !== ")") {
|
|
232
|
-
$name_is_literal = false;
|
|
233
|
-
break;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
124
|
+
// Collect all locations where the constant value needs to be updated.
|
|
125
|
+
$updates = array();
|
|
126
|
+
foreach ( $this->tokens as $i => $token ) {
|
|
127
|
+
$is_string_token = is_array( $token ) && T_STRING === $token[0];
|
|
128
|
+
if ( $is_string_token && 'define' === strtolower( $token[1] ) ) {
|
|
129
|
+
$args = $this->collect_function_call_argument_locations( $i );
|
|
130
|
+
$const_name = $this->evaluate_constant_name(
|
|
131
|
+
array_slice( $this->tokens, $args[0][0], $args[0][1] )
|
|
132
|
+
);
|
|
236
133
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
foreach ($defined_expressions as $defined_expression) {
|
|
243
|
-
if ($defined_expression === stringify_tokens(skip_whitespace($name_buffer))) {
|
|
244
|
-
$output = array_merge($output, $buffer);
|
|
245
|
-
continue 2;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
$output = array_merge(
|
|
249
|
-
$output,
|
|
250
|
-
["if(!defined("],
|
|
251
|
-
$name_buffer,
|
|
252
|
-
[")) {\\n "],
|
|
253
|
-
['define('],
|
|
254
|
-
$name_buffer,
|
|
255
|
-
[','],
|
|
256
|
-
$value_buffer,
|
|
257
|
-
$third_arg_buffer,
|
|
258
|
-
[");"],
|
|
259
|
-
["\\n}\\n"]
|
|
260
|
-
);
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
134
|
+
if ( $name === $const_name ) {
|
|
135
|
+
$updates[] = $args[1];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
263
139
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
140
|
+
// Modify the token array to define the constant. Apply updates in reverse
|
|
141
|
+
// order, so splices at earlier positions don't shift indices after them.
|
|
142
|
+
for ( $i = count( $updates ) - 1; $i >= 0; $i -= 1 ) {
|
|
143
|
+
list ( $value_start, $value_length ) = $updates[ $i ];
|
|
144
|
+
array_splice( $this->tokens, $value_start, $value_length, $value_tokens );
|
|
145
|
+
}
|
|
267
146
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
147
|
+
// If it's a new constant, inject it at the anchor location.
|
|
148
|
+
if ( 0 === count( $updates ) ) {
|
|
149
|
+
$anchor = $this->get_new_constant_location();
|
|
150
|
+
array_splice( $this->tokens, $anchor, 0, $define_tokens );
|
|
274
151
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
152
|
+
/*
|
|
153
|
+
* Ensure at least one newline (one "\\n") before the new constant.
|
|
154
|
+
* This must be done after inserting the constant definition in order
|
|
155
|
+
* to avoid shifting the anchor location when a new token is inserted.
|
|
156
|
+
*/
|
|
157
|
+
$this->ensure_newlines( $anchor - 1, 1 );
|
|
158
|
+
}
|
|
159
|
+
}
|
|
282
160
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
);
|
|
161
|
+
/**
|
|
162
|
+
* Define multiple constants in the wp-config.php file.
|
|
163
|
+
*
|
|
164
|
+
* @param array<string, mixed> $constants An array of name-value pairs of constants to define.
|
|
165
|
+
*/
|
|
166
|
+
public function define_constants( array $constants ): void {
|
|
167
|
+
foreach ( $constants as $name => $value ) {
|
|
168
|
+
$this->define_constant( $name, $value );
|
|
169
|
+
}
|
|
170
|
+
}
|
|
294
171
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
];
|
|
305
|
-
foreach ($constants as $name => $value) {
|
|
306
|
-
$prepend = array_merge(
|
|
307
|
-
$prepend,
|
|
308
|
-
[
|
|
309
|
-
"define(",
|
|
310
|
-
var_export($name, true),
|
|
311
|
-
',',
|
|
312
|
-
var_export($value, true),
|
|
313
|
-
");\\n"
|
|
314
|
-
]
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
$prepend[] = "?>";
|
|
318
|
-
$output = array_merge(
|
|
319
|
-
$prepend,
|
|
320
|
-
$output
|
|
321
|
-
);
|
|
322
|
-
}
|
|
172
|
+
/**
|
|
173
|
+
* Inject code block into the wp-config.php file.
|
|
174
|
+
*
|
|
175
|
+
* @param string $code The code to inject.
|
|
176
|
+
*/
|
|
177
|
+
public function inject_code_block( string $code ): void {
|
|
178
|
+
// Tokenize the injected code for insertion in the token array.
|
|
179
|
+
$tokens = token_get_all( sprintf( '<?php %s', trim( $code ) ) );
|
|
180
|
+
$code_tokens = array_slice( $tokens, 1 );
|
|
323
181
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
182
|
+
// Inject the code at the anchor location.
|
|
183
|
+
$anchor = $this->get_injected_code_location();
|
|
184
|
+
array_splice( $this->tokens, $anchor, 0, $code_tokens );
|
|
327
185
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
return $output;
|
|
338
|
-
}
|
|
186
|
+
/*
|
|
187
|
+
* Ensure empty line before and after the code block (at least two "\\n").
|
|
188
|
+
* This must be done after inserting the injected code, and the location
|
|
189
|
+
* AFTER must be updated prior to the location BEFORE, in order to avoid
|
|
190
|
+
* shifting the anchor location when a new token is inserted.
|
|
191
|
+
*/
|
|
192
|
+
$this->ensure_newlines( $anchor + count( $code_tokens ), 2 );
|
|
193
|
+
$this->ensure_newlines( $anchor - 1, 2 );
|
|
194
|
+
}
|
|
339
195
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Remove code block defined by two comment fragments from the wp-config.php file.
|
|
198
|
+
*
|
|
199
|
+
* @param string $from_comment_fragment A comment fragment from which to remove the code.
|
|
200
|
+
* @param string $to_comment_fragment A comment fragment to which to remove the code.
|
|
201
|
+
*/
|
|
202
|
+
public function remove_code_block( string $from_comment_fragment, string $to_comment_fragment ): void {
|
|
203
|
+
$start = $this->find_first_token_location( T_COMMENT, $from_comment_fragment );
|
|
204
|
+
$end = $this->find_first_token_location( T_COMMENT, $to_comment_fragment );
|
|
205
|
+
if ( null === $start || null === $end ) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Remove the code, including the comment fragments.
|
|
210
|
+
array_splice( $this->tokens, $start, $end - $start + 1 );
|
|
211
|
+
|
|
212
|
+
// If previous and next tokens are whitespace, merge them.
|
|
213
|
+
$prev = $this->tokens[ $start - 1 ];
|
|
214
|
+
$next = $this->tokens[ $start ] ?? null;
|
|
215
|
+
if (
|
|
216
|
+
is_array( $prev ) && T_WHITESPACE === $prev[0]
|
|
217
|
+
&& is_array( $next ) && T_WHITESPACE === $next[0]
|
|
218
|
+
) {
|
|
219
|
+
$this->tokens[ $start - 1 ][1] = $prev[1] . $next[1];
|
|
220
|
+
array_splice( $this->tokens, $start, 1 );
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Remove up to two empty lines (before & after), keeping at least one.
|
|
224
|
+
$token = $this->tokens[ $start - 1 ];
|
|
225
|
+
if ( is_array( $token ) && T_WHITESPACE === $token[0] ) {
|
|
226
|
+
$newlines = substr_count( $token[1], "\\n" );
|
|
227
|
+
if ( $newlines > 2 ) {
|
|
228
|
+
$limit = min( $newlines - 2, 4 );
|
|
229
|
+
$value = $token[1];
|
|
230
|
+
for ( $i = 0; $limit > 0; $i += 1 ) {
|
|
231
|
+
if ( "\\n" === $value[ $i ] ) {
|
|
232
|
+
$value = substr_replace( $value, '', $i, 1 );
|
|
233
|
+
$limit -= 1;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
$this->tokens[ $start - 1 ][1] = $value;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Parse arguments of a function call and collect their locations.
|
|
243
|
+
*
|
|
244
|
+
* @param int $start The location of the first token of the function call.
|
|
245
|
+
* @return array<array<int, int>> The arguments of the function call.
|
|
246
|
+
*/
|
|
247
|
+
private function collect_function_call_argument_locations( int $start ): array {
|
|
248
|
+
// Find location of the opening parenthesis after the function name.
|
|
249
|
+
$i = $start;
|
|
250
|
+
while ( '(' !== $this->tokens[ $i ] ) {
|
|
251
|
+
$i += 1;
|
|
252
|
+
}
|
|
253
|
+
$i += 1;
|
|
254
|
+
|
|
255
|
+
// Collect all function call argument locations.
|
|
256
|
+
$args = array();
|
|
257
|
+
$arg_start = $this->skip_whitespace_and_comments( $i );
|
|
258
|
+
$parens_level = 0;
|
|
259
|
+
for ( $i = $arg_start; $i < count( $this->tokens ); $i += 1 ) {
|
|
260
|
+
// Skip whitespace and comments, but preserve the index of the last
|
|
261
|
+
// non-whitespace token to calculate the exact argument boundaries.
|
|
262
|
+
$prev_i = $i;
|
|
263
|
+
$i = $this->skip_whitespace_and_comments( $i );
|
|
264
|
+
$token = $this->tokens[ $i ];
|
|
265
|
+
|
|
266
|
+
if ( 0 === $parens_level && ( ',' === $token || ')' === $token ) ) {
|
|
267
|
+
$args[] = array( $arg_start, $prev_i - $arg_start );
|
|
268
|
+
if ( ',' === $token ) {
|
|
269
|
+
// Start of the next argument.
|
|
270
|
+
$arg_start = $this->skip_whitespace_and_comments( $i + 1 );
|
|
271
|
+
$i = $arg_start;
|
|
272
|
+
} else {
|
|
273
|
+
// End of the argument list.
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
} elseif ( '(' === $token || '[' === $token || '{' === $token ) {
|
|
277
|
+
$parens_level += 1;
|
|
278
|
+
} elseif ( ')' === $token || ']' === $token || '}' === $token ) {
|
|
279
|
+
$parens_level -= 1;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return $args;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Evaluate the constant name value from its tokens.
|
|
287
|
+
*
|
|
288
|
+
* @param array $name_tokens The tokens containing the constant name.
|
|
289
|
+
* @return string|null The evaluated constant name.
|
|
290
|
+
*/
|
|
291
|
+
private function evaluate_constant_name( array $name_tokens ): ?string {
|
|
292
|
+
// Decide whether the array represents a constant name or an expression.
|
|
293
|
+
$name_token = null;
|
|
294
|
+
foreach ( $name_tokens as $token ) {
|
|
295
|
+
if ( $this->is_whitespace( $token ) ) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if ( is_array( $token ) ) {
|
|
299
|
+
if ( T_STRING === $token[0] || T_CONSTANT_ENCAPSED_STRING === $token[0] ) {
|
|
300
|
+
$name_token = $token;
|
|
301
|
+
} else {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
} elseif ( '(' !== $token && ')' !== $token ) {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if ( null === $name_token ) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Get the constant name value.
|
|
314
|
+
return eval( 'return ' . $name_token[1] . ';' );
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Skip whitespace and comment tokens and return the location of the first
|
|
319
|
+
* non-whitespace and non-comment token after the specified start location.
|
|
320
|
+
*
|
|
321
|
+
* @param int $start The start location in the token array.
|
|
322
|
+
* @return int The location of the first non-whitespace and non-comment token.
|
|
323
|
+
*/
|
|
324
|
+
private function skip_whitespace_and_comments( int $start ): int {
|
|
325
|
+
for ( $i = $start; $i < count( $this->tokens ); $i += 1 ) {
|
|
326
|
+
if ( $this->is_whitespace( $this->tokens[ $i ] ) ) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
return $i;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Ensure minimum number of newlines are present at the given index.
|
|
336
|
+
*
|
|
337
|
+
* @param int $index The index of the token to ensure newlines.
|
|
338
|
+
* @param int $count The number of newlines that should be present.
|
|
339
|
+
*/
|
|
340
|
+
private function ensure_newlines( int $index, int $count ): void {
|
|
341
|
+
$token = $this->tokens[ $index ] ?? null;
|
|
342
|
+
if ( is_array( $token ) && ( T_WHITESPACE === $token[0] || T_OPEN_TAG === $token[0] ) ) {
|
|
343
|
+
$newlines = substr_count( $token[1], "\\n" );
|
|
344
|
+
if ( $newlines < $count ) {
|
|
345
|
+
$this->tokens[ $index ][1] .= str_repeat( "\\n", $count - $newlines );
|
|
346
|
+
}
|
|
347
|
+
} else {
|
|
348
|
+
$new_token = array( T_WHITESPACE, str_repeat( "\\n", $count ) );
|
|
349
|
+
array_splice( $this->tokens, $index, 0, array( $new_token ) );
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get the location to inject new constant definitions in the token array.
|
|
355
|
+
*
|
|
356
|
+
* @return int The location for new constant definitions in the token array.
|
|
357
|
+
*/
|
|
358
|
+
private function get_new_constant_location(): int {
|
|
359
|
+
// First try to find the "That's all, stop editing!" comment.
|
|
360
|
+
$anchor = $this->find_first_token_location( T_COMMENT, "That's all, stop editing!" );
|
|
361
|
+
if ( null !== $anchor ) {
|
|
362
|
+
return $anchor;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// If not found, try the "Absolute path to the WordPress directory." doc comment.
|
|
366
|
+
$anchor = $this->find_first_token_location( T_DOC_COMMENT, 'Absolute path to the WordPress directory.' );
|
|
367
|
+
if ( null !== $anchor ) {
|
|
368
|
+
return $anchor;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// If not found, try the "Sets up WordPress vars and included files." doc comment.
|
|
372
|
+
$anchor = $this->find_first_token_location( T_DOC_COMMENT, 'Sets up WordPress vars and included files.' );
|
|
373
|
+
if ( null !== $anchor ) {
|
|
374
|
+
return $anchor;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// If not found, try "require_once ABSPATH . 'wp-settings.php';".
|
|
378
|
+
$anchor = $this->find_first_token_location( T_REQUIRE_ONCE );
|
|
379
|
+
if ( null !== $anchor ) {
|
|
380
|
+
return $anchor;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// If not found, fall back to the PHP opening tag.
|
|
384
|
+
$open_tag_anchor = $this->find_first_token_location( T_OPEN_TAG );
|
|
385
|
+
if ( null !== $open_tag_anchor ) {
|
|
386
|
+
return $open_tag_anchor + 1;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// If we still don't have an anchor, the file is not a valid PHP file.
|
|
390
|
+
throw new Exception( "The 'wp-config.php' file is not a valid PHP file." );
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Get the location to inject new code in the token array.
|
|
395
|
+
*
|
|
396
|
+
* @return int The location for injected code in the token array.
|
|
397
|
+
*/
|
|
398
|
+
private function get_injected_code_location(): int {
|
|
399
|
+
// First try to find the "/** Sets up WordPress vars and included files. */" comment.
|
|
400
|
+
$anchor = $this->find_first_token_location( T_DOC_COMMENT, 'Sets up WordPress vars and included files.' );
|
|
401
|
+
if ( null !== $anchor ) {
|
|
402
|
+
return $anchor;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// If not found, try "require_once ABSPATH . 'wp-settings.php';".
|
|
406
|
+
$anchor = $this->find_require_wp_settings_location();
|
|
407
|
+
if ( null !== $anchor ) {
|
|
408
|
+
return $anchor;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// If not found, fall back to the PHP opening tag.
|
|
412
|
+
$open_tag_anchor = $this->find_first_token_location( T_OPEN_TAG );
|
|
413
|
+
if ( null !== $open_tag_anchor ) {
|
|
414
|
+
return $open_tag_anchor + 1;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// If we still don't have an anchor, the file is not a valid PHP file.
|
|
418
|
+
throw new Exception( "The 'wp-config.php' file is not a valid PHP file." );
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Find location of the "wp-settings.php" require statement in the token array.
|
|
423
|
+
*
|
|
424
|
+
* This method searches for the following statement:
|
|
425
|
+
*
|
|
426
|
+
* require_once ABSPATH . 'wp-settings.php';
|
|
427
|
+
*
|
|
428
|
+
* @return int|null The location of the require statement.
|
|
429
|
+
*/
|
|
430
|
+
private function find_require_wp_settings_location(): ?int {
|
|
431
|
+
$require_anchor = $this->find_first_token_location( T_REQUIRE_ONCE );
|
|
432
|
+
if ( null === $require_anchor ) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
$abspath = $this->tokens[ $require_anchor + 2 ] ?? null;
|
|
437
|
+
$path = $this->tokens[ $require_anchor + 6 ] ?? null;
|
|
438
|
+
if (
|
|
439
|
+
( is_array( $abspath ) && 'ABSPATH' === $abspath[1] )
|
|
440
|
+
&& ( is_array( $path ) && "'wp-settings.php'" === $path[1] )
|
|
441
|
+
) {
|
|
442
|
+
return $require_anchor;
|
|
443
|
+
}
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Find location of the first token of a given type in the token array.
|
|
449
|
+
*
|
|
450
|
+
* @param int $type The type of the token.
|
|
451
|
+
* @param string $search Optional. A search string to match against the token content.
|
|
452
|
+
* @return int|null The location of the first token.
|
|
453
|
+
*/
|
|
454
|
+
private function find_first_token_location( int $type, ?string $search = null ): ?int {
|
|
455
|
+
foreach ( $this->tokens as $i => $token ) {
|
|
456
|
+
if ( is_array( $token ) && $type === $token[0] ) {
|
|
457
|
+
if ( null === $search || false !== strpos( $token[1], $search ) ) {
|
|
458
|
+
return $i;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Check if a token is whitespace or a comment.
|
|
467
|
+
*
|
|
468
|
+
* @param array|string $token The token to check.
|
|
469
|
+
* @return bool True if the token is whitespace or a comment.
|
|
470
|
+
*/
|
|
471
|
+
private function is_whitespace( $token ): bool {
|
|
472
|
+
return is_array( $token )
|
|
473
|
+
&& ( T_WHITESPACE === $token[0] || T_COMMENT === $token[0] || T_DOC_COMMENT === $token[0] );
|
|
474
|
+
}
|
|
349
475
|
}
|
|
350
476
|
`;
|
|
351
|
-
async function
|
|
352
|
-
const
|
|
353
|
-
if ((await e.run({
|
|
354
|
-
code: `<?php ob_start(); ?>
|
|
355
|
-
${x}
|
|
356
|
-
$wp_config_path = ${r.wpConfigPath};
|
|
357
|
-
$wp_config = file_get_contents($wp_config_path);
|
|
358
|
-
$new_wp_config = rewrite_wp_config_to_define_constants($wp_config, ${r.constants}, ${r.whenAlreadyDefined});
|
|
359
|
-
$return_value = file_put_contents($wp_config_path, $new_wp_config);
|
|
360
|
-
ob_clean();
|
|
361
|
-
echo false === $return_value ? '0' : '1';
|
|
362
|
-
ob_end_flush();
|
|
363
|
-
`
|
|
364
|
-
})).text !== "1")
|
|
365
|
-
throw new Error("Failed to rewrite constants in wp-config.php.");
|
|
366
|
-
}
|
|
367
|
-
async function I(e, n) {
|
|
368
|
-
const t = o(n, "wp-config.php"), i = {
|
|
477
|
+
async function I(t, e) {
|
|
478
|
+
const n = s(e, "wp-config.php"), i = {
|
|
369
479
|
DB_NAME: "wordpress"
|
|
370
480
|
};
|
|
371
|
-
!
|
|
372
|
-
|
|
373
|
-
await
|
|
374
|
-
|
|
481
|
+
if (!t.fileExists(n) && t.fileExists(s(e, "wp-config-sample.php")) && await t.writeFile(
|
|
482
|
+
n,
|
|
483
|
+
await t.readFileAsBuffer(
|
|
484
|
+
s(e, "wp-config-sample.php")
|
|
375
485
|
)
|
|
376
|
-
),
|
|
486
|
+
), !t.fileExists(n))
|
|
487
|
+
return;
|
|
488
|
+
const a = $({ wpConfigPath: n, constants: i });
|
|
489
|
+
if ((await t.run({
|
|
490
|
+
code: `${m}
|
|
491
|
+
$wp_config_path = ${a.wpConfigPath};
|
|
492
|
+
$transformer = WP_Config_Transformer::from_file($wp_config_path);
|
|
493
|
+
foreach ( ${a.constants} as $name => $value ) {
|
|
494
|
+
if ( ! $transformer->constant_exists( $name ) ) {
|
|
495
|
+
$transformer->define_constant($name, $value);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
$transformer->to_file($wp_config_path);
|
|
499
|
+
`
|
|
500
|
+
})).errors.length > 0)
|
|
501
|
+
throw new Error("Failed to auto-configure wp-config.php.");
|
|
502
|
+
}
|
|
503
|
+
async function B(t, e, n) {
|
|
504
|
+
const i = $({ wpConfigPath: e, constants: n });
|
|
505
|
+
if ((await t.run({
|
|
506
|
+
code: `${m}
|
|
507
|
+
$wp_config_path = ${i.wpConfigPath};
|
|
508
|
+
$transformer = WP_Config_Transformer::from_file($wp_config_path);
|
|
509
|
+
$transformer->define_constants(${i.constants});
|
|
510
|
+
$transformer->to_file($wp_config_path);
|
|
511
|
+
`
|
|
512
|
+
})).errors.length > 0)
|
|
513
|
+
throw new Error("Failed to rewrite constants in wp-config.php.");
|
|
377
514
|
}
|
|
378
|
-
async function
|
|
379
|
-
const
|
|
380
|
-
return await
|
|
515
|
+
async function Q(t) {
|
|
516
|
+
const e = await C(t);
|
|
517
|
+
return await x(e, t), e;
|
|
381
518
|
}
|
|
382
|
-
async function
|
|
383
|
-
var
|
|
384
|
-
const
|
|
385
|
-
if ((
|
|
386
|
-
for (const l in
|
|
387
|
-
|
|
388
|
-
|
|
519
|
+
async function x(t, e) {
|
|
520
|
+
var c, r;
|
|
521
|
+
const n = await t.getPrimaryPhp();
|
|
522
|
+
if ((c = e.hooks) != null && c.beforeWordPressFiles && await e.hooks.beforeWordPressFiles(n), e.wordPressZip && await N(n, await e.wordPressZip), e.constants)
|
|
523
|
+
for (const l in e.constants)
|
|
524
|
+
n.defineConstant(l, e.constants[l]);
|
|
525
|
+
e.dataSqlPath && (n.defineConstant("DB_DIR", y(e.dataSqlPath)), n.defineConstant("DB_FILE", b(e.dataSqlPath))), n.defineConstant("WP_HOME", e.siteUrl), n.defineConstant("WP_SITEURL", e.siteUrl), await I(n, t.documentRoot), (r = e.hooks) != null && r.beforeDatabaseSetup && await e.hooks.beforeDatabaseSetup(n);
|
|
389
526
|
let i = !1;
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
await
|
|
527
|
+
e.sqliteIntegrationPluginZip && (i = !0, await F(
|
|
528
|
+
n,
|
|
529
|
+
await e.sqliteIntegrationPluginZip
|
|
393
530
|
));
|
|
394
|
-
const
|
|
531
|
+
const a = e.wordpressInstallMode ?? "download-and-install", o = !!e.dataSqlPath;
|
|
395
532
|
if (["download-and-install", "install-from-existing-files"].includes(
|
|
396
|
-
|
|
533
|
+
a
|
|
397
534
|
)) {
|
|
398
|
-
await
|
|
535
|
+
await h(t, {
|
|
399
536
|
usesSqlite: i,
|
|
400
|
-
hasCustomDatabasePath:
|
|
537
|
+
hasCustomDatabasePath: o
|
|
401
538
|
});
|
|
402
539
|
try {
|
|
403
|
-
await
|
|
540
|
+
await _(n);
|
|
404
541
|
} catch (l) {
|
|
405
|
-
throw
|
|
542
|
+
throw o || await u(t), l;
|
|
406
543
|
}
|
|
407
|
-
|
|
408
|
-
} else if (
|
|
409
|
-
if (await
|
|
544
|
+
o || await u(t);
|
|
545
|
+
} else if (a === "install-from-existing-files-if-needed") {
|
|
546
|
+
if (await h(t, {
|
|
410
547
|
usesSqlite: i,
|
|
411
|
-
hasCustomDatabasePath:
|
|
412
|
-
}), !await
|
|
548
|
+
hasCustomDatabasePath: o
|
|
549
|
+
}), !await w(n))
|
|
413
550
|
try {
|
|
414
|
-
await
|
|
551
|
+
await _(n);
|
|
415
552
|
} catch (l) {
|
|
416
|
-
throw
|
|
553
|
+
throw o || await u(t), l;
|
|
417
554
|
}
|
|
418
|
-
|
|
555
|
+
o || await u(t);
|
|
419
556
|
}
|
|
420
|
-
return
|
|
557
|
+
return t;
|
|
421
558
|
}
|
|
422
|
-
async function
|
|
423
|
-
usesSqlite:
|
|
424
|
-
hasCustomDatabasePath:
|
|
559
|
+
async function h(t, {
|
|
560
|
+
usesSqlite: e,
|
|
561
|
+
hasCustomDatabasePath: n
|
|
425
562
|
}) {
|
|
426
|
-
const i = await
|
|
563
|
+
const i = await t.getPrimaryPhp();
|
|
427
564
|
if (i.isFile("/internal/shared/preload/0-sqlite.php"))
|
|
428
565
|
return;
|
|
429
|
-
const
|
|
430
|
-
|
|
566
|
+
const a = s(
|
|
567
|
+
t.documentRoot,
|
|
431
568
|
"wp-content/mu-plugins/sqlite-database-integration"
|
|
432
569
|
);
|
|
433
|
-
if (!i.isDir(
|
|
570
|
+
if (!i.isDir(a) && !e && !n)
|
|
434
571
|
throw new Error("Error connecting to the MySQL database.");
|
|
435
572
|
}
|
|
436
|
-
async function
|
|
437
|
-
const
|
|
438
|
-
if (await
|
|
573
|
+
async function u(t) {
|
|
574
|
+
const e = await t.getPrimaryPhp();
|
|
575
|
+
if (await O(e))
|
|
439
576
|
return;
|
|
440
|
-
if (
|
|
577
|
+
if (e.isFile("/internal/shared/preload/0-sqlite.php"))
|
|
441
578
|
throw new Error("Error connecting to the SQLite database.");
|
|
442
|
-
const i =
|
|
443
|
-
|
|
579
|
+
const i = s(
|
|
580
|
+
t.documentRoot,
|
|
444
581
|
"wp-content/mu-plugins/sqlite-database-integration"
|
|
445
582
|
);
|
|
446
|
-
throw
|
|
583
|
+
throw e.isDir(i) ? new Error("Error connecting to the SQLite database.") : new Error("Error connecting to the MySQL database.");
|
|
447
584
|
}
|
|
448
|
-
async function
|
|
449
|
-
const
|
|
450
|
-
async function
|
|
451
|
-
const
|
|
452
|
-
if (
|
|
453
|
-
for (const l in
|
|
454
|
-
|
|
455
|
-
return
|
|
585
|
+
async function C(t) {
|
|
586
|
+
const e = t.spawnHandler ?? T;
|
|
587
|
+
async function n(a, o = !1) {
|
|
588
|
+
const c = await t.createPhpRuntime(o), r = new v(c);
|
|
589
|
+
if (t.sapiName && r.setSapiName(t.sapiName), a && (r.requestHandler = a), t.phpIniEntries && R(r, t.phpIniEntries), r.defineConstant("WP_SQLITE_AST_DRIVER", !0), t.constants)
|
|
590
|
+
for (const l in t.constants)
|
|
591
|
+
r.defineConstant(l, t.constants[l]);
|
|
592
|
+
return o && /**
|
|
456
593
|
* Only the first PHP instance of the first worker created
|
|
457
594
|
* during WordPress boot writes these files – otherwise we'll keep
|
|
458
595
|
* overwriting them with concurrent writers living in other worker
|
|
@@ -462,45 +599,45 @@ async function U(e) {
|
|
|
462
599
|
* mechanism. It works, because secondary workers are only booted
|
|
463
600
|
* once the primary worker has fully booted.
|
|
464
601
|
*/
|
|
465
|
-
!
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
), await
|
|
602
|
+
!r.isFile("/internal/.boot-files-written") && (await U(r), await f(r, "/", t.createFiles || {}), await W(
|
|
603
|
+
r,
|
|
604
|
+
s(new URL(t.siteUrl).pathname, "phpinfo.php")
|
|
605
|
+
), await f(r, "/internal", {
|
|
469
606
|
".boot-files-written": ""
|
|
470
|
-
})),
|
|
471
|
-
|
|
472
|
-
|
|
607
|
+
})), e && await r.setSpawnHandler(
|
|
608
|
+
e(
|
|
609
|
+
a ? () => a.instanceManager.acquirePHPInstance() : void 0
|
|
473
610
|
)
|
|
474
|
-
),
|
|
475
|
-
recreateRuntime:
|
|
611
|
+
), r.enableRuntimeRotation({
|
|
612
|
+
recreateRuntime: t.createPhpRuntime,
|
|
476
613
|
maxRequests: 400
|
|
477
|
-
}),
|
|
614
|
+
}), t.onPHPInstanceCreated && await t.onPHPInstanceCreated(r, { isPrimary: o }), r;
|
|
478
615
|
}
|
|
479
616
|
const i = new E({
|
|
480
|
-
documentRoot:
|
|
481
|
-
absoluteUrl:
|
|
482
|
-
rewriteRules:
|
|
483
|
-
pathAliases:
|
|
484
|
-
getFileNotFoundAction:
|
|
485
|
-
cookieStore:
|
|
617
|
+
documentRoot: t.documentRoot || "/wordpress",
|
|
618
|
+
absoluteUrl: t.siteUrl,
|
|
619
|
+
rewriteRules: q,
|
|
620
|
+
pathAliases: t.pathAliases,
|
|
621
|
+
getFileNotFoundAction: t.getFileNotFoundAction ?? L,
|
|
622
|
+
cookieStore: t.cookieStore,
|
|
486
623
|
/**
|
|
487
624
|
* If maxPhpInstances is 1, the PHPRequestHandler constructor needs
|
|
488
625
|
* a PHP instance. Internally, it creates a SinglePHPInstanceManager
|
|
489
626
|
* and uses the same PHP instance to handle all requests.
|
|
490
627
|
*/
|
|
491
|
-
php:
|
|
628
|
+
php: t.maxPhpInstances === 1 ? await n(void 0, !0) : void 0,
|
|
492
629
|
/**
|
|
493
630
|
* If maxPhpInstances is not 1, the PHPRequestHandler constructor needs
|
|
494
631
|
* a PHP factory function. Internally, it creates a PHPProcessManager that
|
|
495
632
|
* maintains a pool of reusable PHP instances.
|
|
496
633
|
*/
|
|
497
|
-
phpFactory:
|
|
498
|
-
maxPhpInstances:
|
|
634
|
+
phpFactory: t.maxPhpInstances !== 1 ? async ({ isPrimary: a }) => n(i, a) : void 0,
|
|
635
|
+
maxPhpInstances: t.maxPhpInstances
|
|
499
636
|
});
|
|
500
637
|
return i;
|
|
501
638
|
}
|
|
502
|
-
async function
|
|
503
|
-
return (await
|
|
639
|
+
async function w(t) {
|
|
640
|
+
return (await t.run({
|
|
504
641
|
code: `<?php
|
|
505
642
|
ob_start();
|
|
506
643
|
$wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
|
|
@@ -514,19 +651,19 @@ async function $(e) {
|
|
|
514
651
|
ob_end_flush();
|
|
515
652
|
`,
|
|
516
653
|
env: {
|
|
517
|
-
DOCUMENT_ROOT:
|
|
654
|
+
DOCUMENT_ROOT: t.documentRoot
|
|
518
655
|
}
|
|
519
656
|
})).text === "1";
|
|
520
657
|
}
|
|
521
|
-
async function
|
|
658
|
+
async function _(t) {
|
|
522
659
|
var i;
|
|
523
|
-
const
|
|
524
|
-
|
|
660
|
+
const e = await S(
|
|
661
|
+
t,
|
|
525
662
|
{
|
|
526
663
|
disable_functions: "fsockopen",
|
|
527
664
|
allow_url_fopen: "0"
|
|
528
665
|
},
|
|
529
|
-
async () => await
|
|
666
|
+
async () => await t.request({
|
|
530
667
|
url: "/wp-admin/install.php?step=2",
|
|
531
668
|
method: "POST",
|
|
532
669
|
body: {
|
|
@@ -543,14 +680,14 @@ async function h(e) {
|
|
|
543
680
|
}
|
|
544
681
|
})
|
|
545
682
|
);
|
|
546
|
-
if (!await
|
|
683
|
+
if (!await w(t))
|
|
547
684
|
throw new Error(
|
|
548
|
-
`Failed to install WordPress – installer responded with "${(i =
|
|
685
|
+
`Failed to install WordPress – installer responded with "${(i = e.text) == null ? void 0 : i.substring(
|
|
549
686
|
0,
|
|
550
687
|
100
|
|
551
688
|
)}"`
|
|
552
689
|
);
|
|
553
|
-
(await
|
|
690
|
+
(await t.run({
|
|
554
691
|
code: `<?php
|
|
555
692
|
ob_start();
|
|
556
693
|
$wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
|
|
@@ -573,18 +710,18 @@ async function h(e) {
|
|
|
573
710
|
ob_end_flush();
|
|
574
711
|
`,
|
|
575
712
|
env: {
|
|
576
|
-
DOCUMENT_ROOT:
|
|
713
|
+
DOCUMENT_ROOT: t.documentRoot
|
|
577
714
|
}
|
|
578
715
|
})).text !== "1" && g.warn("Failed to default to pretty permalinks after WP install.");
|
|
579
716
|
}
|
|
580
|
-
function
|
|
717
|
+
function L(t) {
|
|
581
718
|
return {
|
|
582
719
|
type: "internal-redirect",
|
|
583
720
|
uri: "/index.php"
|
|
584
721
|
};
|
|
585
722
|
}
|
|
586
|
-
async function
|
|
587
|
-
return (await
|
|
723
|
+
async function O(t) {
|
|
724
|
+
return (await t.run({
|
|
588
725
|
code: `<?php
|
|
589
726
|
ob_start();
|
|
590
727
|
$wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
|
|
@@ -598,35 +735,35 @@ async function C(e) {
|
|
|
598
735
|
ob_end_flush();
|
|
599
736
|
`,
|
|
600
737
|
env: {
|
|
601
|
-
DOCUMENT_ROOT:
|
|
738
|
+
DOCUMENT_ROOT: t.documentRoot
|
|
602
739
|
}
|
|
603
740
|
})).text === "1";
|
|
604
741
|
}
|
|
605
|
-
async function
|
|
606
|
-
const { php:
|
|
742
|
+
async function V(t) {
|
|
743
|
+
const { php: e, reap: n } = await t.instanceManager.acquirePHPInstance();
|
|
607
744
|
try {
|
|
608
|
-
const
|
|
745
|
+
const a = (await e.run({
|
|
609
746
|
code: `<?php
|
|
610
|
-
require '${
|
|
747
|
+
require '${t.documentRoot}/wp-includes/version.php';
|
|
611
748
|
echo $wp_version;
|
|
612
749
|
`
|
|
613
750
|
})).text;
|
|
614
|
-
if (!
|
|
751
|
+
if (!a)
|
|
615
752
|
throw new Error("Unable to read loaded WordPress version.");
|
|
616
|
-
return
|
|
753
|
+
return A(a);
|
|
617
754
|
} finally {
|
|
618
|
-
|
|
755
|
+
n();
|
|
619
756
|
}
|
|
620
757
|
}
|
|
621
|
-
function
|
|
622
|
-
if (/-(alpha|beta|RC)\d*-\d+$/.test(
|
|
758
|
+
function A(t) {
|
|
759
|
+
if (/-(alpha|beta|RC)\d*-\d+$/.test(t))
|
|
623
760
|
return "trunk";
|
|
624
|
-
if (/-(beta|RC)\d*$/.test(
|
|
761
|
+
if (/-(beta|RC)\d*$/.test(t))
|
|
625
762
|
return "beta";
|
|
626
|
-
const i =
|
|
627
|
-
return i !== null ? i[1] :
|
|
763
|
+
const i = t.match(/^(\d+\.\d+)(?:\.\d+)?$/);
|
|
764
|
+
return i !== null ? i[1] : t;
|
|
628
765
|
}
|
|
629
|
-
const
|
|
766
|
+
const q = [
|
|
630
767
|
/**
|
|
631
768
|
* Substitutes the multisite WordPress rewrite rule:
|
|
632
769
|
*
|
|
@@ -642,8 +779,8 @@ const D = [
|
|
|
642
779
|
replacement: "$2"
|
|
643
780
|
}
|
|
644
781
|
];
|
|
645
|
-
async function
|
|
646
|
-
await
|
|
782
|
+
async function U(t) {
|
|
783
|
+
await t.mkdir("/internal/shared/mu-plugins"), await t.writeFile(
|
|
647
784
|
"/internal/shared/preload/env.php",
|
|
648
785
|
`<?php
|
|
649
786
|
|
|
@@ -673,7 +810,7 @@ async function A(e) {
|
|
|
673
810
|
}
|
|
674
811
|
}
|
|
675
812
|
`
|
|
676
|
-
), await
|
|
813
|
+
), await t.writeFile(
|
|
677
814
|
"/internal/shared/mu-plugins/1-auto-login.php",
|
|
678
815
|
`<?php
|
|
679
816
|
/**
|
|
@@ -830,7 +967,7 @@ async function A(e) {
|
|
|
830
967
|
return $interval;
|
|
831
968
|
});
|
|
832
969
|
`
|
|
833
|
-
), await
|
|
970
|
+
), await t.writeFile(
|
|
834
971
|
"/internal/shared/mu-plugins/0-playground.php",
|
|
835
972
|
`<?php
|
|
836
973
|
// Needed because gethostbyname( 'wordpress.org' ) returns
|
|
@@ -875,7 +1012,7 @@ async function A(e) {
|
|
|
875
1012
|
define('ERROR_LOG_FILE', $log_file);
|
|
876
1013
|
ini_set('error_log', $log_file);
|
|
877
1014
|
?>`
|
|
878
|
-
), await
|
|
1015
|
+
), await t.writeFile(
|
|
879
1016
|
"/internal/shared/mu-plugins/sitemap-redirect.php",
|
|
880
1017
|
`<?php
|
|
881
1018
|
/**
|
|
@@ -902,7 +1039,7 @@ async function A(e) {
|
|
|
902
1039
|
}
|
|
903
1040
|
}
|
|
904
1041
|
`
|
|
905
|
-
), await
|
|
1042
|
+
), await t.writeFile(
|
|
906
1043
|
"/internal/shared/preload/error-handler.php",
|
|
907
1044
|
`<?php
|
|
908
1045
|
(function() {
|
|
@@ -955,13 +1092,13 @@ async function A(e) {
|
|
|
955
1092
|
})();`
|
|
956
1093
|
);
|
|
957
1094
|
}
|
|
958
|
-
async function
|
|
959
|
-
await
|
|
1095
|
+
async function W(t, e = "/phpinfo.php") {
|
|
1096
|
+
await t.writeFile(
|
|
960
1097
|
"/internal/shared/preload/phpinfo.php",
|
|
961
1098
|
`<?php
|
|
962
1099
|
// Render PHPInfo if the requested page is /phpinfo.php
|
|
963
|
-
if ( isset($_SERVER['REQUEST_URI']) && ${
|
|
964
|
-
|
|
1100
|
+
if ( isset($_SERVER['REQUEST_URI']) && ${d(
|
|
1101
|
+
e
|
|
965
1102
|
)} === $_SERVER['REQUEST_URI'] ) {
|
|
966
1103
|
phpinfo();
|
|
967
1104
|
exit;
|
|
@@ -969,29 +1106,29 @@ async function q(e, n = "/phpinfo.php") {
|
|
|
969
1106
|
`
|
|
970
1107
|
);
|
|
971
1108
|
}
|
|
972
|
-
async function F(
|
|
973
|
-
await
|
|
1109
|
+
async function F(t, e) {
|
|
1110
|
+
await t.isDir("/tmp/sqlite-database-integration") && await t.rmdir("/tmp/sqlite-database-integration", {
|
|
974
1111
|
recursive: !0
|
|
975
|
-
}), await
|
|
976
|
-
const
|
|
977
|
-
await
|
|
978
|
-
const
|
|
979
|
-
|
|
1112
|
+
}), await t.mkdir("/tmp/sqlite-database-integration"), await p(t, e, "/tmp/sqlite-database-integration");
|
|
1113
|
+
const n = "/internal/shared/sqlite-database-integration", i = `/tmp/sqlite-database-integration/${(await t.listFiles("/tmp/sqlite-database-integration"))[0]}`;
|
|
1114
|
+
await t.mv(i, n), await t.defineConstant("SQLITE_MAIN_FILE", "1");
|
|
1115
|
+
const o = (await t.readFileAsText(
|
|
1116
|
+
s(n, "db.copy")
|
|
980
1117
|
)).replace(
|
|
981
1118
|
"'{SQLITE_IMPLEMENTATION_FOLDER_PATH}'",
|
|
982
|
-
|
|
1119
|
+
d(n)
|
|
983
1120
|
).replace(
|
|
984
1121
|
"'{SQLITE_PLUGIN}'",
|
|
985
|
-
|
|
986
|
-
),
|
|
1122
|
+
d(s(n, "load.php"))
|
|
1123
|
+
), c = s(await t.documentRoot, "wp-content/db.php"), r = `<?php
|
|
987
1124
|
// Do not preload this if WordPress comes with a custom db.php file.
|
|
988
|
-
if(file_exists(${
|
|
1125
|
+
if(file_exists(${d(c)})) {
|
|
989
1126
|
return;
|
|
990
1127
|
}
|
|
991
1128
|
?>`, l = "/internal/shared/mu-plugins/sqlite-database-integration.php";
|
|
992
|
-
await
|
|
1129
|
+
await t.writeFile(l, r + o), await t.writeFile(
|
|
993
1130
|
"/internal/shared/preload/0-sqlite.php",
|
|
994
|
-
|
|
1131
|
+
r + `<?php
|
|
995
1132
|
|
|
996
1133
|
/**
|
|
997
1134
|
* Loads the SQLite integration plugin before WordPress is loaded
|
|
@@ -1041,7 +1178,7 @@ class Playground_SQLite_Integration_Loader {
|
|
|
1041
1178
|
$GLOBALS['wpdb']->$name = $value;
|
|
1042
1179
|
}
|
|
1043
1180
|
protected function load_sqlite_integration() {
|
|
1044
|
-
require_once ${
|
|
1181
|
+
require_once ${d(l)};
|
|
1045
1182
|
}
|
|
1046
1183
|
}
|
|
1047
1184
|
/**
|
|
@@ -1067,7 +1204,7 @@ if(!function_exists('mysqli_connect')) {
|
|
|
1067
1204
|
}
|
|
1068
1205
|
|
|
1069
1206
|
`
|
|
1070
|
-
), await
|
|
1207
|
+
), await t.writeFile(
|
|
1071
1208
|
"/internal/shared/mu-plugins/sqlite-test.php",
|
|
1072
1209
|
`<?php
|
|
1073
1210
|
global $wpdb;
|
|
@@ -1078,64 +1215,64 @@ if(!function_exists('mysqli_connect')) {
|
|
|
1078
1215
|
`
|
|
1079
1216
|
);
|
|
1080
1217
|
}
|
|
1081
|
-
async function N(
|
|
1082
|
-
|
|
1083
|
-
|
|
1218
|
+
async function N(t, e) {
|
|
1219
|
+
t.mkdir("/tmp/unzipped-wordpress"), await p(t, e, "/tmp/unzipped-wordpress"), t.fileExists("/tmp/unzipped-wordpress/wordpress.zip") && await p(
|
|
1220
|
+
t,
|
|
1084
1221
|
"/tmp/unzipped-wordpress/wordpress.zip",
|
|
1085
1222
|
"/tmp/unzipped-wordpress"
|
|
1086
1223
|
);
|
|
1087
|
-
let
|
|
1088
|
-
if (!
|
|
1089
|
-
const
|
|
1090
|
-
if (
|
|
1091
|
-
const
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
) && (
|
|
1224
|
+
let n = t.fileExists("/tmp/unzipped-wordpress/wordpress") ? "/tmp/unzipped-wordpress/wordpress" : t.fileExists("/tmp/unzipped-wordpress/build") ? "/tmp/unzipped-wordpress/build" : "/tmp/unzipped-wordpress";
|
|
1225
|
+
if (!t.fileExists(s(n, "wp-config-sample.php"))) {
|
|
1226
|
+
const a = t.listFiles(n);
|
|
1227
|
+
if (a.length) {
|
|
1228
|
+
const o = a[0];
|
|
1229
|
+
t.fileExists(
|
|
1230
|
+
s(n, o, "wp-config-sample.php")
|
|
1231
|
+
) && (n = s(n, o));
|
|
1095
1232
|
}
|
|
1096
1233
|
}
|
|
1097
|
-
const i = (
|
|
1098
|
-
if (
|
|
1099
|
-
for (const
|
|
1100
|
-
const l =
|
|
1101
|
-
i(l,
|
|
1234
|
+
const i = (a, o, c) => {
|
|
1235
|
+
if (c.isDir(a) && c.isDir(o))
|
|
1236
|
+
for (const r of c.listFiles(a)) {
|
|
1237
|
+
const l = s(a, r), k = s(o, r);
|
|
1238
|
+
i(l, k, c);
|
|
1102
1239
|
}
|
|
1103
1240
|
else {
|
|
1104
|
-
if (
|
|
1105
|
-
const
|
|
1241
|
+
if (c.fileExists(o)) {
|
|
1242
|
+
const r = a.replace(
|
|
1106
1243
|
/^\/tmp\/unzipped-wordpress\//,
|
|
1107
1244
|
"/"
|
|
1108
1245
|
);
|
|
1109
1246
|
g.warn(
|
|
1110
|
-
`Cannot unzip WordPress files at ${
|
|
1247
|
+
`Cannot unzip WordPress files at ${o}: ${r} already exists.`
|
|
1111
1248
|
);
|
|
1112
1249
|
return;
|
|
1113
1250
|
}
|
|
1114
|
-
|
|
1251
|
+
c.mv(a, o);
|
|
1115
1252
|
}
|
|
1116
1253
|
};
|
|
1117
|
-
i(
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1254
|
+
i(n, t.documentRoot, t), t.fileExists(n) && t.rmdir(n, { recursive: !0 }), !t.fileExists(s(t.documentRoot, "wp-config.php")) && t.fileExists(s(t.documentRoot, "wp-config-sample.php")) && t.writeFile(
|
|
1255
|
+
s(t.documentRoot, "wp-config.php"),
|
|
1256
|
+
t.readFileAsText(
|
|
1257
|
+
s(t.documentRoot, "/wp-config-sample.php")
|
|
1121
1258
|
)
|
|
1122
1259
|
);
|
|
1123
1260
|
}
|
|
1124
|
-
const
|
|
1125
|
-
async function Z(
|
|
1126
|
-
if (
|
|
1127
|
-
|
|
1128
|
-
else if (
|
|
1261
|
+
const D = P(fetch), M = "https://github.com/WordPress/WordPress/archive/refs/heads/master.zip";
|
|
1262
|
+
async function Z(t = "latest") {
|
|
1263
|
+
if (t === null)
|
|
1264
|
+
t = "latest";
|
|
1265
|
+
else if (t.startsWith("https://") || t.startsWith("http://")) {
|
|
1129
1266
|
const i = await crypto.subtle.digest(
|
|
1130
1267
|
"SHA-1",
|
|
1131
|
-
new TextEncoder().encode(
|
|
1132
|
-
),
|
|
1268
|
+
new TextEncoder().encode(t)
|
|
1269
|
+
), a = Array.from(new Uint8Array(i)).map((o) => o.toString(16).padStart(2, "0")).join("");
|
|
1133
1270
|
return {
|
|
1134
|
-
releaseUrl:
|
|
1135
|
-
version: "custom-" +
|
|
1271
|
+
releaseUrl: t,
|
|
1272
|
+
version: "custom-" + a.substring(0, 8),
|
|
1136
1273
|
source: "inferred"
|
|
1137
1274
|
};
|
|
1138
|
-
} else if (
|
|
1275
|
+
} else if (t === "trunk" || t === "nightly") {
|
|
1139
1276
|
const i = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1140
1277
|
return {
|
|
1141
1278
|
releaseUrl: `${M}?ts=${i}`,
|
|
@@ -1143,52 +1280,52 @@ async function Z(e = "latest") {
|
|
|
1143
1280
|
source: "inferred"
|
|
1144
1281
|
};
|
|
1145
1282
|
}
|
|
1146
|
-
let
|
|
1283
|
+
let n = await (await D(
|
|
1147
1284
|
"https://api.wordpress.org/core/version-check/1.7/?channel=beta"
|
|
1148
1285
|
)).json();
|
|
1149
|
-
|
|
1286
|
+
n = n.offers.filter(
|
|
1150
1287
|
(i) => i.response === "autoupdate"
|
|
1151
1288
|
);
|
|
1152
|
-
for (const i of
|
|
1153
|
-
if (
|
|
1289
|
+
for (const i of n) {
|
|
1290
|
+
if (t === "beta" && (i.version.includes("beta") || i.version.includes("RC")))
|
|
1154
1291
|
return {
|
|
1155
1292
|
releaseUrl: i.download,
|
|
1156
1293
|
version: i.version,
|
|
1157
1294
|
source: "api"
|
|
1158
1295
|
};
|
|
1159
|
-
if (
|
|
1296
|
+
if (t === "latest" && !i.version.includes("beta") && !i.version.includes("RC"))
|
|
1160
1297
|
return {
|
|
1161
1298
|
releaseUrl: i.download,
|
|
1162
1299
|
version: i.version,
|
|
1163
1300
|
source: "api"
|
|
1164
1301
|
};
|
|
1165
|
-
if (i.version.substring(0,
|
|
1302
|
+
if (i.version.substring(0, t.length) === t)
|
|
1166
1303
|
return {
|
|
1167
1304
|
releaseUrl: i.download,
|
|
1168
1305
|
version: i.version,
|
|
1169
1306
|
source: "api"
|
|
1170
1307
|
};
|
|
1171
1308
|
}
|
|
1172
|
-
return
|
|
1173
|
-
releaseUrl: `https://wordpress.org/wordpress-${
|
|
1174
|
-
version:
|
|
1309
|
+
return t.match(/^\d+\.\d+\.0$/) && (t = t.split(".").slice(0, 2).join(".")), {
|
|
1310
|
+
releaseUrl: `https://wordpress.org/wordpress-${t}.zip`,
|
|
1311
|
+
version: t,
|
|
1175
1312
|
source: "inferred"
|
|
1176
1313
|
};
|
|
1177
1314
|
}
|
|
1178
1315
|
export {
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1316
|
+
C as bootRequestHandler,
|
|
1317
|
+
x as bootWordPress,
|
|
1318
|
+
Q as bootWordPressAndRequestHandler,
|
|
1319
|
+
B as defineWpConfigConstants,
|
|
1183
1320
|
I as ensureWpConfig,
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1321
|
+
L as getFileNotFoundActionForWordPress,
|
|
1322
|
+
V as getLoadedWordPressVersion,
|
|
1323
|
+
W as preloadPhpInfoRoute,
|
|
1187
1324
|
F as preloadSqliteIntegration,
|
|
1188
1325
|
Z as resolveWordPressRelease,
|
|
1189
|
-
|
|
1326
|
+
U as setupPlatformLevelMuPlugins,
|
|
1190
1327
|
N as unzipWordPress,
|
|
1191
|
-
|
|
1192
|
-
|
|
1328
|
+
A as versionStringToLoadedWordPressVersion,
|
|
1329
|
+
q as wordPressRewriteRules
|
|
1193
1330
|
};
|
|
1194
1331
|
//# sourceMappingURL=index.js.map
|