Automatically including file in package when included as a dependency in Composer - 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).

Related

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.

Autoloading and PSR-0/PSR-4

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 |
+------------------------------+------------------+------------------------+---------------------------------------+

What Are the Differences Between PSR-0 and PSR-4?

Recently I've read about namespaces and how they are beneficial. I'm currently creating a project in Laravel and trying to move from class map autoloading to namespacing. However, I can't seem to grasp what the actual difference is between PSR-0 and PSR-4.
Some resources that I've read are...
Battle of the Autoloaders
Laracasts PSR-4 autoloading
PSR-0
PSR-4
What I understand:
PSR-4 does not convert underscores to directory separators
Certain specific rules of composer cause the directory structure to become complex which in turn makes PSR-0 namespacing verbose and thus PSR-4 was created
Examples explaining the difference would be appreciated.
They are very similar so it is not surprising that it's a bit confusing. The summary is that PSR-0 had some backwards compatibility features for PEAR-style classnames that PSR-4 dropped, as such it only supports namespaced code. On top of that PSR-4 does not force you to have the whole namespace as a directory structure, but only the part following the anchor point.
For example if you define that the Acme\Foo\ namespace is anchored in src/, with PSR-0 it means it will look for Acme\Foo\Bar in src/Acme/Foo/Bar.php while in PSR-4 it will look for it in src/Bar.php, allowing for shorter directory structures. On the other hand some prefer to have the full directory structure to clearly see what is in which namespace, so you can also say that Acme\Foo\ is in src/Acme/Foo with PSR-4 which will gives you the equivalent of the PSR-0 behavior described above.
Long story short for new projects and for most intents and purposes, you can use PSR-4 and forget all about PSR-0.
Here are the major differences,
1. For example if you define that the Acme\Foo\ namespace is anchored in src/,
with PSR-0 it means it will look for Acme\Foo\Bar in src/Acme/Foo/Bar.php
while in PSR-4 it will look for Acme\Foo\Bar in src/Bar.php(where Bar class is).
2. PSR-4 does not convert underscores to directory separators
3. You would prefer using PSR-4 with namespaces
4. PSR-0 will not work even if the class name is different from file name, like considering above example:
Acme\Foo\Bar ---> src/Acme/Foo/Bar.php (for Bar class) will work
Acme\Foo\Bar ---> src/Acme/Foo/Bar2.php (for Bar class) will not work
PSR-4 is something like 'relative path', PSR-0, 'absolute path'.
e.g.
config:
'App\Controller' => 'dir/'
PSR-0 autoload:
App\Controller\IndexController --> dir/App/Controller/IndexController.php
PSR-4 autoload:
App\Controller\IndexController --> dir/IndexController.php
And there are some more difference in details between PSR-0 and PSR-4, see here: http://www.php-fig.org/psr/psr-4/
Namespace/ folder convention.
Classes should be stored in folders according to their namespaces.
In general, you will create an src/ directory in your root folder, sitting at the same level as vendor/, and add your projects there. Below is an example of the folder structure:
.
+-- src
|
+-- Book
| +-- History
| | +-- UnitedStates.php - namespace Book\History;
+-- Vehicle
| +-- Air
| | +-- Wings
| | | +-- Airplane.php - namespace Vehicle\Air\Wings;
| +-- Road
| | +-- Car.php - namespace Vehicle\Road;
+-- tests
+-- test.php
+-- vendor
Difference between psr-0 and psr-4
psr-0
It is deprecated. Looking at vendor/composer/autoload_namespaces.php file you can see the namespaces and the directories that they are mapped to.
composer.json
"autoload": {
"psr-0": {
"Book\\": "src/",
"Vehicle\\": "src/"
}
}
Looking for Book\History\UnitedStates in src/Book/History/UnitedStates.php
Looking for Vehicle\Air\Wings\Airplane in src/Vehicle/Air/Wings/Airplane.php
psr-4
Looking at vendor/composer/autoload_psr4.php file you can see the namespaces and the directories that they are mapped to.
composer.json
"autoload": {
"psr-4": {
"Book\\": "src/",
"Vehicle\\": "src/"
}
}
Looking for Book\History\UnitedStates in src/History/UnitedStates.php
Looking for Vehicle\Air\Wings\Airplane in src/Air/Wings/Airplane.php
composer.json
"autoload": {
"psr-4": {
"Book\\": "src/Book/",
"Vehicle\\": "src/Vehicle/"
}
}
Looking for Book\History\UnitedStates src/Book/History/UnitedStates.php
Looking for Vehicle\Air\Wings\Airplane in src/Vehicle/Air/Wings/Airplane.php
Even when I tried but Composer is a mess. Sadly, it's the only alternative.of the market.
Why is a mess?.
The Composer's autocomplete works fine if you are in control of the code. However, if you are importing a different project, you find yourself with lots of styles and ways to create folders. For example, some projects are /company/src/class.php while others are company/class.php and others are company/src/class/class.php
I created a library that solves it:
https://github.com/EFTEC/AutoLoadOne (it's free, MIT).
It generates an autoinclude by scanning all the classes of a folder, so it works in every case (psr-0 psr-4, classes without namespace, file with multiple classes..
edit: And again, downvoted without any reason. ;-)

Composer, PSR-4 Autoloading Iteration

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!

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.

Categories