views:

446

answers:

4

Hi, here is what my code looks like

i have two forms:

class Form_1 extends Form_Abstract {

    public $iId = 1;

}
class Form_2 extends Form_1 {

    public $iId = 2;

}

i expect the code behave like this:

$oForm = new Form_2;
echo $oForm->getId(); // it returns '2'
echo $oForm->getParentId(); // i expect it returns '1'

here is my Form_Abstract class:

class Form_Abstract {

    public $iId = 0;

    public function getId() {
        return $this->iId;
    }

/**
this method will be called from a child instance
*/
    public function getParentId() {
        return parent::$iId;
    }
}

but it throws a Fatal Error:

Fatal error: Cannot access parent:: when current class scope has no parent

please help me with the method getParentId()

PS: i know the reason of what happens, i am seeking for the solution.

+1  A: 

I don't think it is even possible to access the "parent"'s version of $iId : you don't actually re-define it in the child class : you only chance the value that was defined in the parent's class.

To makes things very simple : when you declare the Form_2 class that extends Form_1, it takes all the properties and methods of Form_2, and put them in Form_1, overriding what was already existing there.
There is no longer "two distinct classes" : there is only one resulting object, that's both Form_1 and Form_2 at the same time.


And here's an example that kind of -- I hope -- will help understand what I mean :

class Form_Abstract {}
class Form_1 extends Form_Abstract {
    public $iId = 1;
    public function methodInParent() {
        var_dump($this);
    }
}
class Form_2 extends Form_1 {
    public $iId = 2;
    public function tryingToGetParentProperty() {
        var_dump(parent::$iId); 
    }
}

$form2 = new Form_2();
$form2->methodInParent();
$form2->tryingToGetParentProperty();


Using this portion of code, the call to $form2->methodInParent() will get you :

object(Form_2)#1 (1) {
  ["iId"]=>
  int(2)
}

i.e. even if calling/executing a method that's defined in the parent's class, the $iId property is still the value defined in the child class : there is one, and only one, version of that property !


And the call to $form2->tryingToGetParentProperty() will get you :

Fatal error: Access to undeclared static property: Form_1::$iId

As there is no static property called $iId in Form_1.


I suppose a solution to avoid that situation would be to declare $iId as static -- but note that it would change the meaning of your code, and the way it behaves !

i.e. the static variable will be shared accross all instances of the class -- which is probably not what you want ^^

Pascal MARTIN
thanks for the answer BUT i don't accept it, because within a class one could have access to parent::someMethod(), so i think it could be a way to have access to parent::$someVar
takpar
thanks, please note parent::$someVar does not mean static variable. it is the PHP syntax.
takpar
You're welcome :-) ;;; OK, but the Fatal error I got said *"Access to undeclared static property"* -- not much I can do about that ^^
Pascal MARTIN
+1  A: 

The error is because you are calling the parent of a class that does not have a parent (it does not extend an existing class).

Macmade
i know, but my object is Form_2 not the Form_Abstract
takpar
Sure, but parent is not resolved at run-time, so you're still in the abstract context.
Macmade
ok, maybe. so what's your solution for the case?
takpar
Meaning you have to declare the getParentId in the Form2 class. Then you'll be able to use parent::
Macmade
i don't want to copy/past the same code in every single parent class; it should have another solution, shouldn't?
takpar
Traits, but that's PHP6...
Macmade
+1  A: 

You have to use Reflection Api to access the parent class' property default value. Substitute getParentId, in Form_Abstract, with this, and all works fine:

public function getParentId() {
    $refclass = new ReflectionClass($this);
    $refparent = $refclass->getParentClass();
    $def_props = $refparent->getDefaultProperties();

    return $def_props['iId'];
}

Clearly you cannot call getParentId() in the root class, so it's better to check if a parent class exists.

UDATE:

You can do the same with classes/objects functions:

public function getParentId() {
    $def_values = get_class_vars(get_parent_class($this));
    return $def_values['iId'];
}
Nicolò Martini
it worked. thanks, what's your opinon about the performance of the code?
takpar
I don't know precisely. However, using classes/objects functions should be faster than reflection. In general, reflection is not fast, so use reflection carefully and only if there is no other "right" way to achieve what you want..It depends, however, how you use it.
Nicolò Martini
I've done some rough benchmarks, and performance seems not to be an issue in this simple case. Making million of calls, I get that, for example, that my second version of getparentid is as fast as getId(). The version with reflection is only a 10% slower
Nicolò Martini
Is there some reason because you don't accept the answer?
Nicolò Martini
A: 

i made it work by this:

public function getParentId() {
    $sClass = get_parent_class($this);
    $tmp = new $sClass;
    return $tmp->iId;
}

BUT is it a standard solution, does it have any performance issue?

takpar
That's a very bad idea, as you create a new instance just to get a property... What if your class, when instanciated, connects to a database, for instance?
Macmade
For performance, I have done some rough benchmark tests with some millions calls to getparentid, and your version and my versions (with reflection api and classes objects functions) are equally fast. So I don't think performance is an issue in this simple case.I don't like too instantiating a class only to have the default value, it's not a beautiful code.
Nicolò Martini
Macmade is right, in this way you depends on what constructor does, every time you call the constructor, so it can't be considered a "standard" solution
Nicolò Martini