autoloader with upper and lowercase classname - php

I am using this class in php for autoloading.
http://pastebin.com/m75f95c3b
But when I have somewhere
class Bar extends Foo
And I have a file called foo.class.php it won't find the class.
But when i chagne the filename to Foo.class.php it will find the class.
I am trying to add some functionallity to my class to always find the file if its there no matter wether the filename starts with a capital or not. But so far I haven't scuceeded.
Anyone?

If all of your file have lowercase names, apply strtolower to the $className variable value in each method of your class:
$className = strtolower($className);
But I suggest that you draft some coding guidelines that every developer has to stick to. Otherwise you will have to test each of the 2<length of file name> possibilities of writing a file name using uppercase and lowercase letters.

Gumbo's solution is the best one and that's what almost everyone uses. I do not understand why you do not like. Of course you can first check whether a file with the class name capitalized exists or not, and if not then check whether lowercased version exists or not. That's definitely not better than Gumbo's solution. After all "your" programmers have to follow some conventions/rules.

I'd do it more or less like this
function __autoload($class_name) {
$upperName = strtoupper(substr($class_name,0,1)) . substr($class_name, 1);
$lowerName = strtolower(substr($class_name,0,1)) . substr($class_name, 1);
//Check to see if the class file exists as-is
if(file_exists($class_name . '.php')) {
require_once($class_name . '.php');
}elseif(file_exists($upperName . '.php')) {
require_once($upperName . '.php');
}elseif(file_exists($lowerName . '.php')) {
require_once($lowerName . '.php');
}else {
throw new Exception("The class $class_name could not be found.");
}
}
It's easy to enough to add other conditionals as different naming conventions arise.

I use an __autoload() function that takes into account several different file naming conventions to provide backwards compatibility with previous developers.
It does a simple loop over each convention and checks to see if that file exists. If the file exists, then it loads it.
For my function, I do this for different file extensions, such as .inc, .class or .inc.php . You could do the same, but search for upper and lower first characters.
I would put this in the searchForClassFile() method, in the else part with the comment 'Found a file'.
EDIT (more information):
Rather than recursively descend into a class directory looking for the correct file, I map the class name to a specific location. This is a common practice. For example, foo_bar is mapped to [CLASS_DIRECTORY]/foo/bar.[EXTENSION]. In our case, we check several different extensions.
In your case, you have to make a design decision about how you want to search for the class file, but modifying your code:
} else {
// Found a file
if ($f == $className . self::$classFileSuffix ||
$f == strtolower($className) . self::classFileSuffix) {
return $subPath;
}
}
Instead of strtolower() you could write a function that only lowers the first character, or if using PHP > 5.3.0 (not officially released) use the lcfirst() function.

You can use an autoloader that scans the directories for .php files, and actually looks into the contents of each file for patterns such as "class Foo" using a regular expression. You will need to cache this, of course, preferably by using an array of the form class => directory. You also need to make sure you invalidate the cache correctly, e.g. when there are new .php files in the folder.
I do this for my website, and it works great.

Related

Php files should have upper case?

