PHP Autoloading with Composer using Namespaces - php

I previously had a pretty simple autoload script working nicely, but as I've noticed that Doctrine2 is using Composer for this, I thought it might be nice to streamline everything. Unfortunately, Composer does not seem to be working as I understood it to.
Here is the relevant part of my composer.json
"autoload": {
"psr-0": {
"": "models/",
"Catalog2\\Config": "class/"
}
}
Note that the "": "models/" line used by Doctrine2 has been working just fine. After I ran composer update , the bottom part of my vendor/composer/autoload_namespaces.php looks like so:
'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib'),
'Catalog2\\Config' => array($baseDir . '/class'),
'' => array($baseDir . '/models'),
So far so good, I think. In my routes.php file (basically a front-controller) I have the following:
<?php
use Catalog2\Config;
//autoload classes
require_once __DIR__.'/vendor/autoload.php';
try {
$router = new Router;
} catch(Exception $e ) {
echo "<strong>Can't create router object</strong><br/>";
}
Here Catalog2\Config\Router should be calling my class/Router.php, which begins as follows:
<?php
namespace Catalog2\Config;
class Router {
protected $resource; //what are we manipulating? A product? An order?
protected $action; //what are we doing with that resource?
When I go to the page I get this:
Fatal error: Class 'Router' not found in /home/tom/Code/productCatalog2/routes.php on line 14
What is going wrong here? I repeat that Doctrine2 was able to autoload my model code from /models, so why aren't my changes working?

According to PSR-0 the namespace prefix will be included to the path.
So the complete filename for your class must be:
class/Catalog2/Config/Router.php
Meanwhile PSR-4 would behave like you expected: it will just match the namespace prefix and will not append it additionally to the given path.
References:
https://getcomposer.org/doc/04-schema.md#autoload
PS: you probably want the namespace prefix to be "Catalog2\\Config\\" (see the trailing slash)

Related

Autoload for controller classes in slim framework

I'm trying to build a site with slim and autoloading my controller classes for the routes. I'm currently setting up the base structure and testing with a single route with nothing more than a simple "Test" output.
I prevously did this stuff with defining a spl_autoload_register function, but as this approach isn't recommended by slim and composer I want to do it right and I'm not trying to autoload my classes.
My Project is set up as this:
The class BlockController inside the file with the same Name under Controller is inside a namespace defined with namespace MyAPI\Controller;
app/Controller/BlockController.php
namespace MyAPI\Controller;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
class BlockController
{
public function getList(Request $request, Response $response, $args)
{
return $response->withStatus(200)
->withHeader('Content-Type', 'text/html')
->write("Test");
}
}
I'm loading the dependencies and settings and after that all my routes (which contains currently only some small ones for testing my architecture):
public/index.php:
require __DIR__ . '/../vendor/autoload.php';
$settings = require __DIR__ . '/../app/settings.php';
$app = new \Slim\App($settings);
require __DIR__ . '/../app/dependencies.php';
require __DIR__ . '/../app/routes.php';
$app->run();
app/routes.php (is very simple, will be extended with more Route-files):
require 'Routes/BlockRoute.php';
app/Routes/BlockRoute.php:
use MyAPI\Controller\BlockController;
$container["BlockController"] = function ($container) {
return new BlockController($container);
};
$app->group('/block', function() use ($container) {
$this->get('[/]', 'BlockController::getList');
});
So the first command inside BlockRoute.php is the use of the BlockController-namespace. Everything under app/ should have the Base-Namespace MyAPI.
As described in the slim-documentation I planned to do that with to autoload feature of composer, so I modified my composer.json and added the following:
{
"require": { .. },
"autoload": {
"psr-4": {
"MyAPI\\": "app"
}
}
}
Edit: updated path to app-folder after the answer from Adam Lavin
After that I ran composer update. Is that the right command for those changes? Or should I use composer install? Couldn't find any more information what I have to do after making those additions in the autoload-section.
When I run the site now with the php webserver and navigate to this route /block I get the following RuntimeException:
Callable BlockController::getList does not exist
File: C:\Prog\src\vendor\slim\slim\Slim\CallableResolver.php
So the problem is that BlockController doesn't get included/autoloaded correctly but I don't understand why or what exactly the problem is. I tried to find some examples of working configurations with slim+composer+autoloading of classes but couldn't find something related.
Any input appreciated.
Since you're pointing MyApp\\ to ../src (the same directory as composer), the autoloader is going to try and find the controller in src/Controllers/BlockController.php.
It should be pointing to ../src/app, though since composer.json is in the src folder it can be simplified to app in the resulting composer.json file.
{
"require": { .. },
"autoload": {
"psr-4": {
"MyAPI\\": "app"
}
}
}
Additionally, In your example, the namespace of the BlockController is MoinAPI\Controllers, and should be MyAPI\Controllers.
And finally, in slim, you use a single colon instead of a double to refer to a callable route. BlockController::getList should be BlockController:getList
Run this from within docker container, or using the same php binary that composer used.
composer dump-autoload -o -vvv #-o fixed my problem in my case

Fatal error class MainController not found

so i have the following structure for what i'm trying to target but what i'm getting is the Fatal error class MainController not found , i'm new to autoload and namespace thing (but getting into them fast) i just need to know why this is happening , hope you guys have bit explained situation for me? i did saw few of answer around stackoverflow but nuthing helped, i know i'm doing something really wrong, but thats how i will learn :). structure:
composer.json
src/controllers/MainController.php
the following is my autoload inside composer.json file:
"autoload": {
"psr-4": {
"controllers\\": "src/controllers/"
}
}
and this is how my MainController.php looks alike :
namespace MainController;
class MainController{
function test($name){
echo 'holaaaa'.$name;
}
}
controller call inside: app/loads/loadController.php :::
use MainController;
$MainController = new MainController();
more info on vendor/autoload.php
it's included inside : index.php and inside index.php i have included mainapp.php and inside mainapp.php i have included loadcontroller.php vich calls the controller
structure screenshot:
Okay, in your Composer file you say the namespace is controllers. In your PHP file you say the namespace is MainController. They need to be the same for the autoloading to work.
If we are to go by your Composer file then the PHP should look like this:
namespace controllers;
class MainController {}
And the class should be called like this:
$MainController = new \controllers\MainController;
Or like this:
use controllers\MainController;
$MainController = new MainController;
Or, if you want a nicer-looking class name:
use controllers\MainController as Controller;
$MainController = new Controller;
In my case I only managed to correct it after deleting my folder from the composer (vendor) and rerunning the command composer dump-autoload

