Page 2 of 5

Posted: Fri May 11, 2007 5:53 am
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...

Posted: Fri May 11, 2007 5:55 am
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

Posted: Fri May 11, 2007 5:56 am
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.

Posted: Fri May 11, 2007 6:09 am
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.

Posted: Fri May 11, 2007 6:30 am
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.

Posted: Fri May 11, 2007 8:33 am
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.

Posted: Fri May 11, 2007 8:50 am
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.

Posted: Fri May 11, 2007 9:21 am
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 :)

Posted: Fri May 11, 2007 11:11 am
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.

Posted: Fri May 11, 2007 11:35 am
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.

Posted: Fri May 11, 2007 7:59 pm
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.

Posted: Fri May 11, 2007 10:16 pm
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 ...

Posted: Sat May 12, 2007 4:53 am
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.

Posted: Sat May 12, 2007 5:08 am
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'));

Posted: Sat May 12, 2007 9:31 am
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.