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

Laravel Zip Validator Laravel Package

orkhanahmadov/laravel-zip-validator

Laravel validation rules for ZIP archives. Verify uploads are valid ZIPs, restrict allowed or denied file extensions, cap file counts and uncompressed sizes, and inspect archive contents with simple, chainable rules—no manual extraction required.

View on GitHub
Deep Wiki
Context7

Getting Started

orkhanahmadov/laravel-zip-validator adds a single, focused validation rule that inspects the contents of an uploaded ZIP archive — something the built-in mimes:zip / extensions:zip rules cannot do (they only look at the file itself).

composer require orkhanahmadov/laravel-zip-validator

Requires the zip PHP extension. As of 1.11.0 the package requires Laravel 9+ / PHP 8.0+ — Laravel 8 support was dropped, so an existing L8 app will simply resolve to 1.10.x when constrained with ^1.0. If you pin explicitly, use ^1.10 on L8 and ^1.11 on L9+.

First use case — an upload form that must accept exactly two files, only images:

use Orkhanahmadov\ZipValidator\Rules\Zip;

$request->validate([
    'archive' => ['required', 'file', 'mimes:zip', new Zip(2, ['jpg', 'png'])],
]);

Where to look first: the package README for the rule constructor signature (file count, allowed extensions, required file names), and the tests/ directory — the 1.11.0 wildcard feature is best understood by reading the test cases that were added alongside it.

Implementation Patterns

FormRequest over inline validation. The rule is stateful per configuration, so defining it once in rules() keeps controllers clean:

public function rules(): array
{
    return [
        'archive' => [
            'bail', 'required', 'file', 'mimes:zip', 'max:20480',
            new Zip(
                fileCount: 3,
                extensions: ['csv'],
                fileNames: ['data-*.csv', 'README.txt'], // 1.11.0 wildcards
            ),
        ],
    ];
}

Lean on wildcards instead of looping rules. Before 1.11.0 you had to enumerate every expected entry (['image-1.png', 'image-2.png', 'image-3.png']). With shell-style * patterns you can assert a shape: ['*.csv'], ['data-*.json', '*.txt'], ['export-2024-*.csv']. This is the single biggest day-to-day improvement in this release — it turns the rule from "validates one very specific archive" into "validates a vendor's file convention".

Chain after the cheap checks. Always place file and mimes:zip/extensions:zip before the Zip rule and use bail. The rule needs a real path on disk to open; letting it run against a string or a non-zip upload produces a confusing failure instead of the obvious "must be a zip".

Tier the configuration. For multi-tenant/plan-based limits, build the rule from config rather than hardcoding:

new Zip(config('imports.max_files'), config('imports.allowed_extensions'))

Separate rule instances for separate messages. One Zip instance yields one failure message. If you need distinct feedback for "wrong count" vs "wrong file names", use two instances (new Zip(3), new Zip(null, [], ['*.csv'])) rather than one combined rule.

Testing. Fake uploads have no useful contents, so build real fixtures:

$path = tempnam(sys_get_temp_dir(), 'zip');
$zip = new \ZipArchive();
$zip->open($path, \ZipArchive::CREATE);
$zip->addFromString('data-2024-01.csv', 'a,b');
$zip->close();

$file = new UploadedFile($path, 'import.zip', 'application/zip', null, true);

Then assert with assertValid() / assertInvalid(['archive' => ...]). Wrap fixture creation in a helper — you will need it repeatedly.

Gotchas and Tips

  • Laravel 8 is gone in 1.11.0. This was shipped as a minor, so nothing breaks at the API level, but an ^1.11 constraint on an L8 project fails to resolve. Pin ^1.10 if you are mid-upgrade, and check your CI matrix still installs the expected tag.
  • __MACOSX/ and .DS_Store poison file counts. Archives created on macOS carry extra entries that count toward fileCount and can trip extension checks. If your users are on Macs, validate wildcards/extensions rather than an exact count, or document "re-zip without resource forks".
  • Directory entries are entries. A zip with csv/ plus csv/a.csv may count as two. Exact fileCount assertions are fragile against any producer that stores explicit folder records.
  • Nested paths. Wildcards match against entry names as stored, which include directory prefixes (folder/data-1.csv). Test a pattern against your real producer's layout before trusting *.csv to mean "any CSV anywhere".
  • Wildcards are for file names, not extensions. Do not put *.jpg into the extensions argument — that argument is a plain allow-list. Case sensitivity also bites: .JPG is not .jpg.
  • One message per rule. Debugging "why did this fail?" means inspecting the message text, since the rule does not tell you which condition tripped. Log the entry list (ZipArchive::statIndex) in a temporary test when users report false negatives.
  • ext-zip is a hard dependency. It is easy to omit locally and discover in CI or on a minimal container. Add it to composer.json require and to your deploy image.
  • Validation ≠ safe extraction. The rule lists entries via the central directory and does not decompress, so it is cheap and low-risk — but never treat a passed validation as clearance to extractTo() without path-traversal checks (../ entries), size limits, and a per-entry cap. The package validates; it does not make the archive trusted.
  • Extension points are thin by design. There is no config file to publish; customization means wrapping the rule in your own Rule class or composing instances. That is usually the right call for anything project-specific (e.g. rejecting directories explicitly).
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels