Skip to content

Commit 10277ed

Browse files
authored
PHP 8.5 support (#2950)
🦄 **TIP** To review code changes in this PR open [Files change using this link](https://github\.com//pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs) it will hide all the compiled files, so you can see the actual code changes without GitHub unicorns. ## What is this PR doing? Adds PHP 8.5 to Playground. Additionally, it [solves a bug where the php-chunk-alloc-zend-assert patch](https://github\.com//pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs#r2570571617) wasn't correctly applied to PHP versions before PHP 8.3. ## Implementation Details ### Opcache PHP 8.5 made Opcache a required part of PHP, so [PHP 8.5 had to be compiled with Opcache](https://github\.com//pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs#r2570572781). #### Intl We had to [enable Opcache when compiling Intl to PHP 8.5](https://github\.com//pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs#r2572307438). Opcache grew in the web Asyncify build size over the 8MB limit for synchronous WASM compilation on the main thread, so we had to [optimize the WASM file size.](https://github\.com//pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs#r2581296925) #### Xdebug Similarly to Intl, we had to [enable Opcache when compiling Xdebug to PHP 8.5.](https://github.com/WordPress/wordpress-playground/pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs#r2572307837) #### File locking The behavior or file locking changed in PHP 8.5 because it uses Opcache. Before PHP 8.5 file locking would use `F_SETLK`, but with Opcache it uses `F_SETLKW` [when calling `fcntl`](https://github.com/php/php-src/blob/master/ext/opcache/zend_shared_alloc.c?rgh-link-date=2025-12-02T09%3A41%3A50Z#L510). Because `F_SETLKW` wasn't implemented in PHP-wasm file locking, this [PR adds a partial implementation of `F_SETLKW` based on `F_SETLK`](https://github.com/WordPress/wordpress-playground/pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs&file-filters%5B%5D=.js#r2580401797). For the feature to be fully implemented, we still need to make it blocking. ### Deprecated functions PHP 8.5 deprecated resource free-up functions like `curl_close` and `imagedestroy`, which aren't needed since PHP 8.0. This PR removes these function calls from tests, [including CURL PHP 7 tests](https://github.com/WordPress/wordpress-playground/pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs#r2572324571). `imagedestroy` used to [run only for PHP 8.1+ tests](https://github\.com//pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs/53e6604bccaf721d6367b0dc64ab287fd6a3c8e4#diff-eb57c8ef57583ca8dae1d36a9c6c4533a4529bba4cbd03a2de04df4a3e40bb75), so it didn't do anything and is safe to remove. ### Object `clone` PHP 8.5+ changed clone from an opcode to a function, which changes how asyncify errors are handled during clone operations, which broke one of our PHP crash tests. In this [PR we disabled the test for PHP 8.5+](https://github.com/WordPress/wordpress-playground/pull/2950/files?file-filters%5B%5D=.json&file-filters%5B%5D=.md&file-filters%5B%5D=No+extension&file-filters%5B%5D=.patch&file-filters%5B%5D=.ts&file-filters%5B%5D=.mjs#r2580468254). ## Testing Instructions 1. Go to http://localhost:5400/website-server/ and switch the PHP version to 8.5. 2. Confirm that WordPress is functional and you can update the site in the site editor 3. Go to `/phpinfo.php`, confirm the php version it 8.5 4. Start Playground CLI `nx dev playground-cli server --php=8.5` 5. Confirm that WordPress is functional and you can update the site in the site editor 6. Go [to PHP info](http://127.0.0.1:9400/phpinfo.php), confirm the php version it 8.5
1 parent 0ce8e47 commit 10277ed

File tree

112 files changed

+622269
-499462
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+622269
-499462
lines changed

.vscode/launch.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
"8.2",
138138
"8.3",
139139
"8.4",
140+
"8.5"
140141
]
141142
},
142143
{

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"recompile:php": "npm run recompile:php:web && npm run recompile:php:node",
2929
"recompile:php:web": "nx recompile-php:all php-wasm-web ",
3030
"recompile:php:web:jspi:all": "nx recompile-php:jspi:all php-wasm-web",
31+
"recompile:php:web:jspi:8.5": "nx recompile-php:jspi php-wasm-web -- --PHP_VERSION=8.5 ",
3132
"recompile:php:web:jspi:8.4": "nx recompile-php:jspi php-wasm-web -- --PHP_VERSION=8.4 ",
3233
"recompile:php:web:jspi:8.3": "nx recompile-php:jspi php-wasm-web -- --PHP_VERSION=8.3 ",
3334
"recompile:php:web:jspi:8.2": "nx recompile-php:jspi php-wasm-web -- --PHP_VERSION=8.2 ",
@@ -37,6 +38,7 @@
3738
"recompile:php:web:jspi:7.3": "nx recompile-php:jspi php-wasm-web -- --PHP_VERSION=7.3 ",
3839
"recompile:php:web:jspi:7.2": "nx recompile-php:jspi php-wasm-web -- --PHP_VERSION=7.2 ",
3940
"recompile:php:web:asyncify:all": "nx recompile-php:asyncify:all php-wasm-web",
41+
"recompile:php:web:asyncify:8.5": "nx recompile-php:asyncify php-wasm-web -- --PHP_VERSION=8.5 ",
4042
"recompile:php:web:asyncify:8.4": "nx recompile-php:asyncify php-wasm-web -- --PHP_VERSION=8.4 ",
4143
"recompile:php:web:asyncify:8.3": "nx recompile-php:asyncify php-wasm-web -- --PHP_VERSION=8.3 ",
4244
"recompile:php:web:asyncify:8.2": "nx recompile-php:asyncify php-wasm-web -- --PHP_VERSION=8.2 ",
@@ -47,6 +49,7 @@
4749
"recompile:php:web:asyncify:7.2": "nx recompile-php:asyncify php-wasm-web -- --PHP_VERSION=7.2 ",
4850
"recompile:php:node": "nx recompile-php:all php-wasm-node ",
4951
"recompile:php:node:jspi:all": "nx recompile-php:jspi:all php-wasm-node ",
52+
"recompile:php:node:jspi:8.5": "nx recompile-php:jspi php-wasm-node -- --PHP_VERSION=8.5 ",
5053
"recompile:php:node:jspi:8.4": "nx recompile-php:jspi php-wasm-node -- --PHP_VERSION=8.4 ",
5154
"recompile:php:node:jspi:8.3": "nx recompile-php:jspi php-wasm-node -- --PHP_VERSION=8.3 ",
5255
"recompile:php:node:jspi:8.2": "nx recompile-php:jspi php-wasm-node -- --PHP_VERSION=8.2 ",
@@ -56,6 +59,7 @@
5659
"recompile:php:node:jspi:7.3": "nx recompile-php:jspi php-wasm-node -- --PHP_VERSION=7.3 ",
5760
"recompile:php:node:jspi:7.2": "nx recompile-php:jspi php-wasm-node -- --PHP_VERSION=7.2 ",
5861
"recompile:php:node:asyncify:all": "nx recompile-php:asyncify:all php-wasm-node ",
62+
"recompile:php:node:asyncify:8.5": "nx recompile-php:asyncify php-wasm-node -- --PHP_VERSION=8.5 ",
5963
"recompile:php:node:asyncify:8.4": "nx recompile-php:asyncify php-wasm-node -- --PHP_VERSION=8.4 ",
6064
"recompile:php:node:asyncify:8.3": "nx recompile-php:asyncify php-wasm-node -- --PHP_VERSION=8.3 ",
6165
"recompile:php:node:asyncify:8.2": "nx recompile-php:asyncify php-wasm-node -- --PHP_VERSION=8.2 ",

packages/docs/site/docs/blueprints/03-data-format.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ The `landingPage` property tells Playground which URL to navigate to after the B
5555

5656
The `preferredVersions` property declares your preferred PHP and WordPress versions. It can contain the following properties:
5757

58-
- `php` (string): Loads the specified PHP version. Accepts `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`, `8.4`, or `latest`. Minor versions like `7.4.1` are not supported.
59-
- `wp` (string): Loads the specified WordPress version. Accepts the last six major WordPress versions. As of September 1, 2025, that's `6.3`, `6.4`, `6.5`, `6.6`, `6.7` or `6.8`. You can also use the generic values `latest`, `nightly`, or `beta`. To use a pre-release version of WordPress, `beta` will load the latest beta or release candidate versions of a release cycle (Beta or RC).
58+
- `php` (string): Loads the specified PHP version. Accepts `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`, `8.4`, `8.5`, or `latest`. Minor versions like `7.4.1` are not supported.
59+
- `wp` (string): Loads the specified WordPress version. Accepts the last six major WordPress versions. As of September 1, 2025, that's `6.3`, `6.4`, `6.5`, `6.6`, `6.7` or `6.8`. You can also use the generic values `latest`, `nightly`, or `beta`. To use a pre-release version of WordPress, `beta` will load the latest beta or release candidate versions of a release cycle (Beta or RC).
6060

6161
```js
6262
{
@@ -71,7 +71,7 @@ The `preferredVersions` property declares your preferred PHP and WordPress versi
7171

7272
You can use the `features` property to turn on or off certain features of the Playground instance. It can contain the following properties:
7373

74-
- `networking`: Defaults to `true`. Enables or disables the networking support for Playground. If enabled, [`wp_safe_remote_get`](https://developer.wordpress.org/reference/functions/wp_safe_remote_get/) and similar WordPress functions will actually use `fetch()` to make HTTP requests. If disabled, they will immediately fail instead. You will need this property enabled if you want the user to be able to install plugins or themes.
74+
- `networking`: Defaults to `true`. Enables or disables the networking support for Playground. If enabled, [`wp_safe_remote_get`](https://developer.wordpress.org/reference/functions/wp_safe_remote_get/) and similar WordPress functions will actually use `fetch()` to make HTTP requests. If disabled, they will immediately fail instead. You will need this property enabled if you want the user to be able to install plugins or themes.
7575

7676
```js
7777
{
@@ -85,7 +85,7 @@ You can use the `features` property to turn on or off certain features of the Pl
8585

8686
You can preload extra libraries into the Playground instance. The following libraries are supported:
8787

88-
- `wp-cli`: Enables WP-CLI support for Playground. If included, WP-CLI will be installed during boot. If not included, you will get an error message when trying to run WP-CLI commands using the JS API. WP-CLI will be installed by default if the blueprint contains any `wp-cli` steps.
88+
- `wp-cli`: Enables WP-CLI support for Playground. If included, WP-CLI will be installed during boot. If not included, you will get an error message when trying to run WP-CLI commands using the JS API. WP-CLI will be installed by default if the blueprint contains any `wp-cli` steps.
8989

9090
```js
9191
{

packages/docs/site/docs/developers/06-apis/query-api/01-index.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ You can go ahead and try it out. The Playground will automatically install the t
2424

2525
| Option | Default Value | Description |
2626
| ------------------------ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
27-
| `php` | `8.0` | Loads the specified PHP version. Accepts `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`, `8.4` or `latest`. |
27+
| `php` | `8.0` | Loads the specified PHP version. Accepts `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`, `8.4`, `8.5` or `latest`. |
2828
| `wp` | `latest` | Loads the specified WordPress version. Accepts the last three major WordPress versions. As of June 1, 2024, that's `6.3`, `6.4`, or `6.5`. You can also use the generic values `latest`, `nightly`, or `beta`. |
2929
| `blueprint-url` | | The URL of the Blueprint that will be used to configure this Playground instance. |
3030
| `networking` | `yes` | Enables or disables the networking support for Playground. Accepts `yes` or `no`. |
@@ -60,17 +60,17 @@ To import files from a URL, such as a site zip package, they must be served with
6060

6161
The following additional query parameters may be used to pre-configure the GitHub export form:
6262

63-
- `gh-ensure-auth`: If set to `yes`, Playground will display a modal to ensure the
64-
user is authenticated with GitHub before proceeding.
65-
- `ghexport-repo-url`: The URL of the GitHub repository to export to.
66-
- `ghexport-pr-action`: The action to take when exporting (create or update).
67-
- `ghexport-playground-root`: The root directory in the Playground to export from.
68-
- `ghexport-repo-root`: The root directory in the repository to export to.
69-
- `ghexport-content-type`: The content type of the export (plugin, theme, wp-content, custom-paths).
70-
- `ghexport-plugin`: Plugin path. When the content type is `plugin`, pre-select the plugin to export.
71-
- `ghexport-theme`: Theme directory name. When the content type is `theme`, pre-select the theme to export.
72-
- `ghexport-path`: A path relative to `ghexport-playground-root`. Can be provided multiple times. When the
73-
content type is `custom-paths`, it pre-populates the list of paths to export.
74-
- `ghexport-commit-message`: The commit message to use when exporting.
75-
- `ghexport-allow-include-zip`: Whether to offer an option to include a zip file in the GitHub
76-
export (yes, no). Optional. Defaults to `yes`.
63+
- `gh-ensure-auth`: If set to `yes`, Playground will display a modal to ensure the
64+
user is authenticated with GitHub before proceeding.
65+
- `ghexport-repo-url`: The URL of the GitHub repository to export to.
66+
- `ghexport-pr-action`: The action to take when exporting (create or update).
67+
- `ghexport-playground-root`: The root directory in the Playground to export from.
68+
- `ghexport-repo-root`: The root directory in the repository to export to.
69+
- `ghexport-content-type`: The content type of the export (plugin, theme, wp-content, custom-paths).
70+
- `ghexport-plugin`: Plugin path. When the content type is `plugin`, pre-select the plugin to export.
71+
- `ghexport-theme`: Theme directory name. When the content type is `theme`, pre-select the theme to export.
72+
- `ghexport-path`: A path relative to `ghexport-playground-root`. Can be provided multiple times. When the
73+
content type is `custom-paths`, it pre-populates the list of paths to export.
74+
- `ghexport-commit-message`: The commit message to use when exporting.
75+
- `ghexport-allow-include-zip`: Whether to offer an option to include a zip file in the GitHub
76+
export (yes, no). Optional. Defaults to `yes`.

packages/php-wasm/compile/php/Dockerfile

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ RUN if [ "$WITH_OPCACHE" = "yes" ]; \
9292
then \
9393
# Ensure mmap() shared memory is recognized, even if not true.
9494
# This will #declare HAVE_SHM_MMAP_ANON 1 in php_config.h
95-
if [[ "${PHP_VERSION:0:1}" -eq "8" && "${PHP_VERSION:2:1}" -eq "4" ]]; then \
95+
if [[ "${PHP_VERSION:0:1}" -eq "8" && "${PHP_VERSION:2:1}" -ge "4" ]]; then \
9696
# Name changed in 8.4. See https://github.com/php/php-src/commit/ca55603d8a27b4e1f9c12cf72acd0fd39a6ac059
9797
/root/replace.sh 's/php_cv_shm_mmap_anon=no/php_cv_shm_mmap_anon=yes/g' /root/php-src/ext/opcache/config.m4; \
9898
else \
@@ -103,8 +103,11 @@ RUN if [ "$WITH_OPCACHE" = "yes" ]; \
103103
fi; \
104104
# Force OPcache to be built as a static extension. OPcache is always built as a shared extension.
105105
/root/replace.sh 's/ext_shared=yes/ext_shared=no/g' /root/php-src/ext/opcache/config.m4; \
106-
# Add the dummy opcache_module.c to the list of files to build.
107-
/root/replace.sh 's/shared_alloc_mmap.c/shared_alloc_mmap.c opcache_module.c/g' /root/php-src/ext/opcache/config.m4; \
106+
# Add the dummy opcache_module.c to the list of files to build for PHP < 8.5.0.
107+
# PHP 8.5 made OPcache a non-optional part of PHP https://wiki.php.net/rfc/make_opcache_required
108+
if [[ "${PHP_VERSION:0:1}" -lt "8" || ("${PHP_VERSION:0:1}" -eq "8" && "${PHP_VERSION:2:1}" -le "4") ]]; then \
109+
/root/replace.sh 's/shared_alloc_mmap.c/shared_alloc_mmap.c opcache_module.c/g' /root/php-src/ext/opcache/config.m4; \
110+
fi; \
108111
# Enable OPcache.
109112
echo -n ' --enable-opcache --disable-opcache-jit --disable-huge-code-pages ' >> /root/.php-configure-flags; \
110113
else \
@@ -365,13 +368,15 @@ RUN cd /root && \
365368
fi && \
366369
chmod +x /root/apply-mysqlnd-patch.sh && \
367370
/root/apply-mysqlnd-patch.sh && \
368-
( [[ "${PHP_VERSION:0:3}" == '8.3' ]] && \
369-
git apply --no-index /root/php-chunk-alloc-zend-assert-8.3.patch -v || \
370-
( [[ "${PHP_VERSION:0:3}" == '8.4' ]] && \
371-
git apply --no-index /root/php-chunk-alloc-zend-assert-8.4.patch -v || \
372-
git apply --no-index /root/php-chunk-alloc-zend-assert.patch -v \
373-
) \
374-
) && \
371+
if [[ "${PHP_VERSION:0:3}" < '8.3' ]]; then \
372+
git apply --no-index /root/php-chunk-alloc-zend-assert.patch -v; \
373+
elif [[ "${PHP_VERSION:0:3}" == '8.3' ]]; then \
374+
git apply --no-index /root/php-chunk-alloc-zend-assert-8.3.patch -v; \
375+
elif [[ "${PHP_VERSION:0:3}" == '8.4' ]]; then \
376+
git apply --no-index /root/php-chunk-alloc-zend-assert-8.4.patch -v; \
377+
elif [[ "${PHP_VERSION:0:3}" == '8.5' ]]; then \
378+
git apply --no-index /root/php-chunk-alloc-zend-assert-8.5.patch -v; \
379+
fi && \
375380
touch php-src/patched
376381

377382
# Install Mysql support if needed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
diff --git a/php-src/Zend/zend_alloc.c b/php-src/Zend/zend_alloc.c
2+
index bf2116fc91..bec65453d8 100644
3+
--- a/php-src/Zend/zend_alloc.c
4+
+++ b/php-src/Zend/zend_alloc.c
5+
@@ -806,7 +806,7 @@ static void *zend_mm_chunk_alloc(zend_mm_heap *heap, size_t size, size_t alignme
6+
#if ZEND_MM_STORAGE
7+
if (UNEXPECTED(heap->storage)) {
8+
void *ptr = heap->storage->handlers.chunk_alloc(heap->storage, size, alignment);
9+
- ZEND_ASSERT(((uintptr_t)((char*)ptr + (alignment-1)) & (alignment-1)) == (uintptr_t)ptr);
10+
+ ZEND_ASSERT(((uintptr_t)((char*)ptr + ~(alignment-1)) & (alignment-1)) == (uintptr_t)ptr);
11+
return ptr;
12+
}
13+
#endif
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
diff --git a/php-src/ext/opcache/zend_accelerator_debug.c b/php-src/ext/opcache/zend_accelerator_debug.c
2+
index b2a3105dc62..f7e328312a8 100644
3+
--- a/php-src/ext/opcache/zend_accelerator_debug.c
4+
+++ b/php-src/ext/opcache/zend_accelerator_debug.c
5+
@@ -25,6 +25,9 @@
6+
#include <time.h>
7+
#ifdef ZEND_WIN32
8+
# include <process.h>
9+
+#else
10+
+/* Error: call to undeclared function 'getpid' */
11+
+# include <unistd.h>
12+
#endif
13+
#include "ZendAccelerator.h"
14+
15+
diff --git a/php-src/ext/standard/file.c b/php-src/ext/standard/file.c
16+
index 01f49640e4a..e559b61b0c6 100644
17+
--- a/php-src/ext/standard/file.c
18+
+++ b/php-src/ext/standard/file.c
19+
@@ -1459,6 +1459,20 @@ PHPAPI zend_result php_copy_file_ctx(const char *src, const char *dest, int src_
20+
goto safe_to_copy;
21+
break;
22+
case 0:
23+
+ // Fix for https://github.com/WordPress/wordpress-playground/issues/54:
24+
+ // Problem: Calling copy() on an empty source file crashes the JavaScript
25+
+ // runtime.
26+
+ // Solution: Avoid copying empty files. Just create create an empty
27+
+ // destination file and return.
28+
+ if (src_s.sb.st_size == 0) {
29+
+ zend_string *opened_path = zend_string_init("", strlen(""), 0);
30+
+ php_stream *stream = php_stream_open_wrapper(dest, "w", REPORT_ERRORS, &opened_path);
31+
+ if (stream) {
32+
+ php_stream_close(stream);
33+
+ return SUCCESS;
34+
+ }
35+
+ return FAILURE;
36+
+ }
37+
break;
38+
default: /* failed to stat file, does not exist? */
39+
return ret;

0 commit comments

Comments
 (0)