Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

php: Class lazy-loading?

Tags:

php

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');
like image 745
Timo Huovinen Avatar asked Jul 27 '10 11:07

Timo Huovinen


2 Answers

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.

like image 140
Gordon Avatar answered Oct 10 '22 16:10

Gordon


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.

like image 29
Morfildur Avatar answered Oct 10 '22 16:10

Morfildur