Include multiple composer repository configuration files - php

I want to break up the composer repository configuration into different files.
I use local and external repositories and I would like to keep their configurations separate.
These files would then be pulled into the main composer.json when running the install or update task.
Is this possible?

There is now a plugin called composer-merge-plugin which can merge multiple composer files at runtime.
Usage:
Assume you have your main composer.json file located in a base path, and then you have other composer.json files that you'd like to load from subdirectories in /plugins:
/
|-- composer.json
|-- /plugins
|-- /kittenizer
|-- composer.json
|-- /puppyizer
|-- composer.json
In your main composer.json file, you can load these with:
{
"require": {
"wikimedia/composer-merge-plugin": "~1"
},
"extra": {
"merge-plugin": {
"include": [
"plugins/*/composer.json"
],
"recurse": true,
"replace": false,
"merge-dev": true,
"merge-extra": false
}
}
}

I'm quite late to the party, but I had a similar issue and solved it without a composer plugin.
/root
|-- composer.json
|-- /vendor
|-- /more-dependencies
|-- composer.json
Your composer file in the root-dir:
{
"repositories": [
{
"type": "path",
"url": "more-dependencies"
}
],
"require": {
"something/more-dependencies": "dev-master"
}
}
Your composer file in your other directory (more-dependencies) has to contain at least the following:
{
"name": "something/more-dependencies"
}
You can now composer install in your root directory and anything specified in your subfolder's composer.json will land in your root's vendor folder.
❗ Warning: Only root's composer.json can include repository metadata, not the one in the subfolder (composer-doc).
Side note: In my case the composer.json in the subdir is a cloneable resource. I use the composer hook pre-install-cmd to make sure "more-dependencies" are existing and up-to-date.
"scripts": {
"more-dependencies": [
"git -C more-dependencies pull || git clone https://example/more-dependencies.git more-dependencies"
],
"pre-install-cmd": [ "#more-dependencies" ],
"pre-update-cmd": [ "#more-dependencies" ]
},

Related

Composer - autoload classes in CodeIgniter outside vendor folder

I've been working on setting up a CodeIgniter project with composer. I'm wanting to include php classes stored in files outside the vendor folder - in a shared folder.
My directory structure:
/
--application/
--shared/
-application/
-class1.php
-class2.php
-class3.php
-base/
-classb1.php
--vendor/
--composer.json
--composer.lock
Looking at the composer documentation, I see there is an autoload property in the root package that I'm trying to use to load the classes in the shared directory. These classes aren't namespaced.
My composer.json file is as follows:
{
"description" : "The CodeIgniter Application with Composer",
"require": {
"php": ">=5.3.2",
"codeigniter/framework": "3.1.*"
},
"require-dev": {
"mikey179/vfsStream": "1.1.*"
},
"autoload":{
"psr-0":{
"":"shared/application/",
"":"shared/base/",
"":"shared/data/"
}
}
}
My search led me to this question, but the classes are still not being loaded. I've ran composer update on the terminal.
Well after looking further, there's a property called classmap (documentation) in the root package.
"autoload":{
"classmap":["shared/application/","shared/base/", "shared/data/"]
}
This loads all the required files in the folders.

Composer not autoloading files included through repositories > type > path

The Problem
I have two PSR-4 composer projects and I wish to classes from one in the other, the file structure is as follows:
projectfoo
-public
--index.php
-src
--CompanyName
---Foo
----Foo.php
-composer.json
projectbar
-src
--CompanyName
---Bar
----Bar.php
-composer.json
The composer.json files are defined for projectfoo as (note the repositories > type > path dependency):
{
"name": "companyname/foo",
"require": {
"companyname/bar": "*"
},
"repositories": [
{
"type": "path",
"url": "../projectbar"
}
],
"autoload": {
"psr-4": {
"CompanyName\\": "src/CompanyName"
}
}
}
And projectbar as:
{
"name": "companyname/bar",
"version": "1.0.0",
"autoload": {
"psr-4": {
"CompanyName\\": "src/CompanyName"
}
}
}
Running composer update correctly produces the vendor folder containing the companyname/foo folder.
In index.php in projectfoo I have:
require_once '../vendor/autoload.php';
$bar = new \CompanyName\Bar\Bar();
However, when run \CompanyName\Bar\Bar is undefined.
The question
Why is Bar not being included in the autoload.php file?
What I've tried
Refreshing the autoload file using:
composer install
composer update
composer dump-autoload
Changing the vendor name CompanyName to something different.
Looking at the installed.json file in the vendor\composer folder the bar project is listed as:
#
{
"name": "companyname/bar",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"dist": {
"type": "path",
"url": "../companyname/bar",
"reference": "f35ed0ad82c8280db9b603712dd256074f99e196",
"shasum": null
},
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"CompanyName\\": "src/CompanyName",
}
}
}
Strangely, when bootstrapping the autoload.php file to phpunit the files autoload correctly and are available in my test classes.
The issue revolves around the way composer links "path" projects using symlinks.
In short
Because I was developing on a windows system and executing the code on an Ubuntu vagrant box the symlinks set up on windows, when running composer install were resolving to broken paths inside the box. This also explains why tests run on windows managed to locate Bar but not tests run in the virtual box.
Solutions
To solve this I initially forced composer to mirror the project rather than symlink it, however, this still caused issues with symlinks in the vendor folder.
I have now started hosting my libraries in seperate repositories and importing them using the "type": "vcs" tag which works well. Composer also handles updating package when new commits are made which doesn't happen when using the "type": "path" tag with mirroring.

