PHP - Namespace not found, autoload project with composer - php

I'm quite new to autoloading and namespaces, and attempting to add namespaces to my MVC project.
So In my composer I added:
"autoload": {
"psr-0": {
"prj\\app\\": "app/",
"prj\\app\\controller\\": "app/controller/",
"prj\\app\\classes\\": "app/classes/"
}
}
And then updated composer and ran the autodump command.
I then went back to my app to try use one of these namespaces however I just get the following back:
use \app\classes\engine; // use statement I tried
Fatal error: Uncaught Error: Class 'app\classes\engine' not found in C:\inetpub\wwwroot\web\index.php:87 Stack trace: #0 {main} thrown in C:\inetpub\wwwroot\web\index.php on line 87
I'm not sure why it is unable to find the class using the namespace, here's my entire folder structure if it may be of any use:
PRJ
├───app
│ ├───classes
│ └───controller
├───web
│ └───index.php
├───vendor
│ ├───bin
│ ├───composer
│ ├───...
└───view
├───bootstrap
└───default
/app stores the logic such as controllers and classes.
/web is the web root - the index.php is the page which visitors see and also everything is handled through here.
/vendor is the composer directory where my dependencies are stored.

There are several things going wrong. First of all, you're adding duplicate namespaces in your composer.json:
The lines:
"prj\\app\\controller\\": "app/controller/",
"prj\\app\\classes\\": "app/classes/"
Are unnecessary as they are already covered by:
"prj\\app\\": "app/",
As long as the directory under app matches the name of the namespace you use, there is no need to define it explicitly. So you can just add:
"autoload": {
"psr-0": {
"prj\\app\\": "app/"
}
}
Secondly, your use statement seems off, you're trying:
use \app\classes\engine;
The leading slash should not be neccessary here, if you are already in the same namespace. Additionally, you're autoloading your namespaces as prj\app and not app, so you're missing the prj bit. It should look something like this (when this is a file inside the app folder):
<?php
namespace prj\app\controller;
use prj\app\classes\engine;
class MyController
{
/**
* #var engine
*/
private $engine;
public function __construct()
{
// This should now work since engine should be autoloaded
$this->engine = new engine();
}
}
Also take a look at the PSR-0 naming conventions as you don't seem to follow them. Class names and namespace folders should be capitalized, like App\Classes\Engine instead of app\classes\engine.

Related

PSR4 Composer Autoloading namespaces

I've been having a little play around with some Composer autoloading and i'm getting some issues so the directory structure is
index.php
app/
helpers/
router.php
vendor/
composer/
/*usual files*/
autoload.php
Inside my composer.json I have the following
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
Inside my index.php I have
<?php
// Autoload our namespaces
require __DIR__.'/vendor/autoload.php';
use App\Helpers\Router;
$route = new Router;
Getting the following error
Fatal error: Class 'App\Helpers\Router' not found in /var/www/public/index.php on line 6
I have tried a few different things to try and get it working but i'm unsure where i'm going wrong. This is my first time looking into autoloading using Composer outside of a framework so would appreciate any guidance.
PSR-4 is case sensitive. The structure has to be app/Helpers/Router.php or better App with capital A.
All class names MUST be referenced in a case-sensitive fashion.
The subdirectory name MUST match the case of the sub-namespace names.
The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.
http://www.php-fig.org/psr/psr-4/

Why specify the namespace when using psr-4 autoloading with Composer?

