Download the PHP package shyim/composer-binary-downloader without Composer
On this page you can find all versions of the php package shyim/composer-binary-downloader. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download shyim/composer-binary-downloader
More information about shyim/composer-binary-downloader
Files in shyim/composer-binary-downloader
Package composer-binary-downloader
Short Description Generic Composer plugin that downloads platform-specific binaries — FFI shared libraries, CLI tools, WASM modules — from GitHub releases or any URL, driven by composer.json extra configuration
License MIT
Informations about the package composer-binary-downloader
composer-binary-downloader
A generic Composer plugin that downloads platform-specific binaries. Any package
declares what it needs in composer.json; the plugin fetches the right build for
the host on composer install.
It replaces the pattern of writing a bespoke Composer plugin per native dependency — download from GitHub releases, verify the checksum, unpack, put the file where the loader expects it — with configuration.
Works for anything shipped as a per-platform release asset:
- FFI shared libraries (
.so/.dylib/.dll) — the original use case - CLI tools — a Rust or Go binary your package shells out to
- WASM modules, ICU data, model files, or any other platform-specific blob
The engine does not care which: it fetches an asset, verifies it, and puts one file at a configured path.
Install
Composer plugins must be allowed explicitly:
Configure
Add extra.binaries to the package that needs the binary. Every installed
package is scanned, as is the root project, so a wrapper package declares its
native dependency once. Consumers approve the package a single time — via the
interactive prompt or an extra.allow-binaries entry (see
Security) — and need no other setup.
The library lands at
vendor/shyim/sasso-ffi/bin/aarch64-apple-darwin/libsasso.dylib.
Options
| Key | Required | Default | Meaning |
|---|---|---|---|
version |
yes | — | Release version. A leading v is stripped. "self" uses the declaring package's own version — see below. |
repository |
yes* | — | owner/repo for GitHub releases. |
tag |
no | v{version} |
Release tag template. Use {version} for projects that tag without the v. |
url |
yes* | — | Full URL template; use instead of repository for S3, GitLab, self-hosted. |
asset |
no | {name}-v{version}-{target}.{ext} |
Published asset file name. |
archive |
no | tar.gz |
tar.gz, tar.xz, tar.bz2, tar, zip, or none. |
library |
yes** | — | Shared-library file name inside the archive. |
path |
no | lib/{target}/{library} |
Install path, relative to the declaring package. |
targets |
yes | — | Map of target triple to per-target settings (or a library-name string). |
checksums |
no | — | Map of target triple to expected sha256. |
env-prefix |
no | upper-cased library name | Prefix for the environment variables below. |
optional |
no | true |
When false, a download failure aborts composer install. |
bin |
no | — | Expose the tool as a vendor/bin command: true (named after the binary) or a command name. |
download |
no | eager |
lazy defers the download to first use instead of fetching at composer install. |
* One of repository or url is required.
** Not required when archive is none; the asset's own name is used.
Per-target settings (asset, library, archive, checksum, url) override
the library-wide value — real pipelines ship a .zip of foo.dll on Windows
and a .tar.gz of libfoo.so elsewhere.
Placeholders
Usable in asset, url, library, and path:
| Placeholder | Expands to |
|---|---|
{name} |
The library key (sasso) |
{version} |
The configured version |
{target} |
Target triple (aarch64-apple-darwin) |
{arch} |
Architecture (aarch64) |
{os} |
Rest of the triple (apple-darwin) |
{ext} |
Archive extension (tar.xz); the library extension when archive is none |
{libext} |
Shared-library extension for the target (dylib, so, dll) |
{binext} |
Executable extension for the target (.exe on Windows, empty elsewhere) |
{goos} / {goarch} |
Go-style names for the target (linux/darwin/windows, amd64/arm64) — matches goreleaser asset naming like tool_1.0_linux_amd64 |
{library} |
Resolved library file name (path only) |
{libext} and {binext} follow the target rather than the host, so cross-target
prefetching names files correctly. {binext} includes the dot, so
"library": "mytool{binext}" yields mytool.exe on Windows and mytool
elsewhere without a per-target override.
CLI tools on vendor/bin
A binary can be a command, not just a library. "bin" exposes it in Composer's
bin-dir, and "download": "lazy" defers the fetch to first use — together they
reproduce the pattern tools like frosh/shopmon-cli hand-write in a bin stub,
as pure configuration:
Any project requiring this package gets vendor/bin/shopmon-cli. Running it
downloads the release on first use (with a notice on stderr), then executes it
with stdin/stdout/stderr and the exit code passed straight through — via a true
exec where pcntl is available, a waiting child process otherwise.
The pieces:
"version": "self"— the binary version is the Composer package's own version. Tagging the wrapper repo releases both;extranever needs a version bump. A branch install (dev-main) has no tagged release, so it fails at parse time with a message saying exactly that."bin": true— generate a proxy named after the binary (or pass a string for a different command name, e.g."bin": "rg"for ripgrep). Generated proxies carry a marker; the plugin never overwrites or deletes a bin-dir file it did not generate, and removes its own when the declaration goes away."download": "lazy"— nothing fetched duringcomposer install; the proxy (orBinaries::install()) fetches on demand.composer binary:installand--targetprefetch it anyway — an image build asking explicitly should not be refused.{version}inpath— an upgrade fetches the new release instead of finding the old file at the same path and skipping it.{goos}/{goarch}— goreleaser asset naming from one template.
Downloaded files are chmod 0755, so the tool is runnable as fetched. PHP code
can call the same binary via Binaries::path('shopmon-cli').
For a package that must work even with plugins disabled, skip "bin" and ship
a two-line stub as your own bin entry — Composer core links those without any
plugin:
See examples/shopmon-cli.json and examples/ripgrep.json for complete
files.
Supported target triples
Detection follows the PHP binary, not the CPU — an x86_64 PHP under Rosetta gets
an x86_64 library. musl is detected via the dynamic linker, so Alpine resolves to
-musl.
Any triple works as a key — these are just what host detection produces.
Commands
binary:list is the diagnostic for a failing FFI::cdef(): it prints the exact
path that was expected and whether it exists.
--target prefetches a platform other than the build host, which is what image
builds need (an arm64 runner baking an x86_64 image). Libraries with no build for
a requested target are reported and skipped rather than failing the run.
Without the plugin
Composer never loads the root package as a plugin, and --no-plugins disables
it. The bundled script covers both, reading vendor/composer/installed.json
directly:
Wire it into a checkout of the declaring package with a script:
Runtime API
Binaries is the API for consuming packages — what you call to get the path of a
downloaded binary, whether that means FFI::cdef() on a shared library or
exec() on a CLI tool. It reads a data file the plugin generates at
autoload-dump time (vendor/composer/installed-binaries.php), in the same spirit
as Composer\InstalledVersions: no composer.json parsing per lookup.
| Method | Behaviour |
|---|---|
path(string $name): string |
Resolved path. Never downloads. Throws if unusable. |
install(string $name): string |
Same, but downloads when missing. |
isInstalled(string $name): bool |
Never throws. For graceful degradation. |
status(string $name): LibraryStatus |
Full diagnostic snapshot; never throws for a configured library. |
names(): list<string> |
Every configured library name. |
path() deliberately does no network I/O — a web request should not stall on a
release host. Use install() where fetching is acceptable: a CLI tool, a warmup
command, a test bootstrap.
Resolution order is <PREFIX>_LIBRARY, then the installed path, then (for
install() only) a download.
Exceptions
All of them implement LibraryUnavailableExceptionInterface, so code that only
cares "usable or not" needs one catch:
| Exception | Meaning |
|---|---|
UnknownLibraryException |
No package declares that name. Lists what is configured. |
PlatformNotSupportedException |
No build for this platform. Lists the configured targets. |
LibraryNotInstalledException |
Configured, but not on disk. |
LibraryNotInstalledException::reason() returns a Reason enum so callers can
branch without matching message text:
Reason |
Cause |
|---|---|
Missing |
Not downloaded yet — usually composer install --no-plugins. |
Declined |
<PREFIX>_SKIP_DOWNLOAD or BINARY_DOWNLOADER_SKIP forbade it. |
DownloadFailed |
A download was attempted and failed; getPrevious() has the cause. |
ExplicitPathMissing |
<PREFIX>_LIBRARY names a file that is not there. |
Each message names the specific cause and the command that fixes it, e.g.
Diagnostics
status() never throws for a configured library, so a health check can report a
broken install rather than handle exceptions:
The generated data file
The plugin writes vendor/composer/installed-binaries.php on every autoload dump.
Paths are emitted relative to __DIR__, not baked in absolutely, so a vendor/
directory built in CI and copied into an image (COPY vendor/) still resolves —
the same approach Composer uses for installed.php:
Output is sorted and stable, so an unchanged installation produces no diff. Do not commit or edit it; re-dump the autoloader instead.
If the file is absent — the autoloader was dumped with --no-plugins, or you are
in a checkout of the declaring package — Binaries falls back to reading
composer.json directly. Same results, just slower.
Lower-level access
LibraryLocator exposes the parsed configuration (definition(),
definitions()) for tooling that needs URLs, checksums, or every target rather
than one resolved path.
Environment variables
Per binary, prefixed by env-prefix (default: the upper-cased binary name — so
a binary named sasso gets SASSO_*):
| Variable | Effect |
|---|---|
SASSO_LIBRARY |
Use this file instead; skips downloading entirely. Works for executables too. |
SASSO_TARGET |
Fetch this target instead of the detected one. |
SASSO_SKIP_DOWNLOAD |
Do not download. Fails at runtime if the file is missing. |
SASSO_DOWNLOAD_BASE_URL |
Fetch assets from this base URL instead. |
BINARY_DOWNLOADER_SKIP |
Global: skip every download this run. |
Setting *_DOWNLOAD_BASE_URL drops pinned checksums, since a mirror may
legitimately repackage an asset. See below.
Root overrides
A project can retarget a dependency's library — an internal mirror, a pinned version — without forking the package that declared it:
Keys are a library name or vendor/package:library, the latter winning on
conflict. An override naming no installed library is an error, not a silent
no-op. Overrides are re-validated, so they cannot introduce a shape the parser
would reject.
Checksums and overrides. A pinned digest describes one exact asset. An
override that moves the download (url, version, repository, asset,
archive) drops inherited checksums, because enforcing them against different
bytes would turn a working mirror into a hard failure. Supply checksums in the
override to keep verification:
An override that only changes path keeps the checksums, since the bytes are
unchanged.
Security: allow-binaries
Downloading — and, through bin proxies, executing — code from a URL chosen by a
transitive dependency is the same decision Composer gates with allow-plugins.
This plugin gates it the same way: nothing a dependency declares is fetched
until the root project allows the package in extra.allow-binaries.
Keys are package names or globs; the first matching entry wins, in declaration
order. The root project's own extra.binaries are always trusted — that file is
yours.
On an interactive composer install, each unreviewed package is prompted for
once, Composer-style, and the answer is written to composer.json:
y allows and persists, n denies and persists (no further prompts), d
skips for this run only. Non-interactively (CI) nothing is downloaded and the
warning carries the exact snippet to commit.
The gate holds on every path, not just the install hook:
- the installer skips the package (fatally, if it set
optional: false— it cannot both work and be refused); - no
vendor/binproxy is generated, and a previously generated one is removed when a package becomes denied; - the runtime refuses too:
Binaries::path()/install()— and therefore a lazy first-use download — throwNotAllowedException, so composer.json is the only place permission can come from. No CLI flag or env variable bypasses it.
An explicitly false package is skipped silently; an unlisted one warns. Both
show up in composer binary:list with the entry that would enable them.
Failure behaviour
Libraries are optional by default: a failure warns and composer install
still succeeds. That keeps installs working on an unsupported platform or behind
a blocked network, where the package may only be needed for autoloading, and the
runtime can still fetch on first use. Set optional: false when the package
cannot function without the binary.
Malformed configuration always fails loudly — it is the package author's bug, and ignoring it would hide a library that should have been installed.
binary:install exits non-zero on any failure, optional or not: an explicit
request wants the binary, not a best effort.
Verification
Downloads are checked against checksums when present, and an archive is
unpacked to a temporary directory and moved into place only once complete — an
interrupted or mismatched download never leaves a partial library where the
loader would find it.
Checksums are optional but recommended. Without one, a compromised release host or mirror is trusted implicitly.
License
MIT
All versions of composer-binary-downloader with dependencies
composer-plugin-api Version ^2.0