Should an object's method be able to access a protected property of another object of the same class?
I'm coding in PHP, and I just discovered that an object's protected property is allowed to be accessed by a method of the same class even if not of the same object.
In the example, at first, you'll get "3" in the output - as function readOtherUser will have successfully accessed the value -, and after that a PHP fatal error will occur - as the main program will have failed accessing the same value.
<?php
class user
{
protected $property = 3;
public function readOtherUser ()
{
$otherUser = new user ();
print $otherUser->property;
}
}
$user = new user ();
$user->readOtherUser ();
print $user->property;
?>
Is this a PHP bug or is it the intended behaviour (and I'll have to relearn this concept… :)) (and are there references to the fact)? How is it done in other programming languages?
Thanks!
This is intended. It's even possible to access the private members of the same class. So think of the modifiers to be class wise modifiers, not objectwise modifiers.
PHP is not the only language that has this feature. Java for example has this too.
It's intended behavior. A protected variable or function means that it can be accessed by the same class or any class that inherits from that class. A protected method may only be called from within the class, e.g. you cannot call it like this:
$object = new MyClass();
$object->myProtectedFunction();
This will give you an error. However, from within the defined class 'MyClass', you can perfectly call the protected function.
Same applies for variabeles. Summarized:
use PROTECTED on variables and functions when:
1. outside-code SHOULD NOT access this property or function.
2. extending classes SHOULD inherit this property or function.
Related
I reveal iteresting behavior of php >= 5.2
class A {
protected $a = 'A';
public function __get($f){ return 'field_'.$f; }
}
class B extends A {}
class C extends A {
public function foo() {
$b = new B();
echo $b->a;
}
}
$c = new C();
$c->foo();
I expect it print field_a, but it print A.
Also. If I remove magic from A - I expect fatal error, but it still print A in php>=5.2.
If we overwrite B::$a we get another behavior - fatal error.
Why?
Is it feature or bug?
Fiddles:
- http://3v4l.org/tiOC5 - get foreign field
- http://3v4l.org/uT9PC - get fatal error
This is because of PHP's very funky rules for who can and cannot access class properties.
Read about it here:
http://php.net/manual/en/language.oop5.visibility.php
The key part is this:
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.
Emphasis mine. You are allowed to access the protected variables of any object that inherits from the same class you inherit from, even on another object. This also includes accessing the private properties of other objects of the exact same class.
Whether this is a good idea or a weird feature is up for debate, but it seems to be intended.
I believe if you declare a variable it won't use the __get magic method.
So by declaring protected $a = 'A';, you are excluding a from the __get cycle. It will skip the magic method and go straight for the actual property.
If you look at the documentation for the magic methods __get() and __set() you'll see that they only run in the case of reading/writing to an inaccessible field.
In your example $a is accessible from class C as it's defined as a protected field (inheriting/parent classes can view protected fields/methods). If you change $a to a private field it should have to call the magic method for your example.
I understand OOP. What I understand so far is that private and protected cannot be referenced from outside the class using $this->blah notation. If that is correct, how can the following code work?
<?php
class test {
protected $a = "b";
public static function oo(){
$instance = new static();
echo $instance->a;
}
}
test::oo();
Gives me an output of b! Now, how in Lord's name can that happen?
In PHP 5.3, a new feature called late static bindings was added – and this can help us get the polymorphic behavior that may be preferable in this situation. In simplest terms, late static bindings means that a call to a static function that is inherited will “bind” to the calling class at runtime. So, if we use late static binding it would mean that when we make a call to “test::oo();”, then the oo() function in the test class will be called.after that you return $instance->a; static keyword allows the function to bind to the calling class at runtime.so if you use static then whatever access modifier(private,public,protected) you use it's just meaning less...
please read this link,another
That happens because you're "presenting it" by echo'ing it. You can't reference it like this for example:
class test {
private $a = 'b';
function __construct() {
echo 'instantiated';
}
}
$test = new test();
echo $test->a; // This line won't work, since it's a private var.
It would give you an error message that looks like this:
Fatal error: Cannot access private property test::$a
Example (https://eval.in/226435)
As I said before, you're accessing it from within the class itself, so you CAN view it. (That's the $instance you have there.) If you modify your code to use it like this:
class test {
protected $a = "b";
public static function oo(){
$instance = new static();
return $instance;
}
}
echo test::oo()->a;
Example of the above (https://eval.in/226439)
You'll get that "private acess blah blah" error.
You're understanding the statement wrong. "private and protected cannot be referenced from outside the class" means that as the examples above show, you CAN NOT access the variables outside the class, but with your example, you're accessing it from INSIDE the class, which means they'll do as you require (echo'ing out as you did)
What I understand so far is that private and protected cannot be referenced from outside the class
As follows, since oo is defined in the same type as a, oo has access to a.
This is actually a very good question, and shouldn't be down-voted.
From what I understand, the reason why you can access a protected/private property from within a static method of the same class, is because "the implementation specific details are already known when inside this class". I rephrased this a little from what is documented on the official page on Visibility:
Objects of the same type will have access to each others private and protected members even though they are not the same instances. This is because the implementation specific details are already known when inside those objects.
This makes sense. Visibility access is meant to only expose things that are safe for the public to use. But if you already have access to the code of the Class you're using, then there is no point of preventing you to use what you already see. Hope that makes sense..
I understand you cannot duplicate constant. I am just confused as to why it does not work with different objects.
In one of my project I used them to pass settings to my object.
Here is an example:
class someClass {
function __construct($config) {
define("PRIVATE_KEY", $config['private_key']);
}
}
and here is how I create the objects
$objectA = new someClass($config['A']);
$objectB = new someClass($config['B']); //ERROR!!
I get the error:
Constant PRIVATE_KEY already defined
Most people that get this error had included the same constant multiple times.
In My case they are being used in separate objects. I will add some check to make sure they are not being redefined. but I am still curious to know why this happening.
Aren't object disposed/destroyed when no longer used?
Yes, objects are destroyed at some point, but define declarations are global and persist until they are undefined. Your code is defining the same constant twice.
Private properties, static properties, or maybe class constants are more appropriate for what you're attempting to do since they are encapsulated within the object.
class someClass {
private $private_key;
// constructor
function __construct($config) {
$this->private_key = $config['private_key'];
}
}
What are you using PRIVATE_KEY for? Is it supposed to be an instance variable? If so, you shouldn't use define() because its scope is global. You could instead do $this->private_key = $config['private_key'].
MApp uses $database_object. I got an error that I could not use it because it was private. However I changed it to protected and now it works. Note that in the class hierarchy MApp is above MAppAMAdder.
I thought that protected meant that child classes could use the resource not parent classes. Is PHP different from other languages or is my understanding of how inheritance works not correct?
MAppAdder Snippet
class MAppAMAdder extends MApp
{
protected $database_object; // private will cause a fail.
MApp
abstract class MApp extends M
{
protected function getID($pipe)
{
$temp = $this->database_object->_pdoQuery('single', 'pull_id_by_h_token',
array($pipe['server']['smalls']['h_token']));
$pipe['id'] = $temp['id'];
return $pipe;
}
protected function addTweetTop($pipe, $comment)
{
$input = array( $pipe['server']['smalls']['h_token'],
$pipe['server']['smalls']['picture'],
$pipe['server']['smalls']['name'],
$comment,
time(),
$pipe['server']['smalls']['h_file'] );
$this->database_object->_pdoQuery( 'none', 'tweet_insert', $input);
return $pipe;
}
}
Error
Fatal error: Cannot access private property MAppTweet::$database_object in...
In PHP protected means that parent classes can also access the property:
Members declared protected can be accessed only within the class
itself and by inherited and parent classes.
You are correct in that this behavior is different from the "classical" behavior of strongly typed languages such as C++ and Java. In such languages (commonly called statically typed) the compiler prevents you from accessing class members in a way that is not provably correct by issuing a compile-time error. That's why a parent class cannot speculatively access a member defined in a child class: there is no guarantee that the member will be there at runtime.
On the other hand, PHP is dynamically typed and does let you refer to any member, even ones that do not exist at all (the access results to null in that case). The check for the existence of such a member is performed at runtime and can result in a wide array of outcomes (from nothing out of the ordinary to a runtime error in certain cases).
How can the super class use a field that is only defined in the subclass? That can't work properly. You can access any field of the superclass from within the subclass, as long as it is declared as public or protected. private fields are not accessible for the subclass. The superclass however knows nothing about any subclass that might exist.
Why in PHP you can access static method via instance of some class but not only via type name?
UPDATE: I'm .net developer but i work with php developers too. Recently i've found this moment about static methods called from instance and can't understand why it can be usefull.
EXAMPLE:
class Foo
{
public static Bar()
{
}
}
We can accept method like this:
var $foo = new Foo();
$foo.Bar(); // ??????
In PHP
the class is instantiated using the new keyword for example;
$MyClass = new MyClass();
and the static method or properties can be accessed by using either scope resolution operator or object reference operator. For example, if the class MyClass contains the static method Foo() then you can access it by either way.
$MyClass->Foo();
Or
MyClass::Foo()
The only rule is that static methods or properties are out of object context. For example, you cannot use $this inside of a static method.
Class Do {
static public function test() {
return 0;
}
}
use like this :
echo Do::test();
Why in PHP you can access static method via instance of some class but not only via type name?
Unlike what you are probably used to with .NET, PHP has dynamic types. Consider:
class Foo
{
static public function staticMethod() { }
}
class Bar
{
static public function staticMethod() { }
}
function doSomething($obj)
{
// What type is $obj? We don't care.
$obj->staticMethod();
}
doSomething(new Foo());
doSomething(new Bar());
So by allowing access to static methods via the object instance, you can more easily call a static function of the same name across different types.
Now I don't know if there is a good reason why accessing the static method via -> is allowed. PHP (5.3?) also supports:
$obj::staticMethod();
which is perhaps less confusing. When using ::, it must be a static function to avoid warnings (unlike ->, which permits either).
In PHP, while you're allowed to access the static method by referencing an instance of the class, you don't necessarily need to do so.
For example, here is a class with a static function:
class MyClass{
public static function MyFunction($param){
$mynumber=param*2;
return $mynumber;
}
You can access the static method just by the type name like this, but in this case you have to use the double colon (::), instead of "->".
$result= MyClass::MyFunction(2);
(Please note you can also access the static method via an instance of the class as well using "-->"). For more information: http://php.net/manual/en/language.oop5.static.php
In PHP 7 it seems to be absolutely necessary for you to be able to do $this->staticFunction(). Because, if this code is written within an abstract class and staticFunction() is also abstract in your abstract class, $this-> and self:: deliver different results!
When executing $this->staticFunction() from a (non-abstract) child of the abstract class, you end up in child::staticFunction(). All is well.
However, executing self::staticFunction() from a (non-abstract) child of the abstract class, you end up in parent::staticFunction(), which is abstract, and thusly throws an exception.
I guess this is just another example of badly designed PHP.
Or myself needing more coffee...