I mean, if I have the class Map located in the map.php file, should I have this file as Map.php? 'Cause I made an autoloader:
spl_autoload_register(function ($class_name)
{
$CorePath = dirname(__DIR__) . '/fod/' . $class_name . '.php';
if(file_exists($CorePath))
{
include $CorePath;
}
});
now the problem is if I call: $x = new Map(); the autoloader doesn't locate it, but if I rename as Map.php the autoloader locate it. Any idea?
The classname must be the same as the filename, as you've said. If you want to stick with the lower case filenames, then make the $class_name lowercase.
The function is strtolower()
$CorePath = dirname(__DIR__) . '/fod/' . strtolower($class_name) . '.php';
But the file must have the same name as the class, so that the autoloader can work.
PHP is case-sensitive. So, yes, you should either use the lowercase version of $class_name to load files and use lowercase for the file names or have the class names uppercase. I would prefer the latter but this is a matter of taste.
Apart from everything else PHP class names (and namespaces) are not case sensitive. For PHP (8.1)
class Map {
...
}
and
class map {
...
}
are the same thing. Equally both new \Map() and new \map() will construct the same thing. If the class Map is in a class file names Map.class.php this will be no problem in a cases insensitive OS, (like for instance Windows, where AAMOF the NTFS is case sensitve!); map.class.php and Map.class.php will be equally the same thing. On a case sensitive OS/fs like Linux things will be dramatically different and care must be taken when autoloading class files as map.class.php and Map.class.php are two different things.
Using spl_autoload_extensions()
If the now preferred autoloading with
spl_autoload_extensions('.class.php');
spl_autload_register();
is used there is a important thing to realize. As spl_autoload_extensions is made aware of the fact that class names are not case-sensitive it will do a mb_strtolower to every class name it encounters. Suddenly a class named class Map can no longer be found in a file called Map.class.php. It has to be named map.class.php because of this mb_strtolower().
Options
There are two options.
ignore the PSR standard and use lower case for class name (reflecting the actual situation) and class filename.
apply the PSR standard naming but use all lowercase in the class filename.
Option 1) is good in a way that class name and class filename are in sync which IMO is preferable whereas Option 2) might confuse other developers reading your code. But neither is ideal. Btw. (PSR)[https://www.php-fig.org/psr/psr-4/] seems to ignore this fact of default PHP life and suggest their own class loader.

Autoloaders in PHP - two running at a time

I understand how to register autoloaders and even how to create one, that's no issue at all. How ever the main issue is - how do you have two auto loaders running side by side for something like:
class project_one_folder_class extends project_two_folder_class{}
You'll notice that the child class belongs to a project which is reaching out and calling the parent class which is locate in a different project.
The way the projects are linked project two's classes are always seen by the auto loader, how ever project one's classes are never seen.
So the way I thought around this was to write two auto loaders and register them because php will look in on then the other. How ever php seems to be looking in only one and not the other.
how would you solve this?
Edit
Project two is parent, Project one is child. This is more expanded question then What was posted on this question.
To better expand this is my class.
class AisisCore_Loader_AutoLoader{
protected static $_instance;
public function get_instance(){
if(null == self::$_instance){
self::$_instance = new self();
}
return self::$_instance;
}
public function reset_instance(){
self::$_instance = null;
}
public function register_auto_loader(){
spl_autoload_register(array($this, 'load_class'));
spl_autoload_register(array($this, 'load_child_class'));
}
public function load_class($class){
$path = str_replace('_', '/', $class);
if(file_exists(get_template_directory() . '/' . $path . '.php')){
require_once(get_template_directory() . '/' . $path . '.php');
}
}
public function load_child_class($class){
$path = str_replace('_', '/', $class);
if(file_exists(get_stylesheet_directory() . '/' . $path . '.php')){
require_once(get_stylesheet_directory() . '/' . $path . '.php');
}
}
}
Currently this class will load anything in the parent project. It will even load parent project objects in the child project. How ever no child object can be loaded using this class as it is not found.
Those familiar with WordPress will instantly say, yes its because you have get_template_directory when you want get_stylesheet_directory How ever - Knowing this - I want to write then two auto loaders, one that will load child projects objects using get_stylesheet_directory and then one that will load parent objects via get_stylesheet_directory so that:
class project_one_folder_class extends project_two_folder_class{}
works and loads, with out error.
This is a little rough, but you actually only need one autoloader that just checks within multiple directories:
abstract class Hakre_Theme_AutoLoader
{
public static $directories;
public static function init()
{
// configure paths
self::$directories = array(
get_template_directory(), // current theme, parent in case of childtheme
get_stylesheet_directory(), // current theme, child in case of childtheme
);
// please double check if you really need to prepend
return spl_autoload_register('Hakre_Theme_AutoLoader::autoload', true, true);
}
public static function autoload($class)
{
$filename = str_replace('_', '/', $class) . '.php';
foreach (self::$directories as $directory) {
$path = $directory . '/' . $filename;
if (is_file($path)) {
require($path);
return; # leave because the class has been loaded
}
}
}
}
Hakre_Theme_AutoLoader::init();
## If you're unsure all worked out properly:
var_dump(Hakre_Theme_AutoLoader::$directories);
This should actually do it, however I've not tested it. See the var_dump, you can debug if the correct directories are registered or not. No need to have multiple callbacks only because you want to check in two directories.
Well, of course you could just look up a second path when the first file_exists returns false - in the very same autoloader.
You can also register a second autoloader for the second path.
PHP docs addresses this:
If there must be multiple autoload functions, spl_autoload_register() allows for this. It effectively creates a queue of autoload functions, and runs through each of them in the order they are defined. By contrast, __autoload() may only be defined once.
Separate the autoloading process!
PHP is working like this: At some point, the code execution stumbles upon a class reference that is not yet loaded. $obj = new project_one_folder_class()
Now the registered autoloader functions are called, starting with the one added first (unless the ones added later are using the "prepend" parameter).
The first (and all subsequent) autoload function gets the name of the class that should be loaded. The function now has the task to decide
Is it it's task to load this class? If not, do nothing, let the other functions try to load it.
If this function is responsible to load the class, try to make up a path and filename out of the known class name, and require this file.
Step 1 is usually solved by looking if the class name to be loaded starts with a certain prefix. In your case, the autoloader responsible for loading classes starting with "project_one_" should NOT try to load any other classes starting with something else.
Step 2 takes place if the autoloader knows that it must perform the autoloading. It now transforms the class name into a relative path and filename, adds the base directory, and then requires the file.
Requiring a file that defines a class which extends another class (or implements interfaces) can then trigger another run of autoloading with any still unknown classes. In your case, the autoload process is started again with the class name "project_two_folder_class". Note that the first autoloader for "project_one_" classes will be called again with this second class name, but it must not do anything, because it does not know how to load these classes. This is up to the autoloader that knows about "project_two_" classes.
Order of registration should not matter for the different autoloaders. If it does, autoloaders misbehave.
Because it is a common pattern to simply transform any underscore characters in class names into DIRECTORY_SEPARATOR, as well as the backslashes from namespaces, add ".php" at the end, and then try to load this file from a defined base directory, the code for two of these autoloaders will be identical, and the problem is reduced to configuring only one autoload function with these two cases: Which prefix should be present, and in which base directory should the file be expected to be, provided that the class name can be transformed into a relative path to the file.
Checking in every case whether the file exists in a certain directory or not is not optimal for performance. You'll end up doing plenty of checks for files that you will be able to know you cannot load by simply looking at the class name.
And by the way: Autoloader functions do not need to be singletons. In fact, your singleton implementation is broken, and useless. Instantiate the autoloader with new, maybe pass some configuration values into the constructor, and call spl_autoload_register(), and you should be done.
You can add more than one loader, just register each function with spl_autoload_register().
procedural style:
spl_autoload_register('loader_function1'));
// your second class location
spl_autoload_register('loader_function2'));
or in a OO style
spl_autoload_register(array($this, 'loader_function1'));
spl_autoload_register(array($this, 'loader_function2'));
However based on your question: you probably can accomplish the same thing by having a more flexible autoloader
see my answer in a different question here:
Class autoloader in wordpress plugin

