How to use __autoload for different folders? - php

I am trying to autoload classes that are in different folders. Is that possible?
function __autoload($class){
require $class.'.php';
require 'libs/'.$class.'.php';
//this won't work.
}
I want to autoload classes that are either in libs folder or on the root. Any thoughts? Thanks a lot.

First, I suggest you look into spl_autoload_register, which is superior for doing this.
Second, you should use is_file to test whether the file exists, then try to load it. If you require a file that doesn't exist, your script will halt.
spl_autoload_register(function($class) {
if (is_file($class . '.php')) {
require $class . '.php';
} elseif (is_file('libs/' . $class . '.php')) {
require 'libs/' . $class . '.php';
}
});
If you have multiple folders where the file could be, you could do something like this:
spl_autoload_register(function($class) {
$folders = array ('.', 'libs', 'somewhere');
foreach ($folders as $folder) {
if (is_file($folder . '/' . $class . '.php')) {
require $folder . '/' . $class . '.php';
}
if (class_exists($class)) break;
}
});

Related

PHP Load every class in a directory

I have coded a PHP script that includes every file in a directory. But im wondering if there is a way to load the classes in the files im including like a autoloader or something?
<?php
define("include_dir", dirname(__FILE__) . '/includes/');
foreach (scandir(include_dir) as $filename)
{
if (is_file(include_dir . '/' . $filename))
{
//its a php file, lets do this!
if (substr($filename, -4) == '.php')
{
include include_dir . $filename;
}
}
}
?>
try this-
function __autoload($class_name) {
include $class_name . '.php';
}
$obj = new MyClass1();
$obj2 = new MyClass2();
the manual -
http://php.net/manual/en/language.oop5.autoload.php

spl_autoload_register() in different directories

I have a file that auto loads each of my classes.
This is what it contains:
spl_autoload_register(function($class){
require_once 'classes/' . $class . '.php';
});
require_once 'functions/sanitize.php';
require_once 'functions/hash.php';
But when I require_once this file from another php file that is inside my ajax folder, it will try looking for the classes, the function will look from my classes with the path: main_folder/ajax/classes instead of just main_folder/classes.
Does anyone know how to fix this?
FIX:
spl_autoload_register(function($class){
if (file_exists('classes/' . $class . '.php')) {
require_once 'classes/' . $class . '.php';
}
elseif (file_exists('../classes/' . $class . '.php')) {
require_once '../classes/' . $class . '.php';
}
elseif (file_exists('../../classes/' . $class . '.php')) {
require_once '../../classes/' . $class . '.php';
}
You should simple use this function just once - in the main file (usually index.php) and not in another files.
However if it's not possible (but I don't see any reason when could it be not possible) you can change it for example that way:
spl_autoload_register(function($class){
if (file_exists('classes/' . $class . '.php')) {
require_once 'classes/' . $class . '.php';
}
elseif (file_exists( $class . '.php')) {
require_once $class . '.php';
}
});
Here is a proper way, It should universally work.
EDIT: Make sure to specify levels on dirname in order to find the correct path.
spl_autoload_register(function ($classname) {
$file_realpath = dirname(realpath(__FILE__), levels: 1 /* Change that? */) . DIRECTORY_SEPARATOR . dirname($classname);
$classpath = sprintf('%s' . DIRECTORY_SEPARATOR . '%s.php', $file_realpath, basename($classname, '.{php,PHP}'));
if (file_exists($classpath)) {
echo "Loading <b>$classpath</b>...<br>";
require($classpath);
}
});
You need to know the absolute path to the classes directory, then just use a full qualified path to get there.
Make sure your document root is correctly configured with your http server. Furthermore this is usually solved by routing all requests to index.php and deriving the base path (document root) from there. Here is your code modified:
spl_autoload_register(function($class) {
$resolved = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . $class . '.php';
if(file_exists($resolved)) {
require_once $resolved;
} else {
// make it known to your that a class failed to load somehow
return;
}
})
Here is another implementation that I use for simple projects

Restrict my spl_autoloader to only load classes in my namespace?

I've just begun using autoloader lazy loading in my app, and I'm running afoul of namespacing. The autoloader is trying to load things like new DateTime() and failing. Is there a trick to making my autoloader spcific to only my own namespaced classes?
Here is the code I have currently. I suspect it is a mess, but I'm not seeing just how to correct it:
<?php namespace RSCRM;
class Autoloader {
static public function loader($className) {
$filename = dirname(__FILE__) .'/'. str_replace("\\", '/', $className) . ".php";
if (file_exists($filename)) {
include_once($filename);
if (class_exists($className)) {
return TRUE;
}
}
return FALSE;
}
}
spl_autoload_register('\RSCRM\Autoloader::loader');
Happy to RTM if someone can point to a solid example.
What I use is actually adapted from the autoloader used to Unit Test a few of the AuraPHP libraries:
<?php
spl_autoload_register(function ($class) {
// a partial filename
$part = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
// directories where we can find classes
$dirs = array(
__DIR__ . DIRECTORY_SEPARATOR . 'src',
__DIR__ . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'src',
__DIR__ . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR . 'src',
);
// go through the directories to find classes
foreach ($dirs as $dir) {
$file = $dir . DIRECTORY_SEPARATOR . $part;
if (is_readable($file)) {
require $file;
return;
}
}
});
Just make sure the array of '$dirs' values point to the root of your namespaced code.
You can also take a look at the PSR-0 example implementation (http://www.php-fig.org/psr/psr-0/).
You might also want to look an into existing autoloader, like Aura.Autoload or the Symfony ClassLoader Component, although those might be overkill depending on what your requirements are.
I hope this helps.

PHP: autoloading multiple classes with namespaces

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.

PHP Autoload ignorecase

Is there any way to call require_once with some "case insensitive flag" ?
In windows it's okay, but linux is case sensitive. Is there any way to override ?
Thanks
Sure, load
strtolower($className . ".php")
and name your files in lowercase.
Regardless of how you try to load your files, only the lowercase version will ever be loaded.
Just include some where else in you header.php or some other common file.,
<?php
$filename = basename($_SERVER['SCRIPT_FILENAME']);
$request = basename($_SERVER['SCRIPT_NAME']);
if($filename != $request)
die('Case of filename and request do not match!');
?>
I think this may help you resolve your problem. also refer the following location https://superuser.com/questions/431342/linux-both-case-sensitive-and-case-insensitive-and-always-inconvenient
You can use this every time you are loading you auto load the appropriate classes. yu have to change the directories depends on you project. you can you the echo or print_r to print what classes are loaded every time when you are calling something. also all you class names must ot have the same format for example, className.class.php e.g Dashboard.class.php, Category.class.php. you can use
ucwords to make the first letter capital.
function __autoload($className)
{
if (file_exists(__DIR__ . '/../library/' . strtolower($className) . '.class.php'))
{
require_once(__DIR__ . '/../library/' . strtolower($className) . '.class.php');
}
else if (file_exists(__DIR__ . '/../application/controllers/' . strtolower($className) . '.php'))
{
require_once(__DIR__ . '/../application/controllers/' . strtolower($className) . '.php');
}
else if (file_exists(__DIR__ . '/../application/models/' . strtolower($className) . '.php'))
{
require_once(__DIR__ . '/../application/models/' . strtolower($className) . '.php');
}
}

Categories