I'm a little confused with how I should be using psr-4 autoloading in Composer. Let's say I've got a folder structure like this:
/
|- Core/
| - Router.php
|- App/
| - Models
| User.php
|- composer.json
Basically, in the project root: composer.json; a Core folder containing a Router php class; an App folder containing a Models folder that contains a User class.
The Router class looks like this:
<?php
namespace Core;
class Router {
}
and the Users class looks like this:
<?php
namespace App\Models;
class User {
}
So I can autoload these classes using the Composer psr-4 autoloader, I can do this in composer.json:
{
"autoload": {
"psr-4": {
"Core\\": "Core",
"App\\Models\\": "App/Models"
}
}
}
So I can then use the classes without requiring them (after running composer dump-autoload) like this:
$router = new Core\Router();
$user = new App\Models\User();
which works with no problems.
However, I can also do this in composer.json:
{
"autoload": {
"psr-4": {
"": ""
}
}
}
which, according to the documentation is a fallback directory where any namespace can be, relative to the root. So by having this "empty" entry in the composer autoloader, which I believe says "starting in the root, look in any directory for a class in any namespace", I can autoload any of my classes if I follow the correct folder naming / namespace structure.
So my question is, why would I do the former if the latter works and is much simpler? Is it a performance thing? Or is there another reason?
Why shouldn't you always do "psr-4": {"": ""}?
Reason 1: It cost performance. The definition says that for EVERY class that needs autoloading, Composer should look into the root directory. These classes are not only the ones in your package, but ALL other classes as well.
Composer tries to optimizes this effort a bit by remembering fruitless searches, but this only pays if you load another class with the same prefix.
Reason 2: The essence of PSR-4 is that you don't have to have the whole namespace path mapped to a directory path. Assuming that you have a package which deals with a very specific group of classes like \Vendor\Template\Escaping\Output\*, and nothing else (having small packages makes it easier to reuse them without adding too much code), you can have them in src/Vendor/Template/Escaping/Output/AnyClass.php and define
"psr-4": {
"\\Vendor\\Template\\Escaping\\Output\\": "src/Vendor/Template/Escaping/Output/"
}
You can also put the class into src/AnyClass.php and define
"psr-4": {
"\\Vendor\\Template\\Escaping\\Output\\": "src/"
}
And this shortens the directory path significantly, marginally improving speed (I think - have no figures though), but mostly improving developing the thing due to less opening of empty folders.
Having both a Core namespace and an App namespace in the same package makes me suspicious: Why isn't there one package for each of these?
Usually you only have one folder for your own project when using composer. Then you only need to specify one namespace.
Consider that you would rearrange your file structure to
/
|- lib/
| - Core/
| - Router.php
| - App/
| - Models
| User.php
|- composer.json
and change your composer.json to
{
"autoload": {
"psr-4": {
"MyApp\\": "lib/"
}
}
}
then you only have one specified namespace and you dont need to add any further namespaces. You can call your classes like this:
$router = new \MyApp\Core\Router;
$user = new \MyApp\App\Models\User;
or like this:
namespace MyApp;
$router = new Core\Router;
$user = new App\Models\User;
PSR-4 is a standard that translates namespaces INTO physical directories.

override symfony 3rd party bundle resource