Test whether a file exists anywhere in the include path

I'm writing an autoload function and in the inner logic of it would like to test whether a certain file exists somewhere in the path prior to including it.
This is the logic:
If a file named $className'.specialversion.php' exists anywhere in the include path include it. Otherwise, let other autoloaders take care of including a file for this class.
At the moment I just do: #include($calculatedPath);
I'm not sure if it's a good approach to include and suppress the error. I would rather check if the file exists (somewhere in the include path) prior to including it.
My question is:
Can I test for existence of a file anywhere in the include path?
Is it really problematic to do #include($calculatedPath);?
Edit
An important accent: I don't know where the file should be. I just want to know whether it exists in one of the directories in the include path. So I can't just do file_exists() or something like that.
As of PHP 5.3.2 there is the option to use the stream_resolve_include_path() function whose purpose is to
Resolve [a] filename against the include path according to the same rules as fopen()/include() does.
If the file exists on one of the include paths, then that path (including the file name) will be returned. Otherwise (i.e. the file was not on any of the include paths) it will return FALSE.
Relating this to your needs, your autoloader might look something like:
function my_autoloader($classname) {
$found = stream_resolve_include_path($classname . '.specialversion.php');
if ($found !== FALSE) {
include $found;
}
}
You should avoid the error supressor operator #.
function autoload($class) {
// Build path (here is an example).
$path = DIR_CLASSES .
strtollower(str_replace('_', DIRECTORY_SEPARATOR, $class)) .
'.class.php';
if (file_exists($path)) {
include $path;
}
}
spl_autoload_register('autoload');
$front = new Controller_Front;
// Loads "application/classes/controller/front.class.php" for example.
Update
An important accent: I don't know where the file should be, I just want to know whether it exists in one of the directories in the include path. So I can't just do file_exists or something like this
If your class could be in a number of directories, you could...
Have your autoload function traverse them all, looking for the class. I would not recommend this.
Rename your classes to have a name that easily maps to a file path, like in the example code above.
If you do decide to traverse all folders looking for the class, and it becomes a bottleneck (benchmark it), you could benefit from caching the class name to file location mapping.
I would use file_exists rather than a warnings-suppressed include.
Then you'll have to iterate through the include_path:
$paths = explode(';', get_include_path());
foreach($paths as $p){
if(file_exists($p . '/' . $calculatedPath)){
include $p . '/' . $calculatedPath;
break;
}
}
As a simple resolution, you should a test within the SPL-function file_get_contents() by setting the second argument to TRUE.
--Rolf
I've written a function that can test it nicely
function fileExists($file) {
if(function_exists('stream_resolve_include_path'))
return stream_resolve_include_path($file);
else {
$include_path = explode(PATH_SEPARATOR, get_include_path());
foreach($include_path as $path)
if(file_exists($path.DS.$file))
return true;
return false;
}
}

