Missing PHP Extensions in OpenCart: How to Fix mcrypt and ZIP Errors
OpenCart can report a missing PHP extension during OpenCart installation, after a PHP upgrade, while opening the administration area, or when an extension package is being installed. The first reaction is often to search for a package named php-zip or php-mcrypt and install it. That can solve a genuine ZIP problem, but it is the wrong starting point for mcrypt.
ZIP and mcrypt represent two different classes of failure. PHP ZIP is normal archive functionality, and OpenCart uses ZipArchive when processing extension packages. Mcrypt belongs to legacy PHP code: it was deprecated in PHP 7.1 and removed from the PHP core in PHP 7.2. When an OpenCart store asks for mcrypt, first identify which old part of the application still expects it.
There is another trap. Running php -m over SSH only tells you what the command-line PHP binary has loaded. OpenCart may be using another PHP version, another php.ini, PHP-FPM, or a domain-specific handler. A server can therefore show ZIP in the terminal while the store still has no ZipArchive class.
Start with the exact error, the action that triggers it, and the PHP runtime serving that hostname. Then decide whether you are dealing with a missing ZIP extension, a PHP configuration mismatch, a broken extension package, or legacy code that still calls mcrypt.
- If
ZipArchiveis missing, enable or install ZIP for the exact PHP build serving the OpenCart domain. - If mcrypt is missing, locate the old OpenCart code, extension, modification, or library that calls it before changing PHP.
- If
php -mshows ZIP but OpenCart does not, compare the web PHP version, SAPI, loaded INI files, PHP-FPM pool, and domain handler. - If
ZipArchivealready works but an.ocmod.zippackage still fails, continue with upload, archive, package-structure, and filesystem checks instead of reinstalling ZIP.
Why does OpenCart say mcrypt or ZIP is missing?
OpenCart reports a missing PHP feature either because the web PHP runtime really does not provide it or because application code expects functionality that does not belong on the current PHP stack. The exact error message normally tells you which path to investigate.
For example, these two fatal errors may appear after the same server migration, but they point in different directions:
PHP Fatal error: Uncaught Error:
Class "ZipArchive" not found
in /path/to/opencart/...
PHP Fatal error: Uncaught Error:
Call to undefined function mcrypt_decrypt()
in /path/to/opencart/extension/...
The first error says that the PHP process executing the request cannot provide ZipArchive. The second says that some executed code still calls an old mcrypt function. Installing php-zip may be appropriate for the first case. Treating the second case as another missing-package problem can push the server in the wrong direction.
| Symptom | Likely source | First check |
|---|---|---|
Class "ZipArchive" not found |
ZIP unavailable in the active PHP runtime | class_exists('ZipArchive') from the web request |
| ZIP is marked as missing | PHP configuration or wrong PHP version | Check ZIP in the OpenCart web runtime |
Call to undefined function mcrypt_encrypt() |
Legacy PHP code | Find the caller in the stack trace |
Call to undefined function mcrypt_decrypt() |
Legacy encryption/decryption code | Search the OpenCart tree for mcrypt_ |
| A third-party module requires mcrypt | Old extension or bundled library | Check module, OpenCart, and PHP compatibility |
| ZIP works but an extension still will not install | Upload, archive, installer, package, or filesystem problem | Stop reinstalling ZIP and inspect the next layer |
When the error appears, record five things before making changes: the complete message, the URL or action that triggered it, whether it came from PHP or the OpenCart installer, the file and line number if PHP provides them, and whether the problem affects the whole store or one specific feature.
A storefront that works until one payment module is used tells a different story from an administration area where every extension upload fails. Likewise, an error that starts immediately after changing a domain from one PHP version to another is strong evidence to compare the old and new runtime before modifying OpenCart files.
First find out who is asking for the extension. That usually narrows the problem faster than installing packages at random.
Is mcrypt still required by OpenCart?
Mcrypt should be treated as a legacy compatibility clue, not as a normal extension to add automatically to a current PHP environment. PHP deprecated mcrypt in PHP 7.1 and removed it from the PHP core in PHP 7.2. A separate mcrypt extension can exist outside the core, but restoring it does not remove the reason old application code depends on it.
This usually surfaces during migrations. A store may have run for years on an older server where mcrypt happened to be available. The same files are moved to a newer PHP environment, the homepage loads, and only checkout, an API integration, or a particular admin page fails. The dependency was already there; the migration merely exposed it.
Record the OpenCart, PHP, and module versions before changing mcrypt
Do not decide that “OpenCart requires mcrypt” from the CMS name alone. First identify the actual application generation and the component that fails. Old OpenCart installations often include third-party extensions, custom modifications, and files carried through several upgrades.
Record at least:
- the OpenCart version;
- the PHP version serving the affected hostname;
- the name and version of the failing extension if one is involved;
- the file containing the mcrypt call;
- whether that file belongs to core code, a third-party extension, OCMOD/VQMod output, or custom code.
If the OpenCart version is not obvious in the administration area, search the active application tree rather than relying on memory:
grep -RIn --include='*.php' "define('VERSION'" /path/to/opencart | head
Multiple results require care. Old deployment copies or another application under the same account can contain their own version constants. Match the result to the document root and entry point used by the affected domain.
A mcrypt call inside a third-party payment extension points toward that extension first. A call inside a custom modification points toward the modification. A failure that appeared only after moving unchanged application files to a newer PHP runtime points toward an application/PHP compatibility gap.
Why mcrypt disappeared from modern PHP
A current PHP installation normally does not expose functions such as mcrypt_encrypt() and mcrypt_decrypt(). When legacy code executes one of those functions without a compatibility implementation, PHP stops with an undefined-function error.
That is different from a disabled ZIP module. With ZIP, enabling the correct extension is normally part of repairing the server runtime. With mcrypt, restoring the function can make the fatal error disappear while leaving an outdated application dependency in place.
| Extension | Status in troubleshooting | Why OpenCart may mention it | Correct first action | What not to do first |
|---|---|---|---|---|
ZIP / ZipArchive |
Normal archive functionality | OpenCart or an extension needs to read a ZIP package | Verify and enable ZIP in web PHP | Do not assume CLI PHP represents the site |
| mcrypt | Legacy dependency | Old code still calls mcrypt functions or constants | Identify the caller and compatibility requirements | Do not downgrade PHP or restore mcrypt blindly |
| OpenSSL or Sodium | Current cryptographic facilities | Maintained code may use them instead of old mcrypt implementations | Follow a tested application migration path | Do not replace crypto functions one-for-one without checking data compatibility |
How to tell whether OpenCart core or a module requires mcrypt
A PHP fatal generated during an OpenCart request may originate far below the controller the user sees. A payment module, shipping integration, OCMOD change, VQMod modification, custom library, or bundled vendor package can introduce the actual call.
The file path in the fatal error is the first strong clue. The stack trace is better because it shows the execution chain. If a path clearly belongs to one extension, investigate that extension's supported PHP versions before changing the server for the entire store.
Also account for files left behind by old deployments. Replacing OpenCart core files does not necessarily remove abandoned modules or custom libraries. A source-code search may therefore find mcrypt code that is no longer executed, which is why grep results need to be confirmed against the real stack trace.
How do you check which PHP extensions OpenCart actually sees?
The decisive test is the PHP environment serving the OpenCart domain. SSH gives you a useful baseline, but package-manager output and php -m do not prove what PHP-FPM, FastCGI, or Apache loads for the website.
Check the CLI PHP first
Run:
php -v
php --ini
php -m | grep -Ei 'zip|mcrypt'
php -r "var_dump(extension_loaded('zip'), class_exists('ZipArchive'));"
php -v identifies the command-line PHP build. php --ini shows the main configuration and the directory used for additional INI files. php -m lists modules loaded by that CLI process. The last command confirms whether the CLI runtime exposes both the ZIP extension and the ZipArchive class.
A healthy CLI result might be:
bool(true)
bool(true)
That means ZIP works in CLI PHP. Nothing more. OpenCart can still be using another runtime. The same runtime-versus-CLI distinction also appears in other cases such as a missing Phar extension.
Check PHP from the OpenCart web environment
Create a temporary diagnostic PHP file and request it through the exact hostname where OpenCart fails. A small script gives enough information without publishing a full phpinfo() page:
<?php
header('Content-Type: text/plain; charset=UTF-8');
echo 'PHP_VERSION=' . PHP_VERSION . PHP_EOL;
echo 'PHP_SAPI=' . PHP_SAPI . PHP_EOL;
echo 'php.ini=' . (php_ini_loaded_file() ?: 'none') . PHP_EOL;
echo 'scanned_ini=' . (php_ini_scanned_files() ?: 'none') . PHP_EOL;
echo 'extension_dir=' . ini_get('extension_dir') . PHP_EOL;
echo 'zip_loaded=';
var_dump(extension_loaded('zip'));
echo 'ZipArchive=';
var_dump(class_exists('ZipArchive'));
echo 'mcrypt_loaded=';
var_dump(extension_loaded('mcrypt'));
Compare the first five values before focusing on ZIP. PHP_VERSION identifies the web PHP build, PHP_SAPI shows how PHP is invoked, php_ini_loaded_file() identifies the main configuration file, and php_ini_scanned_files() shows additional INI files loaded after it. extension_dir tells you where that PHP build expects shared extensions.
A mismatch can look like this:
CLI:
PHP 8.x
php.ini=/etc/php/8.x/cli/php.ini
ZIP=yes
Web:
PHP 8.y
PHP_SAPI=fpm-fcgi
php.ini=/etc/php/8.y/fpm/php.ini
ZIP=no
In that situation, there is little value in reinstalling the extension for the CLI build. The OpenCart request is executing a different PHP version.
| Check | CLI | OpenCart web request | Why it matters |
|---|---|---|---|
| PHP version | php -v |
PHP_VERSION |
ZIP must belong to the PHP build serving the store |
| SAPI | Usually cli |
PHP_SAPI |
CLI, FPM, CGI, and Apache can load different configuration |
| Main INI | php --ini |
php_ini_loaded_file() |
Shows whether you are editing the configuration the website reads |
| Additional INI files | php --ini |
php_ini_scanned_files() |
An extension can be enabled through a separate INI file |
| Extension directory | php -i |
ini_get('extension_dir') |
Useful when PHP reports that an extension library cannot be loaded |
| ZIP extension | extension_loaded('zip') |
extension_loaded('zip') |
Shows whether ZIP loaded in each runtime |
| ZipArchive | class_exists('ZipArchive') |
class_exists('ZipArchive') |
Confirms that OpenCart can use the archive class |
Delete the diagnostic file when the comparison is complete. Do not simply rename it inside the public document root; remove it.
How do you enable ZIP for OpenCart?
If the OpenCart web runtime returns false for extension_loaded('zip') or cannot find ZipArchive, ZIP needs attention in that exact PHP environment. Four states need to be kept separate: a ZIP package can exist on the server, the extension can be configured for one PHP build, CLI PHP can load it, and the OpenCart web runtime can still fail to provide ZipArchive.
Enable ZIP in a hosting control panel
On shared hosting or managed hosting, start with the PHP version assigned to the affected hostname. This matters when the account contains several sites, a separate admin hostname, or subdomains with their own PHP selectors.
- Open the PHP settings for the exact OpenCart domain or subdomain.
- Record the assigned PHP version.
- Open the extension list for that version.
- Enable
zipif the panel exposes it as an optional extension. - Apply the change.
- Request the web diagnostic again and verify
extension_loaded('zip')andclass_exists('ZipArchive').
cPanel hosting with CloudLinux, DirectAdmin, Plesk, ISPmanager, and custom hosting panels use different names for the PHP selector and extension screen. The label is secondary. The result must be visible from the OpenCart web request.
A common dead end looks like this: the panel shows ZIP enabled, support sees the same checkbox, but ZipArchive is still missing through the domain. At that point the checkbox has stopped being useful evidence. Compare the PHP version shown by the panel with PHP_VERSION from the web request. If they differ, investigate the domain's handler or ask the provider to confirm which PHP service actually processes that hostname.
Install ZIP on a Linux VPS
On a VPS, install the ZIP package that belongs to the PHP build serving OpenCart. Debian- and Ubuntu-family systems commonly provide:
apt install php-zip
On systems where several PHP branches are installed, the generic package can follow the distribution's default PHP rather than the version selected for the store. Repositories may therefore provide a version-specific package name. Confirm the web PHP version first instead of copying a version number from another server guide.
On RHEL-family systems, a common package pattern is:
dnf install php-zip
Alternative repositories and hosting panels can package PHP separately from the operating system. If the server contains both OS PHP and panel-managed PHP, installing the operating-system package may change a runtime OpenCart never uses.
How to confirm ZIP was loaded by the PHP build you installed it for
Installation is only the first checkpoint. Verify the resulting PHP configuration:
php --ini
php -m | grep -i zip
php -r "var_dump(extension_loaded('zip'), class_exists('ZipArchive'));"
Then compare those CLI values with the web diagnostic. The useful chain is:
- The package exists for the intended PHP build.
- That PHP build reads the ZIP extension configuration.
- The web SAPI serving OpenCart loads the same extension.
class_exists('ZipArchive')returnstruethrough the OpenCart hostname.
If package installation succeeds but PHP still cannot load ZIP, check for PHP startup warnings. An error such as this changes the diagnosis:
PHP Warning: PHP Startup:
Unable to load dynamic library 'zip' ...
Now the problem is not simply “ZIP disabled”. PHP is trying to load an extension and failing. Check the PHP version, extension_dir, the actual extension configuration file, and whether the installed extension binary belongs to the same PHP build.
On a PHP-FPM stack, reload or restart the PHP-FPM service that serves the affected pool if the packaging or configuration change requires it. Do not restart every PHP service on a multi-version host.
Windows/IIS and other non-Linux PHP stacks should follow the same verification model even though Linux package commands do not apply: identify the PHP build handling the site, use that build's extension configuration, recycle the relevant PHP/FastCGI process if necessary, and verify ZipArchive from a web request.
extension=zip to the first php.ini file you find.
An INI file used only by CLI will not repair PHP-FPM. An extension directive that points to a missing or incompatible library can also introduce a PHP startup warning instead of solving the OpenCart error.
Why does php -m show ZIP while OpenCart still says it is missing?
If CLI PHP already loads ZIP but the OpenCart web request does not, stop treating this as an installation problem. You have proved that ZIP exists somewhere on the server. Now you need to find why the request serving OpenCart reaches a different PHP configuration.
Start with one comparison only:
CLI:
php -v
php --ini
php -r "var_dump(class_exists('ZipArchive'));"
Web:
PHP_VERSION
PHP_SAPI
php_ini_loaded_file()
php_ini_scanned_files()
class_exists('ZipArchive')
If CLI says true and web PHP says false, follow the mismatch rather than reinstalling ZIP.
Check whether the domain uses another PHP interpreter
The first hard mismatch is the PHP version itself. A generic php command can point to the system default while the domain is configured for a different PHP-FPM service. Hosting panels make this even easier because each domain can have its own PHP selector.
The same account can also behave differently by hostname. For example, shop.example.com may run one PHP version while admin.example.com or a staging subdomain uses another. Run the diagnostic through the exact hostname where the error occurs.
If the hosting panel reports one PHP version but PHP_VERSION reports another, do not assume the panel has merely failed to refresh. Possible causes include a different domain-level handler, another virtual host matching the hostname, an unapplied panel configuration, or a separate PHP-FPM mapping.
On a self-managed VPS, inspect the virtual host that handles the request. With Nginx, the PHP destination is usually visible in the active configuration around fastcgi_pass:
nginx -T 2>&1 | grep -nE 'server_name|fastcgi_pass'
With Apache, first identify which virtual host matches the hostname:
apachectl -S
Then inspect that virtual host's PHP handler or FastCGI/FPM mapping. Configuration layouts differ between distributions and panels, so the goal is not to find a specific file name from a tutorial. The goal is to establish which PHP process receives this request.
Check where PHP-FPM loads extension configuration
If CLI and web PHP report the same major PHP version but ZIP still differs, compare the main and scanned INI files. An extension can be enabled through a file such as a module-specific INI under one SAPI configuration tree while the FPM tree does not load it.
The web diagnostic should show:
php_ini_loaded_file()
php_ini_scanned_files()
ini_get('extension_dir')
CLI gives the corresponding information with:
php --ini
php -i | grep -E 'extension_dir|Scan this dir|Additional .ini files parsed'
If CLI scans a ZIP configuration file that does not appear in the web output, you have found the layer where the configurations diverge. Fix the extension configuration for FPM rather than adding duplicate directives to unrelated INI files.
Check the PHP-FPM pool and service you actually reloaded
Multi-version servers can run several PHP-FPM services at once, and each service can contain multiple pools. Restarting one of them proves nothing about a domain attached to another.
These commands can help identify what is running on a systemd-based Linux server:
systemctl list-units --type=service | grep -Ei 'php|fpm'
ps aux | grep '[p]hp-fpm'
Compare the running services with the virtual host or panel assignment. If the site points to one FPM socket or service and you reloaded another, OpenCart will continue to report the old extension state.
A particularly useful symptom is that one hostname starts seeing ZIP after a reload while another does not. That usually means the two requests are not reaching the same pool or PHP configuration.
Check for a PHP startup error instead of another missing-extension error
Sometimes the configuration files match and contain a ZIP directive, yet ZipArchive remains unavailable. Read the PHP-FPM or web-server error log around a fresh request. A startup message such as:
PHP Warning: PHP Startup:
Unable to load dynamic library 'zip' ...
means PHP found an instruction to load ZIP but could not load the module successfully. At that point compare the extension binary, PHP build, extension directory, and package source. Re-adding the same INI directive only produces the same startup failure.
The advanced path is therefore straightforward: compare versions, compare SAPI and INI sources, identify the domain's handler, identify the FPM service and pool, check startup logs, reload the correct runtime, and then retest through the hostname. Once web PHP returns true for ZipArchive, the runtime mismatch is resolved.
What should you do if an old OpenCart store still requires mcrypt?
If an older OpenCart installation or extension fails with mcrypt_encrypt(), mcrypt_decrypt(), or another mcrypt_* call, treat it as an application compatibility problem first. The preferred long-term repair is to remove the dependency by updating, replacing, or correctly migrating the code that still uses it.
The practical order is:
- Update OpenCart or the affected extension to code that no longer requires mcrypt.
- Replace an abandoned extension with a maintained alternative.
- Use a vendor-provided or thoroughly tested compatibility patch.
- Keep the legacy application temporarily in an isolated compatible runtime while the migration is prepared.
- Use an external mcrypt implementation only as a deliberate compatibility workaround when the application cannot yet be changed.
Should you install PECL mcrypt for an old OpenCart store?
PECL mcrypt should not be the default response to an OpenCart mcrypt error. It can be a temporary compatibility measure when you have confirmed that the active application really executes mcrypt-dependent code and replacing that code immediately is not practical.
Before taking that route, record why it is needed, which component depends on it, which PHP environment will load it, and how the application will eventually move away from it. Otherwise a temporary migration workaround can quietly become permanent server architecture.
Why a PHP downgrade is not a good first fix
Downgrading an entire server merely to restore one obsolete dependency exchanges a visible compatibility error for an older runtime. It can also affect newer applications on the same host and make the next upgrade harder.
An older isolated runtime can still make sense during a controlled migration. Suppose a legacy store must remain online while payment, shipping, and custom modules are replaced. Keeping that store on a known compatible environment for a defined transition period may be safer than editing cryptographic code directly on production. That is a migration strategy, not a final repair.
Before choosing a runtime, build a compatibility record:
OpenCart version:
Affected extension:
Extension version:
Current web PHP:
PHP version supported by the extension:
File calling mcrypt:
Maintained replacement available?:
Existing encrypted data involved?:
This often exposes a narrow dependency: one old extension may be the only component holding the application on an older PHP stack.
Why replacing mcrypt with OpenSSL is not a one-line patch
Do not replace mcrypt_encrypt() with openssl_encrypt() merely because both functions encrypt data. Existing ciphertext can depend on cipher selection, mode, IV length, key handling, padding behavior, binary encoding, and transformations performed before or after encryption.
A patch can therefore appear successful with newly created values while making historical values impossible to decrypt. This matters when a module stores tokens, credentials, integration secrets, or any data that must remain readable after the migration.
Before modifying the implementation, identify:
- the cipher and mode;
- key length and key derivation;
- initialization-vector handling;
- padding behavior;
- binary versus encoded ciphertext;
- how existing stored values are read;
- how backward compatibility will be tested.
Make a backup before experimenting with code that reads or rewrites encrypted data. Then test on staging with representative historical values as well as newly generated ones. Removing the PHP fatal while silently losing the ability to decrypt old data is a worse outcome than the original error.
How do you find the OpenCart extension that still calls mcrypt?
If only one OpenCart action triggers the mcrypt error, locate the caller before changing the server. A storefront may work normally until checkout reaches one payment extension, or the admin area may fail only when a particular integration page opens. That pattern already suggests a narrow code path.
Start with the fatal error:
PHP Fatal error: Uncaught Error:
Call to undefined function mcrypt_decrypt()
in /path/to/opencart/extension/example/library/client.php:84
The path and line number turn an abstract “mcrypt problem” into a specific code investigation.
Search the OpenCart codebase for mcrypt calls
Search PHP files under the active OpenCart tree:
grep -RIn --include='*.php' -E 'mcrypt_|MCRYPT_' /path/to/opencart
A result may look like:
/path/to/opencart/extension/example/library/client.php:84:
$data = mcrypt_decrypt(...);
The path identifies the component worth investigating, while the line number can be matched with the PHP fatal. Search both mcrypt_ functions and MCRYPT_ constants because legacy libraries can reference them in different parts of the implementation.
Do not assume every grep match is active. Backups, disabled modules, old vendor libraries, and files left after upgrades can contain mcrypt code without ever being executed. If backups live inside the application tree, exclude them from the search or move them outside the document root before drawing conclusions.
Use the PHP stack trace to confirm the caller
A grep result finds candidates; a stack trace shows what executed. Read the trace from the failure backward until you reach the controller, model, extension, or modification that initiated the call.
Also check the PHP and OpenCart logs around the same request. If the store works until one payment method is selected, reproduce that action on staging and trace that component. Disabling unrelated modules or downgrading PHP for the whole server adds risk without improving the evidence.
OCMOD, VQMod, and custom modifications require extra attention on older stores because the file you inspect may not be the only source influencing the executed code. Check generated modification output and refresh mechanisms appropriate to that OpenCart generation.
Once you can name the extension or library that owns the mcrypt call, you can choose between updating it, replacing it, applying a tested migration, or temporarily isolating its compatible runtime. Find the caller before touching the stack.
What if enabling ZIP does not fix the OpenCart error?
If extension_loaded('zip') and class_exists('ZipArchive') both return true through the OpenCart hostname, PHP ZIP has passed its test. An extension installation can still fail before the archive reaches OpenCart, while the archive is being opened, while its package structure is being processed, or while files are being written.
Use the failure stage to decide where to look next:
| Stage | What to test | What a failure usually means |
|---|---|---|
| HTTP/PHP upload | Did the uploaded file reach PHP? | Upload limits, request size, temporary directory, or upload error |
| ZIP archive | Can ZipArchive open the file? |
Corrupted, incomplete, or non-ZIP package |
| OpenCart package | Does the archive contain the structure expected by that OpenCart generation? | Wrong package, incompatible extension, missing metadata, or bad layout |
| Filesystem | Can the installer create and write the required files/directories? | Permissions, ownership, storage path, full disk, or inode exhaustion |
| Application | What does the OpenCart/PHP log report after the previous checks pass? | Installer-specific or extension-specific failure |
Did PHP receive the extension package successfully?
A failed upload and a failed ZIP extraction are not the same problem. If PHP never receives the package, OpenCart cannot inspect it with ZipArchive.
Check the effective web values for:
upload_max_filesize
post_max_size
upload_tmp_dir
Read them from the same web PHP environment that serves OpenCart. CLI values can differ.
Package size must fit the request limits. A request that exceeds the effective POST limit can fail before the installer gets a normal uploaded file to process. If small uploads work but larger extension archives disappear or fail immediately, check these limits before examining the archive structure.
The temporary upload directory also has to be usable by the web PHP process. If uploads in other parts of the application fail as well, inspect the configured temporary directory, filesystem permissions, and available space.
On a Linux VPS, two quick checks are:
df -h
df -i
df -h catches a full filesystem by capacity. df -i catches inode exhaustion, where a filesystem can report free megabytes but still be unable to create another file.
Can ZipArchive open the package?
Once the upload reaches PHP, separate archive validity from OpenCart validity. A ZIP file can be syntactically readable yet still be the wrong package for OpenCart.
A minimal diagnostic test outside the installer can confirm whether PHP itself can open a known uploaded file:
<?php
$zip = new ZipArchive();
$result = $zip->open('/path/to/test-package.zip');
var_dump($result);
if ($result === true) {
echo 'entries=' . $zip->numFiles . PHP_EOL;
$zip->close();
}
Use this only with a test copy and a controlled path. If open() fails even though ZipArchive exists, inspect the archive itself. Re-download the package, compare it with the original source if possible, and make sure an HTML error page or incomplete download was not saved with a .zip extension.
If PHP opens the archive, the ZIP extension is doing its job. The next question is whether the package layout and metadata match what the relevant OpenCart generation expects.
Does the extension package match the OpenCart version?
An archive that opens successfully can still be rejected because it was built for another OpenCart generation, is packaged one directory too deep, lacks expected installation metadata, or contains files in a structure the installer does not recognize.
Inspect the installer error before repacking anything manually. The exact OpenCart package format differs between generations and extension types, so avoid “fixing” the archive structure based on a guide for another release.
This is also where version context becomes useful. If the extension vendor lists compatibility for one OpenCart branch and the store runs another, enabling more PHP modules will not make the package compatible.
Can OpenCart write the extracted extension files?
If the archive opens and OpenCart begins processing it but installation fails while creating files or directories, move to the filesystem layer.
Check:
- the OpenCart storage and extension-related paths used by that installation;
- file and directory ownership;
- whether the PHP-FPM or web-server user can write where the installer expects to write;
- free disk space and inodes;
- whether a conflicting file or directory already exists;
- the PHP and OpenCart logs for the exact failed request.
On a self-managed Linux server, inspect the path rather than applying broad permissions recursively. Commands such as:
ls -ld /path/to/opencart
ls -ld /path/to/opencart/storage
namei -l /path/to/opencart/storage
can reveal which directory in the path has unexpected ownership or permissions. Do not respond by setting the whole application tree to world-writable permissions. Fix the specific ownership or access problem that the installer actually hits.
The diagnostic boundary is now clear: if ZipArchive is absent, stay with PHP. If ZipArchive exists but cannot open the package, inspect the archive. If the archive opens but OpenCart rejects it, inspect package compatibility and structure. If processing reaches file creation and fails there, inspect the filesystem and logs.
How do you verify that the PHP extension problem is fixed?
A successful repair must pass the runtime test and the original OpenCart workflow. For ZIP, verify through the affected hostname:
<?php
var_dump(extension_loaded('zip'));
var_dump(class_exists('ZipArchive'));
Expected result:
bool(true)
bool(true)
Then repeat the action that failed. If the error occurred during an extension upload, repeat that upload with a compatible package. If it occurred during installation, rerun the installation on staging or through the normal administrative workflow. Check the PHP and OpenCart logs after the test.
For mcrypt, the desired result is different. You are normally trying to stop the application from executing obsolete mcrypt-dependent code, not trying to force extension_loaded('mcrypt') to become true. Reproduce the exact feature that failed and confirm that the old fatal no longer appears.
If cryptographic code was migrated, test both new and historical encrypted values on staging. A replacement that handles new data but cannot read existing ciphertext has not passed acceptance testing.
10-minute OpenCart PHP extension checklist
- Copy the exact OpenCart or PHP error before changing the server.
- Record the OpenCart version, web PHP version, and affected extension.
- Decide whether the failure concerns ZIP or a legacy mcrypt call.
- Run
php -vandphp --iniover SSH. - Use CLI extension checks only as a baseline.
- Check
PHP_VERSION,PHP_SAPI, loaded INI files, andextension_dirthrough the affected hostname. - For ZIP, verify
extension_loaded('zip')andclass_exists('ZipArchive'). - If CLI and web disagree, identify the domain handler, FPM service/pool, and INI tree.
- If mcrypt is requested, find the executing caller before changing PHP.
- If
ZipArchivealready works, move on to upload, package, and filesystem diagnostics. - Repeat the exact OpenCart action that originally failed and read the logs.
- Remove temporary diagnostic files.
Test the failing path, not just the module list.
Which fix should you keep as the long-term solution?
The long-term fix depends on what the diagnostic path actually proved. Keep ZIP enabled in the PHP runtime that serves OpenCart. Treat mcrypt as an application compatibility dependency that should be removed, migrated, or deliberately isolated rather than restored everywhere by default.
| Situation | Best next action | Possible temporary measure | Main risk to avoid |
|---|---|---|---|
| ZIP is genuinely missing from web PHP | Enable or install ZIP for the exact PHP build serving OpenCart | Usually none | Installing ZIP for a different PHP version |
| ZIP exists in CLI only | Correct the domain handler, PHP-FPM configuration, pool, or extension set | Usually none | Repeatedly reinstalling CLI packages |
| ZIP is configured but PHP reports a startup error | Check the PHP build, extension binary, INI file, and extension_dir |
Restore the last known working runtime configuration | Adding duplicate extension directives |
ZipArchive works but package upload fails |
Check upload limits and the temporary upload path | Test with a smaller known-good package | Troubleshooting archive structure before PHP receives the upload |
ZipArchive opens the package but OpenCart rejects it |
Check extension compatibility, metadata, and package layout | Test the vendor package on staging | Reinstalling php-zip after PHP already passes |
| Installer reaches file creation and fails | Check storage paths, ownership, permissions, disk space, and inodes | Reproduce on staging with the same filesystem layout | Applying broad world-writable permissions |
| An old module requires mcrypt | Update or replace the module | Use a controlled compatibility environment during migration | Holding the whole server on an old stack for one extension |
| Custom code calls mcrypt | Design and test a proper cryptographic migration | Keep the legacy environment isolated until compatibility is proven | Blind function replacement that breaks existing ciphertext |
| Several parts of an old OpenCart store require legacy PHP behavior | Plan the OpenCart and PHP migration together | Maintain an isolated temporary runtime with backups and a defined exit plan | Accumulating more server-level workarounds instead of removing legacy dependencies |
If one obsolete module is the only thing blocking a PHP upgrade, solve that module rather than designing the whole server around it. If the store contains several legacy dependencies, treat the work as an application migration: inventory the modules, verify stored data, use staging, keep a rollback path, and retire compatibility workarounds as each dependency is removed.
For ZIP, the stopping point is equally concrete: once the OpenCart web runtime exposes ZipArchive, ZIP itself is no longer the layer to troubleshoot. Any remaining installation failure belongs to the upload, package, application, or filesystem path.


