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

Zipstream Php Laravel Package

maennchen/zipstream-php

ZipStream-PHP streams ZIP archives directly to the browser or any PSR-7 stream without writing to disk, quickly creating downloads from files, paths, or S3 buckets. Simple named arguments and a single finish call.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. ZipStream-PHP is a single-purpose, framework-agnostic Composer library that emits ZIP bytes incrementally to an output stream instead of materializing an archive on disk. For a Laravel estate this maps cleanly onto a recurring class of features: bulk export endpoints, media/asset bundles, invoice and statement packs, data-room downloads, and CSV/JSON dumps zipped on the fly. The architectural value is not the ZIP format — ext-zip already does that — it's the removal of the temp-file tier: no scratch disk, no /tmp lifecycle, no cleanup jobs, no disk-full pages, flat memory under concurrency, and a much better time-to-first-byte. The public surface is deliberately tiny in v3 (ZipStream\ZipStream, CompressionMethod, and the Exception* hierarchy; everything else is marked @internal), which is exactly what a TPM wants to see in a dependency that will be called from controllers, jobs, and Artisan commands.

Integration feasibility. Low. One composer require, no service provider, no config file, no migrations, no published assets. It accepts PSR-7 streams, so it composes with Flysystem 3 and the S3 adapter that Laravel already ships, and it can write to php://output for a StreamedResponse or to a custom sink via CallbackStreamWrapper::open() (added in 3.2.0) for fan-out to S3 plus a local copy. The realistic effort is one to two engineer-days for a spike, not a sprint.

Technical risk. Concentrated in operations rather than in the code path:

  • PHP floor and platform. Requires PHP ≥ 8.2 (since 3.1.2) and 64-bit only. Fine for Laravel 10/11/12, but it should be asserted in CI rather than discovered in production.
  • Header ownership. sendHttpHeaders: true calls header() directly, which will collide with Laravel/Symfony response middleware, CORS, and ETag layers. The integration rule should be sendHttpHeaders: false with Laravel owning the response.
  • Zip64 and >4 GB. 3.1.0 stopped enabling Zip64 unless required (a real compatibility win), and 3.2.2 fixed corrupted directory local-header size fields past the 4 GB offset. That fix is recent enough that large-archive behavior deserves explicit regression coverage rather than inherited trust.
  • Data descriptors by default. defaultEnableZeroHeader flipped to true in 3.0, meaning sizes are written in trailing descriptors. Modern clients are fine; legacy enterprise unzip tooling is the population to test before promising compatibility.
  • Maintainer concentration. Effectively one primary maintainer plus contributors. Mitigated by MIT license, OpenSSF Best Practices and Scorecard badges, coveralls coverage, and a published version-support table, but it is a bus-factor item, not a non-issue.
  • Metric hygiene. Dependents: 0 against 1,907 stars is almost certainly a dataset artifact — verify via Packagist download counts and GitHub network before it enters any business case. The composite score of 32.6 similarly reads as a narrow-scope penalty, not a quality signal.

