How do I get my composer package autoloaded properly? - php

I have created a package to separate out business logic into easier to distribute modules. The composer file looks like this:
{
"name": "aggiq/johnny-cash",
"description": "A collection of controllers, models, migrations, and tests for a phonebanking backend.",
"license": "MIT",
"authors": [ ... ],
"require": {
"illuminate/database": ">=5.5"
},
"require-dev": {
"fzaninotto/faker": "~1.4"
},
"autoload": {
"psr-4": {
"Johnny\\Phonebanking\\": "src/"
}
}
}
And our source files are indeed in src/:
src/Controllers/PhonebankController.php
src/Models/Phonebank.php
...
I saved and pushed this to our gitlab repo, and then included it as a dependency in a test project:
{
...,
"repositories": [{
"type": "package",
"package": {
"name": "aggiq/johnny-cash",
"version": "0.1",
"type": "package",
"source": {
"url": "gitlab url",
"type": "git",
"reference": "dev"
}
}
}],
"require": {
"aggiq/johnny-cash": "*",
},
...
}
And when I do composer update, it successfully grabs the project and downloads it into the vendor folder:
vendor/aggiq/johnny-cash/Controllers/PhonebankController.php
...
However, when I look in the test project's autoload_psr4.php, it's not there. Is there a step I missed?
Edit: updates the directories to have capital letters to match the namespaces, and here is the generated PSR4 php file:
<?php
// autoload_psr4.php #generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

You have registered autoloading at your package's composer.json correctly:
"autoload": {
"psr-4": {
"Johnny\\Phonebanking\\": "src/"
}
}
This means, any class in Johnny\Phonebanking namespace will be in src directory. E.g.:
Johnny\Phonebanking\SomeClass => src/SomeClass.php
Johnny\Phonebanking\SomeNamespace\AnotherClass => src/SomeNamespace\AnotherClass.php
As you can see, it has to respect CapitalLetters.
Saying that, you should correct first letters of your directories, from:
src/controllers/PhonebankController.php
src/models/Phonebank.php
to
src/Controllers/PhonebankController.php
src/Models/Phonebank.php

I have solved it. We needed to do two things.
The repository type specified in the parent pacakge should be vcs not package since we are loading from a git server:
"repositories": [{
"type": "vcs",
"url": "git#xxx.git"
}]
The package type in the child package should be library:
"type": "library"
Once those two changes were made, composer update installed not only the child package but also its dependencies, proving it is being recognized by composer.

Related

Private repo composer.json not being used

My problem is that a privately made repo's composer.json seems to be broken when trying to use it as a package elsewhere.
I have a private repo with code needed for other projects. The repo's composer.json looks like this:
{
"name": "somevendor/global",
"require": {
"nesbot/carbon": "^1.21"
},
"autoload": {
"psr-4": {
"" : "src/"
},
"files": [
"somedir/somefile.php"
]
}
}
The src is in the base directory of the repo, and contains PSR-4 namespaced classes. I have namespace folders within that, e.g. a Foo directory with classes in the Foo namespace:
-- src
-- Foo
// some Foo\... classes
// some global namespace classes
-- somedir
somefile.php // A file with helper functions
In the project folder, I'm accessing the somevendor/global repo via a composer.json file:
{
"require": {
"somevendor/global-folder": "dev-master"
},
"repositories": [
{
"type": "package",
"package": {
"name": "somevendor/global",
"version": "dev-master",
"type": "package",
"source": {
"url": "git#bitbucket.org/somevendor/global.git",
"type": "git",
"reference": "master"
}
}
}
]
}
Running composer install in the project folder seems to work at first. I have installed SSH keys properly so it can access the private repo on Bitbucket and grab the files:
$ composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing somevendor/global (dev-master master)
Cloning master
Writing lock file
Generating autoload files
And then in the project's PHP code I require vendor/autoload.php, but none of the classes are being autoloaded, including the Carbon package specified in the first repo's composer.json file:
Fatal error: Uncaught Error: Class 'Foo\Foo' not found in...
I've clearly made a mistake here, have I structured the first repo wrongly?
I "solved" this by taking out all of the "require" entries from the remote repo's composer.json file and moving them to the local website's composer.json file.
This is what the files looked like:
The remote private repo's composer.json:
{
"name": "somevendor/global",
"license": "proprietary",
"autoload": {
"psr-4": {
"" : "src/"
},
"files": [
"functions/functions.php"
]
}
}
The local website's composer.json:
{
"require": {
"nesbot/carbon": "^1.21",
"somevendor/global": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "git#bitbucket.org:somevendor/global.git"
}
]
}
It kept throwing Composer\Repository\InvalidRepositoryException because I forgot to put the name into the remote repo's composer.json file, so don't forget that bit!
Also remember to set up your git ssh keys if you've set them up. I used this Bitbucket tutorial to do this.

