Autoloading and PSR-0/PSR-4 - php

I, like many others it seems, am having trouble getting my head around when and how to use auto-loading. I think I understand the concept of composer and PSR-0/PSR-4 and directory structure that this requires. But if I'm building my own project using my own MVC framework
Should I now put all my class files in a src folder inside the vendor folder
Do I then edit the composer autoload file?
Or do I still keep the original structure and just use my own autoloader?
-project
-app
-core /Main.php
-controllers /Controller.php
-models /User.php
/index.php
Since composer comes with its own autoloader which will load all dependenies that I may want to include with my project and if I'm not going to make my website into a distributed project do I even need my own namespacing? Why don't I just stick with includes/requires?
And finally, if I do adopt namespacing using a closure like this
function __autoload($class){
require $class .'.php';
});
do I need to require the autoload.php file in all pages where I load my classes as I do with the old include/require of the past. Is the above file correct? I think the namespace would be
<?php
namespace app\core; //for Main.php
namespace app\controllers; //for Controller.php
use app\controllers\Controller; //if I call the class

At the beginning of your main file (maybe /var/www/dist/index.php) you just include the Composer or whatever autoloader you use.
<?php
if ( file_exists( __DIR__.'/path/to/vendor/autoload.php' ) )
require __DIR__.'/path/to/vendor/autoload.php';
Then you can either add shortcuts to the classes that you use in a file by making use of the use statement
use MyNamespace\Controller\Index,
MyNamespace\Service\FooService;
use Zend\Foo\Bar;
use Symfony\Baz\Biz;
use Etc\Etc\Etc;
// Refers to \Zend\Foo\Bar
$bar = new Bar;
or just use the full path when instantiating a class
$bar = new \Zend\Foo\Bar;
To add your own namespace, just add it to your composer.json file
"autoload" : {
"psr-4" : {
"MyNamespace\\" : "src/"
}
}
and open your command line interface/terminal/console and add the namespace to the autoloader
# local install of Composer in your project
php composer.php dump-autoload
# or global install and `composer` is in your $PATH
composer dump-autoload
There is a rule:
One class per file
And if your project really is (PSR-0 deprecated or) PSR-4 compatible, then you don't use more than one namespace in one file. A class name gets (in the PSR-4 FIG standard) defined as
A fully qualified class name has the following form:
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
The contiguous sub-namespace names after the "namespace prefix" [note: \ is the "root") correspond to a subdirectory within a "base directory", in which the namespace separators represent directory separators.
with the following example:
+------------------------------+------------------+------------------------+---------------------------------------+
| Fully Qualified Class Name | Namespace Prefix | Base Directory | Resulting File Path |
+------------------------------+------------------+------------------------+---------------------------------------+
| \Acme\Log\Writer\File_Writer | Acme\Log\Writer | ./acme-log-writer/lib/ | ./acme-log-writer/lib/File_Writer.php |
+------------------------------+------------------+------------------------+---------------------------------------+

Related

symfony4.3 adding custom folder with classes

In my default symfony4 structure I want to add lib folder, where I have additional classes. So something like this:
-bin
-config
-lib
- Importer.php
...(other files with classes)
-public
-src
- Controller
- TestController.php
- Entity
- Form
...
...
But I cannot figure out how to later use my files (i.e.: Importer.php).
Let's say Importer.php has a single class Importer() inside. If I try to use it from TestController.php I get:
Attempted to load class "Importer" from namespace "lib". Did you
forget a "use" statement for another namespace?
TestController.php has
use Importer;
specified on top (autodetected by PhpStorm). I also tried adding namespace in my Importer.php file, for example:
namespace lib;
and then in TestController:
use lib\Importer;
But it produces the same result.
Lastly after reading about services, I tried adding the file to config/services.yaml
lib\:
resource: '../lib/Importer.php'
Which gives the same result...
What to do, how to live?
First of all read about php namespaces.
Next read about the psr-4 standart.
Select a prefix for your folder, let's say Lib. Make sure that all files in the lib folder has a properly namespace. E.g. Importer class must be stored in the lib\Importer.php and must have the namespace Lib;, Items\Item class must be stored in the lib\Items\Item.php and must have the namespace Lib\Items\Item; and so on.
Your files are ready. Just need to inform Symfony about them.
Symfony uses composer's autoloader, so check composer's autoload section. Than add new folder for autoloading in composer.json:
"autoload": {
"psr-4": {
"App\\": "src/",
"Lib\\": "lib/"
}
},
It says that all classes in lib folder have their own separate files and Lib prefix in their namespace and other part of namespace is similar to directories structure.
Next you need to clear autoloader's cache. Run in console:
composer dump-autoload
And finally you can use your class:
use Lib\Importer;
$importer = new Importer;
Also you can add your files to autowire.

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.

Composer ClassLoader Namespaces Trouble

