Reputation: 15656
What is the cleanest way to check whether a package (of any version) is installed/present, using PHP within our application?
Basically, inside our application we want to call a function with the following signature:
bool function hasComposerPackage(string $packageName)
What would this function have to contain so that we can do something like:
if (hasComposerPackage('phpunit/phpunit')) {
echo 'PHPUnit is installed!';
}
Ideally this needs to happen without any command-line exec calls and it should not autoload any unnecessary files in the process.
Upvotes: 10
Views: 44440
Reputation: 197659
This is an old question and I think the accepted answer is fine, but just for those who would like to have this portable across Composer versions, perhaps even on a system deployed that does not have the composer
user command and also to understand the underlying protocol with the file-system.
In general you can easily obtain the vendor folder from within your own package after it has been installed.
hakre/xmlreaderiterator
(exemplary "own" package relative path in the composer vendor folder, refs)
Additionally you can similarly easily obtain the installation status of other composer configured packages after they have been installed.
composer/semver
(exemplary another, Composer Semver, package relative path in the composer vendor folder, refs)
As usual, we may "only" need to understand how Composer itself works with our (file-)system, which I elaborate in my answer here to a greater extend to make it prominent you neither need to depend on a specific Composer runtime version nor shell_exec()
as we're already executing PHP code, a) this is not necessary, b) we may not have composer(1)
(the user-command named composer) and c) we may not have the location of composer.json
nor of the vendor
folder.
Take it with a grain of salt, YMMV. E.g. if I have composer(1)
at hand during build time, I would not stress myself with the details of the file-system. However I may already benefit from knowing about the installed.json
file.
Let's see what the vendor
folder actually is, where it is located and how packages are placed inside. Then get any packages installation status just by their package name:
vendor
. It is relative to the working directory from which by default composer loads the project configuration from the relative path composer.json
(for the composer update command) and composer.lock
(for the composer install command if the lock file has not been disabled).composer.json
(and .lock
) by the COMPOSER
environment parameter and the vendor directory by the COMPOSER_VENDOR_DIR
parameter.#/config/vendor-dir
mainly, but also the behaviour of having or not having the lock file #/config/lock
(all JSON Pointers after #
marked relative to the JSON Text merge production of $COMPOSER_HOME/config.json
and overriding $COMPOSER
).shell_exec()
) you have to figure out the vendor directory yourself.__DIR__
magic constant.package-vendor-name/package-name-name
directory, this is composer.json#/name
for each package, e.g. from the root of the vendor folder */*/composer.json#/name
.__DIR__
magic constant by obtaining the grandparent directory in the hierarchy. E.g. given your php file is in the root of your project, dirname(__DIR__, 2)
(or dirname(dirname(dirname(__FILE__)))
for PHP versions below 7.0/5.3 if you require such compatibility - which is supported by Composers' default autoloader code as well btw., it is compatible with PHP 5.2, so while at this level of detail, you may want to support it).composer/installed.json
which is composers sentinel file for the composer install
command invocations' vendor folder that also installed your package. Its format did change in history but not much. And while we're here at this end, this should not be the show-stopper.Non-default locations in the vendor folder at install (build) time:
composer/installed.json
file is that Composer itself keeps its protocol of what has been installed in the vendor folder. So despite things get moved away from default locations, as long as this file has not been moved, you can normally rely on it. So does Composer.composer/installed.json
file what the contents of the vendor folder is and would not update if it deems the vendor folder production already complete.--no-scripts
and --no-plugins
flags when you want to have a clean vendor folder production, perhaps with --no-dev
for a production only vendor.composer/installed.json
by making a forced copy of which will also give you an updated timestamp.COMPOSER
environment parameter to them. This comes with the benefit that invalidating the vendor folder is still possible for all these and you get additional book-keeping.As usual every project is different, therefore you may only want to cherry pick a single detail from this answer or even the inverse, you may not want to make use of that after you've learned about it. There is quite some benefit to rely on the command-line interface of composer
only and have your projects dependencies and the autoloader afterwards.
You known which packages are installed by default, by specifying your required ones in composer.json
. Composer then can either install them (you know those packages are there), or it fails installing them (you know those packages are not there).
Upvotes: 1
Reputation: 951
Composer 2 (Oct 2020) now supports looking for package installation status! https://blog.packagist.com/composer-2-0-is-now-available/
There is a new class, Composer\InstalledVersions, which is autoloaded in every project and is available at runtime. It allows you to check which packages/versions are present at runtime of your own project.
The following usage example is from Installed versions - Runtime Composer utilities:
\Composer\InstalledVersions::isInstalled('vendor/package'); // returns bool
@user1132363 using shell_exec()
to run something like composer show
is the only way to know for sure (Composer below 2), but you seem to refuse to want to go this route. I'm not sure why you refuse, this is the solution to your problem. There is no other reliable means. Using class_exists
is also unreliable as class names can change in packages.
That said, I think there's a bigger question here that you aren't asking: What problem are you actually trying to solve? As in, why do you need to check to see if a package is installed?
Upvotes: 15