Win32: WP-CLI: Warning: PHP Startup: Unable to load dynamic library ‘php_imagick.dll’

Bug Summary

On windows, opening a site shell results in a bunch of "Unable to load dynamic library ‘php_imagick.dll’ " warnings to be printed. This happens both with the “command prompt” and “git bash” terminal setting.

See also:

Steps to reproduce

Simply open a site shell on Windows and observe the warnings.
This happens both with the “command prompt” and “git bash” terminal setting.

Environment Info

Describe your environment.

For me this happens on Windows 10 using Local 6.2.0+5679 with at least PHP 7.4.1
I have not tested other PHP versions but due to the nature of how ImageMagick and the imagick PHP extension are included in the package I have no doubt those are going to suffer from similar if not identical bugs.

Other environments described in the provided community threads above.

How to fix

There’s a couple of things in %APPDATA%\Local\lightning-services\php-7.4.1+16\lib\PhpService.js that can be improved regarding how ImageMagick paths are handled. Complete file attached. Patch for quick review below.

PhpService.zip (2.5 KB)

--- <unnamed>
+++ <unnamed>
@@ -87,13 +87,15 @@
         };
     }
     get siteShellStartupBat() {
-        return `\nSET PATH=${path_1.default.join(this.$PATH, 'ImageMagick')};%PATH%\n`;
+        return `\nSET PATH=${path_1.default.join(this.$PATH, 'ImageMagick', 'bin')};%PATH%\n`;
     }
     get siteShellStartupPOSIX() {
         let shellStartup = `\nexport MAGICK_CODER_MODULE_PATH="${this.imageMagickCodersDir}"\n`;
         switch (os_1.default.platform()) {
             case 'linux':
                 shellStartup += `\nexport LD_LIBRARY_PATH="${path_1.default.join(__dirname, '../bin', 'linux', 'shared-libs')}"\n`;
+            case 'win32':
+                shellStartup += `\nexport PATH="${this.toPOSIX(this.imageMagickBinDir)}:$PATH\"n`;
         }
         return shellStartup;
     }
@@ -115,13 +117,38 @@
         });
     }
     /**
+     * This probably has an importable helper somewhere but no idea how to import it here
+     */
+     toPOSIX(t) {
+        let e = t.replace(/\\/g, "/"),
+            r = !1;
+        if ("win32" === os_1.default.platform()) {
+            0 === t.indexOf("\\\\") && (r = !0), 1 === t.indexOf(":") && (e = e.replace(":", ""));
+            const a = e.split("/").filter((t) => t);
+            1 === a[0].length && (a[0] = a[0].toLowerCase()), (e = "/" + a.join("/")), r && (e = "/" + e);
+        }
+        return e;
+    }
+    /**
      * Coders directory required by ImageMagick
      */
