很多开发者写面向对象的应用程序时对每个类的定义建立一个 PHP 源文件。一个很大的烦恼是不得不在每个脚本开头写一个长长的包含文件列表(每个类一个文件)。
在 PHP 5 中,不再需要这样了。可以定义一个 __autoload() 函数,它会在试图使用尚未被定义的类时自动调用。通过调用此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。
spl_autoload_register() 提供了一种更加灵活的方式来实现类的自动加载。因此,不再建议使用 __autoload() 函数,在以后的版本中它可能被弃用。
Note:
在 5.3.0 版之前,__autoload 函数抛出的异常不能被 catch 语句块捕获并会导致一个致命错误。从 5.3.0+ 之后,__autoload 函数抛出的异常可以被 catch 语句块捕获,但需要遵循一个条件。如果抛出的是一个自定义异常,那么必须存在相应的自定义异常类。__autoload 函数可以递归的自动加载自定义异常类。
Note:
自动加载不可用于 PHP 的 CLI 交互模式。
Example #1 自动加载示例
本例尝试分别从 MyClass1.php 和 MyClass2.php 文件中加载 MyClass1 和 MyClass2 类。
<?php
function __autoload($class_name) {
require_once $class_name . '.php';
}
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
Example #2 另一个例子
本例尝试加载接口 ITest。
<?php
function __autoload($name) {
var_dump($name);
}
class Foo implements ITest {
}
/*
string(5) "ITest"
Fatal error: Interface 'ITest' not found in ...
*/
?>
Example #3 自动加载在 PHP 5.3.0+ 中的异常处理
本例抛出一个异常并在 try/catch 语句块中演示。
<?php
function __autoload($name) {
echo "Want to load $name.\n";
throw new Exception("Unable to load $name.");
}
try {
$obj = new NonLoadableClass();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}
?>
以上例程会输出:
Want to load NonLoadableClass. Unable to load NonLoadableClass.
Example #4 自动加载在 PHP 5.3.0+ 中的异常处理 - 没有自定义异常机制
本例将一个异常抛给不存在的自定义异常处理函数。
<?php
function __autoload($name) {
echo "Want to load $name.\n";
throw new MissingException("Unable to load $name.");
}
try {
$obj = new NonLoadableClass();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}
?>
以上例程会输出:
Want to load NonLoadableClass. Want to load MissingException. Fatal error: Class 'MissingException' not found in testMissingException.php on line 4
Richard Lynch (2013-06-19 17:50:14)
"write a long list of needed includes at the beginning of each script"
Errrr. Wouldn't you put that long list in one file and 'require' that?
Christian Mnch (2013-03-28 07:16:00)
Could it be that autoloading in interactive mode works since PHP 5.4.
I added a auto prepend script which registers the composer autoloader.
It works for me in PHP 5.4.6-1ubuntu1.2 and it fails with 5.3.10-1ubuntu3.5
tlang at halsoft dot com (2013-02-14 21:56:48)
This page states that autoloading does not work when PHP is used in CLI mode but a simple test seems to contradict this.
Create a file /tmp/Foo.php containing:
<?php
class Foo {
public function __construct() {
echo "Inside the Foo constructor\n";
}
}
?>
Create a script (NOT in /tmp) containing:
<?php
function test_autoload($class) {
require_once '/tmp/'.$class.'.php';
}
spl_autoload_register('test_autoload');
$foo = new Foo();
?>
Execute the script on the command line. The echo statement in the constructor produces output to STDOUT.
This also works with __autoload
<?php
function __autoload($class) {
require_once '/tmp/'.$class.'.php';
}
$foo = new Foo();
?>
shaftouille (2012-01-12 01:14:42)
I think there shouldn't be tests in an autoload callback function, this callback will trigger because the class you're trying to load is not defined... in any case if your class is not defined, the code must fail. Therefore an autoload function should be like :
<?php
spl_autoload_register(function($className)
{
require(str_replace('\\', '/', ltrim($className, '\\')) . '.php');
});
?>
As the "require" function uses the include_path, the folders for the autoinclusion should be added using set_include_path, let's say your index.php is located in a "public" directory and your MVC classes are in "core", the index.php should be :
<?php
define('ROOT_DIR', realpath(__DIR__ . '/..'));
set_include_path(ROOT_DIR . PATH_SEPARATOR . get_include_path());
?>
And of course you want to catch the loading errors, so you can use class_exists :
<?php
$className = '\core\Controller\Hello\World';
if (!class_exists($className))
{
throw new \ErrorException('Class Not Found !');
}
else
{
$object = new $className();
}
?>
This code sample will autoload the "World.php" file located in your "core/Controller/Hello" directory, assuming that your class declaration is like :
<?php
namespace core\Controller\Hello;
class World
{
function __construct()
{
echo "Helloworld";
}
}
?>
qfox at ya dot ru (2011-11-16 14:48:18)
More simpler example of using spl_autoload_register in 5.3:
<?php
spl_autoload_register(function($classname) /* usign $app */ {
# ... your logic to include classes here
});
?>
Nic (2011-08-25 12:22:00)
Autoloading camel case classes, ie. loading "controller/product/detail.php" from "ControllerProductDetail":
<?php
// Autoloading
function __autoload($class) {
$path = strtolower(preg_replace('/([a-z])([A-Z])/', '$1/$2', $class));
if(file_exists(DIR_APPLICATION . $path . '.php')) {
require_once(DIR_APPLICATION . $path . '.php');
}
}
?>
bimal at sanjaal dot com (2011-06-05 11:58:53)
When multiple scripts try to use __autoload(), there is a collision.
I experienced it while upgrading the Smarty and my own autoload function.
So, the better way is to avoid __autoload() and use spl_register_autoload() instead. If you have written this already, just rename your function to something like __autoload_my_classes, and in the next, call spl_autoload_register as:
<?php
function __autoload_my_classes($classname)
{
# ... your logic to include classes here
}
spl_autoload_register('__autoload_my_classes');
?>
You can assign multiple functions to spl_autoload_register() by calling it repeatedly with different function names. Be sure to limit your every function to include a CLASS file.
rahj_1986 at (yahoo) dot com (2011-05-30 22:22:10)
in my case i use config.php and put all the settings there including the _autoload functions. Just include the config.php in all of your php file. Heres how:
config.php:
<?php
function __autoload($classname) {
include_once("classfolder/" . $classname . ".php");
}
?>
ClassFolder: with SampleClass.php
<?php
class SampleClass {
public function hello_rahj() {
echo "Hello rahj!";
}
}
?>
In your Typical Php File: e.g. Index.php
<?php
include("config.php")
$hello = new SampleClass();
$hello->hello_rahj(); // outputs Hello rahj!
?>
When using the __autoload functions, Make Sure you use a Proper naming Convention.
Hope this Helps!
superzouz at hotmail dot com (2011-05-01 16:19:59)
After a while trying to get __autoload() to work, I used spl_register_autoload instead. __autoload() unexplainably failed so I gave up on it.
T dot Alexander Lystad tal at lystadonline dot no (2011-02-11 07:19:56)
This is an example of how I used autoloading in my application:
<?php
spl_autoload_register(function ($className) {
$possibilities = array(
APPLICATION_PATH.'beans'.DIRECTORY_SEPARATOR.$className.'.php',
APPLICATION_PATH.'controllers'.DIRECTORY_SEPARATOR.$className.'.php',
APPLICATION_PATH.'libraries'.DIRECTORY_SEPARATOR.$className.'.php',
APPLICATION_PATH.'models'.DIRECTORY_SEPARATOR.$className.'.php',
APPLICATION_PATH.'views'.DIRECTORY_SEPARATOR.$className.'.php',
$className.'.php'
);
foreach ($possibilities as $file) {
if (file_exists($file)) {
require_once($file);
return true;
}
}
return false;
});
?>
b dot rense at gmail dot com (2010-12-01 08:54:45)
php autoloading is a b*tch, nevertheless, i think the following class is a pretty good solution.
<?php
class autoload_autoloader {
public static $instance;
private $_src=array('application/controllers/', 'application/models/', 'application/views/helpers/', 'library/');
private $_ext=array('.php', 'class.php', 'lib.php');
/* initialize the autoloader class */
public static function init(){
if(self::$instance==NULL){
self::$instance=new self();
}
return self::$instance;
}
/* put the custom functions in the autoload register when the class is initialized */
private function __construct(){
spl_autoload_register(array($this, 'clean'));
spl_autoload_register(array($this, 'dirty'));
}
/* the clean method to autoload the class without any includes, works in most cases */
private function clean($class){
global $docroot;
$class=str_replace('_', '/', $class);
spl_autoload_extensions(implode(',', $this->_ext));
foreach($this->_src as $resource){
set_include_path($docroot . $resource);
spl_autoload($class);
}
}
/* the dirty method to autoload the class after including the php file containing the class */
private function dirty($class){
global $docroot;
$class=str_replace('_', '/', $class);
foreach($this->_src as $resource){
foreach($this->_ext as $ext){
@include($docroot . $resource . $class . $ext);
}
}
spl_autoload($class);
}
}
?>
Obviously you still have to include this class the dirty way, for example:
<?php
$path='../';
$docroot=$_SERVER['DOCUMENT_ROOT'] . implode('/',array_slice(explode('/',$_SERVER['PHP_SELF']),0,-2)) . '/';
include($path . 'library/autoload/autoloader.php');
autoload_autoloader::init();
?>
please note that this autoloader class does require the $docroot variable which is the absolute path of the root of your application, you can set it manually or copy the code i used.
my directory structure looks like this:
- www/
+ myapp/
+ application/
+ controllers/
+ models/
+ views/
+ helpers/
+ library/
+ autoload/
- autoloader.php
fka at fatihkadirakin dot com (2010-07-31 18:16:34)
Or you can use this, without using any "require/include":
<?php
class autoloader {
public static $loader;
public static function init()
{
if (self::$loader == NULL)
self::$loader = new self();
return self::$loader;
}
public function __construct()
{
spl_autoload_register(array($this,'model'));
spl_autoload_register(array($this,'helper'));
spl_autoload_register(array($this,'controller'));
spl_autoload_register(array($this,'library'));
}
public function library($class)
{
set_include_path(get_include_path().PATH_SEPARATOR.'/lib/');
spl_autoload_extensions('.library.php');
spl_autoload($class);
}
public function controller($class)
{
$class = preg_replace('/_controller$/ui','',$class);
set_include_path(get_include_path().PATH_SEPARATOR.'/controller/');
spl_autoload_extensions('.controller.php');
spl_autoload($class);
}
public function model($class)
{
$class = preg_replace('/_model$/ui','',$class);
set_include_path(get_include_path().PATH_SEPARATOR.'/model/');
spl_autoload_extensions('.model.php');
spl_autoload($class);
}
public function helper($class)
{
$class = preg_replace('/_helper$/ui','',$class);
set_include_path(get_include_path().PATH_SEPARATOR.'/helper/');
spl_autoload_extensions('.helper.php');
spl_autoload($class);
}
}
//call
autoloader::init();
?>
takingsides at gmail dot com (2010-06-30 08:15:44)
file_exists would work perfectly fine, unless people intentionally create directories ending with ".php". However sometimes classes can be called either "MyClass.php" or "MyClass.class.php"...
I have a number of configuration constants defined (before the autoload function) i.e:
<?php
// in: config.php
define('DEV_MODE', 1);
$error_reporting = error_reporting(DEV_MODE ? E_ALL : 0);
define('CONF_DIR_LIB', '/path/to/classes/');
?>
Finally inside the config.php after my constants, i define the autoload...
A. DEV_MODE when 1 or true, will show all errors.
B. DEV_MODE when 0 or false, will supress ALL errors including those thrown from inside classes.
<?php
// also in: config.php
function __autoload($f)
{
if (($f1 = file_exists($c1 = CONF_DIR_LIB . "$f.class.php"))
|| ($f2 = file_exists($c2 = CONF_DIR_LIB . "$f.php")))
if (DEV_MODE)
include_once ($f1 ? $c1 : $c2);
else
@include_once ($f1 ? $c1 : $c2);
}
?>
I hope this helps anyone :)
DaWG (2010-05-12 09:21:14)
Be careful with the use of file_exists, if there were a directory with the name ($classname . "php") then file_exists would return true. (is_file "may" be preferred, YMMV).
<?php
if (is_file($className . '.php')) { ...
?>
ej at campbell dot name (2010-04-03 11:21:41)
You don't need exceptions to figure out if a class can be autoloaded. This is much simpler.
<?php
//Define autoloader
function __autoload($className) {
if (file_exists($className . '.php')) {
require_once $className . '.php';
return true;
}
return false;
}
function canClassBeAutloaded($className) {
return class_exists($className);
}
?>
tom at r dot je (2010-03-11 03:30:28)
To find out whether a class can be autoloaded, you can use autoload in this way:
<?php
//Define autoloader
function __autoload($className) {
if (file_exists($className . '.php')) require $className . '.php';
else throw new Exception('Class "' . $className . '" could not be autoloaded');
}
function canClassBeAutloaded($className) {
try {
class_exists($className);
return true;
}
catch (Exception $e) {
return false;
}
}
?>
Anonymous (2010-02-08 15:35:17)
It's worth to mention, if your operating system is case-sensitive you need to name your file with same case as in source code eg. MyClass.php instead of myclass.php
EastGhostCom (2009-12-23 09:04:57)
IMPORTANT: Look into spl_autoload_register() which lets you manage a stack of several __autoload() functions.
khan at swcombine dot com (2009-11-05 05:38:52)
As an addendum to #91119 I would suggest adding class_exists() into that solution. I've just implemented autoloading based on the code provided there and ran into a problem where a file had the same name as a class, existed in the directory structure prior to the file that had the actual class and as a result was being included first and resulting in a 'class not found' error.
<?php
if(file_exists($path)) {
require_once $path;
if(class_exists($class_name)) {
return;
}
}
?>
h762017(at)stud(dot)u-szeged(dot)hu (2009-08-20 19:24:11)
Hi,
Because the scripting engine just find the class declaration in the body of the __autoload() function, you also can declare the missing class in the __autoload() function. (No need to include or require a file.)
Let's see the following code:
Example 1.:
<?php
function __autoload($className){
echo "Now loading: $className<br />";
class SuperClass {
}
}
class DerivedClass extends SuperClass {
}
class AnotherDerivedClass extends SuperClass {
}
?>
The scripting engine will found the SuperClass class.
Example 2.:
You also can do it with the eval function, and if you dinamycally declare the class, you don't get a Fatal Error, and you can do many interesting things with the eval function;)
<?php
function __autoload($className){
echo "Now loading: $className<br />";
eval("class $className {}");
}
class DerivedClass extends SuperClass {
}
class AnotherDerivedClass extends SuperClass {
}
?>
Peminator (2009-07-07 07:01:06)
My idea for autoloading FUNCTIONS however only in a weird way :
<?php
function ex($parms)
{
$argvar = func_get_args();
$func = $argvar[0];
$funcargs = array_slice($argvar,1);
if (function_exists($func))
{
$returnvalue = call_user_func_array($func,$funcargs);
}
else
{
$funcpath = "scripts/".$func.".php";
require_once($funcpath);
if (function_exists($func))
{
$returnvalue = call_user_func_array($func,$funcargs);
}
else
{
die "SORRY $func IS NOT USABLE";
}
}
// return returned value :-)
return $returnvalue;
}
?>
USAGE EXAMPLE:
must be caled using the X function giving the real function as first parameter, like:
$result = ex("add",1,2);
// returns 3 if add function defined in add.php sums the first and second parameter..
ranskills at yahoo dot co dot uk (2009-05-26 04:02:09)
This custom autoload function allows for the flexibility to add directories from which the classes are expected to be found and loads classes/interfaces with different file naming formats. Additionally, it handles classes included in the include path and files in the PEAR package/directory.
You can check this blog entry for a video tutorial http://ranskills.com/blog/.
<?php
/**
*
* @param string $className Class or Interface name automatically
* passed to this function by the PHP Interpreter
*/
function autoLoader($className){
//Directories added here must be
//relative to the script going to use this file.
//New entries can be added to this list
$directories = array(
'',
'classes/'
);
//Add your file naming formats here
$fileNameFormats = array(
'%s.php',
'%s.class.php',
'class.%s.php',
'%s.inc.php'
);
// this is to take care of the PEAR style of naming classes
$path = str_ireplace('_', '/', $className);
if(@include_once $path.'.php'){
return;
}
foreach($directories as $directory){
foreach($fileNameFormats as $fileNameFormat){
$path = $directory.sprintf($fileNameFormat, $className);
if(file_exists($path)){
include_once $path;
return;
}
}
}
}
spl_autoload_register('autoLoader');
?>
contact at casual-web dot com (2009-04-29 09:22:33)
Hi,
To include all classes from differents directories, you can specify the path in the name of class.The replace a caracter of your choice by '/'. Here I used "_".
ex : the class : dir1_dir2_myclass.class.php will be in dir1/dir2/
<?php
function __autoload($className) {
$fullclasspath="";
// get separated directories
$pathchunks=explode("_",$className);
//re-build path without last item
for($i=0;$i<(count($pathchunks)-1);$i++) {
$fullclasspath.=$pathchunks[$i].'/';
}
require_once '../../'.$fullclasspath.$className.'.class.php';
}
?>*
bye
CasualWeb
roman dot drapeko at gmail dot com (2009-04-05 04:33:33)
Hi there,
I have developed a small script, that can scan recursively folders and files ang generate array of associations between classes/interfaces and their locations. It accepts several incoming parameters and it's very simple to use.
An example of generated array is shown bellow.
<?php
$autoload_list = array (
'classes' => array (
'A' => array ('path' => 'Project/Classes/Children/A.php',
'extends' => array (), 'implements' => array ('I1')),
'C' => array ('path' => 'Project/Classes/C.php',
'extends' => array ('B'), 'implements' => array ('I1', 'I3')),
),
'interfaces' => array (
'I2' => array ('path' => 'Project/Interfaces/blablabla.php', 'extends' => array ('I1')),
'I1' => array ('path' => 'Project/Interfaces/I1.php', 'extends' => array ()),
),
);
?>
When you know names and their locations, you know everything to load these classes.
It uses regular expressions to identify if class/interfaces is located in the current file.
I tried to post the code, but it's very long. You can download the script from http://wp.drapeko.com/store/php-autoloading-files/.
claude dot pache at gmail dot com (2009-03-11 19:04:55)
About static classes that need initialisation before use (problem discussed by adam at greatbigmassive dot net and kalkamar at web dot de below).
Simple problems have often simple solutions. Here is my approach:
First, my __autoload function is very simple:
<?php
function __autoload ($class_name)
{
if (preg_match('|^\w+$|', $class_name))
include "./packages/$class_name.php";
}
?>
(The "if/preg_match" line is just a simple yet robust security check. Moreover I use "include" and not "require"/"require_once", so that if the file is not found, the __autoload function does nothing, and my script dies eventually with a meaningful "Class 'foo' not found" fatal error.)
Now, when I define a class "foo" which requires initialisation before use, I just write the initialisation code after the definition of the class in the file "packages/foo.php":
<?php
/** Content of file "packages/foo.php" **/
class foo
{
/* definition of the class is found here */
}
/* initialisation code of the class is found here. */
/** End of file "packages/foo.php" **/
?>
That's it. No need for an <? init() ?> or a <? __construct() ?> method.
jarret dot minkler at gmail dot com (2009-03-07 22:55:20)
You should not have to use require_once inside the autoloader, as if the class is not found it wouldn't be trying to look for it by using the autoloader.
Just use require(), which will be better on performance as well as it does not have to check if it is unique.
adam at greatbigmassive dot net (2009-02-25 03:31:19)
I've been playing with the idea of automatically running the __construct() function after initiating a static/singleton class.
As we know, it doesn't run unless you create new instance() of the class which doesn't happen on the singleton method.
I've seen the methods of using the "getInstance" call but this is a manual call that I wanted to avoid. As an additional idea I've done this and would be interested in opinions. Seems to work for me and my framework and it means I don't have to run the __construct() manually via the internal "new self" method. Obviously, in this case, I probably don't even need to call the booter function __construct() but for relationship recognition I have.
Basic idea
----------
Make the __construct() protected instead of private.
This means an extended class can access/override it.
Then, in autoload, I use eval to extend the class I've just initiated which simply runs the parent __construct() if it exists..
<?php
function __autoload($class){
require_once(INC_FILES."/class/class.".$class.".php");
if(method_exists($class,'__construct')){
eval("class al_$class extends $class{function __construct(){parent::__construct();}}new al_$class;");
}
return true;
}
?>
pinochet dot pl at gmail dot com (2009-01-04 13:11:04)
To use autoload function with namespaces you should remember to define it in main scope in "\" namespace.
zachera (2008-12-31 16:55:30)
I found out a neat way to centralize one single class which will give accessibility to other classes. I also added a parameter to the __construct method which would be an array of classes you want loaded. This isn't completely necessary, but it will stop "excessive memory" if you're loading a bunch of unused classes.
<?php
class Bot {
private $classes = array (
'Socket' => "connection/class.Socket.php",
'Input' => "io/class.Input.php",
'Output' => "io/class.Output.php",
'Parse' => "io/parse/class.Parse.php"
);
public $Socket, $Input, $Output, $Parse; // Accessible by other classes
public function __construct($load=false){
if(is_array($load)){
foreach($load as $class){
if(isset($this->classes[$class])){
require($this->classes[$class]);
$this->$class = new $class($this);
}
}
} else {
foreach($this->classes as $class => $path){
require($path);
$this->$class = new $class($this);
}
}
}
}
?>
kalkamar at web dot de (2008-10-07 10:23:41)
Because static classes have no constructor I use this to initialize such classes.
The function init will (if available) be called when you first use the class.
The class must not be included before, otherwise the init-function wont be called as autoloading is not used.
<?php
function __autoload($class_name)
{
require_once(CLASSES_PATH.$class_name.'.cls.php');
if(method_exists($class_name,'init'))
call_user_func(array($class_name,'init'));
return true;
}
?>
I use it for example to establish the mysql-connection on demand.
It is also possilbe do add a destructor by adding this lines to the function:
<?php
if(method_exists($class_name,'destruct'))
register_shutdown_function(array($class_name,'destruct'));
?>
jbarker at erepublic dot com (2008-09-17 08:34:05)
In a subclass, I was trying to call an overridden parent method with an arbitrary number of arguments:
<?php
call_user_func_array(array('parent', 'someNonStaticMethod'), $args);
?>
It turns out this triggers an E_STRICT level warning. So I changed to this:
<?php
call_user_func_array(array($this, 'parent::someNonStaticMethod'), $args);
?>
This doesn't trigger any warnings, but it has the undesirable (if not downright buggy) effect of calling my __autoload() function with the argument 'parent'. I had to modify __autoload() to handle this special situation:
<?php
function __autoload($cls)
{
if ('parent' != $cls)
{
require("class.$cls.php");
}
}
?>
Tested on Linux with PHP 5.1.6 and 5.2.5.
andrzeje from wit.edu.pl (2008-09-13 20:18:00)
Throwing versions of __autoload based on eval will fail if __autoload will be caled with interface name.
<?php
/* eval+throw __autoload
*/
class Cls implements Iface {/* ... */}; // Error
?>
sebasg37 at gmail dot com (2008-08-23 16:09:25)
When __autoload doesn't seem to work or if you are in PHP 4:
<?php
$include_paths = explode( PATH_SEPARATOR , get_include_path() );
foreach( $include_paths as $path ) {
if( is_dir( $path ) ) {
$d = opendir( $path );
while (false !== ($f = readdir($d))) {
if( ereg( "^.*\.php$" , $f ) && strstr( $path , ABS_PATH ) ) { ## avoid PEAR and other files for automatic requirement
require_once $f;
}
}
closedir($d);
}
}
// replace ABS_PATH with your app root
?>
matias dot cohen at gmail dot com (2008-08-01 16:33:53)
Another way of throwing exceptions inside an __autoload() function:
<?php
function myExceptionHandler($e) {
// Add code here
}
set_exception_handler('myExceptionHandler');
function __autoload($class) {
if (class_exists($class, false) || interface_exists($class, false)) {
return;
}
try {
@require_once('path/to/' . $class . '.php');
if (!class_exists($class, false) || !interface_exists($class, false)) {
throw new Exception('Class ' . $class . ' not found');
}
}
catch (Exception $e) {
myExceptionHandler($e);
}
}
?>
chris (at) xeneco (dot) co (dot) uk (2008-07-04 06:21:35)
I'm very taken with the autoload function, and thought I would share with you my implementation of it:
<?php
function __autoload($class_name) {
//my settings class is a singleton instance that has parsed an ini file containing the locations of all classes
$settings = Settings::Load();
$classes = $settings->getSettings('classes');
$path = $classes[$class_name];
if(file_exists($path)) {
require_once($path);
return true;
} else {
clearstatcache();
$classes = $settings->ReLoadSettings();
$path = $classes['classes'][$class_name];
}
if(file_exists($path)) {
require_once($path);
return true;
} else {
die("The requested library,".$class_name.", could not be found at ".$classes[$class_name][$i].". Please check your ini file");
}
}
?>
Chris Continanza (2008-04-29 11:43:08)
Decided to warm up to autoload,
but wanted it to use the include_path.
Good default behavior.
function __autoload($class_name) {
$include_path = get_include_path();
$include_path_tokens = explode(':', $include_path);
foreach($include_path_tokens as $prefix){
$path = $prefix . '/' . $class_name . '.php';
if(file_exists($path)){
require_once $path;
return;
}
}
}
james dot dot dot dunmore at gmail dot com (2008-04-18 04:43:16)
Andrew: 03-Nov-2006 12:26
That seems a bit messy to me, this is a bit neater:
<?php
function __autoload($class_name)
{
//class directories
$directorys = array(
'classes/',
'classes/otherclasses/',
'classes2/',
'module1/classes/'
);
//for each directory
foreach($directorys as $directory)
{
//see if the file exsists
if(file_exists($directory.$class_name . '.php'))
{
require_once($directory.$class_name . '.php');
//only require the class once, so quit after to save effort (if you got more, then name them something else
return;
}
}
}
jjfitzgerald at gmail dot com (2008-01-30 07:39:26)
Regardless of the situation you're in (developing a web-app or writing some code that may use someone else's web-app), always a good idea not to use __autoload() but instead write a differently-named function and register it as an autoloader:
<?php
function class_autoloader($c) {
if(file_exists(dirname(__FILE__). "/classes/{$c}.php") && include_once(dirname(__FILE__). "/classes/{$c}.php")) {
return true;
} else {
trigger_error("Could not load class '{$c}' from file '{$c}.php'", E_USER_WARNING);
return false;
}
}
spl_autoload_register("class_autoloader");
?>
Using spl_autoload_register(), the function class_autoloader() will now be used when classes are instantiated, just like __autoload() does, but without the potential conflict with other web-apps __autoload() function.
muratyaman at gmail dot com (2008-01-28 07:57:10)
__autoload() function can be very useful to optimize your code esp. when you have so many classes.
Unlike class extensions, optional parameters with class restrictions may not load your class.
<?php
class bClass{
function fun($p1, aClass $p2=NULL){
//do something
}
}
//depending on the usage
$b = new bClass();
$b->fun('No!');//this will not load class file for aClass
$b->fun('Really?', new aClass('Yes!'));//this will
?>
So, it's very encouraging to use classes everywhere!
Even encapsulating your functions inside simple classes to use like static modules, will help a lot!
Let's say, you have <b>50k</b> lines of code inside <b>100</b> classes/files.. If you need a simple task to do very quickly, you should not be loading all of those files, except the ones you need.
Though, it may be dangerous on some cases regarding the dependencies, load order, etc. Carefully design your classes.
Mexi-Fry (2008-01-16 05:19:46)
Seems like a strange layer of obscurity to me (to be honest). It would be really handy if that could be implemented and remain in memory though, enabling pages to instantiate classes without having to repeatedly include documents. Being able to construct an object without having to include the document that contained it (AND without having to include that function in some form on each document) would be very nice.
boltclock aInACircle NOVALISTIC dot com (2008-01-11 21:31:07)
You can allow __autoload() to throw exceptions without resulting in fatal errors by trying class_exists(), literally.
<?php
function __autoload($class)
{
if (file_exists($file = "./inc/$class.php"))
{
include($file);
}
else
{
throw new Exception("Class $class not found");
}
}
// By 'literally', I mean this
try
{
// Since the second argument defaults to true, __autoload()
// will be called from this function
class_exists('MyClass');
}
catch (Exception $e)
{
// Catch the exception and handle it as usual
die($e->getMessage());
}
// Safely initialize an object from the class
$class = new MyClass();
?>
ostapk (2007-12-19 14:46:48)
To sandrejev at gmail dot com below:
if you create the exception in the __autoload() and serialize it, then when you try to access the same missing class later (in another place in the code), then the exception will contain invalid stack trace.
Also, here's an excellent blog post that discusses the consequences of using eval() as well as provides an example to handle static method calls and namespaces: http://www.onphp5.com/article/61
richard [at ] xanox [dot] net (2007-12-05 07:46:38)
I've made this little script here which looks in a dir, and loads all the classed, and includes their files.
$myDirectory = opendir("required/classes");
// get each entry
while($entryName = readdir($myDirectory)) {
$dirArray[] = $entryName;
}
// close directory
closedir($myDirectory);
// count elements in array
$indexCount = count($dirArray);
sort($dirArray);
for($index=0; $index < $indexCount; $index++) {
if($dirArray[$index] != '.' AND $dirArray[$index] != '..') {
include("required/classes/$dirArray[$index]");
$classname = strtolower($dirArray[$index]);
$classname = str_replace('.php','',$classname);
$classinit = str_replace('.php','',$dirArray[$index]);
$$classname = new $classinit;
}
}
rojoca (2007-10-04 20:38:44)
Be careful when using eval (as always) in __autoload. The following:
<?php
echo 'Start->';
function __autoload($class) {
eval('class ' . $class . ' {};');
}
$class = 'Class1{}; echo "uh oh"; class Class2';
$obj = new $class;
echo 'end';
?>
outputs:
Start->uh oh
You can use preg_replace to clean up $class to prevent executing abitrary code but in this case you won't be able to throw a catchable exception and your script will end with a fatal error.
christian.reinecke at web.de (2007-09-23 09:28:12)
do not use is_subclass_of() in your __autoload() function to identify a class type and thereby its path (f.e exceptions). is_subclass_of() needs to know the class, but you want to check BEFORE you include the class.
knoopx at gmail dot com (2007-09-06 12:56:13)
Quick and easiest way to be able to throw exceptions on __autoload() method:
<?php
function __autoload($class_name) {
find_file($class_name); //your own routine to locate the class file
if (!class_exists($class_name, false)) {
eval("class $class_name {}");
throw new Exception('Class not found');
}
}
?>
rlee0001 at sbcglobal dot net (2007-08-30 13:16:22)
If you would like __autoload to throw an exception when the class cannot be loaded instead of causing a fatal error, consider this:
<?php
function __autoload ($className)
{
$fileName = str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
$status = (@include_once $fileName);
if ($status === false) {
eval(sprintf('class %s {func' . 'tion __construct(){throw new Project_Exception_AutoLoad("%s");}}', $className, $className));
}
}
$pageController = 'Project_My_Class'; // "Project/My/Class.php"
try {
$obj = new $pageController();
} catch (Project_Exception_AutoLoad $e) {
header('HTTP/1.0 404 Not Found');
printf('<h1>Not Found</h1><p>The requested page %s was not found on this server.</p><hr /><em>$id$</em>', $_SERVER['REQUEST_URI']);
}
?>
emcmanus at gmail dot com (2007-07-16 18:04:45)
Note: if you're experiencing unexpected "failed opening required 'filename.php' (include..." errors:
If you placed your autoload function in an external file which you're requiring at the head of every script, be cautious of some odd behavior regarding PHP's idea of the current working directory.
I ran into some unexpected path issues when my include file was placed in a subdirectory directory. The solution to my problems was to make sure that the autoload script being included is in the same directory as the calling script.
peter dot gooman at gmail dot com (2007-06-17 20:33:02)
Before you start using __autload, remember that it holds no scope/namespace. This means that if you are depending on third party applications and they have an autoload function defined and so do you, your application will error.
To remedy this, everyone should look at the spl_autoload functions, eg: spl_autoload_register. This function allows more than one custom functions to be called through the default spl_autoload (default __autoload) handler.
Andrea Giammarchi (2007-03-21 03:54:33)
Another workaround for Exception problem (Klaus Schneider style)
<?php
define('CLASS_DIR', 'php/classes/');
function __autoload($name) {
if($exists = !class_exists($name) && file_exists($class = CLASS_DIR.$name.'.class.php'))
require $class;
elseif(!$exists) {
eval('class '.$name.' extends Exception {}');
throw new $name('[__autoload] this file doesn\'t exists: '.$class);
}
}
try {
new Undefined;
}
catch(Undefined $e) {
echo $e->getMessage();
}
// You should use generic Exception too
catch(Exception $e) {
echo $e->getMessage();
}
?>
Klaus Schneider (2007-03-01 07:35:22)
I just stumbled over one quite nice solution to the __autoload-exception problem. It allows for any kind of exception to be thrown inside __autoload().
It appears one has to define the requested class (using "eval", which is not nice but inevitable here) and after that can simply throw an exception (and catch it if so desired):
<?php
function __autoload($className)
{
// Do your stuff to load a class here, set $ok if everything went fine.
if (! $ok) {
eval("class $className{};");
throw new Exception('My message');
} // if
}
try {
UndefinedClass::undefinedFunction();
} catch (Exception $ex) {
echo $ex->getMessage();
} // try/catch
?>
Output: "My Message".
:-)
david dot thalmann at gmail dot com (2007-02-02 07:19:42)
Note to Ricos posting:
A lot of useless Coding. However, I improved the code, so now it will be able to find any folders ("." and ".." will not being tested... oO) and search as deep as possible. Now it will find CLASS_DIR/foo/bar.class.php also like CLASS_DIR/foo/bar/baz/buz/fii/and/so/on/class.php
Warning: This code will check ALL dirs who're "deeper" / "lower" than the class dir, so prevent deeply hidden files (or use just a few folders).
Improved Version:
<?php
// change this, if this code isn't "higher" than ALL classfiles
define("CLASS_DIR", dirname(__FILE__));
/**
* autoload classes (no need to include them one by one)
*
* @uses classFolder()
* @param $className string
*/
function __autoload($className) {
$folder = classFolder($className);
if($folder)
require_once($folder.$className.".class.php");
}
/**
* search for folders and subfolders with classes
*
* @param $className string
* @param $sub string[optional]
* @return string
*/
function classFolder($className, $sub = "/") {
$dir = dir(CLASS_DIR.$sub);
if(file_exists(CLASS_DIR.$sub.$className.".class.php"))
return CLASS_DIR.$sub;
while(false !== ($folder = $dir->read())) {
if($folder != "." && $folder != "..") {
if(is_dir(CLASS_DIR.$sub.$folder)) {
$subFolder = classFolder($className, $sub.$folder."/");
if($subFolder)
return $subFolder;
}
}
}
$dir->close();
return false;
}
?>
Rico (2007-01-04 05:00:20)
This autoload function searches for the class Location before requiring it. So there's no need of putting the classes all in one folder.
Requirements:
- the subfolders must be at least 3 letters long
- the filenames must be in the form CLASSNAME.class.php
Note:
- in this example the main class folder is 'lib'
define('ROOT_DIR', dirname(__FILE__).'/');
function __autoload($className) {
$folder=classFolder($className);
if($folder) require_once($folder.'/'.$className.'.class.php');
}
function classFolder($className,$folder='lib') {
$dir=dir(ROOT_DIR.$folder);
if($folder=='lib' && file_exists(ROOT_DIR.$folder.'/'.$className.'.class.php')) return $folder;
else {
while (false!==($entry=$dir->read())) {
$checkFolder=$folder.'/'.$entry;
if(strlen($entry)>2) {
if(is_dir(ROOT_DIR.$checkFolder)) {
if(file_exists(ROOT_DIR.$checkFolder.'/'.$className.'.class.php')) return $checkFolder;
else {
$subFolder=classFolder($className,$checkFolder);
if($subFolder) return $subFolder;
}
}
}
}
}
$dir->close();
return 0;
}
sandrejev at gmail dot com (2006-11-08 23:23:40)
Here is the most complete version of __autoload exception i guess.
The best thing is that it can throw any exception plus the exception is fully functional.
<?php
class AutoloadException extends Exception { }
class AutoloadExceptionRetranslator extends Exception
{
public function __construct($serializedException)
{
throw unserialize($serializedException);
}
}
function __autoload($classname)
{
if(!file_exists($classname))
{
$autoloadException = serialize(new AutoloadException("Class $classname could not be found"));
return eval("
class $classname
{
function __construct(\$a=0, \$b=0, \$c=0, \$d=0, \$e=0, \$f=0, \$g=0, \$h=0, \$i=0)
{
throw new AutoloadExceptionRetranslator('$autoloadException');
}
}
");
}
else
{
require_once $classname;
}
}
try
{
$anyObject = new AnyNonExistantClass();
}
catch (AutoloadException $e)
{
print_r($e->getTrace());
}
?>
andrew dot delete dot cornes at gmail dot delete dot com (2006-11-03 04:26:50)
If you'd like '__autoload()' to support multiple class folders, each containing multiple class files (one per class), you may want to try something like this (file '__autoload.php'):
<?php
define('CLASS_FILENAME_SUFFIX', '.class.php');
function __autoload($className)
{
$__autoloadAbsolutePath = dirname(__FILE__);
// 'pathStart' is your web application root folder.
// (This may or may not be where '__autoload.php'
// resides; let's assume here that it resides one
// level 'below' the web app root.)
$pathStart = $__autoloadAbsolutePath .
DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR;
// 'classPath' is a list of class folders to look in.
// (In this example, there's just one: 'classlibs/lib1'.
// To add more, simply append them; start with
// 'PATH_SEPARATOR . $pathStart .', and off you go...)
$classPath = PATH_SEPARATOR . $pathStart .
'classlibs' . DIRECTORY_SEPARATOR . 'lib1';
// Add list of class folders to 'include_path' for the
// forthcoming 'require()' (or similar directive).
$oldIncludePath = get_include_path();
set_include_path($oldIncludePath . $classPath);
require_once($className . CLASS_FILENAME_SUFFIX);
// Reinstate initial 'include_path'.
set_include_path($oldIncludePath);
}
?>
As your web application develops, new paths containing class files can be added into the '$classPath' variable within '__autoload()'. If hard-coding the '$classPath' variable isn't to your taste, you could arrange for its value to come from 'outside' in whatever way you like.
Any comments gratefully received.
Chris Corbyn (chris AT w3style.co.uk) (2006-09-08 06:23:45)
I'm sure this is needed by more than me.
My objective was to allow __autoload() to be easily extended in complex systems/frameworks where specific libraries etc may need loading differently but you don't want to hard-code little adjustments into your working __autoload() to allow this to happen.
Using a ServiceLocator object with some static methods and properties to allow loosely coupled locators to be attached to it you can swap/change and add to the functionality of your __autoload() at runtime.
The core stuff:
<?php
/**
* Defines the methods any actual locators must implement
* @package ServiceLocator
* @author Chris Corbyn
*/
interface Locator
{
/**
* Inform of whether or not the given class can be found
* @param string class
* @return bool
*/
public function canLocate($class);
/**
* Get the path to the class
* @param string class
* @return string
*/
public function getPath($class);
}
/**
* The main service locator.
* Uses loosely coupled locators in order to operate
* @package ServiceLocator
* @author Chris Corbyn
*/
class ServiceLocator
{
/**
* Contains any attached service locators
* @var array Locator
*/
protected static $locators = array();
/**
* Attach a new type of locator
* @param object Locator
* @param string key
*/
public static function attachLocator(Locator $locator, $key)
{
self::$locators[$key] = $locator;
}
/**
* Remove a locator that's been added
* @param string key
* @return bool
*/
public static function dropLocator($key)
{
if (self::isActiveLocator($key))
{
unset(self::$locators[$key]);
return true;
}
else return false;
}
/**
* Check if a locator is currently loaded
* @param string key
* @return bool
*/
public static function isActiveLocator($key)
{
return array_key_exists($key, self::$locators);
}
/**
* Load in the required service by asking all service locators
* @param string class
*/
public function load($class)
{
foreach (self::$locators as $key => $obj)
{
if ($obj->canLocate($class))
{
require_once $obj->getPath($class);
if (class_exists($class)) return;
}
}
}
}
/**
* PHPs default __autload
* Grabs an instance of ServiceLocator then runs it
* @package ServiceLocator
* @author Chris Corbyn
* @param string class
*/
function __autoload($class)
{
$locator = new ServiceLocator();
$locator->load($class);
}
?>
An example Use Case:
<?php
require 'ServiceLocator.php';
//Define some sort of service locator to attach...
class PearLocator implements Locator
{
protected $base = '.';
public function __construct($directory='.')
{
$this->base = (string) $directory;
}
public function canLocate($class)
{
$path = $this->getPath($class);
if (file_exists($path)) return true;
else return false;
}
public function getPath($class)
{
return $this->base . '/' . str_replace('_', '/', $class) . '.php';
}
}
// ... attach it ...
ServiceLocator::attachLocator(new PearLocator(), 'PEAR');
// ... and code away....
$foo = new Foo_Test();
?>
gonix (2006-08-03 06:39:52)
in response to alexey at renatasystems dot org:
You may add ``global $somedata;`` before ``$somedata = 'Some data';`` and it should work as expected.
file bar.class.php:
<?php
global $somedata;
$somedata = 'Some data'; /* global scope in common way */
class bar {
function __construct()
{
global $somedata; /* reference to global scope variable */
if ( isset($somedata) )
{
var_dump($somedata);
}
else
{
die('No data!');
}
}
}
?>
'common way':
<?php
require 'bar.class.php';
$foo = new bar();
?>
'__autoload way':
<?php
function __autoload($classname)
{
require $classname . '.class.php';
}
$foo = new bar();
?>
Both 'comon way' and '__autoload way' should give same result:
string(9) "Some data"
alexey at renatasystems dot org (2006-07-06 12:15:21)
While using an "autoloading" method you should pay attention to variables scope. Because of new file will be included INSIDE of magic function __autoload - all of declared in such file global scope variables will be only available within this function and nowhere else. This will cause strange behaviour in some cases. For example:
file bar.class.php:
<?php
$somedata = 'Some data'; /* global scope in common way */
class bar {
function __construct()
{
global $somedata; /* reference to global scope variable */
if ( isset($somedata) )
{
var_dump($somedata);
}
else
{
die('No data!');
}
}
}
?>
Attempt to load this file in common way:
<?php
require 'bar.class.php';
$foo = new bar();
?>
this will output (as expected):
string(9) "Some data"
But in case of __autoload:
<?php
function __autoload($classname)
{
require $classname . '.class.php';
}
$foo = new bar();
?>
you could expect that this script will return the same but no, it will return "No data!", because defenition of $somedata after requiring treats as local within user-defined function __autoload().
RQuadling at GMail dot com (2006-03-08 00:55:45)
An issue I've had with using the __autoload function is getting it into the application.
You have to have the function included in every topmost script. This is a pain if the entire application is OOP and an "app" can be just a component of another "app".
A solution I've found is to use php.ini's auto_prepend_file setting.
Mine is set to ...
auto_prepend_file = auto_loader.php
The auto_loader.php script contains a single function. The __autoload() function.
The include_dir path IS examined to find this file, so you can just put it with the rest of your includable files.
A useful additional facility here is that you could log which classes are used by a script at runtime. Very useful if you have object factories and can't know the load at design time.
Also, assigning the uncaught exception handler and the error handlers in this file means your entire site WILL have some global protection without you having to deal with it on a script by script basis.
If you do not have access to the PHP.INI file, or you are running on a shared server, you may not be able to set this property. In those cases, you may be able to set the value using .htaccess. (NOTE: UNTESTED as I don't use Apache).
<IfModule mod_php5.c>
php_value auto_prepend_file "auto_loader.php"
</IfModule>
You COULD therefore have a different set of rules per subdomain (if you have multiple subdomains, say, live, test, beta, devel) or whatever takes your fancy.
For more details on this see the "Description of core php.ini directives" (http://www.php.net/manual/en/ini.core.php)
dave60 /at/ gmail /dot/ com (2005-12-29 13:25:53)
In reply to quetzalcoatl:
Generally, I would advise for each class to have it's own file, and hold nothing besides that class. Just define __autoload() in a/the infrastructure file -- a/the file that does the behavioral logic, and there should be no need to redefine it in a class' file.
me at mydomain dot com (2005-11-11 16:07:54)
You can enable this behaviour for undefined classes while unserializing objects by setting the .ini-variable 'unserialize_callback_func' to '__autoload'.
php at kaiundina dot de (2005-09-20 11:42:11)
The autoload-feature allows to add the behavior of static constructors (like in C#). Static constructors should be called on the first occurence of a class reference - typically a 'new' operator or a static call to a class's operation.
They can be used used to initialize complex static properties.
And here is an easy and save way how it can be done:
Content of MyClass.class.php5:
<?php
// demo class persisting of a static and a dynamic constructor
class MyClass
{
// static constructor operation
public static function _construct()
{
// just say hello
echo '<div>static constructor</div>';
}
// default dynamic constructor operation
public function __construct()
{
// just say hello
echo '<div>dynamic constructor</div>';
}
}
?>
Content of index.php5:
<?php
// declare handler for any unknown class request
function __autoload($aClassName)
{
// load the class
require_once ($aClassName . '.class.php5');
// create a reference to the static constructor's operation
$staticConstructorReference = array($aClassName, '_construct');
// if the static constructor was declared properly...
if (is_callable($staticConstructorReference))
{
// call the static constructor
call_user_func($staticConstructorReference);
}
}
// create an example object to see both constructors being executed
$article = new MyObject();
?>
scott at webscott dot com (2005-05-04 05:40:51)
__autoload() seems to work when saving objects as session variables as well:
classLoader.php
<?php
function __autoload($className) {
require_once("$className.php");
}
?>
testClass.php
<?php
class testClass {
function __construct($propValue) {
$this->prop1 = $propValue;
}
function showProp() {
return $this->prop1;
}
}
?>
page1.php
<?php
require_once('classLoader.php');
session_start();
$_SESSION['testObj'] = new testClass('foo');
echo '<a href="page2.php">Go to page 2</a>';
?>
page2.php
<?php
require_once('classLoader.php');
session_start();
echo $_SESSION['testObj']->showProp(); // displays foo
?>
Works with multiple session objects as well. Tested on a Win2k/IIS machine.
trini0 (2005-02-01 20:04:17)
Be careful with using that eval() trick within __autoload().
If you use reflection in your code, the so called trick,
*can* provide ill side effects.
For example ->
$reflection = new reflectionClass('some_class');
if (FALSE === $reflection->isSubClassOf('another_class'))
{
throw new Exception('Class "some_class" must extend base class "another_class"');
}
If the real class "another_class" doesnt exist at the time, or "some_class" doesn't extend "another_class", with the reflection test, the so called eval() trick, creates a dummy "another_class",
thereby making the reflection test useless...
petyo()architect . bg (2005-01-30 01:27:27)
The following function may be useful if you want to simulate namespaces and autoloading behavior:
define ("CLASS_ROOT", '/classes/');
function __autoload ($className)
{
require_once CLASS_ROOT.str_replace('_', '/', $className).'.class.php';
}
Then you will just have to use the folder structure and name the classes accordingly. If you want to have a class named Page, which will be in the pseudo namespace System.Web.UI, create a directory named System in /classes, then create Web, then UI, then name the class System_Web_UI_Page. Kind of long to type if you don't have autocomplete, but at least you will not have to manage the loading of all the classes' definitions.
thomas dot revell at uwe dot ac dot uk (2005-01-27 06:31:22)
If you want to throw an exception if a class isn't defined yet, use class_exists ():
<?php
// See if the class is defined
if (!class_exists ($className, false)) {
throw new Exception ("Class $className is not defined.");
}
?>
The second parameter indicates whether or not the __autoload () function should be called before checking for the class's existence.
nhartkamp at eljakim dot N0SP4M dot nl (2004-12-11 09:14:51)
The following might provide a good work-around for throwing exceptions from the __autoload function when a file containing the correct class doesn't exists.
function __autoload ($class_name) {
$file = 'system/objects/' . $class_name . '.inc.php';
if (!file_exists ($file)) {
return eval ("class $class_name {" .
" function $class_name () {" .
" throw new Exception ();" .
" }" .
"}");
}
require_once ($file);
}
Cheers,
Nolan