Question about autoloading

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

There was a whole debate on the Zend Framework mailing list about __autoload() - If I remember right one point that stood out was that APC would cache the class file, but could not cache the actual class/methods it contained once they were parsed. So there is some level of caching (the file) and that's usually enough to offer a significant benefit since it skips the filesystem read. The smaller cost of using __autoload is that the file's contents are only captured and parsed after __autoload executes (i.e. after the initial parsing and opcode caching has taken place) so it misses that specific boat compared to an upfront include.

Important to distinguish between the caching of the file content vs caching the intermediate parsed code. Wish I had a thoroughly useless benchmark to point to, but I believe the difference between both isn't that much. Of course the problem with thoroughly useless benchmarks is that they tend to test specific things in isolation, not within a real application where that incredible difference amounts to squat in reality...
User avatar
dreamscape
Forum Commoner
Posts: 87
Joined: Wed Jun 08, 2005 10:06 am
Contact:

Post by dreamscape »

kyberfabrikken wrote:Appearently, there are some problems with autoload vs. opcode caches. I haven't had time to investigate it in details, but according to this post, using autoload, makes it impossible for apc to cache the file.
That is false. Of coarse autoloaded files are cached. See this follow up thread on pecl-dev: http://marc.info/?l=pecl-dev&m=116512075914909&w=2
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

My point was this..

Code: Select all

include_once 'somefile.php';
initiates opcode caching.. so what's the difference of:

Code: Select all

function __autoload($classname)
{
    // jiggery pokery
    include_once($somefile);
}
both use include_once, so both will use the same opcode caching.
User avatar
kyberfabrikken
Forum Commoner
Posts: 84
Joined: Tue Jul 20, 2004 10:27 am

Post by kyberfabrikken »

Jenk wrote:so what's the difference of
I'm guessing here, but in the first example, inclusion will always happen; Thus the inclusion can happen at parse-time, while in the latter, the code must be interpreted, in order to determine, if the file should be included.
User avatar
stereofrog
Forum Contributor
Posts: 386
Joined: Mon Dec 04, 2006 6:10 am

Post by stereofrog »

Include always takes place at run time, however, one can imagine a kind of optimizer/preloader that is smart enough to see the difference between " include 'const_string' " and "include $var_string" and act accordingly. From this point of view, autoload is obvious evil.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

both use include_once, so both will use the same opcode caching.
Not quite the same. The entire source code for that request must be compiled and executed, then it picks up on any conditional (i.e. includes hidden in functions, statement blocks, etc.) includes. At this point a missing class, is just a missing class. Autoload is basically what arborint described - an error handler. It's only the original phase which went through the entire process of compilation and execution that can be fully cached as expected - everything else makes do with a lesser form of caching which is slightly slower.
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

http://sb2.info/better-solutions/techno ... erability/

cached and compiled just the same, the only difference being that the conditional includes are cached at every request (as you say, but not with a lesser cache). This is a completely negligible difference given that everything PHP is parsed and compiled at every request anyway.
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

kyberfabrikken wrote:
The Ninja Space Goat wrote:

Code: Select all

function __autoload($classname)
{
    if (!class_exists($classname))
    {
(...)
Calling class_exists() inside __autoload makes little sense. If anything, it should be class_exists($classname, FALSE), but it's still completely redundant.
Noted, thanks :)
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

Maugrim_The_Reaper wrote:Personally I stick with require_once. If I want __autoload I'll use the SPL registry (safer than using __autoload when embedded applications could already have defined it) and likely use a Phing task to strip out the require_once calls. So I can move between both worlds with little more than a command line parameter ;).
Awesome, because this gets to kinda what I was after in this thread... Using the SPL registry, you are not required to define an __autoload routine, correct? So if you were to do something like:

Code: Select all

<?php
spl_autoload_register(array('MyClass', 'ClassMethod'));
?>
That then does what with the MyClass class and what with the MyClass::ClassMethod method? Would it matter if that method was a static method, or if the class was essentially a static class?

I gotta say this is some fantastic discussion. It covers a topic I truly had no knowledge of (let alone interest in) and I am totally digging the caliber of opinion in this thread.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

cached and compiled just the same, the only difference being that the conditional includes are cached at every request (as you say, but not with a lesser cache). This is a completely negligible difference given that everything PHP is parsed and compiled at every request anyway.
I don't disagree that the difference is negligible, just that the state of the conditionally included file's cache isn't as optimal as something that was included outside any condition. Whether you term that a lesser cache, suboptimal cache, I haven't the foggiest. I just know it constantly requires extra processing for each request to define the classes/methods. The cache itself is persistent in shared memory across requests.
Everah wrote:That then does what with the MyClass class and what with the MyClass::ClassMethod method? Would it matter if that method was a static method, or if the class was essentially a static class?
I don't think it matters. I tend to just use a plain function though ;). I would recommend the SPL registry as much as possible. The more libraries and applications get mashed together, the more you really need to play nice and avoid conflicts. Since there can only be one __autoload, it's almost necessary to avoid it.
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

I would like to urge people to leave debates about performance out of the thread, and in fact all others. If you care about performance, get a profiler and see what takes time in your application, in it's own production environment.
Arborint wrote:The are simply error callback functions for the new keyword. If there is a "class not found" error when a new is executed
Not really. You aren't handling an error because the code you write in __autoload does both, prevent one from occuring, or allow it to occur without being able to handle the error (because it's a fatal). Handling an error means choosing how to deal with it, log it, display it etc. that's not what we are doing here.

