php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #49625 spl_autoload and case sensitivity
Submitted: 2009-09-22 16:01 UTC Modified: 2009-09-23 07:11 UTC
From: jo at feuersee dot de Assigned:
Status: Not a bug Package: SPL related
PHP Version: 5.3.0 OS: Linux
Private report: No CVE-ID:
 [2009-09-22 16:01 UTC] jo at feuersee dot de
Description:
------------
This is basically the same as PHP bug #48129.

Yes, I have read it "won't fix"

My opinion on this is "won't fix" is not an option because it _is_ a bug and not fixing bugs does not work:

1) It is common practice in OO languages (including PHP) to give classes case sensitive names. Even the classes of PHP itself are case sensitive and usually start with capital letters (eg. DateTime, Exception, ...). PHP related projects like PEAR, Zend Framework etc. do the same.

2) In order to get a proper 1:1 mapping from class name to the file containing the PHP class definition, projects like PEAR or Zend Framework use the case sensitive class name, eg. System.php contains the class System. Again, this is common practice in other OO languages like C++.

3) What happens when the file system is case sensitive?
See example: the script fails because the PEAR class System will be looked for in a file named system.php which does not exist because it is called System.php
The workaround is using SPL_autoload_suxx instead. But look at the code: there are several compatibility issues (include_path separator : vs. ;), it does work but is not at all convenient.

4) What would happen if spl_autoload() wouldn't lowercase the class name when looking for a class definition?
a) Filesystem is case sensitive
It would work!
The spl_autoload() would look for a file called System.php which exists, thus will be require'd

b) Filesystem is not case sensitive
It would still work!
The spl_autoload() would look for a file called System.php
Because the file system is case insensitive, it would use either System.php or system.php (or sYSTEM.PHP - you got the point?).
Because on case insentive filesystems both files "System.php" and "system.php" are not allowed in the same directory, there is _no_ issue with backward compatibility.

The only circumstances where it would break backwards compatibility would be on filesystem which is case insensitive but does not allow capital letters. Any real live examples of such a file system? 

Conclusion:
The current specification of spl_autoload() with implicit lowercasing is excactly wrong. There has been, is and never will be any gain in this 'feature' since the class name itself inside PHP is case sensitive.


Reproduce code:
---------------
<?php
/**
 * Demonstration of the current incompatibility 
 * Make sure you have PEAR inside your PHP include_path
 */

// this should work but doesn't
spl_autoload_register('spl_autoload');

// this does work
//spl_autoload_register('SPL_autoload_suxx');

/**
 * Does the same as spl_autoload, but without lowercasing
 */
function SPL_autoload_suxx($name)
{
	$rc = FALSE;
	
	$exts = explode(',', spl_autoload_extensions());
	$sep = (substr(PHP_OS, 0, 3) == 'Win') ? ';' : ':';
	$paths = explode($sep, ini_get('include_path'));
	foreach($paths as $path) {
		foreach($exts as $ext) {
			$file = $path . DIRECTORY_SEPARATOR . $name . $ext;
			if(is_readable($file)) {
				require_once $file;
				$rc = $file;
				break;
			}
		}
	}
	
	return $rc;
}

$binaries = array(
	'mysql' => System::which('mysql'),
	'mysqlbinlog' => System::which('mysqlbinlog'),
	'php' => System::which('php')
);
print_r($binaries);
		
?>

Expected result:
----------------
Array
(
    [mysql] => /usr/bin/mysql
    [mysqlbinlog] => /usr/bin/mysqlbinlog
    [php] => /usr/local/bin/php
)


Actual result:
--------------
PHP Fatal error:  Class 'System' not found in /srv/www/vhosts/www.easy-sew.de/ftpjung/bin/autoload.php on line 38


Patches

php_sql_autoload_mixed_case_fix (last revision 2012-06-15 20:00 UTC) by jdornan at stanford dot edu)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-09-22 16:22 UTC] sjoerd@php.net
Thank you for your bug report.

Wontfix means: we agree that there is a bug, but there are reasons not to fix it. The reason here is that is spl_autoload becomes case sensitive, it will break scripts which depend on spl_autoload being case insensitive.
 [2009-09-22 19:37 UTC] jo at feuersee dot de
>The reason here is that is spl_autoload becomes case
>sensitive, it will break scripts which depend on spl_autoload being
>case insensitive.

spl_autoload() was introduced in PHP 5.1.2 which is case sensitive concerning class names. This implies that if an operation on an unknown class is done, spl_autoload() is triggered and executed with the case sensitive name of the class.
Thus we have 4 different possibilities:

1) The class name all lower case, the file containing the class definition is all lower case (eg. $foo = system::bar(); system.php)

This will work independent wether spl_autoload() is lowercasing or not, since all is lowercased. 
Note that if the class defined in the file system.php is actually named System it wouldn't have ever worked because the class system is still not defined, which would trigger an error.

2) The class name all lower case, the file containing the class definition is uppercased (eg. $foo = system::bar(); System.php)

This wouldn't work anymore on file systems which are case sensitive if spl_autoload() would skip lowercasing.