How to add the component's version with specific tag from GitHub to PHP project using Composer

I have GitHub repo tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001
with example PHP component. This component does not have itself composer.json file.
My objective is getting this component to my project using project's composer.json.
I follow the Julien's answer to Contributing to open source bundles from vendor directory?
I wrote composer.json that describes component as "package"
{
"repositories": [
{
"type": "package",
"package": {
"name": "tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001",
"version": "dev-master",
"source": {
"url": "https://github.com/tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001.git",
"type": "git",
"reference": "master"
},
"autoload": {
"psr-4": {
"TygrolewGmail\\Zawartosc\\Tworcy\\" : "src/Zawartosc/Tworcy/"
},
"files": [
"/src/Zawartosc/Funkcje/_d.php"
]
}
}
}
],
"require": {
"tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001": "dev-master"
},
"autoload": {
"psr-4": {
"": "src/"
}
}
}
It is installing the last commit from master branch.
$ php composer.phar install
Loading composer repositories with package information
Installing dependencies (including require-dev)
- Installing tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001 (dev-master master)
Cloning master
Writing lock file
Generating autoload files
My repo history gitk all branches history
fc70af [branch:master, no tag]
|
|
| 9941b7 [branch:trial, tag:"v0.0.2"]
| /
|/
c2849e [branch:master, tag:"v0.1"]
|
|
6f8ff7 [branch:master, tag:"v0.0.1"]
My composer.json file is downloading the last commit from master branch. But i want to install some previous versions of component or trial versions.
How to install
The last commit from trial branch.
Commit with specific tag (either from master or trial branch), for example "v0.1"
The Git repos have
branches,
tags
and commits.
composer.json has fields:
"version" inside "repositories"/"package"
"reference" inside "repositories"/"package"/"source"
version value after the package name in the "require"
What is their meaning and how do they relate to GitHub's branches, tags and commits?
Edit
Flosculus answer worked, but I m still a bit confuzed. Correct me, if I do wrong, but I guess "repositories"/"package"/"version" has no connection with GitHub. However it must been used during Composer's "require".
On the other hand, "repositories"/"package"/"source"/"reference" could be anything that Git can checkout, it can be branch, tag, or commit's hash. I tried defining three items in repositories array.
"repositories": [
{
"type": "package",
"package": {
"name": "tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001",
"version": "dev-trial",
"source": {
"url": "https://github.com/tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001.git",
"type": "git",
"reference": "trial"
},
"autoload": {
"psr-4": {
"TygrolewGmail\\Zawartosc\\Tworcy\\" : "src/Zawartosc/Tworcy/"
},
"files": [
"/src/Zawartosc/Funkcje/_d.php"
]
}
}
},
{
"type": "package",
"package": {
"name": "tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001",
"version": "0.1",
"source": {
"url": "https://github.com/tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001.git",
"type": "git",
"reference": "v0.1"
},
"autoload": {
"psr-4": {
"TygrolewGmail\\Zawartosc\\Tworcy\\" : "src/Zawartosc/Tworcy/"
},
"files": [
"/src/Zawartosc/Funkcje/_d.php"
]
}
}
},
{
"type": "package",
"package": {
"name": "tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001",
"version": "1.2.3",
"source": {
"url": "https://github.com/tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001.git",
"type": "git",
"reference": "fc70af"
},
"autoload": {
"psr-4": {
"TygrolewGmail\\Zawartosc\\Tworcy\\" : "src/Zawartosc/Tworcy/"
},
"files": [
"/src/Zawartosc/Funkcje/_d.php"
]
}
}
}
],
First defines version "dev-trial" as checkout trial branch which gives the last commit of that branch
Second defines version "0.1" as checkout tag "v0.1"
Third defines version "1.2.3" (non-existing in the GitHub) as checkout commit "fc70af"
To get them I use respectively three requires
"require": {
"tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001": "dev-trial"
},
2.
"require": {
"tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001": "0.1"
},
3.
"require": {
"tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001": "1.2.3"
},
When using the VCS type for repositories, Composer will scan each branch and tag for Composer files. From there, based on the branch and tag names, it can assemble a list of packages.
These packages (like the one you created to reference that repository) exist on packagist.org, which is the default lookup for Composer. The Authors of the repositories put them on Github.
In this case however, the repository is not on Packagist, and it has no composer file on any of it's versions. So what you have done, is created a generic repository. You can do the same thing with local directories, and zip files (local or remotely). However your one is a Git repository.
See modified json:
{
"repositories": [
{
"type": "package",
"package": {
"name": "tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001",
"version": "dev-master",
"source": {
"url": "https://github.com/tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001.git",
"type": "git",
"reference": "v0.0.1"
},
"autoload": {
"psr-4": {
"TygrolewGmail\\Zawartosc\\Tworcy\\" : "src/Zawartosc/Tworcy/"
},
"files": [
"/src/Zawartosc/Funkcje/_d.php"
]
}
}
}
],
"require": {
"tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001": "dev-master"
},
"autoload": {
"psr-4": {
"": "src/"
}
}
}
When Packagist or Composer (Packagist also has a copy of Composer), scans a VCS repository, it flattens out all of the versions into multiple packages with the same name, but different versions. In your case "references". The modified json above uses "reference": "v0.0.1" to indicate a specific tag/branch.
https://github.com/composer/satis/issues/29
This is an issue requesting support for VCS repositories without composer files. However it doesn't work. When you modify the repository like so:
{
"type": "vcs",
"url": "https://github.com/tygrolew-gmail/przykladowy-komponent-php-tygrolewa-gmaila-0001.git"
}
This should work if the repo had the composer.json file. In fact when you run composer update with this, you can see it searching each branch and tag. But without the relevant composer file, all you will get is this:
[Composer\Repository\InvalidRepositoryException]
No valid composer.json was found in any branch or tag of
https://github.com/tygrolew-gmail/przykladowy-komponent-p
hp-tygrolewa-gmaila-0001.git, could not load a package from it.

