I've just begun using autoloader lazy loading in my app, and I'm running afoul of namespacing. The autoloader is trying to load things like new DateTime() and failing. Is there a trick to making my autoloader spcific to only my own namespaced classes?
Here is the code I have currently. I suspect it is a mess, but I'm not seeing just how to correct it:
<?php namespace RSCRM;
class Autoloader {
static public function loader($className) {
$filename = dirname(__FILE__) .'/'. str_replace("\\", '/', $className) . ".php";
if (file_exists($filename)) {
include_once($filename);
if (class_exists($className)) {
return TRUE;
}
}
return FALSE;
}
}
spl_autoload_register('\RSCRM\Autoloader::loader');
Happy to RTM if someone can point to a solid example.
What I use is actually adapted from the autoloader used to Unit Test a few of the AuraPHP libraries:
<?php
spl_autoload_register(function ($class) {
// a partial filename
$part = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
// directories where we can find classes
$dirs = array(
__DIR__ . DIRECTORY_SEPARATOR . 'src',
__DIR__ . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'src',
__DIR__ . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR . 'src',
);
// go through the directories to find classes
foreach ($dirs as $dir) {
$file = $dir . DIRECTORY_SEPARATOR . $part;
if (is_readable($file)) {
require $file;
return;
}
}
});
Just make sure the array of '$dirs' values point to the root of your namespaced code.
You can also take a look at the PSR-0 example implementation (http://www.php-fig.org/psr/psr-0/).
You might also want to look an into existing autoloader, like Aura.Autoload or the Symfony ClassLoader Component, although those might be overkill depending on what your requirements are.
I hope this helps.
So I already asked this question here earlier, but the solutions provided didn't work for me.
Here's my setup:
/mylib
/Vendor/Module/MyClass.php
/document_root
index.php
Here's my index.php
<?php
define('CLASSDIR', 'mylib');
define('BASEPATH', #realpath( dirname (__FILE__).'/../').'/'.CLASSDIR);
spl_autoload_register(null, false);
spl_autoload_extensions('.php');
function autoLoader($className){
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strrpos($className, '\\')) {
echo 'does it come here? nope.';
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= BASEPATH.'/'.str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
require $fileName;
}
spl_autoload_register('autoLoader');
//$obj = new MyClass();
$obj = new \Vendor\Module\MyClass();
$obj::test();
?>
Here's my MyClass.php
<?php
namespace Vendor\Module;
class MyClass{
public function __construct(){
echo 'weird';
}
public function test(){
echo 'strange';
}
}
?>
None of my echo's display anything. Obviously my class is also not loaded. Instead I get this error.
Fatal error: Call to undefined method MyClass::test() in /<documentroot>/index.php on line 29
Please help. I've been stuck on this for quite a while now and the rest of my development is suffering. I tried moving to spl_autoload_register() only because it's the recommended way. Now the lost time is making me regret it.
Your are calling the test() function incorrectly (using static way?).
Call the function with:
$obj = new MyClass();
$obj->test();
If you intend to use static method like MyClass::test(), declare your function in your class as:
public static function test() {
}
Moreover, your autoloader is over-complicated. It can be simplified as:
$class_dir = array(
'/mylib/Vendor/Module/',
// Add more paths here ( or you can build your own $class_dir )
);
function class_loader($class_name) {
global $class_dir;
foreach ($class_dir as $directory) {
if (file_exists($directory . $class_name . '.php')) {
require_once($directory . $class_name . '.php');
return;
}
}
}
spl_autoload_register('class_loader');
3rd edit:
I noticed the path you set is incorrect.
$fileName .= BASEPATH.'/'.str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
It should be:
$filename = BASEPATH .'/' . $filename . str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
As said before, it can be easily debugged by echoing the value of $filename.
F**k yes. Yes, I can take the liberty to swear on a public forum this one time.
A huge shout out to #Shivan Raptor for helping me along the way and not giving up.
There were numerous minor issues in the auto-loader function. But the debugging took me so long for just a simple reason that I couldn't see any echo messages. Only lord and XAMPP knows why. Seemed like XAMPP had somehow cached the class on first run or something and no changes later showed any effect. But creating a new class and class file all of a sudden started showing all my echo including the ones inside autoload. Anyone who has picked up the auto-loader code from the link below, please ensure you look at all the variables' values. It doesn't work "out of the box", if you don't keep everything in the document root. And if you are new to both PSR-0 and concept of auto loading, this can kill at least a sizable portion of your perfectly capable brain cells.
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
Here's the final index.php that worked for me.
<?php
define('CLASSDIR', 'mylib');
define('BASEPATH', #realpath( dirname (__FILE__).'/../').'/'.CLASSDIR);
spl_autoload_register(null, false);
spl_autoload_extensions('.php');
function autoLoader($className){
$className = ltrim($className, '\\');
$classPath = '';
$namespace = '';
if ($lastNsPos = strrpos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$classPath = str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR;
}
$fileName = BASEPATH.'/'.$classPath.str_replace('_', DIRECTORY_SEPARATOR, $className).'.php';
require $fileName;
}
spl_autoload_register('autoLoader');
$obj = new \Vendor\Module\MyClass();
$obj::test();
?>
I'm trying to learn about spl_autoload_register().
My index.php is under document root, my MyClass.php is put under document root /MyProject/MyClass/MyClass.php
Here's my index.php
<?php
define('CLASSDIR', 'mylib');
define('BASEPATH', #realpath( dirname (__FILE__).'/../').'/'.CLASSDIR);
spl_autoload_register(null, false);
spl_autoload_extensions('.php');
// PSR-0 provided autoloader.
function autoLoader($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 .= BASEPATH.'/'.str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
require $fileName;
}
spl_autoload_register('autoLoader');
$obj = new MyClass();
$obj->test();
?>
Here's my Class: MyClass.php
<?php
namespace MyProject\MyClass;
class MyClass{
public function __contruct(){
echo('weird');
}
function test(){
echo 'issue';
}
}?>
Here's the error:
Fatal error: Call to undefined method MyClass::test() in /path/to/file/index.php on line 26
So, I'm assuming it found the class (since it didn't complain)? But the messages 'weird' and 'issue' are not displayed. Telling me that the constructor didn't fire.
Okay, assuming your class file is located in a seperate folder called classes (example)
Structure like this:
DOCUMENT_ROOT/
->index.php
->classes/
->Myclass/
->Myclass.php
Somewhere on your index.php You'd have something looking like this:
<?php
DEFINE('__BASE', realpath(dirname(__FILE__)));
require_once('load.php');
?>
Now your load.php file should have the __autoload() function in there, looking something like this:
// Auto load function to load all the classes as required
function __autoload($class_name) {
$filename = ucfirst($class_name) . '.php';
$file = __BASE . DIRECTORY_SEPARATOR .'classes/' . ucfirst($class_name) . $filename;
// First file (model) doesnt exist
if (!file_exists($file)) {
return false;
} else {
// include class
require $file;
}
}
EDIT:
If you'd like to do it with spl_autoload_register(), you'd have something similar to this in your load.php
// Auto load function to load all the classes as required
function load_classes($class_name) {
$filename = ucfirst($class_name) . '.php';
$file = __BASE . DIRECTORY_SEPARATOR .'classes/' . ucfirst($class_name) . $filename;
// First file (model) doesnt exist
if (!file_exists($file)) {
return false;
} else {
// include class
require $file;
}
}
spl_autoload_register('load_classes');
I have a tiny application that i need an autoloader for. I could easily use the symfony2 class loader but it seems like overkill.
Is there a stable extremely lightweight psr-0 autloader out there?
You ask extremely lightweight, let's do so ;)
Timothy Boronczyk wrote a nice minimal SPL autoloader : http://zaemis.blogspot.fr/2012/05/writing-minimal-psr-0-autoloader.html
I condensed the code like this:
function autoload1( $class ) {
preg_match('/^(.+)?([^\\\\]+)$/U', ltrim( $class, '\\' ), $match ) );
require str_replace( '\\', '/', $match[ 1 ] )
. str_replace( [ '\\', '_' ], '/', $match[ 2 ] )
. '.php';
}
Then compare (minified versions of) this [autoload3] with short #Alix Axel code [autoload4] :
function autoload3($c){preg_match('/^(.+)?([^\\\\]+)$/U',ltrim($c,'\\'),$m);require str_replace('\\','/',$m[1]).str_replace(['\\','_'],'/',$m[2]).'.php';}
function autoload4($c){require (($n=strrpos($c=ltrim($c,'\\'),'\\'))!==false?str_replace('\\','/',substr($c,0,++$n)):null).str_replace('_','/',substr($c,$n)).'.php';}
autoload3 is the shortest !
Let's use stable & extremely lightweight (175b !) autoloader file :
<?php spl_autoload_register(function ($c){preg_match('/^(.+)?([^\\\\]+)$/U',ltrim($c,'\\'),$m);require str_replace('\\','/',$m[1]).str_replace(['\\','_'],'/',$m[2]).'.php';});
Maybe i'm crazy but you Asked for extreme, no?
EDIT: Thanks to Alix Axel, i've shorten the code (only 100b !) and used include instead of require in case you have various autoloading strategy for old libs (and then various autoloader in spl autoload stack...).
<?php spl_autoload_register(function($c){#include preg_replace('#\\\|_(?!.+\\\)#','/',$c).'.php';});
If you want to make it shorter / better, please use this gist.
The PSR-0 specification document has an examplary compatible autoloader function that is already pretty short:
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;
}
It's usage is pretty straight forward:
spl_autoload_register('autoload');
The shortcoming with it is, that you need to configure base-directories it works on with the include_path directive. To support a hybrid PSR-0 autoloaders leaned onto SPL semantics, the following one supportss include path and spl autoload extensions:
$spl_autoload_register_psr0 = function ($extensions = null)
{
$callback = function ($className, $extensions = null)
{
if (!preg_match('~^[a-z0-9\\_]{2,}$~i', $className)) {
return;
}
null !== $extensions || $extensions = spl_autoload_extensions();
$extensions = array_map('trim', explode(',', $extensions));
$dirs = array_map('realpath', explode(PATH_SEPARATOR, get_include_path()));
$classStub = strtr($className, array('_' => '/', '\\' => '/'));
foreach ($dirs as $dir) {
foreach ($extensions as $extension) {
$file = sprintf('%s/%s%s', $dir, $classStub, $extension);
if (!is_readable($file)) {
continue;
}
include $file;
return;
}
}
};
return spl_autoload_register($callback);
};
The The Symfony2 ClassLoader Component has the benefit to allow more configuration here. You can install it easily via Pear or Composer (symfony/class-loader on Packagist). It is a component on it's own that is used by many and fairly well tested and supported.
SplClassLoader seems like a right choice. It's an implementation proposed by PSR-0 itself.
An exact equivalent of the answer #hakre provided, just shorter:
function autoload($class) {
$path = null;
if (($namespace = strrpos($class = ltrim($class, '\\'), '\\')) !== false) {
$path .= strtr(substr($class, 0, ++$namespace), '\\', '/');
}
require($path . strtr(substr($class, $namespace), '_', '/') . '.php');
}
You can also set the base directory by changing $path = null; to another value, or just do like this:
$paths = array
(
__DIR__ . '/vendor/',
__DIR__ . '/vendor/phunction/phunction.php',
);
foreach ($paths as $path)
{
if (is_dir($path) === true)
{
spl_autoload_register(function ($class) use ($path)
{
if (($namespace = strrpos($class = ltrim($class, '\\'), '\\')) !== false)
{
$path .= strtr(substr($class, 0, ++$namespace), '\\', '/');
}
require($path . strtr(substr($class, $namespace), '_', '/') . '.php');
});
}
else if (is_file($path) === true)
{
require($path);
}
}
The doctrine classloader is another good choice. You can easily install it with composer
This is not a direct answer to the question, but I found that the above answers worked great on standalone PHP scripts, but were causing issues when used in certain frameworks, such as Joomla.
For anyone using Joomla, it turns out that there is a compatible autoloader already built into the framework, therefore you won't need to use the above functions. In that instance, just call JLoader::registerNamespace().... for example:
JLoader::registerNamespace('Solarium', JPATH_LIBRARIES . DS . 'solarium-3.2.0' . DS . 'library');
function autoload($fullClassName) {
$name_elems = explode('\\', $fullClassName);
require __DIR__.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $name_elems).'.php';
}
This even supports things like:
$transformerContstraint = new \Recurr\Transformer\Constraint\AfterConstraint(new DateTime());
Just put it in /vendor/Recurr/Transformer/Constraint/AfterConstraint.php
After 4 years of absence in PHP programming I'm trying to make some new project in it.
I'm collecting some useful libraries. And I have problem with "use" keyword. Here is my part of code where error is thrown.
<?PHP
use Assetic\Asset\AssetCollection;
use Assetic\Asset\FileAsset;
use Assetic\Asset\GlobAsset;
$js = new AssetCollection(array(
...
?>
And I'm getting error:
Fatal error: Class 'Assetic\Asset\AssetCollection' not found in /home/php/index.php on line 7
I thought that is maybe something wrong with include_path in php.ini, but it looks like that:
include_path = ".:/usr/share/php5:/usr/share/php"
Did I miss something?
BTW. I'm using nginx + php-fpm.
The use keyword does not actually include any files. I'm afraid you either have to register an autoload function with the spl_register_autoload() call, or manually include the files.
http://www.php.net/manual/en/function.spl-autoload-register.php
Usually a good default autoloader will look for files following the same path as the namespaces, like this:
spl_autoload_register(
function($className)
{
$className = str_replace("_", "\\", $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;
}
);
More on autloading in PHP, a structure that many (newer) projects are following: http://groups.google.com/group/php-standards/web/psr-0-final-proposal