Mustache_Autoloader missing with Composer

I dowload the last versión of Mustache (2.7) with Composer,
"require": {
"mustache/mustache" : "2.7.*",
// etc...
}
but when I try:
use Mustache\Mustache_Autoloader;
abstract class BaseController {
public function __construct() {
Mustache_Autoloader::register();
/...
}
/...
}
the error.log said:
PHP Fatal error: Class 'Mustache\\Mustache_Autoloader' not found in
Although, Mustache_Autoloader hasn't namespaces.
Composer has: composer/autoload_namespaces.php:
return array(
'Mustache' => array($vendorDir . '/mustache/mustache/src'),
//etc
);
And in my main file I don't forget include require 'vendor/autoload.php'; But I don't know what happend. Any idea? Thanks.
SOLUTION:
Only I need to add '\' at the beginning of the word. like new \Mustache_Engine().
Now it works. Thanks for your help :)
First, why do you want to use the Mustache\Mustache_Autoloader ?
composer should take care of the autoloading.
Further i see in https://github.com/bobthecow/mustache.php/blob/master/src/Mustache/Autoloader.php
that this class has no namespace.
Therefor use Mustache\Mustache_Autoloader; fails.
If you want to use the autoloader you better use:
require '/path/to/mustache/src/Mustache/Autoloader.php';
Mustache_Autoloader::register();.

PHP Class not found from a different namespace

