composer packages.json autoload definition - php

I use composer libraries in my private project to manage common logic and I used to always install them with a "vcs" repository definition like this:
"require": {
"private/package": "dev-develop",
},
"repositories": {
{
"type": "vcs",
"url": "http://localhost/git/private-package.git"
}
}
... which used to work like a charm but was tedious for project where I would use many libraries at once.
So I decided to write my own "composer" type repository that dynamically generates a "packages.json" with all my libraries that looks something like this:
"packages" {
"private/package": {
"dev-develop": {
"name": "private/package",
"version": "dev-develop",
"source": {
"type": "git",
"url": "http://localhost/git/private-package.git",
"reference": "origin/develop"
}
}
}
}
The installation works fine but it does not generate the autoloader properly anymore. The installation does not read the "composer.json" file of the actual vcs repository anymore.
The autoloader only works properly if I include the autoloader definition in the "packages.json" file.
I want composer to use the "packages.json" definition, but still read the autoloader definition from the actual package. My question is if that is possible.
If the autoloader definition changes with a newer version of the library, the definition in "packages.json" will not work with an older version anymore so I am thinking there should be another way to solve this problem.

Related

How to create a monolithic Composer package with a built-in composer-plugin?

I want my package to ship with a built-in composer-plugin.
I have a structure like this:
composer.json
src/
...
plugin/
composer.json
src/
...
The root composer.json is configured like this:
{
"name": "foo/bar",
"type": "library",
"autoload": {
"psr-4": {
"Foo\\Bar\\": "src/"
}
},
"repositories": [
{
"type": "path",
"url": "./tools",
"options": {
"symlink": false
}
}
],
"require": {
"foo/bar-plugin": "*"
}
}
And the built-in composer-plugin's plugin/composer.json like this:
{
"name": "foo/bar-plugin",
"type": "composer-plugin",
"require": {
"composer-plugin-api": "^1",
"composer/composer": "^1",
"foo/bar": "*"
},
"autoload": {
"psr-4": {
"Foo\\Bar\\Plugin\\": "src/"
}
},
"extra": {
"class": "Foo\\Bar\\Plugin\\MyComposerPlugin"
}
}
Notice how there's a two-way dependency here - the plugin depends on foo/bar, and the project itself depends on foo/bar-plugin.
Here's where it gets weird. During a fresh installation with e.g. composer install or composer update, everything is fine - the plugin does it's thing, which, right now, means just announcing itself on the console.
Now, after installation, if I type just composer, I'd expect to see the plugin announce itself, same as before, right?
Instead, it generates a fatal "class not found error", as soon as it tries to reference any class belonging to the foo/bar package.
It's as though composer lost track of the fact that foo/bar-plugin requires foo/bar, and for some reason it's classes aren't auto-loadable.
Is there any reason this shouldn't be possible? Why not?
Of course I can just package this stuff in separate external package, but that isn't going to make much sense, since these packages are just going to depend on each other - they're effectively one unit, a packaging them as two packages is going to result in a mess of major version increases with every small change, as basically every release of foo/bar will break foo/bar-plugin.
Ideally, I'd like to simply add the composer-plugin directly into the main package, but it appears that's not possible for some reason? Only a package with type composer-plugin is allowed to add plug-ins, it seems?
If the plugin is essentially a part of your package, you should not use it as such. Composer offers alternatives.
As Jens mentioned in a comment to your question, there is 'scripts' key in composer.json. You can invoke shell commands inside, but also call static class methods.
About plugin solution - composer explicitly mentions this on its site:
Composer makes no assumptions about the state of your dependencies prior to install or update. Therefore, you should not specify scripts that require Composer-managed dependencies in the pre-update-cmd or pre-install-cmd event hooks. If you need to execute scripts prior to install or update please make sure they are self-contained within your root package.
(my side note - this also roughly applies to plugins).
Anyway - to provide you with a solution: discard 'plugin' approach. Instead modify your composer.json file so it looks as follows:
composer.json
{
"name": "foo/bar",
"type": "library",
"autoload": {
"psr-4": {
"Foo\\Bar\\": "src/"
}
},
"require": {
},
"scripts": {
"post-install-cmd": [
"Foo\\Bar\\Composer\\Plugin::postInstall"
],
"post-update-cmd": [
"Foo\\Bar\\Composer\\Plugin::postUpdate"
]
}
}
Additionally, in src/Composer folder create Plugin.php:
src/Composer/Plugin.php
<?php
namespace Foo\Bar\Composer;
use Foo\Bar\Test;
/**
* Composer scripts.
*/
class Plugin
{
public static function postInstall()
{
print_r("POST INSTALL\n");
print_r(Test::TEST_CONST);
print_r("\n");
}
public static function postUpdate()
{
print_r("POST UPDATE\n");
print_r(Test::TEST_CONST);
print_r("\n");
}
}
As you see, it prints constant from Test class. Create it in src/:
src/Test.php
<?php
namespace Foo\Bar;
/**
* Test class.
*/
class Test
{
const TEST_CONST = "HERE I AM";
}
Run this and check, how it plays out.

