I'm revising my OOPs concepts in PHP. I have a simple php code here for practising visibility.
When i declare $name as private in Big_Animal,
1)Why it doesn't throw an error when i try to assign a different value for $name from outside the class (ie. $lion->name="King")?
2)Why it doesn't throw an error when i try to reassign $name in Lion class (ie. $this->name="John").
I'm confused coz as per my knowledge, private properties can only be accessed within the class that defines the property.
The other thing i'm not clear about is protected properties. As per my understanding, protected properties can be accessed only within the class itself and its child classes. Can it be accessed by its grandchild classes?
Thank you.
<?php
abstract class Big_Animal{
private $name="Mary";
abstract public function Greet();
public function Describe(){
return "name: ".$this->name;
}
public function __set($name,$value){
$this->name=$value;
}
public function __get($name){
return $this->name;
}
}
class Lion extends Big_Animal{
public function Greet(){
$this->name="John"; //no error for this
return " roar!";
}
public function Describe(){
return parent::Describe()." I'm a Lion!";
}
}
$lion = new Lion();
$lion->name="King"; //no error for this
echo $lion->Describe();
echo $lion->Greet();
?>
Your magic method accessors (__set and __get) are public in the base, abstract class. They are the ones writing to the private data when you access the property directly. Try commenting out the magic methods and see. Then, the output is "name: Mary I'm a Lion! roar!".
Add this as the first statement in Lion::Describe():
echo "Lion's name: " . $this->name . " ";
As you can see, the output is now: "Lion's name: King". Both $this->name="John"; and $lion->name="King"; are modifying the public property on Lion class' object. It's unfortunate that you can have both public and private properties of the same name, but you can. They are simply different variables (in different scopes).
Protected properties can be accessed from grandchildren. Most of your properties should be protected, unless you have a really strong reason to protect it (therefore using private). Public properties aren't used much in larger projects (depending on your style). I'd prefer to stick with explicit accessors. As a project progresses and becomes more complex, you'll be glad you chose to use accessors for each variable. I prefer to use a generator that will generate the scaffolding accessors for you. It saves a lot of time, cuts down on errors, and makes the creation of accessors a lot cheaper (and therefore more common).
UPDATE (a response to the comment below): 1) and 2) You're not getting errors because you're editing the Public variable in both of the instances that I listed in my 2) above. Try var_dump($lion):
object(Lion)#1 (2) {
["name":"Big_Animal":private]=>
string(4) "Mary"
["name"]=>
string(4) "John"
}
Also, if you explicitly add a private or protected member variable to the Lion class, you will get the error that you expect. I would agree with you that that's not very intuitive, but appears to be the current reality in PHP.
3) http://www.ibm.com/developerworks/opensource/library/os-php-7oohabits/ has an example of writing public accessors for private member variables (although, again, I'd recommend writing public accessors for protected member variables instead).
Because you are using magic method of __set()
__set() is run when writing data to inaccessible properties.
It is also possible to get the value by $lion->name
because you also use __get()
.
I think the answer for your question is here
Abstract methods cannot be private, because by definition they must be implemented by a derived class. If you don't want it to be public, it needs to be protected, which means that it can be seen by derived classes, but nobody else.
To elaborate on my comment... You are using a function called __set, what this does is that every time you try to set a value on an unknown property on this class, this specific function is called.
In your function, you are always changing the private field name, into the value provided. Since this class has access to it's field, this is set.
Even if you wrote $lion->foo = "bar", the name would be set to bar, due to your function __set