Composer with PSR-4 autoloading: classes from namespace not loading - php

I have the follow project structure:
- root
|- src <- Application specifc source
|- [...]
|- tests
|- [...]
|- Vendor
|- myusername <- shared packages for all projects
|- src
|- MyNamespace
|- File.php
|- autoload.php
|- test.php
|- composer.json
composer.json already have a PSR-4 entry:
"autoload": {
"psr-4": {
"MyNamespace\\":"myusername/src"
}
}
/Vendor/test.php
<?php
require 'autoload.php';
$file = new MyNamespace\File();
echo $file->isDone();
Vendor/myusername/src/MyNamespace/File.php
<?php
namespace MyNamespace;
class File
{
public function isDone()
{
return 'Done!';
}
}
But I always get fatal error Fatal error: Class 'MyNamespace\File' not found in [...]
Are the composer settings or file structure correct? What I can do?
EDIT 1:
I can load external vendors fine

There are 2 things wrong with your code.
You are using PSR-4 wrong.
They removed the need to embed the namespace in your folders, making a cleaner footprint in your project folder.
PSR-0
vendor/<VendorName>/<ProjectName>/src/<NamespaceVendor>/<NamespaceProject>/File.php
PSR-4 (See that they removed the namespaces folders? Because you already reference that in composer.json
vendor/<VendorName>/<ProjectName>/src/File.php
So in your case it would be:
Vendor/myusername/src/File.php
Your composer.json is invalid
"MyNamespace\\":"myusername/src"
Doesn't include the full path to the directory with your project's code. It should be like this:
"autoload": {
"psr-4": {
"MyNamespace\\": "Vendor/myusername/src"
}
}
but the best way to store your files would be outside the vendor directory, as that is used by automatically downloaded libraries, instead choose a different "development" directory:
"autoload": {
"psr-4": {
"MyUsername\\MyProject\\": "src/myusername/myproject/src"
}
}
Thanks to Sven in the comments.

Related

Why this Simple case of psr-4 not working with composer

I am trying to understand how psr-4 works using composer. These are the contents of my composer.json file
{
"autoload": {
"psr-4": {
"Vehicle\\Car\\":"src/"
}
}
}
The Folder Structure is given below (please note I am using windows 10, so directory name casing should not matter I think)
The folder where I have created 'vendor' folder is inside D:\temp\composer_test
D:\
temp\
composer_test\
test.php
composer.json
composer.lock
vendor\
vehicle\
car\
src\
Tire.php
Contents of test.php
require __DIR__ . '/vendor/autoload.php';
$tire = new Vehicle\Car\Tire();
Contents of Tire.php
<?php
namespace Vehicle\Car;
class Tire {
public function __construct()
{
echo "initialize Tire\n";
}
}
But when I run the code (snapshot below) . I see error
D:\temp\composer_test>php test.php
PHP Fatal error: Uncaught Error: Class 'Vehicle\Car\Tire' not found in D:\temp\composer_test\test.php:3
Stack trace:
#0 {main}
thrown in D:\temp\composer_test\test.php on line 3
I Don't know what is wrong I am doing. Problem is when I install any other standard package I can use it successfully which seems like using the same format
Your composer.json should be:
{
"autoload": {
"psr-4": {
"Vehicle\\":"src/vehicle/"
}
}
}
Your directory structure should be:
D:\
temp\
composer_test\
test.php
composer.json
composer.lock
src\
vehicle\
car\
Tire.php
Make sure to run composer dump-autoload to re-general the autoload files.
Also your namespace does not make much sense, naming your namespace Vehicle would mean that you only have vehicles in your repo. Usually you would name your namespace based on your project name, could be your brand or your username, or something more generic such as App.
So this would make more sense:
{
"autoload": {
"psr-4": {
"App\\":"src/"
}
}
}
then you have:
D:\
temp\
composer_test\
test.php
composer.json
composer.lock
src\
vehicle\
car\
Tire.php
plane\
...
and test.php
require __DIR__ . '/vendor/autoload.php';
$tire = new App\Vehicle\Car\Tire();

Composer autoload - load class from parent directory

I'm currently working on a Laravel project that needs to access classes from its parent directory.
composer.json > PSR-4:
"psr-4": {
...
"ModuleA\\": "../ModuleA/baseObjects",
"ModuleB\\": "../ModuleB/baseObjects"
}
Example file structure:
/var/www
+- /xxx (project)
+- /ModuleA
+- /baseObjects
- configClass.inc
+- /ModuleB
+- /baseObjects
- configClass.inc
+- /laravel
- composer.json
I run composer dump-autoload but the project still can't find ModuleA\configClass neither ModuleB\configClass.
Furthermore, inside my autoload_psr4.php, the above gets referenced as following:
'MobuleA\\' => array($baseDir . '/../MobuleA/baseObjects')
'MobuleB\\' => array($baseDir . '/../MobuleB/baseObjects')
PSR-4 requires the loaded files to have a namespaced class, and the namespace structure has to match the directory structure, relative to the "base directory" defined in the configuration (http://www.php-fig.org/psr/psr-4/).
So, in file /var/xxx/ModuleA/baseObjects/configClass.inc should be the class
namespace ModuleA\baseObjects;
class configClass {
...
}
Then in var/www/laravel/composer.json you could have
"psr-4": {
"App\\": "app/",
"ModuleA\\": "../ModuleA"
}
Which means: "directory ../ModuleA should be the root for ModuleA namespace, then follow subnamespaces by matching subdirectories from there".
Use classmap autoload will solve this problem.
{
...
"autoload": {
"classmap": ["ModuleA/", "ModuleB/"]
}
}
It could be used with PSR-4
{
...
"autoload": {
"psr-4": {
"Acme\\": "src/Acme/"
},
"classmap": ["ModuleA/", "ModuleB/"]
}
}
Ref: https://getcomposer.org/doc/04-schema.md#classmap
The problem you're experiencing is not related to parent directories. In fact, your Composer.json autoload configuration is correct for your directory structure.
The problem is the .inc file extension, which is incompatible with the PSR-4 specification. More info here: How To Make Composer (PSR-4) To Work With ".class.php" Extension?
If you cannot update your source code to match the PSR-4 spec, you can use Class Mapping:
The classmap references are all combined, during install/update, into a single key => value array which may be found in the generated file vendor/composer/autoload_classmap.php. This map is built by scanning for classes in all .php and .inc files in the given directories/files.
You can use the classmap generation support to define autoloading for all libraries that do not follow PSR-0/4. To configure this you specify all directories or files to search for classes.
So your config might look like:
"autoload": {
"classmap": [
"../ModuleA/baseObjects",
"../ModuleB/baseObjects"
]
}
Remember, if you use class mapping, you'll need to run composer dump-autoload any time you change composer.json, add a class, modify a class' name/filename/path, etc.
Extra: as pointed out by #alepeino, using autoloader optimization will generate a class map from any PSR-0 and PSR-4 autoload definitions, using the same underlying code that classmap autoload uses. This will "allow" you to use PSR-4 autoloader and the .inc extension. This will still require you to run composer dump-autoload --optimize every time you make a file change, though, just like classmap.
Best recommendation: change your source code to follow PSR-4 specifications, and use the .php extension.
Next best if you can't do that: use classmap for autoloading.
try it:
"psr-4": {
...
"ModuleA\\": "ModuleA/baseObjects",
"ModuleB\\": "ModuleB/baseObjects"
}
According to this answer you can add it in the index.php file:
$loader = require 'vendor/autoload.php';
$loader->add('Namespace\\Somewhere\\Else\\', __DIR__);
$loader->add('Namespace\\Somewhere\\Else2\\', '/var/www/html/xxx');

PhpStorm autocompletes my class but I get Class not found in Laravel 5

I've added my classes normally in Laravel below app directory. Today, when I added an API client that has a directory structure I can't make a class recognized in Laravel PSR-4 default structure.
My API client directory structure is:
app -- MySubdirectory -- Partners +
|- Partner1
|- Partner2 +
|- Includes -- (other subdirectories)
|- Version -- (other subdirectories)
|- ApiClient.php
Now, I've created a model MyModel that resides below app.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class MyModel extends Model
{
/**
*/
protected $api;
/**
* Returns MyModel API Client connected to the API.
*/
public function __construct()
{
parent::__construct();
// ApiClient is not recognized thought PhpStorm do recognized.
$this->api = \ApiClient::factory(PROTOCOL_JSON, VERSION_DEFAULT);
$credentials = json_decode(OtherModel::where('name', 'some_name')->configuration);
$this->api->setConnectId($credentials['connect_id']);
$this->api->setSecretKey($credentials['secret_key']);
}
}
Well, when I've wrote MyModel in line
$this->api = \ApiClient::factory(PROTOCOL_JSON, VERSION_DEFAULT);
PhpStorm suggested that class, apparently recognizing an autoloaded class.
But when I run the script I get error:
FatalThrowableError in MyModel.php line 20:
Class 'ApiClient' not found
Ok, maybe some problem with composer autoload - even until now other classes that I've been created where automatically recognized by PHP.
So, I ran composer dump-autoload -o and verified autoload_classmap.php file and really all other subdirectories/classes below app directory are recognized, minus MySubdirectory and other subdirectories and classes below MySubdirectory.
My composer.json file has the basic autoload section:
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/"
}
},
So, why running composer dump-autoload can't catch MySubdirectory and other subdirectories and classes below that?
And why PhpStorm does catch ApiClient.php?

