Installing imagick on a Windows localhost WAMP
Fed up with the ambiguity in installing imagick on Windows, I decided to script it up. I run a number of environments - linux and Windows - and the Windows one had problems with installing imagick after upgrades. This comes down to a problem with the installation location of the CORE_*dll and IM_MOD_RL*dll files which seem to need to be in the ImageMagic folder instead of the PHP location.
Script
It's not pretty - but I found this worked for me...
Place this script into a tools folder that your Windows path runs through, I use c:
powerscript :: c:/ wamp64/ bin/ install-imagick.ps1
################################################
# install-imagick.ps1 - Install or repair Imagick for WAMP PHP
# Features: automatic version detection, download/ cache, stray DLL quarantine, ImageMagick check
################################################
# 1 - Detect Apache and PHP via live probe
Write-Host "nn"
Write-Host "################################################" -ForegroundColor Gray
$tsNow = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$tsNow = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
do {
# Check if Apache is running (httpd process)
$apache = Get-Process -Name "httpd" -ErrorAction SilentlyContinue
if ($apache) {
Write-Host " Apache is running. Continuing..." -ForegroundColor Green
break
}
Write-Host " $tsNow" -ForegroundColor Yellow
$response = Read-Host " WAMP/ Apache is not detected as running. Please start WAMP and press [Enter]"
$tsNow = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
} while (-not $apache)
Write-Host "`n - Detecting Apache and PHP via live probe -" -ForegroundColor Cyan
$apacheService = Get-WmiObject Win32_Service | Where-Object { $_.Name -match "apache" }
if (!$apacheService) {
Write-Host "ERROR: No Apache service detected." -ForegroundColor Red
return
}
$apacheExePath = $apacheService.PathName -replace '"',''
$apacheExePath = $apacheExePath.Split(" ")[0]
$apacheDir = Split-Path $apacheExePath
Write-Host " Apache executable detected at: " -ForegroundColor Green -NoNewline
Write-Host "$apacheExePath"
# 2 - Extract DocumentRoot
Push-Location $apacheDir
$configDump = & $apacheExePath -t -D DUMP_RUN_CFG 2>&1
Pop-Location
$docRootLine = $configDump | Where-Object { $_ -match "DocumentRoot" }
if (!$docRootLine) { Write-Host "ERROR: Could not detect DocumentRoot." -ForegroundColor Red; return }
$docRoot = ($docRootLine -replace '.*"(.*)".*','$1').Trim()
Write-Host " DocumentRoot detected: " -ForegroundColor Green -NoNewline
Write-Host "$docRoot"
# 3 - Probe Apache PHP
$probeFileName = "__apache_php_probe.php"
$probePath = Join-Path $docRoot $probeFileName
$probeCode = @ "
<?php
echo json_encode([
'php_ini' => php_ini_loaded_file(),
'version' => PHP_VERSION,
'majorMinor' => PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION,
'zts' => PHP_ZTS ? 'TS' : 'NTS',
'arch' => PHP_INT_SIZE*8 . 'bit',
'extension_dir' => ini_get('extension_dir'),
'modules' => get_loaded_extensions()
]);
?>
"@
Set-Content -Path $probePath -Value $probeCode -Encoding ASCII
$probeUrl = "http://localhost/ $probeFileName"
try {
$apachePhpInfo = (Invoke-WebRequest $probeUrl -UseBasicParsing -TimeoutSec 5).Content | ConvertFrom-Json
}
catch {
Write-Host " ERROR: Could not access localhost. Is Apache running?" -ForegroundColor Red
Remove-Item $probePath -ErrorAction SilentlyContinue
return
}
# 4 - Set PHP variables
$phpVer = $apachePhpInfo.majorMinor
$iniPath = $apachePhpInfo.php_ini
$arch = if ($apachePhpInfo.arch -eq '64bit') { 'x64' } else { 'x86' }
$ts = $apachePhpInfo.zts
$apacheExtensionDir = $apachePhpInfo.extension_dir
$apacheModules = $apachePhpInfo.modules
# 5 - Detect PHP executable in WAMP
$wampPath = "C:\wamp64\bin\php"
$phpDirs = Get-ChildItem -Path $wampPath -Directory
$phpExe = $phpDirs | ForEach-Object {
$candidate = Join-Path $_.FullName "php.exe"
if (Test-Path $candidate) {
return $candidate
}
} | Select-Object -First 1
if (-not $apacheExtensionDir) {
Write-Host " ERROR: Could not locate php.exe.nn`n" -ForegroundColor Red
return
}
$phpDir = Split-Path $apacheExtensionDir
$extDir = Join-Path $phpDir "ext"
Write-Host " Apache PHP version detected: " -ForegroundColor Green -NoNewline
Write-Host "$phpVer ($ts, $arch)"
Write-Host " Apache php.ini detected at: " -ForegroundColor Green -NoNewline
Write-Host "$iniPath"
Write-Host " Using [Apache] PHP executable: " -ForegroundColor Green -NoNewline
Write-Host "$apacheExtensionDir`n"
# 6 - Check ImageMagick
Write-Host "`n - Checking for ImageMagick installation -" -ForegroundColor Cyan
$magickExe = Get-Command IMDisplay.exe -ErrorAction SilentlyContinue
if ($magickExe) {
# Determine actual path to the exe
$exePath = if ($magickExe.Source) { $magickExe.Source } else { $magickExe.Definition }
$magickDir = Split-Path $exePath
Write-Host " ImageMagick found in PATH: $exePath in $magickDir" -ForegroundColor Green
}
else {
Write-Host " ImageMagick not found. You may install it from https://imagemagick.org" -ForegroundColor Red
return
}
# 6.1 - Check Windows PATH includes ImageMagick folder
Write-Host "`n - Verifying ImageMagick folder is in Windows PATH -" -ForegroundColor Cyan
if ($magickDir) {
# Get current PATH as an array
$pathEntries = $env:Path -split ';'
# Normalize: remove trailing slashes, case-insensitive
$pathEntriesNormalized = $pathEntries | ForEach-Object { ($_ -replace '\\+$','').ToLower() }
$magickDirNormalized = $magickDir.TrimEnd('\').ToLower()
if ($pathEntriesNormalized -contains $magickDirNormalized) {
Write-Host " ImageMagick folder is present in PATH: $magickDir" -ForegroundColor Green
}
else {
Write-Host " WARNING: ImageMagick folder is NOT in PATH: $magickDir" -ForegroundColor Red
Read-Host " Please confirm you have added it to your system PATH environment variable before continuing"
}
}
# 7 - Detect correct Imagick build
Write-Host "`n - Checking for for php_imagick download -" -ForegroundColor Cyan
$zipName = "php_imagick-3.7.0-$phpVer-$ts-vs16-$arch.zip"
$fullUrl = "https://downloads.php.net/ ~windows/ pecl/ releases/ imagick/ 3.7.0/ $zipName"
$cacheDir = Join-Path $wampPath "_imagick_cache"
if (!(Test-Path $cacheDir)) {
New-Item -ItemType Directory -Path $cacheDir | Out-Null
}
$tempZip = Join-Path $cacheDir $zipName
$tempDir = Join-Path $env:TEMP "imagick_files"
if (Test-Path $tempDir) {
Remove-Item $tempDir -Recurse -Force
}
if (-not (Test-Path $tempZip)) {
Write-Host " Downloading " -ForegroundColor Green -NoNewline
Write-Host "$zipName ..." -ForegroundColor Gray -NoNewline
Write-Host "..." -ForegroundColor Green -NoNewline
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri $fullUrl -OutFile $tempZip -UseBasicParsing
} else {
Write-Host " Using cached download: " -ForegroundColor Green -NoNewline
Write-Host "$tempZip" -ForegroundColor Gray
}
# Extract DLLs for reference
Write-Host " Expanding downloaded file..." -ForegroundColor Green
Expand-Archive -Path $tempZip -DestinationPath $tempDir -Force
$expectedDLLs = Get-ChildItem $tempDir -Filter "*.dll" | ForEach-Object { $_.Name }
# 7.1 Ensure WAMP is stopped
do {
# Check if Apache is running (httpd process)
Write-Host " WAMP/ Apache now needs to be stopped..." -ForegroundColor Green
$apache = Get-Process -Name "httpd" -ErrorAction SilentlyContinue
if ($apache) {
$response = Read-Host " Please now stop WAMP and press [Enter] to continue (timestamp:$tsNow)"
}
$apache = Get-Process -Name "httpd" -ErrorAction SilentlyContinue
$tsNow = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
} while ($apache)
Write-Host " WAMP/ Apache seen as stopped..." -ForegroundColor Green
# 8 - Checking for stray DLLs in PHP folders or full-drive (optional)
Write-Host "`n - Checking for stray Imagick files -" -ForegroundColor Cyan
# Ask the user if they want a deep scan
$deepSearch = Read-Host " Perform deep scan for stray Imagick DLLs across the drive? [y/ N]"
$searchDrives = if ($deepSearch -match '^[Yy]') { Get-PSDrive -PSProvider FileSystem } else { $phpDirs }
# Collect all DLLs from the downloaded zip
$zipDlls = Get-ChildItem $tempDir -Filter "*.dll" -File
# Separate deployment destinations
$phpDlls = $zipDlls | Where-Object { $_.Name -like "php_*.dll" }
$otherDlls = $zipDlls | Where-Object { $_.Name -notlike "php_*.dll" }
# Case-insensitive HashSet of names from zip
$zipNameSet = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ($dll in $zipDlls) { $null = $zipNameSet.Add($dll.Name) }
# Expected PHP root & ext directories
$phpRootDirs = $phpDirs | ForEach-Object { $_.FullName }
$phpExtDirs = $phpDirs | ForEach-Object { Join-Path $_.FullName "ext" }
# Start scanning
foreach ($driveOrFolder in $searchDrives) {
$rootPath = if ($driveOrFolder -is [System.Management.Automation.PSDriveInfo]) { $driveOrFolder.Root } else { $driveOrFolder.FullName }
Write-Host " Scanning root: ${rootPath}" -ForegroundColor Green
# List top-level folders first for trace
$topFolders = Get-ChildItem -Path $rootPath -Directory -ErrorAction SilentlyContinue
foreach ($top in $topFolders) {
Write-Host ("`n Scanning top-level folder: ") -ForegroundColor Green -NoNewline
Write-Host ("$($top.FullName)") -ForegroundColor Gray
$strayFilesByFolder = @ {} # init per top folder
# Get all files in this top folder once
Write-Host (" Scanning folder...") -NoNewline -ForegroundColor Yellow
Write-Host "`r" -NoNewline
$allFiles = Get-ChildItem -Path $top.FullName -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -notmatch "_imagick_quarantine" }
$counter = 0
foreach ($f in $allFiles) {
$counter++
if ($counter % 123 -eq 0) {
Write-Host (" Checked $counter files ...") -ForegroundColor Yellow -NoNewline
Write-Host "`r" -NoNewline
}
# Check PHP DLLs
if ($phpDlls.Name -contains $f.Name -and $phpExtDirs -notcontains $f.DirectoryName) {
if (-not $strayFilesByFolder.ContainsKey($f.DirectoryName)) { $strayFilesByFolder[$f.DirectoryName] = @ () }
$strayFilesByFolder[$f.DirectoryName] += $f
}
# Check other DLLs
if ($otherDlls.Name -contains $f.Name -and $magickDir -notcontains $f.DirectoryName) {
if (-not $strayFilesByFolder.ContainsKey($f.DirectoryName)) { $strayFilesByFolder[$f.DirectoryName] = @ () }
$strayFilesByFolder[$f.DirectoryName] += $f
}
}
Write-Host " Checked $counter files." -ForegroundColor Green # finish the counter line
# report and quarantine as before
foreach ($folder in $strayFilesByFolder.Keys) {
$files = $strayFilesByFolder[$folder]
Write-Host "`n Stray files found in ${folder}:" -ForegroundColor Red
foreach ($f in $files) {
Write-Host (" {0,-30} {1}" -f $f.Name, $f.LastWriteTime) -ForegroundColor Red
}
$quarantineDir = Join-Path $folder "_imagick_quarantine"
$move = Read-Host "`n Quarantine these files into $quarantineDir ? [y/ N]"
if ($move -match '^[Yy]') {
if (!(Test-Path $quarantineDir)) { New-Item -ItemType Directory -Path $quarantineDir | Out-Null }
foreach ($f in $files) { Move-Item $f.FullName -Destination $quarantineDir -Force }
Write-Host " Moved $($files.Count) file(s) to $quarantineDir" -ForegroundColor Green
}
}
}
}
# 9 Install downloaded files...
Write-Host "`n --- Preparing to install Imagick DLLs into $apacheDir and $magickDir..." -ForegroundColor Cyan
# 9.1. Define all DLL patterns to deploy
$copyPatterns = @ (
"php_imagick.dll",
"CORE_RL_*.dll",
"IM_MOD_RL_*.dll"
)
# 9.2. Stage 1 — Delete any existing DLLs
Write-Host "`n Removing existing DLLs..." -ForegroundColor Cyan
foreach ($pattern in $copyPatterns) {
$dllFiles = Get-ChildItem -Path $phpDir -Filter $pattern -ErrorAction SilentlyContinue
foreach ($dll in $dllFiles) {
Remove-Item $dll.FullName -Force -ErrorAction Stop
#Write-Host " Deleted existing ${dll.FullName}" -ForegroundColor Yellow
}
}
Write-Host "`n All old DLLs matching deploy patterns have been deleted."
# Read-Host " Press Enter when ready to copy new DLLs into place (verify via File Explorer if desired)"
# 9.3. Stage 2 — Copy new DLLs into place
Write-Host "`n Deploying new DLLs..." -ForegroundColor Cyan
foreach ($pattern in $copyPatterns) {
$dllFiles = Get-ChildItem -Path $tempDir -Filter $pattern -ErrorAction SilentlyContinue
foreach ($dll in $dllFiles) {
# Determine destination folder
$destPath = switch -Regex ($dll.Name) {
"^php_imagick\.dll$" { Join-Path $extDir $dll.Name }
"^CORE_RL_.*\.dll$" { Join-Path $magickDir $dll.Name }
"^IM_MOD_RL_.*\.dll$" { Join-Path $magickDir $dll.Name }
Default { $null }
}
if (-not $destPath) {
Write-Warning "No destination pattern matched for $($dll.Name), skipping..."
continue
}
Copy-Item -Path $dll.FullName -Destination $destPath -Force
Unblock-File $destPath
Write-Host " Copied $($dll.Name) to $destPath" -ForegroundColor Green
}
}
Write-Host "`n DLL deployment complete. Remember to restart Apache/ WAMP services."
Write-Host " DLLs installed and unblocked successfully." -ForegroundColor Green
# 10 - Final verification / cleanup -
Remove-Item $tempDir -Recurse -ErrorAction SilentlyContinue
Write-Host "`n - REPAIR FINISHED -" -ForegroundColor Green
$tsNow = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Host " IMPORTANT: Restart WAMP Services now."
Read-Host " Please now restart WAMP and press [Enter] to continue (timestamp: $tsNow)"
# Verify CLI PHP
Write-Host "`n Verifying Module Load via CLI..." -ForegroundColor Cyan
$check = & $phpExe -m | Select-String "imagick"
if ($check) { Write-Host " RESULT: Imagick loaded successfully!" -ForegroundColor Green }
else { Write-Host " WARNING: Imagick not loaded in CLI." -ForegroundColor Yellow }
# Verify Apache PHP
Write-Host " Verifying Module Load via Apache PHP..." -ForegroundColor Cyan
$probeUrl = "http://localhost/ $probeFileName"
try {
$apachePhpInfo = (Invoke-WebRequest $probeUrl -UseBasicParsing -TimeoutSec 5).Content | ConvertFrom-Json
}
catch {
Write-Host " WARNING: Could not access Apache PHP probe.nn`n" -ForegroundColor Yellow; Remove-Item $probePath -ErrorAction SilentlyContinue
return
}
if ($apachePhpInfo.modules -contains "imagick") {
Write-Host " RESULT: Imagick loaded successfully in Apache PHP!" -ForegroundColor Green
}
else {
Write-Host " WARNING: Imagick not loaded in Apache PHP. Check php.ini and Apache error_log." -ForegroundColor Yellow
}
# Cleanup probe
Remove-Item $probePath -ErrorAction SilentlyContinue
Write-Host "################################################" -ForegroundColor Gray
Write-Host "nn"
Running the script
Simply run the script using a PowerShell window...
The script will
1-5. create a small probe webpage to run on the localhost in order to acquire which versions you're running, and where
6. It then looks for the ImageMagick app and prompts the user to install it if not
7-8. Then it tries to download the appropriate php_imagick version from https://downloads.php.net
9. It then replaces any existing DLLs with those found in the download, distributing the php_*dll into the php
You will find # comments in the script to match those numbered steps.
Why not do this manually
No reason - but this will do a scan to look for any "stray" files for you, and it will automate the download of the right install file based on the localhost web-probe - as you change versions of PHP you can just run this script again to get the right version installed without thinking about it.
WARNING : BUYER-BEWARE: This script will download and manipulate files on your Windows environment - take a moment to look through the script to ensure that no rogue commands run on your PC.
powershell output..
> install-imagick.ps1
################################################
Apache is running. Continuing...
- Detecting Apache and PHP via live probe -
Apache executable detected at: c:\\wamp64\\bin\\apache\\apache2.4.54.2\\bin\\httpd.exe
DocumentRoot detected: C:/ wamp64/ www
Apache PHP version detected: 8.2 (TS, x64)
Apache php.ini detected at: C:\\wamp64\\bin\\apache\\apache2.4.54.2\\bin\php.ini
Using [Apache] PHP executable: c:/ wamp64/ bin/ php/ php8.2.0/ ext/
- Checking for ImageMagick installation -
ImageMagick found in PATH: C:\ImageMagick\IMDisplay.exe in C:\ImageMagick
- Verifying ImageMagick folder is in Windows PATH -
ImageMagick folder is present in PATH: C:\ImageMagick
- Checking for for php_imagick download -
Using cached download: C:\\wamp64\\bin\\php\\_imagick_cache\\php_imagick-3.7.0-8.2-TS-vs16-x64.zip
Expanding downloaded file...
WAMP/ Apache now needs to be stopped...
Please now stop WAMP and press [Enter] to continue (timestamp:2026-03-04 18:03:18):