I'm currently trying to write my first ever Composer package, but I can't seem to get it to work properly.
All I get is: Class 'Craftsman\EzServer' not found
when running the following code:
<?php
require_once __DIR__ . "/vendor/autoload.php";
echo Craftsman\EzServer::Hello();
After a far and wide search (literally a hundred-ish websites and guides), I only could get it to work by manually adding a classmap
to any project I try to use my package in.
This is my project's composer.json
:
{
"repositories": [
{
"type": "package",
"package": {
"name": "finlaydag33k/craftsman",
"version": "dev-master",
"type": "library",
"source": {
"url": "https://gitlab.com/FinlayDaG33k/craftsman.git",
"type": "git",
"reference": "master"
}
}
}
],
"require":{
"predis/predis": "dev-master",
"finlaydag33k/craftsman": "dev-master"
}
}
And the package's composer.json
:
{
"name": "finlaydag33k/craftsman",
"description": "",
"type": "library",
"license": "FinlayDaG33k License",
"authors": [
{
"name": "Aroop 'FinlayDaG33k' Roelofs",
"email": "me@finlaydag33k.nl"
}
],
"minimum-stability": "dev",
"require": {
"php": ">=7.0.0"
},
"autoload":{
"psr-4":{
"Craftsman\\": "src/"
}
}
}
This is the tree of my vendor dir:
vendor
|-finlaydag33k
|-craftsman
|-src
|-EzServer.php
And last but not least, my EzServer.php
:
<?php
namespace Craftsman;
class EzServer {
public function Hello(){
return "World!";
}
}
It downloads properly from the Git repo, so that doesn't seem to be an issue.
I've ran composer dump-autoload
a few hundred times while trying to try all sorts of stuff (change casing, add slashes, remove slashes, change namespace, move files in different folders etc. etc.) so that shouldn't be an issue either.
What am I doing wrong?
The first thing that immediately jumps into my eye: You are using a package
definition for your library.
Don't do this if you can avoid it. If you do it, you tell Composer to not look for a composer.json
in the target directory - and you have to give ALL information of this file again, including the autoloading section.
Composer can deal very easily with repositories itself. Simply use this integration:
"repositories": [
{
"type": "vcs",
"url": "https://gitlab.com/FinlayDaG33k/craftsman.git"
}
]
This will allow Composer to access the composer.json
of this repository and use all information given there: The package name, other required packages, and the autoloading definition. (Hint: the repository name and URL do not define any portion of the Composer package at all - the Composer package name is only defined by the "name"
property in the composer.json
file - so Composer will look for this file in the repo in all branches and tags and then see that the repo contains possible versions of your package "finlaydag33k/craftsman").
One more word about the autoloading, based on my comment: Autoloading has several steps that may fail due to case inconsistent names. If your code uses a class named Ezserver
, which hasn't been loaded before, the autoloader will go with this spelling and try to find the matching file. If the file is named EzServer.php
, and you are using a case-sensitive file system (e.g. Linux, MacOS), PHP will not find the file. If you are using a case-insensitive file (e.g. Windows), PHP WILL FIND THE FILE. But as soon as you deploy your code to case-sensitive environments, it will fail.
Also note that PHP will treat the class names case-insensitive. So if your code first uses the spelling EzServer
(and autoloading will succeed), and the second time it uses Ezserver
, IT WILL NOT FAIL the second time. The class is already loaded.
So the case-insensitive internal handling of PHP classnames combined with the autoloading that uses the codes classname literally and searches case-sensitive file systems is the source for subtle autoload failures.
A general hint that something like this is at work is if you can fix the problem by using the classmap autoloader. This will work around any possible case-sensitive spelling errors, but is is not recommended to use if you can avoid it.