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';
}
}
Related
Is it possible to use an external PHP library with custom namespace, without changing its source?
I want to use all external libraries with namespace prefix "ext".
For example, I want to be able to instantiate Predis 's Client class as new ext\Predis\Client() instead of new Predis\Client() (from root).
ps.: I'm autoloading all internal classes like this:
spl_autoload_register(function($class)
{
$path = str_replace("\\", "/", $class);
$file = __DIR__ . "/" . $path . ".php";
require_once $file;
});
It's not possible. Namespaces are resolved at compile time. It also contradicts the PSR standards:
The fully qualified class name MUST have a top-level namespace name,
also known as a “vendor namespace”.
I was studying a couple PHP frameworks and then decided to build my own, of course. But i'm facing one issue. I have a Router class that handles dynamically the HTTP requests and it basically explodes the URL into elements dividing it by the slash and storing it into an array, then a function is called to check if the first element is a valid Controller. If it is valid, the function should require it, but that's where i'm stuck, because it seems that i can't require a file like:
if (file_exists(CONTROLLERS_DIR . $this->url[0] . '.php')) { require \App\Controllers\$this->url[0] }
How can I require a file like that using namespaces?
Thanks.
"How can I require a file like that using namespaces?"
You can't. Namespaces have nothing to do with it.
"PHP Namespaces provide a way in which to group related classes, interfaces, functions and constants." ~ Namespaces overview
require is about file dependancies, regardless the namespace:
if (file_exists(CONTROLLERS_DIR . $this->url[0] . '.php')) {
require(CONTROLLERS_DIR . $this->url[0] . '.php');
}
EDIT: You might want to instantiate a class using a namespace and class name retrieved in run-time though, i.e. something like:
namespace \App\Controllers;
class C {
protected $_i;
public function __construct($i){ $this->_i = $i; }
public function foo(){ echo $this->_i; }
}
and somewhere:
$className = "C"; // or $className = $this->whatever...
$class = "\\App\\Controllers\\".$className;
$instance = new $class(7);
$instance->foo(); // outputs 7
I've built couple of frameworks and I understand what you trying to do...
Basically when you have some path for example "HelloWorld\addComment"
You want to create controller instance
\App\Controllers\HelloWorldController
There are multiple ways to solve it, the one I like is:
Using spl autoloader
http://php.net/manual/en/function.spl-autoload.php
In the link I provided you got the examples you need.
Then you can end up just doing
$controller = new \App\Controllers\HelloWorldController();
You should put the HelloWorldController at the right namespace + maintain directory structure matching the namespace
app
Controllers
HelloWorldController
spl autoloader will do the right including for you, often the default implementation is sufficient - but it is easy to create your own spl autoloader and register it
Later you can test if the $controller has the method you need via method_exist or reflection...
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).
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;
}
}
});
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