+    get imageMagickBinDir() {
+        if (os_1.default.platform() === 'win32' && !Local.isWindows32Bit()) {
+            return path_1.default.join(__dirname, '../bin', 'win64', 'ImageMagick', 'bin');
+        }
+        return path_1.default.join(__dirname, '../bin', os_1.default.platform(), 'ImageMagick', 'bin');
+    }
     get imageMagickCodersDir() {
+        if (os_1.default.platform() === 'win32' && !Local.isWindows32Bit()) {
+            return path_1.default.join(__dirname, '../bin', 'win64', 'ImageMagick', 'modules-Q16', 'coders');
+        }
         return path_1.default.join(__dirname, '../bin', os_1.default.platform(), 'ImageMagick', 'modules-Q16', 'coders');
     }
     get ghostscriptLib() {
         // GS_LIB env variable in fpm www config
+        if (os_1.default.platform() === 'win32' && !Local.isWindows32Bit()) {
+            return path_1.default.join(__dirname, '../bin', 'win64', 'ghostscript', 'Resource', 'Init');
+        }
         return path_1.default.join(__dirname, '../bin', os_1.default.platform(), 'ghostscript', 'Resource', 'Init');
     }
     get lightningServicePlatform() {

Hi Dieterv,

Thanks for this information.

I tested your solution and after finding the place the PhpService.js file needs to go I got a positive result when opening a Bash shell from Local :+1:

My location for the PhpService.js was slightly different and could be found at:

%APPDATA%\Roaming\Local\lightning-services\php-7.4.1+16\lib\PhpService.js

Another question which came to mind; the ImageMagick Coders directory does this allows come pre-installed with either Win10, or Local itself?

Thanks again,
Ruud

Hi Ruud,

Looks like there might be slight variations in the exact location %APPDATA% expands to. I guess %USERPROFILE%\AppData\Roaming\Local\lightning-services\php-7.4.1+16\lib\PhpService.js would probably be more correct then :slight_smile:

I don’t understand your other question, but the way things are set up in PhpService.js the MAGICK_CODER_MODULE_PATH variable locks the path where ImageMagick can locate its coder modules to the %USERPROFILE%\AppData\Roaming\Local\lightning-services\php-7.4.1+16\bin\win64\ImageMagick\modules-Q16\coders\ directory. So that would be the version that comes with LocalWP. This directory does not actually exist in my LocalWP installation though.

Thanks for your reply which helped me find the ImageMagick directory too.
Mine does also not have a modules-Q16\coders directory.

I checked this error and its still around.

Local 6.4.0 has a warning on PHP startup. The CLI is fixed by this code but not the WordPress Version area inside the UI.

Does anyone know why the Local team have not fixed this?

Hi!

I think that recently my PHP 7.4.1 setup got updated to version PHP7.4.1+18 (instead of +16)

This triggered the return of the issues with the missing imagick.dll

On the location we patched the PhpService.js before there was no php-7.4.1+18 folder but instead it had moved to a new location =>

%AppData%\Local\Programs\Local\resources\extraResources\lightning-services\php-7.4.1+18\lib\PhpService.js

After merging in the changes from the 7.4.1+16 version it worked again.

Hopes this helps!
Ruud

1 Like

Just wanted to give an update that this fix is still working on Jan 23, with the latest version of Local running on Windows 11.

1 Like

In case someone looking for the full PHPService.js code for version 7.4.30+5, here it is:

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Local = __importStar(require("@getflywheel/local"));
const LocalMain = __importStar(require("@getflywheel/local/main"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const slash_1 = __importDefault(require("slash"));
const fs_extra_1 = __importDefault(require("fs-extra"));
class PhpService extends LocalMain.LightningService {
    constructor() {
        super(...arguments);
        this.serviceName = 'php';
        this.label = 'PHP';
        this.binVersion = '7.4.30';
    }
    get requiredPorts() {
        return { cgi: os_1.default.platform() !== 'win32' ? 1 : 2 };
    }
    get configTemplatePath() {
        return path_1.default.join(__dirname, '../conf');
    }
    get env() {
        switch (os_1.default.platform()) {
            case 'win32':
                return {
                    PATH: `${process.env.PATH}${path_1.default.delimiter}${path_1.default.join(this.$PATH, 'ImageMagick', 'bin')}${path_1.default.delimiter}${path_1.default.join(this.$PATH, 'ghostscript', 'bin')}`,
                    GS_LIB: `${path_1.default.join(this.$PATH, 'ghostscript', 'Resource', 'Init')}`,
                };
            case 'linux':
                return {
                    LD_LIBRARY_PATH: `${path_1.default.join(__dirname, '../bin', 'linux', 'shared-libs')}`,
                };
            case 'darwin':
                return {
                    PATH: `${process.env.PATH}${path_1.default.delimiter}${path_1.default.join(this.$PATH, 'ghostscript', 'bin')}`,
                };
        }
        return {};
    }
    get bins() {
        return {
            [LocalMain.LightningServicePlatform.Darwin]: {
                'php': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Darwin], 'php'),
                'phpCgi': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Darwin], 'php-cgi'),
                'phpFpm': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Darwin], '../sbin', 'php-fpm'),
            },
            [LocalMain.LightningServicePlatform.DarwinArm64]: {
                'php': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.DarwinArm64], 'php'),
                'phpCgi': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.DarwinArm64], 'php-cgi'),
                'phpFpm': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.DarwinArm64], '../sbin', 'php-fpm'),
            },
            [LocalMain.LightningServicePlatform.Linux]: {
                'php': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Linux], 'php'),
                'phpCgi': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Linux], 'php-cgi'),
                'phpFpm': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Linux], '../sbin', 'php-fpm'),
            },
            [LocalMain.LightningServicePlatform.Win32]: {
                'php': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Win32], 'php.exe'),
                'phpCgi': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Win32], 'php-cgi.exe'),
            },
            [LocalMain.LightningServicePlatform.Win32x64]: {
                'php': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Win32x64], 'php.exe'),
                'phpCgi': path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Win32x64], 'php-cgi.exe'),
            },
        };
    }
    get $PATHs() {
        return {
            [LocalMain.LightningServicePlatform.Darwin]: path_1.default.join(__dirname, '../bin', 'darwin', 'bin'),
            [LocalMain.LightningServicePlatform.DarwinArm64]: path_1.default.join(__dirname, '../bin', 'darwin-arm64', 'bin'),
            [LocalMain.LightningServicePlatform.Win32]: path_1.default.join(__dirname, '../bin', 'win32'),
            [LocalMain.LightningServicePlatform.Win32x64]: path_1.default.join(__dirname, '../bin', 'win64'),
            [LocalMain.LightningServicePlatform.Linux]: path_1.default.join(__dirname, '../bin', 'linux', 'bin'),
        };
    }
    get siteShellStartupBat() {
        return `\nSET PATH=${path_1.default.join(this.$PATH, 'ImageMagick', 'bin')};%PATH%\n`;
    }
    get siteShellStartupPOSIX() {
        let shellStartup = `\nexport MAGICK_CODER_MODULE_PATH="${this.imageMagickCodersDir}"\n`;
        switch (os_1.default.platform()) {
            case 'linux':
                shellStartup += `\nexport LD_LIBRARY_PATH="${path_1.default.join(__dirname, '../bin', 'linux', 'shared-libs')}"\n`;
            case 'win32':
                shellStartup += `\nexport PATH="${this.toPOSIX(this.imageMagickBinDir)}:$PATH\"n`;
        }
        return shellStartup;
    }
    get extensionsDir() {
        return {
            [LocalMain.LightningServicePlatform.Darwin]: path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Darwin], '../lib', 'php', 'extensions', 'no-debug-non-zts-20190902'),
            [LocalMain.LightningServicePlatform.DarwinArm64]: path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.DarwinArm64], '../lib', 'php', 'extensions', 'no-debug-non-zts-20190902'),
            [LocalMain.LightningServicePlatform.Linux]: path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Linux], '../lib', 'php', 'extensions', 'no-debug-non-zts-20190902'),
            [LocalMain.LightningServicePlatform.Win32]: path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Win32], 'ext'),
            [LocalMain.LightningServicePlatform.Win32x64]: path_1.default.join(this.$PATHs[LocalMain.LightningServicePlatform.Win32x64], 'ext'),
        };
    }
    get socket() {
        return path_1.default.join(this.runPath, 'php-fpm.socket');
    }
    preprovision() {
        return __awaiter(this, void 0, void 0, function* () {
            yield fs_extra_1.default.ensureDir(this.logsPath);
            yield fs_extra_1.default.ensureDir(this.runPath);
        });
    }
    toPOSIX(t) {
        let e = t.replace(/\\/g, "/"),
            r = !1;
        if ("win32" === os_1.default.platform()) {
            0 === t.indexOf("\\\\") && (r = !0), 1 === t.indexOf(":") && (e = e.replace(":", ""));
            const a = e.split("/").filter((t) => t);
            1 === a[0].length && (a[0] = a[0].toLowerCase()), (e = "/" + a.join("/")), r && (e = "/" + e);
        }
        return e;
    }
    /**
     * Coders directory required by ImageMagick
     */
    get imageMagickBinDir() {
        if (os_1.default.platform() === 'win32' && !Local.isWindows32Bit()) {
            return path_1.default.join(__dirname, '../bin', 'win64', 'ImageMagick', 'bin');
        }
        return path_1.default.join(__dirname, '../bin', os_1.default.platform(), 'ImageMagick', 'bin');
    }
    get imageMagickCodersDir() {
        if (os_1.default.platform() === 'win32' && !Local.isWindows32Bit()) {
            return path_1.default.join(__dirname, '../bin', 'win64', 'ImageMagick', 'modules-Q16', 'coders');
        }
        return path_1.default.join(__dirname, '../bin', os_1.default.platform(), 'ImageMagick', 'modules-Q16', 'coders');
    }
    get ghostscriptLib() {
        // GS_LIB env variable in fpm www config
        if (os_1.default.platform() === 'win32' && !Local.isWindows32Bit()) {
            return path_1.default.join(__dirname, '../bin', 'win64', 'ghostscript', 'Resource', 'Init');
        }
        return path_1.default.join(__dirname, '../bin', os_1.default.platform(), 'ghostscript', 'Resource', 'Init');
    }
    get lightningServicePlatform() {
        if (os_1.default.platform() === 'win32' && !Local.isWindows32Bit()) {
            return LocalMain.LightningServicePlatform.Win32x64;
        }
        if (os_1.default.platform() === 'darwin' && os_1.default.arch() === 'arm64') {
            return LocalMain.LightningServicePlatform.DarwinArm64;
        }
        return os_1.default.platform();
    }
    get configVariables() {
        var _a, _b;
        const site = this._site;
        const mailhogService = this._lightningServices.getSiteService(site, 'mailhog');
        const dbService = this._lightningServices.getSiteServiceByRole(site, Local.SiteServiceRole.DATABASE);
        const apache = this._lightningServices.getSiteServiceByRole(site, Local.SiteServiceRole.HTTP).serviceName === 'apache';
        const wpCaBundlePath = path_1.default.join(site.longPath, "app", "public", "wp-includes", "certificates", "ca-bundle.crt");
        const xdebugEnabled = Object.keys(site).includes('xdebugEnabled') ? site.xdebugEnabled : true;
        return {
            wpCaBundlePath,
            apache,
            phpFpm: {
                socket: slash_1.default(this.socket),
                logs: {
                    errorLog: path_1.default.join(this.logsPath, 'php-fpm.log'),
                },
            },
            imageMagick: {
                codersDir: this.imageMagickCodersDir,
                ghostscriptLib: this.ghostscriptLib,
            },
            extensionsDir: slash_1.default(this.extensionsDir[this.lightningServicePlatform]),
            logs: {
                errorLog: path_1.default.join(this.logsPath, 'error.log'),
            },
            mail: {
                mailhogPath: mailhogService === null || mailhogService === void 0 ? void 0 : mailhogService.bin.mailhog.replace(/\'/g, "\\\'"),
                mailhogSmtpAddr: `127.0.0.1:${(_a = mailhogService === null || mailhogService === void 0 ? void 0 : mailhogService.ports) === null || _a === void 0 ? void 0 : _a['SMTP'][0]}`,
            },
            mysql: {
                port: os_1.default.platform() === 'win32' ? (_b = dbService === null || dbService === void 0 ? void 0 : dbService.port) === null || _b === void 0 ? void 0 : _b.toString() : '',
                socket: os_1.default.platform() !== 'win32' ? dbService === null || dbService === void 0 ? void 0 : dbService.socket : '',
            },
            xdebugEnabled,
        };
    }
    start() {
        fs_extra_1.default.ensureDirSync(this.runPath);
        /**
         * PHP-FPM isn't available on Windows so we will handle statically pooling PHP CGI.
         */
        if (os_1.default.platform() === 'win32') {
            const processes = [];
            for (const phpPort of this.ports.cgi) {
                processes.push({
                    name: 'phpCgi',
                    binPath: this.bin.phpCgi,
                    env: this.env,
                    args: [
                        '-b',
                        `127.0.0.1:${phpPort}`,
                        '-c',
                        this.configPath,
                    ],
                });
            }
            return processes;
        }
        return [
            {
                name: 'phpFpm',
                binPath: this.bin.phpFpm,
                env: this.env,
                args: [
                    '-F',
                    '--prefix',
                    this.configPath,
                    `--fpm-config`,
                    path_1.default.join(this.configPath, 'php-fpm.conf'),
                    `-c`,
                    this.configPath,
                ],
            },
        ];
    }
}
exports.default = PhpService;

