Class not found using custom composer package - php

I have created a custom comoposer package and I want to use it on my project with this composer.json:
{
"name": "papillon/test",
"type": "library",
"version": "dev-master",
"require": {
"php": "^7.1.11"
},
"autoload": {
"psr-4": {
"Papillon\\Fountaine\\Eau\\": "src/Papillon/Fountaine/Eau/"
}
}
}
I compress it in zip. In the main project, I add a folder called repo, where I add de composer package zip. Then, I modify the composer.json of the main project like this:
{
"repositories": [
{
"type": "artifact",
"url": "var/main/repo"
}
],
"require": {
"papillon/test": "dev-master"
}
}
I execute composer update and the pakage is added to vendor folder; all seems to be going well... but if I want to test the package from the main project with this script:
<?php
require (__DIR__ . '/vendor/autoload.php');
use Papillon\Fountaine\Eau\FlowerClass;
echo FlowerClass::bloom();
It returns: PHP Fatal error: Uncaught Error: Class 'Papillon\Fountaine\Eau\FlowerClass' not found in .../test_package.php:6
Stack trace:
#0 {main}
thrown in .../test_package.php on line 6
I think that the package may not be recognized by the main project; maybe the package was improperly installed in the main project?

Debugging autoload can be very useful to catch errors. Take care with the route paths, the autoload tryed to find the classes files in a path with a lowercase folder when in the package composer.json the route was definded with that folder uppercase.

Related

Composer not autoloading classes in directory with same name

I am using composer to include a private package in my project that will includes some classes I will use to test against with PHPUnit. Most of the package is being autoloaded correctly and I can call the classes from my unit test, however any class that is named the same as the directory it is in is throwing a "Class not found" error.
The repository is conforming to psr-0 and is located at https://github.com/DeschutesDesignGroupLLC/IPS-Source
File structure example throwing error:
--src
----IPS
------DateTime
--------DateTime.php
Calling $date = new \IPS\DateTime; throws an error.
File structure example NOT throwing error:
--src
----IPS
------Http
--------Url.php
Calling $url = new \IPS\Http\Url; does not throw an error.
Composer.json of private package:
{
"name": "deschutesdesigngroupllc/ips",
"description": "Invision power board source files used to test against",
"homepage": "https://www.invisioncommunity.com",
"version": "4.3.6",
"autoload": {
"psr-0": {
"IPS\\": "src/"
}
},
"extra": {
"branch-alias": {
"dev-master": "4.3.6"
}
},
"require": {
"phpdocumentor/phpdocumentor": "dev-master"
}
}
In the first example, you want a file yet you give the path to it's parent. In the second, you again want a file, but this time supply the full path. Unsurprisingly, the first fails and the second succeeds.
It would appear that
$date = new \IPS\DateTime\DateTime;
is what you intended to ask for.

Composer plugin autoload depedency

So I'm working on a composer plugin that adds a custom command that can be run after an install or update.
I understand the autoloading configuration, and composer is autoloading all classes but it's also missing a file that just contains namespaced functions.
In my plugin composer.json I have the following:
{
"name": "myco/composer-s3-downloader",
"type": "composer-plugin",
"version": "0.1",
"require": {
"composer-plugin-api": "1.1.*",
"aws/aws-sdk-php": "3.20.*"
},
"autoload": {
"psr-4": {"MyCo\\Composer\\": "MyCo/"}
},
"extra": {
"class": "MyCo\\Composer\\S3Downloader"
}
}
My plugin classes load without a problem. All of the classes for my dependencies also load without a problem. So my plugin command code starts off just fine using the AWS SDK.
The problems comes here, when I try to instantiate an S3Client:
private function initClient() {
if (is_null($this->_s3Client)) {
$this->_s3Client = new \Aws\S3\S3Client([
"version" => "latest",
"region" => 'us-west-2',
"credentials" => [
"key" => $this->_creds['key'],
"secret" => $this->_creds['secret'],
]]);
}
}
I get the following error:
PHP Fatal error: Call to undefined function Aws\manifest()
in .../test/vendor/aws/aws-sdk-php/src/AwsClient.php on line 143
I can see the autoload config in the AWS composer.json and it's correct:
"autoload": {
"psr-4": {
"Aws\\": "src/"
},
"files": ["src/functions.php"]
}
The Aws\manifest function is declared in functions.php. And functions.php is then specified in vendor/composer/autoload_files.php. But near as I can tell that file isn't being loaded. So not all of my dependencies are actually available.
What step am I missing that forces the inclusion of autoload_files.php? I'm not doing a single include for anything in the vendor folder. I'm assuming that composer will handle that for me. But I guess I'm wrong.
So after posting an issue at the Composer Github repo, I did learn that the autoloader that runs during composer execution only includes classes. So if you do need to include loose functions, you'll have to manually run the full autoloader.
I added the following method to my Command class that is loaded by composer and defined in the extra section of the plugin's composer.json.
private function getIncludes() {
$vendorDir = $this->composerInstance->getConfig()->get('vendor-dir');
require $vendorDir . '/autoload.php';
}
I just call it in my plugin constructor and everything I need becomes available.

Issue with Composer's Autoloader

I'm starting work on a new mini-framework project, which I have in a local GIT repo on my machine. I've set up a test project that pulls in the local repo via Composer, however the autoloader isn't working as expected (Fatal Error: Class X not found errors). This is the first time I've used autoloading outside of what is automatically generated (e.g. when using an existing framework) and despite reading around, I can't seem to solve this.
Package
In an attempt to get this working, the package only contains a src directory with a single App.php class on top of the composer.json file in the root.
composer.json
{
"name": "myvendor/framework",
"description": "Framework Description",
"license": "MIT",
"authors": [
{
"name": "Joe Bloggs",
"email": "joe#email.com"
}
],
"autoload": {
"psr-0": {
"Framework": "src/"
}
}
}
Project
composer.json
{
"repositories": [
{
"type": "vcs",
"url" : "../Framework"
}
],
"require": {
"myvendor/framework": "dev-master"
}
}
This successfully clones the local repo and adds the code to the vendor directory.
The namespace is also successfully added to Composer's autoload_namespaces.php file like so;
vendor/composer/autoload_namespaces.php
'Framework' => array($vendorDir . '/myvendor/framework/src'),
When I attempt to load the App class however using the following code, I get the error;
web/index.php
<?php
require_once '../vendor/autoload.php';
$app = new \Framework\App();
You're using the psr-0 specification for the class loader. This means that the full namespace has to be visible in the file structure. The prefix only tells the autoloader were to look for this namespace.
So in your case, you configured that the "Framework" namespace is available in the "src/" directory. This means that the class \Framework\App should life in src/Framework/App.php. In your case, it exists in src/App.php. This means that the autoloader cannot find your class.
However, there is a class loader specification that does what you want: psr-4. This is also the recommended specification (psr-0 might be removed in the future). With PSR-4, the file structure only includes the namespaces after the configured prefixes. So when doing "psr-4": { "Framework\": "src/" }, a class called \Framework\App should life in src/App.php and a class called \Framework\Some\Special\App should life in src/Some/Special/App.php.

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