Local Community

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 @sammunoz, let’s hope we’ll no longer need to patch this up soon :+1:

1 Like

@dieterv -

Definitely!

Thank you,

Sam