php cannot redeclare _autoload() - php

I getting more indepth with php and I am creating my own mini mvc framework to learn OOP.
I have a .htaccess file that redirects everything to the index.php. In the index.php I include a file called boootstrap.php to parse the url and load the class php file.
Now that I am adding ActiveRecord http://www.phpactiverecord.org to add database access. I get the error:
Fatal error: Cannot redeclare class AutoLoader in /home/i554246/public_html/mvc/lib/Autoloader.php on line 4
I am not sure how to stop the conflict.
index.php:
include(MVC_CORE_INCLUDE_PATH . DS . 'Bootstrap.php')
include(MVC_CORE_INCLUDE_PATH . DS . 'activerecord/ActiveRecord.php');
autoloader.php which is included in bootstrap.php
<?php
class AutoLoader
{
public static function Load($Class)
{
$File = BASEDIR.$Class.'.php';
if(is_readable($File))
{
require($File);
}
else
{
die('Requested module "'.$Class.'" is missing. Execution stopped.');
}
}
}
spl_autoload_register('AutoLoader::Load');
ActiveRecord.php
if (!defined('PHP_ACTIVERECORD_AUTOLOAD_DISABLE'))
spl_autoload_register('activerecord_autoload',false,PHP_ACTIVERECORD_AUTOLOAD_PREPEND);
function activerecord_autoload($class_name)
{
$path = ActiveRecord\Config::instance()->get_model_directory();
$root = realpath(isset($path) ? $path : '.');
if (($namespaces = ActiveRecord\get_namespaces($class_name)))
{
$class_name = array_pop($namespaces);
$directories = array();
foreach ($namespaces as $directory)
$directories[] = $directory;
$root .= DIRECTORY_SEPARATOR . implode($directories, DIRECTORY_SEPARATOR);
}
$file = "$root/$class_name.php";
if (file_exists($file))
require $file;
}
?>

Perhaps change
if (file_exists($file))
require $file;
to
if (file_exists($file))
require_once($file);

You either try to include a file twice (you might want to use require_once) or you have 2 classes (one from a library you use?) with that same name.
if There seem to be 2 classes called AutoLoader, you might want to look into namespaces. I can't recall phpactiverecord having a class called that, but as you might use several libraries you were bound to run into this.
The best way would be to put your own autoloader class in a namespace. Make sure you keep all your calls correct, so calls to the autoloader should have \yournamespace\ in front of it, and calls inside the autoloader might need prepending a \ to (like \Exception for instance)

PHP-ActiveRecord doesn't have any AutoLoader class. What happens here I guess, is that you have two loaders that are loading the file.
Since it's PSR-0 compliant, you can load it using your own loading utility (assuming it's embracing that convention). If you do so just disable PHP-AR autoloading utility.
define('PHP_ACTIVERECORD_AUTOLOAD_DISABLE', true);
The vanilla loader is moslty useful for finding model classes which won't be needed if you're putting them in their own namespace. As your framework might not follow PHP-AR convention regarding where the models are, it seems correct to disable that autoloader.
Check that example of PHP-AR integration with lithium framework: li3_activerecord

I moved the line
$Bootstrap = new Bootstrap();
under the
include(MVC_CORE_INCLUDE_PATH . DS . 'Bootstrap.php');
include(MVC_CORE_INCLUDE_PATH . DS . 'Controller.php');
but above
require_once 'lib/activerecord/ActiveRecord.php'; the ActiveRecord seemed to want to load it again

Related

Ignoring autoloader in certain files

