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.
Related
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');
I did my best to find a question/answer that applied, but I don't think I understand enough about the autoloader to recognize a suitable answer.
I have a package with the following composer.json:
{
"name": "Pva_agent",
"type":"library",
"description" : "query the pva agent",
"version":"0.1b",
"authors" : [
{
"name":"Ed Greenberg",
"email":"ed#precisionpros.com"
}
],
"minimum-stability":"dev",
"require": {},
"autoload": {
"psr-0": {
"Pva_agent": "."
}
}
}
My directory structure after composer installation of the package:
.
./vendor
./vendor/autoload.php
./vendor/Pva_agent
./vendor/Pva_agent/Agent.php
./vendor/Pva_agent/composer.json
./vendor/Pva_agent/.gitignore
./vendor/composer
./vendor/composer/autoload_psr4.php
./vendor/composer/autoload_real.php
./vendor/composer/autoload_classmap.php
./vendor/composer/autoload_namespaces.php
./vendor/composer/installed.json
./vendor/composer/autoload_static.php
./vendor/composer/ClassLoader.php
./vendor/composer/LICENSE
./composer.lock
./composer.json
./test_pva_agent.php
My test program:
<?php
require_once('vendor/autoload.php');
use Pva_agent\Agent;
$agent = new Agent();
My result:
edg#arthur pva_project $ php test_pva_agent.php
PHP Fatal error: Class 'Pva_agent\Agent' not found in /home/edg/PhpstormProjects/pva_project/test_pva_agent.php on line 6
PHP Stack trace:
PHP 1. {main}() /home/edg/PhpstormProjects/pva_project/test_pva_agent.php:0
edg#arthur pva_project $
I didn't think I needed the 'use' statement, since the autoloader should find the class, right?
Can somebody tell me where the problem lies?
Thanks,
Ed Greenberg
Your Pva_agent library should not sit in the vendor/ directory. This directory should contain only auto-generated data from Composer. This directory is usually not stored in VCS.
You should consider refactoring your directory structure to something similar to this one:
.
|____composer.json
|____composer.lock
|____src
| |____Pva_agent
|____vendor
Your library functionality should be added to src/Pva_agent directory.
Consider to use PSR-4 instead of PSR-0 for autoload functionality, as there is no need to regenerate the autoloader when you add classes. dump-autoloader has to be run in case of PSR-0 after adding classed.
For the directory structure above and the PSR-4 autoloader your composer.json autoload section should look similar to this one:
"autoload": {
"psr-4": { "Pva_agent\\": "src/Pva_agent" }
}
Your library should be auto loaded after this. Your auto-loaded library will be registered under the Pva_agent namespace.
I have a github repository https://github.com/KoulSlou/UPS and I would like to add it to my project.
In project root I created composer.json file and defined the following autoloading properties:
{
"autoload": {
"files": [
"libraries/Ups/Ups.php",
"libraries/Ups/Ups_Base.php",
"libraries/Ups/Ups_Base_Response.php",
"libraries/Ups/Ups_Live_Rates.php"
]
}
}
When I run
php composer.phar install
repository is being downloaded, but it looks like autoloader is not working. When I try to initialize one of the classes
$test = new Ups()
I got the following error:
Fatal error: Class 'Ups' not found in application/....
Did I define "autoload" property incorrectly?
I'd suggest not using the "files" autoloader, because that isn't very automatic - the files mentioned here are ALWAYS included. Replacing it with "classmap" would be better. And then you'd not be required to mention ALL files, but you can simply state the directory you want to have scanned for classes.
Now what I don't see anywhere: Did you initialize Composer's autoloader anywhere? This usually is something like
require "vendor/autoload.php";
Finaly, I have found out what was the problem. composer.json file in the project I was trying to load - UPS library -was invalid. I was able to download files when I ran:
composer.phar install
but it looks like composer.json file was ignored. I found it out when I ran
composer.phar update
and got
No valid composer.json was found
With option -v I got error that "name" is undefined index. So, I simply added "name" field to the composer.json. Final version is:
{
"name":"KoulSlou/UPS",
"autoload": {
"files": [
"libraries/Ups/Ups.php",
"libraries/Ups/Ups_Base.php",
"libraries/Ups/Ups_Base_Response.php",
"libraries/Ups/Ups_Live_Rates.php"
]
}
}
I am developing a package and I want to store it in the vendor directory before I publish it.
So, The path to say Basset's service provider once it is installed via composer is
/siteroot/vendor/jasonlewis/basset/src/Basset/BassetServiceProvider.php
which composer maps in it's autoloader fine, now If mine is at
/siteroot/vendor/hailwood/databaseconfigloader/src/DatabaseConfigLoader/DatabaseConfigLoaderServiceProvider.php
What do I need to add to the composer.json file to make it generate the autoload mapping correctly?
I can manually edit the autoload_namespaces.php file to add the mapping
'Hailwood\\DatabaseConfigLoader' => $vendorDir . '/hailwood/databaseconfigloader/src/'
and that works, but obviously gets overwritten when I do a composer update, I tried adding
"psr-0": {
"Hailwood\\DatabaseConfigLoader": "src/"
}
to the composer.json but that outputs 'Hailwood\\DatabaseConfigLoader' => $baseDir . '/src/', which doesn't work.
In state, you don't respect PSR-0. So you have two solutions :
Respect PSR-0
You must put your code in /siteroot/vendor/hailwood/databaseconfigloader/src/Hailwood/DatabaseConfigLoader/DatabaseConfigLoaderServiceProvider.php. After that, simply do :
"psr-0":
{
"Hailwood\\DatabaseConfigLoader": "vendor/hailwood/databaseconfigloader/src/"
}
Use classmap autoloading
Just try :
"classmap": ["vendor/hailwood/databaseconfigloader/src/"]
using composer in a Php project, with Twig and my own framework.
I would like to "override" the Twig_Node_Expression_GetAttr class from Twig with my own class.
Everything it's working fine, but I have to manually add in composer autoload_classmap.php file :
'Twig_Node_Expression_GetAttr' => 'ebuildy/ebuildy/src/eBuildy/Templating/Twig_Node_Expression_GetAttr.php',
How can I declare this in my composer.json description file ?
Thanks,
You can just define the classmap entry in your project's composer.json, or using PSR-0 mapping as well. See the composer docs on autoloading for details. If you define the PSR-0 namespace with a more restrictive namespace than what Twig has, then you're sure yours will take over, .e.g:
{
"autoload": {
"psr-0": {
"Twig_Node_": "path/to/src/"
}
}
}
This however only works if in this src/ dir you have a file called: src/Twig/Node/Expression/GetAttr.php.