I'm developing a REST API with Silex and I'm facing a problem regarding autoloading of my custom librairy. It looks like Composer's autoload is not including it, because when I include it myself it works.
# The autoload section in composer.json
# Tried with :
# "Oc\\": "src/Oc"
# "Oc\\": "src/"
# "": "src/"
"autoload": {
"psr-4": {
"Oc\\": "src/"
}
}
<?php
// api/index.php <-- public-facing API
require_once __DIR__.'/../vendor/autoload.php';
$app = require __DIR__.'/../src/app.php';
require __DIR__.'/../src/routes.php'; // <--
$app->run();
<?php
// src/routes.php
// When uncommented, it works!
//include('Oc/ParseImport.php');
use Symfony\Component\HttpFoundation\Response;
use Oc\ParseImport;
$app->get('/hello', function () use ($app) {
return new Response(Oc\ParseImport(), 200);
});
<?php
// src/Oc/ParseImport.php
namespace Oc {
function ParseImport() {
return 'foobar!';
}
}
I run composer dumpautoload after each composer.json manipulation, and I do see the line 'Oc\\' => array($baseDir . '/src/Oc') (or anything I tried) in vendor/composer/autoload_psr4.php.
I can't figure out what is wrong.
Almost everything you did was correct.
When trying to autoload classes in a namespace, given that a class is named Oc\Foo and is located in the file src/Oc/Foo.php, the correct autoloading would be "PSR-4": { "Oc\\": "src/Oc" }.
However, you do not have a class. You have a function. And functions cannot be autoloaded by PHP until now. It has been proposed more than once (the one proposal I found easily is https://wiki.php.net/rfc/function_autoloading), but until now this feature hasn't been implemented.
Your alternative solutions:
Move the function into a static method of a class. Classes can be autoloaded.
Include the function definition as "files" autoloading: "files": ["src/Oc/ParseImport.php"] Note that this approach will always include that file even if it isn't being used - but there is no other way to include functions in PHP.
As illustration see how Guzzle did it:
Autoloading in composer.json
Conditional include of functions based on function_exists
Function definition
Related
I have problem with autoloading with composer when i use psr-4 autoloading it doesn't work and give me error.
I tried:
$ composer dump-autoload
and a lot of other thing but it doesn't work without
require one;
error:
You are now a master builder, that knows how to autoload with a
classmap!
Fatal error: Uncaught Error: Class 'VegithemesLibraryGreeting' not
found in /home/vaclav/Server/vssk/VSSK/project/aldemo/index.php:10
Stack trace: #0 {main} thrown in
/home/vaclav/Server/vssk/VSSK/project/aldemo/index.php on line 10
composer.json:
{
"autoload": {
"files": ["mylibrary/functions.php"],
"classmap": [
"classmap"
],
"psr-4": {
"one\\": "src/"
}
}
}
greeting.php (file with class to load):
<?php
namespace one;
Class Greeting
{
public function hi()
{
return "We got you covered";
}
}
index.php file:
<?php
require 'vendor/autoload.php';
echo lego();
$cm = new Cmautoload;
echo $cm->classmap();
$vt = new oneGreeting;
echo $vt->hi();
It is generally good practice to capitalize the first letter of a class name. It also adheres with the rules of PSR-1.
Change your composer.json file to look like this:
{
"autoload": {
"files": [
"mylibrary/functions.php"
],
"classmap": [
"classmap"
],
"psr-4": {
"One\\": "src/"
}
}
}
Now, in your index file. We are going to import the autoloader. To do this simply require it:
require 'vendor/autoload.php';
Now that you have included the autoloader, go into every class and set the namespace.
The classes in your src/ == namespace One;
Check your classes in src/ and make sure they are all namespaced. Meaning that they should all have the following line of code at the top:
namespace One;
As mentioned before, update your file names to Foo.php and class names to
class Foo to adhere to PSR. (This is not required but highly recommended and standard procedure.)
To use one of your classes you would say use One\Greeting;
$greeting = new Greeting();
echo $greeting->hi(); //"We got you covered"
I found the problem, there was missing:
use One\Greeting;
In a lot of tutorials there isn't a word about it.
Another relevant detail about this is the namespace must match with the folder structure.
If not it will throw the warning.
In my case the filename was
src/One/GreetingClass.php
but the class name was in lowercase, causing this error:
class Greetingclass {
Changing the class declaration as GreetingClass fixed the issue.
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
I'm trying to figure out how to get composer to look for classes for a specific namespace under 2 directories. What I'm thinking of is this:
Default location: /src/MyModule/myClass.php
Override location: /config/override/MyModule/myClass.php
Now, I'd like to use composer to configure the autoloader to check if a class exists under the Override location. If it does, use this class. If not, load the class from the default location.
Is this possible using composer or would I have to implement this logic using my own autoloader?
From Composer documentation:
If you need to search for a same prefix in multiple directories, you
can specify them as an array as such:
{
"autoload": {
"psr-4": { "Monolog\\": ["src/", "lib/"] }
}
}
You would add that to your composer.json file of course. To do it programatically you can do this:
$autoloader = require __DIR__.'/../vendor/autoload.php';
$autoloader->addPsr4('MyModule\\', [ '/first/dir/MyModule', '/another/dir/MyModule' ]);
app/Core
contollers
This is my website structure to put the main class, I user composer psr-4 rule to import class under app/Core folder.
composer.json
{
"autoload": {
"psr-4": {
"Core\\": ["app/Core"]
}
}
}
index.php
<?php
include 'vendor/autoload.php';
new Core/Api; // it's work
It works fine, but I want to autoload class under controllers folder without use namespace, so I use __autoload function like:
index.php
<?php
include 'vendor/autoload.php';
function __autoload($class_name) {
include 'controllers/' . $class_name . '.php';
}
new Test // Fatal error: Class 'test'
If I remove include 'vendor/autoload.php'; it will work, so I think the code is correct, I know I can use classmap in composer.json, but it need to dump-autoload everytime I add a new class, how to deal with the conflict?
You do not have to use your own implementation of autoloading. You could use composer autoloading for all classes.
{
"autoload": {
"psr-0": { "": "src/" }
}
}
https://getcomposer.org/doc/04-schema.md#psr-0
Or, you could create class map
https://getcomposer.org/doc/04-schema.md#classmap
p.s. indeed, you could use empty namespace with psr-4.
I'm experimenting with creating an extension with the Silex php micro framework for user authentication but I can't seem to get the autoloader to work. Can anyone shed any light?
I have a directory structure like this (truncated)
usertest
|_lib
| |_silex.phar
| |_MyNamespace
| |_UserExtension.php
| |_User.php
|_www
|_index.php
The pertinent bits of index.php, which serves as the bootstrap and the front controller look like this:
require '../lib/silex.phar';
use Silex\Application;
use MyNamespace\UserExtension;
$app = new Application();
$app['autoloader']->registerNamespace( 'MyNamespace', '../lib' );
$app->register( new UserExtension() );
The class I'm trying to load looks similar this:
namespace MyNamespace;
use Silex\Application;
use Silex\ExtensionInterface;
class UserExtension implements ExtensionInterface {
public function register( Application $app ) {
$app['user'] = $app->share( function() use( $app ) {
return new User();
});
}
}
All pretty straight forward except it throws this error:
Fatal error: Class 'MyNamespace\UserExtension' not found in /home/meouw/Projects/php/usertest/www/index.php on line 8
I have dabbled with symfony2 and have successfully followed the instructions for setting up the universal class loader, but in this instance I am stumped. Am I missing something? Any help would be appreciated.
In recent versions of Silex the autoloader is deprecated and you should register all your namespaces through the composer.json file which imo is a nicer solution because you are centralizing your autoloading definitions.
Example:
{
"require": {
"silex/silex": "1.0.*#dev"
},
"autoload": {
"psr-0": {
"MyNameSpace": "src/"
}
}
}
In fact when you try to access the autoloader in any recent version of Silex the following RuntimeException is thrown:
You tried to access the autoloader service. The autoloader has been removed from Silex. It is recommended that you use Composer to manage your dependencies and handle your autoloading. See http://getcomposer.org for more information.
I'd use
$app['autoloader']->registerNamespace('MyNamespace', __DIR__.'/../lib');
Deprecated - As of 2014-10-21 PSR-0 has been marked as deprecated.
PSR-4 is now recommended as an alternative
That is why you should use PSR-4 syntax in composer.json
{
"require": {
"silex/silex": "1.0.*#dev",
},
"autoload": {
"psr-4": {
"Vendor\\Namespace\\": "/path"
}
}
}
To register namespaces, just call registerNamespaces() like this:
$app = new Silex\Application();
$app['autoloader']->registerNamespaces(array(
'Symfony' => __DIR__.'/../vendor/',
'Panda' => __DIR__.'/../vendor/SilexDiscountServiceProvider/src',
'Knp' => __DIR__.'/../vendor/KnpSilexExtensions/',
// ...
));
Both adding appropriate statement to the autoload section of composer.json and registering namespaces directly calling registerNamespace was not working for me, until I executed composer update in the projects folder.