Composer require local package

I've got a couple of libraries [Foo and Bar] that I'm developing in concert, but are still technically separate things. Previously I've just re-defined the autoloader to like "Foo\\": "../Foo/src", but now that I've added a Guzzle dependency to Foo, Bar flips it's lid because it's not one of its dependencies.
Directory structure:
/home/user/src/
Foo/
src/
FooClient.php
composer.json
Bar/
src/
BarClient.php
composer.json
Theoretical Autoload Statement: [in Bar/composer.json]
"require": {
"local": "../Foo/composer.json"
}
Example code:
require('vendor/autoload.php');
$f = new \Bar\BarClient(new \Foo\FooClient());
How can I resolve this without setting up a local Composer repo? I want to maintain these as separate packages, just that one requires the other, and therefor processes the other's dependencies.
post-answer edit:
Thanks to infomaniac I've done the following:
Initialized the git repo:
cd ~/src/Foo && git init && echo -e "vendor\ncomposer.lock" > .gitignore && git add ./ && git commit -m "Initial Commit"
Added the composer config:
"require": {
"sammitch/foo": "dev-master"
},
"repositories": [{
"type": "vcs",
"url": "/home/sammitch/src/Foo"
}],
And then composer update!
The way to link to a local, in-development package is to first add in your main project's composer.json a repository, like this:
"repositories": [
{
"type": "path",
"url": "/full/or/relative/path/to/development/package"
}
]
You also need to either have a version specified in your development package's composer.json or the way I do it is to require the package using #dev, like this:
composer require "vendorname/packagename #dev"
It should output:
- Installing vendor/packagename (dev-develop)
Symlinked from /full/or/relative/path/to/development/package
The #dev in the require command is important, composer uses this to pickup the source code and symlink it to your new package.
It's a stability flag added to the version constraint (see package link).
These allow you to further restrict or expand the
stability of a package beyond the scope of the minimum-stability
setting.
The minimum-stability flags are:
Available options (in order of stability) are dev, alpha, beta, RC, and stable.
You can use Composer's repositories feature
https://getcomposer.org/doc/05-repositories.md#path
{
"repositories": [
{
"type": "path",
"url": "../../packages/my-package"
}
],
"require": {
"my/package": "*"
}
}
Instead of using the http format, specify a file path on disk.
After spending some time, I finally understood the solution. Maybe it'll be useful for someone like me and will save you some time, so I've decided that I have to share it here.
Assuming that you have the following directory structure (relative to your project root directory):
composer.json
config
config/composition-root.php
local
local/bar-project
local/bar-project/composer.json
local/bar-project/src
local/bar-project/src/Bar.php
public
public/index.php
src
src/Foo.php
In this example you may see that the local folder is meant for nested projects of your company, e.g. bar-project. But you could configure any other layout, if you wish.
Each project has to have its own composer.json file, e.g. root composer.json and local/bar-project/composer.json. Then their contents would be as follows:
(root composer.json:)
{
"name": "your-company/foo-project",
"require": {
"php": "^7",
"your-company/bar-project": "#dev"
},
"autoload": {
"psr-4": {
"YourCompany\\FooProject\\": "src/"
}
},
"repositories": [
{
"type": "path",
"url": "local/bar-project"
}
]
}
(local/bar-project/composer.json:)
{
"name": "your-company/bar-project",
"autoload": {
"psr-4": {
"YourCompany\\BarProject\\": "src/"
}
}
}
If, for example, you wish to locate each project in a separate sibling directory, as follows:
your-company
your-company/foo-project
your-company/foo-project/composer.json
your-company/foo-project/config
your-company/foo-project/config/composition-root.php
your-company/foo-project/public
your-company/foo-project/public/index.php
your-company/foo-project/src
your-company/foo-project/src/Foo.php
your-company/bar-project
your-company/bar-project/composer.json
your-company/bar-project/src
your-company/bar-project/src/Bar.php
- then you need to link to respective directory in repositories section:
"repositories": [
{
"type": "path",
"url": "../bar-project"
}
]
After that don't forget to composer update (or even rm -rf vendor && composer update -v as the docs suggest)!
Under the hood, composer will create a vendor/your-company/bar-project symlink that targets to local/bar-project (or ../bar-project respectively).
Assuming that your public/index.php is just a front controller, e.g.:
<?php
require_once __DIR__ . '/../config/composition-root.php';
Then your config/composition-root.php would be:
<?php
declare(strict_types=1);
use YourCompany\BarProject\Bar;
use YourCompany\FooProject\Foo;
require_once __DIR__ . '/../vendor/autoload.php';
$bar = new Bar();
$foo = new Foo($bar);
$foo->greet();
The command line way to do this is
composer config repositories.package_name local path/to/package
composer require group/package_name
If the group/package_name is available both from repository and local, the local version is used.
The local command can be replaced with vcs to reference a direct repository, or composer for default.

