I have recently started OOP in PHP and I am on visibility concept (public, protected, private). Here I'm confused about protected visibility. Protected members can only be accessible within the declaring clas or a subclass or child class. I have this example on PHP's site:
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
class MyClass2 extends MyClass
{
// We can redeclare the public and protected method, but not private
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj2->public; // Works
echo $obj2->protected; // Fatal Error
echo $obj2->private; // Undefined
$obj2->printHello(); // Shows Public, Protected2, Undefined
In this code in the third last line (echo $obj2->protected; // Fatal Error) it gives fatal error. But isn't protected variable inherited into child class which is MyClass2 in this case? So why this fatal error? Can someone please enlighten me about this?
Protected members can only be accessible within the declaring class or
a subclass or child class.
You must pay attention to the within.
You're getting the fatal error because you're accessing $protected from outside both MyClass2 and MyClass.
Within means that you can access it from MyClass2 like you're doing on MyClass2::printHello() and/or MyClass::printHello() methods.
Related
I am now really confused about the following:
class A {
public function setPropertyValue($prop, $val) {
$this->$prop = $val;
}
}
class B extends A {
private $foo;
}
$obj = new B();
$obj->setPropertyValue("foo", "whatever"); // Fatal error: Uncaught Error: Cannot access private property B::$foo
Why and what is the point of not being able to access the private property since it is a property of B object that was instantiated and that the method is called on?
The error would make sense if it were the other way around: $foo being a private property of A and therefore not visible through inheritance to B.
I just cannot figure out why would this behavior be useful.
Private is only for the class instance using it. If you want to make it available for child or parent classes, but keep it unavailabe for public, make it protected.
class A
{
public function setPropertyValue($prop, $val)
{
$this->$prop = $val;
}
}
class B extends A
{
protected $foo;
}
$obj = new B();
$obj->setPropertyValue("foo", "whatever");
Per OOP principles:
The visibility of a property, a method or a constant can be defined by prefixing the declaration with the keywords public, protected or private.
Class members declared public can be accessed everywhere.
Members declared protected can be accessed only within the class itself and by inheriting and parent classes.
Members declared as private may only be accessed by the class that defines the member.
private - the property or method can ONLY be accessed within the class
You can't access or set a private property from anywhere else except the class it is created.
The only way of working with a private property in a class is by modyfying it in a public method of the same class.
I am trying to write a PHP class in which I change the visibility of a few methods from protected to public. I believe I remember you can do this in C++, but I did a few searches and I am not coming up with anything for that in PHP. Does anyone know if this is even possible in PHP?
For example, suppose this class:
class ABC {
protected function foo() {
// Do something
}
}
class DEG extends ABC {
// can I make foo public now?
}
You can change the visibility of members when deriving from a base class like this:
class Base
{
protected function foo() {}
}
class Derived extends Base
{
public function foo() { return parent::foo(); }
}
You can also do the same with properties (redefine a protected property as public).
However, be aware that if the base property is private then you will not actually increase its accessibility but rather declare a new property with the same name. This is not an issue with functions, as if you tried to call a private base method you would immediately get a runtime error.
You can overwrite a method in a derived class to highten it´s visibility (e.g. protected->public). Make the new function return it´s parent.
You cannot do so to limit it´s visibility (e.g. public->protected), but you can implement a method that checks the backtrace for the caller and thwors an exception if it´s a foreign class.
You can always use the reflection API to do all kinds of changes to the visibility.
Yes, it can be done. Quoting from PHP manual..
The visibility of a property or method can be defined by prefixing the
declaration with the keywords public, protected or private. 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.
And the example from there as well..
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private
Edit : Yes, you can change visibility of public and protected members. Another example from PHP manual..
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// We can redeclare the public and protected method, but not private
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj2->public; // Works
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error
$obj2->printHello(); // Shows Public, Protected2, Undefined
?>
I'd like to implement the following using a private constructor.
The problem is that get_class() returns ParentBase; eventhough; get_called_class() returns ChildClass.
How can I have __construct() be called from the calling class context instead of the base class context?
There will be many child classes, so I only want one shared factory method, and I also want to make sure a child cannot be extended (so that it cannot be created with the new keyword).
Seems like there should be a way of making ChildClass::createObject() work with a private ChildClass constructor and a public ParentBase factory method.
<?php
class ParentBase
{
public static function createObject()
{
echo get_class() . "<br/>"; // prints ParentBase
echo get_called_class() . "<br/>"; // prints ChildClass
return new static();
}
}
class ChildClass extends ParentBase
{
private $greeting = "bye";
private function __construct()
{
$this->greeting = "hi";
}
public function greet()
{
echo $this->greeting;
}
}
$child = ChildClass::createObject();
$child->greet();
The output from the above is:
ParentBase
ChildClass
Fatal error: Call to private ChildClass::__construct() from context 'ParentBase'
Protected contstructor works:
http://codepad.viper-7.com/sCgJwA
Private constructor doesn't:
http://codepad.viper-7.com/YBs7Iz
That is an expected behavior createObject(); is a function of ParentBase, So it will return ParentBase from get_class() but, it was called from ChildClass So, it will return ChildClass from get_called_class().
And about the constructor, since the constructor is assigned as private, you restrict the object creation from within the class only. By making it protected, now Parent Class can create the object of ChildClass
Probable solution would be to override, the createObject() class in the ChildClass itself.
class ChildClass extends ParentBase
{
public static function createObject()
{
echo get_class() . "<br/>";
echo get_called_class() . "<br/>";
return new static();
}
}
Or, you could make the constructor protected, then you will make the constructor accessible to parent classes and restrict any sub classes of child classes making it final, thus making it accessible from parent class only.
The child constructor must be protected or public to my knowledge. I ran into a similar issue for a different problem, I was attempting to access a private property.
But for some reason your question "Can I call a private child constructor from a base factory method?" does not reflect your code so I suggest you edit that as I am troubling myself over how to answer this.
If this is PHP 5.4......
After playing around with some other things and reading up on PHP OOP. It looks like Traits can do this.
I like the use Trait notataion, which in this case makes it obvious that you should use the Factory to instantiate the class.
Using a Trait is advantageous in that it the Factory Trait can be shared across multiple Class hierarchies that do not have common lineages:
<?php
// NOTE: traits are only avaialable in ** PHP 5.4 **
trait Factory
{
public static function createObject()
{
echo get_class() . "<br/>"; // prints ChildClass
echo get_called_class() . "<br/>"; // prints ChildClass
return new static();
}
}
class ChildClass
{
use Factory;
private $greeting = "bye";
private function __construct()
{
$this->greeting = "hi";
}
public function greet()
{
echo $this->greeting;
}
}
$child = ChildClass::createObject();
$child->greet();
Working Code Example
I can't see anything wrong with this, but I see the above exception occasionally in the logs. What's wrong?
PHP Fatal error: Cannot access protected property Exception::$message in /web/index.php on line 23
On line 23 I have,
echo '<?xml version=\'1.0\'?><error-response status="error">
<message><![CDATA['.$e->message.']]></message>
</error-response>';
Use $e->getMessage() instead of $e->message because message is a protected property :)
$message is a protected member of class Exception, as the error message states. You want the public accessor getMessage:
$e->getMessage()
Members declared protected can be accessed only within the class itself and by inherited and parent classes.
class MyClass {
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private
You can dig more into Property Visibility here
I know the manual definition, but from a real life usage standpoint, what's the difference? When would you use one over the other?
EDIT:
use protected methods when you want a child class (one that extends your current (or parent) class) to be able to access methods or variables within the parent.
Here is the PHP Visibility Manual
private can be seen by no other classes except the one the variable/method is contained in.
protected can be seen by any class that is in the same package/namespace.
Code from the manual.
<?php
/**
* Define MyClass
*/
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// We can redeclare the public and protected method, but not private
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj2->public; // Works
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error
$obj2->printHello(); // Shows Public, Protected2, Undefined
?>
When you know that a variable will be used only in that class and not in any other or extending class you would name it private. So if you extend the class and mistakenly name the variable as the name of a private this would give you error and thus prevent you from making mistakes.
If you for example use many pages in your web application and all of the pages are classes that extend one single class that handles header and footer of the page (cause it's always the same) you can override for example the default title of the page which is set up in parent class with protected variable setting.
I hope this helps.