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

Archive Process Bundle Laravel Package

cleverage/archive-process-bundle

CleverAge ArchiveProcessBundle integrates Archive handling into the ProcessBundle project, adding archive processing support to Symfony applications. Built for stable Symfony and LTS releases, it helps manage archived data within process workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation Require the package via Composer:

    composer require cleverage/archive-process-bundle
    

    Register the bundle in config/bundles.php:

    return [
        // ...
        Cleverage\ArchiveProcessBundle\CleverageArchiveProcessBundle::class => ['all' => true],
    ];
    
  2. Where to Look First The day-to-day surface area is the two process tasks:

    • Cleverage\ArchiveProcessBundle\Task\ZipTask — packs a set of files/directories into an archive.
    • Cleverage\ArchiveProcessBundle\Task\UnzipTask — extracts an archive to a destination directory.

    Read those two classes before anything else. Most configuration questions ("why is this file not in the archive?", "why did extraction land in the wrong place?", "why was my upload rejected?") resolve to how a task's inputs and options are evaluated.

  3. First Use Case Add a ZipTask step to a process so that a finished process's output directory is archived:

    # config/packages/cleverage_process.yaml
    cleverage_process:
        processes:
            my_process:
                tasks:
                    archive_output:
                        class: Cleverage\ArchiveProcessBundle\Task\ZipTask
                        inputs:
                            - files: ['%kernel.project_dir%/var/output']
                              files_base_path: '%kernel.project_dir%/var/output'
                              archive_path: '%kernel.project_dir%/var/archives/my_process.zip'
    

    Then run the process. If the archive cannot be written you will now get an exception rather than a silently returned path (see Gotchas).

    Prefer task codes (the class: key above) over the deprecated service: key in your process definitions — the cookbook and reference docs were updated in v2.4 to reflect this.


Implementation Patterns

Core Workflows

  1. Archiving as a Process Step Prefer wiring ZipTask/UnzipTask as regular steps in the process definition rather than calling services from controllers. The tasks are designed to sit in the middle of a pipeline:

    generate_report -> zip_output -> upload_archive
    

    The zip step's output (the archive path) is then consumed by the next task.

  2. Multiple Inputs, Per-Input Options Both tasks accept an array of inputs, each input carrying its own options. Since v2.2 the options are resolved on every execution, so each input is handled with its own settings:

    inputs:
        - files: ['/var/output/reports']
          files_base_path: '/var/output'
          archive_path: '/var/archives/reports.zip'
        - files: ['/var/output/exports']
          files_base_path: '/var/output/exports'
          archive_path: '/var/archives/exports.zip'
    

    Before v2.2 the first input's options silently leaked into subsequent inputs — audit any multi-input zip/unzip step you already have, since archive contents may legitimately change after the upgrade.

  3. files_base_path Semantics files_base_path is stripped from each file path to compute the path inside the archive:

    • Only the leading occurrence is removed (v2.2 fix — previously every occurrence was stripped, producing mangled internal paths when the base path also appeared deeper in the tree).
    • If files_base_path is empty, files are read at the given path as-is; relative paths now resolve from the current working directory rather than from the filesystem root (v2.2 fix).

    Practical rule: always set files_base_path explicitly to the directory whose contents you want at the archive root, and use absolute paths when you can.

  4. Round-Tripping Archives Pair ZipTask with UnzipTask when processes need to hand off data between environments or replay an archived run:

    tasks:
        unarchive_inputs:
            class: Cleverage\ArchiveProcessBundle\Task\UnzipTask
            inputs:
                - archive_path: '/var/archives/my_process.zip'
                  extract_path: '/var/working/restored'
    
  5. Untrusted Archives: Bound the Extraction (v2.4) UnzipTask gained max_files and max_size options (default null, i.e. no limit). Both are checked before anything is extracted, and the task throws a RuntimeException when a limit is exceeded:

    tasks:
        unarchive_upload:
            class: Cleverage\ArchiveProcessBundle\Task\UnzipTask
            inputs:
                - archive_path: '%kernel.project_dir%/var/uploads/incoming.zip'
                  extract_path: '%kernel.project_dir%/var/working/upload'
                  max_files: 5000
                  max_size: 104857600   # 100 MiB of *uncompressed* data
    
    • max_files counts entries in the archive (files and directories) — budget for the directory entries too, not just the payload.
    • max_size is compared against the total uncompressed size, i.e. what a zip bomb expands to, not the size of the .zip file.
    • Because the check runs up front, an over-limit archive leaves extract_path untouched — no partial extraction to clean up.
    • Rule of thumb: any archive that crossed a trust boundary (user upload, partner SFTP drop, fetched from a remote URL) should have both limits set. Internal archives you produced yourself with ZipTask can leave them at null.

