0

What is the best way to share a class instance between two inherited classes?

// Let's create a main class that hold common methods and connects to the database
class MyApp {
    public $DB;
    function __construct () {
        // Connect to the database here
        $this->DB = new Database();
    }
}

// This will hold methods specific to the user management
class Users extends MyApp {
    function __construct() {
        parent::__construct();
    }
}

// This will hold methods specific for fetching news articles
class News extends MyApp {
    function __construct() {
        parent::__construct();
    }
}

Now, in my application I have page that displays both Users and News on the same page. So I do something like this:

// Instantiate the users class (this will connect to the db)
$users = new Users();

// Instantiate the news calls (PROBLEM! This will create a new db connection instead of re-using the previous one)
$news = new News();

I can keep the DB instance separate and then pass it in. Something like this:

$db = new Database();
$users = new Users($db);
$news = new News($db);

But now I'm passing this stupid $db variable everywhere -- which feels messy and error-prone.

Is there a better way to structure this? The ideal solution would look something like this:

// Instantiate a global class which will connect the DB once
$App = new MyApp();

// Reference sub-classes which all share the parent class variables.
// Unfortunately PHP doesn't seem to have sub-class support :(
$App->Users->someMethod();
$App->News->someMethod();
Ev Haus
  • 1,578
  • 1
  • 11
  • 23
  • "I can keep the DB instance separate and then pass it in". That is the correct way to do it, especially from a TDD point of view. – vascowhite Mar 29 '15 at 21:40
  • possible duplicate of [Sharing objects between PHP classes](http://stackoverflow.com/questions/819334/sharing-objects-between-php-classes) – vascowhite Mar 29 '15 at 22:52

1 Answers1

1

If you want to achieve what you want, you could do it this way:

class Database
{
    public function __construct()
    {
        echo "connecting to database...<br />";
    }
}

class MyApp
{
    public $Users;
    public $News;

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

    public function initialize(array $classes)
    {
        foreach ($classes as $class) {
            $this->$class = new $class($this->db);
        }
    }
}

class Users
{
    public function __construct(Database $db)
    {
        echo "users have now db connection<br />";
    }
    public function someMethod()
    {
        echo "hello from users<br />";
    }
}

class News
{
    public function __construct(Database $db)
    {
        echo "news have now db connection<br />";
    }
    public function someMethod()
    {
        echo "hello from news<br />";
    }
}

$App = new MyApp();
$App->initialize(['Users', 'News']);
// Reference sub-classes which all share the parent class variables.
// Unfortunately PHP doesn't seem to have sub-class support :(
$App->Users->someMethod();
$App->News->someMethod();

Of course it's just a sample, you should add more checking for this, output for running above code would be:

connecting to database...
users have now db connection
news have now db connection
hello from users
hello from news

However much better would be injection to MyApp constructor Database connection instead of creating new instance in constructor.

EDIT

As alternative you could pass Database to constructor and use of magic __get function so you could now use:

class Database
{
    public function __construct()
    {
        echo "connecting to database...<br />";
    }
}

class MyApp
{
    protected $data;

    protected $allowed = ['Users','News'];

    public function __construct(Database $db)
    {
        $this->db = $db;
    }

    public function __get($name) {
        if (in_array($name, $this->allowed)) {
            if (!isset($this->data[$name])) {
                $this->data[$name] = new $name($this->db);
            }
            return $this->data[$name];
        }
        throw new Exception('No '.$name.' property!');
    }
}

class Users
{
    public function __construct(Database $db)
    {
        echo "users have now db connection<br />";
    }
    public function someMethod()
    {
        echo "hello from users<br />";
    }
}

class News
{
    public function __construct(Database $db)
    {
        echo "news have now db connection<br />";
    }
    public function someMethod()
    {
        echo "hello from news<br />";
    }
}

$App = new MyApp(new Database());
$App->Users->someMethod();
$App->News->someMethod();

and the output for this would be:

connecting to database...
users have now db connection
hello from users
news have now db connection
hello from news
Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291