Lazy loading class methods in PHP
I have a class with a few rather large methods. In it's basic and most common state most of the functionality is not required though, so I was wondering if there is a way of lazy loading just parts of the class. The methods need to be able to access private/protected members so it would be ideal if the methods were native to the class, however in looking for other solutions I came across this which discusses using private members in callbacks which would be a workable solution (I'd use separate classes开发者_如何学JAVA that contain a function that calls the callback and lazy load that class). That was 2009 though and whether this functionality has been removed in later versions of PHP i don't know, but it doesn't seem to be working here with 5.3.5
Is there a way of doing this, or do you have any suggestions for other patterns I should be looking at?
Thanks.
Wow! Thanks for all the answers. I think the point a number of you make regarding this being a probable premature optimization, or worse, not an optimization at all is very valid and I will be doing profiling to check that any solution I settle on is actually helping not hurting. ... Now to read and digest all your thoughts properly. Thanks again.
As of PHP 5.4 you can (re-)bind Anonymous Functions and Closures:
<?php
class Foo
{
private $bar = 1;
}
$getBar = function() { return $this->bar; };
$foo = new Foo;
$foo->getBar = $getBar->bindTo($foo, $foo);
echo call_user_func($foo->getBar); // prints "1"
See https://wiki.php.net/rfc/closures/object-extension for a discussion of the Closure implementation and potential gotchas.
In general, if you find your class has many long methods, try to break them down into smaller chunks. Describe what the methods do in plain english. For every "and" make a new method and move the code there.
Also have a look at the various properties in that class. If some of them go conceptually together, consider making them into an object of their own. Move any methods accessing those properties to the new object for cohesion.
I also somewhat question your motives for wanting to "lazy load" methods into the class. There is no performance complication for having them there, even when you dont use them. If this is for a performance optimization, you are probably approaching it from the wrong end.
Another option would be to use Traits or, even simpler, Composition.
Callback solutions looks very ugly.
You can use Composition pattern and autoloading:
class MyClass
{
protected $logger;
function SomeFunction($arg)
{
$this->Logger()->write($arg);
}
function Logger()
{
if (empty($this->logger)) $this->Logger = new Logger(); //lazy initialization of method
return $this->logger;
}
}
But, I have to say, that all of it is just micro-optimizations, don't waste your time. Time of creating new object and autoloading another file (it's using disk), will be bigger, than simple initialization of the object with "large" methods.
I'm not aware of a (performance effective) way to load only parts of a class.
I think you will need to separate the class's methods into sub-classes, and use autoloading to load them.
Once you have done that, you could think about doing something like this:
class myMainClass
{
function bigFatMethod($argument, $argument2)
{
return mySubClass::bigFatMethod($this, $argument, $argument2);
// (pass $this if necessary)
}
}
This would keep bigFatMethod()
callable inside myMainClass
, but internally, because you are using autoloading, the necessary code gets loaded only when bigFatMethod()
is actually called.
Obviously, you would need to rewrite bigFatMethod()
so it can be called statically and instead of accessing $this
, you would have to make it access the object passed in its first parameter (to which you pass $this
in the parent class).
I have never done this myself - I would tend to split the class into sub-classes, and address them separately - but I can't see any huge downside to doing things this way.
If you wanted, you could even abstract bigFatMethod()
using a __call()
magic method, which would look up which subclass it has to load, executes the method, and returns the result.
Your class is probably trying to do too much. I would suggest trying to separate it into separate services. You could then use a Dependency Injection Container (eg. Pimple) to lazily load only those services that are actually used.
I'd advise against abusing inheritance. Instead you should favor composition over inheritance. This makes your design cleaner and your code base more maintainable.
<?php
class BigClass
{
public function lazy()
{
return include 'lazy.func.php';
}
}
That should work and satisfy your requirements. I haven't really thought much about any side effects it may have.
to load just part of class - it's simply impossible. When you do make new instance of class, it does initialize whole class and therefore there isn't lazy loading of function you only need
You could write a base class which encompasses all the basic functionality, and then use inheritance to incrementally add more specialized features, and only use the inheriting classes where needed.
PHP Object Inheritance.
You can use the magic class method __get
to load properties dynamically or the magic method __call
for delegating to other classes/methods. These are awesome for when you need to initialise properties or methods, etc. only when they are accessed.
Strangely enough i've recently blogged about this very thing because i needed it in a huge class in one of my projects. It's a little involved but i've tried to explain it simply. It may give you a few pointers.
http://www.kalekold.net/index.php?post=16
http://php.net/manual/en/language.oop5.magic.php
精彩评论