Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Zip Bundle Laravel Package

etshy/php-zip-bundle

A Symfony bundle that brings convenient ZIP archive handling to PHP applications. It provides services and configuration around native ZIP support so you can create, inspect, and extract compressed files with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Before anything else: confirm you actually need this package. This is a Symfony bundle (Bundle subclass + DI extension + compiled service definitions), not a Laravel package. Laravel does not load Symfony bundles, does not process their Extension/Configuration classes, and will not autowire the services the bundle registers. composer require etshy/php-zip-bundle will install (dragging symfony/dependency-injection and symfony/http-kernel transitively) but nothing will boot, and debug:container will show no zip service. So "getting started" in Laravel really means one of two paths:

  1. Port the idea, not the package (recommended). Read the bundle's source, extract the concrete behavior, and reimplement behind a first-party service in one engineer-day. Start by confirming the runtime:
php -m | grep -i zip           # "zip" must be present
# Debian/Ubuntu base images:
#   apt-get install -y libzip-dev && docker-php-ext-install zip

Then create your own contract and implementation:

// app/Contracts/ArchiveService.php
interface ArchiveService
{
    public function create(string $dest, array $files, array $opts = []): string;
    public function extract(string $archive, string $to): array;
    public function list(string $archive): array;
}

// app/Services/ZipArchiveService.php  — ~80 lines over ZipArchive

Bind it in AppServiceProvider; test with Storage::fake() and fixture archives.

  1. Use it only if a Symfony app is already wired and you're consuming it from Laravel. Even then, don't composer require in Laravel — expose the archive functionality via an HTTP endpoint or CLI on the Symfony side and call it. Cross-framework invocation through shared code is painful; RPC/CLI is honest.

If you insist on inspecting it: read in this order — composer.json (PHP/Symfony bounds, ext-zip requirement, license), src/DependencyInjection/Configuration.php (what config keys exist: temp dir, compression level, extension), src/DependencyInjection/*Extension.php and Resources/config/services.* (service IDs, public vs private), then the service class itself. That's the whole bundle. If it isn't on Packagist with tags, plan to vendor it from GitLab.

First use case once you've ported it: a console command that exports a directory.

#[AsCommand('app:export:zip')]
class ExportZipCommand extends Command
{
    public function __construct(private ArchiveService $archives) { parent::__construct(); }

    protected function execute(InputInterface $in, OutputInterface $out): int
    {
        $path = $this->archives->create(storage_path('app/exports/report.zip'), [
            'report.pdf' => storage_path('app/reports/report.pdf'),
        ]);
        $out->writeln("Wrote {$path}");
        return self::SUCCESS;
    }
}

Implementation Patterns

Async export via queues. Zip work is CPU and I/O bound and can exceed max_execution_time on large sets. The pattern that holds up: controller stores an "export requested" record, dispatches a job, the job builds the archive into a per-job unique temp directory, then moves the finished file onto Storage (local or S3):

class BuildExportJob implements ShouldQueue
{
    public int $timeout = 900;          // explicit — default 60s will kill big zips
    public int $tries = 1;

    public function handle(ArchiveService $archives): void
    {
        $tmp = storage_path('app/tmp/export-' . $this->export->uuid);
        Filesystem::ensureDirectoryExists($tmp);

        $zip = $archives->create("{$tmp}/bundle.zip", $this->collectFiles());
        Storage::disk('exports')->putFileAs('', new File($zip), "{$this->export->uuid}.zip");

        Filesystem::deleteDirectory($tmp);
    }
}

Unique working paths. Never write to a shared tmp/export.zip — concurrent workers will collide. Use storage_path('app/tmp/'.$uuid) or tempnam() and clean it in finally.

Stream downloads for large archives. Don't ->download() a 2GB file through PHP-FPM with a memory ceiling. For streaming zips use ZipStream-PHP (maennchen/zipstream-php is maintained and Laravel-friendly) so you never materialize the whole archive:

return (new ZipStream())->addFileFromPath('report.pdf', $path)->finish();

Port the config into config/. If the bundle had temp_dir / compression level, put them in config/filesystems.php or a dedicated config/archive.php and inject via config('archive.temp_dir'). That's how you get the Symfony-container configurability the original offered.

Keep it behind the contract. Controllers and jobs should depend on ArchiveService, never on ZipArchive or the bundle class. When you swap to a streaming writer for large archives, one binding changes.

Testing. Build fixture archives (small .zip files committed once), and test against hostile ones too — a zip containing ../../etc/passwd, absolute paths, symlinks. Assert your extract rejects them.

Gotchas and Tips

  • Framework mismatch is the headline. No bundle registration path exists in Laravel. Any "install and it works" tutorial you find is Symfony-specific. Do not treat this as a config-level gap; it's architectural.
  • ext-zip is not on by default in many PHP base images. If it isn't installed, ZipArchive is undefined and every call throws. Add libzip-dev + docker-php-ext-install zip to your image and document it in CI.
  • Composer constraint conflicts. Laravel pins symfony/console, symfony/process, symfony/http-foundation, symfony/finder. A bundle with loose or stale Symfony bounds can force a downgrade or fail resolution outright.
  • Zip Slip / path traversal. Extraction of user-supplied archives is a security concern. Sanitize entry names, reject absolute paths, block .., and skip symlink entries. This is exactly the reason to own the code rather than depend on an unmaintained wrapper.
  • Zip bombs and size limits. Cap file size, entry count (Zip64 issues above 65k entries / 4GB), and total uncompressed bytes before extracting.
  • Memory and temp disk. ZipArchive operates against a materialized file. Large exports hit memory_limit, max_execution_time, and fill /tmp. Use streaming or queue-friendly timeouts and cleanup.
  • Concurrency. Shared temp paths collide across queue workers; ephemeral containers lose partial archives. Per-job directories and a cleanup step are non-negotiable.
  • Encrypted / non-UTF-8 entries. Encrypted archives often fail silently; non-UTF-8 filenames produce mangled entries or exceptions. Decide up front whether to support them.
  • Debugging: $zip->open($path, ZipArchive::CREATE | ZipArchive::OVERWRITE) returning true/false/error code is far more useful than exception strings. Wrap it and map error codes.
  • Maintenance risk. Zero stars, zero dependents, GitLab-only, single maintainer, no CI or security policy. If you adopt it, vendor it, pin it, and treat it as abandoned. Revisit only if upstream publishes a framework-agnostic core with tags and tests.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core