Bug #66670 [Nab]: property_exist cannot find private properties defined in superclass

From: Date: Tue, 11 Feb 2014 23:03:41 +0000
Subject: Bug #66670 [Nab]: property_exist cannot find private properties defined in superclass
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-184266@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=66670&edit=1

 ID:                 66670
 User updated by:    mpope at homeimprovementleads dot com
 Reported by:        mpope at homeimprovementleads dot com
 Summary:            property_exist cannot find private properties
                     defined in superclass
 Status:             Not a bug
 Type:               Bug
 Package:            Class/Object related
 Operating System:   windows 7
 PHP Version:        5.5.9
 Block user comment: N
 Private report:     N

 New Comment:

Ok, we're getting close to being on the same page, even if I think that page is a bad design
:D. I was incorrect about my third reply that super class private properties were not accessible
from super class magic methods triggered by subclasses - they are accessible from inside the class
proper, they are just not accessible from a utility function that resides outside the super class
even when that super class passes in a reference of $this because $this in the superclass is really
just the $this from the subclass (which explains why get_class($this) and get_called_class() return
the same value, and why that value is always the subclass in super classes.)  My mistake stemmed
from reducing a more complex codebase for the purpose of this ticket, and in that system I was
checking property_exists($ref, 'private_var') which would only ever work if I instantiated
my base classes directly. Which just seems *crazy* to me because I don't see the purpose in
such a design.

So let's see if you concur with my new understanding.  Basically in PHP $this does not refer to
the class scope of the keyword's location in the source code, it's only ever a reference
to the constructed child-most subclass instance.  And the child-most subclass is granted temporal
privileged access to private variables of any super class in its inherency chain when the code
execution is physically located inside the respective super class (which screams closure to me, not
OO class inheritance.)  

Passing "$this" to an external utility function from a function inside the super class
actually passes the sub-classed instance and revokes access to that super class's private
variables.  So that means you cannot use a utility library to check base-level private property
existence using the $this reference from said base class, you have to check it explicitly inside the
base class itself and copy/paste that logic to any other base class that needs it.

If those assumptions are correct I think it's a bug, I think the privileged temporal access to
private super class variables should be attached to the $this reference, not just granted based on
the line number/file name of where $this is evaluated with respect to the class definition.  Besides
the DRY code concerns, there is no way that I know of to externally reflect on the private
properties of a live super class instance, which is the whole point of reflection.  If I have to
write the logic inside each class explicitly, there's really no need for private variable
reflection in PHP.

Thanks for your time/effort/hand-holding on this one, I'm fairly adept with OO concepts from a
variety of programming languages, but I'm discovering that PHP deviates from the norm in this
manner.


Previous Comments:
------------------------------------------------------------------------
[2014-02-11 01:10:14] requinix@php.net

Re: third reply

You're right in that it would belong in a separate ticket, but I don't think your code is
quite as you've described.

<?php

class ParentClass {
	private $foo = 123;
	function __call($method, $args) { var_dump($this->$method); }
}

class ChildClass extends ParentClass {
	private $bar = 456;
}

echo "Parent:\n";
$p = new ParentClass();
$p->foo(); $p->bar();

echo "\nChild:\n";
$c = new ChildClass();
$c->foo(); $c->bar();

// Parent:
// int(123)
// NULL

// Child:
// int(123)
// Fatal error: Cannot access private property ChildClass::$bar...

?>

------------------------------------------------------------------------
[2014-02-11 01:01:17] requinix@php.net

Re: first two replies

I think what you're missing is that even though $foo and $goo are not inherited and don't
"exist" in $this, they must clearly exist *somewhere* for A and B to be able to use them.
Here's three simple statements, all restating the same underlying rule:
1. $this->foo exists in an instance of A (1a) and in code inherited from A (1b)
2. $this->goo exists in an instance of B (2a) and in code inherited from B (2b)
3. $this->poo exists in an instance of C (3a) and in code inherited from C (3b)

They fully explain the output:
// C::__construct
bool(false) // $foo: neither 1a nor 1b apply
bool(false) // $goo: neither 2a nor 2b apply
bool(true)  // $poo: both 3a and 3b apply

// B::__construct
bool(false) // $foo: neither 1a nor 1b apply
bool(true)  // $goo: 2a does not apply but 2b does
bool(true)  // $poo: 3a applies (3b does not)

// A::__construct
bool(true)  // $foo: 1a does not apply but 1b does
bool(false) // $goo: neither 2a nor 2b apply
bool(true)  // $poo: 3a applies (3b does not)

// check_scope
bool(false) // $foo: neither 1a nor 1b apply
bool(false) // $goo: neither 2a nor 2b apply
bool(true)  // $poo: 3a applies (3b does not)

------------------------------------------------------------------------
[2014-02-11 00:19:14] mpope at homeimprovementleads dot com

And this might be worthy of its own ticket, but it looks like all magic methods use an incorrect
scope during execution.  I have a __call() method that cannot access a private property declared in
the same class when __call() is invoked via a subclassed object.  

In the context of the example script, if class 'a' had a __call() method defined and we
called $c->UnknownFunc(), the __call method in class 'a' would not be able to access
$foo, even though they're in the same class.  And yet with $c->UnknownFunc() I can access
$poo from class a's __call() method even though $poo is defined in class 'c', not
class 'a' where the __call method is defined.

------------------------------------------------------------------------
[2014-02-10 23:22:52] mpope at homeimprovementleads dot com

Additionally if we're going to be strict to inheritance rules about private properties, we
shouldn't see private properties of our subclasses either.  Just the private properties of the
currently scoped class.  But in the example you'll notice that property_exist returns true for
both the current class's scope AND the 'c' subclass.  Meaning in class a's
constructor I can see two private properties, $foo and $poo.  By your statement I should only see
$foo.

------------------------------------------------------------------------
[2014-02-10 23:11:00] mpope at homeimprovementleads dot com

Ok, but there is still a bug in that if you call property_exists from the class proper, vs from some
externally defined method from the class you get different results. In the example I posted, the
check_scope() function should echo the same results as the explicit checks inside the constructors.

------------------------------------------------------------------------


The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at

    https://bugs.php.net/bug.php?id=66670


-- 
Edit this bug report at https://bugs.php.net/bug.php?id=66670&edit=1


Thread (9 messages)

« previous php.bugs (#184266) next »