Class and interface location
| From: | Larry Garfield | Date: | Wed, 19 Jan 2011 07:07:09 +0000 |
| Subject: | Class and interface location | ||
| Groups: | php.general | ||
| Request: | Send a blank email to php-general+get-310858@lists.php.net to get a copy of this message | ||
Hi all. I'm trying to come up with a solution to an architectural problem I
could use some extra eyes on.
I have (or will have) a large code base with lots of classes, spread out over
many optional plugin components. These classes, of course, will have
interfaces on them. These classes will, of course, lazy-load using PHP's
autoload capability.
What I need to know is which classes implement which interfaces, so that I can
load and use "all classes that implement interface Foo".
There are a couple of approaches that I've considered, all of which I dislike
for one reason or another.
1) Magic file naming. That is, any class that implements interface Foo lives
in a $something.Foo.inc file, or some similar pattern. This has a number of
problems. First, it means a huge number of file_exists() calls to determine
if a given potential class exists, and then disk hits to load those files.
While that information is possible to cache, it also means that we cannot have
a class that implements two of the interfaces I'm interested in (a feature I
will need) because then it would have to live in two files, which is clearly
impossible.
2) Reflection. PHP makes it quite easy to get a list of all loaded classes,
and to get a list of all interfaces that a loaded class implements. The catch
there is "loaded". Reflection only works once you've loaded the code file
into memory. Once you've done so, you cannot unload it. That means the code
is sitting there in memory wasting space. Given the size of the code base in
question, that could easily blow out the process memory limit. Even if we
cache that information we derive from reflection we still have to load it the
first time (or periodically when rebuilding the cache), which will blow the
memory limit. The only alternative would be to have some sort of incremental
rebuild across a multi-request batch process, the complexity of which makes my
skin crawl.
3) Static analysis. Instead of reflection, either tokenize or string parse
all files to determine what classes implement what interfaces and then cache
that information. We are actually using this method now to locate classes,
and it works surprisingly well. Because we never parse code into memory it
does not ever spike the memory usage. However, it is impossible to determine
if a class implements a given interface by static analysis unless it declare
so itself with the implements keyword. If it does so indirectly via
inheritance, either via the class or via the interface, it would not find it.
That necessitates that any "detectable" classes must explicitly themselves
declare their interfaces, even if it is redundant to do so. I don't like that
approach, but it's the first one that strikes me as even viable.
4) Explicit declaration. In this approach we detect nothing and rely on the
plugin developer to do everything. That is, they must provide somewhere
(either in code or a configuration file) an index of all classes they offer,
the interfaces they implement, and the file in which they live. While this
makes the implementation easy, it is a huge burden on the plugin developer and
I'm quite sure they'll forget to do so or get it wrong on a regular basis.
5) Wave a wand and let the magic ponies figure it out. I wish. :-)
Can anyone suggest a better alternative? At the moment option 3 seems like
the most viable approach, but I'm not wild about the implied performance
impact nor the potentially redundant interface definitions it would require.
--Larry Garfield