Test my own composer package does not work

I want to make a composer package. However, I am still in the development phase, would or would but the earlier test out.
I have an empty vendor folder with the autoloader from composer:
/vendor
/composer
autoload.php
So now I've tried my package "simulate" and creates my folder structure and composer.json:
/vendor
/composer
/me
/package
/src
/tests
composer.json
autoload.php
This is my composer.json:
{
"name": "me/package",
"description": "",
"license": "",
"authors": [
{
"name": "",
"email": ""
}
],
"minimum-stability": "dev",
"require": {
"php": ">=5.4.0"
},
"autoload": {
"psr-4": {
"Me\\Package\\": "src/"
}
}
}
And here is my class:
namespace Me\Package;
class Test {
// ...
}
If I want to call it:
if(file_exists('vendor/autoload.php')) require 'vendor/autoload.php';
$test = new \Me\Package\Test();
i become Fatal error: Class 'Me\Package\Test' not found.
Of course, I also inserted a composer.json in the root directory, but I can still bad at require my package state since it was not published, right? But how do I test it then and say to composer he should autoload my package?
If you want to use composer to include a package that is not listed on http://Packagist.org/ you would add a 'repositories' stanza into the composer.json (project root file). This reads the project, and gets the composer.json from it, using the name for the main-'requires' section.
"repositories": [
{
"type": "vcs",
"url": "https://github.com/example/private-repo.git"
}
}
The 'url' part, can also in fact be any valid URL for a git, SVN or HG repository - even a file:// based reference.

Composer, Laravel and local packages

