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.
Related
In YII2 Framework
Path of File is root\vendor\yiisoft\yii2-httpclient\Client.php
Namespace defined in above mentioned file is - namespace yii\httpclient;
Now when I use this namespace in other file while setting up Google ReCaptcha
by writing "use yii\httpclient\Client"
then I am getting error "Class yii\httpclient\Client" not found
So I want to know whether namespaces are path dependent ? or is there a routing file or htaccess..etc where I can define the actual path of namespaces used in project, which YII2 compiler will refer to locate the file / class ?
Namespaces themselves are not dependent on file path.
But you are probably mistaking what use clause does.
If you have this line in file:
use yii\httpclient\Client;
It doesn't mean that the class is loaded. It only tells parser that you mean yii\httpclient\Client every time you use Client class in that file.
PHP has something called autoload to make sure you don't have manually require files for each class you are using. Autoloaders are called every time you are using some class if that class hasn't been loaded yet. When they are called they are given the class name and they check if they know how to load that class.
Now, even if the namespaces itself are not dependent on file path autoloaders usually uses those namespaces to decide where to look for the file containing that class.
And as Nigel Ren mentioned in comment, there exist PSR-4 recommendation how to choose namespace and file structure to make sure that autoloader will know where to look for class.
Yii2 projects usually uses 2 autoloaders.
The Yii's own autoloader and autoloader generated by composer.
Since your question is about class that comes from vendor\yiisoft\yii2-httpclient the autoloader generated by composer.
If you check the composer.json file in that package you can see that it has autoloader section with psr-4 key. That tells composer that when it generates its autoloader it should be set to look for any class from yii\httpclient namespace in src folder of that package.
To make sure the composer's autoloader is working properly you have to go through following steps:
The yiisoft\yii2-httpclient package should be installed by composer.
If you need to regenerate composer's autoloader you can run:
composer dump-autoload
The composer autoloader must be included in your application's entry point (usually /web/index.php or /yii files).
Check if those files have this line:
// in case of /web/index.php
require(__DIR__ . '/../vendor/autoload.php');
//in case of /yii
require(__DIR__ . '/vendor/autoload.php');
I have a package that must automatically load a non-namespaced PHP file when the package is included in an application.
Below is my general directory structure
packages/
+-- PackageA/
+-- Entities/
+-- Mappers/
+-- Services/
+-- composer.json
+-- constants.php
apps/appA/
+-- vendors/
+-- autoload/
+-- composer.json
apps/appB/
+-- vendors/
+-- composer.json
I've followed the directions here to use a path repository in making PackageA a dependency for appA/. This part works smoothly.
Some files within PackageA require access to constants, mostly filepaths. This is what "constants.php" is for, and these values are defined procedurally:
<?php
define('XML_REPO_PATH', __DIR__ . '/../blah/xml/');
// --etc--
I originally thought to use the 'files' autoloading mechanism in "packages/PackageA/composer.json" with:
{
...
"autoload": {
"psr-4": { ... }
"files": ["constants.php"]
}
}
However, this is not requiring constants.php when PackageA is included as a dependency in appA. To fix this, instead of putting "files": [...] in "packages/PackageA/composer.json", I put the following in the autoload section of "app/appsA/composer.json":
"files": ["vendors/packages/PackageA/constants.php"]
This isn't very desirable because every application using PackageA would need this. I would think that the nature of composer would allow me to make sure that files within PackageA have access to (i.e., are meant to include) certain procedural code, like in the case of config constants. Is there a way to do this?
Don't use Composer's files autoloading to include configuration files or files with constants. Please think about the performance impact for all other libraries. A file in the files section is loaded on every invocation of your script, regardless, if you are using PackageA or not. Also think about possible clashes of constant names, due to non namespaced constant usage. files autoloading is only! meant to be used for legacy code that cannot otherwise be made working. You should avoid using it.
because in php < 5.6 i can't concatenate class constants with other constants like __DIR__
The main problem is not the concatenation, but that the constants file isn't a class. Autoloading won't work here, because Composer's Autolader loads classes only.
So, one solution could be to introduce an empty class for Constants, but add the side-effects on top.
Then namespace it under your vendor\PackageA umbrella.
This enables you to add use vendor\PackageA\Constants; in other classes,
in order to trigger the autoloading, right?
You are including an empty class, but when the file is autoloaded, the defines are happening as side-effects. A good IDE will place an error flag on this file, because it causes side-effects. It's still ugly, because other developers don't know where the defines come from, when they simply include a class - but better than using autoloading files section.
composer.json
"autoload": {
"psr-4": { "\Vendor\PackageA\\" : "./src/packages/PackageA/" }
}
constants.php
<?php
namespace Vendor\PackageA;
class Constants
{
// #todo PHP 5.6 namespaced class constants
}
// global side effect: constant definition
define('XML_REPO_PATH', __DIR__ . '/../blah/xml/');
// etc..
The best-practice is probably to use a Configuration class with a constructor that accepts a configuration object or array for the configuration of your package.
This decouple package and application from hard-coded global configurations.
Basically, configuration injection (App environment into Package, Package configures itself based on that context).
Okay, as I browse around there are lots of posts and answers on the line. To my understanding
psr-0: SPL autoload only
psr-4: SPL autoload + namespace
Most of the answers contain LONG methods. psr-4 should not be HARD to implement since it purposes is to simplify file structure yet remaining its own advantage.
I have a structure like this project\view\main.php
in main.php
namespace project\view;
class main {
.......
}
out of the project folder which is my root directory, i have a index.php
spl_autoload_register( function ($ClassName) {
require $ClassName . '.php';
});
$main = new project\view\main();
Question: Am I doing it right with psr-4 or am I still missing something from the document?
From the specification PSR-0[1] is deprecated (since 2014-10-21) and is replaced by PSR-4
From PSR-4 documentation:
This PSR describes a specification for autoloading classes from file
paths. It is fully interoperable, and can be used in addition to any
other autoloading specification, including PSR-0. This PSR also
describes where to place files that will be autoloaded according to
the specification.
http://www.php-fig.org/psr/psr-4/
If you want the full rationnale for the specification change you can consult the PSR-4 meta document.
If you want to see working example, you can search at the same location
My ultimate recommandation would be to take a look at composer and let it handle it. You would only have to include the automatically generated autoload.php file to have fully functionnal autoloading.
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.
I am currently running Slim and Twig, my folder structure is as follow
/application/modules
In my composer.json I have
"autoload": {
"psr-4": {
"Application\\": "application/modules/",
"Core\\": "application/",
"Middleware\\": "application/slim/middleware/"
}
}
My issue is is that in Application\modules\ I have a directory for each module. Now my question is, is it possible to make composer iterate through the sub directories when using PSR-4 autoload?
I see an issue with your PSR-4 declaration.
You shouldn't place "Core" classes inside a folder that has subfolders for other namespaces.
PSR-4 autoloading in Composer works like this: If the fully qualified class name of the class to load matches a prefix declared in any of the PSR-4 definitions, then the prefix mentioned in the prefix is removed from the class name, and the remaining class name is mapped to a filename and being searched.
If you have classes named Application\ in the folder application/modules, and you have classes named Core\ in the folder application, then technically Composer might find files that match a class name like Core\modules\Whatever, although these file will contain a class Application\Whatever instead.
I recommend moving all the Core classes into their own folder and point to this in the PSR-4 declaration.
The problem with your original question is that you omit an important information: What is the class and file structure for your modules?
Composer's autoloader will happily resolve any class that starts with the namespace prefix Application, remove that prefix from the classname, convert the remainders into a path name and search for that file in application/modules/. Given that you have a module class Application\MyModule\Foobar, it will be searched in application/modules/MyModule/Foobar.php. Why? Because the prefix Application will be removed to allow for shorter path names in PSR-4. (Using PSR-0 would mean you have to have a folder for every level of namespace in the classname.)
Note that it is recommended for optimum performance to make the prefix for namespaces as long as possible, because Composer allows to have more than one directory for any given prefix, but then has to search for the correct file in more than one directory. Searching for files is time consuming!