How to use the PSR-4 autoload in my /Classes/ folder? - php

I've tried multiple PSR-4 loaders, but they either don't work or I can't access the classes from another folder.
My current folder structure:
-Classes
--Config.php
--Session.php
--Frontend (folder)
---Login.php
PSR-4 Autoloader:
I tried to load all classes using the PSR-4 autoload register. I modified it slightly to my folder structure. I've given all classes the namespace Classes, but the ones in the Frotend folder has the namespace Classes\Frontend.
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = 'Classes\\';
// base directory for the namespace prefix
$base_dir = __DIR__ . '/Classes/';
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// if the file exists, require it
if (file_exists($file)) {
require $file;
}
});
I'm not sure if this has to do with the autoloader, but I also want to call the class from any file wherever it's located. So if I have a file in
/Frontend/templates/login-page.php
I want to be able to call the class "Classes\Frontend\Login".
Is this possible and how would I do that?

There are mainly two ways to get it working: The first option is to use an absolute server path (starting with a '/'), to set the base directory for your classes in your autoload function:
spl_autoload_register(function ($class) {
$prefix = 'Classes\\';
$base_dir = '/var/www/html/my_project/src/'; // your classes folder
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
$relative_class = substr($class, $len);
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
if (file_exists($file)) {
require $file;
}
});
The better solution is, as #FĂ©lix suggested, to stick with the __DIR__ constant to keep things relative to your project folder. Absolute paths are brittle between deployments on different servers. __DIR__ refers to the directory of the file it is used in; in this case it is where you register the autoload function. Starting from this directory, you can navigate to the classes base directory, for example $base_dir = __DIR__ . '/../../src/;
Don't forget to namespace your classes:
namespace Classes;
class Foo
{
public function test()
{
echo 'Hurray';
}
}
Then use classes like this:
$foo = new Classes\Foo();
$foo->test();

Related

php - autoload not working with static method

I using spl_autoload_register to autoload class like
My Structure
index.php
Module\Autoloader.php
Module\MyClass.php
Test\test.php
in index.php file
require_once ("Module\Autoloader.php");
use Module\MyClass;
include 'Test\test.php';
in Module\Autoloader.php file
class Autoloader {
static public function loader($className) {
$filename = __DIR__."/" . str_replace("\\", '/', $className) . ".php";
echo $filename.'<br>';
if (file_exists($filename)) {
include($filename);
}
}
}
spl_autoload_register('Autoloader::loader');
in Module\MyClass.php file
namespace Module;
class MyClass {
public static function run() {
echo 'run';
}
}
in Test\test.php file
MyClass::run();
But it has error
Fatal error: Uncaught Error: Class 'MyClass' not found in ..\Test\test.php
How to fix that thank
your issue is that you prepend __DIR__
__DIR__ is based on where the file from which it gets called resides:
__DIR__
The directory of the file. If used inside an include, the directory of the included file is returned. This is equivalent to dirname(__FILE__). This directory name does not have a trailing slash unless it is the root directory.
http://php.net/manual/en/language.constants.predefined.php
So because your autoloader routine resides in ./Module/
__DIR__ will not return / when called from index.php but Module, making your finished classpath Module/Module/MyClass.php which obviously can't be found.
Either use another means of prepending the directory, like a predetermined list, use the first part of the namespace (so just ditch the __DIR__) or move the classes to location relative to directory in which your include file resides.
Your autoloader is inside the Module dir so it will apppend an extra "Module" when you try to append "DIR" to the class full name. The file location will be something like this:
../Module/Module/MyClass.php
Try to move your autoloader the same dir as index.php or change it as the following:
<?php
class Autoloader {
static public function loader($className) {
$filename = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
str_replace("\\", DIRECTORY_SEPARATOR, $className) . ".php";
if (file_exists($filename)) {
include($filename);
} else {
echo "$filename not found!\n";
}
}
}
spl_autoload_register('Autoloader::loader');

Loading files without instantiable classes with autoloader

This is the structure of my project:
- project
-- src
--- class1.php
--- class2.php
--- class3.php
--- class4.php
--- class.php
- autoload.php
This is how my autoloader (autoload.php) looks:
<?php
/**
* #param string $class The fully-qualified class name.
* #return void
*/
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = 'ProjectName';
// base directory for the namespace prefix
$base_dir = __DIR__ . '/src/';
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// if the file exists, require it
if (file_exists($file)) {
require $file;
}
});
Now, I have a file (class.php) which contains all configuration parameters for the rest of files located in src directory (constants, interface, abstract classes and some global functions). What's the best way to load it without interrupting whole process?

