I have the following php code:
index.php
<?php
spl_autoload_extensions(".php");
spl_autoload_register();
use modules\standard as std;
$handler = new std\handler();
$handler->delegate();
?>
modules\standard\handler.php
<?php
namespace modules\standard {
class handler {
function delegate(){
echo 'Hello from delegation!';
}
}
}
?>
Under Windows 7, running WAMP, the code produces the message "Hello from Delegation!" however under Linux, I get the following:
Fatal error: spl_autoload(): Class modules\standard\handler could not be loaded in /var/www/index.php on line 15
Windows is running PHP 5.3.0 under WAMP, and Linux is running the 5.3.2 dotdeb package under Ubuntu 9.10.
Is this a configuration issue on my linux box, or just a difference in the way namespaces and autoloading is handled on the different operating systems
The SPL autoloader is extremely primitive - it has no knowledge of namespaces, so it tries to load a file with \ in it's name while on Linux/Unix the path separator is / not .
Herman Radtke says he has submitted a patch :
http://www.hermanradtke.com/blog/hidden-features-with-spl_autoload-and-namespaces/
:s
I'm hoping it'll be implemented soon.
For now I use this workaround :
<?php
set_include_path( './classes/' . PATH_SEPARATOR . get_include_path() );
spl_autoload_extensions( '.php , .class.php' );
spl_autoload_register();
function linux_namespaces_autoload ( $class_name )
{
/* use if you need to lowercase first char *
$class_name = implode( DIRECTORY_SEPARATOR , array_map( 'lcfirst' , explode( '\\' , $class_name ) ) );/* else just use the following : */
$class_name = implode( DIRECTORY_SEPARATOR , explode( '\\' , $class_name ) );
static $extensions = array();
if ( empty($extensions ) )
{
$extensions = array_map( 'trim' , explode( ',' , spl_autoload_extensions() ) );
}
static $include_paths = array();
if ( empty( $include_paths ) )
{
$include_paths = explode( PATH_SEPARATOR , get_include_path() );
}
foreach ( $include_paths as $path )
{
$path .= ( DIRECTORY_SEPARATOR !== $path[ strlen( $path ) - 1 ] ) ? DIRECTORY_SEPARATOR : '';
foreach ( $extensions as $extension )
{
$file = $path . $class_name . $extension;
if ( file_exists( $file ) && is_readable( $file ) )
{
require $file;
return;
}
}
}
throw new Exception( _( 'class ' . $class_name . ' could not be found.' ) );
}
spl_autoload_register( 'linux_namespaces_autoload' , TRUE , FALSE );
?>
function __autoload($class_name) {
$paths[] = dirname(__FILE__) . "/../libs/misc/";
$paths[] = dirname(__FILE__) . "/../../libs/misc/";
$paths[] = dirname(__FILE__) . "/../../libs/helpers/";
$paths[] = dirname(__FILE__) . "/../../libs/simpleimage/";
foreach($paths as $path)
{
if(file_exists($path.strtolower($class_name).'.class.php')){
require_once($path.strtolower($class_name).'.class.php');
}
}
}
function __autoload($class_name)
{
$class_name = strtolower(str_replace('\\', DIRECTORY_SEPARATOR, $class_name));
include $class_name . '.php';
}
The srttolower is needed on Apache because it is (contrary to IIS) case sentive.
This is a common problem occurs when autoloading. The fix is to use DIRECTORY_SEPARATOR constant in the autoload function.
So your autoload function will look like following
<?php
spl_autoload_register(function($className) {
$className = str_replace("\", DIRECTORY_SEPARATOR, $className);
include_once $_SERVER['DOCUMENT_ROOT'] . '/class/' . $className . '.php';
});
If you need to learn more on namespace/class autoloading visit here
Thanks.
Related
I have an issue with my autoloader and namespaces.
Below the autoloader
<?php
spl_autoload_register( function( $class ) {
$folder = 'include/';
$prefix = 'class.';
$ext = '.php';
$fullPath = $folder . $prefix . $class . $ext;
if( !file_exists( $fullPath ) ){
print 'Class file not found!';
return false;
}
require_once $fullPath;
});
?>
Below the index file
<?php
require 'autoload.php';
//use backslash for namespace
$pers = new Person\Person();
?>
The file for the class Person is saved in the directory root->include->Person
I used the namespace in the class file like this
<?php
namespace Person;
class Person{
function __construct(){
print 'autoload works';
}
}
?>
If I visit the index file in the browser it returns 'Class file not found'.
Do I use the namespace correctly?
Change your autoload code a little
<?php
spl_autoload_register( function( $class ) {
$folder = 'include/';
$prefix = '.class';
$ext = '.php';
//replace the backslash
$fullPath = $folder . str_replace( "\\", '/', $class ) . $prefix . $ext;
if( !file_exists( $fullPath ) ){
print 'Class file not found!';
return false;
}
require_once $fullPath;
});
?>
You try to include
Include/class.Person\Person.php
If your Os is linux, you must know that have different between / and \
Prefix class is exist in folder name?
This question already has answers here:
Best Way To Autoload Classes In PHP
(8 answers)
Closed 1 year ago.
I'm learning to use php autuoloader...
As much as I understand, we can use __autoloader or spl_autoloader_* to auto load files.
Assume this is my directories structure :
ROOT
|
|
ADMIN
| |
| |
| DIST
| |
| SOME_FOLDER
| SOME_FOLDER
| TPL
| |
| |
| SOME_FOLDER1
| |
| test.php
| SOME_FOLDER2
| |
| example1.php
| example2.php
| example3.php
|
|
CLASSES
|
basics.php
class1.php
class2.php
class3.php
class4.php
|
index.php
I made this class for autoload files in CLASSES directory :
basics.php :
class MyAutoLoader
{
public function __construct()
{
spl_autoload_register( array($this, 'load') );
}
function load( $file )
{
//spl_autoload( 'load1' );
echo 'Try to call ' . $file . '.php inside ' . __METHOD__ . '<br>';
require( $file . '.php' );
}
}
and in index.php I will include basics.php and every thing is fine for files are stored in CLASSES folder...
require_once("CLASSES/basics.php");
$loaderObject = new MyAutoLoader();
with this code, I can declare class1 ... class3
Now I want to have an autoloder that could be load files in SOME_FOLDER2 which in this case are example1.php , example2.php and example3.php
I tried some cases but the files in SOME_FOLDER2 won't be load using autoloader.
My attempts :
I made a function named load2 in MyAutoLoader class that try to include files from SOME_FOLDER2
function load2( $file )
{
//spl_autoload_register('load2');
echo 'Inside LOADER2 ' . __METHOD__ . '<br>';
require ( 'ADMIN/TPL/' . $file . '.php' );
}
And I changed spl_autoload_register in MyAutoLoader constructor :
$allMethods = get_class_methods( 'MyAutoLoader' );
$allMethods = array_splice( $allMethods, 1 );
foreach( $allMethods as $method )
{
spl_autoload_register( array($this, $method) );
}
But none of them didn't work for me...
Would you please tell me what's wrong in my code or what is my misunderstanding about auloader?
Thanks in Advance
I think your problem basically boils down to not checking if the file exists before requireing it... that will produce a fatal error if the file doesn't exist in the first folder that you attempt to include from.
I don't know your use case, but here are some suggestions:
Can you just use a single autoloader?
function my_autoload($class_name) {
if (is_file('CLASSES/' . $class_name . '.php')) {
require_once 'CLASSES/' . $class_name . '.php';
} else if (is_file('ADMIN/TPL/SOME_FOLDER2/' . $class_name . '.php')) {
require_once 'ADMIN/TPL/SOME_FOLDER2/' . $class_name . '.php';
}
}
spl_autoload_register("my_autoload");
Or if you need to declare them independently (ie. two or more autoloaders):
function classes_autoload($class_name) {
if (is_file('CLASSES/' . $class_name . '.php')) {
require_once 'CLASSES/' . $class_name . '.php';
}
}
spl_autoload_register("classes_autoload");
function admin_autoload($class_name) {
if (is_file('ADMIN/TPL/SOME_FOLDER2/' . $class_name . '.php')) {
require_once 'ADMIN/TPL/SOME_FOLDER2/' . $class_name . '.php';
}
}
spl_autoload_register("admin_autoload");
Or make your autoloader class generic:
class MyAutoLoader {
private $path;
public function __construct($path) {
$this->path = $path;
spl_autoload_register( array($this, 'load') );
}
function load( $file ) {
if (is_file($this->path . '/' . $file . '.php')) {
require_once( $this->path . '/' . $file . '.php' );
}
}
}
$autoloader_classes = new MyAutoLoader('CLASSES');
$autoloader_admin = new MyAutoLoader('ADMIN/TPL/SOME_FOLDER2');
If you don't want to manually keep the list of child folders inside ADMIN/TPL up to date you could even do something like this to autoload from any of them (this obviously assumes that all subfolders of ADMIN/TPL contains classes):
function my_autoload($class_name) {
if (is_file('CLASSES/' . $class_name . '.php')) {
require_once 'CLASSES/' . $class_name . '.php';
} else {
$matching_files = glob('ADMIN/TPL/*/' . $class_name . '.php');
if (count($matching_files) === 1) {
require_once $matching_files[0];
} else if (count($matching_files) === 0) {
trigger_error('Could not find class ' . $class_name . '!', E_USER_ERROR);
} else {
trigger_error('More than one possible match found for class ' . $class_name . '!', E_USER_ERROR);
}
}
}
spl_autoload_register("my_autoload");
Your problem may actually be as simple as this:
Your basics.php is inside the folder CLASSES, so when you include or require files from within basics.php it starts in that folder.
So if you want to include files from ADMIN/TPL/SOME_FOLDER2 you actually need to "jump" up one level first, ie require '../ADMIN/TPL/SOME_FOLDER2/' . $file . '.php';
To avoid this kind of confusion I would recommend adding a constant to the beginning of index.php like this:
define('BASEPATH', __DIR__);
...and then prepending that to all require and include statements - ie:
class MyAutoLoader {
public function __construct() {
spl_autoload_register( array($this, 'load') );
spl_autoload_register( array($this, 'load2') );
}
function load( $file ) {
echo 'Try to call ' . $file . '.php inside ' . __METHOD__ . '<br>';
$classfile = BASEPATH . '/CLASSES/' . $file . '.php';
if ( is_file( $classfile ) ) {
require( $classfile );
}
}
function load2( $file ) {
echo 'Try to call ' . $file . '.php inside ' . __METHOD__ . '<br>';
$classfile = BASEPATH . '/ADMIN/TPL/SOME_FOLDER2/' . $file . '.php';
if ( is_file( $classfile ) ) {
require( $classfile );
}
}
}
I'm trying to build my own framework for internal usage.
I got structure like this:
index.php
boot /
booter.php
application /
controllers /
indexcontroller.php
core /
template.class.php
model.class.php
controller.class.php
cache /
memcached.php
something /
something.php
Booter.php contains: (it's currently working only with files located in core directory):
class Booter
{
private static $controller_path, $model_path, $class_path;
public static function setDirs($controller_path = 'application/controllers', $model_path = 'application/models', $classes_path = 'core')
{
self::$controller_path = $controller_path;
self::$model_path = $model_path;
self::$class_path = $classes_path;
spl_autoload_register(array('Booter', 'LoadClass'));
if ( DEBUG )
Debugger::log('Setting dirs...');
}
protected static function LoadClass($className)
{
$className = strtolower($className);
if ( file_exists(DIR . '/' . self::$model_path . '/' . $className . '.php') )
{
require(DIR . '/' . self::$model_path . '/' . $className . '.php');
}
else if ( file_exists(DIR . '/' . self::$class_path . '/' . $className . '.class.php') )
{
require(DIR . '/' . self::$class_path . '/' . $className . '.class.php');
}
else if ( file_exists(DIR . '/' . self::$controller_path . '/' . $className . '.php') )
{
require(DIR . '/' . self::$controller_path . '/' . $className . '.php');
}
if ( DEBUG )
Debugger::log('AutoLoading classname: '.$className);
}
}
My application/controllers/indexcontroller looks like this:
<?
class IndexController extends Controller
{
public function ActionIndex()
{
$a = new Model; // It works
$a = new Controller; //It works too
}
}
?>
And here comes my questions:
[Question 1]
My code is working currently like this:
$a = new Model; // Class Model gets included from core/model.class.php
How I can implement including files by classes with namespaces? For example:
$a = new Cache\Memcached; // I would like to include file from /core/CACHE/Memcached.php
$a = new AnotherNS\smth; // i would like to include file from /core/AnotherNS/smth.php
and so on. How I can produce the handling of the namespace?
[Question 2]
Is it a good practice to use single autoload for Classes, Controllers and models or I should define 3 different spl_autoload_register with 3 different methods and why?
I normally have a bootstrap.php file inside a folder conf that is in the application root. My code is normally located inside an src folder, also located in the root, so, this works fine for me:
<?php
define('APP_ROOT', dirname(__DIR__) . DIRECTORY_SEPARATOR);
set_include_path(
implode(PATH_SEPARATOR,
array_unique(
array_merge(
array(
APP_ROOT . 'src',
APP_ROOT . 'test'
),
explode(PATH_SEPARATOR, get_include_path())
)
)
)
);
spl_autoload_register(function ($class) {
$file = sprintf("%s.php", str_replace('\\', DIRECTORY_SEPARATOR, $class));
if (($classPath = stream_resolve_include_path($file)) != false) {
require $classPath;
}
}, true);
You can generalize this into your "Booter" class and append directories to the include path. If you have well defined namespace names, there will be no problems with colisions.
Edit:
This works if you follow PSR-1.
Question 1:
In your autoloader, change the \ (for the namespace) into DIRECTORY_SEPARATOR. This should work:
protected static function LoadClass($className)
{
$className = strtolower($className);
$className = str_replace('\\', DIRECTORY_SEPARATOR, $className);
...
}
Always use DIRECTORY_SEPARATOR, especially if the software has the potential to be used on other platforms.
Question 2:
I would use one and separate your classes by namespace. However, I think that comes down to how you want to structure your framework and how you separate your code. Someone else may be able to better answer that question.
I'm writing an application where all classes are using namespaces, and using spl_autoload_register() to load all classes dynamically.
Now I want to make use of a non-namespaced library (WideImage). As WideImage does not use namespaces, spl_autoload_register() does not work. So included the script manually:
require( 'Library/WideImage/WideImage.php');
$w = new WideImage();
But it still tries to autoload; and gives a fatal class not found error.
How can I override this autoload function?
By request:
spl_autoload_register(function( $class ) {
$path = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . CMS_PATH . DIRECTORY_SEPARATOR;
$classFile = str_replace( '\\', DIRECTORY_SEPARATOR, $class );
$classPI = pathinfo( $classFile );
$classPath = $path . $classPI[ 'dirname' ] ;
$file = $classPath . DIRECTORY_SEPARATOR . $classPI[ 'filename' ] . '.class.php';
if (file_exists($file)) {
require_once( $file );
}
});
Edit: solution:
Change
if (file_exists($file)
to
if (file_exists($file) && !class_exists($class)) {
Try to use class_exists() before loading.
You can register multiple autoloaders with spl_autoload_register. This way you can write autoloaders for your own calsses and libraries. They trigger after each other and should return true or false - if the loaded souccessfully or not.
I've a project like this:
Now i want to autoload all the php files in the folder classes and sub folders.
I can do that with this:
$dirs = array(
CMS_ROOT.'/classes',
CMS_ROOT.'/classes/layout',
CMS_ROOT.'/classes/layout/pages'
);
foreach( $array as $dir) {
foreach ( glob( $dir."/*.php" ) as $filename ) {
require_once $filename;
}
}
But i dont like this. For example.
"layout/pages/a.php" extends "layout/pages/b.php"
Now i get an error because a.php was loaded first. How do you people load your project files? Classes?
SOLVED :)
This is my code now:
spl_autoload_register('autoloader');
function autoloader($className) {
$className = str_replace('cms_', '', $className);
$className = str_replace('_', '/', $className);
$file = CLASSES.'/'.$className.'.php';
if( file_exists( $file ) ) {
require_once $file;
}
}
You should try this
<?php
spl_autoload_register('your_autoloader');
function your_autoloader($classname) {
static $dirs = array(
CMS_ROOT.'/classes',
CMS_ROOT.'/classes/layout',
CMS_ROOT.'/classes/layout/pages'
);
foreach ($dirs as $dir) {
if (file_exists($dir . '/'. $classname. '.php')) {
include_once $dir . '/' . $classname . '.php';
}
}
}
After registering your_autoloader with spl_autoload_register() it will be called by the php interpreter every time you access a class that:
Has not already been loaded with require_once() or include_once()
Is not part of the PHP internals