michael_morris1 — 2011-08-18T11:18:48-04:00 — #1
Classes can be autoloaded through spl_autoload. Functions cannot. This has lead to functions becoming increasingly 2nd class citizens. On the PHP dev list a proposal for PHP 5.4 has been made to allow autoloading of functions. However there are problems in the implementation and it remains to be seen if the RFC will settle down before last call for API changes and we go to 5.4 beta (I sort of doubt it will).
While discussing it yesterday I hit upon an elegant PHP 5.3 legal solution. Whenever you reference a class for the first time the existing autoload system will provide your autoload function with the fully qualified namespace path to the class. So you know implicitly that a namespace is about to get used.
So I thought to myself, if I was going to do namespace functions I would put them all in one file. Same with constants - to be honest likely the same file. So I thought, why can't the existing class autoload system handle the problem?
I don't think there's a reason it can't. Consider
- The class is requested. Say "MyNamespace\MyClass"
- The autoloader sees if there's a "MyNamespace/functions.php" file. If so, it calls require_once() on the file (PHP itself will track whether the file actually needs inclusion).
There are other ways to do this of course. It isn't foolproof of course, but it's highly unlikely in a primarily object-oriented system that a class method will call a function from a foreign namespace, and if it does there are still ways to handle this. One is to have the namespace file that defines the function itself call require for any files it depends on.
michael_morris1 — 2011-08-22T10:01:37-04:00 — #2
There was a time that something like this would have attracted more than a few comments. What's up guys - am I on everyone's ignore list?
Anyway, this is working well. The core namespace must have it's definitions loaded via configuration as a member of a list of files we have to load to support the autoloader itself. Previously I had the constants in the configuration - but this was clumsy insofar as these are things that almost should never change.
Because of how Gazelle is structured most all code is already in a class, and no class references functions in another class' namespace.
So far I haven't put a function in the definition file, just constants. If I do put a function in there then for testing reasons it must fit two conditions: 1) Have no internal state and 2) Reference no external entities other than (possibly) other functions within the same name space or the PHP core namespace.
Thoughts on this?
ahundiak — 2011-08-22T15:00:50-04:00 — #3
I for one enjoy your posts.
It's just that I honestly can't remember the last time I wrote a stand alone global function. I'm really quite surprised that this sort of auto loading functionality was even considered.
So just out of curiosity, what sort of functions have you written that need to be auto loaded?
michael_morris1 — 2011-08-22T15:29:16-04:00 — #4
Currently, none. But I want to have a mechanism in place just in case. And constants do get defined regularly (easier to change a constant definition than look up a string everywhere). I did have a group of functions that extended the bcmath library, but when the head count reached 5 I decided to put them in a static class because much of the time loading them was pointless overhead as their purpose is narrow.
ren — 2011-08-22T21:10:16-04:00 — #5
TBH, I think PHP autoloading is complete pants, together with the stupid one class per file rule.
If you care about performance then you use APC. If you use APC then that has lazy loading (See apc.lazy_functions & apc.lazy_classes). Functions and or class will be only copied out of APC until they are actually needed.
ahundiak — 2011-08-22T21:15:00-04:00 — #6
One class per file is a convention not a rule. A rather widely followed convention among many languages but a convention (in PHP at least) nonetheless.
Put as many classes in a file as you want then write a class loader to figure out which file they are actually in. Works fine.
ren — 2011-08-22T21:24:09-04:00 — #7
Yes, but still sucks.
I rather use dependency injection which explicitly loads in components/modules on as needed basis.
Less to maintain, if want to change an implementation of a service provided by the dependency injector, the require line is right there together with the new () call, together in a single location.
ahundiak — 2011-08-22T21:47:55-04:00 — #8
Hmmm. We seem to be straying off topic but perhaps Mr Morris won't mind.
Autoload also loads classes as needed so no difference there. Plus, no need for individual requires.
And are you really loading all of your classes using some sort of dependency injection container? That just doesn't seem quite practical. Be interested in seeing some details.
michael_morris1 — 2011-08-22T21:57:00-04:00 — #9
I'm not one of those guys who feels he has any ownership on a thread. Let it meander where it will.
I use APC caching as well. And it's invaluable.
As far as the 1 class 1 file convention, that is the best organization scheme I've seen so far.