My issue is I have a package which isn't a repository and I am trying to get it to play nice with Laravel and composer. It is still located under the vendor folder, the only issue is that if I simply set:
"psr-0": {
"Test\\Test": "vendor/test/test/src/"
}
This will load the service provider but none of the controllers etc will autoload. What is the correct way to implement a package with larval that does not have it's own repository. Or does this go against the nature of packages and this should simply be structured under the applications controllers.
The package was created by me using workbench but I found i did not really need this as a separate repository but it would still be good to keep it as a package. Therefore the structure is exactly the same as a regular package:
vendor
testvendor
testpackage
public
src
tests
.gitignore
composer.json
phpunit.xml
UPDATE:
As a solution for the time being I am using:
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php",
"vendor/package"
]
},
As an entry in the class map. Looking forward I will probably refactor this into the app folder or create a repository for this package.
If you have some classes that you're calling "package", you're not supposed to add those files to your vendor folder. This folder is managed by composer and at any time you might loose it. Create a subfolder in your application and put those files there.
You have to be sure your PSR-0 autoloading will work for every single file in your folder structure. So, if your root is vendor/test/test/src/ and your namespace is
Test\\Test
All your files must be in
vendor/test/test/src/Test/Test/ClassFileName.php
PSR-4 is easier to deal and understand, this
"psr-4": {
"Test\\Test\\": "vendor/test/test/src/"
}
Means that your files would have to be like:
vendor/test/test/src/ClassFileName.php
Doublecheck your namespaces. It's easy to make mistakes when using namespaces with PSR-0 and remember that
composer dump-autoload
Must be ran every time you change things in composer.json or create new files. If it's a simple class autoloading, every time you create a file, if it's a PSR-X autoloading, everytime you create or update a namespace in your composer.json file.
If what you have is is really a package you should use Composer: when your package is structured as a composer package (check Laravel's composer.json as an example), the correct way of adding it to your application, if it's not list in Packagist, is via repositories.
You can have (non-packagist) packages in a public VCS repository:
{
"require": {
"monolog/monolog": "dev-bugfix"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/igorw/monolog"
}
]
}
You can have (non-packagist) packages in a protected by password VCS repository (git, bitbucket...):
{
"require": {
"vendor/my-private-repo": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "git#bitbucket.org:vendor/my-private-repo.git"
}
]
}
You can have your packages zipped in your hard drive and load them via the artifact repository type:
"repositories": [
{
"type": "artifact",
"url": "path/to/directory/with/zips/"
}
],
Though #Antonio Carlos Ribeiro's answer is really nice, I had problem with installing custom packages locally(which is also stated in the last part of his answer)
Let's assume this is the directory structure of the package we are trying to install:
D:/test_pack
src/
composer.json
If you do not want to upload your custom package (that most likely you have developed, yourself) to online repositories you can use one of the following two methods:
Method I
(You have to specify version for your package, otherwise you'll get this error: The requested package could not be found in any version, there may be a typo in the package name.)
1) In composer.json, Add version to your package. your package's json should look something like this:
{
"name": "gandalf/test_pack",//This is your package's name
"description": "some desc",
"version": "1.0.0",//This is the version that you have to specify
"authors": [
{
"name": "gandalf the grey",
"email": "fake#yahoo.com"
}
],
"minimum-stability": "dev",
"require": {
"laravel/framework": "~5.4"
},
"autoload": {
"psr-4": {
"Gandalf\\BotPack\\": "src/"
}
} }
2) zip your package(let's assume the zip file is in D:/test_pack/test_packa.zip)
3) In laravel's composer.json add your package name (in our case gandalf/test_pack into require part of json) and add the repository array to the composer.json file and in that array specify the directory in which your package's zip file exists(in our case D:/test_pack) . like this
{
...,
"require": {//adding our package name to laravel's composer.json
...,
"gandalf/test_pack": "*"//package's name
},
...,
"repositories": [
{
"type": "artifact",
"url": "D:/test_pack"
}
]
}
Method II(My Favorite method, You have to initialize your package directory as git local repository using git init and then git add . and git commit -m "your message")
1) initialize the package directory as git directory and commit all your changes to the local repository
(let's say D:/test_pack is the directory that contains your package(src/ directory and composer.json))
go to D:/test_pack directory and run these commands
git init
git add .
git commit -m "your message for this commit"
2) In your packages composer.json file add minimum-stability
{
"name": "gandalf/test_pack",
"description": "some desc",
"authors": [
{
"name": "gandalf the grey",
"email": "fake#yahoo.com"
}
],
"minimum-stability": "dev",//setting minimum-stability
"require": {
//dependencies that your package needs
},
"autoload": {
"psr-4": {
"Gandalf\\BotPack\\": "src/"
}
}
}
3)In laravel's composer.json file require the "dev-master" of your package
{
...,
"require": {
...,//some dependencies that laravel needs
"gandalf/test_pack": "dev-master"//requiring dev-master from repository
},
"repositories": [
{
"type": "git",
"url": "D:/test_pack"//path of the local repository directory which contains your package
}
]
}
To any Laravel project load local packages. which is stored in your machine.
In laravel's (Project) composer.json file add
"autoload": {
"psr-4": {
"YourPackage\\Namespace\\": "./local_Package_path/src"
}
},
and fire command in Laravel Project directory
composer dump-autoload
Optional
If package is still not available in your Project. then
Register your package's Service Provider.
To register your service provider, you just need to add an entry to the array of service providers in the config/app.php file.
'providers' => [
/*
* Laravel Framework Service Providers...
*/
...
YourPackage\Namespace\PackageServiceProvider::class,
],
Hope now your package loaded successfully in your laravel project.

Categories