phplaravelphp-ziparchive

Class "ZipArchive" not found in web application


I am facing a strange issue in the interaction between a view and ZipArchive in laravel. I made some test class to test the issue:

<?php

namespace App\Services;

use ZipArchive;

class Test
{
    public static function zipTest()
    {   
        $zip = new ZipArchive();
        echo "zipped";
    }   
}

When I call the function through the console via a dedicated command I made, php82 artisan command:test, there is no issue and \App\Services\Test::zipTest(); executes just fine, meaning the installation of zip went fine:

<?php

namespace App\Console\Commands;

class TestBase extends Command
{
    /** 
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:test';

    /** 
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Test some code';

    /** 
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {   
        \App\Services\Test::zipTest();
    }   
}
php82 artisan command:test
zipped

However when I call \App\Services\Test::zipTest(); in a blade, I get the error Class "ZipArchive" not found.

<div>
    @php
        \App\Services\Test::zipTest();
    @endphp
    hello!
</div>

The server running the application has several versions of php running, the one used for the application is 8.2.20 (php82). I had to request the admin to install the php zip functionality and they told me they installed the php82-php-pecl-zip package.

The version of laravel is v10.16.1.

Can you help me figure out what is going on?

Full error message if it helps:

local.ERROR: Class "ZipArchive" not found {"view":{"view":"/home/photodb/gabriel/resources/views/test.blade.php","data":[]},"userId":1,"exception":"[object] (Spatie\LaravelIgnition\Exceptions\ViewException(code: 0): Class \"ZipArchive\" not found at /home/photodb/gabriel/app/Services/Test.php:11)
[stacktrace]
#0 /home/photodb/gabriel/resources/views/test.blade.php(11): App\Services\Test::zipTest()
#1 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php(124): require('...')
#2 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php(125): Illuminate\Filesystem\Filesystem::Illuminate\Filesystem\{closure}()
#3 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/View/Engines/PhpEngine.php(58): Illuminate\Filesystem\Filesystem->getRequire()
#4 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/View/Engines/CompilerEngine.php(72): Illuminate\View\Engines\PhpEngine->evaluatePath()
#5 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/View/View.php(195): Illuminate\View\Engines\CompilerEngine->get()
#6 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/View/View.php(178): Illuminate\View\View->getContents()
#7 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/View/View.php(147): Illuminate\View\View->renderContents()
#8 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Http/Response.php(69): Illuminate\View\View->render()
#9 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Http/Response.php(35): Illuminate\Http\Response->setContent()
#10 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Routing/Router.php(911): Illuminate\Http\Response->__construct()
#11 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Routing/Router.php(878): Illuminate\Routing\Router::toResponse()
#12 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Routing/Router.php(798): Illuminate\Routing\Router->prepareResponse()
#13 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\Routing\Router->Illuminate\Routing\{closure}()
#14 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#15 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Routing\Middleware\SubstituteBindings->handle()
#16 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#17 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Foundation\Http\Middleware\VerifyCsrfToken->handle()
#18 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#19 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\View\Middleware\ShareErrorsFromSession->handle()
#20 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#21 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\Session\Middleware\StartSession->handleStatefulRequest()
#22 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Session\Middleware\StartSession->handle()
#23 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#24 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse->handle()
#25 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#26 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Cookie\Middleware\EncryptCookies->handle()
#27 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#28 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Routing/Router.php(798): Illuminate\Pipeline\Pipeline->then()
#29 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Routing/Router.php(777): Illuminate\Routing\Router->runRouteWithinStack()
#30 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Routing/Router.php(741): Illuminate\Routing\Router->runRoute()
#31 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Routing/Router.php(730): Illuminate\Routing\Router->dispatchToRoute()
#32 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\Routing\Router->dispatch()
#33 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\Foundation\Http\Kernel->Illuminate\Foundation\Http\{closure}()
#34 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#35 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle()
#36 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull->handle()
#37 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#38 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle()
#39 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Foundation\Http\Middleware\TrimStrings->handle()
#40 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#41 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Foundation\Http\Middleware\ValidatePostSize->handle()
#42 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#43 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance->handle()
#44 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#45 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Http\Middleware\HandleCors->handle()
#46 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(39): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#47 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Http\Middleware\TrustProxies->handle()
#48 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}()
#49 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\Pipeline\Pipeline->then()
#50 /home/photodb/gabriel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter()
#51 /home/photodb/gabriel/public/index.php(51): Illuminate\Foundation\Http\Kernel->handle()
#52 {main}

Solution

  • The PHP that runs embedded in the web server and the PHP that runs in the CLI use two different configurations, but you've got the zip extension enabled only in the CLI version. You'll need it enabled in the web version as well.

    Run php -m from the CLI and you'll see that it includes zip in the list of loaded modules.

    But run phpinfo(); in a script served by the web server (i.e., not from the CLI) and you'll see that the zip module is not loaded.

    You can see what INI file(s) it's loading via the sections titled Loaded Configuration File, Scan this dir for additional .ini files, and Additional .ini files parsed. These will list all the ini files read by the web server, make sure that one of them contains the line:

    extension=zip.so
    

    This change will require restarting the web server.