@wp-playground/wordpress 3.1.1 → 3.1.2

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 CHANGED
@@ -1,358 +1,489 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("@php-wasm/util"),c=require("@wp-playground/common"),h=require("@php-wasm/logger"),u=require("@php-wasm/universal"),x=`<?php
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("@php-wasm/util"),p=require("@wp-playground/common"),_=require("@php-wasm/logger"),d=require("@php-wasm/universal"),$=`<?php
2
2
 
3
3
  /**
4
- * Rewrites the wp-config.php file to ensure specific constants are defined
5
- * with specific values.
6
- *
7
- * Example:
8
- *
9
- * \`\`\`php
10
- * <?php
11
- * define('WP_DEBUG', true);
12
- * // The third define() argument is also supported:
13
- * define('SAVEQUERIES', false, true);
14
- *
15
- * // Expression
16
- * define(true ? 'WP_DEBUG_LOG' : 'WP_DEBUG_LOG', 123);
17
- *
18
- * // Guarded expressions shouldn't be wrapped twice
19
- * if(!defined(1 ? 'A' : 'B')) {
20
- * define(1 ? 'A' : 'B', 0);
21
- * }
22
- *
23
- * // More advanced expression
24
- * define((function() use($x) {
25
- * return [$x, 'a'];
26
- * })(), 123);
27
- * \`\`\`
28
- *
29
- * Rewritten with
30
- *
31
- * $constants = [
32
- * 'WP_DEBUG' => false,
33
- * 'WP_DEBUG_LOG' => true,
34
- * 'SAVEQUERIES' => true,
35
- * 'NEW_CONSTANT' => "new constant",
36
- * ];
37
- *
38
- * \`\`\`php
39
- * <?php
40
- * define('WP_DEBUG_LOG',true);
41
- * define('NEW_CONSTANT','new constant');
42
- * ?><?php
43
- * define('WP_DEBUG',false);
44
- * // The third define() argument is also supported:
45
- * define('SAVEQUERIES',true, true);
46
- *
47
- * // Expression
48
- * if(!defined($const ? 'WP_DEBUG_LOG' : 'WP_DEBUG_LOG')) {
49
- * define($const ? 'WP_DEBUG_LOG' : 'WP_DEBUG_LOG', 123);
50
- * }
51
- *
52
- * // Guarded expressions shouldn't be wrapped twice
53
- * if(!defined(1 ? 'A' : 'B')) {
54
- * define(1 ? 'A' : 'B', 0);
55
- * }
56
- *
57
- * // More advanced expression
58
- * if(!defined((function() use($x) {
59
- * return [$x, 'a'];
60
- * })())) {
61
- * define((function() use($x) {
62
- * return [$x, 'a'];
63
- * })(), 123);
64
- * }
65
- * \`\`\`
4
+ * Transforms the "wp-config.php" file.
66
5
  *
67
- * @param mixed $content A PHP file content.
68
- * @param array $constants An array of constants to define.
69
- * @param bool $when_already_defined Optional. What to do if the constant is already defined.
70
- * Possible values are:
71
- * 'rewrite' - Rewrite the constant, using the new value.
72
- * 'skip' - Skip the definition, keeping the existing value.
73
- * Default: 'rewrite'
74
- * @return string
6
+ * This parses the "wp-config.php" file contents into a token array and provides
7
+ * methods to modify it and serialize it back to a string with the modifications.
75
8
  */