How to run library with composer?

I'm trying to install this github repo to my project (running on codeigniter). The steps I'm doing is very simple:
{
"name": "project",
"description": "",
"license": "MIT",
"require": {
"php" : ">=5.3.0",
"blockchain/blockchain" : "1.*",
"ext-curl": "*"
},
"require-dev": {
}
} // composer.json
and run php composer.phar update. So the package installs but I can't use it in my project - I don't think its autoloaded. /vendor/autoload.php is required in my index.php. When I try it with different package for test purposes (kriswallsmith/buzz) - it works. So what I'm doing wrong?
Also I checked my vendor/composer/installed.json and I see this:
[
{
"name": "blockchain/blockchain",
"version": "v1.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/blockchain/api-v1-client-php.git",
"reference": "c219b9b00778cf6c025628bd34fd6543922fe81b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/blockchain/api-v1-client-php/zipball/c219b9b00778cf6c025628bd34fd6543$
"reference": "c219b9b00778cf6c025628bd34fd6543922fe81b",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": ">=5.3.0"
},
"time": "2015-02-03 18:34:11",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Blockchain\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Blockchain API client library",
"homepage": "https://github.com/blockchain/api-v1-client-php",
"keywords": [
"bitcoin",
"blockchain"
]
}
]
and my function where I'm trying to use this lib:
private function __check_btc_balance()
{
error_reporting(E_ALL);
$Blockchain = new \Blockchain\Blockchain(PAYMENTS_BTC_API_CODE);
}
Have you followed the installation steps?
Basically there are some differences from common composer packages. Over here it says download the source code and run composer install from it's own folder
Then include the autoloader file from the folder of the downloaded files so you will have somewhere a folder Blockchain/vendor/autoload.php to include
Download the source or clone the repository. This php library works
with the Composer package manager. Navigate to the root of the
repository and run
$ composer install
This will create the /vendor folder in the repository root. In the php
source, simply:
// Include the autoload.php from its vendor directory require
'vendor/autoload.php'
// Create the base Blockchain class instance
I've seen ...
"autoload": {
"psr-4": {
"Blockchain\\": "src/"
}
},
I always keep all my code in src\Vendor\Project\Filename.php and composer autoloader works with this. Try to add this lines of code:
"autoload": {
"psr-0": {
"": "src/"
}
},

