I've been working with the Magento Firegento custom Composer installer, and I ran into this odd bit of Composer behavior I don't understand.
Consider the following dead simple composer.json file
{
"require": {
"magento-hackathon/magento-composer-installer": "*"
}
}
If I run compser.phar install with this composer.json file, I get the following.
$ composer.phar install --no-dev
Loading composer repositories with package information
Installing dependencies
- Installing aoepeople/composer-installers (v0.0.1)
Loading from cache
Writing lock file
Generating autoload files
From my mostly lay-person's understanding composer.phar, I've said
Hey, composer, please install the magento-hackathon/magento-composer-installer package from packagist.org
And composer has said back to me
Sir, yes sir! Here's the aoepeople/composer-installers package
I don't understand why composer installed aoepeople/composer-installers, when I asked for magento-hackathon/magento-composer-installer.
To be clear: I understand the reason magento-hackathon/magento-composer-installer wasn't installed is this is a package that lives in a different composer repository. My original mistake was not including this repository in my composer.json file.
However, it doesn't make sense to me that composer would install a different package than the one I asked for. When I search packagist there's no magento-hackathon/magento-composer-installer extension.
Why does packagist install a different extension? What's happening behind the scenes to make magento-hackathon/magento-composer-installer resolve to aoepeople/composer-installers? How/where in the composer source could I debug this sort of thing myself in the future?
Vinai published a great writeup on Magento and Composer here.
Quoting from there
Composer will use packagist.org to look where to get the libraries. Because Magento modules aren’t listed there, you will have to add the packages.firegento.org repository to the configuration as follows [...]
So you will need the repository
"repositories":[
{
"type":"composer",
"url":"http://packages.firegento.com"
}
],
to get the magento composer installer.
And yes composer offers replacements. On the packaging entry for aoepeople/composer-installers you will notice the replaces section:
And since magento-hackathon/magento-composer-installer is not available on packagist composer will deliver you aoepeople/composer-installers.
OK, so that's a weird behavior :) I'm the author of the aoepeople/composer-installers package and the idea behind this is that this package provides an alternative (very basic and simplified implementation of the composer package-type magento-module - and adds another type magento-source. I only want the installer to put the package in the right place - keeping it simple. That's why I decided to come up with an alternative package.
The reason why the aoepeople/composer-installer replaces the magento-hackathon/magento-composer-installer is because many Magento modules already come with a composer.json that requires the magento-hackathon/magento-composer-installer. In order to be able to seamlessly use the simpler installer that one is "pretending" to be the hackathon-installer. But unless you actively require aoepeople/composer-installers in your project's composer.json you should continue using the original installer as no Magento module's composer.json out there (not even ours) is directly referring to aoepeople/composer-installers.
The fact that packagist tries to be "smart" and returns a package that replaces a package that's not registered is new to me and - to be honest - very disturbing. While this might be intended behavior I share the opinion that this could easily be abused. I start liking Alan's idea to bypass packagist completely using "packagist":"disabled". Especially in the case of Magento modules this seems to happen easily because most of the Magento modules are unknown to packagist while being registered at packages.firegento.com only.
A simple quickfix/workaround to this specific situation could be to register magento-hackathon/magento-composer-installer on packagist.org.
The Packagist package at https://packagist.org/packages/aoepeople/composer-installers has metadata saying that it replaces the magento-hackathon/magento-composer-installer package. Packagist would then install this as it's the package that replaces what you asked for.
Docs here: https://getcomposer.org/doc/04-schema.md#replace
Related
I'm using Laravel + VueJS to recreate a POS system from work. I needed to install sped-nfe package to work with it on a system for work.
This package requires many other packages in order to function properly, like ext-curl, ext-soap, ext-json.
As per instructions, I added
"nfephp-org/sped-nfe" : "^5.0"
to my composer.json.
When I ran composer install or composer update, the following error ocurred:
Your requirements could not be resolved to an installable set of packages.
Problem 1
- nfephp-org/sped-nfe[v5.0.100, ..., v5.0.122] require ext-soap * -> it is missing from your system. Install or enable PHP's soap extension.
- Root composer.json requires nfephp-org/sped-nfe ^5.0 -> satisfiable by nfephp-org/sped-nfe[v5.0.100, ..., v5.0.122].
To enable extensions, verify that they are enabled in your .ini files:
- C:\laragon\bin\php\php-7.4.19-Win32-vc15-x64\php.ini
You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.
I'm using Windows and I'm not authorized to modify our local server nor the actual server, I was getting frustrated trying to find a solution to my problem - almost every answer would tell me to modify my php.ini or install curl with sudo apt-get - I arbitrarily decided to add the following lines to my composer.json:
"provide": {
"ext-curl":"*",
"ext-soap":"*"
},
Et voilà, composer update and composer install were working smoothly.
What's bothering me is, according to the composer documentation,
provide
Map of packages that are provided by this package. This is mostly
useful for implementations of common interfaces. A package could
depend on some virtual package e.g. psr/logger-implementation, any
library that implements this logger interface would list it in
provide. Implementors can then be found on Packagist.org.
Using provide with the name of an actual package rather than a virtual
one implies that the code of that package is also shipped, in which
case replace is generally a better choice. A common convention for
packages providing an interface and relying on other packages to
provide an implementation (for instance the PSR interfaces) is to use
a -implementation suffix for the name of the virtual package
corresponding to the interface package.
I am not providing this package, I simply wanted to require it but ended up putting it differently. Also, I've tried requiring it, but the error was still there.
Was this a good solution to my problem or should I do it differently?
Is there anything about this that I should worry?
Can someone explain the 'provide' syntax for me?
If you add a package or a PHP extension to the provide section, you tell composer that your package itself or the external system setup "provides" this one. The dependency resolver is fine with this.
This does not check further whether this dependency is actually properly resolved or not. Composer relies on your statement that this is not a lie ;) So, if you only add this to the section without properly providing that package, you cannot be sure that your application works properly.
In your example: the package you want to install requires the SOAP extension. It won't work properly without it. If you cannot install that extension on your server, you should not use this package.
I have a hard time figuring this out; I am also puzzled of the connection - if any - with the create-project command.
As far as I can tell, the only difference between install and create-project is the execution of post-root-package-install and post-create-project-cmd hooks...
Can someone shed some more light on this?
My goal is to set up a composer infrastructure where I run create-project and this sets up a project skeleton (creates and sets directory permissions, creates default configuration files, creates blank data stores)
What exactly is the difference between project and library types in composer?
Practically, there is none. It doesn't affect how composer gets executed. You can use both install and create-project with both types of package types.
This metadata is meant to inform plugins, IDE, or even packagist.org when parsing composer.json, but on a vainilla installation, there is no practical difference in using one or the other.
(Docs on package types)
As far as I can tell, the only difference between install and create-project is the execution of post-root-package-install and post-create-project-cmd hooks.
The docs are your friends:
You can use Composer to create new projects from an existing package. This is the equivalent of doing a git clone/svn checkout followed by a composer install of the vendors.
Any time you do create-project, install is executed as well. Which means that the install hooks are executed as well.
First it clones the whole package via the appropriate CVS (git, usually), and then immediately executes composer install. By default, it removes CVS information (e.g. the .git directory), unless one uses the --keep-vcs option.
create-project is useful to bootstrap applications, so the app's directory is setup beyond what downloading composer`s dependencies would do. You can create a skeleton directory structure, etc.
Usually one would have a package proper (that could be required into an application), and a "application-skeleton" package, that would include the directory structure and would depend on the original package.
I'm posting a more succint answer to my questions, based on experimentation:
What is the difference between project and library types?
Absolutely none as far as composer is concerned. Some plugins might implement logic to treat the two package types differently though.
How does that relate to require, install, create-project commands?
In no way whatsoever. The project in package type has nothing to do with project in create-project.
How does create-project work?
Let's say we are talking about a single <package>. We have 2 workflows:
composer init <project_path> && composer require <package>
composer create-project <package> <project_path>
The first workflow will create a blank root package and add <package> as a requirement to it.
The second workflow will "clone" <package> into <project_path> as the root package.
NB: If you are working with local path type repos for development, create-project will actually create <project_path> as a symlink to <package>'s source dir. This is the default behavior of path repos, and probably a miss for composer in the need to treat create-project differently. This can create a heap of confusion (as you might be inadvertently changing and adding to <package>s sources while thinking you are just editing project_path). So for local development and testing, you are better off with cp -A rather than composer create-project.
I'm currently working on a package (cms), which has a dev-dependency to a certain package (code-generator) to create code. This package is not needed in production.
However, when creating a website that uses the cms package, dev-dependencies (including the code-generator) are not installed (which is correct composer behavior btw).
But while developing the website, the code-generator is required.
Is there any way to force a certain dev-dependency to also install when the package is installed?
This is not possible. Dependency can either be required for the package to work properly (then it should be in require section and it is always installed), or required only for development of this package (then it should be in require-dev section and is installed only when package repository is root). There is nothing in between. If this code-generator dependency is required by your package to work it clearly fails into first category (require section).
Usually in this case the best solution is to split this package into 2 packages: regular package and dev package with all tools used only during development process. So it should be installed by 2 commands:
composer require myvendor/mypackage
composer require myvendor/mypackage-dev --dev
This will still require everyone to install two packages instead of one, but it should not be a big problem if it is properly documented. Result should is more clear (it should be quite obvious what is the purpose of myvendor/mypackage-dev package) and gives more control to package owner (he can easily add new dependencies for dev package) and end user (he can always skip installing myvendor/mypackage-dev if he don't want to use this code generator).
i am on symfony 2.8, and my php version is 5.5.9.
i'm trying to install TINYMCE bundle from composer. I used this command line:
php composer.phar require stfalcon/tinymce-bundle='1.0'
i had this message error:
i tried with v2.0 but same problem.
Have you an idea ?
Thank's a lot for your help.
It would seem that in your composer.json's config section you have platform requirements that conflict with the bundle.
https://getcomposer.org/doc/06-config.md#platform
You could try installing the requirements using the option --ignore-platform-reqs:
composer require stfalcon/tinymce-bundle --ignore-platform-reqs
composer install --ignore-platform-reqs
This will momentarily lift the specified platform requirements. Obviously if they are in place, because they are actually needed - and since they were put there deliberately, at least at some point they were - this might break things on production. So be careful and look at the above option first (updating or removing the platform requirements).
Have you tried either of these two commands:
composer.phar require stfalcon/tinymce-bundle='1.0'
composer require stfalcon/tinymce-bundle='1.0'
I'm not sure if it will make a difference, but it might.
Trying to use composer's provide feature, I added a provide section to my implementation repository ffa-php-mock, in which I say it provides shadiakiki1986/ffa-php-implementation. In my repository consuming this implementation, ffa-php-cli, I replaced the composer require entry requiring ffa-php-mock with an entry requiring ffa-php-implementation. If I try to run a composer update, I get the following
> composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
Problem 1
- The requested package shadiakiki1986/ffa-php-implementation could not be found in any version, there may be a typo in the package name.
Potential causes:
- A typo in the package name
- The package is not available in a stable-enough version according to your minimum-stability setting
see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.
Read <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.
Can you give me any hints as to how to debug what the problem is?
Edit: simplified example
I tried the provide feature in a dummy project on something that already exists. jackalope/jackalope provides phpcr/phpcr-implementation, which in its turn is already required by DoctrinePHPCRBundle.
If I start a new empty project with composer init and specify that my project depends on phpcr/phpcr-implementation, I get the same error as above. I also tried putting phpcr/phpcr-implementation directly in the composer.json file and running a composer update, but to no avail.
> cat composer.json
{
"require": {
"phpcr/phpcr-implementation": "2.1.0"
}
}
I would think that this is perhaps a bug in composer, but it seems from this issue that it is already in use.
> composer --version
Composer version 1.3.0 2016-12-24 00:47:03
The closest composer github issue I can find is #2811, but that one says that the reporting isn't clear, not that provide doesn't work
So I ended up learning that the specific package with the implementation should still be included in the composer.json file. For packages using the library, that's ok as they get added to the require section. For the library that is directly requiring the implementation, that should be done in the require-dev so that the unit tests can work and so that other projects using the library are not required to use the same implementation