Efficient PHP auto-loading and naming strategies

Like most web developers these days, I'm thoroughly enjoying the benefits of solid MVC architecture for web apps and sites. When doing MVC with PHP, autoloading obviously comes in extremely handy.
I've become a fan of spl_autoload_register over simply defining a single __autoload() function, as this is obviously more flexible if you are incorporating different base modules that each use their own autoloading. However, I've never felt great about the loading functions that I write. They involve a lot of string checking and directory scanning in order to look for possible classes to load.
For example, let's say I have an app that has a base path defined as PATH_APP, and a simple structure with directories named models, views and controllers. I often employ a naming structure whereby files are named IndexView.php and IndexController.php inside the appropriate directory, and models generally have no particular scheme by default. I might have a loader function for this structure like this that gets registered with spl_autoload_register:
public function MVCLoader($class)
{
if (file_exists(PATH_APP.'/models/'.$class.'.php')) {
require_once(PATH_APP.'/models/'.$class.'.php');
return true;
}
else if (strpos($class,'View') !== false) {
if (file_exists(PATH_APP.'/views/'.$class.'.php')) {
require_once(PATH_APP.'/views/'.$class.'.php');
return true;
}
}
else if (strpos($class,'Controller') !== false) {
if (file_exists(PATH_APP.'/controllers/'.$class.'.php')) {
require_once(PATH_APP.'/controllers/'.$class.'.php');
return true;
}
}
return false;
}
If it's not found after that, I might have another function to scan sub-directories in the models directory. However, all the if/else-ing, string checking and directory scanning seems inefficient to me, and I'd like to improve it.
I'm very curious what file naming and autoloading strategies other developers might employ. I'm looking specifically for good techniques to employ for efficient autoloading, and not alternatives to autoloading.
This is what I have been using in all of my projects (lifted straight from the source of the last one):
public static function loadClass($class)
{
$files = array(
$class . '.php',
str_replace('_', '/', $class) . '.php',
);
foreach (explode(PATH_SEPARATOR, ini_get('include_path')) as $base_path)
{
foreach ($files as $file)
{
$path = "$base_path/$file";
if (file_exists($path) && is_readable($path))
{
include_once $path;
return;
}
}
}
}
If I look for SomeClass_SeperatedWith_Underscores it will look for SomeClass_SeperatedWith_Underscores.php followed by SomeClass/SeperatedWith/Underscores.php rooted at each directory in the current include path.
EDIT: I just wanted to put out there that I use this for efficiency in development, and not necessarily processing time. If you have PEAR on your path then with this you can just use the classes and don't have to include them when you need them.
I tend to keep my classes in a hierarchy of directories, with underscores breaking up namespaces... This code lets me keep the file structure nice and tidy if I want, or to inject a quick class file without nested directories if I want (for adding a single class or two to a library that it is defendant on, but not part of the project I am currently working on.)
I landed on this solution:
I created a single script that traverses my class library folder (which contains subfolders for separate modules / systems), and parses the file contents looking for class definitions. If it finds a class definition in a php file (pretty simple regex pattern), it creates a symlink:
class_name.php -> actual/source/file.php
This lets me use a single, simple autoload function that needs only the class name and the path to the main symlink folder, and doesn't have to do any path/string manipulation.
The best part is that I can rearrange my source code completely or add a new subsystem and just run the link generating script to have everything autoloaded.
If you want efficiency then you shouldn't be using the autoload feature at all. The autoload feature is for being lazy. You should be providing an explicit path to your include files when you include them. If your autoload function can find these files then you could code to find them explicitly. When you are working on the view part of the code and about to load a new view class, by letting the autoload function handle it, it first assumes your class is a model class? That's inefficient. Instead your code should just be:
include_once $this->views_path . $class . '.php';
If you need multiple "view" paths, make a function that loads views:
public function load_view($class) {
// perhaps there's a mapping here instead....
foreach ($this->views_paths as $path) {
$filename = $path . $class . '.php';
if (file_exists($filename)) {
include_once $filename;
}
}
throw ....
}
In any case, at the point where the include occurs, you have the greatest/most accurate information about the class you want to load. Using that information to load the class fully is the only efficient class loading strategy. Yes, you may end up with more class variables or (heaven forbid) some global variables. But that is a better tradeoff than just being lazy and scanning parts of the file system for your class.

