没有不值得去解决的问题,也没有不值得去学习的技术!

Upgrading PHP From 8.1.19 to 8.5.9 in OneinStack: Imagick Compilation Fails and WordPress Multidomain Cache Is Verified

Upgrading PHP From 8.1.19 to 8.5.9 in OneinStack: Imagick Compilation Fails and WordPress Multidomain Cache Is Verified

作者:

在

,

1. Upgrade Background

My WordPress production environment runs on Alibaba Cloud ECS, using OneinStack to deploy Nginx, PHP-FPM, and Redis.

The current website is not a standard single-domain WordPress installation; instead, multiple domains access the same WordPress installation:

Plaintext
Chinese site: https://www.shuijingwanwq.com
English site: https://en.shuijingwanwq.com
Admin domain: https://admin.shuijingwanwq.com
Media domain: https://media.shuijingwanwq.com

Specifically:

  • Polylang identifies languages based on different domains;
  • W3 Total Cache provides Page Cache and Object Cache;
  • Object Cache uses Redis;
  • The Chinese site is connected to EdgeOne;
  • The English site is connected to Cloudflare;
  • The backend is accessed via an independent admin subdomain.

Before the formal upgrade, I reviewed the major software versions and the upgrade order on the server. For relevant background, you can refer to the previous record:

Previous record on server software versions and upgrade order

The main versions confirmed at that time included:

Plaintext
Operating System: Alibaba Cloud Linux 3
WordPress: 7.0.2
Nginx: 1.24.0
PHP: 8.1.19
Redis Server: 7.0.11
W3 Total Cache: 2.10.3
Polylang: 3.8.6

I ultimately decided not to upgrade all components simultaneously, but to handle PHP first.

The reason is simple: PHP directly affects WordPress, plugins, WP-CLI, PHP-FPM, and extension compatibility. If Nginx, PHP, and Redis are upgraded at the same time, it is difficult to quickly determine which layer caused an issue when something goes wrong.

Therefore, this round only upgrades PHP from 8.1.19 to 8.5.9, leaving Nginx and the Redis server for subsequent separate handling.


2. Confirm the Production Environment Structure Before Upgrading

The current PHP installation directory is:

Plaintext
/usr/local/php

The Socket used by PHP-FPM is:

Plaintext
/dev/shm/php-cgi.sock

The systemd service file also starts PHP-FPM from this directory:

Plaintext
/usr/local/php/sbin/php-fpm

PHP-FPM currently uses dynamic process management:

INI
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 500
request_terminate_timeout = 600s
request_slowlog_timeout = 3s

The WordPress memory limit is set to:

PHP
define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '512M');

Before upgrading, I also confirmed that all three domains point to the same WordPress root directory and process PHP requests through the same PHP-FPM Socket.

This step is crucial because, after the PHP upgrade is complete, you cannot just check php -v in the command line; you must also verify that Nginx is actually connecting to the new PHP-FPM.


3. Adjusting the OneinStack PHP 8.5 Installation Script

I downloaded the new version of OneinStack and used its PHP 8.5 installation logic.

However, using the original script directly does not fully adapt to the current environment, so I made a few adjustments first.

1. Adjust OPcache Configuration

After this PHP 8.5 build completed, php -v directly displayed:

Plaintext
with Zend OPcache v8.5.9

Therefore, the configuration only needs to retain the opcache.* parameter; there is no need to repeatedly load opcache.so as a regular external extension.

2. Retain Phar

WP-CLI on the server is a Phar file:

Plaintext
/usr/local/bin/wp

Therefore, Phar must be retained when building PHP 8.5; otherwise, WP-CLI might not run after the upgrade.

3. Handle iconv Header Conflict

The server contains:

Plaintext
/usr/local/include/iconv.h

During the configuration phase, PHP 8.5 detected both GNU libiconv and the system iconv, resulting in a mixed-usage conflict.

The final solution was to temporarily move this header file during PHP configuration, compilation, and installation, and then automatically restore it after the installation finished.

This handling only targeted the header file; the original libiconv dynamic library was not deleted.

4. Retain Necessary Extensions

Before upgrading, I confirmed that the main extensions actually required by the current WordPress installation include:

Plaintext
mysqli
pdo_mysql
mbstring
curl
intl
zip
gd
fileinfo
Phar
LDAP
Redis
Imagick
OPcache

Although the MongoDB extension was previously installed in the old PHP, no usage traces were found in the WordPress configuration, Drop-ins, MU Plugins, or active plugins, so the MongoDB extension was not reinstalled this time.


4. Executing the PHP 8.5.9 Production Upgrade

For the formal installation, I first stopped PHP-FPM and moved the original PHP directory to a new timestamped directory:

Plaintext
/usr/local/php-8.1.19-before-php85-20260804-182956

