7

I have a problem here, which I have been thinking about for the past few days. In a php application to do something with a object you need to:

  1. define it
  2. run a function with it like so: (with autoloading, and a registry object)
  3. $registry->obj = new mathClass($var1,$var2); //creates object where $var1 holds the a database object, and $var2 holds the value 1 for example
  4. $registry->obj->calculate('value'); //fetches product rows and returns their total value. This way at any time in the script i can simply run the calculate function (or some other function) that I defined beforehand.

Imagine a web application that has hundreds of classes that might or might not be required for this specific page load, but can only be defined at the start of the application. The desired solution is that I simply run

$obj->calculate('price'); 

without creating the object, for example like this

mathclass::calculate('price'); 

this then autoloads the mathclass as required without having the principal overhead, the problem here is that I can no longer give the mathclass any variables at the start

($var1,$var2).

What I want is to be able to pseudo-create the object without any autoloading of the class happening, as to not add the overhead, but that the object creates itself with the variables but only when I actually need to do something with it.

I mean does php really expect me to define each and every class at the start so that I can later use them? is this Lazy-loading? Eager loading?

I might be explaining this badly so please point me in the right direction.

Edit 2015: Simple pseudocode example solution:

class Service {
    private $cb, $instance;
    public function __construct($cb){
        $this->cb = $cb;
    }
    public function __invoke() {
        if(!$this->instance){
            $this->instance = call_user_func($this->cb);
        }
        return $this->instance;
    }
}

// setup autoloading
set_include_path(__DIR__.'/vendor'. PATH_SEPARATOR .get_include_path()); // optional
spl_autoload_register(function($c){
    include preg_replace('#\\\|_(?!.+\\\)#','/',$c).'.php';
});

// simple dependency injection
$service['db'] = new Service(function(){
    return new Database('sqlite::filename.sqlite');
});
$service['config'] = function() use(&$service){
    return new Config($service['db']());
};
$service['math'] = function() use(&$service){
    return new Math($service['config']());
};

// usage
$service['math']()->calculate('price');
2
  • Can you give a better example please. Your calculate() simply adds the input arguments, so it does not illustrate why $var1 and $var2 are actually needed in the class at all. Commented Jul 27, 2010 at 11:49
  • ok I edited it, sorry for my cruddy explanation, but usually my problems spawn from the fact that I cannot define the problem properly. Commented Jul 27, 2010 at 12:07

5 Answers 5

6

Use a Dependency Injection Framework. It lets you configure your classes from config files and when you need a class you simply call it through the service builder.

Sign up to request clarification or add additional context in comments.

7 Comments

excellent read, where do you keep finding these Gordon. but how does it does not solve my problem! (im probably to blame here with my explanation)
@Yuri you find most of these at articles at planet-php.org. I am sorry, but I didnt get the remainder of your comment. Did you mean, it helped solve the problem or it didnt? If not, what part do you need help with?
thanks Gordon for the offer to help, i REALLY appreciate it. The problem i'm encountering is better described here:stackoverflow.com/questions/3346932/… the question is: how to lazy-load dependent classes.
@Yuri if you use a DI Container, you configure how classes should be instantiated. When you need need them, you simply tell the container to get a specific class and the container will then instantiate it for you. That's pretty much lazy loading. It's very similar to @dbemerlin's solution.
sorry Gordon, I really want to understand how dependency injection helps with lazy-loading and how to configure how a class should be instantiated, could you explain with a bit more detail?
|
4

You can use a lazy loading factory, i.e.

class Registry
{
  private $registeredClasses;
  private $loadedClasses;
  private $objects;

  public function RegisterClass($className, array $parameters)
  {
    // ... store class ...
  }

  private function Load($className)
  {
     // Load the class via some sort of autoloader
  }

  private function CreateInstance($className)
  {
    $parameters = $this->GetParametersFor($className);
    $this->CreateNewInstanceWithParameters($className, $parameters);
  }

  public function GetObject($className)
  {
     if (!$this->IsAvailable($className))
     {
       $this->Load($className);
       $this->CreateInstance($className);
     }
     return $this->GetInstanceOf($className);
  }
}

Later in your code you use it like this:

$registry = new Registry();
$registry->RegisterClass("math", array("var1" => $var1, "var2" => $var2));
...

$registry->GetObject("math")->calculate($x1, $x2);
...

Ofc you need to add the parts i was too lazy to add, i.e. the autoloading.

5 Comments

That's more like a ServiceLocator than a Registry, isnt it?
It's mostly a mix of both. I called it Registry because the Question seemed to use a Registry and (depending on the implementation and use) Registrys and ServiceLocators are quite related.
True, it's a registry with added capability to build services. Just felt the Service Locator pattern would be a more appropriate class name since it's not "just" a Registry.
This is the closest to a solution, I'm still thinking my problem through so bear with me.
this is essentially the same answer as Gordon's, but without a explanation of how this should be used. in the end, what I will use will be A. load main config file B. config file imports class starting options C. I call class and it loads as needed, too bad there's no 2 best answers button's.
2

if you use the autoload functionality it will only load the math class when you instantiate it, 1 option is to instantiate it when you need it, another option is to use some kind of wrapper class that will include and call the class.

3 Comments

how to predefine the way the class loads the other class before it is loaded?
You'd have to make a wrapper class specifically for that class (or some extendable class). Make it check if the class is instantiated as a member variable, if not require_once and instantiate. Note this solution sounds sub-optimal to me.
agreed, it's one of the things I also encountered.
0

What you can use is Static classes in PHP. Although this is something you might consider not doing for high-traffic websites.

Declare a class like so:

class Something
{
    private static $var = "something";

    public static function PrintVar()
    {
        echo self::$var;
    }
}

Now you can include this class and execute the code anywhere you like without initializing the object.

Like so:

Something::PrintVar();

prints

something

Good luck!

1 Comment

thanks for the luck, but static classes is not what I'm looking for because you still have to load them beforehand even if you're not going to use them.
0

Part of the reason why class objects require defining using new() is because they consume memory. Normally PHP will perform memory cleanup at the end of script if you havent done so, but usually in a constructor/destructor object-oriented environment you would want to unset() that class object to free up memory. Earlier versions of PHP (before php4) had issues with memory leaks due to these reasons.

If you want to avoid the whole initialization process you may just want to try a simple include library, such as this:

<?
if (!function_exists("calculate"))
 {
 function calculate($var1={default},$var2={default}) 
   {
   ...routine...
   }
 }
?>

And then you do not have to deal with the whole pain of defining a full class for a simple routine.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.