I have this function below that autoloads classes before registering them as new classes.
function __autoload($controller){
$ce = explode('\\', $controller);
require ROOT . '/app/base/classes/' .
end($ce) . '/class.' .
end($ce) . '.php';
}
How can I ignore this autoloader for one class? The reason behind this is because I installed a package and the class file is another directory to what my class files are in...
Try to require your package file containing the class you need, before calling new OneClass().
For example, if you have [ROOT]/app/base/classes/OneClass/class.OneClass.php alongside with [ROOT]/app/custom/packages/OneClass.php, you may:
require ROOT . '/app/custom/packages/OneClass.php';
$class = new OneClass(); // will be an instance of /app/custom/packages/OneClass.php
But the best solution is using namespaces, as described in PSR-0 and PSR-4 recommendations.
And when you use namespaces, and you have correct directory/file structure/names, and you have correctly described namespaces inside your classes:
$my_class = new \app\base\OneClass();
// The same class name without any conflicts
$class_from_package = new \app\custom\packages\OneClass();
I think it's a more useful approach. You will have some logical directories/files structure which will reflect namespace structure. And if you want load the same class name from other folder, you just will be able to do it by using fully qualified class name with correspond namespace.
It can be a bit weird, to use namespaces, if you did not use them before. You need to support correlations between file path and namespace definition all the time (when moving class file to another folder, for example), but after some time of using it, you will pay no more attention to namespaces than you are paying to tying your shoes before going outdoor.
You can do something like this:
function __autoload($controller){
$ce = explode('\\', $controller);
$ignore=array('class_1', 'class_2');
if (!in_array(end($ce), $ignore)) {
require ROOT . '/app/base/classes/' .
end($ce) . '/class.' .
end($ce) . '.php';
}
}

How do I do to autoload composer classes only after the former autoload function failed to find a loadable class?

I'm taking over a project that uses a custom framework. Built-in, you find the most common PHP autoload function in the world:
function __autoload($class_name) {
include APP_PATH . '/model/' . $class_name . '.php';
}
Now, I'd like to install composer. How do I do to autoload composer classes only after the former autoload function failed to find a loadable class?
Turns out that there is a way to register autoloader functions as a stack of functions that will be called until one succeeds. The last parameter prepends the autoloader function to the current stack.
spl_autoload_register('app_autoload', false, true);
But in order to be stacked it needed of course not to use the magic syntax (__autoload()) and also to be recognized as "not successful" the function must not include a file. So I changed the original function a little bit:
function app_autoload($class_name) {
$file = APP_PATH . '/model/' . $class_name . '.php';
if (file_exists($file)) {
include $file;
}
return false;
}
And it worked as charm.

Autoloading Namespaced Classes