How do you manage your dependency libraries?

How do you manage your dependency libraries? I separate my project into a bunch of libraries, because these libraries are also used in several other projects. In the beginning, I make each of them as Git repo, and I use Git submodule to manage them. Soon it becomes a nightmare. Once I make some changes, I have to commit in submodule, this is tedious, and need tremendous work.
I am wondering how Sylius did that, they keep each bundle as Git and Packagist repo, but they don't use Git or Composer to manage their own bundles.
The best way is to use composer to manage your dependencies and to autoload all your classes.
The first step in order to achieve that is to prepare all your components to be ready for composer, so each one of your dependencies will have their own composer.json at the root. A basic configuration may look like that:
{
"name": "your/component-name",
"description": "your description",
"license": "proprietary",
"authors": [
{
"name": "Your name",
"email": "you#mail"
}
],
"autoload": {
"psr-4": {
"Your\\Complete\\Namespace": "src/"
}
},
... etc ...
}
The name field is the name you will use to load the dependencies in your main project
The autoload section is very important since it will determine the base namespace of all your classes. When you'll import your dependencies in your main project, you will access to your component classes via this namespace.
When your dependencies will be ready, you will prepare your main project to load them via composer. So basically, the composer.json structure of this project will look quite the same as the previous with more options in order to load your dependencies
{
"name": "your/project-name",
"description": "your description",
"license": "proprietary",
"authors": [
{
"name": "Your name",
"email": "you#mail"
}
],
"autoload": {
"psr-4": {
"Your\\Project\\Namespace": "src/"
}
},
"require": {
"your/dependency1-name" : "dev-master",
"your/dependency2-name" : "dev-master",
....
},
"repositories": [
{
"type": "git",
"url": "https://github.com/the-git-url-of-your-project1"
},
{
"type": "git",
"url": "https://github.com/the-git-url-of-your-project2"
}
]
... etc ...
}
each line of the require part will allow you to configure all the dependencies you want to load (its the name part of the dependency composer.json) in which version (dev-master or the number of the tag if you have some).
repositories part: Except if your dependencies are on packagist (https://packagist.org/), you'll have to add the repository of your dependencies (it could be github, bitbucket etc...). It the same url you can find in the clone section of your repo.
This is the steps you have to follow in order to manage your dependencies with composer. Obsviously, you really need to check the documentation to adapt it to your needs cause its just an basic overview of what you can do with composer.
when everything is ready, a composer install should load your dependencies in a vendor directory and all your classes available by its namespace.
You can look at the documentation for more options:
https://getcomposer.org/doc/
And this usefull Cheat Sheet
http://composer.json.jolicode.com/
I see in comment (i can't add comments) that you want to commit all changes made in your main application to all bundles.
You can look at the no-api option of composer:
"repositories": [
{
"type": "git",
"no-api": true,
"url": "https://github.com/the-git-url-of-your-project1"
}
]
composer will do a git clone when you do a composer install
Another solution without the use of composer is to use git submodules

How to set a different path for composer installer based on components inside a framework

I've been working on a PHP Framework development and we'd like to separate the components into individual packages from the core. Like this:
vendor /
username/frameworkname /
core/
Namespace /
Core /
..Framework core files here
components/
Namespace /
Component
ComponentOne
ClassOne.php
ClassTwo.php
composer.json
LICENSE.txt
README.md
ComponentTwo
ClassOne.php
ClassTwo.php
ClassThree.php
composer.json
README.md
LICENSE.txt
ComponentHopeYouFotIT!
Core being required by composer like this:
composer require name/framework-core
And be able to install it in
vendor/username/frameworkname/src/core/Namespace/Core/
And any component like this:
composer require name/componentname
And be able to install it in:
vendor/username/frameworkname/src/components/Namespace/Component/
So that we don't have to create a separate folder for any new component and could use like this:
use Namespace\Core\CoreClass;
use Namespace\Component\{
ComponentOne\ClassTwo,
ComponentTwo\ClassOne
};
It that possible?
I've personally have tried Composer composer/installers but couldn't figure out the correct settings in composer.json to do the trick.
"config": {
"vendor-dir": "vendor/username"
},
"repositories": [
{
"type": "package",
"package": {
"name": "frameworkname/componentname",
"version": "master",
"source": {
"url": "https://github.com/username/componentname",
"type": "git",
"reference": "master"
}
},
"require": {
"composer/installers": "~1.0"
}
}
],
Have you looked at the Composer Custom Installer documentation? It does this.
The tl;dr is that you need to create a (very simple) Composer plugin to set custom install locations, and the documentation walks you through it very specifically.
Aside: My sense, having experimented a bit with composer/installers, is that it may not support custom types, but might allow you to set custom paths for supported types. That is: if you wanted to use a WordPress plugin, but install it somewhere different than normal, you could override the install-paths, but you couldn't necessarily use composer/installers to define your own type whooptido-type with a custom install location. This inference is based, I strenuously underscore, on empirical experience experimenting with it this morning, rather than actual documentation. ;)

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.

How do I define an explicit SVN revision for a Composer repository?

I'm trying to include google-api-php-client library to my project using Composer. Simpliest way is to get library from VCS trunk branch, but I think it's not the best idea in my case. Much better will be point to some stable library state (tag or revision). Whereas there's no tags available, get a particular svn revision is the only option. But I have no idea how to do this.
I tried different package configs with no success, something like this:
{
"repositories":[
{
"type":"package",
"package":{
"name":"project/google-api-php-client",
"version":"0.2.1",
"source":{
"type":"svn",
"url":"http://google-api-php-client.googlecode.com/svn",
"reference":"trunk/?r=515"
}
}
}
]
}
Whether it possible at all to checkout svn revision with composer? Thanks in advance.
Using a package repository when you are defining the version you can specify a revision in the reference. An example from my wordpress composer.json
{
"repositories": [
"type": "package",
"package": {
"name": "wordpress-plugin/wp-minify",
"type": "wordpress-plugin",
"version": "1.2",
"source": {
"type": "svn",
"url": "http://plugins.svn.wordpress.org/wp-minify",
"reference": "trunk#691320"
},
"require": {
"composer/installers": "~1.0"
}
}
]
}
This installs the plugin from the trunk with an explicit revision of 691320.
The version can be set in the require part (that you don't show).
The only SVN options available are:
{
"repositories": [
{
"type": "vcs",
"url": "http://svn.example.org/projectA/",
"trunk-path": "Trunk",
"branches-path": "Branches",
"tags-path": "Tags"
}
]
}
Look's like you want to use a package, but you could also just define this repository and require the appropriate version (can you tag? It's easier).
You could also try to put the revision insive the version parameter but I don't think that will work.
Also, the documentation state that about the "trunk" path:
Since Subversion has no native concept of branches and tags, Composer
assumes by default that code is located in $url/trunk, $url/branches
and $url/tags. If your repository has a different layout you can
change those values.

Categories