Symfony 2.7.5
Problem
I'm trying to override a class file in a 3rd party vendor file so that I can extend the functionality a little. This file isn't part of a service, or a controller.
Rather confusingly (I'm not sure why this worked), I've over-ridden part of FOSUserBundle by creating the directory structure as follows:
src
│
│
└───FOS
│
│
├───Model
│ User.php
│ Group.php
│ ...
This has allowed me to change the visibility of some of the FOS user class members. And it allowed me to do with this without any other config. Not even a child-class set-up.
Tried so far
I've read this, but it only talks about basic resources.
I've also read this, but it seems to only work for controllers or anything in the Resources folder.
Ive tried setting up a child bundle, similar to the above link but to no avail.
I cant think of a way of successfully using inheritance in this instance to accomplish what I need to do. As the file in question isn't referred to outside of the vendor bundle itself.
Question(edited)
Is there a way that I could extend/override a bundle class file that lays in a directory structure such as this? I don't need to touch much of the bundle, but I really need to get at this file. This is an extract from APYDataGridBundle.
vendor
│
│
└───APY
│
│
├───datagrid-bundle
│
│
├───Grid
|
| .. grid.php <-- this file
You can do this by using Composer’s autoload feature.
Composer, as you might already know, will automatically load the PHP file containing a class whenever you first call that class. So you don’t have to use require statements all the time.
To achieve what you’re looking for, you can “trick” the autoloader into loading a different file whenever APY\DataGridBundle\Grid\Grid is called.
How to do that
In your composer.json there should be a section like this:
"autoload": {
"psr-4": {
"": "src/"
}
},
This will tell Composer that your classes are in the src/ directory. But since APYDataGridBundle already defined a more specific namespace path in their composer.json, the autoloader will never look at your files.
If you change the block like this:
"autoload": {
"psr-4": {
"": "src/"
},
"classmap": ["src/APY/DataGridBundle/Grid/Grid.php"]
},
Composer will analyze the file Grid.php and look for classes inside it. It will then create a class map so that whenever the class inside it is called, it will know which file to load before even checking the PSR-0 or PSR-4 namespace paths.
So basically, you’re telling it the path to the file at a higher priority so it gets loaded first.
Of course, you don’t even have to match the namespace with your directory structure, you could also do something like src/replacements/grid.php if that’s better for you. Just make sure that the namespace declaration inside the file is the right one.
Edit: After changing composer.json you have to execute composer dump-autoload so the new autoload files get generated based on the new config.
In general, you will only be able to override a class in a bundle in one of two cases:
If symfony has defined a generalized way to look for these classes so that you can override them just by putting a new class in a directory. This happens with controllers, for example.
If the bundle loads this class by using a parameter in its service definition. In this case you can override it by setting this parameter in your config file
In your case you are lucky and this bundle loads this class through a parameter defined in its services.xml file, so you can provide your own class by overriding this parameter in your config.yml file:
# app/config/config.yml
parameters:
grid.class: your own class, for example AppBundle/Grid/Grid
If in the future you find that you need to override a class and it cannot be done through one of these methods, you can always create your own fork of the bundle, though this is a pain and hard to maintain, so I would only recommend it if you find no other alternative

Custom composer namespace doesn't find class

I'm trying to use my custom namespace for my personal classes.
The directory structure is (as usual):
my_project/
- src/
|- myComponent.class.php
\- myWrapper.class.php
- vendor
|- OtherLibrary
\- Symfony
- composer.json
- index.php
in my composer.json I specify my own namespace with:
"autoload": {
"psr-0": {
"my_namespace\\": "src/"
}
}`
then in my PHP code I have something like:
myComponent.class.php
namespace my_namespace;
class myComponent
{
.... code
}
index.php
namespace my_namespace;
require_once __DIR__.'/vendor/autoload.php';
$component = new myComponent();
Running this I get a:
Fatal error: Class 'my_namespace\myComponent' not found in /path_to_root/my_project/index.php on line 5
while...
I would expect myComponent to be searched under my_project/src/, as specified in the composer.json and as defined into vendor/composer/autoload_namespaces.php ('my_namespace\\' => array($baseDir . '/src')).
I would expect to directly call my custom myComponent, when I define the namespace to my own namespace. Am I wrong?
What's wrong in my code and my assumptions?
How should I fix it?
You found the errors yourself, but here is a quick collection of what the useful autoload directives in Composer do:
PSR-0 converts the class name into a path name (underscores and backslashes from namespaces are converted into a directory separator), adds ".php" at the end, and tries to find this file in the path that you have given in the composer.json file. A class myNamespace\myClass and "psr-0":{"myNamespace\\": "src"} will try to load src/myNamespace/myClass.php.
PSR-4 only works with namespaces. It removed the namespace prefix given in composer.json from the full class name, and the remainder is converted into a path, ".php" added at the end, and searched in the path given. A class myNamespace\myClass and "psr-4":{"myNamespace\\": "src"} will try to load src/myClass.php.
Classmap autoloading will work by scanning all the files for classes, interfaces and traits (everything that can be autoloaded), and compiles an array map of it. It works with any file name schema and any directory layout, but try to avoid it because it will need an update to the map every time you add a new class. Also, it takes time to scan the files while installing, and it takes some CPU and memory to load and hold that map.

Class not found with composer autoload

I have my file structure as
/
/app/
/app/logs/
/app/templates/
/app/index.php
/public_html/
/public_html/.htaccess
/public_html/index.php
/vendor
/vendor/(all vendor here)
My vhost is pointing to /public_html
in app/Index.php
namespace App;
class Index {}
Composer.json
"autoload":
{
"psr-0":
{
"App\\": "app/"
}
}
Yet it is still showing as ( ! ) Fatal error: Class 'App\Index' not found in C:\wamp\www\project\public_html\index.php on line 34
Line 34:
new \App\Index();
Using Slimframework as well if that matters, can't think what is wrong
Since you were using the PSR-0 standard, PHP was looking for the file app/App/Index.php, which does not exist. Note that in PSR-0, you define a base directory (app in your case) where the mapped namespace (App) can be found. However, the file structure within that base directory should exactly match the fully qualified class names. So class App\FooBar should be in the file app/App/FooBar.php. Note that app is the base directory, and App is the directory that contains all subdirectories and PHP files for that namespace.
Since this is not the case in your application (and also because PSR-0 has been deprecated), you should (as you already did) use PSR-4, the new autoloading standard, instead. In PSR-4, you can directly map a certain namespace to a certain directory. In your case, you have mapped the App namespace to the app directory, so that PHP will open the app/Index.php file if you need to use the App\Index class.

Categories