GitHub repository autoloading with Composer issue - php

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'),

Related

Composer Override a package file

I am trying to override a package file, as it doesn't work without modification and it hasn't been updated to fix this.
Package is webklex\laravel-pdfmerger and the file is PDFMerger.php
In it has:
use fpdi\FPDI;
for it to work correctly it needs to be:
use FPDI;
Obviously whenever I update using composer, this file is of course then incorrect, and at this stage I have to manually go in and change the file to get it working.
There must be a better way?
I've attempted making a copy of the git package, and then using composer to import it and attempt to override, composer snippet is below, but it doesn't do what I want it to do.
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"type": "project",
"repositories": {
"laravel-pdfmerger": {
"type": "package",
"package": {
"name": "****/laravel-pdfmerger-mirror",
"version": "1.1.1",
"source": {
"url": "https://bitbucket.org/****/laravel-pdfmerger-mirror.git",
"type": "git",
"reference": "origin/master"
}
}
}
},
"require": {
...
"****/laravel-pdfmerger-mirror": "1.1.1",
"webklex/laravel-pdfmerger": "1.1"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/",
"VendorExtensions\\xerolaravel\\": "packages/vendorExtensions/xerolaravel",
"Webklex\\PDFMerger\\PDFMerger\\": "vendor/****/laravel-pdfmerger-mirror/src/PDFMerger"
}
},
Any help here would be great.
--- EDIT ---
updated my composer.json file to remove the repository and change the psr-4 autoload to the following:
psr-4": {
"App\\": "app/",
"VendorExtensions\\xerolaravel\\": "packages/vendorExtensions/xerolaravel",
"Webklex\\laravel-pdfmerger\\PDFMerger\\": "vendor/webklex/laravel-pdfmerger/src/PDFMerger"
}
But it is still not overriding the original file whenever I do use PDFMerger. Is it because I'm using a Facade?
Figured this one out at last...
1) Import Forked respository in your composer.json file, but ensure the name matches the name of the package you are replacing; in my case webklex/laravel-pdfmerger, like so:
"repositories": {
"webklex/laravel-pdfmerger": {
"type": "package",
"package": {
"name": "webklex/laravel-pdfmerger",
"version": "1.1.1",
"source": {
"url": "url to git",
"type": "git",
"reference": "origin/master"
}
}
}
},
Note: Ensure you set a version higher than what is currently available on the original package, so here I added .1 to differentiate the change and tag the commit as this so I can select it.
2) require the package, but ensure you request the version you tagged in your commit:
"require": {
"php": ">=5.6.4",
"webklex/laravel-pdfmerger": "1.1.1"
},
3) Update the autoload to point to where the package has been imported:
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/",
"Webklex\\PDFMerger\\": "vendor/webklex/laravel-pdfmerger/src"
}
},
Run composer update and then dump the autoload.
You should then be able to reference the package in your application as per the instructions in the original package.
You can do this by putting your edited package in a vendor-custom directory and adding it to the autoload section of your composer.json.
"autoload": {
"psr-4": {
"App\\": "app/",
"Vendor\\Package": "vendor-custom/vendor/package"
}
},
Gregwar psr-0 example:
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/"
},
"psr-0": {
"Gregwar\\Cache": "vendor-custom/gregwar/cache",
"Gregwar\\Image": "vendor-custom/gregwar/image",
"Gregwar\\ImageBundle": "vendor-custom/gregwar/image-bundle"
}
},
In PHP, you can actually alias a class to another, and that behaviour include namespaces.
This is extremely useful in legacy projects where you want to start using namespaces but don't want to refactor all usage of the said class.
You have the opposite here, the class is defined in a namespace but the class you are using is not in a namespace, still, aliasing the class would do.
So the function you might be looking for is class_alias.
Here is an example
<?php
class FPDI {
public function __construct() {
var_dump('I am FPDI');
}
}
class_alias('FPDI', 'fpdi\FPDI');
$noNamespace = new FPDI; // would, off course work
$namespace = new fpdi\FPDI(); // would work too, thanks to the class alias
So if you are using Laravel, your job should be as simple as aliasing the class correctly.
This can actually be done where you define your providers and aliases.
So at the beginning of config/app.php
<?php
class_alias('FPDI', 'fpdi\FPDI');
return [
// all of the below array stays as you have it, it was just snipped for concision purpose
];

How do I get my composer package autoloaded properly?

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.

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 autoload a repository in composer which does not follow PSR-0 or PSR-4?

So, I'm trying to use jcleblanc/reddit-php-sdk, but it follows no standards whatsoever and does not have a repository available, so I've had to manually define it myself in my composer.json file:
"repositories" : [{
"type": "package",
"package": {
"name": "jcleblanc/reddit-php-sdk",
"version": "dev-master",
"source": {
"url": "https://github.com/jcleblanc/reddit-php-sdk",
"type": "git",
"reference": "origin/master"
},
"autoload": {
"classmap": ["reddit-php-sdk/", "/", "reddit.php", "config.php"]
}
}
}],
Directory structure in vendor/ here:
However, when I then run composer dump-autoload, the classes in this project are not autoloaded, and don't appear in any of the autoload_*.php composer files. This means I of course get a "Class 'reddit' not found" error whenever I try and use it.
Solutions?
You can use Composer's file autoloading.
{
"autoload": {
"files": ["src/MyLibrary/functions.php"]
}
}
However, that's more geared towards helper function files and I've not tried it with a Class file (although there's no reason it shouldn't work).
Ended up forking the project myself, but it turns out the original project is broken anyway.

Autoloading classes from my private repository included in Composer

I've added my own repository to a Composer, it download properly into my another project.
Unfortunately Composer doesn't take my repository code under consideration while updating autoloading.
autoload_namespaces.php has many namespaces generated but any of them is my repository code.
I could add namespaces in my "autoloading" section in composer.json or I could also add it in PHP using Autoloader9287463497853476 object but those solutions (ideologically equal) doesn't interest me.
How can I force my Composer to generate autoloading for my repository code also?
If you add your package using the repository section of composer.json, i would sugest you to include there the code for autoload, as i used here:
"repositories": [
{
{
"type": "package",
"package": {
"name": "brand/name",
"type": "library",
"version": "1.0.0",
"dist": {
"url": "file:///path-to-file.zip",
"type": "zip",
"reference": "XXXX"
},
"autoload": {"psr-0": { "Name\\Space\\": "dest-folder" }
},
}
}
I hope it helps.

Categories