I then used OneinStack to install PHP 8.5.9 and specified the extensions:

Bash
cd /root/oneinstack-latest-20260804

./install.sh \
    --php_option 15 \
    --php_extensions imagick,ldap,redis

At the end of the installation, OneinStack output:

Plaintext
####################Congratulations########################
Total OneinStack Install Time: 10 minutes

PHP install dir: /usr/local/php

However, the status received by the outer installation script was:

Plaintext
INSTALL_STATUS=1
PHP 8.5.9 installation not completed.

If you only look at the exit status, it is easy to conclude that the entire PHP upgrade failed.

But I did not immediately reinstall; instead, I checked the actual results first.


5. Installation Status of 1 Does Not Mean the PHP Core Failed to Install

After checking, I confirmed:

Plaintext
PHP 8.5.9 (cli)
Zend Engine v4.5.9
Zend OPcache v8.5.9

PHP-FPM had also started normally:

Plaintext
Active: active (running)

The Socket exists normally:

Plaintext
/dev/shm/php-cgi.sock

The main modules have been loaded:

Plaintext
curl
fileinfo
gd
intl
ldap
mbstring
mysqli
pdo_mysql
Phar
redis
Zend OPcache
zip

Requests to the three origin sites were also all normal:

Plaintext
www.shuijingwanwq.com HTTP=200
en.shuijingwanwq.com HTTP=200
admin.shuijingwanwq.com HTTP=200

This shows that the PHP 8.5.9 core, PHP-FPM, and the LDAP and Redis extensions were actually all installed successfully.

After further checking the installation logs, the only error that actually caused the return status of 1 was Imagick:

Plaintext
fatal error: ext/standard/php_smart_string.h:
No such file or directory

PHP imagick module install failed

OneinStack was using Imagick 3.8.0 at the time. When compiled in the PHP 8.5 environment, it referenced a file that does not exist in the current PHP headers, so it failed.

Therefore, this was not a PHP 8.5.9 installation failure, but a failure to install the additional Imagick extension.


6. Switching to Imagick 3.8.1

I then downloaded Imagick 3.8.1:

Plaintext
imagick-3.8.1.tgz
SHA-256:
3a3587c0a524c17d0dad9673a160b90cd776e836838474e173b549ed864352ee

When I first ran configure, I encountered a new error:

Plaintext
Please provide a path to MagickWand-config
or Wand-config program.

Indeed, none of these could be found among the system commands:

Plaintext
convert
magick
MagickWand-config

However, further inspection of the old PHP 8.1 imagick.so revealed that the ImageMagick it actually linked to was located at:

Plaintext
/usr/local/imagemagick

The corresponding dynamic libraries are:

Plaintext
/usr/local/imagemagick/lib/libMagickWand-7.Q16HDRI.so.10
/usr/local/imagemagick/lib/libMagickCore-7.Q16HDRI.so.10

The ImageMagick version is:

Plaintext
ImageMagick 7.1.1-10 Q16-HDRI

The problem was simply that /usr/local/imagemagick/bin was not located within the current PATH.

Therefore, I reconfigured the environment variables and explicitly specified the ImageMagick installation path:

Bash
export PATH="/usr/local/imagemagick/bin:/usr/local/php/bin:$PATH"

export PKG_CONFIG_PATH="/usr/local/imagemagick/lib/pkgconfig:/usr/local/imagemagick/lib64/pkgconfig:${PKG_CONFIG_PATH:-}"

export LD_LIBRARY_PATH="/usr/local/imagemagick/lib:${LD_LIBRARY_PATH:-}"

/usr/local/php/bin/phpize

./configure \
    --with-php-config=/usr/local/php/bin/php-config \
    --with-imagick=/usr/local/imagemagick

make -j2
make install

This time, the compilation succeeded, generating:

Plaintext
/usr/local/php/lib/php/extensions/no-debug-non-zts-20250925/imagick.so

I then wrote it into the PHP configuration:

INI
[imagick]
extension=imagick.so

And reloaded PHP-FPM.

Final confirmation:

Plaintext
imagick module version => 3.8.1
Imagick compiled with ImageMagick version =>
ImageMagick 7.1.1-10 Q16-HDRI

Imagick using ImageMagick library version =>
ImageMagick 7.1.1-10 Q16-HDRI

I also actually generated a 2×2 pixel PNG via PHP:

Plaintext
imagick=3.8.1 bytes=127 format=PNG

This demonstrates that Imagick is not only loadable as an extension, but actual image operations can be performed successfully.


7. Verifying Whether WordPress is Actually Running on PHP 8.5.9

After upgrading PHP, you cannot just verify the PHP-FPM process.

My site also involves:

  • WordPress;
  • Polylang;
  • W3 Total Cache;
  • Redis Object Cache;
  • Three domains: Chinese, English, and admin;
  • canonical and hreflang;
  • REST API.