76
- function rewrite_wp_config_to_define_constants($content, $constants = [], $when_already_defined = 'rewrite')
77
- {
78
- $tokens = array_reverse(token_get_all($content));
79
- $output = [];
80
- $defined_expressions = [];
81
-
82
- // Look through all the tokens and find the define calls
83
- do {
84
- $buffer = [];
85
- $name_buffer = [];
86
- $value_buffer = [];
87
- $third_arg_buffer = [];
88
-
89
- // Capture everything until the define call into output.
90
- // Capturing the define call into a buffer.
91
- // Example:
92
- // <?php echo 'a'; define (
93
- // ^^^^^^^^^^^^^^^^^^^^^^
94
- // output |buffer
95
- while ($token = array_pop($tokens)) {
96
- if (is_array($token) && $token[0] === T_STRING && (strtolower($token[1]) === 'define' || strtolower($token[1]) === 'defined')) {
97
- $buffer[] = $token;
98
- break;
99
- }
100
- $output[] = $token;
101
- }
9
+ class WP_Config_Transformer {
10
+ /**
11
+ * The tokens of the wp-config.php file.
12
+ *
13
+ * @var array<array|string>
14
+ */
15
+ private $tokens;
16
+
17
+ /**
18
+ * Constructor.
19
+ *
20
+ * @param string $content The contents of the wp-config.php file.
21
+ */
22
+ public function __construct( string $content ) {
23
+ $this->tokens = token_get_all( $content );
24
+
25
+ // Check if the file is a valid PHP file.
26
+ $is_valid_php_file = false;
27
+ foreach ( $this->tokens as $token ) {
28
+ if ( is_array( $token ) && T_OPEN_TAG === $token[0] ) {
29
+ $is_valid_php_file = true;
30
+ break;
31
+ }
32
+ }
33
+ if ( ! $is_valid_php_file ) {
34
+ throw new Exception( "The 'wp-config.php' file is not a valid PHP file." );
35
+ }
36
+ }
102
37
 
103
- // Maybe we didn't find a define call and reached the end of the file?
104
- if (!count($tokens)) {
105
- break;
106
- }
38
+ /**
39
+ * Create a new config transformer instance from a file.
40
+ *
41
+ * @param string $path The path to the wp-config.php file.
42
+ * @return self The new config transformer instance.
43
+ */
44
+ public static function from_file( string $path ): self {
45
+ if ( ! is_file( $path ) ) {
46
+ throw new Exception( sprintf( "The '%s' file does not exist.", $path ) );
47
+ }
48
+ return new self( file_get_contents( $path ) );
49
+ }
107
50
 
108
- // Keep track of the "defined" expressions that are already accounted for
109
- if($token[1] === 'defined') {
110
- $output[] = $token;
111
- $defined_expression = [];
112
- $open_parenthesis = 0;
113
- // Capture everything up to the opening parenthesis, including the parenthesis
114
- // e.g. defined (
115
- // ^^^^
116
- while ($token = array_pop($tokens)) {
117
- $output[] = $token;
118
- if ($token === "(") {
119
- ++$open_parenthesis;
120
- break;
121
- }
122
- }
51
+ /**
52
+ * Get the transformed wp-config.php file contents.
53
+ *
54
+ * @return string The transformed wp-config.php file contents.
55
+ */
56
+ public function to_string(): string {
57
+ $output = '';
58
+ foreach ( $this->tokens as $token ) {
59
+ $output .= is_array( $token ) ? $token[1] : $token;
60
+ }
61
+ return $output;
62
+ }
123
63
 
124
- // Capture everything up to the closing parenthesis, including the parenthesis
125
- // e.g. defined (
126
- // ^^^^
127
- while ($token = array_pop($tokens)) {
128
- $output[] = $token;
129
- if ($token === ")") {
130
- --$open_parenthesis;
131
- }
132
- if ($open_parenthesis === 0) {
133
- break;
134
- }
135
- $defined_expression[] = $token;
136
- }
64
+ /**
65
+ * Save the transformed wp-config.php file contents to a file.
66
+ *
67
+ * @param string $path The path to the wp-config.php file.
68
+ */
69
+ public function to_file( string $path ): void {
70
+ $result = file_put_contents( $path, $this->to_string() );
71
+ if ( false === $result ) {
72
+ throw new Exception( sprintf( "Failed to write to the '%s' file.", $path ) );
73
+ }
74
+ }
137
75
 
138
- $defined_expressions[] = stringify_tokens(skip_whitespace($defined_expression));
139
- continue;
140
- }
76
+ /**
77
+ * Check if a constant is defined in the wp-config.php file.
78
+ *
79
+ * @param string $name The name of the constant.
80
+ * @return bool True if the constant is defined, false otherwise.
81
+ */
82
+ public function constant_exists( string $name ): bool {
83
+ foreach ( $this->tokens as $i => $token ) {
84
+ $is_string_token = is_array( $token ) && T_STRING === $token[0];
85
+ if ( $is_string_token && 'define' === strtolower( $token[1] ) ) {
86
+ $args = $this->collect_function_call_argument_locations( $i );
87
+ $const_name = $this->evaluate_constant_name(
88
+ array_slice( $this->tokens, $args[0][0], $args[0][1] )
89
+ );
90
+ if ( $name === $const_name ) {
91
+ return true;
92
+ }
93
+ }
94
+ }
95
+ return false;
96
+ }
141
97
 
142
- // Capture everything up to the opening parenthesis, including the parenthesis
143
- // e.g. define (
144
- // ^^^^
145
- while ($token = array_pop($tokens)) {
146
- $buffer[] = $token;
147
- if ($token === "(") {
148
- break;
149
- }
150
- }
98
+ /**
99
+ * Define a constant in the wp-config.php file.
100
+ *
101
+ * @param string $name The name of the constant.
102
+ * @param mixed $value The value of the constant.
103
+ */
104
+ public function define_constant( string $name, $value ): void {
105
+ // Tokenize the new constant value for insertion in the tokens array.
106
+ $definition_tokens = token_get_all(
107
+ sprintf(
108
+ "<?php define( %s, %s );\\n",
109
+ var_export( $name, true ),
110
+ var_export( $value, true )
111
+ )
112
+ );
151
113
 
152
- // Capture the first argument it's the first expression after the opening
153
- // parenthesis and before the comma:
154
- // Examples:
155
- // define("WP_DEBUG", true);
156
- // ^^^^^^^^^^^
157
- //
158
- // define(count([1,2]) > 2 ? 'WP_DEBUG' : 'FOO', true);
159
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
160
- $open_parenthesis = 0;
161
- while ($token = array_pop($tokens)) {
162
- $buffer[] = $token;
163
- if ($token === "(" || $token === "[" || $token === "{") {
164
- ++$open_parenthesis;
165
- } elseif ($token === ")" || $token === "]" || $token === "}") {
166
- --$open_parenthesis;
167
- } elseif ($token === "," && $open_parenthesis === 0) {
168
- break;
169
- }
114
+ // Full constant definition statement, e.g.: define( 'WP_DEBUG', true );\\n
115
+ $define_tokens = array_slice( $definition_tokens, 1 );
170
116
 
171
- // Don't capture the comma as a part of the constant name
172
- $name_buffer[] = $token;
173
- }
117
+ // The value of the constant, e.g.: "my-database-name"
118
+ $value_tokens = array_slice( $definition_tokens, 7, -4 );
174
119
 
175
- // Capture everything until the closing parenthesis
176
- // define("WP_DEBUG", true);
177
- // ^^^^^^
178
- $open_parenthesis = 0;
179
- $is_second_argument = true;
180
- while ($token = array_pop($tokens)) {
181
- $buffer[] = $token;
182
- if ($token === ")" && $open_parenthesis === 0) {
183
- // Final parenthesis of the define call.
184
- break;
185
- } else if ($token === "(" || $token === "[" || $token === "{") {
186
- ++$open_parenthesis;
187
- } elseif ($token === ")" || $token === "]" || $token === "}") {
188
- --$open_parenthesis;
189
- } elseif ($token === "," && $open_parenthesis === 0) {
190
- // This define call has more than 2 arguments! The third one is the
191
- // boolean value indicating $is_case_insensitive. Let's continue capturing
192
- // to $third_arg_buffer.
193
- $is_second_argument = false;
194
- }
195
- if ($is_second_argument) {
196
- $value_buffer[] = $token;
197
- } else {
198
- $third_arg_buffer[] = $token;
199
- }
200
- }
120
+ // Collect all locations where the constant value needs to be updated.
121
+ $updates = array();
122
+ foreach ( $this->tokens as $i => $token ) {
123
+ $is_string_token = is_array( $token ) && T_STRING === $token[0];
124
+ if ( $is_string_token && 'define' === strtolower( $token[1] ) ) {
125
+ $args = $this->collect_function_call_argument_locations( $i );
126
+ $const_name = $this->evaluate_constant_name(
127
+ array_slice( $this->tokens, $args[0][0], $args[0][1] )
128
+ );
201
129
 
202
- // Capture until the semicolon
203
- // define("WP_DEBUG", true) ;
204
- // ^^^
205
- while ($token = array_pop($tokens)) {
206
- $buffer[] = $token;
207
- if ($token === ";") {
208
- break;
209
- }
210
- }
130
+ if ( $name === $const_name ) {
131
+ $updates[] = $args[1];
132
+ }
133
+ }
134
+ }
211
135
 
212
- // Decide whether $name_buffer is a constant name or an expression
213
- $name_token = null;
214
- $name_token_index = $token;
215
- $name_is_literal = true;
216
- foreach ($name_buffer as $k => $token) {
217
- if (is_array($token)) {
218
- if ($token[0] === T_WHITESPACE || $token[0] === T_COMMENT || $token[0] === T_DOC_COMMENT) {
219
- continue;
220
- } else if ($token[0] === T_STRING || $token[0] === T_CONSTANT_ENCAPSED_STRING) {
221
- $name_token = $token;
222
- $name_token_index = $k;
223
- } else {
224
- $name_is_literal = false;
225
- break;
226
- }
227
- } else if ($token !== "(" && $token !== ")") {
228
- $name_is_literal = false;
229
- break;
230
- }
231
- }
136
+ // Modify the token array to define the constant. Apply updates in reverse
137
+ // order, so splices at earlier positions don't shift indices after them.
138
+ for ( $i = count( $updates ) - 1; $i >= 0; $i -= 1 ) {
139
+ list ( $value_start, $value_length ) = $updates[ $i ];
140
+ array_splice( $this->tokens, $value_start, $value_length, $value_tokens );
141
+ }
232
142
 
233
- // We can't handle expressions as constant names. Let's wrap that define
234
- // call in an if(!defined()) statement, just in case it collides with
235
- // a constant name.
236
- if (!$name_is_literal) {
237
- // Ensure the defined expression is not already accounted for
238
- foreach ($defined_expressions as $defined_expression) {
239
- if ($defined_expression === stringify_tokens(skip_whitespace($name_buffer))) {
240
- $output = array_merge($output, $buffer);
241
- continue 2;
242
- }
243
- }
244
- $output = array_merge(
245
- $output,
246
- ["if(!defined("],
247
- $name_buffer,
248
- [")) {\\n "],
249
- ['define('],
250
- $name_buffer,
251
- [','],
252
- $value_buffer,
253
- $third_arg_buffer,
254
- [");"],
255
- ["\\n}\\n"]
256
- );
257
- continue;
258
- }
143
+ // If it's a new constant, inject it at the anchor location.
144
+ if ( 0 === count( $updates ) ) {
145
+ $anchor = $this->get_new_constant_location();
146
+ array_splice( $this->tokens, $anchor, 0, $define_tokens );
259
147
 
260
- // Yay, we have a literal constant name in the buffer now. Let's
261
- // get its value:
262
- $name = eval('return ' . $name_token[1] . ';');
148
+ /*
149
+ * Ensure at least one newline (one "\\n") before the new constant.
150
+ * This must be done after inserting the constant definition in order
151
+ * to avoid shifting the anchor location when a new token is inserted.
152
+ */
153
+ $this->ensure_newlines( $anchor - 1, 1 );
154
+ }
155
+ }
263
156
 
264
- // If the constant name is not in the list of constants we're looking,
265
- // we can ignore it.
266
- if (!array_key_exists($name, $constants)) {
267
- $output = array_merge($output, $buffer);
268
- continue;
269
- }
157
+ /**
158
+ * Define multiple constants in the wp-config.php file.
159
+ *
160
+ * @param array<string, mixed> $constants An array of name-value pairs of constants to define.
161
+ */
162
+ public function define_constants( array $constants ): void {
163
+ foreach ( $constants as $name => $value ) {
164
+ $this->define_constant( $name, $value );
165
+ }
166
+ }
270
167
 
271
- // If "$when_already_defined" is set to 'skip', ignore the definition, and
272
- // remove the constant from the list so it doesn't get added to the output.
273
- if ('skip' === $when_already_defined) {
274
- $output = array_merge($output, $buffer);
275
- unset($constants[$name]);
276
- continue;
277
- }
168
+ /**
169
+ * Inject code block into the wp-config.php file.
170
+ *
171
+ * @param string $code The code to inject.
172
+ */
173
+ public function inject_code_block( string $code ): void {
174
+ // Tokenize the injected code for insertion in the token array.
175
+ $tokens = token_get_all( sprintf( '<?php %s', trim( $code ) ) );
176
+ $code_tokens = array_slice( $tokens, 1 );
177
+
178
+ // Inject the code at the anchor location.
179
+ $anchor = $this->get_injected_code_location();
180
+ array_splice( $this->tokens, $anchor, 0, $code_tokens );
181
+
182
+ /*
183
+ * Ensure empty line before and after the code block (at least two "\\n").
184
+ * This must be done after inserting the injected code, and the location
185
+ * AFTER must be updated prior to the location BEFORE, in order to avoid
186
+ * shifting the anchor location when a new token is inserted.
187
+ */
188
+ $this->ensure_newlines( $anchor + count( $code_tokens ), 2 );
189
+ $this->ensure_newlines( $anchor - 1, 2 );
190
+ }
278
191
 
279
- // We now have a define() call that defines a constant we're looking for.
280
- // Let's rewrite its value to the one
281
- $output = array_merge(
282
- $output,
283
- ['define('],
284
- $name_buffer,
285
- [','],
286
- [var_export($constants[$name], true)],
287
- $third_arg_buffer,
288
- [");"]
289
- );
290
-
291
- // Remove the constant from the list so we can process any remaining
292
- // constants later.
293
- unset($constants[$name]);
294
- } while (count($tokens));
295
-
296
- // Add any constants that weren't found in the file
297
- if (count($constants)) {
298
- $prepend = [
299
- "<?php \\n"
300
- ];
301
- foreach ($constants as $name => $value) {
302
- $prepend = array_merge(
303
- $prepend,
304
- [
305
- "define(",
306
- var_export($name, true),
307
- ',',
308
- var_export($value, true),
309
- ");\\n"
310
- ]
311
- );
312
- }
313
- $prepend[] = "?>";
314
- $output = array_merge(
315
- $prepend,
316
- $output
317
- );
318
- }
192
+ /**
193
+ * Remove code block defined by two comment fragments from the wp-config.php file.
194
+ *
195
+ * @param string $from_comment_fragment A comment fragment from which to remove the code.
196
+ * @param string $to_comment_fragment A comment fragment to which to remove the code.
197
+ */
198
+ public function remove_code_block( string $from_comment_fragment, string $to_comment_fragment ): void {
199
+ $start = $this->find_first_token_location( T_COMMENT, $from_comment_fragment );
200
+ $end = $this->find_first_token_location( T_COMMENT, $to_comment_fragment );
201
+ if ( null === $start || null === $end ) {
202
+ return;
203
+ }
319
204
 
320
- // Translate the output tokens back into a string
321
- return stringify_tokens($output);
322
- }
205
+ // Remove the code, including the comment fragments.
206
+ array_splice( $this->tokens, $start, $end - $start + 1 );
207
+
208
+ // If previous and next tokens are whitespace, merge them.
209
+ $prev = $this->tokens[ $start - 1 ];
210
+ $next = $this->tokens[ $start ] ?? null;
211
+ if (
212
+ is_array( $prev ) && T_WHITESPACE === $prev[0]
213
+ && is_array( $next ) && T_WHITESPACE === $next[0]
214
+ ) {
215
+ $this->tokens[ $start - 1 ][1] = $prev[1] . $next[1];
216
+ array_splice( $this->tokens, $start, 1 );
217
+ }
323
218
 
324
- function stringify_tokens($tokens) {
325
- $output = '';
326
- foreach ($tokens as $token) {
327
- if (is_array($token)) {
328
- $output .= $token[1];
329
- } else {
330
- $output .= $token;
331
- }
332
- }
333
- return $output;
334
- }
219
+ // Remove up to two empty lines (before & after), keeping at least one.
220
+ $token = $this->tokens[ $start - 1 ];
221
+ if ( is_array( $token ) && T_WHITESPACE === $token[0] ) {
222
+ $newlines = substr_count( $token[1], "\\n" );
223
+ if ( $newlines > 2 ) {
224
+ $limit = min( $newlines - 2, 4 );
225
+ $value = $token[1];
226
+ for ( $i = 0; $limit > 0; $i += 1 ) {
227
+ if ( "\\n" === $value[ $i ] ) {
228
+ $value = substr_replace( $value, '', $i, 1 );
229
+ $limit -= 1;
230
+ }
231
+ }
232
+ $this->tokens[ $start - 1 ][1] = $value;
233
+ }
234
+ }
235
+ }
335
236
 
336
- function skip_whitespace($tokens) {
337
- $output = [];
338
- foreach ($tokens as $token) {
339
- if (is_array($token) && ($token[0] === T_WHITESPACE || $token[0] === T_COMMENT || $token[0] === T_DOC_COMMENT)) {
340
- continue;
341
- }
342
- $output[] = $token;
343
- }
344
- return $output;
237
+ /**
238
+ * Parse arguments of a function call and collect their locations.
239
+ *
240
+ * @param int $start The location of the first token of the function call.
241
+ * @return array<array<int, int>> The arguments of the function call.
242
+ */
243
+ private function collect_function_call_argument_locations( int $start ): array {
244
+ // Find location of the opening parenthesis after the function name.
245
+ $i = $start;
246
+ while ( '(' !== $this->tokens[ $i ] ) {
247
+ $i += 1;
248
+ }
249
+ $i += 1;
250
+
251
+ // Collect all function call argument locations.
252
+ $args = array();
253
+ $arg_start = $this->skip_whitespace_and_comments( $i );
254
+ $parens_level = 0;
255
+ for ( $i = $arg_start; $i < count( $this->tokens ); $i += 1 ) {
256
+ // Skip whitespace and comments, but preserve the index of the last
257
+ // non-whitespace token to calculate the exact argument boundaries.
258
+ $prev_i = $i;
259
+ $i = $this->skip_whitespace_and_comments( $i );
260
+ $token = $this->tokens[ $i ];
261
+
262
+ if ( 0 === $parens_level && ( ',' === $token || ')' === $token ) ) {
263
+ $args[] = array( $arg_start, $prev_i - $arg_start );
264
+ if ( ',' === $token ) {
265
+ // Start of the next argument.
266
+ $arg_start = $this->skip_whitespace_and_comments( $i + 1 );
267
+ $i = $arg_start;
268
+ } else {
269
+ // End of the argument list.
270
+ break;
271
+ }
272
+ } elseif ( '(' === $token || '[' === $token || '{' === $token ) {
273
+ $parens_level += 1;
274
+ } elseif ( ')' === $token || ']' === $token || '}' === $token ) {
275
+ $parens_level -= 1;
276
+ }
277
+ }
278
+ return $args;
279
+ }
280
+
281
+ /**
282
+ * Evaluate the constant name value from its tokens.
283
+ *
284
+ * @param array $name_tokens The tokens containing the constant name.
285
+ * @return string|null The evaluated constant name.
286
+ */
287
+ private function evaluate_constant_name( array $name_tokens ): ?string {
288
+ // Decide whether the array represents a constant name or an expression.
289
+ $name_token = null;
290
+ foreach ( $name_tokens as $token ) {
291
+ if ( $this->is_whitespace( $token ) ) {
292
+ continue;
293
+ }
294
+ if ( is_array( $token ) ) {
295
+ if ( T_STRING === $token[0] || T_CONSTANT_ENCAPSED_STRING === $token[0] ) {
296
+ $name_token = $token;
297
+ } else {
298
+ return null;
299
+ }
300
+ } elseif ( '(' !== $token && ')' !== $token ) {
301
+ return null;
302
+ }
303
+ }
304
+
305
+ if ( null === $name_token ) {
306
+ return null;
307
+ }
308
+
309
+ // Get the constant name value.
310
+ return eval( 'return ' . $name_token[1] . ';' );
311
+ }
312
+
313
+ /**
314
+ * Skip whitespace and comment tokens and return the location of the first
315
+ * non-whitespace and non-comment token after the specified start location.
316
+ *
317
+ * @param int $start The start location in the token array.
318
+ * @return int The location of the first non-whitespace and non-comment token.
319
+ */
320
+ private function skip_whitespace_and_comments( int $start ): int {
321
+ for ( $i = $start; $i < count( $this->tokens ); $i += 1 ) {
322
+ if ( $this->is_whitespace( $this->tokens[ $i ] ) ) {
323
+ continue;
324
+ }
325
+ break;
326
+ }
327
+ return $i;
328
+ }
329
+
330
+ /**
331
+ * Ensure minimum number of newlines are present at the given index.
332
+ *
333
+ * @param int $index The index of the token to ensure newlines.
334
+ * @param int $count The number of newlines that should be present.
335
+ */
336
+ private function ensure_newlines( int $index, int $count ): void {
337
+ $token = $this->tokens[ $index ] ?? null;
338
+ if ( is_array( $token ) && ( T_WHITESPACE === $token[0] || T_OPEN_TAG === $token[0] ) ) {
339
+ $newlines = substr_count( $token[1], "\\n" );
340
+ if ( $newlines < $count ) {
341
+ $this->tokens[ $index ][1] .= str_repeat( "\\n", $count - $newlines );
342
+ }
343
+ } else {
344
+ $new_token = array( T_WHITESPACE, str_repeat( "\\n", $count ) );
345
+ array_splice( $this->tokens, $index, 0, array( $new_token ) );
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Get the location to inject new constant definitions in the token array.
351
+ *
352
+ * @return int The location for new constant definitions in the token array.
353
+ */
354
+ private function get_new_constant_location(): int {
355
+ // First try to find the "That's all, stop editing!" comment.
356
+ $anchor = $this->find_first_token_location( T_COMMENT, "That's all, stop editing!" );
357
+ if ( null !== $anchor ) {
358
+ return $anchor;
359
+ }
360
+
361
+ // If not found, try the "Absolute path to the WordPress directory." doc comment.
362
+ $anchor = $this->find_first_token_location( T_DOC_COMMENT, 'Absolute path to the WordPress directory.' );
363
+ if ( null !== $anchor ) {
364
+ return $anchor;
365
+ }
366
+
367
+ // If not found, try the "Sets up WordPress vars and included files." doc comment.
368
+ $anchor = $this->find_first_token_location( T_DOC_COMMENT, 'Sets up WordPress vars and included files.' );
369
+ if ( null !== $anchor ) {
370
+ return $anchor;
371
+ }
372
+
373
+ // If not found, try "require_once ABSPATH . 'wp-settings.php';".
374
+ $anchor = $this->find_first_token_location( T_REQUIRE_ONCE );
375
+ if ( null !== $anchor ) {
376
+ return $anchor;
377
+ }
378
+
379
+ // If not found, fall back to the PHP opening tag.
380
+ $open_tag_anchor = $this->find_first_token_location( T_OPEN_TAG );
381
+ if ( null !== $open_tag_anchor ) {
382
+ return $open_tag_anchor + 1;
383
+ }
384
+
385
+ // If we still don't have an anchor, the file is not a valid PHP file.
386
+ throw new Exception( "The 'wp-config.php' file is not a valid PHP file." );
387
+ }
388
+
389
+ /**
390
+ * Get the location to inject new code in the token array.
391
+ *
392
+ * @return int The location for injected code in the token array.
393
+ */
394
+ private function get_injected_code_location(): int {
395
+ // First try to find the "/** Sets up WordPress vars and included files. */" comment.
396
+ $anchor = $this->find_first_token_location( T_DOC_COMMENT, 'Sets up WordPress vars and included files.' );
397
+ if ( null !== $anchor ) {
398
+ return $anchor;
399
+ }
400
+
401
+ // If not found, try "require_once ABSPATH . 'wp-settings.php';".
402
+ $anchor = $this->find_require_wp_settings_location();
403
+ if ( null !== $anchor ) {
404
+ return $anchor;
405
+ }
406
+
407
+ // If not found, fall back to the PHP opening tag.
408
+ $open_tag_anchor = $this->find_first_token_location( T_OPEN_TAG );
409
+ if ( null !== $open_tag_anchor ) {
410
+ return $open_tag_anchor + 1;
411
+ }
412
+
413
+ // If we still don't have an anchor, the file is not a valid PHP file.
414
+ throw new Exception( "The 'wp-config.php' file is not a valid PHP file." );
415
+ }
416
+
417
+ /**
418
+ * Find location of the "wp-settings.php" require statement in the token array.
419
+ *
420
+ * This method searches for the following statement:
421
+ *
422
+ * require_once ABSPATH . 'wp-settings.php';
423
+ *
424
+ * @return int|null The location of the require statement.
425
+ */
426
+ private function find_require_wp_settings_location(): ?int {
427
+ $require_anchor = $this->find_first_token_location( T_REQUIRE_ONCE );
428
+ if ( null === $require_anchor ) {
429
+ return null;
430
+ }
431
+
432
+ $abspath = $this->tokens[ $require_anchor + 2 ] ?? null;
433
+ $path = $this->tokens[ $require_anchor + 6 ] ?? null;
434
+ if (
435
+ ( is_array( $abspath ) && 'ABSPATH' === $abspath[1] )
436
+ && ( is_array( $path ) && "'wp-settings.php'" === $path[1] )
437
+ ) {
438
+ return $require_anchor;
439
+ }
440
+ return null;
441
+ }
442
+
443
+ /**
444
+ * Find location of the first token of a given type in the token array.
445
+ *
446
+ * @param int $type The type of the token.
447
+ * @param string $search Optional. A search string to match against the token content.
448
+ * @return int|null The location of the first token.
449
+ */
450
+ private function find_first_token_location( int $type, ?string $search = null ): ?int {
451
+ foreach ( $this->tokens as $i => $token ) {
452
+ if ( is_array( $token ) && $type === $token[0] ) {
453
+ if ( null === $search || false !== strpos( $token[1], $search ) ) {
454
+ return $i;
455
+ }
456
+ }
457
+ }
458
+ return null;
459
+ }
460
+
461
+ /**
462
+ * Check if a token is whitespace or a comment.
463
+ *
464
+ * @param array|string $token The token to check.
465
+ * @return bool True if the token is whitespace or a comment.
466
+ */
467
+ private function is_whitespace( $token ): bool {
468
+ return is_array( $token )
469
+ && ( T_WHITESPACE === $token[0] || T_COMMENT === $token[0] || T_DOC_COMMENT === $token[0] );
470
+ }
345
471
  }
346
- `;async function g(e,n,t,i="rewrite"){const r=a.phpVars({wpConfigPath:n,constants:t,whenAlreadyDefined:i});if((await e.run({code:`<?php ob_start(); ?>
347
- ${x}
348
- $wp_config_path = ${r.wpConfigPath};
349
- $wp_config = file_get_contents($wp_config_path);
350
- $new_wp_config = rewrite_wp_config_to_define_constants($wp_config, ${r.constants}, ${r.whenAlreadyDefined});
351
- $return_value = file_put_contents($wp_config_path, $new_wp_config);
352
- ob_clean();
353
- echo false === $return_value ? '0' : '1';
354
- ob_end_flush();
355
- `})).text!=="1")throw new Error("Failed to rewrite constants in wp-config.php.")}async function $(e,n){const t=a.joinPaths(n,"wp-config.php"),i={DB_NAME:"wordpress"};!e.fileExists(t)&&e.fileExists(a.joinPaths(n,"wp-config-sample.php"))&&await e.writeFile(t,await e.readFileAsBuffer(a.joinPaths(n,"wp-config-sample.php"))),await g(e,t,i,"skip")}async function I(e){const n=await m(e);return await w(n,e),n}async function w(e,n){var d,s;const t=await e.getPrimaryPhp();if((d=n.hooks)!=null&&d.beforeWordPressFiles&&await n.hooks.beforeWordPressFiles(t),n.wordPressZip&&await S(t,await n.wordPressZip),n.constants)for(const l in n.constants)t.defineConstant(l,n.constants[l]);n.dataSqlPath&&(t.defineConstant("DB_DIR",a.dirname(n.dataSqlPath)),t.defineConstant("DB_FILE",a.basename(n.dataSqlPath))),t.defineConstant("WP_HOME",n.siteUrl),t.defineConstant("WP_SITEURL",n.siteUrl),await $(t,e.documentRoot),(s=n.hooks)!=null&&s.beforeDatabaseSetup&&await n.hooks.beforeDatabaseSetup(t);let i=!1;n.sqliteIntegrationPluginZip&&(i=!0,await T(t,await n.sqliteIntegrationPluginZip));const r=n.wordpressInstallMode??"download-and-install",o=!!n.dataSqlPath;if(["download-and-install","install-from-existing-files"].includes(r)){await f(e,{usesSqlite:i,hasCustomDatabasePath:o});try{await _(t)}catch(l){throw o||await p(e),l}o||await p(e)}else if(r==="install-from-existing-files-if-needed"){if(await f(e,{usesSqlite:i,hasCustomDatabasePath:o}),!await b(t))try{await _(t)}catch(l){throw o||await p(e),l}o||await p(e)}return e}async function f(e,{usesSqlite:n,hasCustomDatabasePath:t}){const i=await e.getPrimaryPhp();if(i.isFile("/internal/shared/preload/0-sqlite.php"))return;const r=a.joinPaths(e.documentRoot,"wp-content/mu-plugins/sqlite-database-integration");if(!i.isDir(r)&&!n&&!t)throw new Error("Error connecting to the MySQL database.")}async function p(e){const n=await e.getPrimaryPhp();if(await L(n))return;if(n.isFile("/internal/shared/preload/0-sqlite.php"))throw new Error("Error connecting to the SQLite database.");const i=a.joinPaths(e.documentRoot,"wp-content/mu-plugins/sqlite-database-integration");throw n.isDir(i)?new Error("Error connecting to the SQLite database."):new Error("Error connecting to the MySQL database.")}async function m(e){const n=e.spawnHandler??u.sandboxedSpawnHandlerFactory;async function t(r,o=!1){const d=await e.createPhpRuntime(o),s=new u.PHP(d);if(e.sapiName&&s.setSapiName(e.sapiName),r&&(s.requestHandler=r),e.phpIniEntries&&u.setPhpIniEntries(s,e.phpIniEntries),s.defineConstant("WP_SQLITE_AST_DRIVER",!0),e.constants)for(const l in e.constants)s.defineConstant(l,e.constants[l]);return o&&!s.isFile("/internal/.boot-files-written")&&(await E(s),await u.writeFiles(s,"/",e.createFiles||{}),await R(s,a.joinPaths(new URL(e.siteUrl).pathname,"phpinfo.php")),await u.writeFiles(s,"/internal",{".boot-files-written":""})),n&&await s.setSpawnHandler(n(r?()=>r.instanceManager.acquirePHPInstance():void 0)),s.enableRuntimeRotation({recreateRuntime:e.createPhpRuntime,maxRequests:400}),e.onPHPInstanceCreated&&await e.onPHPInstanceCreated(s,{isPrimary:o}),s}const i=new u.PHPRequestHandler({documentRoot:e.documentRoot||"/wordpress",absoluteUrl:e.siteUrl,rewriteRules:k,pathAliases:e.pathAliases,getFileNotFoundAction:e.getFileNotFoundAction??P,cookieStore:e.cookieStore,php:e.maxPhpInstances===1?await t(void 0,!0):void 0,phpFactory:e.maxPhpInstances!==1?async({isPrimary:r})=>t(i,r):void 0,maxPhpInstances:e.maxPhpInstances});return i}async function b(e){return(await e.run({code:`<?php
472
+ `;async function g(t,e){const n=r.joinPaths(e,"wp-config.php"),i={DB_NAME:"wordpress"};if(!t.fileExists(n)&&t.fileExists(r.joinPaths(e,"wp-config-sample.php"))&&await t.writeFile(n,await t.readFileAsBuffer(r.joinPaths(e,"wp-config-sample.php"))),!t.fileExists(n))return;const a=r.phpVars({wpConfigPath:n,constants:i});if((await t.run({code:`${$}
473
+ $wp_config_path = ${a.wpConfigPath};
474
+ $transformer = WP_Config_Transformer::from_file($wp_config_path);
475
+ foreach ( ${a.constants} as $name => $value ) {
476
+ if ( ! $transformer->constant_exists( $name ) ) {
477
+ $transformer->define_constant($name, $value);
478
+ }
479
+ }
480
+ $transformer->to_file($wp_config_path);
481
+ `})).errors.length>0)throw new Error("Failed to auto-configure wp-config.php.")}async function I(t,e,n){const i=r.phpVars({wpConfigPath:e,constants:n});if((await t.run({code:`${$}
482
+ $wp_config_path = ${i.wpConfigPath};
483
+ $transformer = WP_Config_Transformer::from_file($wp_config_path);
484
+ $transformer->define_constants(${i.constants});
485
+ $transformer->to_file($wp_config_path);
486
+ `})).errors.length>0)throw new Error("Failed to rewrite constants in wp-config.php.")}async function x(t){const e=await w(t);return await m(e,t),e}async function m(t,e){var c,o;const n=await t.getPrimaryPhp();if((c=e.hooks)!=null&&c.beforeWordPressFiles&&await e.hooks.beforeWordPressFiles(n),e.wordPressZip&&await R(n,await e.wordPressZip),e.constants)for(const l in e.constants)n.defineConstant(l,e.constants[l]);e.dataSqlPath&&(n.defineConstant("DB_DIR",r.dirname(e.dataSqlPath)),n.defineConstant("DB_FILE",r.basename(e.dataSqlPath))),n.defineConstant("WP_HOME",e.siteUrl),n.defineConstant("WP_SITEURL",e.siteUrl),await g(n,t.documentRoot),(o=e.hooks)!=null&&o.beforeDatabaseSetup&&await e.hooks.beforeDatabaseSetup(n);let i=!1;e.sqliteIntegrationPluginZip&&(i=!0,await E(n,await e.sqliteIntegrationPluginZip));const a=e.wordpressInstallMode??"download-and-install",s=!!e.dataSqlPath;if(["download-and-install","install-from-existing-files"].includes(a)){await h(t,{usesSqlite:i,hasCustomDatabasePath:s});try{await f(n)}catch(l){throw s||await u(t),l}s||await u(t)}else if(a==="install-from-existing-files-if-needed"){if(await h(t,{usesSqlite:i,hasCustomDatabasePath:s}),!await k(n))try{await f(n)}catch(l){throw s||await u(t),l}s||await u(t)}return t}async function h(t,{usesSqlite:e,hasCustomDatabasePath:n}){const i=await t.getPrimaryPhp();if(i.isFile("/internal/shared/preload/0-sqlite.php"))return;const a=r.joinPaths(t.documentRoot,"wp-content/mu-plugins/sqlite-database-integration");if(!i.isDir(a)&&!e&&!n)throw new Error("Error connecting to the MySQL database.")}async function u(t){const e=await t.getPrimaryPhp();if(await C(e))return;if(e.isFile("/internal/shared/preload/0-sqlite.php"))throw new Error("Error connecting to the SQLite database.");const i=r.joinPaths(t.documentRoot,"wp-content/mu-plugins/sqlite-database-integration");throw e.isDir(i)?new Error("Error connecting to the SQLite database."):new Error("Error connecting to the MySQL database.")}async function w(t){const e=t.spawnHandler??d.sandboxedSpawnHandlerFactory;async function n(a,s=!1){const c=await t.createPhpRuntime(s),o=new d.PHP(c);if(t.sapiName&&o.setSapiName(t.sapiName),a&&(o.requestHandler=a),t.phpIniEntries&&d.setPhpIniEntries(o,t.phpIniEntries),o.defineConstant("WP_SQLITE_AST_DRIVER",!0),t.constants)for(const l in t.constants)o.defineConstant(l,t.constants[l]);return s&&!o.isFile("/internal/.boot-files-written")&&(await T(o),await d.writeFiles(o,"/",t.createFiles||{}),await v(o,r.joinPaths(new URL(t.siteUrl).pathname,"phpinfo.php")),await d.writeFiles(o,"/internal",{".boot-files-written":""})),e&&await o.setSpawnHandler(e(a?()=>a.instanceManager.acquirePHPInstance():void 0)),o.enableRuntimeRotation({recreateRuntime:t.createPhpRuntime,maxRequests:400}),t.onPHPInstanceCreated&&await t.onPHPInstanceCreated(o,{isPrimary:s}),o}const i=new d.PHPRequestHandler({documentRoot:t.documentRoot||"/wordpress",absoluteUrl:t.siteUrl,rewriteRules:b,pathAliases:t.pathAliases,getFileNotFoundAction:t.getFileNotFoundAction??P,cookieStore:t.cookieStore,php:t.maxPhpInstances===1?await n(void 0,!0):void 0,phpFactory:t.maxPhpInstances!==1?async({isPrimary:a})=>n(i,a):void 0,maxPhpInstances:t.maxPhpInstances});return i}async function k(t){return(await t.run({code:`<?php
356
487
  ob_start();
357
488
  $wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
358
489
  if (!file_exists($wp_load)) {
@@ -363,7 +494,7 @@ function skip_whitespace($tokens) {
363
494
  ob_clean();
364
495
  echo is_blog_installed() ? '1' : '0';
365
496
  ob_end_flush();
366
- `,env:{DOCUMENT_ROOT:e.documentRoot}})).text==="1"}async function _(e){var i;const n=await u.withPHPIniValues(e,{disable_functions:"fsockopen",allow_url_fopen:"0"},async()=>await e.request({url:"/wp-admin/install.php?step=2",method:"POST",body:{language:"en",prefix:"wp_",weblog_title:"My WordPress Website",user_name:"admin",admin_password:"password",admin_password2:"password",Submit:"Install WordPress",pw_weak:"1",admin_email:"admin@localhost.com"}}));if(!await b(e))throw new Error(`Failed to install WordPress – installer responded with "${(i=n.text)==null?void 0:i.substring(0,100)}"`);(await e.run({code:`<?php
497
+ `,env:{DOCUMENT_ROOT:t.documentRoot}})).text==="1"}async function f(t){var i;const e=await d.withPHPIniValues(t,{disable_functions:"fsockopen",allow_url_fopen:"0"},async()=>await t.request({url:"/wp-admin/install.php?step=2",method:"POST",body:{language:"en",prefix:"wp_",weblog_title:"My WordPress Website",user_name:"admin",admin_password:"password",admin_password2:"password",Submit:"Install WordPress",pw_weak:"1",admin_email:"admin@localhost.com"}}));if(!await k(t))throw new Error(`Failed to install WordPress – installer responded with "${(i=e.text)==null?void 0:i.substring(0,100)}"`);(await t.run({code:`<?php
367
498
  ob_start();
368
499
  $wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
369
500
  if (!file_exists($wp_load)) {
@@ -383,7 +514,7 @@ function skip_whitespace($tokens) {
383
514
  echo '0';
384
515
  }
385
516
  ob_end_flush();
386
- `,env:{DOCUMENT_ROOT:e.documentRoot}})).text!=="1"&&h.logger.warn("Failed to default to pretty permalinks after WP install.")}function P(e){return{type:"internal-redirect",uri:"/index.php"}}async function L(e){return(await e.run({code:`<?php
517
+ `,env:{DOCUMENT_ROOT:t.documentRoot}})).text!=="1"&&_.logger.warn("Failed to default to pretty permalinks after WP install.")}function P(t){return{type:"internal-redirect",uri:"/index.php"}}async function C(t){return(await t.run({code:`<?php
387
518
  ob_start();
388
519
  $wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
389
520
  if (!file_exists($wp_load)) {
@@ -394,10 +525,10 @@ function skip_whitespace($tokens) {
394
525
  ob_clean();
395
526
  echo $wpdb->check_connection( false ) ? '1' : '0';
396
527
  ob_end_flush();
397
- `,env:{DOCUMENT_ROOT:e.documentRoot}})).text==="1"}async function W(e){const{php:n,reap:t}=await e.instanceManager.acquirePHPInstance();try{const r=(await n.run({code:`<?php
398
- require '${e.documentRoot}/wp-includes/version.php';
528
+ `,env:{DOCUMENT_ROOT:t.documentRoot}})).text==="1"}async function L(t){const{php:e,reap:n}=await t.instanceManager.acquirePHPInstance();try{const a=(await e.run({code:`<?php
529
+ require '${t.documentRoot}/wp-includes/version.php';
399
530
  echo $wp_version;
400
- `})).text;if(!r)throw new Error("Unable to read loaded WordPress version.");return y(r)}finally{t()}}function y(e){if(/-(alpha|beta|RC)\d*-\d+$/.test(e))return"trunk";if(/-(beta|RC)\d*$/.test(e))return"beta";const i=e.match(/^(\d+\.\d+)(?:\.\d+)?$/);return i!==null?i[1]:e}const k=[{match:new RegExp("^(/[_0-9a-zA-Z-]+)?(/wp-(content|admin|includes)/.*)"),replacement:"$2"}];async function E(e){await e.mkdir("/internal/shared/mu-plugins"),await e.writeFile("/internal/shared/preload/env.php",`<?php
531
+ `})).text;if(!a)throw new Error("Unable to read loaded WordPress version.");return y(a)}finally{n()}}function y(t){if(/-(alpha|beta|RC)\d*-\d+$/.test(t))return"trunk";if(/-(beta|RC)\d*$/.test(t))return"beta";const i=t.match(/^(\d+\.\d+)(?:\.\d+)?$/);return i!==null?i[1]:t}const b=[{match:new RegExp("^(/[_0-9a-zA-Z-]+)?(/wp-(content|admin|includes)/.*)"),replacement:"$2"}];async function T(t){await t.mkdir("/internal/shared/mu-plugins"),await t.writeFile("/internal/shared/preload/env.php",`<?php
401
532
 
402
533
  // Allow adding filters/actions prior to loading WordPress.
403
534
  // $function_to_add MUST be a string.
@@ -424,7 +555,7 @@ function skip_whitespace($tokens) {
424
555
  require_once $mu_plugin;
425
556
  }
426
557
  }
427
- `),await e.writeFile("/internal/shared/mu-plugins/1-auto-login.php",`<?php
558
+ `),await t.writeFile("/internal/shared/mu-plugins/1-auto-login.php",`<?php
428
559
  /**
429
560
  * Returns the username to auto-login as, if any.
430
561
  * @return string|false
@@ -578,7 +709,7 @@ function skip_whitespace($tokens) {
578
709
  }
579
710
  return $interval;
580
711
  });
581
- `),await e.writeFile("/internal/shared/mu-plugins/0-playground.php",`<?php
712
+ `),await t.writeFile("/internal/shared/mu-plugins/0-playground.php",`<?php
582
713
  // Needed because gethostbyname( 'wordpress.org' ) returns
583
714
  // a private network IP address for some reason.
584
715
  add_filter( 'allowed_redirect_hosts', function( $deprecated = '' ) {
@@ -620,7 +751,7 @@ function skip_whitespace($tokens) {
620
751
  $log_file = WP_CONTENT_DIR . '/debug.log';
621
752
  define('ERROR_LOG_FILE', $log_file);
622
753
  ini_set('error_log', $log_file);
623
- ?>`),await e.writeFile("/internal/shared/mu-plugins/sitemap-redirect.php",`<?php
754
+ ?>`),await t.writeFile("/internal/shared/mu-plugins/sitemap-redirect.php",`<?php
624
755
  /**
625
756
  * Redirect sitemap.xml to wp-sitemap.xml for non-root installations.
626
757
  *
@@ -644,7 +775,7 @@ function skip_whitespace($tokens) {
644
775
  exit;
645
776
  }
646
777
  }
647
- `),await e.writeFile("/internal/shared/preload/error-handler.php",`<?php
778
+ `),await t.writeFile("/internal/shared/preload/error-handler.php",`<?php
648
779
  (function() {
649
780
  $playground_consts = [];
650
781
  if(file_exists('/internal/shared/consts.json')) {
@@ -692,18 +823,18 @@ function skip_whitespace($tokens) {
692
823
  }
693
824
  return false;
694
825
  });
695
- })();`)}async function R(e,n="/phpinfo.php"){await e.writeFile("/internal/shared/preload/phpinfo.php",`<?php
826
+ })();`)}async function v(t,e="/phpinfo.php"){await t.writeFile("/internal/shared/preload/phpinfo.php",`<?php
696
827
  // Render PHPInfo if the requested page is /phpinfo.php
697
- if ( isset($_SERVER['REQUEST_URI']) && ${a.phpVar(n)} === $_SERVER['REQUEST_URI'] ) {
828
+ if ( isset($_SERVER['REQUEST_URI']) && ${r.phpVar(e)} === $_SERVER['REQUEST_URI'] ) {
698
829
  phpinfo();
699
830
  exit;
700
831
  }
701
- `)}async function T(e,n){await e.isDir("/tmp/sqlite-database-integration")&&await e.rmdir("/tmp/sqlite-database-integration",{recursive:!0}),await e.mkdir("/tmp/sqlite-database-integration"),await c.unzipFile(e,n,"/tmp/sqlite-database-integration");const t="/internal/shared/sqlite-database-integration",i=`/tmp/sqlite-database-integration/${(await e.listFiles("/tmp/sqlite-database-integration"))[0]}`;await e.mv(i,t),await e.defineConstant("SQLITE_MAIN_FILE","1");const o=(await e.readFileAsText(a.joinPaths(t,"db.copy"))).replace("'{SQLITE_IMPLEMENTATION_FOLDER_PATH}'",a.phpVar(t)).replace("'{SQLITE_PLUGIN}'",a.phpVar(a.joinPaths(t,"load.php"))),d=a.joinPaths(await e.documentRoot,"wp-content/db.php"),s=`<?php
832
+ `)}async function E(t,e){await t.isDir("/tmp/sqlite-database-integration")&&await t.rmdir("/tmp/sqlite-database-integration",{recursive:!0}),await t.mkdir("/tmp/sqlite-database-integration"),await p.unzipFile(t,e,"/tmp/sqlite-database-integration");const n="/internal/shared/sqlite-database-integration",i=`/tmp/sqlite-database-integration/${(await t.listFiles("/tmp/sqlite-database-integration"))[0]}`;await t.mv(i,n),await t.defineConstant("SQLITE_MAIN_FILE","1");const s=(await t.readFileAsText(r.joinPaths(n,"db.copy"))).replace("'{SQLITE_IMPLEMENTATION_FOLDER_PATH}'",r.phpVar(n)).replace("'{SQLITE_PLUGIN}'",r.phpVar(r.joinPaths(n,"load.php"))),c=r.joinPaths(await t.documentRoot,"wp-content/db.php"),o=`<?php
702
833
  // Do not preload this if WordPress comes with a custom db.php file.
703
- if(file_exists(${a.phpVar(d)})) {
834
+ if(file_exists(${r.phpVar(c)})) {
704
835
  return;
705
836
  }
706
- ?>`,l="/internal/shared/mu-plugins/sqlite-database-integration.php";await e.writeFile(l,s+o),await e.writeFile("/internal/shared/preload/0-sqlite.php",s+`<?php
837
+ ?>`,l="/internal/shared/mu-plugins/sqlite-database-integration.php";await t.writeFile(l,o+s),await t.writeFile("/internal/shared/preload/0-sqlite.php",o+`<?php
707
838
 
708
839
  /**
709
840
  * Loads the SQLite integration plugin before WordPress is loaded
@@ -753,7 +884,7 @@ class Playground_SQLite_Integration_Loader {
753
884
  $GLOBALS['wpdb']->$name = $value;
754
885
  }
755
886
  protected function load_sqlite_integration() {
756
- require_once ${a.phpVar(l)};
887
+ require_once ${r.phpVar(l)};
757
888
  }
758
889
  }
759
890
  /**
@@ -778,11 +909,11 @@ if(!function_exists('mysqli_connect')) {
778
909
  function mysqli_connect() {}
779
910
  }
780
911
 
781
- `),await e.writeFile("/internal/shared/mu-plugins/sqlite-test.php",`<?php
912
+ `),await t.writeFile("/internal/shared/mu-plugins/sqlite-test.php",`<?php
782
913
  global $wpdb;
783
914
  if(!($wpdb instanceof WP_SQLite_DB)) {
784
915
  var_dump(isset($wpdb));
785
916
  die("SQLite integration not loaded " . get_class($wpdb));
786
917
  }
787
- `)}async function S(e,n){e.mkdir("/tmp/unzipped-wordpress"),await c.unzipFile(e,n,"/tmp/unzipped-wordpress"),e.fileExists("/tmp/unzipped-wordpress/wordpress.zip")&&await c.unzipFile(e,"/tmp/unzipped-wordpress/wordpress.zip","/tmp/unzipped-wordpress");let t=e.fileExists("/tmp/unzipped-wordpress/wordpress")?"/tmp/unzipped-wordpress/wordpress":e.fileExists("/tmp/unzipped-wordpress/build")?"/tmp/unzipped-wordpress/build":"/tmp/unzipped-wordpress";if(!e.fileExists(a.joinPaths(t,"wp-config-sample.php"))){const r=e.listFiles(t);if(r.length){const o=r[0];e.fileExists(a.joinPaths(t,o,"wp-config-sample.php"))&&(t=a.joinPaths(t,o))}}const i=(r,o,d)=>{if(d.isDir(r)&&d.isDir(o))for(const s of d.listFiles(r)){const l=a.joinPaths(r,s),v=a.joinPaths(o,s);i(l,v,d)}else{if(d.fileExists(o)){const s=r.replace(/^\/tmp\/unzipped-wordpress\//,"/");h.logger.warn(`Cannot unzip WordPress files at ${o}: ${s} already exists.`);return}d.mv(r,o)}};i(t,e.documentRoot,e),e.fileExists(t)&&e.rmdir(t,{recursive:!0}),!e.fileExists(a.joinPaths(e.documentRoot,"wp-config.php"))&&e.fileExists(a.joinPaths(e.documentRoot,"wp-config-sample.php"))&&e.writeFile(a.joinPaths(e.documentRoot,"wp-config.php"),e.readFileAsText(a.joinPaths(e.documentRoot,"/wp-config-sample.php")))}const U=c.createMemoizedFetch(fetch),C="https://github.com/WordPress/WordPress/archive/refs/heads/master.zip";async function O(e="latest"){if(e===null)e="latest";else if(e.startsWith("https://")||e.startsWith("http://")){const i=await crypto.subtle.digest("SHA-1",new TextEncoder().encode(e)),r=Array.from(new Uint8Array(i)).map(o=>o.toString(16).padStart(2,"0")).join("");return{releaseUrl:e,version:"custom-"+r.substring(0,8),source:"inferred"}}else if(e==="trunk"||e==="nightly"){const i=new Date().toISOString().split("T")[0];return{releaseUrl:`${C}?ts=${i}`,version:"trunk",source:"inferred"}}let t=await(await U("https://api.wordpress.org/core/version-check/1.7/?channel=beta")).json();t=t.offers.filter(i=>i.response==="autoupdate");for(const i of t){if(e==="beta"&&(i.version.includes("beta")||i.version.includes("RC")))return{releaseUrl:i.download,version:i.version,source:"api"};if(e==="latest"&&!i.version.includes("beta")&&!i.version.includes("RC"))return{releaseUrl:i.download,version:i.version,source:"api"};if(i.version.substring(0,e.length)===e)return{releaseUrl:i.download,version:i.version,source:"api"}}return e.match(/^\d+\.\d+\.0$/)&&(e=e.split(".").slice(0,2).join(".")),{releaseUrl:`https://wordpress.org/wordpress-${e}.zip`,version:e,source:"inferred"}}exports.bootRequestHandler=m;exports.bootWordPress=w;exports.bootWordPressAndRequestHandler=I;exports.defineWpConfigConstants=g;exports.ensureWpConfig=$;exports.getFileNotFoundActionForWordPress=P;exports.getLoadedWordPressVersion=W;exports.preloadPhpInfoRoute=R;exports.preloadSqliteIntegration=T;exports.resolveWordPressRelease=O;exports.setupPlatformLevelMuPlugins=E;exports.unzipWordPress=S;exports.versionStringToLoadedWordPressVersion=y;exports.wordPressRewriteRules=k;
918
+ `)}async function R(t,e){t.mkdir("/tmp/unzipped-wordpress"),await p.unzipFile(t,e,"/tmp/unzipped-wordpress"),t.fileExists("/tmp/unzipped-wordpress/wordpress.zip")&&await p.unzipFile(t,"/tmp/unzipped-wordpress/wordpress.zip","/tmp/unzipped-wordpress");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";if(!t.fileExists(r.joinPaths(n,"wp-config-sample.php"))){const a=t.listFiles(n);if(a.length){const s=a[0];t.fileExists(r.joinPaths(n,s,"wp-config-sample.php"))&&(n=r.joinPaths(n,s))}}const i=(a,s,c)=>{if(c.isDir(a)&&c.isDir(s))for(const o of c.listFiles(a)){const l=r.joinPaths(a,o),S=r.joinPaths(s,o);i(l,S,c)}else{if(c.fileExists(s)){const o=a.replace(/^\/tmp\/unzipped-wordpress\//,"/");_.logger.warn(`Cannot unzip WordPress files at ${s}: ${o} already exists.`);return}c.mv(a,s)}};i(n,t.documentRoot,t),t.fileExists(n)&&t.rmdir(n,{recursive:!0}),!t.fileExists(r.joinPaths(t.documentRoot,"wp-config.php"))&&t.fileExists(r.joinPaths(t.documentRoot,"wp-config-sample.php"))&&t.writeFile(r.joinPaths(t.documentRoot,"wp-config.php"),t.readFileAsText(r.joinPaths(t.documentRoot,"/wp-config-sample.php")))}const W=p.createMemoizedFetch(fetch),q="https://github.com/WordPress/WordPress/archive/refs/heads/master.zip";async function A(t="latest"){if(t===null)t="latest";else if(t.startsWith("https://")||t.startsWith("http://")){const i=await crypto.subtle.digest("SHA-1",new TextEncoder().encode(t)),a=Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("");return{releaseUrl:t,version:"custom-"+a.substring(0,8),source:"inferred"}}else if(t==="trunk"||t==="nightly"){const i=new Date().toISOString().split("T")[0];return{releaseUrl:`${q}?ts=${i}`,version:"trunk",source:"inferred"}}let n=await(await W("https://api.wordpress.org/core/version-check/1.7/?channel=beta")).json();n=n.offers.filter(i=>i.response==="autoupdate");for(const i of n){if(t==="beta"&&(i.version.includes("beta")||i.version.includes("RC")))return{releaseUrl:i.download,version:i.version,source:"api"};if(t==="latest"&&!i.version.includes("beta")&&!i.version.includes("RC"))return{releaseUrl:i.download,version:i.version,source:"api"};if(i.version.substring(0,t.length)===t)return{releaseUrl:i.download,version:i.version,source:"api"}}return t.match(/^\d+\.\d+\.0$/)&&(t=t.split(".").slice(0,2).join(".")),{releaseUrl:`https://wordpress.org/wordpress-${t}.zip`,version:t,source:"inferred"}}exports.bootRequestHandler=w;exports.bootWordPress=m;exports.bootWordPressAndRequestHandler=x;exports.defineWpConfigConstants=I;exports.ensureWpConfig=g;exports.getFileNotFoundActionForWordPress=P;exports.getLoadedWordPressVersion=L;exports.preloadPhpInfoRoute=v;exports.preloadSqliteIntegration=E;exports.resolveWordPressRelease=A;exports.setupPlatformLevelMuPlugins=T;exports.unzipWordPress=R;exports.versionStringToLoadedWordPressVersion=y;exports.wordPressRewriteRules=b;
788
919
  //# sourceMappingURL=index.cjs.map