@wp-playground/wordpress 0.7.19 → 0.7.20

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.
Files changed (3) hide show
  1. package/index.d.ts +30 -1
  2. package/index.js +205 -1
  3. package/package.json +2 -5
package/index.d.ts CHANGED
@@ -1,2 +1,31 @@
1
+ import { BasePHP, UniversalPHP } from '../../../php-wasm/universal/src/index.ts';
1
2
  export * from './rewrite-rules';
2
- export declare const RecommendedPHPVersion = "8.0";
3
+ /**
4
+ * Preloads the platform mu-plugins from /internal/shared/mu-plugins.
5
+ * This avoids polluting the WordPress installation with mu-plugins
6
+ * that are only needed in the Playground environment.
7
+ *
8
+ * @param php
9
+ */
10
+ export declare function enablePlatformMuPlugins(php: UniversalPHP): Promise<void>;
11
+ export declare function preloadRequiredMuPlugin(php: UniversalPHP): Promise<void>;
12
+ /**
13
+ * Runs phpinfo() when the requested path is /phpinfo.php.
14
+ */
15
+ export declare function preloadPhpInfoRoute(php: UniversalPHP, requestPath?: string): Promise<void>;
16
+ export declare function preloadSqliteIntegration(php: UniversalPHP, sqliteZip: File): Promise<void>;
17
+ /**
18
+ * Prepare the WordPress document root given a WordPress zip file and
19
+ * the sqlite-database-integration zip file.
20
+ *
21
+ * This is a TypeScript function for now, just to get something off the
22
+ * ground, but it may be superseded by the PHP Blueprints library developed
23
+ * at https://github.com/WordPress/blueprints-library/
24
+ *
25
+ * That PHP library will come with a set of functions and a CLI tool to
26
+ * turn a Blueprint into a WordPress directory structure or a zip Snapshot.
27
+ * Let's **not** invest in the TypeScript implementation of this function,
28
+ * accept the limitation, and switch to the PHP implementation as soon
29
+ * as that's viable.
30
+ */
31
+ export declare function unzipWordPress(php: BasePHP, wpZip: File): Promise<void>;
package/index.js CHANGED
@@ -1 +1,205 @@
1
- const e=[{match:/^\/(.*?)(\/wp-(content|admin|includes)\/.*)/g,replacement:"$2"}],n="8.0";export{n as RecommendedPHPVersion,e as wordPressRewriteRules};
1
+ function o(...e){let t=e.join("/");const i=t[0]==="/",n=t.substring(t.length-1)==="/";return t=c(t),!t&&!i&&(t="."),t&&n&&(t+="/"),t}function c(e){const t=e[0]==="/";return e=f(e.split("/").filter(i=>!!i),!t).join("/"),(t?"/":"")+e.replace(/\/$/,"")}function f(e,t){let i=0;for(let n=e.length-1;n>=0;n--){const r=e[n];r==="."?e.splice(n,1):r===".."?(e.splice(n,1),i++):i&&(e.splice(n,1),i--)}if(t)for(;i;i--)e.unshift("..");return e}function s(e){return`json_decode(base64_decode('${_(JSON.stringify(e))}'), true)`}function g(e){const t={};for(const i in e)t[i]=s(e[i]);return t}function _(e){return w(new TextEncoder().encode(e))}function w(e){const t=String.fromCodePoint(...e);return btoa(t)}const a="/tmp/file.zip",d=async(e,t,i)=>{if(t instanceof File){const r=t;t=a,await e.writeFile(t,new Uint8Array(await r.arrayBuffer()))}const n=g({zipPath:t,extractToPath:i});await e.run({code:`<?php
2
+ function unzip($zipPath, $extractTo, $overwrite = true)
3
+ {
4
+ if (!is_dir($extractTo)) {
5
+ mkdir($extractTo, 0777, true);
6
+ }
7
+ $zip = new ZipArchive;
8
+ $res = $zip->open($zipPath);
9
+ if ($res === TRUE) {
10
+ $zip->extractTo($extractTo);
11
+ $zip->close();
12
+ chmod($extractTo, 0777);
13
+ } else {
14
+ throw new Exception("Could not unzip file");
15
+ }
16
+ }
17
+ unzip(${n.zipPath}, ${n.extractToPath});
18
+ `}),await e.fileExists(a)&&await e.unlink(a)},m=[{match:/^\/(.*?)(\/wp-(content|admin|includes)\/.*)/g,replacement:"$2"}];async function $(e){await e.mkdir("/internal/shared/mu-plugins"),await e.writeFile("/internal/shared/preload/env.php",`<?php
19
+
20
+ // Allow adding filters/actions prior to loading WordPress.
21
+ // $function_to_add MUST be a string.
22
+ function playground_add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
23
+ global $wp_filter;
24
+ $wp_filter[$tag][$priority][$function_to_add] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
25
+ }
26
+ function playground_add_action( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
27
+ playground_add_filter( $tag, $function_to_add, $priority, $accepted_args );
28
+ }
29
+
30
+ // Load our mu-plugins after customer mu-plugins
31
+ // NOTE: this means our mu-plugins can't use the muplugins_loaded action!
32
+ playground_add_action( 'muplugins_loaded', 'playground_load_mu_plugins', 0 );
33
+ function playground_load_mu_plugins() {
34
+ // Load all PHP files from /internal/shared/mu-plugins, sorted by filename
35
+ $mu_plugins_dir = '/internal/shared/mu-plugins';
36
+ if(!is_dir($mu_plugins_dir)){
37
+ return;
38
+ }
39
+ $mu_plugins = glob( $mu_plugins_dir . '/*.php' );
40
+ sort( $mu_plugins );
41
+ foreach ( $mu_plugins as $mu_plugin ) {
42
+ require_once $mu_plugin;
43
+ }
44
+ }
45
+ `)}async function h(e){const i=Object.values({addTrailingSlashToWpAdmin:`<?php
46
+ // Redirect /wp-admin to /wp-admin/
47
+ add_filter( 'redirect_canonical', function( $redirect_url ) {
48
+ if ( '/wp-admin' === $redirect_url ) {
49
+ return $redirect_url . '/';
50
+ }
51
+ return $redirect_url;
52
+ } );
53
+ ?>`,allowRedirectHosts:`<?php
54
+ // Needed because gethostbyname( 'wordpress.org' ) returns
55
+ // a private network IP address for some reason.
56
+ add_filter( 'allowed_redirect_hosts', function( $deprecated = '' ) {
57
+ return array(
58
+ 'wordpress.org',
59
+ 'api.wordpress.org',
60
+ 'downloads.wordpress.org',
61
+ );
62
+ } );
63
+ ?>`,supportPermalinksWithoutIndexPhp:`<?php
64
+ add_filter( 'got_url_rewrite', '__return_true' );
65
+ ?>`,createFontsDirectory:`<?php
66
+ // Create the fonts directory if missing
67
+ if(!file_exists(WP_CONTENT_DIR . '/fonts')) {
68
+ mkdir(WP_CONTENT_DIR . '/fonts');
69
+ }
70
+ ?>`,configureErrorLogging:`<?php
71
+ $log_file = WP_CONTENT_DIR . '/debug.log';
72
+ define('ERROR_LOG_FILE', $log_file);
73
+ ini_set('error_log', $log_file);
74
+ ?>`}).map(n=>n.trim()).join(`
75
+ `);await e.writeFile("/internal/shared/mu-plugins/0-playground.php",i),await e.writeFile("/internal/shared/preload/error-handler.php",`<?php
76
+ (function() {
77
+ $playground_consts = [];
78
+ if(file_exists('/internal/shared/consts.json')) {
79
+ $playground_consts = @json_decode(file_get_contents('/internal/shared/consts.json'), true) ?: [];
80
+ $playground_consts = array_keys($playground_consts);
81
+ }
82
+ set_error_handler(function($severity, $message, $file, $line) use($playground_consts) {
83
+ /**
84
+ * This is a temporary workaround to hide the 32bit integer warnings that
85
+ * appear when using various time related function, such as strtotime and mktime.
86
+ * Examples of the warnings that are displayed:
87
+ *
88
+ * Warning: mktime(): Epoch doesn't fit in a PHP integer in <file>
89
+ * Warning: strtotime(): Epoch doesn't fit in a PHP integer in <file>
90
+ */
91
+ if (strpos($message, "fit in a PHP integer") !== false) {
92
+ return;
93
+ }
94
+ /**
95
+ * Playground defines some constants upfront, and some of them may be redefined
96
+ * in wp-config.php. For example, SITE_URL or WP_DEBUG. This is expected and
97
+ * we want Playground constants to take priority without showing warnings like:
98
+ *
99
+ * Warning: Constant SITE_URL already defined in
100
+ */
101
+ if (strpos($message, "already defined") !== false) {
102
+ foreach($playground_consts as $const) {
103
+ if(strpos($message, "Constant $const already defined") !== false) {
104
+ return;
105
+ }
106
+ }
107
+ }
108
+ /**
109
+ * Don't complain about network errors when not connected to the network.
110
+ */
111
+ if (
112
+ (
113
+ ! defined('USE_FETCH_FOR_REQUESTS') ||
114
+ ! USE_FETCH_FOR_REQUESTS
115
+ ) &&
116
+ strpos($message, "WordPress could not establish a secure connection to WordPress.org") !== false)
117
+ {
118
+ return;
119
+ }
120
+ return false;
121
+ });
122
+ })();`)}async function b(e,t="/phpinfo.php"){await e.writeFile("/internal/shared/preload/phpinfo.php",`<?php
123
+ // Render PHPInfo if the requested page is /phpinfo.php
124
+ if ( ${s(t)} === $_SERVER['REQUEST_URI'] ) {
125
+ phpinfo();
126
+ exit;
127
+ }
128
+ `)}async function y(e,t){await e.isDir("/tmp/sqlite-database-integration")&&await e.rmdir("/tmp/sqlite-database-integration",{recursive:!0}),await e.mkdir("/tmp/sqlite-database-integration"),await d(e,t,"/tmp/sqlite-database-integration");const i="/internal/shared/sqlite-database-integration";await e.mv("/tmp/sqlite-database-integration/sqlite-database-integration-main",i);const r=(await e.readFileAsText(o(i,"db.copy"))).replace("'{SQLITE_IMPLEMENTATION_FOLDER_PATH}'",s(i)).replace("'{SQLITE_PLUGIN}'",s(o(i,"load.php"))),u=o(await e.documentRoot,"wp-content/db.php"),l=`<?php
129
+ // Do not preload this if WordPress comes with a custom db.php file.
130
+ if(file_exists(${s(u)})) {
131
+ return;
132
+ }
133
+ ?>`,p="/internal/shared/mu-plugins/sqlite-database-integration.php";await e.writeFile(p,l+r),await e.writeFile("/internal/shared/preload/0-sqlite.php",l+`<?php
134
+
135
+ /**
136
+ * Loads the SQLite integration plugin before WordPress is loaded
137
+ * and without creating a drop-in "db.php" file.
138
+ *
139
+ * Technically, it creates a global $wpdb object whose only two
140
+ * purposes are to:
141
+ *
142
+ * * Exist – because the require_wp_db() WordPress function won't
143
+ * connect to MySQL if $wpdb is already set.
144
+ * * Load the SQLite integration plugin the first time it's used
145
+ * and replace the global $wpdb reference with the SQLite one.
146
+ *
147
+ * This lets Playground keep the WordPress installation clean and
148
+ * solves dillemas like:
149
+ *
150
+ * * Should we include db.php in Playground exports?
151
+ * * Should we remove db.php from Playground imports?
152
+ * * How should we treat stale db.php from long-lived OPFS sites?
153
+ *
154
+ * @see https://github.com/WordPress/wordpress-playground/discussions/1379 for
155
+ * more context.
156
+ */
157
+ class Playground_SQLite_Integration_Loader {
158
+ public function __call($name, $arguments) {
159
+ $this->load_sqlite_integration();
160
+ if($GLOBALS['wpdb'] === $this) {
161
+ throw new Exception('Infinite loop detected in $wpdb – SQLite integration plugin could not be loaded');
162
+ }
163
+ return call_user_func_array(
164
+ array($GLOBALS['wpdb'], $name),
165
+ $arguments
166
+ );
167
+ }
168
+ public function __get($name) {
169
+ $this->load_sqlite_integration();
170
+ if($GLOBALS['wpdb'] === $this) {
171
+ throw new Exception('Infinite loop detected in $wpdb – SQLite integration plugin could not be loaded');
172
+ }
173
+ return $GLOBALS['wpdb']->$name;
174
+ }
175
+ public function __set($name, $value) {
176
+ $this->load_sqlite_integration();
177
+ if($GLOBALS['wpdb'] === $this) {
178
+ throw new Exception('Infinite loop detected in $wpdb – SQLite integration plugin could not be loaded');
179
+ }
180
+ $GLOBALS['wpdb']->$name = $value;
181
+ }
182
+ protected function load_sqlite_integration() {
183
+ require_once ${s(p)};
184
+ }
185
+ }
186
+ $wpdb = $GLOBALS['wpdb'] = new Playground_SQLite_Integration_Loader();
187
+
188
+ /**
189
+ * WordPress is capable of using a preloaded global $wpdb. However, if
190
+ * it cannot find the drop-in db.php plugin it still checks whether
191
+ * the mysqli_connect() function exists even though it's not used.
192
+ *
193
+ * What WordPress demands, Playground shall provide.
194
+ */
195
+ if(!function_exists('mysqli_connect')) {
196
+ function mysqli_connect() {}
197
+ }
198
+
199
+ `),await e.writeFile("/internal/shared/mu-plugins/sqlite-test.php",`<?php
200
+ global $wpdb;
201
+ if(!($wpdb instanceof WP_SQLite_DB)) {
202
+ var_dump(isset($wpdb));
203
+ die("SQLite integration not loaded " . get_class($wpdb));
204
+ }
205
+ `)}async function P(e,t){e.mkdir("/tmp/unzipped-wordpress"),await d(e,t,"/tmp/unzipped-wordpress"),e.fileExists("/tmp/unzipped-wordpress/wordpress.zip")&&await d(e,"/tmp/unzipped-wordpress/wordpress.zip","/tmp/unzipped-wordpress");const i=e.fileExists("/tmp/unzipped-wordpress/wordpress")?"/tmp/unzipped-wordpress/wordpress":e.fileExists("/tmp/unzipped-wordpress/build")?"/tmp/unzipped-wordpress/build":"/tmp/unzipped-wordpress";e.mv(i,e.documentRoot),!e.fileExists(o(e.documentRoot,"wp-config.php"))&&e.fileExists(o(e.documentRoot,"wp-config-sample.php"))&&e.writeFile(o(e.documentRoot,"wp-config.php"),e.readFileAsText(o(e.documentRoot,"/wp-config-sample.php")))}export{$ as enablePlatformMuPlugins,b as preloadPhpInfoRoute,h as preloadRequiredMuPlugin,y as preloadSqliteIntegration,P as unzipWordPress,m as wordPressRewriteRules};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-playground/wordpress",
3
- "version": "0.7.19",
3
+ "version": "0.7.20",
4
4
  "description": "WordPress-related plumbing for WordPress Playground",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,8 +27,5 @@
27
27
  "access": "public",
28
28
  "directory": "../../../dist/packages/playground/wordpress"
29
29
  },
30
- "dependencies": {
31
- "@php-wasm/universal": "^0.6.6"
32
- },
33
- "gitHead": "687ec237085853ff5c808814c28be349146a5d1a"
30
+ "gitHead": "5915ef756c88da8dcb665f9f0e49ddc0c0b10d50"
34
31
  }