Key questions.

  1. What is the largest archive in our 12-month roadmap — total bytes, file count, concurrent users — and does any of it cross the 4 GB or 65,535-entry Zip64 thresholds?
  2. Which clients must open these files (Excel, macOS Archive Utility, 7-Zip, Windows Explorer, a partner's SFTP ingest), and does the data-descriptor default pass all of them?
  3. Do we need encryption, resumable downloads, or per-file progress telemetry? None are in scope for this library, so those requirements imply adjacent components.
  4. Are we running on Laravel Vapor or behind API Gateway, where response payload caps make direct streaming of large archives structurally impossible?
  5. What is our tolerated failure mode when a client disconnects mid-stream — a wasted worker, a partial file, a support ticket?
  6. Do we need a support SLA, or is GitHub Discussions plus the maintainer's sponsorship-based 1:1 option sufficient for the tiers this touches?

Integration Approach

Stack fit. Laravel 10+ on PHP 8.2/8.3, 64-bit, 64-bit CI runner. No conflict with ext-zip; the two can coexist, which matters for a phased migration. It slots naturally behind a StreamedResponse or response()->streamDownload() for synchronous downloads, and behind a queued job writing to S3 via Flysystem for anything heavy. It is not an HTTP layer, a job runner, or a storage abstraction — pairing it with Bus/Horizon and Storage::disk('s3') is the intended composition, not a gap.

Migration path. Replace ZipArchive-to-temp-file call sites in four phases:

  1. Pilot one low-risk, read-only export endpoint behind a feature flag; keep the existing implementation as fallback.
  2. Extract a domain-level ZipExport abstraction (add file from path / from stream / from string, set compression per file) so call sites never touch ZipStream directly — this is the seam that makes the vendor's @internal policy irrelevant to us.
  3. Migrate remaining synchronous endpoints, deleting temp-directory creation, unlink calls, and any scheduled cleanup commands as each one moves.
  4. Move heavy exports to queued jobs that stream to S3 through CallbackStreamWrapper, then hand the user a signed URL. Retain synchronous streaming only for small, interactive bundles.

Compatibility. Pin with a caret constraint on 3.2 (^3.2) and treat majors as planned work — the 2.x → 3.x transition replaced option classes with named arguments across the board, so the abstraction layer above pays for itself at the next major. Confirm PHP_INT_SIZE === 8 and PHP_VERSION_ID >= 80200 in CI and in a health check. Explicitly avoid extending internal classes; if we need behavior the public API doesn't expose, that's an upstream issue, not a subclass.

Sequencing. Library spike and client-compatibility test matrix first (that's the long-lead item, not the code); then nginx/FPM tuning (proxy_buffering off, gzip off for the download route, raised max_execution_time per route); then the pilot; then the abstraction; then bulk migration; then decommissioning of temp-file infrastructure and its monitoring. Instrument the pilot for bytes streamed, duration, disconnect rate, and error rate before touching the third endpoint.

Operational Impact

Maintenance. Low, with a predictable shape. The library tracks PHP's own support window, ships frequent small patch releases (3.1.1 → 3.1.2 → 3.2.0 → 3.2.1 → 3.2.2), and its changelog is granular enough to assess exposure quickly. Expect routine dependency bumps in the annual maintenance window and one disruptive major every few years. Budget a half-day per minor, a sprint per major. The internal/public class boundary is a genuine asset here: if our integration only touches ZipStream, CompressionMethod, and exceptions, most releases are invisible to us. The @internal promotion of classes we might be tempted to use is a governance win, not friction — but it does mean any hack against internals becomes a permanent upgrade tax, so the abstraction layer is a maintenance control, not just a design preference.

Support. Community-supported only. GitHub Discussions is the front door, with documentation hosted separately and a sponsorship-based 1:1 channel for escalation. There is no vendor SLO, no security hotline beyond the GitHub advisory flow, and effectively one primary maintainer. For internal tooling and customer-facing exports where a broken ZIP is an annoyance, that is acceptable. If a download failure becomes revenue-affecting, the mitigations are: pin exact patch versions, keep the previous implementation behind a flag for one release cycle, and budget for a sponsored support relationship rather than assuming escalation exists.

Scaling. Two levers dominate. First, compression method per file: JPEGs, MP4s, PDFs, and already-compressed formats should use STORE rather than deflate — this is pure CPU savings with no size cost, and on an asset-bundling endpoint it can be the difference between a comfortable and an oversubscribed worker pool. Second, defaultDeflateLevel: tune down for latency-sensitive interactive downloads, up for background jobs. Memory stays flat regardless of archive size because nothing is buffered whole, so the constraint moves from RAM to worker-seconds and to wall-clock limits. Large exports belong in the queue, not the request cycle — that also gives retries, which the streaming model otherwise lacks.

Failure modes. Design for these explicitly, because most are unrecoverable once the first byte leaves:

  • Client disconnect mid-stream — worker keeps producing until it notices the write fails. The archive is truncated and unusable; there is no resume. Accept it for interactive downloads, avoid it for anything a user will retry three times. Set ignore_user_abort(false) and check connection_aborted() in long loops.
  • Timeout mid-archive — max_execution_time or an upstream proxy timeout produces a valid-looking .zip that won't extract. Per-route timeout configuration is mandatory, and it should be derived from the archive-size estimate, not a global default.
  • Source read failure mid-archive — a missing S3 object or unreadable path throws after headers are sent; the user gets a corrupt file with a 200 status. Pre-flight existence checks on all inputs before writing the first byte.
  • Proxy buffering — nginx buffering a streamed ZIP defeats the entire purpose and can spike proxy memory. Explicit proxy_buffering off on download routes, verified in staging.
  • headers already sent — the predictable result of leaving sendHttpHeaders enabled inside Laravel. Make false the enforced default in the abstraction.
  • Zip64 boundary defects — the 3.2.2 fix is a reminder that the >4 GB path is the least-exercised code in any ZIP writer. Contract-test an archive north of 4 GB in CI, or accept the risk knowingly.
  • Serverless platforms — Vapor/API Gateway response caps make direct streaming of large archives structurally impossible; the queue-plus-signed-URL pattern is the only viable shape there.

Ramp-up. Small. The public API is three entry points and a named-argument constructor; a mid-level Laravel developer is productive in a day and fully fluent in a week. The real onboarding cost is operational, not programmatic: teaching the team why the headers must be owned by Laravel, why the download route is exempt from gzip and buffering, why compression method is a per-file decision, and why these endpoints don't get temp files to inspect when something goes wrong. Put that in the runbook, not the README, and pair it with a dashboard on bytes streamed and incomplete-download rate — because the failure mode here is silent, and a truncated ZIP looks like a support ticket about "the file is broken," not an alert.

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.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log