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

Tooly Composer Script Laravel Package

tm/tooly-composer-script

Tooly Composer Script integrates Tooly with Composer to simplify running common Laravel/PHP development tasks. Define reusable scripts in composer.json for testing, linting, static analysis, and other tooling, then run them consistently in local workflows and CI.

View on GitHub
Deep Wiki
Context7

Getting Started

Tooly is a Composer script (not a Composer plugin) that compiles your project into a phar based on JSON you add to composer.json. It's a lighter, Composer-native alternative to Box.

Minimum setup:

composer require --dev tommy-muehle/tooly-composer-script:^1.4.1

Make sure the PHP binary that runs Composer has phar.readonly disabled:

php -d phar.readonly=0 $(which composer) build
# or permanently: php.ini -> phar.readonly = 0

Add config and a script alias in composer.json:

{
  "require-dev": {
    "tommy-muehle/tooly-composer-script": "^1.4.1"
  },
  "extra": {
    "tooly": {
      "phar": {
        "output": "build/app.phar",
        "stub": "bin/app.php",
        "files": ["src", "vendor", "composer.json"],
        "compression": "GZ",
        "signature": "SHA256"
      }
    }
  },
  "scripts": {
    "build:phar": "Tooly\\Composer\\ScriptHandler::createPhar"
  }
}

Then:

composer build:phar
php build/app.phar        # smoke-test the artifact

Where to look first: the extra.tooly.phar block is the whole mental model — output, stub, files, compression, signature. Everything else is composition.

First real use case in a Laravel project: wrap an artisan/symfony-console entrypoint (bin/app.php) as a distributable CLI, e.g. an internal ops tool or a customer-support "log inspector" binary. Keep it out of public/; phars are executables, not web assets.

Implementation Patterns

Single-binary layout. Keep three things explicit:

  • output — relative to composer.json, always under a build/dist directory you clean.
  • stub — a thin #!/usr/bin/env php header + require 'phar://app.phar/vendor/autoload.php'; style bootstrap. Don't inline the whole app in the stub.
  • files — a whitelist, not "everything" (vendor, src, composer.json, plus any config/locale dirs your app needs at runtime).

Multiple phars in one project. The tooly block is keyed by phar name, so you can emit several artifacts from a single composer build:phar call:

"tooly": {
  "cli":  { "output": "build/cli.phar",  "stub": "bin/cli.php",  "files": ["src", "vendor"] },
  "cron": { "output": "build/cron.phar", "stub": "bin/cron.php", "files": ["src", "vendor"] }
}

Useful when a Laravel app ships a general CLI plus a smaller "worker" phar with a trimmed dependency set.

Finder-style inclusion for vendored trees. For bigger projects prefer glob/finder semantics over hand-listing every directory — e.g. files: ["src/**/*.php", "vendor/**/*.php", "resources/lang"]. Verify with phar -t or by browsing phar://build/app.phar/ that nothing critical (migrations, translations, config defaults) got silently dropped.

Release workflow. Put the script in a chain, not alone:

"scripts": {
  "build:phar": [
    "composer install --no-dev --optimize-autoloader",
    "Tooly\\Composer\\ScriptHandler::createPhar",
    "composer install"
  ]
}

That gives you a lean vendor tree without leaving the dev environment broken.

CI integration (GitHub Actions). A release job that runs composer build:phar and uploads build/*.phar as a release asset is the highest-value pattern. Two things matter in CI: phar.readonly=0 on the runner (php-actions/composer or an explicit php -d), and generating the phar before --dev dependencies are installed.

Signature as a distribution step. SHA256/SHA512 with a .pubkey allows consumers to phar.require_hash=1 and verify updates; for internal tools SHA1 is fine. This is the one feature where Tooly has no equivalent in a plain phar call and is worth the dependency. Pin ^1.4.1 or later — the checksum generation path had a bug fixed in 1.4.1 (issue #18), so a phar built with 1.4.0 may fail verification even though the build itself succeeded.

Verify the artifact, not the config. Bake a check into the build chain: php build/app.phar --version plus, for signed builds, php -r "echo (new Phar('build/app.phar'))->getSignature()['hash_type'];". A phar that builds but doesn't verify is worse than no phar, because the failure surfaces on a customer machine.

Gotchas and Tips

  • phar.readonly=1 is the #1 failure. The error ("creating archive ... disabled by ini setting") won't point at Composer; it will look like the script is broken. Fix in php.ini for the CLI SAPI, or prefix every invocation with php -d phar.readonly=0.
  • Checksum/signature bugs are silent until verification. 1.4.1 fixes checksum generation (#18). If you signed phars with 1.4.0 and have them in the wild, rebuild and redistribute them with ^1.4.1; a bad hash doesn't fail the build, only phar.require_hash=1 at consume time. Add a verification step to CI and never trust "the build succeeded" as proof of a valid signature.
  • This package is dormant. The 1.4.x line is a patch stream on a package last meaningfully updated in 2019. Expect friction on PHP 8.1+ (deprecations around strlen(null), preg_match null coercion in the script handler). If you're on PHP 8.3+, smoke-test on a throwaway branch before wiring it into a release.
  • Composer 2 is fine; Composer plugin API changes are not the issue — Tooly registers as a script, not a plugin, so allow-plugins isn't required. But composer install --no-dev then building means your dev-only Tooly package disappears. Use --no-dev only if Tooly is installed globally or the build script itself doesn't need re-resolving after.
  • files paths are resolved relative to composer.json, not the script's cwd. Globs use PHP's glob/Finder semantics — * doesn't cross directory boundaries the way some expect; prefer explicit directory names over ambitious globs until you can inspect the output.
  • Excluded files are the usual debugging lever. If the phar throws "class not found" but the same code runs from source, unpack with phar://build/app.phar/ or tar-style tools and check vendor/composer/autoload_*.php. Almost always a missing vendor subdir or a PSR-4 root that got excluded.
  • Stub must not depend on __DIR__. Inside a phar, __DIR__ points into phar://, so require __DIR__.'/../vendor/autoload.php' fails. Use Phar::running(false) to resolve the on-disk location when you need to read external files (e.g., a .env next to the phar).
  • Compression choice matters for startup. GZ gives the smallest artifact but decompresses the whole entry on require; BZ2 is smaller and slower; NONE is best when the phar is invoked in a hot loop (cron, queue worker). Benchmark once with hyperfine 'php build/app.phar list' and pick.
  • Signed phars can't be modified in place. Once signature is set, tests that unpack and patch the phar will invalidate it; rebuild instead. This bites hardest during incident debugging — keep an unsigned build/debug.phar target in extra.tooly for poking at internals.
  • Extension points are thin. There's no plugin hook; if you need "post-build: copy to ~/.local/bin", "write a .sha256 sidecar", or "verify the signature we just generated" (worth doing on 1.4.1), chain it as another Composer script in the same array. That's the intended extension point.
  • Don't ship tests/, .git, storage/, or node_modules. The excluded list (or the inverse files whitelist) is the only thing standing between you and a 200 MB phar. Check ls -lh build/app.phar before and after every config change.
  • Keep composer.lock inside the phar if you ever need to debug a customer's installed version — it's the cheapest forensic breadcrumb.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource