|
Posted by Kevin Newman on 11/29/05 00:59
The primary problem I've had with php is the lack of namespaces, which
makes OOP very difficult to organize, since you end up with large number
of classes cluttering up the same namespace - which leads to a secondary
problem involving php's __autoload feature. Since you cannot specify a
namespace when calling a class that may not have been included, you are
forced to store all of your classes in the same folder in your file
system. This can get quite unwieldy. I've also read that __autoload has
a performance hit over using a standard include (I'm not sure if this is
still true). Finally, I've read that require_once can be very buggy in
the php documentation comments. (Is that true?)
After thinking about the namespace problem, I've concluded that php's
conditional include nature keeps the runtime environment pretty clean
(if I have two classes with the same name, I don't usually have to worry
about it, since I will usually only include one at a time) and its
runtime footprint small. While it's not perfect, it does provide enough
of something like runtime namespace protection, at least enough to keep
me from creating a bulky framework to emulate more robust namespaces.
This left me with the problem of organizing all of my class files on the
file system. I do not want to keep 100s of classes in the same one
folder - especially since there are many classes in there that will
probably not be used, but I'd like to keep around for just in cases.
Since __autoload seems to have performance issues, I decided not to use
that either. (Is this still an issue?)
Since I've decided that for my needs php's include functions are a good
enough substitute for real namespace imports I just needed a way to load
class files that are organized in a namespaces like folder structure. I
would have just used require_once, but it has those bugs I mentioned. So
I came up with a single function that I named "using" (borrowed from
..NET and Prado - which actually has a pretty spiffy solution to the
namespace problem that uses __autoload).
Here is the function (it is horribly unoptimized and probably buggy):
$namespaces = array();
$classPath = 'C:/Inetpub/wwwroot/_domains/adcecart/_includes/';
/**
* Includes class files, which are stored in Namespace like folders
* @param string colon delimited namespace string.
*/
function using($namespace) {
global $namespaces, $classPath;
// quick return if repeat import
if(isset($namespaces[$namespace])) return;
// convert $namespace to path
$path = $classPath . str_replace(':','/',$namespace);
if (is_dir($path)) {
// add namespace to hash (to avoid double import)
$namespaces[$namespace] = $namespace;
// add all files in the directory
$dir = dir($path);
while (false !== ($entry = $dir->read()) ) {
if (preg_match('/.php$/i', $entry)) {
$newNamespace = str_replace('/',
':', substr($namespace.'/'.$entry, 0, -4));
if (!isset($namespaces[$newNamespace])) {
$namespaces[$newNamespace] = $newNamespace;
require_once($path.'/'.$entry);
}
}
}
$dir->close();
} else if (is_file($path.'.php')) {
$namespaces[$namespace]=$namespace;
require_once($path.'.php');
} else
exit('Error importing namespace.');
}
// used like this
using('unFocus:Feature:ClassName');
This actually does one more thing than a simple enhanced require_once -
it can import all of the php files in a specific folder (or namespace).
I don't use this feature, and will probably remove it unless I find some
need for it.
This function works differently (and I use it differently) than a
regular namespace implementation would. In other languages, namespace
imports (or using) are tied to the specific class file scope, and only
applied to class within that class. As far as I can tell, php has no
similar scoping rules (since it is an inline interpreter, unless
something has changed that I am unaware of), so this will just include a
class file (and anything else that might be in that file actually - like
spare functions or variables), and dump all that into the global scope
(except variables).
This can also be used with performance in mind - let's say you want to
use a class from another class, and you will only need it if conditions
are met. You don't have to import the class if you don't need it, saving
memory, parsing, and cpu time. To me this is a benefit, since php is
really an inline interpretor at the end of the day (unless you use an
opcode cache I guess).
At this point really just rambling, so I'll just finish up with a summary.
The goal was to be able to organize and group my class files in some
kind of reasonable way - namespaces provided that way, but are
unavailable in php. Understanding the nature of php's inline
interpretation, conditional includes and __autoload's inability to cope
with a namespace like directory structure and its possible performance
consideration convinced me that a simple require_once function used with
classes named carefully would actually be adequate to make sure I was
only importing classes that I needed and that they would not clash with
one another or with built in php classes (I've never run into that
problem yet). After trying out a bunch of different namespace solutions,
I wrote a simple function (which can be a lot simpler) that will include
the necessary class files only once, when they are needed, and not
before, from within their namespace like directory structure.
Now the question! :-) Basically - What do you think of my reasoning and
rational?
Also, if I'm wrong about any of the details of how PHP works, I'd
appreciate some clarification on anything I got wrong.
Thanks,
Kevin N.
[Back to original message]
|