Composer require local package - php

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.

Related

PHP composer: Update local path repository dependency

I have a project which requires a bunch of local path repositories. Those local path repositories partly require each other and also remote repositories.
For example one of the local repo composer.json looks like this:
{
"description": "",
"type": "neos-project",
"name": "mapo/campaign",
"repositories": [
{
"type": "path",
"url": "Source/Mapo.NodeTypes"
}
],
"require": {
"mapo/nodetypes": "*"
},
"autoload": {
"psr-4": {
"Mapo\\Campaign\\": "Classes/"
}
},
"extra": {
"neos": {
"package-key": "Mapo.Campaign"
}
}
}
The mapo/nodetypes local package then requires also a private remote repository:
{
"description": "",
"type": "neos-project",
"name": "mapo/nodetypes",
"minimum-stability": "dev",
"repositories": [
{
"type": "git",
"url": "url to private repo.git"
},
{
"type": "path",
"url": "../Mapo.Somepackage"
},
],
"require": {
"mapo/privateproject": "*",
"mapo/somepackage": "#dev"
},
"autoload": {
"psr-4": {
"Mapo\\NodeTypes\\": "Classes/"
}
},
"extra": {
"neos": {
"package-key": "Mapo.NodeTypes"
}
}
}
Now I need to test a new feature in mapo/privateproject. So I created a new branch in the private repo called issue0815 and made my changes. I also created a new branch issue0815 in the main mapo project (which has the local path composer.json changes locally).
I updated the dependency of mapo/nodetypes to "mapo/privateproject": "dev-issue0815",.
My problem is that, no matter which command, composer refuses to install the issue0815 branch for the private repository. At first composer complained, that it cannot install the dependency dev-issue0815 because the composer.lock prevented it. So I removed all usages of the private repo and the mapo/nodetypes from my composer.lock.
What surprised me the most, was that composer recovered the original composer.lock file. It just completely ignored my current local main project branch - which has the modification for the local path repo composer.json files and just required the contents from the master branches.
So, how can I update a dependency of a local path repo which needs a specific branch from a private repo?
This one took me quite a while, but the reason why my changes to composer.json have been completely ignored including also when I deleted the composer.lock was that, composer ignores your changes if it concludes, that the packages from vendor/composer/installed.json are correct.
From https://github.com/composer/composer/issues/4312#issuecomment-191488570:
Right now Composer prefers installed packages, derived from the installed.json file. So as long as the replacement is valid, it will indeed keep generating the same solution and thus lock. This is just because that's how Composer works internally.
To 'reset' it completely and make it rethink its installed packages (before it automatically 'corrects' because of package updates) you'd have to remove vendor/composer/installed.json as well as the lock. This will trigger a complete reevaluation of dependencies and reinstall. You could also just go for the full cleanup then and delete the entire vendor directory.
Thats also the reason why it is often suggested to remove the vendor directory, I think.

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 loading individually mapped classes

EDIT | I basically would like to tell composer to populate
autoload_classmap.php with a mapping of all files/classes under the
directory "web/". This houses all the application specific classes -
some which follow PSR-0 others do not.
I have a composer.json:
{
"name": "company/project",
"description": "Internal management system",
"require": {
"swiftmailer/swiftmailer": "^5.4",
"slim/slim": "2.4.2"
},
"autoload": {
"psr-0": {
"Application_Ancillary_": "web/private/module/rpi/ancillary/"
}
}
}
This "autoload" will kind of work - but there are cases where the classnames do no map according to PSR-0 the classname might be something like
Application_TestSomething => web/private/module/test/ApplicationTestSomething
When I tried to use 1:1 classname => file mapping it didn't work when I ran:
composer dumpautoload -o
Also the "Slim" classes are being included in the autoload_classmap.php
Ideally all the composer included packages (ie: Slim, etc) would not be part of the classmap file as I have hundreds of legacy files which I would rather include.
I assume that up until now someone manually edited the autoload_classmap.php
Any suggestions?
In case you don't follow any standard (PSR-0/PSR-4), use classmap:
"autoload": {
"classmap": [ "web" ]
}

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