@wp-playground/wordpress 1.0.28 → 1.1.0

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 ADDED
@@ -0,0 +1,696 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("@php-wasm/util"),p=require("@wp-playground/common"),l=require("@php-wasm/universal"),P=require("@php-wasm/logger"),k=`<?php
2
+
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
+ * \`\`\`
66
+ *
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
75
+ */
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
+ }
102
+
103
+ // Maybe we didn't find a define call and reached the end of the file?
104
+ if (!count($tokens)) {
105
+ break;
106
+ }
107
+
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
+ }
123
+
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
+ }
137
+
138
+ $defined_expressions[] = stringify_tokens(skip_whitespace($defined_expression));
139
+ continue;
140
+ }
141
+
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
+ }
151
+
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
+ }
170
+
171
+ // Don't capture the comma as a part of the constant name
172
+ $name_buffer[] = $token;
173
+ }
174
+
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
+ }
201
+
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
+ }
211
+
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
+ }
232
+
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
+ }
259
+
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] . ';');
263
+
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
+ }
270
+
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
+ }
278
+
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
+ }
319
+
320
+ // Translate the output tokens back into a string
321
+ return stringify_tokens($output);
322
+ }
323
+
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
+ }
335
+
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;
345
+ }
346
+ `;async function _(e,r,t,n="rewrite"){const o=i.phpVars({wpConfigPath:r,constants:t,whenAlreadyDefined:n});if((await e.run({code:`<?php ob_start(); ?>
347
+ ${k}
348
+ $wp_config_path = ${o.wpConfigPath};
349
+ $wp_config = file_get_contents($wp_config_path);
350
+ $new_wp_config = rewrite_wp_config_to_define_constants($wp_config, ${o.constants}, ${o.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 c(e,r){const t=i.joinPaths(r,"wp-config.php"),n={DB_NAME:"wordpress"};!e.fileExists(t)&&e.fileExists(i.joinPaths(r,"wp-config-sample.php"))&&await e.writeFile(t,await e.readFileAsBuffer(i.joinPaths(r,"wp-config-sample.php"))),await _(e,t,n,"skip")}async function E(e){var o,d;async function r(a,u){const s=new l.PHP(await e.createPhpRuntime());return e.sapiName&&s.setSapiName(e.sapiName),a&&(s.requestHandler=a),e.phpIniEntries&&l.setPhpIniEntries(s,e.phpIniEntries),u?(await w(s),await l.writeFiles(s,"/",e.createFiles||{}),await m(s,i.joinPaths(new URL(e.siteUrl).pathname,"phpinfo.php"))):l.proxyFileSystem(await a.getPrimaryPhp(),s,["/tmp",a.documentRoot,"/internal/shared"]),e.spawnHandler&&await s.setSpawnHandler(e.spawnHandler(a.processManager)),l.rotatePHPRuntime({php:s,cwd:a.documentRoot,recreateRuntime:e.createPhpRuntime,maxRequests:400}),s}const t=new l.PHPRequestHandler({phpFactory:async({isPrimary:a})=>r(t,a),documentRoot:e.documentRoot||"/wordpress",absoluteUrl:e.siteUrl,rewriteRules:$,getFileNotFoundAction:e.getFileNotFoundAction??h,cookieStore:e.cookieStore}),n=await t.getPrimaryPhp();if((o=e.hooks)!=null&&o.beforeWordPressFiles&&await e.hooks.beforeWordPressFiles(n),e.wordPressZip&&await y(n,await e.wordPressZip),e.constants)for(const a in e.constants)n.defineConstant(a,e.constants[a]);if(n.defineConstant("WP_HOME",e.siteUrl),n.defineConstant("WP_SITEURL",e.siteUrl),await c(n,t.documentRoot),(d=e.hooks)!=null&&d.beforeDatabaseSetup&&await e.hooks.beforeDatabaseSetup(n),e.sqliteIntegrationPluginZip&&await b(n,await e.sqliteIntegrationPluginZip),await f(n)||await T(n),!await f(n))throw new Error("WordPress installation has failed.");return t}async function f(e){return(await e.run({code:`<?php
356
+ ob_start();
357
+ $wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
358
+ if (!file_exists($wp_load)) {
359
+ echo '-1';
360
+ exit;
361
+ }
362
+ require $wp_load;
363
+ ob_clean();
364
+ echo is_blog_installed() ? '1' : '0';
365
+ ob_end_flush();
366
+ `,env:{DOCUMENT_ROOT:e.documentRoot}})).text==="1"}async function T(e){await l.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"}})),(await e.run({code:`<?php
367
+ ob_start();
368
+ $wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
369
+ if (!file_exists($wp_load)) {
370
+ echo '0';
371
+ exit;
372
+ }
373
+ require $wp_load;
374
+ $option_result = update_option(
375
+ 'permalink_structure',
376
+ '/%year%/%monthnum%/%day%/%postname%/'
377
+ );
378
+ ob_clean();
379
+ echo $option_result ? '1' : '0';
380
+ ob_end_flush();
381
+ `,env:{DOCUMENT_ROOT:e.documentRoot}})).text!=="1"&&P.logger.warn("Failed to default to pretty permalinks after WP install.")}function h(e){return{type:"internal-redirect",uri:"/index.php"}}async function R(e){const n=(await(await e.getPrimaryPhp()).run({code:`<?php
382
+ require '${e.documentRoot}/wp-includes/version.php';
383
+ echo $wp_version;
384
+ `})).text;if(!n)throw new Error("Unable to read loaded WordPress version.");return g(n)}function g(e){if(/-(alpha|beta|RC)\d*-\d+$/.test(e))return"nightly";if(/-(beta|RC)\d*$/.test(e))return"beta";const n=e.match(/^(\d+\.\d+)(?:\.\d+)?$/);return n!==null?n[1]:e}const $=[{match:/^\/(.*?)(\/wp-(content|admin|includes)\/.*)/g,replacement:"$2"}];async function w(e){await e.mkdir("/internal/shared/mu-plugins"),await e.writeFile("/internal/shared/preload/env.php",`<?php
385
+
386
+ // Allow adding filters/actions prior to loading WordPress.
387
+ // $function_to_add MUST be a string.
388
+ function playground_add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
389
+ global $wp_filter;
390
+ $wp_filter[$tag][$priority][$function_to_add] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
391
+ }
392
+ function playground_add_action( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
393
+ playground_add_filter( $tag, $function_to_add, $priority, $accepted_args );
394
+ }
395
+
396
+ // Load our mu-plugins after customer mu-plugins
397
+ // NOTE: this means our mu-plugins can't use the muplugins_loaded action!
398
+ playground_add_action( 'muplugins_loaded', 'playground_load_mu_plugins', 0 );
399
+ function playground_load_mu_plugins() {
400
+ // Load all PHP files from /internal/shared/mu-plugins, sorted by filename
401
+ $mu_plugins_dir = '/internal/shared/mu-plugins';
402
+ if(!is_dir($mu_plugins_dir)){
403
+ return;
404
+ }
405
+ $mu_plugins = glob( $mu_plugins_dir . '/*.php' );
406
+ sort( $mu_plugins );
407
+ foreach ( $mu_plugins as $mu_plugin ) {
408
+ require_once $mu_plugin;
409
+ }
410
+ }
411
+ `),await e.writeFile("/internal/shared/mu-plugins/1-auto-login.php",`<?php
412
+ /**
413
+ * Returns the username to auto-login as, if any.
414
+ * @return string|false
415
+ */
416
+ function playground_get_username_for_auto_login() {
417
+ /**
418
+ * Allow users to auto-login as a specific user on their first visit.
419
+ *
420
+ * Prevent the auto-login if it already happened by checking for the
421
+ * playground_auto_login_already_happened cookie.
422
+ * This is used to allow the user to logout.
423
+ */
424
+ if ( defined('PLAYGROUND_AUTO_LOGIN_AS_USER') && !isset($_COOKIE['playground_auto_login_already_happened']) ) {
425
+ return PLAYGROUND_AUTO_LOGIN_AS_USER;
426
+ }
427
+ /**
428
+ * Allow users to auto-login as a specific user by passing the
429
+ * playground_force_auto_login_as_user GET parameter.
430
+ */
431
+ if ( defined('PLAYGROUND_FORCE_AUTO_LOGIN_ENABLED') && isset($_GET['playground_force_auto_login_as_user']) ) {
432
+ return $_GET['playground_force_auto_login_as_user'];
433
+ }
434
+ return false;
435
+ }
436
+
437
+ /**
438
+ * Logs the user in on their first visit if the Playground runtime told us to.
439
+ */
440
+ function playground_auto_login() {
441
+ /**
442
+ * The redirect should only run if the current PHP request is
443
+ * a HTTP request. If it's a PHP CLI run, we can't login the user
444
+ * because logins require cookies which aren't available in the CLI.
445
+ *
446
+ * Currently all Playground requests use the "cli" SAPI name
447
+ * to ensure support for WP-CLI, so the best way to distinguish
448
+ * between a CLI run and an HTTP request is by checking if the
449
+ * $_SERVER['REQUEST_URI'] global is set.
450
+ *
451
+ * If $_SERVER['REQUEST_URI'] is not set, we assume it's a CLI run.
452
+ */
453
+ if (empty($_SERVER['REQUEST_URI'])) {
454
+ return;
455
+ }
456
+ $user_name = playground_get_username_for_auto_login();
457
+ if ( false === $user_name ) {
458
+ return;
459
+ }
460
+ if (wp_doing_ajax() || defined('REST_REQUEST')) {
461
+ return;
462
+ }
463
+ if ( is_user_logged_in() ) {
464
+ return;
465
+ }
466
+ $user = get_user_by('login', $user_name);
467
+ if (!$user) {
468
+ return;
469
+ }
470
+
471
+ /**
472
+ * We're about to set cookies and redirect. It will log the user in
473
+ * if the headers haven't been sent yet.
474
+ *
475
+ * However, if they have been sent already – e.g. there a PHP
476
+ * notice was printed, we'll exit the script with a bunch of errors
477
+ * on the screen and without the user being logged in. This
478
+ * will happen on every page load and will effectively make Playground
479
+ * unusable.
480
+ *
481
+ * Therefore, we just won't auto-login if headers have been sent. Maybe
482
+ * we'll be able to finish the operation in one of the future requests
483
+ * or maybe not, but at least we won't end up with a permanent white screen.
484
+ */
485
+ if (headers_sent()) {
486
+ _doing_it_wrong('playground_auto_login', 'Headers already sent, the Playground runtime will not auto-login the user', '1.0.0');
487
+ return;
488
+ }
489
+
490
+ /**
491
+ * This approach is described in a comment on
492
+ * https://developer.wordpress.org/reference/functions/wp_set_current_user/
493
+ */
494
+ wp_set_current_user( $user->ID, $user->user_login );
495
+ wp_set_auth_cookie( $user->ID );
496
+ do_action( 'wp_login', $user->user_login, $user );
497
+
498
+ setcookie('playground_auto_login_already_happened', '1');
499
+
500
+ /**
501
+ * Confirm that nothing in WordPress, plugins, or filters have finalized
502
+ * the headers sending phase. See the comment above for more context.
503
+ */
504
+ if (headers_sent()) {
505
+ _doing_it_wrong('playground_auto_login', 'Headers already sent, the Playground runtime will not auto-login the user', '1.0.0');
506
+ return;
507
+ }
508
+
509
+ /**
510
+ * Reload page to ensure the user is logged in correctly.
511
+ * WordPress uses cookies to determine if the user is logged in,
512
+ * so we need to reload the page to ensure the cookies are set.
513
+ */
514
+ $redirect_url = $_SERVER['REQUEST_URI'];
515
+ /**
516
+ * Intentionally do not use wp_redirect() here. It removes
517
+ * %0A and %0D sequences from the URL, which we don't want.
518
+ * There are valid use-cases for encoded newlines in the query string,
519
+ * for example html-api-debugger accepts markup with newlines
520
+ * encoded as %0A via the query string.
521
+ */
522
+ header( "Location: $redirect_url", true, 302 );
523
+ exit;
524
+ }
525
+ /**
526
+ * Autologin users from the wp-login.php page.
527
+ *
528
+ * The wp hook isn't triggered on
529
+ **/
530
+ add_action('init', 'playground_auto_login', 1);
531
+
532
+ /**
533
+ * Disable the Site Admin Email Verification Screen for any session started
534
+ * via autologin.
535
+ */
536
+ add_filter('admin_email_check_interval', function($interval) {
537
+ if(false === playground_get_username_for_auto_login()) {
538
+ return 0;
539
+ }
540
+ return $interval;
541
+ });
542
+ `),await e.writeFile("/internal/shared/mu-plugins/0-playground.php",`<?php
543
+ // Needed because gethostbyname( 'wordpress.org' ) returns
544
+ // a private network IP address for some reason.
545
+ add_filter( 'allowed_redirect_hosts', function( $deprecated = '' ) {
546
+ return array(
547
+ 'wordpress.org',
548
+ 'api.wordpress.org',
549
+ 'downloads.wordpress.org',
550
+ );
551
+ } );
552
+
553
+ // Support pretty permalinks
554
+ add_filter( 'got_url_rewrite', '__return_true' );
555
+
556
+ // Create the fonts directory if missing
557
+ if(!file_exists(WP_CONTENT_DIR . '/fonts')) {
558
+ mkdir(WP_CONTENT_DIR . '/fonts');
559
+ }
560
+
561
+ $log_file = WP_CONTENT_DIR . '/debug.log';
562
+ define('ERROR_LOG_FILE', $log_file);
563
+ ini_set('error_log', $log_file);
564
+ ?>`),await e.writeFile("/internal/shared/preload/error-handler.php",`<?php
565
+ (function() {
566
+ $playground_consts = [];
567
+ if(file_exists('/internal/shared/consts.json')) {
568
+ $playground_consts = @json_decode(file_get_contents('/internal/shared/consts.json'), true) ?: [];
569
+ $playground_consts = array_keys($playground_consts);
570
+ }
571
+ set_error_handler(function($severity, $message, $file, $line) use($playground_consts) {
572
+ /**
573
+ * Networking support in Playground registers a http_api_transports filter.
574
+ *
575
+ * This filter is deprecated, and no longer actively used, but is needed for wp_http_supports().
576
+ * @see https://core.trac.wordpress.org/ticket/37708
577
+ */
578
+ if (
579
+ strpos($message, "http_api_transports") !== false &&
580
+ strpos($message, "since version 6.4.0 with no alternative available") !== false
581
+ ) {
582
+ return;
583
+ }
584
+ /**
585
+ * Playground defines some constants upfront, and some of them may be redefined
586
+ * in wp-config.php. For example, SITE_URL or WP_DEBUG. This is expected and
587
+ * we want Playground constants to take priority without showing warnings like:
588
+ *
589
+ * Warning: Constant SITE_URL already defined in
590
+ */
591
+ if (strpos($message, "already defined") !== false) {
592
+ foreach($playground_consts as $const) {
593
+ if(strpos($message, "Constant $const already defined") !== false) {
594
+ return;
595
+ }
596
+ }
597
+ }
598
+ /**
599
+ * Don't complain about network errors when not connected to the network.
600
+ */
601
+ if (
602
+ (
603
+ ! defined('USE_FETCH_FOR_REQUESTS') ||
604
+ ! USE_FETCH_FOR_REQUESTS
605
+ ) &&
606
+ strpos($message, "WordPress could not establish a secure connection to WordPress.org") !== false)
607
+ {
608
+ return;
609
+ }
610
+ return false;
611
+ });
612
+ })();`)}async function m(e,r="/phpinfo.php"){await e.writeFile("/internal/shared/preload/phpinfo.php",`<?php
613
+ // Render PHPInfo if the requested page is /phpinfo.php
614
+ if ( ${i.phpVar(r)} === $_SERVER['REQUEST_URI'] ) {
615
+ phpinfo();
616
+ exit;
617
+ }
618
+ `)}async function b(e,r){await e.isDir("/tmp/sqlite-database-integration")&&await e.rmdir("/tmp/sqlite-database-integration",{recursive:!0}),await e.mkdir("/tmp/sqlite-database-integration"),await p.unzipFile(e,r,"/tmp/sqlite-database-integration");const t="/internal/shared/sqlite-database-integration",n=`/tmp/sqlite-database-integration/${(await e.listFiles("/tmp/sqlite-database-integration"))[0]}`;await e.mv(n,t),e.defineConstant("WP_SQLITE_AST_DRIVER",!0),await e.defineConstant("SQLITE_MAIN_FILE","1");const d=(await e.readFileAsText(i.joinPaths(t,"db.copy"))).replace("'{SQLITE_IMPLEMENTATION_FOLDER_PATH}'",i.phpVar(t)).replace("'{SQLITE_PLUGIN}'",i.phpVar(i.joinPaths(t,"load.php"))),a=i.joinPaths(await e.documentRoot,"wp-content/db.php"),u=`<?php
619
+ // Do not preload this if WordPress comes with a custom db.php file.
620
+ if(file_exists(${i.phpVar(a)})) {
621
+ return;
622
+ }
623
+ ?>`,s="/internal/shared/mu-plugins/sqlite-database-integration.php";await e.writeFile(s,u+d),await e.writeFile("/internal/shared/preload/0-sqlite.php",u+`<?php
624
+
625
+ /**
626
+ * Loads the SQLite integration plugin before WordPress is loaded
627
+ * and without creating a drop-in "db.php" file.
628
+ *
629
+ * Technically, it creates a global $wpdb object whose only two
630
+ * purposes are to:
631
+ *
632
+ * * Exist – because the require_wp_db() WordPress function won't
633
+ * connect to MySQL if $wpdb is already set.
634
+ * * Load the SQLite integration plugin the first time it's used
635
+ * and replace the global $wpdb reference with the SQLite one.
636
+ *
637
+ * This lets Playground keep the WordPress installation clean and
638
+ * solves dillemas like:
639
+ *
640
+ * * Should we include db.php in Playground exports?
641
+ * * Should we remove db.php from Playground imports?
642
+ * * How should we treat stale db.php from long-lived OPFS sites?
643
+ *
644
+ * @see https://github.com/WordPress/wordpress-playground/discussions/1379 for
645
+ * more context.
646
+ */
647
+ class Playground_SQLite_Integration_Loader {
648
+ public function __call($name, $arguments) {
649
+ $this->load_sqlite_integration();
650
+ if($GLOBALS['wpdb'] === $this) {
651
+ throw new Exception('Infinite loop detected in $wpdb – SQLite integration plugin could not be loaded');
652
+ }
653
+ return call_user_func_array(
654
+ array($GLOBALS['wpdb'], $name),
655
+ $arguments
656
+ );
657
+ }
658
+ public function __get($name) {
659
+ $this->load_sqlite_integration();
660
+ if($GLOBALS['wpdb'] === $this) {
661
+ throw new Exception('Infinite loop detected in $wpdb – SQLite integration plugin could not be loaded');
662
+ }
663
+ return $GLOBALS['wpdb']->$name;
664
+ }
665
+ public function __set($name, $value) {
666
+ $this->load_sqlite_integration();
667
+ if($GLOBALS['wpdb'] === $this) {
668
+ throw new Exception('Infinite loop detected in $wpdb – SQLite integration plugin could not be loaded');
669
+ }
670
+ $GLOBALS['wpdb']->$name = $value;
671
+ }
672
+ protected function load_sqlite_integration() {
673
+ require_once ${i.phpVar(s)};
674
+ }
675
+ }
676
+ $wpdb = $GLOBALS['wpdb'] = new Playground_SQLite_Integration_Loader();
677
+
678
+ /**
679
+ * WordPress is capable of using a preloaded global $wpdb. However, if
680
+ * it cannot find the drop-in db.php plugin it still checks whether
681
+ * the mysqli_connect() function exists even though it's not used.
682
+ *
683
+ * What WordPress demands, Playground shall provide.
684
+ */
685
+ if(!function_exists('mysqli_connect')) {
686
+ function mysqli_connect() {}
687
+ }
688
+
689
+ `),await e.writeFile("/internal/shared/mu-plugins/sqlite-test.php",`<?php
690
+ global $wpdb;
691
+ if(!($wpdb instanceof WP_SQLite_DB)) {
692
+ var_dump(isset($wpdb));
693
+ die("SQLite integration not loaded " . get_class($wpdb));
694
+ }
695
+ `)}async function y(e,r){e.mkdir("/tmp/unzipped-wordpress"),await p.unzipFile(e,r,"/tmp/unzipped-wordpress"),e.fileExists("/tmp/unzipped-wordpress/wordpress.zip")&&await p.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(i.joinPaths(t,"wp-config-sample.php"))){const n=e.listFiles(t);if(n.length){const o=n[0];e.fileExists(i.joinPaths(t,o,"wp-config-sample.php"))&&(t=i.joinPaths(t,o))}}if(e.isDir(e.documentRoot)&&S(e.documentRoot,e)){for(const n of e.listFiles(t)){const o=i.joinPaths(t,n),d=i.joinPaths(e.documentRoot,n);e.mv(o,d)}e.rmdir(t,{recursive:!0})}else e.mv(t,e.documentRoot);!e.fileExists(i.joinPaths(e.documentRoot,"wp-config.php"))&&e.fileExists(i.joinPaths(e.documentRoot,"wp-config-sample.php"))&&e.writeFile(i.joinPaths(e.documentRoot,"wp-config.php"),e.readFileAsText(i.joinPaths(e.documentRoot,"/wp-config-sample.php")))}function S(e,r){const t=r.listFiles(e);return t.length===0||t.length===1&&t[0]==="playground-site-metadata.json"}const v=p.createMemoizedFetch(fetch);async function L(e="latest"){if(e.startsWith("https://")||e.startsWith("http://")){const n=await crypto.subtle.digest("SHA-1",new TextEncoder().encode(e)),o=Array.from(new Uint8Array(n)).map(d=>d.toString(16).padStart(2,"0")).join("");return{releaseUrl:e,version:"custom-"+o.substring(0,8),source:"inferred"}}else if(e==="trunk"||e==="nightly")return{releaseUrl:"https://wordpress.org/nightly-builds/wordpress-latest.zip",version:"nightly-"+new Date().toISOString().split("T")[0],source:"inferred"};let t=await(await v("https://api.wordpress.org/core/version-check/1.7/?channel=beta")).json();t=t.offers.filter(n=>n.response==="autoupdate");for(const n of t){if(e==="beta"&&n.version.includes("beta"))return{releaseUrl:n.download,version:n.version,source:"api"};if(e==="latest"&&!n.version.includes("beta"))return{releaseUrl:n.download,version:n.version,source:"api"};if(n.version.substring(0,e.length)===e)return{releaseUrl:n.download,version:n.version,source:"api"}}return{releaseUrl:`https://wordpress.org/wordpress-${e}.zip`,version:e,source:"inferred"}}exports.bootWordPress=E;exports.defineWpConfigConstants=_;exports.ensureWpConfig=c;exports.getFileNotFoundActionForWordPress=h;exports.getLoadedWordPressVersion=R;exports.preloadPhpInfoRoute=m;exports.preloadSqliteIntegration=b;exports.resolveWordPressRelease=L;exports.setupPlatformLevelMuPlugins=w;exports.unzipWordPress=y;exports.versionStringToLoadedWordPressVersion=g;exports.wordPressRewriteRules=$;
696
+ //# sourceMappingURL=index.cjs.map