I have the following setup:
abstract class AParentLy{
private $a;
private $b;
function foo(){
foreach(get_class_vars(get_called_class()) as $name => $value){
echo "$name holds $value";
}
}
}
class ChildClass extends AParentLy{
protected $c='c';
protected $d='d';
}
$object = new ChildClass();
$object->foo();
What I want it to output is:
c holds c
d holds d
What it does output is:
c holds c
d holds d
a holds
b holds
The get_called_class() method correctly outputs "ChildClass"
I'm fairly new to class inheritance in PHP and from what I can gather the problem lies somewhere in the so scope. But I can't figure out how to make it work.
(The somewhat questionable solution would be to just go ahead and add a great big if($name!='a' && $name!='b' ...~ into the mix. But I'm sure there must be another, more sane and stable way to do this)
Change the visibility of Child's properties to PROTECTED.
When properties are private, its not visibles.
More info at:
http://php.net/manual/en/language.oop5.visibility.php
Had another go at the whole experiment this question was a part of.
The eventual solution (just in case anyone stumbles over this and has the same problem) I came to was to create the following method within the parent class:
function get_properties(){
foreach(get_class_vars(get_called_class()) as $name => $value){
if(!in_array($name,array_keys(get_class_vars('Parent')))){
$r[$name]=$this->$name;
}
}
return $r;
}
with this you get every parameter (and their value) of the child class without any of the parent class. You'll have to change the function a bit if you ever change the class name, but at least for me this was a workable solution.
class Parent1 {
//private $a;
//private $b;
function foo(){
foreach(get_object_vars($this) as $name => $value){
echo "$name holds $value";
}
}
}
class Child1 extends Parent1 {
protected $c='c';
protected $d='d';
}
Parent is a reserved name.
in class Parent1 you can see $a and $b so removed.
changed $c/$c to protected.
the other solution would be:
class Parent1 {
private $a;
private $b;
}
class Child1 extends Parent1 {
private $c='c';
private $d='d';
function foo(){
foreach(get_object_vars($this) as $name => $value){
echo "$name holds $value<br>";
}
}
}
putting foo in Child
EDIT
Sorry to wake an old post. I think i have a preferable solution (actually 2 solutions) for this:
The first one is to use a middle class that will create a barrier between the parent and the child:
abstract class Parent1 {
private $a;
private $b;
abstract function foo();
}
class ParentClone1 {
function foo(){
foreach(get_object_vars($this) as $name => $value){
echo "$name holds $value<br />";
}
}
}
class Child1 extends ParentClone1 {
protected $c='c';
protected $d='d';
}
$c = new Child1();
$c->foo();
// c holds c
// d holds d
The other solution is to use visibility:
If you call get_class_vars()/get_object_vars() from inside a class, it sees all the variables (including private/protected). If you run it from outside it will only see public:
function get_object_vars_global($class){
return get_object_vars($class);
}
abstract class Parent1 {
private $a;
private $b;
function foo(){
foreach(get_object_vars_global($this) as $name => $value){
echo "$name holds $value<br />";
}
}
}
class Child1 extends Parent1 {
public $c='c';
public $d='d';
}
$c = new Child1();
$c->foo();
since this will result in putting class fields as public, I'd go with the first solution.
First some basic mistakes:
Don't use a $ in a function name (here: $foo), this will result into a syntax error.
You shouldn't name a class Parent, because it is a reserved word.
Calling this would result into an error like Fatal error: Cannot use 'Parent' as class name as it is reserved
There is a good example how it works in the php manual, and there can be found this important sentence, which answers your question:
Class members declared public can be accessed everywhere. Members
declared protected can be accessed only within the class itself and by
inherited and parent classes. Members declared as private may only be
accessed by the class that defines the member.
Related
Declare a private property on a Parent class, and then redeclare that property as public or protected on a Child class.
When you create an instance of the Child class, and invoke a method that is inherited from the Parent class. The property on the Parent class is used, and not the property on the Child class.
This is not the case if the initial declaration of the property on the Parent class is public or protected.
Could someone explain to me why this is?
<?php
class P {
private $foo = 'bar';
public function e()
{
echo get_class($this);
echo "\n";
echo $this->foo;
echo "\n";
}
}
class C extends P
{
protected $foo = 'baz';
}
$parent = new P();
$child = new C();
$parent->e();
$child->e();
Output:
P
bar
C
bar
Expected Output:
P
bar
C
baz
Private properties are, as the name implies, private. This means they only be accessed from within the class. As others have mentioned in the comments, this question offers a very good in depth explanation of variable scope in PHP, so by all means go check it out, it's an interesting read.
In your specific however, you seem to be concerned about renaming/overloading variables in a child class. More specifically, we're looking at what happens when a parent and child class happen to both have a property of the same name, and the parent class has a function which return that property.
class A {
private $foo = 'bar';
public function getFoo() {
return $this->foo;
}
}
class B extends A {
protected $foo = 'something something';
}
$b = new B;
echo $b->getFoo();
In the example above, the output would be bar, and that's actually the expected behavior. Since the function getFoo is not overwritten in the child class, PHP executes the function in the parent class. When it gets there, $this refers to A and therefore prints the value of $foo as it is defined in A. It's interesting to note that in this specific example, the scope of B::foo does not matter. In fact, it could omitted entirely and the code would still run perfectly fine.
We can experiment with different function scopes and see what happens:
class A {
private $foo = 'bar';
public function getFoo() {
return $this->foo;
}
private function getCapitalizedFoo() {
return strtoupper($this->foo);
}
protected function getDoubleFoo() {
return $this->foo . $this->foo;
}
}
class B extends A {
protected $foo = 'something something';
public function getParentDoubleFoo() {
return parent::getDoubleFoo();
}
}
$b = new B;
echo $b->getFoo().PHP_EOL; // Output: bar
echo $b->getParentDoubleFoo(); // Output: barbar
echo $b->getDoubleFoo(); // Output: Uncaught Error: Call to protected method ...
echo $b->getCapitalizedFoo(); // Output: Uncaught Error: Call to private method ...
Now, the question remains: How can you access private members from a parent class?
If you have read on the subject, you probably know that, unless completely necessary, your object's properties should be private so as to encapsulate its logic and data as much as possible. Knowing that, the best to allow a child class to access and modify its parent's class properties is by creating accessors and mutators for them.
class A {
private $foo = 'foo';
private $bar = 'bar';
public function setFoo($foo) {
$this->foo = $foo;
}
public function getFoo() {
return $this->foo;
}
protected function setBar($bar) {
$this->bar = $bar;
}
protected function getBar() {
return $this->bar;
}
}
class B extends A {
public function __construct() {
parent::setFoo('more foo');
parent::setBar('more bar');
}
public function getCapitalizedFoo() {
return strtoupper(parent::getFoo());
}
public function getCapitalizedBar() {
return strtoupper(parent::getBar());
}
}
$b = new B;
echo $b->getCapitalizedFoo(); // Output: MORE FOO
echo strtoupper($b->getFoo()); // Output: MORE FOO
echo $b->getCapitalizedBar(); // Output: MORE BAR
echo strtoupper($b->getBar()); // Output: Uncaught Error: Call to protected method ...
By creating public functions to access and modify the parent class' properties you allow the child class to overwrite them and perform logic of its own without having to duplicate the variable. However this also means that any instance of B can potentially alter the values defined in A. In order to mitigate this, you can create protected function that will allow B to internally access and consume the parent class attributes, but the code that consumes B will not be able to do the same.
I know that protected attributes are available to subclasses when they are defined in a class, but are they available in parent classes? For example:
class My_Class {
// Is $name available here?
}
class My_Subclass extends My_Class {
protected $name = 'Henry';
}
Code which you write in the parent class can access that property if run in the context of a subclass. Made sense? Example:
class My_Class {
public function test() {
echo $this->name;
}
}
class My_Subclass extends My_Class {
protected $name = 'Henry';
}
$a = new My_Class;
$b = new My_Subclass;
$a->test(); // doesn't work
$b->test(); // works
Obviously (hopefully), instances of My_Class won't suddenly sprout a name property, so $a->test() won't work. Precisely because of that it's a very bad idea to make a class rely on properties which it doesn't define.
Visibility doesn't only relate to $this BTW, watch:
class My_Class {
public function test($obj) {
echo $obj->name;
}
}
class My_Subclass extends My_Class {
protected $name = 'Henry';
}
$a = new My_Class;
$a->test(new My_Subclass); // Amazeballs, it works!
A parent class has access to the property if and when it tries to access it. That doesn't mean all parent classes suddenly get a copy of that property themselves.
The parent class has no information about its subclasses, so no, $name is not available in My_Class.
Edit: As #deceze points out correctly in a comment code in My_Class can access $name, but that only works if the object was instantiated from a subclass implementing that variable. Accessing the variable in the parent class will give an Undefined Property notice.
Also I would consider that bad style and architecture, but that's my opinion ;)
Sometimes it 'can', but you really shouldn't do it
class A {
function set() {
$this->v = 'a';
}
function get() {
return $this->v;
}
}
class B extends A{
protected $v = 'b';
}
echo $b->get();//b
$b->set();
echo $b->get();//a
var_dump($b); //class B#1 (1) { protected $v => string(1) "a"}
$a = new A();
echo $a->get(); //Undefined property: A::$v
$a->set();
$a->get();//a
var_dump($a); //class A#2 (1) { public $v => string(1) "a"}
No. "protected" access modifier makes any property and method to be visible from the derived class. This is what it is used for. But parent class never knows any information about the derived class.
For more information please see this article.
When dealing with inheritance in PHP I found some lack of knowledge, mainly about constructors and private properties.
Let's take this code as example:
<?php
class Module
{
public $type;
public function __construct($type)
{
$this->type = $type;
}
}
class BModule extends Module
{
}
class CModule extends BModule
{
}
class A
{
private $module;
public function __construct(Module $module)
{
echo 'Set module for '.__CLASS__.' to '.$module->type . PHP_EOL;
echo "<br>";
$this->module = $module;
}
public function getModule()
{
echo "I (as " . __CLASS__ . ") have a module of type " . $this->module->type;
return $this->module->type;
}
}
class B extends A
{
}
$m = new Module('base-module');
$bm = new BModule('bi-module');
echo "<br>--------A---------<br>";
$a = new A($m);
echo "<br>A is of type " . $a->getModule();
echo "<br>--------B---------<br>";
$b = new B($bm);
echo "<br>B is of type " . $b->getModule();
Some questions:
shouldn't B construction call the constructor in the context of B? (and so I would expect it to fail cause it didn't inherited the private property $module)
or PHP would simply call the A constructor, using/referencing methods and properties from A? (including the private ones)
I can pass to $b either a Module or a BModule object; this is because BModule is a child of Module. Is PHP checking some inheritance chain (checking the parents) of the passed object when verifying the type hinting?
so can I pass to the constructor either an object of type Module or BModule or CModule?
And this is another example:
<?php
class a
{
private $a;
protected $a_copy;
public function __construct($a_value)
{
$this->a = $a_value;
$this->a_copy = $this->a;
}
public function getA()
{
return $this->a;
}
public function getCopyA()
{
return $this->a;
}
}
class b extends a
{
}
$a = new a('value for a');
$b = new b('value for b');
echo "<br>-----A-----<br>";
echo $a->getA()."<br>";
echo $a->getCopyA()."<br>";
echo "<br>-----B-----<br>";
echo $b->getA()." (I would expect to have no access to \$a)<br>";
echo $b->getCopyA()."<br>";
Being the property $a private, I would expect to not be able to access it or do anything with it from the class b.
It is a little bit non-sense for my actual understanding.
This is expected functionality, although B inherits all of the methods of A, they're not called in the context of B, they're called in the context of A. So the A constructor is called. This means that functions defined in A can access A's properties even when the object is extended. Methods defined in A cannot access properties of B however, which appears to be your understanding.
So to shortly answer your questions:
No, functions are always called in the context of where they are defined, not from where they are called.
PHP will check all the way down the inheritance chain to see if it's correct. Any child of a Class can be assumed to have the same functions. So if B extends A, You can use either B or A as a parameter when it's type-hinted to A, but only use B if it's type-hinted to B
Ad 1: No, the context of the called method is where the method (in this case the constructor) is declared. If the context would be class B then anyone could break your class simply by extending it.
Take a look at this example:
class A
{
private $module;
public function __construct(Module $module)
{
echo 'Set module for '.__CLASS__.' to '.$module->type . PHP_EOL;
echo "<br>";
$this->module = $module;
}
}
class B extends A
{
public function __construct()
{
parent::__construct(new Module()); // call the parent (which is A)
}
}
This illustrates how the scope of A::__construct() is actually the A class.
Ad 2: Yes, each object that is an instance of sub class can be used in place of the super class. This is why you should write your classes so that they can be substituted when the static typing requires a super class. For more information on this subject see the Liskov substitution principle.
As for the last example: again there is no code in sub class that would operate on private members of the super class. All code operates from within the super class context. So there is no problem here.
There would be a problem if you tried to overload methods of super class and use its private members like this:
class b extends a
{
public function getA()
{
return $this->a . "_suffix"; // error
}
}
In such case you must depend on the implementation of getA() method in the super class:
class b extends a
{
public function getA()
{
return parent::getA() . "_suffix"; // ok, we are depending on the super class implementation
}
}
class A {
protected $a;
// SOME CODE
}
class B extends A {
// SOME CODE
}
How can i edit the protected value of the variable $a inside the B class ?
I'm trying to use parent::$a = "Some Value" but doesn't work.
protected instance properties, those which where not declared using static, can be accessed in subclasses using $this :
class A {
protected $a;
// SOME CODE
}
class B extends A {
// SOME CODE
public function edit($val) {
$this->$a = $val;
echo "a is now {$this->a}\n";
}
}
call:
$b = new B();
$b->edit('foo'); // a is now foo
Refer to the manual, especially the examples.
class B extends A {
public function foo($val)
{
$this->a = $val;
}
}
quite simple :)
Remember that
Members declared protected can be accessed only within the class
itself and by inherited and parent classes
from php manual
I stumbled across a very wired error in php:
class A {
public $var = "test";
public function __construct() {
$this->var = "test2";
$b = new B;
$b->method();
}
}
class B extends A {
public function method() {
$c = new C;
$c->method();
}
}
class C extends B {
public function method() {
echo $this->var;
}
}
$a = new A;
I get the output "test", but I do not know why, cause the variable var should be overwritten in Class A.
If I output $var in Class A it says "test2", if I output it in Class B it says "test"…
The code on your question won't work because of the circular references (eg: $b = new B in A's constructor), which will cause PHP to run out of memory. You really shouldn't be instantiating children classes in a parent class.
That being said, by what you are describing, it sounds like you are defining a constructor in B, which overrides the parent constructor. In PHP children classes don't implicitly call the parent constructor (unlike in languages like Java).
So, it just inherits the original value for $var (ie: "test"), which is never changed. If you are overriding __construct() in B, you'll have to explicitly call the parent constructor, like:
class B extends A {
public function __construct() {
parent::__construct();
}
}
And that should give you "test2" when you do something like:
$b = new B;
echo $b->var;
See this demo: http://ideone.com/Q9Bp8
What is the best way to have 3 classes, where the third and second can access variables of the first class?
The answer is, it depends on what you are doing. It sounds like you are not understanding how OOP works, which is a bigger problem. In general you only use inheritance when the children classes could reuse code from the parent class, and/or there is some sort of is-a or has-a relationship.
If your classes don't fit this model, just make the 3 classes independent, and hold a reference to the first class in your other classes. For example:
class A {
public $n = 0;
public function change($n) {
$this->n = $n;
}
}
class B {
public function __construct($a) {
$this->my_a = $a;
}
public function get() {
return $this->my_a->n;
}
}
$a = new A();
$b = new B($a):
echo $b->get(); // 0
$a->change(10);
echo $b->get(); // 10
See this demo: http://codepad.org/xL1Dzs0W