views:

65

answers:

5

I am trying to make a script in which different classes (e.g. Database, Utilities, Config) are all used to form one central Main class. I have tried extending a chain of them:

Main -> Utilities -> Database -> Configuration

But how can I set the different parts so that they can be called like this:

<?php

     $this->db->select("WAFFLES");
     echo($this->config->app_path);

?>
+1  A: 

You need to declare each new object as variable in your Main Class like:

class Main{

    private $db = NULL;
    private $config = NULL;

    $this->db = new Database;
    $this->config = new Config;

}

etc.

While i'm not a professional coder i'll considering a better approach than this. This kind of object-handling can cause a bloated main class and in the worst case you may face some performance issues.

fabrik
A: 

Here is the sample prototype:

include 'db.php'; // include db class
include 'config.php'; // include config class

class main{
  public $db = NULL;
  public $config = NULL;

  function __construct() {
    $this->db = new db;
    $this->config = new config;
  }

}
Sarfraz
+3  A: 

You could create a global class that does you basic initializing

class Base {

 $var1, var2;

 public function __construct() {
   $this->var1 = new DB();
   $this->var2 = new Config();
   ....
 }
}

Then your classes can extend the base class and have access to the data

class Foo extends Base {

  public function bar() {
    $this->var1->someOpertaion();
  }
}
DrColossos
A: 

Creating a composite object with instances of everything that might be needed during code execution up front is a complete waste of resources. You want to create instances only when needed. One way to achieve this would be to add a magic __get method to the class:

public function __get($name) {
    // if self::$instances (or main) contains instance of $name, return instance
    // else if class_exists $name, create, store and return instance
    // else throw exception
}

But even then, chances are you are creating a God Object and magic methods are somewhat slower than regular accessors. If you need to create instances this way, have a look at the Symfony Dependency Injection Container or implement a Registry.

Gordon
+1  A: 

1) use __autoload or spl_autoload_register to load classes

2) use magic methods, to call function when getting unknown property. Following examples demonstrates how to use __get and dynamicaly initialize object only when you use them.

//use __autoload to load db and config class when they are called.
class db{
  function lol(){
    echo 'Hello from db->lol() <br />';
  }
}
class config{
  function lol(){
    echo 'Hello from config->lol() <br />';
  }
}


//Manager class to use with classes where you want to access other object trough $this
class Manager{
  private $_instances=array();
  function __get($name){
    //if instance does not exists, create one
    if (!isset($this->_instances[$name])){
      $this->_instances[$name]=new $name();
    }

    //return instance
    return $this->_instances[$name];    
  }

}

class Some extends Manager{
  function f1(){
    $this->db->lol();
    $this->config->lol();
  }
}

$some=new Some();
$some->f1(); //echoes 'Hello from db->lol()' and 'Hello from config->lol()'

But for accessing global class instances I prefer using following method: Use singleton pattern to access global class trough GloballClass::i() and if global class is not defined use autoload to load that class.

class db extends mysqli{
  private static $_i;
  //Access to singleton instance
  public static function i()    {       
    return (self::$_i instanceof self)?self::$_i:self::$_i = new self();
  }

  //class functions
  function q($q){
    echo 'Hello from db->q()';
  } 
}

class config{
  private static $_i;
  //Access to singleton instance
  public static function i()    {       
    return (self::$_i instanceof self)?self::$_i:self::$_i = new self();
  }

  //class functions
  function somefunction(){
    echo 'Hello from config->somefunction()';
  } 
}


db::i()->q('SELECT * FROM users');
config::i()->somefunction();

Following is solution inspired by Gordons comment: It uses GlobalClassFactory class to define only one instance of global classes.

class db{
  function lol(){
    echo 'Hello from db->lol() <br />';
  }
}
class config{
  function lol(){
    echo 'Hello from config->lol() <br />';
  }
}

class GlobalClassFactory{
  private static $_classes=array();
  public static function getInstance($name){
    if (!isset(self::$_classes[$name])){
      self::$_classes[$name]=new $name();
    }
    return self::$_classes[$name];
  }
}

class Base{
  function __get($name){
    return GlobalClassFactory::getInstance($name);
  }
}

class Some extends Base{
  function f1(){
    $this->db->lol();
    $this->config->lol();
  }
}

$some=new Some();
$some->f1();
codez
Subclassing from a general Base class is almost always a bad idea. You are creating a relationship that says `Some` *is-a* `Manager`, when it really isnt. Also, if you are storing the instances as object instances, any class extending Manager will create separate instances just for itself, which in the case of db and config is unlikely to be wanted. Singletons can mitigate this, but since Singletons cannot be instantiated with `new`, the code in `__get` will no longer work. And Singletons are generally a bad idea anyway.
Gordon
You are right, Gordon, so I created solution where only one instance of every global class is created.But why Singletons are bad idea?
codez
You just exchanged the problem :) How do you create non-Singleton now? Also, you are still creating an *is-a* relationship and now you also coupled the Base class to the factory by hardcoding it into the `__get` method, effectively reducing the Base class to a magic getter. I mean, sure it works, but it's poor design IMO. Try to avoid *global* things.
Gordon
Singletons are a bad idea because they are more difficult to test and add tight couplings and dependencies on the global scope. This makes your application less maintainable.
Gordon