php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #52711 incorrect private property handling in extended class
Submitted: 2010-08-26 23:58 UTC Modified: 2010-09-27 00:54 UTC
Votes:4
Avg. Score:4.8 ± 0.4
Reproduced:4 of 4 (100.0%)
Same Version:1 (25.0%)
Same OS:2 (50.0%)
From: vik_ at hotmail dot com Assigned:
Status: Not a bug Package: Class/Object related
PHP Version: 5.3.3 OS: windows 7
Private report: No CVE-ID: None
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: vik_ at hotmail dot com
New email:
PHP Version: OS:

 

 [2010-08-26 23:58 UTC] vik_ at hotmail dot com
Description:
------------
test script $t->priv2 throws 
Notice: Undefined property: test::$priv2 error.



Test script:
---------------
class base {
	private $priv = "private";
	protected $prot = "protected";
	public $pub = "public";
	 final public function __get($prop){
	 	$cv = get_class_vars(__CLASS__); $ccv = get_class_vars(get_called_class());
		var_dump('GET--------------', $prop, get_called_class(), __CLASS__, $this, $cv, $ccv, isset($this->$prop), property_exists($this, $prop));
		return $this->$prop;
	}
	 final public function __set($prop, $value){
	 	$cv = get_class_vars(__CLASS__); $ccv = get_class_vars(get_called_class());
		var_dump('SET--------------', $prop, get_called_class(), __CLASS__, $this, $cv, $ccv, isset($this->$prop), property_exists($this, $prop));
		$this->$prop = $value;
	}
}

class test extends base {
	private $priv2 = "private";
	protected $prot2 = "protected";
	public $pub2 = "public";
}

// pub2 and prot2 works.
$t = new test();
echo $t->pub2; echo $t->prot2; echo $t->priv2;

Expected result:
----------------
like base class results:

// it works.
$b = new base();
echo $b->pub; echo $b->prot; echo $b->priv;

Actual result:
--------------
Inconsistent behaviour:
1) $this has 6 properties (pub, prot, priv, pub2, prot2, priv2): good 
2) get_class_vars(get_called_class()) has 5, priv2 missing: wrong
3) isset($this->$prop) returns false in this case: wrong
4) property_exists($this, $prop)) returns true: good

maybe similar bug: #47808

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-09-13 23:16 UTC] rayro at gmx dot de
seems not to be really a bug, as the manual explains the following:
http://de.php.net/manual/en/language.oop5.visibility.php
"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."

BUT

there is something wrong.. It seems somehow __get will be invoked on the "parents scope" if not redefined...

Test script:
------------
<?php
class Foo {
	private $var = 'sheep';
	function __construct() {
	}
	public function __call($f,$a) {
		return empty($this->{$f});
	}
	public function __get($v) {
		return $this->{$v};
	}
}
class Bar extends Foo {
}
class Baz extends Foo {
	private $var;
	function __construct() {
		$this->var = 'dog';
	}
}
class Faz extends Foo {
	private $var;
	function __construct() {
		$this->var = 'cat';
	}
	function __call($f,$a) {
		return empty($this->{$f});
	}
	function __get($v) {
		return $this->{$v};
	}
}
$test = new Foo();
echo('called $Foo->var(): '.(int)$test->var().PHP_EOL);
echo('called $Foo->var: '.$test->var.PHP_EOL);
echo('existing property: '.property_exists($test,'var').PHP_EOL);
$test = new Bar();
echo('called $Bar->var(): '.(int)$test->var().PHP_EOL);
echo('called $Bar->var: '.$test->var.PHP_EOL);
echo('existing property: '.property_exists($test,'var').PHP_EOL);
$test = new Baz();
echo('called $Baz->var(): '.(int)$test->var().PHP_EOL);
echo('called $Baz->var: '.$test->var.PHP_EOL);
echo('existing property: '.property_exists($test,'var').PHP_EOL);
$test = new Faz();
echo('called $Faz->var(): '.(int)$test->var().PHP_EOL);
echo('called $Faz->var: '.$test->var.PHP_EOL);
echo('existing property: '.property_exists($test,'var').PHP_EOL);
?>

Expected result:
----------------
called $Foo->var(): 0
called $Foo->var: sheep
existing property: 1
called $Bar->var(): 1 <-----
called $Bar->var:  <-----
existing property: 
called $Baz->var(): 0
called $Baz->var: dog <-----
existing property: 1
called $Faz->var(): 0
called $Faz->var: cat
existing property: 1

Actual result:
--------------
called $Foo->var(): 1
called $Foo->var: sheep
existing property: 1
called $Bar->var(): 0
called $Bar->var: sheep
existing property: 
called $Baz->var(): 0
called $Baz->var: sheep
existing property: 1
called $Faz->var(): 0
called $Faz->var: cat
existing property: 1
 [2010-09-13 23:44 UTC] rayro at gmx dot de
addition to my previous post..

toggling the default value of $var in Foo to null and setting it in the constructor:
<?php
class Foo {
	#private $var = 'sheep';
	private $var;
	function __construct() {
		$this->var = 'sheep';
	}
...
?>

will effect to the following result:
------------------------------------
called $Foo->var(): 0
called $Foo->var: sheep
existing property: 1
called $Bar->var(): 0
called $Bar->var: sheep
existing property: 
called $Baz->var(): 1 <-----
called $Baz->var:  <-----
existing property: 1
called $Faz->var(): 0
called $Faz->var: cat
existing property: 1
 [2010-09-14 02:15 UTC] vik_ at hotmail dot com
Hi rayro!
Thank you for your answer!

I think magic methods must be independent of the place of the declaration.

This is a problem here:
$this and property_exists($this, $prop) is in right scope (test class)
get_class_vars(get_called_class()) and isset($this->$prop) is in parent scope (base class)
 [2010-09-27 00:54 UTC] cataphract@php.net
-Status: Open +Status: Bogus
 [2010-09-27 00:54 UTC] cataphract@php.net
This is expected behavior. __get is a method of base, hence its *calling* scope will be "base" and it will only be able to access private properties of base.

The *called* scope is a different matter altogether (see php.net/lsb); in case of non static calls it will be set to the runtime class of the object, but it's not used to determine access to properties (or methods).
 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Wed Jul 16 01:01:32 2025 UTC