Therefore, I continued with WordPress runtime verification.

WP-CLI returned:

Plaintext
WordPress:7.0.2
PHP_VERSION=8.5.9
WP_MEMORY_LIMIT=256M
WP_MAX_MEMORY_LIMIT=512M
WP_CACHE=true
ext_object_cache=true
redis=6.3.0
imagick=3.8.1
ldap=loaded
opcache=8.5.9

The two W3TC Drop-in files also still exist:

Plaintext
wp-content/advanced-cache.php
wp-content/object-cache.php

The Redis server responded normally:

Plaintext
PONG

8. Verifying Multilingual Page Output

To bypass existing page caches, I added random query parameters to the requests and directly requested the local origin server.

Chinese homepage output:

HTML
<html lang="zh-CN">

<link rel="alternate"
      href="https://www.shuijingwanwq.com/"
      hreflang="zh" />

<link rel="alternate"
      href="https://en.shuijingwanwq.com/"
      hreflang="en" />

<link rel="canonical"
      href="https://www.shuijingwanwq.com/" />

English homepage output:

HTML
<html lang="en-US">

<link rel="alternate"
      href="https://www.shuijingwanwq.com/"
      hreflang="zh" />

<link rel="alternate"
      href="https://en.shuijingwanwq.com/"
      hreflang="en" />

<link rel="canonical"
      href="https://en.shuijingwanwq.com/" />

Next, I checked a pair of linked Chinese and English articles:

Plaintext
Chinese article:
https://www.shuijingwanwq.com/2026/07/31/21018/

English article:
https://en.shuijingwanwq.com/2026/07/31/21022/

For both articles, the following:

  • <html lang>;
  • canonical;
  • Chinese and English hreflang;
  • RSS Feed;
  • oEmbed URL;
  • REST API URL;

all pointed to the correct language domains.

This shows that Polylang’s multi-domain language identification was not broken after the PHP upgrade.


9. An Easily Misjudged WP-CLI Object Cache Test

Initially, I tested the Object Cache using two independent WP-CLI processes:

The first process wrote:

Plaintext
cache_set=true

The second process read:

Plaintext
cache_get=false

Scanning Redis directly also failed to find the corresponding test key.

At first, it looked like the W3TC Redis Object Cache had stopped working after the PHP 8.5 upgrade.

But upon further inspection, I found that the object cache class in WP-CLI was:

Plaintext
WP_Object_Cache

Whereas the object cache class in frontend PHP-FPM requests was:

Plaintext
W3TC\ObjectCache_WpObjectCache

In other words, the current WP-CLI context did not load W3TC’s Redis persistence implementation; it only used WordPress’s default in-process object cache.

Therefore:

  • A value just written can be read within the same WP-CLI process;
  • The second WP-CLI process cannot read it;
  • This cannot be used to determine whether the frontend Redis Object Cache is working properly.

10. Verifying Redis Object Cache via Two Independent FPM Requests

To verify the actual frontend environment, I temporarily created a PHP probe and executed it via two separate HTTP requests:

PHP
wp_cache_set()
wp_cache_get()
wp_cache_delete()

After writing under the Chinese domain, the second Chinese request was able to read it:

Plaintext
host=www.shuijingwanwq.com
object_cache_class=W3TC\ObjectCache_WpObjectCache
set=true

found=true
value=www.shuijingwanwq.com

After writing under the English domain, the second English request could also read it:

Plaintext
host=en.shuijingwanwq.com
object_cache_class=W3TC\ObjectCache_WpObjectCache
set=true

found=true
value=en.shuijingwanwq.com

This proves that the W3TC Redis Object Cache can persist across requests in the PHP 8.5.9 FPM environment.

More importantly, the object written under the Chinese domain could not be read under the English domain:

Plaintext
www write
en read:
found=false

The object written under the English domain likewise could not be read under the Chinese domain:

Plaintext
en write
www read:
found=false

Ultimately, both sides could precisely delete the test objects:

Plaintext
delete=true

This verification confirmed two things:

  1. W3TC Redis Object Cache under PHP 8.5.9 is working properly;
  2. The Object Cache for the Chinese and English sites is isolated by Host.

After the test, the temporary PHP probe was automatically deleted.

Throughout this process, Redis was not flushed, nor was W3TC’s Purge All Caches executed.


11. REST API and Error Log Verification

The REST APIs for both the Chinese and English sites returned 200:

Plaintext
www.shuijingwanwq.com REST HTTP=200
en.shuijingwanwq.com REST HTTP=200

I then checked the PHP-FPM and Nginx error logs and found no:

Plaintext
Fatal error
Parse error
Uncaught exception
Segmentation fault
Cannot load
Undefined symbol
upstream prematurely closed
connect failed

Ultimately, the three origin sites remained normal:

