phine/phar
phine/phar is a simple PHP library for building PHAR archives. Compile your project into a single distributable file with a fluent API, include/exclude rules, and support for Laravel-style workflows. Ideal for packaging CLI tools and standalone PHP apps.
phine/phar is a thin object-oriented layer over PHP's built-in Phar/PharData classes. It exists to make two jobs pleasant: building a .phar from a directory tree in a build script, and unpacking an existing archive. It is archived (last release 2013-12-13, zero dependents), so treat this as a library you inherit or vendor, not one you reach for on a greenfield project.
First steps:
composer.json (phine/phar resolves from Packagist) and run it on a PHP version you actually test against, or copy the src/ into your own namespace.phar extension (php -m | grep phar) — it's on by default but commonly disabled in hardened builds.phar.readonly for any code path that writes an archive. This is the single most common reason a first attempt fails: php -d phar.readonly=0 bin/build-phar.php. Reading/extracting works with the default On.Phine\Phar\Builder (create/modify) and Phine\Phar\Stub (generate the bootstrap). Everything else (Phar, Manifest) is convenience.src/ + vendor/ + a bootstrap file into build/app.phar, then a smoke test that runs php build/app.phar --version.A minimal build looks like:
use Phine\Phar\Builder;
$builder = new Builder('build/app.phar');
$builder->addDirectory('src', 'src'); // local path, in-archive path
$builder->addDirectory('vendor', 'vendor');
$builder->setStub('build/stub.php'); // must end with __HALT_COMPILER();
$builder->build();
Build-script pattern (the main reason to use this). Wrap archive construction in a Composer script or Makefile target so developers never hand-build. The Builder accumulates files/directories, then flushes to disk once. Keep the build in a dedicated bin/build-phar.php and call it from composer run build:phar. Because phar.readonly cannot reliably be flipped with ini_set, the wrapper needs to be invoked with php -d phar.readonly=0.
Generate the stub instead of hand-writing it. Phine\Phar\Stub builds the __HALT_COMPILER(); preamble for you: bootstrap inclusion, mapPhar() for aliasing, and required-file lists. Generating it means the alias in the stub always matches the archive filename you passed to Builder. Commit the generator, not the generated stub.
Incremental/update flows. Re-opening an existing archive and adding files is supported, but prefer a clean rebuild (unlink then build) in CI — incremental updates leave orphaned entries when files are deleted from source, and phar entries don't have a "delete all" that is cheaper than rebuilding.
Extraction for deploy/inspect. The Phar wrapper exposes extractTo()-style helpers so a deploy step can unpack a shipped artifact into a release directory without shelling out to tar. Useful when your release artifact is a phar but your runtime expects loose files.
Integration with a release pipeline. Typical CI stage order: composer install --no-dev → run builder → php build/app.phar --version smoke test → sign or checksum → upload. Signing is done through the underlying \Phar::setSignature(); the package doesn't abstract it, so call it on the returned Phar object after build().
Reading manifests without executing. If you need to inspect what's inside an archive (list entries, read the stub) on a machine where you don't want to boot the app, use the manifest/read path — it opens the file without running the stub. This is the safe way to verify a build in a test.
Keep the builder isolated behind an interface. Because the library is frozen, wrap it in App\Build\PharBuilderInterface in your codebase. When you eventually migrate to humbug/box or raw \Phar, only the one adapter class changes.
phar.readonly is the whole ballgame. ini_set('phar.readonly', '0') does not work at runtime in practice — it must be set in php.ini or on the command line before the interpreter starts. Every "cannot write to phar" bug traces back here. Put the -d phar.readonly=0 in your build script shebang or Makefile so it can't be forgotten.__HALT_COMPILER();. Let Phine\Phar\Stub emit it rather than editing by hand.ext-openssl enabled on the machine that runs the phar, not just the one that builds it. Default SHA-1 signatures are fine but weak; set the signature algorithm explicitly.clearstatcache() plus restarting the process helps; in php-fpm, restart the pool.addDirectory($local, $remote) maps a local tree into an in-archive prefix. A relative remote prefix that doesn't match your stub's mapPhar() alias produces confusing "file not found" errors at runtime../app.phar if the file mode is 0755; set it explicitly after build().phar commands. Everything this package does is just \Phar under the hood, so when the wrapper gives you trouble, drop to new \Phar($path) to inspect state — that's also your migration path.Phar object; that's where you attach signatures, set metadata, and configure compression. Treat that return value as the escape hatch for anything the wrapper doesn't expose.How can I help you explore Laravel packages today?