I'm learning how to make a composer package. So far I've done this:
composer.json
{
"name": "Iv/MyPackage",
"autoload": {
"psr-4": {
"Iv\\MyPackage\\": "src/"
}
},
"require": {
"guzzlehttp/guzzle": "~6.0"
},
"require-dev": {
"phpunit/phpunit": "4.*",
"guzzlehttp/guzzle": "~6.0"
},
"autoload-dev": {
"psr-4": {
"Iv\\MyPackage\\Tests\\": "tests/"
}
}
}
And I have a class under the namespace: namespace Iv\MyPackage\Api; called Consumer and in its __construct method it has this:
public function __construct(array $credentials)
{
$this->client = new Client();
$this->credentials = $credentials;
}
And on the top of that class I have use GuzzleHttp\Client;.
The error I'm getting is:
Fatal error: Class 'GuzzleHttp\Client' not found in path\to\package\Iv\MyPackage\src\Api\Consumer.php on line 27 when I do:
$package = new Iv\MyPackage\Api\Consumer(['user', 'password']);
$query = $api->prepare('/api-endpoint', 'GET');
Edit:
This is what my Consumer class looks like:
<?php
namespace Iv\MyPackage\Api;
use GuzzleHttp\Client;
class Consumer
{
private $credentials = [];
public function __construct(array $credentials)
{
$this->client = new Client();
$this->credentials = $credentials;
}
...
}
Also I'm using PhpStorm, which tells me that I have the GuzzleHttp package, because it autoimports it for me when I type Client() and press ALT + ENTER. Which means I have ran composer install/update.
Edit 2:
I have a file - index.php which has the following:
<?php
include('vendor/autoload.php');
$api = new Iv\MyPackage\Api\Consumer(['user', 'password']);
$query = $api->prepare('/endpoint', 'GET');
var_dump($api->execute($query));
The folder structure is as it follows:
-Iv/
--MyPackage/
---src/
----Api/
----Exceptions/
----vendor/
----tests/
----otherfiles (composer.json, phpunit.xml, etc)
-vendor/
-composer.json
-index.php (I mentioned above)
Edit 3:
The content of my vendor/composer/autoload_psr4.php:
<?php
// autoload_psr4.php #generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Iv\\MyPackage\\Tests\\' => array($baseDir . '/tests'),
'Iv\\MyPackage\\' => array($baseDir . '/src'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
);
Edit 4:
In the folder where is index.php I have this composer:
{
"name": "Test MyPackage",
"autoload": {
"psr-4": {"Iv\\MyPackage\\": "Iv/MyPackage/src"}
}
}
^^ I saw that on SO in a topic that OP was asking how to test his package before uploading it to packagist/git. I can load the Consumer class, no errors about it, but every dependency is not found. (GuzzleHttp\Client, Symfony\Component\Yaml\Parser, etc)
In the composer.json in the main directory you are missing a reference to your package. You are just autoloading it, but you are not autoloading all of its dependencies.
To do this you have to add a section like this in your composer.json in the main directory:
"repositories": [
{
"url": "path/to/your/package",
"type": "path"
}
]
and add it to the require section, some thing like:
"require": {
"Iv/MyPackage" : "dev-master"
}
Alternatively, you could just move your dependencies from the composer.json inside your package to the one in the main directory
I think you pretty much mixed everything up a bit, but it is solvable.
What I see is that you have an odd directory structure: You have Iv/MyPackage/src/... in your main project (i.e. on the same level there is a file composer.json).
IF you want to use your "Iv/MyPackage" as a Composer package in your main project, you have to add it as a dependency. This means that your package name must appear inside a "require"-Section in the main composer.json file. Currently it is not. Instead of this, you manually added autoloading for the path your package is currently in - without telling Composer that this should be treated as a package, and that it has dependencies that should also be downloaded.
You added the dependencies of your package inside the directory of it, but this is not how Composer works.
To fix it, you have to do two simple things:
Add your package as a dependency in your main composer.json.
If your package is currently hosted in a private repository, you have to add it's URL to the repositories key in composer.json.
The first one is just this:
{
"name": "Test MyPackage",
"require": {
"Iv/MyPackage": "dev-master"
},
"autoload": {
}
}
Remove the autoloading for your package - it is contained inside that package already. If you want Composer to autoload your main project, add that autoloading here (and I suggest you do, because you'd probably cannot write a better autoloader yourself).
Second thing is adding the repository:
{
"name": "Test MyPackage",
"repositories": [
{
"type": "vcs",
"url": "your repo url, either http or ssh"
}
],
"require": {
"Iv/MyPackage": "dev-master"
},
"autoload": {
}
}
This tells Composer to look into that repository and see if it can find something. Everything found is added to the collection of known packages (with the default source being Packagist) and used to select the best version matches.
Some corrections you should apply:
Package names should be lower case, so it's better to use iv/mypackage.
If you give a name in your main project, it has to follow the form <vendor>/<package>, not be some random string with a name. Composer will not deny working, but once you get there you will benefit from more of the available infrastructure in the Composer area, and correct names will help you. Otherwise, just leave it out for the moment.
This should set you up and running. I probably can answer more details you didn't even ask, but suggest you read about Composer on the documentation page or here on Stackoverflow, because the common problems usually have already been solved.
Related
i'm a newbie at composer, so just bear with me,
So i have a package, which i'm loading from local folder, and while using it, i get the following error:
Fatal error: Class 'mypkg\Layer\EasyCPT' not found in
C:\xampp\htdocs\testwp\app\Cpt\location.php on line 5
My Composer.json:
"repositories": [
{
"type":"vcs",
"url":"C:/xampp/htdocs/mypkg"
}
],
"require": {
"php": ">=7.0.0",
"mypkg/particles": "master"
},
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
Package's Composer:
"minimum-stability": "dev",
"authors": [
{
"name": "Talha Abrar",
"email": "talha#themegeek.io"
}
],
"autoload": {
"psr-4": {
"Mypkg\\": "particles/"
}
}
Psr 4:
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Mypkg\\' => array($vendorDir . '/Mypkg/particles/particles'),
'App\\' => array($baseDir . '/app'),
);
how i am using it:
<?php
namespace App\Cpt;
use Mypkg\Layer\EasyCPT;
class Location extends EasyCPT{
protected $plural = 'locations';
}
Main auto-loading file:
require __DIR__.'/vendor/autoload.php';
use App\Init\EasyWP;
new EasyWP();
You use the namespace as:
use Particles\Layer\EasyCPT;
but in autoload section defining as:
"Mypkg\\": "particles/"
which is not consistent.
You should replace Mypkg with the right namespace name, e.g.
"autoload": {
"psr-4": {
"Particles\\": "particles/"
}
}
So requesting Particles\Layer\EasyCPT namespace will look for class in particles/Layer/EasyCPT.php file.
As per Composer's PSR-4 documentation:
Under the psr-4 key you define a mapping from namespaces to paths, relative to the package root. When autoloading a class like Foo\\Bar\\Baz a namespace prefix Foo\\ pointing to a directory src/ means that the autoloader will look for a file named src/Bar/Baz.php and include it if present. Note that as opposed to the older PSR-0 style, the prefix (Foo\\) is not present in the file path.
If your project doesn't follow PSR-4 approach, use classmap instead to scan for all of your classes, e.g.
"autoload": {
"classmap": ["particles/"],
"exclude-from-classmap": ["/tests/"]
}
To regenerate autoload manually, run:
composer dump-autoload -o
and check autoload files in vendor/composer/ whether the references to classes were generated correctly.
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.
I'm developing my first Larvel-based package. I want to include the Socialite package, so I put it like this in my composer.json file
"require": {
"laravel/socialite": "^2.0"
},
Now, how do I include the provider and the alias as you'd normally do in /config/app.php ?
I think by now I've read every stackoverflow there is about this matter, but nothing seems to work.
This is my package's serviceprovider:
public function boot()
{
include __DIR__.'/routes.php';
$this->app->register('Laravel\Socialite\SocialiteServiceProvider');
$this->app->alias('Laravel\Socialite\Facades\Socialite', 'Socialite');
$this->loadViewsFrom(__DIR__.'/../views', 'package-name');
$this->loadTranslationsFrom(__DIR__.'/../lang', 'package-name');
$this->publishes([
__DIR__.'/../views' => resource_path('views/vendor/package-name'),
]);
$this->publishes([
__DIR__.'/../database/migrations/' => database_path('migrations')
], 'migrations');
}
Result:
Class 'Laravel\Socialite\SocialiteServiceProvider' not found
UPDATE
"psr-4": {
"App\\": "app/",
"Rubenwouters\\CrmLauncher\\": "packages/rubenwouters/crm-launcher/src/"
}
In your package's service provider in register method you can do:
public function boot() {
$this->app->register(ClassOfScialiteServiceProvider);
$this->app->alias(FacedeClass, 'Alias');
}
EDIT
But first of all... To add your package in the right way (to the vendor that you can adding a requirments) you have to some how add it to the main composer.json - required list. You can do it with one of below:
1
Adding you package to the official composer repository (packagist)
2
Make your own composer repository like Satis
3
The easiest way is to add your git repo dependanci inside composer.json like:
"repositories": [
{
"type": "vcs",
"url": "git#your_repo/crm-launcher.git"
},
],
"require": {
(...)
"rubenwouters/crm-launcher": "dev-master"
}
OR
or just move your dependencies ("laravel/socialite": "^2.0") to the main composer.json. :)
Register Laravel\Socialite\SocialiteServiceProvider::class, to Config\app.php as providers and also
Register 'Socialize' => Laravel\Socialite\Facades\Socialite::class, to Config\app.php as aliases.
May be this will solve your problem.
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.
I'm trying for a while to import an own bundle via composer but I got a few problems. I got following bundle:
<?php
namespace Platform\Bundle\PollBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class PlatformPollBundle extends Bundle
{
}
The bundle is in vendor/platform/pollbundle/.
In the "main" composer.json I definied the namespace for autoloading:
"autoload": {
"psr-0": {
"": "src/" ,
"Platform\\": "vendor/platform"
}
},
and in the composer.json from the bundle I definied:
{
"name" : "platform/pollbundle",
"type": "symfony-bundle",
"extra": {
"servicePath": ""
},
"autoload": {
"psr-0": {
"Platform\\Bundle\\PollBundle": ""
}
},
"target-dir": "pollbundle"
}
In the autoload_namespaces there is correctly following line:
'Platform\\' => array($vendorDir . '/platform'),
But I got the error:
Fatal error: Class 'Platform\Bundle\PollBundle\PlatformPollBundle' not found in ........Controller.php on line 13
I tried about 100 solutions but nothing works. Would be great if somebody can help me.
Bundles aren't loaded by composer, but instead are handled by the Symfony kernel itself. In the app directory, edit AppKernel.php like this:
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
...,
new Platform\Bundle\PollBundle\PlatformPollBundle()//<-- add this
);
}
In the app/autoload.php file, meanwhile, register the new namesepace. It used to be done through the $loader instance, by calling $loader->registerNameSpaces(), but now, you have to call a static method on the AnnotationRegistry class:
AnnotationRegistry::registerAutoloadNamespace('PollBundle', 'path/to/PollBundle');
A hacky fix I suggested, which is apparently what fixed it for you, would be to run php app/console generate:bundle in the console, to generate a new bundle with the same name, and then simply replace that bundle's directory (in src/) with your bundle.
It is wrong to define ANY autoloading in the main application for anything pointing into the vendor folder! That's what composer is for. Composer will read the autoload declaration for every package contained in there and add the appropriate autoloading automatically. There is no need to add this yourself.
And even if you have to use software that didn't yet add a composer.json file, the autoloading of only that package should go into the package definition block, not into the autoload definition.