Note that this would only have worked if the file system is case insensitive and the class definition in System.php would define a class "system". 

3) The class name contains upper case letters, the file containing the class definition is lowercased (eg. $foo = System::bar(); system.php)

This is what currently isn't working at all but would work at least for case insensitive file systems if lowercasing would be dropped.

Note that if the class defined in the file system.php is actually named system it wouldn't have ever worked because the class System is still not defined.

4) The class name contains upper case letters, the file containing the class definition is uppercased (eg. $foo = System::bar(); System.php)

This is what should (and would) work, but currently doesn't.


Conclusion:

The only problem might be (2):

Class name: sample
Filename: Sample.php
Class definition in Sample.php: class sample { ... }
Note: this does work on case insensitive file systems only.

I really can't see any reason for maintaining the "Worse is better" principle here, I really doubt that there is much code around relying on the tolowercase feature/bug of spl_autoload().

As a compromise I propose the following:
1) spl_autoload() additionally tries to find a file _not_ lowercased. 2) Throw a E_DEPRECATED in case the filename had to be lowercased to match.

Until then:
I really don't know why this lowercasing thing was introduced into slp_autoload() to begin with, all it ever did was preventing classes to be named with upper case letters on file systems which are case sensitive. In other words: the only compatibility issue is that code which currently works on platforms like Windows only would suddenly work on UN*X like platforms too.

Pls confirm if this is the compatibility issue you are talking about.
 [2009-09-23 07:11 UTC] sjoerd@php.net
Trying both lowercased and original case could solve this without breaking backwards compatibility. However, you could as well supply your own autoload function defined in PHP to solve this.
 [2011-03-08 13:08 UTC] simon at systemparadox dot co dot uk
Why is this bug marked as bogus?
Even if spl_autoload itself isn't fixed, at the very least a version that does it correctly could be added (although in this case it seriously could just be fixed by trying the correct case first).

Implementing one in PHP is all very well, but that means that it's non-standard and likely incompatible with what each programmer might expect. It's also slower.
 [2011-08-10 01:13 UTC] bram048 at gmail dot com
I agree with Simon. There is absolutely no reason not to fix this, while keeping 
backwards compatibility.

Two years ago the reason for not fixing it was "breaking BC is not an option". 
There are plenty of alternatives, and to be honest, PHP has broken BC alot of 
times in the last few versions (which is a good thing in my opinion, as long as 
the language becomes cleaner/stricter).

Having all the files in lowercase makes them alot harder to read. Having a 
custom autoloader function is slower and more complicated to get right, and just 
makes code more ugly and harder to understand.

At the very least case sensitivity of the SPL autoloader should be configurable, 
or available by the use of an extra suffix. I would love to see this in the new 
5.4 release; shouldn't take more than a few lines of code.
 [2011-09-05 01:32 UTC] blake dot soley at gmail dot com
I agree, this bug is not bogus.  In fact, it's actually quite serious.  Just because spl_autoload was designed poorly doesn't mean it has to remain broken.  Several fixes have been proposed, please consider implementing one of them to make this function cross-platform, and therefore useful.
 [2012-05-14 23:45 UTC] david at panmedia dot co dot nz
Please fix this
 [2012-06-01 23:10 UTC] mrex at interport dot net
After having lost 2 days over this, I agree, this should be fixed.  At the very least, it should be documented that spl_autoload lower cases filenames.  I spent hours trying to register an autoload class that would fix this, but in vain.  How can I know that "dataloader" should be mapped to "DataLoader"?

If it had been documented that filenames were lowercased, I would not have spent hours banging my head against a brick wall...
 [2012-06-15 19:59 UTC] jdornan at stanford dot edu
This seems like a 20 min fix to me, and I've never looked the sql_autoload code 
before. I spent a good deal of time spinning my wheels on this. I don't see why 
anyone should lose time of this very obvious bug. However it's better to patch 
that to bitch, as I always say. I'll submit my patch and see what happens. It's 
not big deal if this is not fixed, since it's so easy to fix I can keep fixing 
it with each release.

Here is a diff, I'll submit the patch with the "Add a Patch" link.