To Local team, please fix this. I know the people who actually uses WP CLI is very small, but the fix is already up there.

1 Like

Thank you for letting us know @volkner - and welcome to the Local community! :wave:

I’ll be sure to flag this to the Local engineering team.

We appreciate you,

Sam

Thanks @sambrockway, let’s hope we’ll no longer need to patch this up soon :+1:

1 Like

@dieterv -

Definitely!

Thank you,

Sam

Thank you for this- I ran into this issue on Win64 using the latest version of local a/o 12 July 2023 (i.e. 7.0.2+6395) when running PHP 7.4.30. I copied the edited PHPService.js from @dieterv to the specified directory and I can confirm this still appears to fix the issue.

1 Like

Thank you for letting us know @nickpish!

Sam

1 Like

I picked up Local because its billed as an “instant” install. I am getting the error described above on the 64bit version: in full:

Warning: PHP Startup: Unable to load dynamic library ‘php_imagick.dll’ (tried: C:/Users/Art/AppData/Roaming/Local/lightning-services/php-8.1.23+0/bin/win64/ext\php_imagick.dll (The specified procedure could not be found), C:/Users/Art/AppData/Roaming/Local/lightning-services/php-8.1.23+0/bin/win64/ext\php_php_imagick.dll.dll (The specified module could not be found)) in Unknown on line 06.4.2

Since it is just a warning, and I’ve no interest in tinkering with config files (my programming days are decades behind me), I’ve got two questions:

  1. will I regret ignoring the warning and pushing on?
  2. can I expect more failures like this, or are others finding this a generally clean product?

Hi @Art_in_MT

will I regret ignoring the warning and pushing on?

While you can technically ignore the warning, it’s not recommended to do so, especially if your application relies on the Imagick extension. The warning indicates that PHP is unable to load the Imagick extension, which means the functionality provided by Imagick won’t be available. This is entirely dependent on your needs however. If you would like to resolve it, the fix shared at the top of this post will be your best bet for now.

can I expect more failures like this, or are others finding this a generally clean product?

For ourselves and our millions of active users worldwide we find Local to be pretty great and dependable! If you have any other questions or concerns don’t hesitate to reach back out however. We are always happy to help!

Thanks! I’ll buckle on my old boots and start wading…

2 Likes

Hi @Art_in_MT -

Let us know if you need any other help along the way!

Sam

This topic was automatically closed after 730 days. New replies are no longer allowed.