Plaintext
www.shuijingwanwq.com HTTP=200
en.shuijingwanwq.com HTTP=200
admin.shuijingwanwq.com HTTP=200

12. Plugin Deprecated Notices Exposed by PHP 8.5

After the PHP 8.5 upgrade was completed, several types of compatibility notices appeared in the WP-CLI output.

1. SyntaxHighlighter Evolved

The current version is:

Plaintext
SyntaxHighlighter Evolved 3.7.2

The plugin source code contains:

PHP
case 'false';
default;

PHP 8.5 suggests using a colon:

PHP
case 'false':
default:

However, I am currently batch processing historical SyntaxHighlighter articles. Once all the historical articles have been converted, this plugin will be directly deactivated.

Therefore, I did not modify the third-party plugin source code this time.

2. Organize Series

Organize Series uses:

PHP
SplObjectStorage::attach()
SplObjectStorage::contains()

PHP 8.5 has already output Deprecated for these two methods, recommending the use of:

PHP
SplObjectStorage::offsetSet()
SplObjectStorage::offsetExists()

Currently, these notices do not cause frontend errors, so they were not handled alongside the PHP upgrade; I will inspect them separately later.

3. WP-CLI Deprecated Output

When executing a WP-CLI command containing an invalid field, the following appeared consecutively:

Plaintext
Using null as an array offset is deprecated

However, after reusing the correct command and capturing standard output and standard error separately, the results were:

Plaintext
command_status=0
stderr_bytes=0
json_valid=true

This indicates that WP-CLI 2.12.0 can still output and parse JSON normally under PHP 8.5.9, without affecting the ongoing historical article batch processing.


13. Deleting the xprober.php Automatically Left by OneinStack

After the OneinStack installation finished, it left behind the following in the default site directory:

Plaintext
/data/wwwroot/default/xprober.php

This file can output a large amount of server and PHP environment information, and there is no need to expose it in the web directory long-term.

I first backed it up to:

Plaintext
/root/xprober.php.after-php85-20260804-190732

I then deleted it from the public directory and specifically checked:

Plaintext
/data/wwwroot/default
/data/wwwroot/www.shuijingwanwq.com

Ultimately, no other public copies of xprober.php were found.

After deletion, PHP-FPM and the three domains remained normal.


14. Final Upgrade Results

The final outcome of this PHP upgrade is as follows:

Plaintext
PHP 8.1.19 → PHP 8.5.9: Pass
PHP-FPM: Pass
Zend OPcache 8.5.9: Pass
Redis PHP Extension 6.3.0: Pass
Imagick 3.8.1: Pass
ImageMagick 7.1.1-10: Pass
LDAP: Pass
Phar / WP-CLI: Pass
WordPress 7.0.2: Pass
W3TC Redis Object Cache: Pass
www / en Object Cache Host isolation: Pass
Chinese page lang / canonical / hreflang: Pass
English page lang / canonical / hreflang: Pass
REST API: Pass
www / en / admin three origin sites: Pass

It should be noted that:

Plaintext
Redis PHP Extension: Recompiled with PHP 8.5
Redis Server: Still 7.0.11
Nginx: Still 1.24.0

So what was completed this time was a PHP layer upgrade, not a unified upgrade of the entire server software stack.


15. Future Plans

The server is currently batch processing historical WordPress articles.

Continuing to upgrade Nginx or the Redis server at this stage would introduce too many variables into the production environment simultaneously. If a batch timeout, object cache anomaly, or page output change occurs, it would be difficult to quickly distinguish whether it is an issue with the article processing script or caused by the underlying service upgrade.

Therefore, the upcoming schedule is:

  1. Continue completing the historical article conversion;
  2. Deactivate SyntaxHighlighter after article processing is complete;
  3. Separately handle PHP 8.5 Deprecated notices that still need to be retained;
  4. Only upgrade Nginx in the next round;
  5. After Nginx stabilizes, upgrade the Redis server separately.

The most noteworthy point to record during this upgrade process is:

A non-zero status returned by the installation script does not necessarily mean the entire software is unusable.

The correct approach is not to immediately reinstall upon seeing INSTALL_STATUS=1, but to continue verifying:

  • Which component failed;
  • Whether the PHP core is installed;
  • Whether PHP-FPM is running;
  • Whether the Socket exists;
  • Whether necessary extensions are loaded;
  • Whether WordPress starts normally;
  • Whether multi-domain language output is correct;
  • Whether the Object Cache is truly persisting across requests;
  • Whether there are any fatal errors in the service logs.

Ultimately, it was confirmed that the reason for the status of 1 this time was merely the Imagick 3.8.0 compilation failure. After installing Imagick 3.8.1, PHP 8.5.9 has been running stably in the current WordPress multi-domain production environment.

系列导航

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系