I'm currently working on a Symfony2 project with Phpspec and I'm having problems to extend a Spec class described in a different namespace.
In my project, for instance, I'm having the following class described in spec/Acme/Model/Foo/FooSpec.php :
namespace spec\Acme\Model\Foo;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
abstract class FooSpec extends ObjectBehavior{
//some code here
}
And I have another class in spec/Acme/Model/Bar/BarSpec.php extending FooSpec :
namespace spec\Acme\Model\Bar;
use spec\Acme\Model\Foo\FooSpec;
class BarSpec extends FooSpec{
//some code here
}
When I try to run phpspec, I have the following error :
PHP Fatal error: Class 'spec\Acme\Model\Foo\FooSpec' not found in /home/user/Projects/Acme/spec/Acme/Model/Bar/BarSpec.php on line 9
The only way I found to make it work was to add the following line in spec/Acme/Model/Bar/BarSpec.php:
include('./spec/Acme/Model/Foo/FooSpec.php');
I don't know why I have to include this specific file to make it run, especially when the other classes (like PhpSpec\ObjectBehavior) are correctly found.
Do you have any idea why is this happening?
Edit:
As suggested by #Phil and #Sheikh Heera in the comments, I tried to set up an autoload to register my spec namespace but it's not working neither. Here is what I tried so far :
require_once getcwd() . '/vendor/composer/ClassLoader.php';
$loader = new \Composer\Autoload\ClassLoader();
// register classes with namespaces
$loader->add('spec', getcwd().'/spec');
// activate the autoloader
$loader->register();
I also tried to modify the file vendor/composer/autoload_namespaces.php to add this :
return array(
//some code here
'spec' => array(getcwd() . '/spec'),
//some more code
);
But still the same error. I also tried with 'spec' => array(getcwd()) or $loader->add('spec', getcwd()); just to see what will happen and this time I get a Cannot redeclare class on another spec class.
My php version is PHP 5.4.9-4ubuntu2.4 (cli).
Thank you in advance for your help.
A more generic solution is to put the spec directory into the autoload-dev configuration of your composer.json:
"autoload-dev": {
"psr-0": {
"spec\\":""
}
},
This way, composer will generate the namespace also for the specs, which should be on the root of the repository:
return array(
'spec\\' => array($baseDir . '/'),
...
);
Finally, what I actually did to "solve" my "problem", as suggested by Phil and Sheikh Heera in the comments, is to autoload my namespace so it is recognized by spec.
Here is what I did to make it work :
I added the following line to the file `vendor/composer/autoload_namespaces.php` :
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
//some code
'spec\\Acme\\Behavior' => array(getcwd()),
//some code
);
Then I created the following folder structure :
│── spec
│  └── Acme
│ └── Behavior
│ └── FooBehavior.php
I declared the namespace in `spec/Acme/Behavior/FooBehavior.php` this way :
namespace spec\Acme\Behavior;
And used it in `spec/Acme/Model/Bar/BarSpec.php` as following :
use spec\Acme\Behavior\FooBehavior;
class BarSpec extends FooBehavior{
//...
}
I know this is not the best practice because everytime someone will be working on this project it will have to reproduce manually the first step. So if you have any better idea, please feel free to comment or post an answer.

Will a directory with a period break autoload resolution based on a namespace in Zend Framework?

I have a folder in my library folder which is named after my website. The folder path is like:
~\www\library\myWebsite.com
If I'm using Zend autoloader to load the namespace of everything in the library path, will I have any trouble autoloading a class from that file with a namespace like this:
\myWebsite.com\myClass::myFunction();
I have looked online for documentation on this and I can't find any info about using periods in this way.
I tried it and the complication is in PHP. I think Zend is registering the namespace fine, because when I call \Zend_Load_Autoloader::getRegisteredNamespaces() it shows that it's registered. but when I call the static method from the fully qualified namespace, php gives an error of this:
Fatal error: Undefined constant 'myWebsite' in /home/jesse/www/application/controllers/MyController.php on line 15
It seems like PHP is terminating the namespace identifier, during parsing, at the . (period character). This is dissapointing because to me having a library named after the website was important to my design.
I will rename the directory to myWebsitecom or possibly make the .com it's own sub directory like
myWebsite\com and incorporate that into my namespace tree like: \MyNamespace\Com\MyClass::myFunction();
The easiest way to find out is to try it.
If it doesn't work, you could always write a custom autoloader to make it work. I don't have much experience with php namespaces, but the autoloader would look something like this (I imagine you'll have to tinker with it a bit to determine the correct file path given the class name):
<?php
class My_Loader_Autoloader_MyWebsite implements Zend_Loader_Autoloader_Interface {
/**
* (non-PHPdoc)
* #see Zend_Loader_Autoloader_Interface::autoload()
*/
public function autoload($class) {
if (strtolower(substr($class, 0, 9)) == 'mywebsite') {
$file = realpath(APPLICATION_PATH . '/../library/myWebsite.com/' . $class);
if ($file) {
require_once $file;
return $class;
}
}
return false;
}
}
then put this in your bootstrap:
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->pushAutoloader(new My_Loader_Autoloader_MyWebsite());
and if this class must be in that myWebsite.com directory, you could just cheat and throw in a require in there too:
require_once(APPLICATION_PATH . '/../library/myWebsite.com/Loader/Autoloader/MyWebsite.php');

Categories