Do remember that __autoload() is also class overloading. Take a few seconds to think about what that means. Here's a real world example that you can look forward to for newer versions of SimpleTest (according to MB's informal ramblings). Assume the simpletest classes have been loaded already...

Code: Select all

class B
{
    public function run() { }
}
class A 
{
    public function runner($toRun)
    {
        $toRun->run(); 
    }
}
class TestOfA extends UnitTestCase
{
    public function testRunWasCalledByA()
    {
        $inst = new A();
        $mock = new Mock_B();
        $mock->expectOnce('run');
        $inst->runner($mock);
    }
}
Here Mock_B is never defined but __autoload() can easily watch for classes beginning with 'Mock' and use the rest of the name to determine what class to generate a mock class of. And thus no longer is there need for Mock::generate() calls.

You could take this futher and dynamically generate classes with different behaviours depending on their names. You could achieve multiple inheritance this way if you wanted to. Of course it's called overloading for a reason, this kind of stuff really changes the language, often to the detriment of predictability and readability.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

ole wrote:Not really. You aren't handling an error because the code you write in __autoload does both, prevent one from occuring, or allow it to occur without being able to handle the error (because it's a fatal). Handling an error means choosing how to deal with it, log it, display it etc. that's not what we are doing here.
Just because the the likes of lastcraft can find obscure and interesting ways to use __autoload() does not change the fact that it simply calls a function when a class not found error occurs. There is no reason why that function couldn't "choose how to deal with it, log it, display it etc." It's just that the obvious intention is to put code that includes a file in there.

My point was it was an easily tacked on feature from a group of folks that quite honestly don't have a deep understanding or appreciation of OOP -- and it shows. That's not to say that __autoload in not a workable solution. I clearly agreed that it is. But there are many other interesting things that could have been done to solve this problem. They could have implemented include_class('path/to/file.php', 'ClassName'); or they could have made new a real overrideable thing or added a file specifier to new like $foo = new Foo() infile 'path/to/foo.php'. It could be done with Metadata given to include_path or many other ways ... but we have what we have ...
(#10850)
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

Just because the the likes of lastcraft can find obscure and interesting ways to use __autoload() does not change the fact that it simply calls a function when a class not found error occurs. There is no reason why that function couldn't "choose how to deal with it, log it, display it etc."
What you say is correct. I just don't agree with the label of "error handler" in this case. If I wanted to log a class cannot be loaded error I would throw an exception and use set_exception_handler()
They could have implemented include_class('path/to/file.php', 'ClassName'); or they could have made new a real overrideable thing or added a file specifier to new like $foo = new Foo() infile 'path/to/foo.php'. It could be done with Metadata given to include_path or many other ways ... but we have what we have ...
Yes. What we have is a space to write our own code to specify what actions are performed. This is clearly more flexible than any fixed system.
quite honestly don't have a deep understanding or appreciation of OOP
I don't see how that is the case. That said, If you could have a separate __autoload() for each namespace and, of course, actual namespaces to go with it, that would be amazing.
User avatar
kyberfabrikken
Forum Commoner
Posts: 84
Joined: Tue Jul 20, 2004 10:27 am

Post by kyberfabrikken »

Everah wrote:So if you were to do something like:

Code: Select all

<?php
spl_autoload_register(array('MyClass', 'ClassMethod'));
?>
That then does what with the MyClass class and what with the MyClass::ClassMethod method? Would it matter if that method was a static method, or if the class was essentially a static class?
spl_autoload_register takes a callback as argument. A callback is not validated until the time, where it's called. So if the class MyClass isn't defined, once autoload is invoked, it will fail. Thus, you'd want to explicitly load that particular class beforehand.

A callback can also be a method on an object instance. So this is valid as well:

Code: Select all

class MyAutoLoader
{
  function loadClass($classname) {
    (...)
  }
}
spl_autoload_register(array(new MyAutoLoader(), 'loadClass'));
User avatar
dreamscape
Forum Commoner
Posts: 87
Joined: Wed Jun 08, 2005 10:06 am
Contact:

Post by dreamscape »

arborint wrote:The are simply error callback functions for the new keyword.
new is not the only autoload trigger. Functions such as class_exists and interface_exists will also trigger it.
Post Reply