I have searched high and low for an answer and tried every example, but this still fails to find my classes. Why do I keep getting Fatal error: Class 'ProjectMorpheus\model\Database' not found in C:\Portables\xampp\htdocs\ProjectMorpheus\config\config.php on line 23
/ProjectMorpheus
/model
Database.class.php
/config
config.php
So my Database class has a namespace like this:
namespace ProjectMorpheus\model;
class Database { ... }
Finally, my config.php has a function autoloader function (Note: __SITE_PATH = 'C:\Portables\xampp\htdocs\ProjectMorpheus\'):
/*** auto load model classes ***/
function __autoload($class){
$parts = explode('\\', $class);
include __SITE_PATH . 'model/' . end($parts) . '.class.php';
}
$dbh = \ProjectMorpheus\model\Database::getInstance($dsn, $username, $password);
Using spl_autoloader appears to work, but why? My only guess is that $class is not the same in both instances. My spl_function looks like:
spl_autoload_register(function($class){
$parts = explode('\\', $class);
include __SITE_PATH . 'model/' . end($parts) . '.class.php';
});
Why don't you use PSR-0 or PSR-4 autoloader standards?
They even have ready-to-use autoloader class on GitHub As long as you will follow the rules, you won't have any issues and your code will be PSR.
Although I wouldn't recommend but if you would like to insist using the code above for autoloading classes (only in models folder) then try to dump what is the value of __SITE_PATH . 'model/' . end($parts) . '.class.php'; and check if you can access it. You can even try to copy and paste the path to your file explorer to see if the location is reachable and file exists in that directory.
P.S. I tried to add this as a comment but I couldn't due to low rep. points (being new around here and all).

How to use spl_autoload_register for multiple diectories in PHP?

I'm actually trying to create a MVC framework for my own, however I'm having troubles with the Autoload. It's not a problem actually, but I'd like to ask the gurus, how are they using the spl_autoload_register function when there are different directories.
Lets say we have the following directories:
Controllers
Libs
Models
Each folder contains different classes, like:
Controllers:
Main.php
File.php
About.php
Libs:
Main.php
Front_controller.php
Models:
Index.php
File.php
Login.php
You can notice that some file names might be found with the same name in different directories. Okay, so this is what I've tried so far:
spl_autoload_register(function ($class) {
$pathContorllers = 'Controllers/' . $class . '.php';
$pathLibs = 'Libs/' . $class . '.php';
$pathModels = 'Models/' . $class . '.php';
if (file_exists($pathContorllers)) {
require_once $pathContorllers;
} elseif (file_exists($pathLibs)) {
require_once $pathLibs;
} elseif (file_exists($pathModels )) {
require_once $pathModels ;
}
});
It is working well, however I'm sure that there is another way to make everything simpler. Can anyone suggest me how can I make this code better or simpler / what are gurus using in this situation?
For the purpose of keeping individuals who may come across this answer from obtaining out-of-date information I have updated it in regards to the latest PSR autoloading standards. The original answer has been maintained for historical purposes and for those who are only interested in the PSR-0 autoloader.
Updated Answer
The PHP-FIG has officially deprecated the PSR-0 standard in favor of the alternative autoloader, PSR-4. Although the two are similar in some aspects they are also very different in others. (E.g.: the handling of underscores in class names.)
You may be thinking to yourself -- "I use PSR-0 now and it works fine." The truth of the matter is that PSR-0 will still work fine for certain projects. This is especially true when backwards compatibility with a package that doesn't use namespaces is concerned. PSR-0 is still a decent autoloading principle, but it has its own shortcomings.
Of course, if there is one thing that is a constant with programming, it is that code eventually changes and programming techniques continue to evolve. You can do yourself a favor today by preparing yourself for tomorrow. Therefore, if you are just starting a project or are trying to port a project to a newer version of PHP that can use namespaces, you should seriously consider using the PSR-4 autoloader.
It is also worth noting that if you are developing a project that does not use namespaces then PSR-4 does not apply to you. In this case PSR-0 or your own custom autoloader applies.
Original Answer
If you want to go with namespaces in your classes, then the PSR-0 route is a pretty good way to autoload. Basically your namespace represents you directory structure and classes can be loaded based on a convention.
If the PSR-0 method doesn't meet all your needs (or doesn't play nice with existing code) you can still add more functions with spl_autoload_register and PHP will go through them one by one in an attempt to load classes.
Example usage:
First thing is first, if you aren't familiar with namespaces in PHP then you will benefit from checking out the PHP manual on the subject. They can be a bit confusing at first, but their benefits are worth the initial confusion.
So I said that PSR-0 works by associating your namespaces with your directory structure. Let's use your directories for an example. You have in your root folder (wherever it may be) the following:
Project directory: <- Let's call this directory "MyProject"
Controllers:
Main.php
File.php
About.php
Libs:
Main.php
Front_controller.php
Models:
Index.php
File.php
Login.php
index.php <- Let's say this is your entry point file, this is where you will be autoloading stuff from.
Now let's take a look at your controller Main.php. Two things to keep in mind is that the class name needs to be the name of the file and the namespace for that class is the directory path to that file. So Main.php should look something like this:
<?php
namespace MyProject\Controllers;
class Main {
//Field vars, contructor, methods, etc. all go here.
}
?>
You would do the same thing for your your Login model
<?php
namespace MyProject\Models;
class Login {
//Field vars, contructor, methods, etc. all go here.
}
?>
Now in your index.php file (out in the root directory - MyProject) you would make your call to the spl_autoload_register and give it the PSR-0 autoloader.
spl_autoload_register( function ($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;
});
//Now you can make your call to your objects without a bunch of include/require statements
$main = new \MyProject\Controllers\Main(); //Instantiates your 'Main' controller
$login = new \MyProject\Models\Login(); //Instantiates your 'Login' model
Hopefully that helps make better sense of it, and again, if you don't want to use namespaces you can always just keep adding closures into the SPL autoload stack. You can have 10 different autoloaders in there if you want and PHP will go through them one by one (in the order you defined them) using each function to try and load a class. However, a couple convention based autoloaders is a bit cleaner and more of a preferred method. Also keep in mind that the autoloader translates both namespace separators \ and underscores _ as a directory separator. So your Front_controller.php would not autoload as you would expect.
The code below will help. But I'll advice you check on Namespaces.
spl_autoload_register ( function ($class) {
$sources = array("Controllers/$class.php", "Lib/$class.php ", "Models/$class.php " );
foreach ($sources as $source) {
if (file_exists($source)) {
require_once $source;
}
}
});