I'm trying to use the Composer ClassLoader.
I'm trying to load up some CodeIgniter Libraries with PSR namespaces.
In my index.php I have:
$loader = include_once ROOTPATH . 'vendor/autoload.php';
$loader->add('CLI', ROOTPATH . 'application/libraries/CLI/');
$loader->register();
A simplified example of my folder structure is:
libaries/
CLI/
Tree/
Parser.php - namespace CLI\Tree;
Settings.php - namespace CLI;
Am I correct in assuming that Parser.php and Settings.php would be autoloaded? As I understood the documentation example it looks into sub-folders.
I want to avoid having to do the following:
$loader->addClassMap([
'CLI\\Settings' => ROOTPATH . 'application/libraries/CLI/Settings.php',
'CLI\\Tree\\Parser' => ROOTPATH . 'application/libraries/CLI/Tree/Parser.php',
]);
$loader->register();
Why don't you simply add the autoloading of your own code to the composer.json file you already have? That way Composer will create the autoloading file also for your own classes, you would be able to include your current project in another project without having to worry about autoloading (may be unlikely, but:), and you learn how to declare autoloading if you want to create your own modules.
From your code I guess this would work:
"autoload": {
"psr-0": {
"CLI": "application/libraries"
}
}
Explanation: CLI is the prefix of the classes that could possibly found in the path. Make this as long as possible if you are using a lot of CLI classes, and only some are found in that path. Otherwise Composer will search a class in more than one directory, which is sort of bad for performance.
application/libraries is the prefix path where the PSR-0 style classes are located. PSR-0 dictates that a class named X_Y_Z or X\Y\Z is located in the path X/Y/Z.php, so the class prefix you were giving is NOT included in the prefix path you tell Composer to search for.
The prefix path is relative to the location of composer.json.
You could also use PSR-4. That would allow to remove empty directory structures, but will work only with namespaces:
"autoload": {
"psr-4": {
"CLI\\": "application/libraries/CLI"
}
}
Two important differences: The class prefix must end with a backslash (and because this is JSON, the backslash has to be escaped, so double backslash).
Second: The class prefix will get removed from the path that is getting created from the classname. So a class W\X\Y\Z with the class prefix W\X\ will only create Y\Z.php as the path to the class and add the path prefix to it.
I added "CLI" to your path to show that PSR-4 would work, but that directory is not really needed in terms of PSR-4 - if it is empty, you could move files one level up.

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.

How to load my own code into Laravel? Differences between those ways?

I'm kind of confused on how to load my own code (classes or just regular functions) into a Laravel app. I've seen this done in several ways:
Creating a folder inside the app directory (for example: app/libs) and add app_path().'/libs' to start/global.php
Add it into composer.json's "require"
Add "psr-0" into composer.json's "autoload" and add there the files
Add a My\Custom\Service\Provider into app/config/app.php's 'providers' and the alias for the facade
What's the difference between them? Why and when should I use any of those ways? Should I load a class, several .php files or simply the folder? Maybe reference those 3 things at the same time?
EDIT:
These are my guesses:
Option 2 is just for packages
Option 3 if you want to load every class inside a custom namespace declared within the new created folder (don't get why the "psr-0" instead of just adding it to "classmap")
Option 1 is the same as option 3, just handled by Laravel instead of Composer
You can reference a folder and it will load every class found inside, or you can reference a certain file and it will load the class found inside
About option 4:
If you want to use the facade anywhere on your code, and that will need the namespace added into composer.json
EDIT 2:
If you add them to "classmap":
"classmap": [
"app/libs"
]
every class from any namespace within files inside the app/libs folder, will be loaded
If you add them to "psr-0":
"psr-0": {
"Libs": "app/"
}
it will load every class within the Libs namespace inside the app/libs folder
Still not sure why/when to use service providers and aliases.
EDIT 3:
"psr-0" if I want to load a namespace that follows a folder structure (it won't load a class within a subnamespace if it doesn't match the folder structure)
"classmap" for "random" classes, functions... sort of "the rest"
you can load your own code 2 (maybe 3) ways in laravel.
use composer
use ClassLoader
Manual include or require anywhere
Option 2 is just for packages
yes, you're right.
Option 1 is the same as option 3, just handled by Laravel instead of
Composer
yes, you're right.
Option 3 if you want to load every class inside a custom namespace
declared within the new created folder (don't get why the "psr-0"
instead of just adding it to "classmap")
some packages or classes adhere psr-0 standard, the rest is not. psr-0 option is mapping namespace to directory. the classmap is mapping
the namespace to certain directory or file and used for the class that is not adhere psr-0 standard.
You can use the classmap generation support to define autoloading for all libraries that do not follow PSR-0/4.
If you want to use the facade anywhere on your code, and that will
need the namespace added into composer.json
nope, instead, you have to add class alias for the facade in app/config/app.php
if your code is just file, not a class, then use composer autoload files
if your code is class but not adhere psr-0/4 standard, use composer autoload classmap or just add the containing directory to app/start/global.php.
otherwise, use composer autoload psr-0 or psr-4.
in Laravel 4,I add all of my class in "mylibrary" folder.
Then at app/start/global.php , I add app_path().'/mylibrary',
ClassLoader::addDirectories(array(
app_path().'/commands',
app_path().'/controllers',
app_path().'/models',
app_path().'/database/seeds',
app_path().'/mylibrary',
));
Within mylibrary there is MyClass.php , within MyClass.php there is test_myfunction()
and at app/view/home.blade.php I add these code :
<?php
$FMyClass11 = new MyClass;
$just_test=($FMyClass1->test_myfunction());
?>
Hope it works for you. :)

Categories