Integration Tips

  • Fail fast, catch narrowly Wrap process execution (or the specific task) in a try/catch for \RuntimeException (I/O failure, limit exceeded) and \UnexpectedValueException (malformed input) so a failed archive doesn't take down an entire batch:
    try {
        $processManager->execute($process);
    } catch (\RuntimeException $e) {
        $this->logger->error('Archive step failed', ['exception' => $e]);
        $process->markAsFailed($e->getMessage());
    }
    
  • Derive limits from your own storage quota Rather than hardcoding magic numbers, compute max_size/max_files from the per-run temp-dir quota you already enforce elsewhere, so the archive task and your disk limits tell the same story.
  • Overwrite policy UnzipTask can overwrite existing files. If extraction targets a directory that may already contain data, clear it first or extract into a run-specific temp directory — otherwise a partially-overwritten directory is hard to reason about after a failure. The v2.4 limits reduce (but don't remove) the need for that hygiene: they stop abusive archives, not legitimate-but-wrong ones.
  • Keep archives out of var/cache Point archive_path at a dedicated directory (e.g. var/archives), not a cache or log directory that other tooling clears.
  • Test with the task directly For unit tests, instantiate the task and feed it inputs. v2.2 added coverage for option precedence, missing/unreadable files, overwriting and open failures; v2.4 adds tests for the max_files/max_size limits. Mirror those cases for your custom file layouts, and add a fixture archive that trips each limit.

Gotchas and Tips

Pitfalls

  1. Silent failures are gone (behaviour change in v2.2) Before v2.2, ZipTask would output the archive path even when the write failed, and UnzipTask did the same on extraction failure. Both now throw \RuntimeException. Any process that "worked" because it ignored a broken archive will now fail — that's correct, but it is a runtime change worth flagging in your upgrade notes.

  2. Non-array input throws Passing a bare string path instead of an input array used to produce an unrelated PHP warning or a TypeError. It now throws an explicit \UnexpectedValueException. If you build inputs dynamically, validate the shape (array of arrays) before handing it to the task.

  3. Options caching across inputs (fixed in v2.2) The v2.2 option-resolution fix means multi-input steps finally honour every input's options. Conversely, if you had compensating workarounds (duplicating inputs, splitting into several steps), remove them — they now produce redundant archives.

  4. files_base_path stripping (fixed in v2.2) Only the leading occurrence is removed now. Archives created before v2.2 may have paths like reports/reports/2024/q1.csv; anything downstream that parses those internal paths will need to cope with the corrected layout.

  5. Empty files_base_path and relative paths With an empty base path, relative file paths are now resolved from the current working directory. If your process runner's cwd isn't what you expect (queue workers, cron), pass absolute paths.

  6. max_files / max_size default to "unlimited" (v2.4) The new protections are opt-in: leave them null and you have exactly the pre-v2.4 behaviour, zip bomb and all. Nothing in your existing definitions becomes safer automatically — you have to set the limits. Also note they live on UnzipTask only; ZipTask doesn't need them, and a limit configured on a zip step is simply not applied.

  7. The limit exception is the same type as I/O failures An over-limit archive raises \RuntimeException, sharing the type with unreadable files and unwritable targets. A blanket catch (\RuntimeException) will treat "someone uploaded a bomb" and "disk is full" identically. Either inspect the message, or log enough context (archive path, resolved limits) in the handler that the two are distinguishable after the fact.

  8. max_size is uncompressed bytes, max_files counts entries Comparing max_size against the archive file size is the classic mistake — it does nothing against a bomb. And a limit tuned only against "number of files I expect" will trip on archives with many directories or macOS-created __MACOSX/.DS_Store entries.

  9. Filesystem permissions Ensure the archive destination and extraction target are writable by the process user — this is surfaced as a RuntimeException rather than a bad path in the output.

Debugging

  • Read the exception message On UnzipTask open failure the message carries the underlying ZipArchive error code; look that code up rather than guessing (ZipArchive::ER_NOENT, ER_NOZIP, ER_OPEN, etc.). Limit failures carry the limit and observed values — use them to decide whether the limit or the archive is wrong.
  • Inspect before you extract When a limit trips and you're unsure whether the archive is malicious or your limit is too tight, list the archive independently with unzip -l / ZipArchive::statIndex() and compare entry count and uncompressed totals to what you configured.
  • Validate the archive independently When a task "succeeds" but the consumer fails, verify the file with unzip -l / ZipArchive::open() outside the process to separate packaging problems from transport problems.
  • Log per-execution options Because options are resolved per input at every execution, log the resolved files_base_path, archive_path, max_files and max_size for each input — that instantly explains "wrong file set" and "why was this rejected" reports.

Extension Points

  1. Custom tasks If you need a different format (tar, gzip) or a remote destination, subclass/compose the existing tasks rather than reinventing option handling — reuse the same input-array contract so process definitions stay uniform. If you add a format, carry the max_files/max_size guard over with it; the protection lives in the task, not in PHP's archive layer.
  2. Pre/post archive hooks Keep archive-adjacent behaviour (notifications, uploads, cleanup) in separate process tasks around the zip step rather than inside the task; the tasks intentionally stay focused on file I/O.
  3. Upgrade checklist — v2.2 → v2.4
    • Re-run any multi-input ZipTask/UnzipTask step and diff the produced archives.
    • Confirm callers tolerate \RuntimeException / \UnexpectedValueException.
    • Remove workarounds for the leaked-options and repeated-files_base_path bugs.
    • Check processes that relied on a "successful" path after a failed write.
    • Add max_files / max_size to every UnzipTask fed by data you didn't generate yourself; verify your exception handling can tell a limit breach from an I/O error.
    • Replace deprecated service: keys in process definitions with task codes where applicable.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route