@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/LICENSE +339 -0
- package/boot.d.ts +25 -1
- package/index.cjs +696 -0
- package/index.cjs.map +1 -0
- package/index.d.ts +2 -1
- package/index.js +661 -42
- package/index.js.map +1 -1
- package/package.json +29 -16
- package/rewrite-wp-config.d.ts +22 -0
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
|