3

I am searching for the best way to call a class method from another class, without having to use Global to fetch the class instance, cus as i understand now that "Global is evil" !

Here is some code to explain it more:

class Assets{
 public $assets = array();
 public function add($asset){
  $this->assets[] = $asset;
 }
}

Now i wanna call the Assets/call method from with in here ..

$assets = new Assets;
class Form{
 public function __construct(){
  global $assets;
  $assets->add('Form');
 }
}

Is using Global in such scenario is THAT bad ? If so, what other way is considered to be the best ?

PS: I need to work with only one instance of the class; means that i don't want to create a new instance inside of the second class.

vdegenne
  • 12,272
  • 14
  • 80
  • 106
Dewan159
  • 2,984
  • 7
  • 39
  • 43
  • Using globals is bad in every scenario. Even when using purely procedural code. – tereško May 28 '13 at 17:24
  • Great. Now, about Dependance – Dewan159 May 28 '13 at 17:44
  • what do you mean with "dependence"? – tereško May 28 '13 at 17:46
  • Clicked enter by mistake. Dependency injection seems great, but i am building a small framework (for me and my friends) and i don't really like the idea of passing the $assets instance every time i create an instance of Form. Isn't there any language-provided/standard solution for this. Sometime i just think: PHP Sucks ! – Dewan159 May 28 '13 at 17:59
  • 1
    Dependency injection is the "language-provided & standard" solution. You are just really bad at OOP. Maybe [this list](http://stackoverflow.com/a/16356866/727208) helps a bit. – tereško May 28 '13 at 18:04
  • Thanks for the links. But, again, it's not right to pass the $assets instance every single time a new instance of Form is created. Talking generally, this is an issue PHP should focus more on, it's somthing everyone face. – Dewan159 May 28 '13 at 18:12
  • If an object require an instance of `Assets` then you pass it that instance. Just because you inject an object in another object it **does not get duplicated**. Instead the same instance gets shared between all objects. – tereško May 28 '13 at 18:16
  • You need to come down. No one said anything about duplication, i am trying to find the best way there is to use a method of a class instance in another without using Global. If you are in peace with a solution for this problem then good luck for you, but i am trying to found out more. – Dewan159 May 28 '13 at 18:25
  • 1
    @Dewan159 I think you're looking at this from the wrong-end in, which is common when one first starts diving deeply into OOP with PHP when coming from a procedural approach and talking about "practice". Do you understand *why* `global` is "bad"? Try to focus on that first. Develop your knowledge of the approach before criticizing the language flatly and you will be better received when looking for help. The advantages will be more readily apparent once you're a little more familiar with why injection is important (it's used all over the Java world, for example.) – DeaconDesperado May 28 '13 at 18:38
  • Criticizing the language isn't the end of the world, we all do it, and sure we all did it, when we don't really get the reason why things are a certain way. So, no harm done, i have been using PHP for 7 years now! - I LOVE IT. About why Global is bad, i am really convinced of the reasons and that's why i am here asking for the BEST way to do it. – Dewan159 May 28 '13 at 19:54
  • Now, it's a simple question, we shouldn't make such a fuss about it; Isn't there a better way than passing the object instance everytime to the other object. I really don't like to ask whoever will use my Class to pass the $assets var everytime an instance is made of the Form class, YES IT'S NEEDED, but it's so un-cool. If there is, share me it, else ... – Dewan159 May 28 '13 at 19:55
  • 1
    @Dewan159 I wasn't making a qualitative statement about your question :) I was simply noting that the snippets you provided evince wrong-headed goals in design. What you're identifying as a weakness is an almost universally accepted coding practice across many languages besides PHP (Python, many JVM langs, Ruby all have some variant of DI available to you.) It's a backbone of OOP. Your predilection to look at this pattern as problem probably means you have some wrong-headed goals in the code your writing. You mention writing a framework... Perhaps we could continue in chat? – DeaconDesperado May 28 '13 at 20:24
  • I will love to, where is that ? – Dewan159 May 28 '13 at 20:31
  • http://chat.stackoverflow.com/rooms/30784/di-in-php – DeaconDesperado May 28 '13 at 20:35
  • 1
    @Dewan159: if you don't always want people to have to pass the assets object, then simply don't force them to use the constructor. Provide a factory class that you pass the Assets instance once, and let the factory always construct your form classes. – Evert May 28 '13 at 21:09
  • Great idea, will do ... – Dewan159 May 28 '13 at 21:19
  • Done it, and now everything works just fine, thanks. – Dewan159 May 28 '13 at 21:24

6 Answers6

7

If you only want to have 1 instance of assets, you need to use some form of dependency injection.

The most obvious in this case, is passing it through the constructor:

class Form{
 public function __construct(Assets $assets){
  $assets->add('Form');
 }
}

It is indeed a bad idea to use globals.

Evert
  • 93,428
  • 18
  • 118
  • 189
  • 2
    Yea it's a great practice. There's a few other ways that are also considered 'good', but `global`, `static` or singletons are all considered kinda bad in acedemic OOP design. Avoid those while you can. – Evert May 28 '13 at 16:56
  • @Evert , while this is a lot better then ever other "answer", you really should avoid performing any computation in the constructors. It tends to turn testing in a nightmare. – tereško May 28 '13 at 18:08
  • @Evert Could you please name me those other few ways. Thanks. – Dewan159 May 28 '13 at 20:17
  • Just research dependency injection. – Evert May 28 '13 at 20:23
2

You could also make Assets a singleton if it makes sense in your context:

class Assets
{
    /////////////////////
    // Your stuff      //
    /////////////////////

    private $assets = array();
    public function add($asset)
    {
        $this->assets[] = $asset;
    }

    /////////////////////
    // Singleton stuff //
    /////////////////////

    static private $instance = null;
    static public function getInstance()
    {
        if(self::$instance == null)
            self::$instance = new self;
        return self::$instance;
    }

    // To restrict construction of this class outside of this class definition
    private function __construct() {}

    // To restrict cloning of this class outside of this class definition
    private function __clone() {}
}

In your other code, you'd use the singleton like this:

class Form
{
    public function __construct()
    {
        Assets::getInstance()->add('Form');
    }
}
Hauke P.
  • 2,695
  • 1
  • 20
  • 43
  • That's cool, but isn't Singleton considered to be a bad practice ? – Dewan159 May 28 '13 at 17:22
  • I think it always depends on what you're trying to do. When taking into account that some pretty large projects (like for example the open source 3D engine [Ogre3D](http://www.ogre3d.org/)) use singletons a lot, I would not say that Singletons are generally a bad practice. – Hauke P. May 28 '13 at 17:43
1

You can try this,

class Form{
 public function __construct(Assets $assets){
  //$assets = new Assets;
  $assets->add('Form');
 }
}
Nisam
  • 2,275
  • 21
  • 32
  • Sure, but i need to work with just one instance of the class. Thanks. – Dewan159 May 28 '13 at 16:50
  • 2
    You can use constructor injection like this, public function __construct(Assets $assets); – Nisam May 28 '13 at 16:51
  • 1
    Injection is definitly the way to go. Hiding the construction inside of another constructor is bad practice and will only make things harder on yourself in the long run (for example, you find a use case where the same Assets instance should be used from a different instance of Form) – smassey May 28 '13 at 17:06
1

I'm not answering this question directly with the provided elements, I just show a way of sharing data between objects without using globals.

class A {

    private $v;

    function __construct ($value)
    {
        $this->v = $value;
    }

    function set_v ($value)
    {
        $this->v = $value;
    }

    function print_v ()
    {
        echo $this->v;
    }

}

.

class B {

    private $a;

    function __construct (&$tha)
    {
        if ($tha) {
          $this->a = $tha;
        }
    }

    function print_a ()
    {
        echo $this->a->print_v();
    }
}

.

$mya = new A(12);
$mya->print_v(); // echo 12
$myb = new B($mya);
$mya->set_v(13); 
$myb->print_a(); // echo 13

You can keep a trace of one object in another and respecting the encapsulation of the objects. If an instance of A however get removed, an instance of B could get unreliable. You should then make sure that the data is always reliable within the object storing it (e.g. making a method that verify if the internal data is alright).

Also as noticed by tereško, arguments explicitly awaiting for a reference should be avoided. In fact since PHP 5.0 a new feature has been introduced to the language called the type hinting. That prevents your program to have undesired memory behavior, plus the fact that any wrong matching can be monitored at the compilation level (more details at Type Hinting (PHP official documentation))

Hence

function __construct (&$tha) ...

Must be redefined as

function __construct (A $tha) ..

in the example.

vdegenne
  • 12,272
  • 14
  • 80
  • 106
  • 1
    Since release of php 5.0 (released some time around 2004th), you should not be passing object around by reference. It messed with refcount and causes leaks. – tereško May 28 '13 at 17:23
1

Dependency injection is the way to go. And i will be using this Factory mechanize to pass the object instance to the other object. As in (Just a sample, didn't test it):

class UI{

public $somthing = 'Cool';

public function newComponent($name){
    return new $name($this);
}

}

$ui = new UI;
class Form{
    public $ui;

    public function __construct(UI $ui){
        $this->ui = $ui;
    }

    public function say(){
        print $this->ui->somthing;
    }
}

$form = $ui->newComponent('Form');

$form->say(); // print 'Cool'.
Dewan159
  • 2,984
  • 7
  • 39
  • 43
-1

What about

<?php
class Assets
{
    protected static $assets = array();

    protected function add($el)
    {
        self::$assets[] = $el;
    }

    public function getAssets()
    {
        return self::$assets;
    }
}

class Form extends Assets
{
    public function __construct()
    {
        parent::add('Form');
    }
}

class Css extends Assets
{
    public function __construct()
    {
        parent::add('Style');
    }
}

$assets = new Assets;
new Form;
new Css;
var_dump($assets->getAssets()); // Prints: array(2) { [0]=> string(4) "Form" [1]=> string(5) "Style" }
eyecatchUp
  • 10,032
  • 4
  • 55
  • 65