A regular use case in our projects is to provide the user with the ability to download multiple files as a compressed archive. With maennchen/zipstream-php, initiated by Jonatan, we found a simplistic and well functioning solution.

A fast and simple streaming zip file downloader for PHP. Using this library will save you from having to write the Zip to disk. You can directly send it to the user, which is much faster. It can work with S3 buckets or any PSR7 Stream.

The PSR-7 support allows for easy integration in a Mezzio application. The following class illustrates the usage in a PSR-compliant request handler. For the full context, have a look at mezzio-zipstream.

<?php

declare(strict_types=1);

namespace App\Handler;

use Laminas\Diactoros\CallbackStream;
use Laminas\Diactoros\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use ZipStream\Option\Archive;
use ZipStream\ZipStream;

use function dirname;
use function file_get_contents;
use function sprintf;

class Zip implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        $dataPath = dirname(__DIR__, 3);
        $items    = [
            'A.pdf' => file_get_contents($dataPath . '/data/A.pdf'),
            'B.pdf' => file_get_contents($dataPath . '/data/B.pdf'),
            'C.pdf' => file_get_contents($dataPath . '/data/C.pdf'),
        ];

        $zipName = 'test.zip';

        $stream = new CallbackStream(
            static function () use ($items, $zipName) {
                $options = new Archive();
                $options->setContentType('application/octet-stream');

                $zip = new ZipStream($zipName, $options);

                foreach ($items as $basename => $content) {
                    $zip->addFile($basename, $content);
                }

                $zip->finish();
            }
        );

        $headers = [
            'Cache-Control'       => 1200,
            'Content-Type'        => 'application/octet-stream',
            'Content-Disposition' => sprintf('attachment; filename="%s"', $zipName),
        ];

        return new Response($stream, 200, $headers);
    }
}