Would performance suffer using autoload in php and searching for the class file?

I've always struggled with how to best include classes into my php code. Pathing is usually an issue but a few minutes ago i found this question which dramatically helps that. Now I'm reading about __autoload and thinking that it could make the process of developing my applications much easier. The problem is i like to maintain folder structure to separate areas of functionality as opposed to throwing everything into a general /lib folder. So if i override autoload to do a deep search of a class folder including all subfolders, what performance hits can i expect?
Obviously this will depend on scale, depth of the folder structure and number of classes but generally I'm asking on a medium scale project will it cause problems.
__autoload is great, but the cost of stating all the files in a recursive search function is expensive. You might want to look at building a tree of files to use for autoloading. In my framework, I consistently name files for their classes and use a map that is cached for the data.
Check out http://trac.framewerk.org/cgi-bin/trac.fcgi/browser/trunk/index.php [dead link] starting at line 68 for an idea of how this can be done.
Edit: And to more directly answer your question, without caching, you can expect a performance hit on a site with medium to heavy traffic.
A common pattern (Pear, Zend Framework as examples...) is to make the classname reflect the path, so Db_Adapter_Mysql will be in at /Db/Adapter/Mysql.php, from somewhere that's added to the include-path.
There are 2 ways that you could easily do this, first of all, name your classes so that they'll define the structure of where to find them
function __autoload($classname)
{
try
{
if (class_exists($classname, false) OR interface_exists($classname, false))
{
return;
}
$class = split('_', strtolower(strval($classname)));
if (array_shift($class) != 'majyk')
{
throw new Exception('Autoloader tried to load a class that does not belong to us ( ' . $classname . ' )');
}
switch (count($class))
{
case 1: // Core Class - matches Majyk_Foo - include /core/class_foo.php
$file = MAJYK_DIR . 'core/class_' . $class[0] . '.php';
break;
case 2: // Subclass - matches Majyk_Foo_Bar - includes /foo/class_bar.php
$file = MAJYK_DIR . $class[0] . '/class_' . $class[1] . '.php';
break;
default:
throw new Exception('Unknown Class Name ( ' . $classname .' )');
return false;
}
if (file_exists($file))
{
require_once($file);
if (!class_exists($classname, false) AND !interface_exists($classname, false))
{
throw new Exception('Class cannot be found ( ' . $classname . ' )');
}
}
else
{
throw new Exception('Class File Cannot be found ( ' . str_replace(MAJYK_DIR, '', $file) . ' )');
}
}
catch (Exception $e)
{
// spl_autoload($classname);
echo $e->getMessage();
}
}
Or, 2, use multiple autoloaders. PHP >=5.1.2 Has the SPL library, which allows you to add multiple autoloaders. You add one for each path, and it'll find it on it's way through. Or just add them to the include path and use the default spl_autoload()
An example
function autoload_foo($classname)
{
require_once('foo/' . $classname . '.php');
}
function autoload_bar($classname)
{
require_once('bar/' . $classname . '.php');
}
spl_autoload_register('autoload_foo');
spl_autoload_register('autoload_bar');
spl_autoload_register('spl_autoload'); // Default SPL Autoloader
Autoload is great PHP feature that helps you very much...
The perfomance wouldn't suffer if will use the smart taxonomy like:
1. every library stays in the folders "packages"
2. every class is located by replacing the "_" in the class name with the "/" and adding a ".php" at the end
class = My_App_Smart_Object
file = packages/My/App/Smart/Object.php
The benefits of this approach(used by almost any framework) is also a smarter organization of your code :-)
Hunting for files all over the place will make things slower (many more disk hits). Loading all of your classes in case you might need them will make things take more memory. Specifying which classes you need in every file is difficult to maintain (i.e. they don't get removed if they're no longer used).
The real question is which of these is more important to you? They're all tradeoffs, in the end, so you have to pick one. It's arguable, though, that most of the overhead in the second and third options has to do with actually compiling the code. Using something like APC can significantly reduce the overhead of loading and compiling every class on every page load.
Given the use of APC, I would likely take the approach of dividing up my code into modules (e.g. the web interface module, the database interaction module, etc.) and have each of those modules import all the classes for their module, plus classes from other modules they may need. It's a tradeoff between the last two, and I've found it works well enough for my needs.
I tend to use a simple approach where __autoload() consults a hash mapping class names to relative paths, which is contained in a file that's regenerated using a simple script which itself performs the recursive search.
This requires that the script be run when adding a new class file or restructuring the code base, but it also avoids "cleverness" in __autoload() which can lead to unnecessary stat() calls, and it has the advantage that I can easily move files around within my code base, knowing that all I need to do is run a single script to update the autoloader.
The script itself recursively inspects my includes/ directory, and assumes that any PHP file not named in a short list of exclusions (the autoloader itself, plus some other standard files I tend to have) contains a class of the same name.
Zend Framework's approach is to do autoload based on the PEAR folder standard (Class_Foo maps to /Class/Foo.php), however rather than using a set base path it uses the include_path.
The problem with their approach is there's no way to check beforehand if a file exists so the autoload will try to include a file that doesn't exist in any of the include_path's, error out, and never give any other autoload functions registered with spl_autoload_register a chance to include the file.
So a slight deviation is to manually provide an array of base paths where the autoload can expect to find classes setup in the PEAR fashion and just loop over the base paths:
<?php
//...
foreach( $paths as $path )
{
if( file_exists($path . $classNameToFilePath) )
include $path . $classNameToFilePath;
}
//...
?>
Granted you'll kinda be search but for each autoload you'll only be doing at worst n searches, where n is the number of base paths you are checking.
But if you find yourself still having to recursively scan directories the question is not "Will autoload hurt my performance," the question should be "why am I tossing my class files around in a random structure?" Sticking to the PEAR structure will save you so many headaches, and even if you decide to go with manually doing your includes as opposed to autoload, there will be no guessing as to where the class files are located when you do your include statements.

Categories