Making Legacy Application PSR-4 Compatible

I have a question with regards to refactoring a legacy PHP application to be compatible with PHP's PSR-4 standards. I have some classes located at app/classes/ folder which are not yet namespaced properly and I want them to be autoloaded directly when I call composer's vendor/autoload.php. I added a name space according to \<NamespaceName>(\<SubNamespaceNames>)*\<ClassName> and I have tried creating a vendor/myapp/classes/src/ directory under the vendor folder of composer, and executed a dump-autoload command but to no avail. The class doesn't get loaded up and composer can't figure out where to find it. Any pointers on how to do this?
Thanks,
Jan
Thing/s to Note:
-> I don't want to upload the source code to any repository that can be publicly searchable like Packagist as the code is for internal use only.
EDIT:
Here is my composer.json:
...
"autoload":{
"psr-4": {
"Myapp\\" : "Myapp"
}
},
...
But now the structure looks like:
->Myapp
-->Classes
--->Module1
---->submodule.php
--->Module2
---->submodule2.php
--->Module3
---->submodule3.php
--->Config
---->config.db.php
->vendor
-->autoload.php
Here are my issues/questions:
When I try to load submodule.php, which in turn would load submodule2.php and submodule3.php, it would tell me that submodule3.php is not found. The namespace of submodule3.php is Myapp\Classes\Module3 but it says its not found.
I want to include forcibly, config.db.php, on every call of autoload.php
I now figured it out. But the source files will not reside in the /vendor folder, which is okay for my case. If you want to make your files be autoloaded automatically no matter which folder, just add it to the psr-4 block in the composer.json, or create it if it's not yet there. In my composer.json, I have this block on the top level object, which adds the folder I want to get autoloaded and includes also the specific file I want to include, like a config file of some sorts:
...
"autoload":{
"files": [
"somefolder/somefile.php"
],
"psr-4": {
"MyApp\\" : "MyApp"
}
},
...
Which simply means that composer should autoload files residing in the MyApp directory which will also have a namespace of MyApp.
So my folder structure looks like this now:
->MyApp
-->Classes
--->MyClass1.php --> namespace MyApp\Classes classname: MyClass1
-->Components
--->MyClass2.php --> namespace MyApp\Classes classname: MyClass2
->somefolder
-->somefile.php
->vendor
-->autoload.php
->index.php
So if I want to include the file MyClass1.php in index.php, I will just add it like:
include_once( __DIR__ . "/vendor/autoload.php");
use \MyApp\Classes\MyClass1;
$foo = new MyClass1();
$foo->bar();
Hope it helps.
You could approach this by creating your own composer package, adding it to your private git repository, and adding the package to your project's composer.json file:
"require": {
"{your-vendor}/{package-name}": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "git#bitbucket.org:{your-vendor}/{package-name}.git"
}
],
Once this is done, run composer update.

Composer - Autoload and PSR-0 vs PSR-4

I'm beginning to study Composer and am developing a system where I separate the files core application files, as follows:
/root
|-- /src
|-- /App
|-- /DBConfig
|-- /Controller
|-- /Model
|-- /Core
|-- /Helper
|-- /Controller
|-- /Model
So, to set this setting in composer.json file and get access to all classes both /App much /Core would be this way?
"autoload" : {
"psr-X" : {
"App\\" : "/src",
"Core\\" : "/src"
}
}
Or is there a more correct way?
I have also read about PSR-0 vs PSR-4 and I am still somewhat in doubt which one to use. In my case what should I implement, PSR-0 or PSR-4?
You didn't need 2 entries just one for the main namespace so something like this for PSR-4:
"autoload" : {
"psr-4" : {
"MyApp\\" : "/src" }
}
As long as everything in src/ uses the same namespace that's all you'll need. Just let the autoloader do it's job.
As to which to use I'd go with PSR-4 because at some point it is expected that PSR-0 will be deprecated and as PSR-4 is made to be backwards compatible minus some warts for older legacy programs there isn't really a difference except of you start using some of it newer features

Categories