cpx: running a Composer package's binary without installing it
Run any command from any composer package, even if it's not installed in your project.
At a glance
- What is it?
- cpx is a Composer Package Executor that works like npx for npm, installing a package into an isolated directory and running its command while keeping it away from both your project and your global dependencies. Its sharper edges are the local binary preference, the local directory mode for package authors, and an exec and tinker pair that boots your framework.
- Who is it for?
- cpx suits a PHP developer who wants to try a tool without adding it to a project, or who wants one command that works identically across every checkout on a machine, since the isolated install area is shared and the local binary preference keeps you on the version your project pins. It does not suit you if you want a pinned, reproducible toolchain, because an isolated install follows the latest version within the constraint and the cache is yours to clean up.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
cpx is npx for Composer, and the package name comes first
cpx is a Composer Package Executor, and the whole pitch is one sentence: it is to Composer what npx is to npm. The expanded form of the description is that you can run any command from any composer package, even if it is not installed in your project.
The invocation shape follows from that comparison. You pass the package name and then the command you want to run:
cpx <package-name> <command> [arguments]
# Example: cpx friendsofphp/php-cs-fixer php-cs-fixer fix ./srcA package name is what you would use to require the package in your composer.json file, for example friendsofphp/php-cs-fixer. You can also use constraints to specify a version, for example friendsofphp/php-cs-fixer:^3.0.
There is a shorthand, and it is the one the second example shows. If the package only has one command, or the command name is the same as the package's name, you can omit the command from the end, so cpx friendsofphp/php-cs-fixer fix ./src works either way.
Behind the scenes, cpx installs the package into a separate directory and runs the command, keeping it separate from both your project and your global Composer dependencies, unless the package is already installed in your project. Subsequent runs of the same package reuse the same installation and run quickly, unless you specify a different version or an update is available.
The project is MIT licensed, written in PHP, with the default branch named 2.x. The recent releases are v1.0.4 in April 2025, v2.0.0 in July 2026 and v2.1.0 in August 2026, and the last push was on 2026-10-14. The 1.x series is frozen and no longer supported, and the repository carries an upgrade guide for moving to 2.x.
Installation is a global Composer require, and the documentation is specific about the part people get wrong:
composer global require cpx/cpxYou have to make sure Composer's global bin directory is on your PATH, and you can find it with composer global config bin-dir --absolute, so that you can run cpx from anywhere. Upgrading is composer global update cpx/cpx, and if you run the standalone PHAR instead, cpx can update itself in place by downloading the latest release from GitHub with cpx self-update.
A local project binary always wins, unless you skip it
The precedence rule is the part of cpx that changes behaviour relative to a naive implementation, and it is stated with an analogy.
Like npx, cpx prefers a binary that is already installed in your project. Before installing an isolated copy, cpx finds the nearest Composer project, walking up from the current directory, and runs the matching binary from its configured bin-dir, which is vendor/bin by default:
cpx pint # runs vendor/bin/pint when the project has it installed
cpx phpunit --filter=Foo # runs vendor/bin/phpunit when present
cpx laravel/pint:^2.0 # uses the local pint only when the installed version satisfies ^2.0That means cpx pint runs vendor/bin/pint when the project has it installed, cpx phpunit with a filter runs vendor/bin/phpunit when that is present, and a version constraint narrows the match, so cpx laravel/pint:^2.0 uses the local pint only when the installed version satisfies that constraint.
The reasoning is stated plainly: this keeps cpx aligned with the versions your project pins. When no matching local binary is found, cpx falls back to installing and running an isolated copy.
So there are three cases, and they are worth separating because they behave differently. A package the project depends on at a satisfying version runs from the project. A package the project depends on at a version that does not satisfy the constraint, or does not depend on at all, gets an isolated install. And a package that is not a project dependency at all gets an isolated install every time you ask for it.
The opt-out is a flag rather than a separate sub-command:
cpx --skip-local laravel/pint --versionThe flag has to come before the package, which is a small usability wrinkle worth knowing before you reach for it. And it is the escape hatch for the case where you want the newest version of a tool even though the project pins an older one, which is a legitimate thing to want when you are checking whether a bug is fixed upstream.
The walking-up behaviour also has a consequence. If you are in a subdirectory of a project, cpx finds the project above you, so running a tool from a nested folder will use the outer project's pinned version. That is the behaviour you want, and it is also the behaviour that would surprise someone who has two projects nested inside each other.
Running a package from its own checkout, with none of the isolation
The local directory mode is the feature that makes cpx useful to package authors rather than only to consumers.
When you are developing a Composer package locally, you pass its directory and cpx runs its declared binary directly from the source checkout:
cpx /absolute/path/to/package --version
cpx ~/path/to/package --version
cpx ./path/to/package --version
cpx ../path/to/package --versionThe forms are an absolute path, a home-relative path, a current-directory-relative path and a parent-relative path. For a package with multiple binaries, you pass the binary name after the directory, exactly as you would after a package name, so cpx ../package binary-name --flag.
The preconditions are explicit. The directory must contain a valid composer.json, and its dependencies must already be installed at vendor/autoload.php. cpx does not run Composer, copy the package, cache it, or include it in package maintenance commands when an explicit local directory is used, and the instruction is to run composer install in the package directory yourself whenever its dependencies need installing.
That is a clear division of labour. In the isolated mode cpx manages an installation; in the directory mode it manages nothing and simply points at what is already there. The reason is obvious once stated, which is that a developer who has just edited the code does not want a cached copy of the previous version executed.
The consequence is that a directory-mode invocation is exactly as reproducible as the checkout itself. Whatever the working tree contains is what runs, including uncommitted changes, and the dependencies are whatever composer install last put in vendor.
It also means the cache is bypassed entirely, which matters because the cached installs are what make repeated isolated runs fast. A developer working in a checkout is not paying that cost, and is not creating cache entries either.
Aliases, the installed list, update and clean
Four management commands sit alongside the execution ones, and they cover the lifecycle of the isolated installs.
The alias command creates a shortcut so you do not have to remember or type the full vendor and package name every time:
cpx alias laravel/pint pintBoth arguments are optional, and if you leave either one out cpx prompts you for it. Leaving out the name defaults it to the package's short name, so cpx alias laravel/pint on its own is enough to create the pint alias. Aliases are saved under ~/.cpx/ and can be listed with cpx aliases, and cpx unalias with a name removes one, while leaving the name off prompts you for it.
The installed command shows all the packages you have run via cpx and have installed. Running cpx with no arguments, or cpx list, shows every available cpx command, which is a small convenience that makes the tool discoverable without the documentation.
The update command is about the packages cpx has installed rather than your project. While cpx will automatically check for updates to a Composer package when you run a command, you can also update manually. cpx update will update the local version of all packages run via cpx to the latest version, according to their version constraints, and cpx update with a vendor, or a vendor and package, updates only the specified ones.
The clean command removes the packages you have run via cpx but have not used recently, and cpx clean with the all flag removes all packages regardless of when they have run.
Together those four are the argument for a separate install area. Because cpx keeps its installations apart from your project and from your global Composer dependencies, it needs its own lifecycle commands, and the update behaviour respects the same version constraints you wrote, so a package installed under a caret constraint does not silently jump to a breaking release.
exec and tinker, and the framework bootstrapping
The last group of commands runs PHP code rather than a package binary, and it is where cpx stops being an npx equivalent and starts doing something the ecosystem does not otherwise offer easily.
cpx exec with a file runs a plain PHP file, and the documentation is careful to say this is the only way to run a file, since a bare cpx with a file path is not routed to exec. cpx exec with the raw flag executes PHP code given on the command line. And cpx exec with a gist URL downloads a GitHub gist and runs it, similar to how npx can run gists.
The gist handling has detail in it. For a gist with multiple PHP files, cpx asks which one to run, or you can append the file's anchor from the gist page to skip the prompt. You can pin a specific revision by appending its SHA to the URL, and setting a GITHUB_TOKEN environment variable authenticates the request if you hit GitHub's rate limit. Raw gist links work too and are downloaded exactly as pinned. The script executes against your current directory, so autoloading and framework bootstrapping still apply.
That last clause is the point, because it is what makes the scratch-file workflow usable in a real application rather than a nice demonstration.
The benefits are listed for these commands and they are substantial. There is automatic autoloader detection, so when running a PHP file cpx finds and uses Composer's autoloader if it exists in the current or a parent directory. There is class aliasing: if a class is used in the file but the namespace is not imported, cpx tries to find an appropriate one to alias. And there is framework bootstrapping: in a Laravel project cpx fully boots the application, so config, facades and the environment file all work with the application instance in scope, and in a Symfony project it boots the kernel and exposes the kernel and the container. The boot can be skipped with the no-boot flag.
And then there is the REPL. cpx tinker opens an interactive REPL in the terminal for your project, and in a Laravel project with laravel/tinker installed, cpx tinker runs your project's own tinker rather than a bundled one.
That is the sharpest detail in the whole command set. Most tools that offer a REPL ship their own, which means your application's service container, your config and your bindings are not there. Running the project's own tinker means the scratch console is the same console the team already uses in production, which removes the usual complaint that a quick REPL is useless for anything involving the framework.
Editorial conclusion
cpx suits a PHP developer who wants to try a tool without adding it to a project, or who wants one command that works identically across every checkout on a machine, since the isolated install area is shared and the local binary preference keeps you on the version your project pins. It does not suit you if you want a pinned, reproducible toolchain, because an isolated install follows the latest version within the constraint and the cache is yours to clean up. Before you adopt it, check that Composer's global bin directory is on your PATH, since a global install that is not on the PATH is the most common reason cpx appears to be missing, and remember that the 1.x series is frozen and unsupported.
Frequently asked questions
How do I install cpx and make sure it is on my PATH?
Run composer global require cpx/cpx, then make sure Composer's global bin directory is on your PATH. You can find that directory with composer global config bin-dir --absolute. To upgrade, run composer global update cpx/cpx, or cpx self-update if you use the standalone PHAR.
Does cpx use the binary already installed in my project?
Yes, and it prefers it. Before installing an isolated copy, cpx finds the nearest Composer project by walking up from the current directory and runs the matching binary from its configured bin-dir, vendor/bin by default. A version constraint narrows the match, so a local binary is used only when the installed version satisfies it, and otherwise cpx falls back to an isolated install.
How do I run a package I am developing locally with cpx?
Pass the directory instead of a package name, for example cpx /absolute/path/to/package --version or cpx ../path/to/package --version. The directory must contain a valid composer.json and its dependencies must already be installed at vendor/autoload.php, since cpx does not run Composer, copy the package or cache it in this mode.
What can cpx exec and cpx tinker do?
cpx exec runs a plain PHP file, raw PHP code with the raw flag, or a GitHub gist, and a bare cpx with a file path is not routed to exec. cpx tinker opens an interactive REPL for your project, and in a Laravel project with laravel/tinker installed it runs your project's own tinker. Both commands detect Composer's autoloader, alias unimported classes, and can bootstrap Laravel or Symfony, with a no-boot flag to skip that.
How do I manage the packages cpx has installed for me?
cpx installed lists them, cpx update updates all of them to the latest version within their constraints or a specific vendor or package, and cpx clean removes the ones you have not used recently while cpx clean with the all flag removes all of them. Aliases are saved under ~/.cpx/ and managed with cpx alias and cpx unalias.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/laravel-cpx)Was this useful?