Namespaces and Class loading in php

I'm new to namespaces in PHP and trying to make use of them to load classes.
Whenever I run my code I get class Cheese cannot be found on line x
PHPstorm recognizes the class via the namespaces and enables its methods.
I have the following files / directory structure.
/Project
/App
Mouse.php
/Test
MouseTest.php
Mouse.php
namespace App\Mouse;
class Cheese
{
}
MouseTest.php
namespace Test\MouseTest;
use \App\Mouse\Cheese as Cheese;
class CheeseTest
{
function test() {
$cheese = new Cheese();
$cheese->eat();
}
}
if you use composer or any autoloader that follow psr-0, then file name must same with class name, change Mouse.php to Cheese.php
This looks like a similar structure to the PSR0 standard found here: http://www.php-fig.org/psr/psr-0/
This is an example autoloader following that structure:
function autoload($className)
{
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strrpos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
require $fileName;
}
spl_autoload_register('autoload');
The spl_autoload_register call registers the autoload function so when a class is instantiated, it will use your autoload function to retrieve the class definition.
PHP namespaces don't load classes automatically, you have to include their files.
To make it automatic (like you want) you have to make an autoloader which will load the classes depending on the namespaces that you want to use.
The best way to make this, is using composer: http://code.tutsplus.com/tutorials/easy-package-management-with-composer--net-25530
but I recommend to search "php auloader" in google

PHP spl_autoload_register namespaces interfere with file includes

I don't know if I'm missing something but using namespaces seems to break my application. It won't find the parent class because it includes the namespace in the class name when the autoload register gets called
spl_autoload_register(function($classname){
if (preg_match('/[a-zA-Z]+Controller$/', $classname)) {
echo __DIR__ . '/controllers/' . $classname.".php" . "<br />";
require __DIR__ . '/controllers/' . $classname.".php";
}
});
//echo produces:
/var/www/web/controllers/DefaultController.php
/var/www/web/controllers/Project\Controllers\BaseController.php
Project\Controllers is the namespace used in both default and base controller.
Default extends base controller.
Why is spl autoload doing this?
Structure:
web/controllers:
BaseController.php
DefaultController.php
BaseController:
namespace Project\Controllers;
class BaseController
{
private $config;
public function __construct($config)
{
$this->config = $config;
}
}
DefaultController:
<?php
namespace Project\Controllers;
class DefaultController extends BaseController
{
}
The main problems seems to be that your autoloader is not aware of the root namespace which it belongs to. So really your autoloader should be made aware of or be part of the namespace so in this case Project.
So the first thing your autoload needs to do is remove the known root namespace (unless of course it is used as part of the file structure which in this case it isn't)
<?php namespace Project;
// Within the namespace
$classname = ltrim(str_replace(__NAMESPACE__ . '\\', '', $classname), '\\');
// Outside of the namespace
$classname = ltrim(str_replace('Project\\', '', $classname), '\\');
Now for the class name and the actual file location.
// find the last backslash
if($last = strripos($classname, '\\')) {
$namespace = substr($classname, 0, $last); // Controllers
$classname = substr($classname, $last+1); // DefaultController
}
Now you can built the actual file location to the PHP class file
$filepath = '/var/www/'; // Root
if(isset($namespace)) {
// Add the controllers bit onto the root
$filepath .= str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
// add the class name (DefaultController) to the file path
$filepath .= str_replace('\\', DIRECTORY_SEPARATOR, $classname) . '.php';
This should result in with the following filepaths which can be checked if exists then included if they do.
/var/www/Controllers/BaseController.php
/var/www/Controllers/DefaultController.php

PHP autoloaders and namespaces

I'm slightly confused by autoloaders and namespaces in PHP.
My classes are organised within an assets file,
-- Assets
-- user
-- User.php
-- database
-- Database.php
This is just a simple version, there are more files in each folder (i.e. user, database).
There is no need to worry about conflicting vendor names as there is only one vendor in this project (i.e. me) but for example, using the PSR0 autoloader:
function autoload($className)
{
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strripos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
require $fileName;
}
Do I need to declare a namespace in each of my class files and initilize a class by using new user\User();. Is it not possible to initilize it by using new user_User;
Many thanks
The PSR-0 autoloader convention doesn't require you to use namespaces that map to your directory structure. You can also use underscores in the class name for basically the same purpose.
Like the \ namespace separator, any underscores in the class name are converted to DIRECTORY_SEPARATOR when looking up the file to load. So, a class user_User and \user\User would both result in the autoloader looking for the same file: user/User.php.

Categories