Component-based development with composer

Let's say I want to write a new application.
/workspace/exampleapp/
.git
src/
vendor/
app/
component1/
.git
src/
composer.json
component2/
.git
src/
composer.json
composer.json
I have three git repositories. They have no remotes. I also have three composer.json files.
/workspace/app/composer.json
{
"name": "app/app",
"type": "project",
"license": "MIT",
"require": {
"app/component1": "dev-master",
"app/component2": "dev-master"
},
"autoload": {
"psr-4": {"app\\": "src/"}
}
}
/workspace/app/vendor/app/component1/composer.json
{
"name": "app/component1",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {"app\\": "src/"}
}
}
/workspace/app/vendor/app/component2/composer.json
{
"name": "app/component2",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {"app\\": "src/"}
}
}
If I run composer update in the project root I receive the error The requested package app/component1 could not be found in any version, there may be a typo in the package name.
What do I have to change to make it work? Is there a way without moving the components directories out of the vendor directory or creating remote repositories?
Thank you!
I think you are suffering from the XY problem - I'm going to pretend that you actually asked "How can I setup a project with Composer with the following requirements?"
Be able to work without a remote Git server i.e. be able to commit and push locally.
Be able to commit code for the components separately to the commits to the main project.
Be able to edit the code in the vendor directories for your repositories, and commit from those directories.
In which case the answer is, setup your directories like this:
/workspace/app/
composer.json
src/
/workspace/components/
component1/
composer.json
src/
component2/
src/
composer.json
Setup each of your component's composer file like this:
/workspace/components/component1/composer.json
{
"name": "app/component1",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {"app\\": "src/"}
}
}
Tell your the composer.json file for your app to use the components directories as local git repositories.
/workspace/app/composer.json
{
"name": "app/app",
"type": "project",
"license": "MIT",
"require": {
"app/component1": "dev-master",
"app/component2": "dev-master"
},
"autoload": {
"psr-4": {"app\\": "src/"}
},
"repositories": [
{
"type": "vcs",
"url": "/workspace/components/component1"
},
{
"type": "vcs",
"url": "/workspace/components/component2"
}
],
}
When you do the first "composer update" you should run it with the option composer update --prefer-source. That will force composer to do a git clone for the code, rather than downloading just a zip-ball and extracting it.
(Adding --prefer-source may not be required, it seems composer may do clone by default when fetching from a local directory repository).
You will now be able to edit the code for the components in the vendor directory, be able to commit it separately to the app code, as well as be able to push it to 'remote' git repository at /workspace/components/component1/.

GitHub repository autoloading with Composer issue