225d224
<       char *lc_class_file;
227d225
<       int lc_class_file_len;
229d226
<       int mixed_case = 0;
235,236c232
<       lc_class_file_len = spprintf(&lc_class_file, 0, "%s%s", lc_name, 
file_extension);
<       class_file_len = spprintf(&class_file, 0, "%s%s", class_name, 
file_extension);
---
>       class_file_len = spprintf(&class_file, 0, "%s%s", lc_name, 
file_extension);
252,261c248
<               mixed_case = 1;
<       }
< 
<         /* fall back to lowercase file name. should issue deprecated warning. 
*/
<         if (ret != SUCCESS) {
<               ret = php_stream_open_for_zend_ex(lc_class_file, &file_handle, 
ENFORCE_SAFE_MODE|USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
<         }
< 
<       if (ret == SUCCESS) {
<               if (!file_handle.opened_path && mixed_case == 1) {
---
>               if (!file_handle.opened_path) {
263,264d249
<               } else if(!file_handle.opened_path && mixed_case == 0) {
<                       file_handle.opened_path = estrndup(lc_class_file, 
lc_class_file_len);
290d274
<                       efree(lc_class_file);
295d278
<       efree(lc_class_file);
331d313
< 

PS: I really hate this bug with a passion.
 [2012-12-17 16:01 UTC] admin at tandyukservers dot co dot uk
Status: 	Not a bug
Bullshit.
IS A BUG.

When will this be recognised as such by you php devs and added to the list of bugs to be fixed!
 [2013-01-09 10:00 UTC] wim at asgc dot be
Since I have an extensive codebase relying on classes defined with uppercase 
starting letter and I saw the 'tip' in the documentation (see below) , I wanted 
to switch. To my surprise I bumped into this issue with spl_autoload_register 
(needless to say that it works on a Windows box as a charm and breaks terribly 
on a Linux box).

It definitely is a bug which should be mentioned clearly in the documentation. 
In addition I would strongly suggest the __autoload function will not be 
deprecated until this is fixed.
--
From the documentation :
Tip
spl_autoload_register() provides a more flexible alternative for autoloading 
classes. For this reason, using __autoload() is discouraged and may be 
deprecated or removed in the future.
--
 [2013-01-30 09:41 UTC] jurchiks101 at gmail dot com
Bumping this bug, at least add a boolean parameter to it for case-sensitivity.
This should be literally a 5-minute job for the devs, why has nobody fixed it for 
3 years and counting? Have you no shame?
 [2013-03-19 12:49 UTC] james at jamestandy dot com
Also add my support to
FIX THIS BUG!!!

Just tried adding the Ebay Trading API to my existing site, which is using spl_autoload.

Because the ebay team (like it seems every other sensible dev on the entire planet) uses CamelCased filenames, I now have 3 choices:

1:  Do not use spl_autoload  (Not an option because this would break our existing site).

2:  Do not use the Ebay Trading API, or write my own implementation of this from scratch. (Also not an option for obvious reasons).

3: Rename over 1000 files, and replace every CamelCase instance in each and every file with lower case names.

3 is looking to be my only option, which of course would need repeating each and every time Ebay update their API, so is really NOT a viable option.
 [2013-03-22 17:45 UTC] abr28 at cam dot ac dot uk
Like so many others I also think this is a much too obvious bug, unexpected behaviour, etc ... you name it. It's a poor implementation that needs to be fixed even if it means breaking compatibility with PHP code that relyies on it, code + files which are poorly cased anyway (!).

However you can maintain compatibility by introducing a new function which alters a case sensitivity flag. Just like you already have spl_autoload_extensions() to hint/restrict the extensions for spl_autoload(), you can have a function spl_autoload_case() and call it once, e.g.:

spl_autoload_case(false); // for the broken lowercase spl_autoload()
spl_autoload_case(true);  // to respect case sensitivity

You'd call this before spl_autoload() gets called. You can even make the default to be lowercase, like you so insist on having.

This way you don't break compatibility -- although you should(too many aspects of PHP encourage bad coding already).
 [2013-05-15 12:19 UTC] martijn at 51north dot nl
There's a few things I'd like to add (actually a lot but it's probably best if I keep most of it to myself):

sjoerd@php.net: "it will break scripts which depend on spl_autoload being case insensitive."
This suggest that right now spl_autoload is in fact case insensitive, which it is not. A case insensitive system should find Core.php when asking for Core.php, just like a case sensitive system would. The difference is that it would ALSO find core.php which would be fine by me. Now it fails to find Core.php making it case destructive at best.

wim at asgc dot be: "In addition I would strongly suggest the __autoload function will not be deprecated until this is fixed."
Thank god I love irony, however, this won't actually be a problem as you can still use custom auto loaders. All you need to do is register it using spl_autoload_register().

And finally, when using namespaces it is quite easy to get around this problem using a short autoloader function:

function SPL_autoload_suxx($class) {
    include \str_replace('\\', '/', $class) .'.php';
}
\spl_autoload_register(__NAMESPACE__ .'\SPL_autoload_suxx');

All you have to do is copy, paste and mop up the river that you've cried.
 [2013-11-09 22:53 UTC] moon at quantentunnel dot de
After searching around a while I found a workaround without performance loss.

1. Slower but not working with camelcase class files (like "MyClass.php"):
spl_autoload_register(function($classname) { require_once(__DIR__ . '/' . str_replace('\\', '/', $classname) . '.php'); });

2. Faster but not working with camelcase class files:
set_include_path(get_include_path() . PATH_SEPARATOR . __DIR__);
spl_autoload_extensions(".php");
spl_autoload_register();
spl_autoload_register( function($classname) {} );

3. Faster and working with camelcase class files:
set_include_path(get_include_path() . PATH_SEPARATOR . __DIR__);
spl_autoload_extensions(".php");
spl_autoload_register(function ($classname) { spl_autoload($classname); });
 [2013-11-09 23:36 UTC] moon at quantentunnel dot de
I'm sorry, my last post was not correct, case 3 does not work :-(
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 16 18:01:53 2014 UTC