multiple spl_autoload_register issue

I'm working on the development of a custom framework.
And I have encountered an issue when I tried to dynamise the calling of my classes.
This is a visual of my files :
So I decided to create a different function for each folder (libs, controllers et modeles):
function autoloadLibs($class) {
//require the general classes
require 'libs/' . $class . '.php';
}
function autoloadModels($class) {
//require the models classes
require 'models/' . $class . '.php';
}
function autoloadControllers($class) {
//require the controllers classes
require 'controllers/' . $class . '.php';
}
spl_autoload_register ('autoloadLibs');
spl_autoload_register ('autoloadControllers');
spl_autoload_register ('autoloadModels');
Nevertheless I have this message : Warning: require(libs/admin.php): failed to open stream, of cours it's not the good folder. But I don't know how to fix that. Is there a good way to optimise my classes calls ?
After few tests, I found this solution for my case :
set_include_path(implode(PATH_SEPARATOR, array(get_include_path(), './libs', './controllers', './models')));
spl_autoload_register();
You need to check the file exists first with is_file() before you attempt to require it.
When using spl_autoload_register(), I've found it's generally better to register one method to include the files. The fact that you can bing multiple functions I believe is to make interoperability with different libraries easy (so they don't clobber __autoload()). It will also save you having to write the code out multiple times to check for the file's existent, map _ to directory separator (if you do that), etc.
So, assuming you change your filenames to suit the convention of Underscore_Separated_Name, e.g. Controller_Admin_Dashboard, you could use...
function autoload($className) {
$path = SYSPATH .
str_replace("_", DIRECTORY_SEPARATOR, strtolower($className)) .
".php";
if (is_file($path)) {
require $path;
}
}
The first time you instantiate Controller_Admin_Dashboard, PHP may include a file such as /app/controller/admin/dashboard.php.
If you have multiple spl_autoload_register calls you need to make sure you don't use the require keyword to include the files, because this means "include the file or die if it can't".
Personally I disagree with others about only having one autoload function, especially if you are including classes from different locations, such as a controllers versus some library directory. I also check if the file exists first, then include it.
tl;dr version: Don't allow spl_autoload_register calls to block each other.
your answer is here.
when you are registering multiple autoloaders, php try to load a class by anyone of those autoloaders, Then, php calls those autoloaders from the first registered to the last.
Then, in anyone of those autoloaders you should check that file_exists or not, else, php try to include it and throws an error if that file does not exist. Then, before inclusion, check existance of file.
Change Your Autoloaders To:
function autoloadLibs($class)
{
#require the general classes
$file = 'libs/' . $class . '.php';
if(file_exists($file))
require $file;
}
function autoloadModels($class)
{
#require the models classes
$file = 'models/' . $class . '.php';
if(file_exists($file))
require $file;
}
function autoloadControllers($class)
{
#require the controllers classes
$file = 'controllers/' . $class . '.php';
if(file_exists($file))
require $file;
}
You should check class names before requiring the file, for example:
function autoloadControllers($class) {
//require the controllers classes
if( substr( $class, -10) == 'Controller')){
require 'controllers/' . $class . '.php';
}
}
I find it correct to cause error if class cannot be loaded, but you should make sure that require is called only on correct path.
Note that spl_autoload_register provides a third parameter (prepend). You can set this to true if you wish to place a specific autoload function on top of the autoload stack. This means that this specific function will then be called first.
Example:
spl_autoload_register(array('My_Class', 'My_Method'), true, true);
http://www.php.net/manual/en/function.spl-autoload-register.php

Categories