I got PHP Fatal error: Class 'sendwithus\sendwithus_php\lib\API' not found
composer.json:
{
"repositories": {
"sendwithus_php": {
"type": "package",
"package": {
"name": "sendwithus/sendwithus_php",
"version": "1.0.3",
"source": {
"url": "https://github.com/sendwithus/sendwithus_php",
"type": "git",
"reference": "0dfed56"
}
}
}
},
"require": {
"sendwithus/sendwithus_php": ">=1.0.3"
}, "autoload": {
"psr-0": {
"Foo\\": "src/",
"sendwithus\\": "vendor/sendwithus/sendwithus_php/lib"
}
}, "minimum-stability" : "dev"
}
test.php:
use sendwithus\sendwithus_php\lib\API;
require_once 'vendor/autoload.php';
$api = new API('KEY');
What am I doing wrong?
There is a bunch of stuff going wrong in your case. I'll try to correct it as well as I can.
First I took a look at the library you are requiring. It is public on Github, and it has a composer.json file that has errors.
{
"name": "sendwithus/api",
"version": "1.0.3",
"require": {},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/sendwithus/sendwithus_php"
}
]
}
These infos are slightly wrong. Only the name is correctly formatted.
There shouldn't be a version. Versions are detected by tagging the code in git.
If nothing is required, that key can be omitted.
The current repository URL shouldn't be included in it's own repository.
Mentioning a license would be nice - currently everybody using this library should be scared about violating copyright.
The important, but missing info: How is autoloading configured?
Investigating the contents of that repository quickly reveals that it does not conform to PSR-0, so the only viable alternativ is classmap autoloading - which is sufficient enough because there are only two files in the lib folder.
A correct autoloading definition would be:
"autoload": {
"classmap": ["lib"]
}
Details on how to construct this are in http://getcomposer.org/doc/04-schema.md#classmap Effectively, the value for the classmap key is a list of directories relative to the repository root directory that should be indexed.
The test folder need not be mentioned here. Using PHPUnit, that folder would be scanned for any file containing test classes. The test bootstrap should include the vendor/autoload.php file generated by Composer.
I don't know if the OP is responsible for this repository or can change it. This info above should be implemented in the repository itself, but it is also helpful if it cannot, because it can also go into a "package" definition.
So now we are going to look at the mentioned composer.json of the OP:
{
"repositories": {
"sendwithus_php": {
"type": "package",
"package": {
"name": "sendwithus/sendwithus_php",
"version": "1.0.3",
"source": {
"url": "https://github.com/sendwithus/sendwithus_php",
"type": "git",
"reference": "0dfed56"
}
}
}
},
"require": {
"sendwithus/sendwithus_php": ">=1.0.3"
}, "autoload": {
"psr-0": {
"Foo\\": "src/",
"sendwithus\\": "vendor/sendwithus/sendwithus_php/lib"
}
},
"minimum-stability" : "dev"
}
The "repositories" key can contain objects that are of type "package" that contain all the necessary info of a project that fails to do so, or fails to do correctly. As I mentioned, the autoloading is broken in the original definition, so it must be fixed here:
"sendwithus_php": {
"type": "package",
"package": {
"name": "sendwithus/api",
"version": "1.0.3",
"source": {
"url": "https://github.com/sendwithus/sendwithus_php",
"type": "git",
"reference": "0dfed56"
},
"autoload": {
"classmap": ["lib"]
}
}
}
This would correctly reference that repository and enable autoloading. Note that the name has changed here to the original - it would probably trigger trouble if this library is known under two different names (one defined here, and the other in the original repository), but using the same namespace and class names.
Now that the repository info is fixed, all other things work as usual in composer.json.
"require": {
"sendwithus/api": "1.0.3"
},
"autoload": {
"psr-0": {
"Foo\\": "src/"
}
},
"minimum-stability" : "dev"
Note that the autoloading defined here is for THIS library or application only. Do not include the autoloading of the dependencies here!
And then we take care of your code:
use sendwithus\sendwithus_php\lib\API;
require_once 'vendor/autoload.php';
$api = new API('KEY');
The namespace is wrong. Don't use the name from Composer. Use the name from the code you are importing. This is correct:
require_once __DIR__ . "../vendor/autoload.php";
use sendwithus\API;
$api = new API("apikey");
Note that you cannot change the namespace of the library with a rename in Composer. Composer only downloads the PHP source files for you, it does not change the code inside the files.
My final composer.json file is this:
{
"repositories": {
"sendwithus_php": {
"type": "package",
"package": {
"name": "sendwithus/api",
"version": "1.0.3",
"source": {
"url": "https://github.com/sendwithus/sendwithus_php",
"type": "git",
"reference": "0dfed56"
},
"autoload": {
"classmap": ["lib"]
}
}
}
},
"require": {
"sendwithus/api": "1.0.3",
},
"autoload": {
"psr-0": {
"Foo\\": "src/"
}
},
"minimum-stability": "dev"
}
If you have a standard directory structure (as in vendor/sendwithus/sendwithus_php/lib) You will need to modify the path to be relative to the composer.json of that package:
"sendwithus\\": "vendor/sendwithus/sendwithus_php/lib"
Becomes:
"sendwithus\\": "lib/"
Take a look at vendor/composer/autoload_namespaces.php Which should list the path that is being used. Notice how composer will prepend the $vendorDir for you so your namespace should not need to reference it
An example from my project:
'Core\\' => array($vendorDir . '/alex-patterson-webdev/core/src'),

Categories