Page 1 of 2

Show me your privates; just for a second

Posted: Wed Aug 23, 2006 6:58 pm
by Ollie Saunders
Calm down.

I am nighty nine point nine percent sure that the answer to this will be you can't but I'm gonna ask anyway.
I have written a complete class and I want to unit test it. I would like to get access to all the private and protected properties within it for the purpose of the test only, is this possible?

Posted: Wed Aug 23, 2006 7:04 pm
by sweatje
Don't. But if you won't listen to me look here.

Posted: Wed Aug 23, 2006 7:15 pm
by volka
can be done via reflection.

Code: Select all

<?php
class Foo {
	private $bar;
	
	public function __construct() {
		$this->bar = 4711;
	}
}

$foo = new Foo;

$ref = new ReflectionClass('Foo');
$property = $ref->getProperty('bar');
echo $property->getValue($foo);
?>
see http://de2.php.net/reflection

Posted: Wed Aug 23, 2006 7:22 pm
by Ollie Saunders
Well I was hoping for something better than reflection but this is actually pretty sweet:

Code: Select all

class OF_UnitTest extends UnitTestCase
{
    static function expose($obj, $prop)
    {
        $reflection = new ReflectionClass($obj);
        $prop = $reflection->getProperty($prop);
        return $prop->getValue($obj);
    }
}

Posted: Wed Aug 23, 2006 7:32 pm
by volka
What's wrong with reflection? Image

Posted: Wed Aug 23, 2006 7:37 pm
by Ollie Saunders
Tell me

Posted: Wed Aug 23, 2006 7:45 pm
by volka
I don't know, just concluded from
ole wrote:Well I was hoping for something better than reflection
reflection is a mediocre maybe even flawed solution.

Posted: Wed Aug 23, 2006 7:53 pm
by Ollie Saunders
O i c

There was a comment on that link sweatje posted saying that PHP's ReflectionProperty should have a makePublic() method, you could then iterate over an entire object pretty easily. Of course what would be really good is a friend class akin to C++.

Posted: Wed Aug 23, 2006 7:55 pm
by Chris Corbyn
People seem to commonly add getters which only exist for the tests.

Posted: Wed Aug 23, 2006 7:57 pm
by Ollie Saunders
Yeah, I don't want to do that. This is a library I'm writing here, I don't want a load of getters clogging up the source code when they should really be insulated. Besides its a lot of effort.

Posted: Wed Aug 23, 2006 7:58 pm
by volka
ole wrote:There was a comment on that link sweatje posted saying that PHP's ReflectionProperty should have a makePublic() method,
What for?

Code: Select all

<?php
class Foo {
	private $bar1;
	private $bar2;
	private $barABC;
	private $barK;
	
	public function __construct() {
		$this->bar1 = rand(1,99);
		$this->bar2 = rand(1,99);
		$this->barABC = rand(1,99);
		$this->barK = rand(1,99);
	}
}

$foo = new Foo;

$ref = new ReflectionObject($foo);
$properties = $ref->getProperties();
foreach($properties as $prop) {
	echo $prop->getName(), ' : ', $prop->getValue($foo), "<br />\n";
}
?>

Posted: Wed Aug 23, 2006 8:14 pm
by sweatje
volka wrote:
ole wrote:There was a comment on that link sweatje posted saying that PHP's ReflectionProperty should have a makePublic() method,
What for?

Code: Select all

<?php
class Foo {
	private $bar1;
	private $bar2;
	private $barABC;
	private $barK;
	
	public function __construct() {
		$this->bar1 = rand(1,99);
		$this->bar2 = rand(1,99);
		$this->barABC = rand(1,99);
		$this->barK = rand(1,99);
	}
}

$foo = new Foo;

$ref = new ReflectionObject($foo);
$properties = $ref->getProperties();
foreach($properties as $prop) {
	echo $prop->getName(), ' : ', $prop->getValue($foo), "<br />\n";
}
?>

Code: Select all

bar1 :
Fatal error: Uncaught exception 'ReflectionException' with message 'Cannot access non-public member' in Command line code:20
when run with

Code: Select all

$ php -v
PHP 5.0.3 (cli) (built: Mar  7 2005 22:56:05)
Copyright (c) 1997-2004 The PHP Group
Zend Engine v2.0.3, Copyright (c) 1998-2004 Zend Technologies
also with PHP 5.1.1

Posted: Wed Aug 23, 2006 8:25 pm
by volka
hm, no problems with
PHP 5.1.5 (cli) (built: Aug 15 2006 23:54:56)
and
PHP 5.1.4-pl6-gentoo (cli) (built: Aug 22 2006 01:59:09)
edit:
http://cvs.php.net/viewcvs.cgi/php-src/ext/reflection/php_reflection.c?revision=1.164.2.33&view=markup wrote:CVS Tags: php_5_1_3, php_5_1_3RC2, php_5_1_3RC3, php_5_1_4, php_5_1_5, php_5_1_5RC1, php_5_1_6

Code: Select all

/* {{{ proto public mixed ReflectionProperty::getValue([stdclass object])
   Returns this property's value */
ZEND_METHOD(reflection_property, getValue)
{
	reflection_object *intern;
	property_reference *ref;
	zval *object;
	zval **member= NULL;

	METHOD_NOTSTATIC(reflection_property_ptr);
	GET_REFLECTION_OBJECT_PTR(ref);

#if MBO_0
	if (!(ref->prop->flags & ZEND_ACC_PUBLIC)) {
		_DO_THROW("Cannot access non-public member");
		/* Returns from this function */
	}
#endif
This MBO_0 was obviously not defined when both php versions used by me were compiled.
I don't know what MBO_0 is good for. But it voids the use of reflection in this case :(

edit #2:
http://www.xy1.org/internals@lists.php.net/msg03498.html wrote:> Hi All,
> what this MBO_0 mean?
cvs convention around these parts. rather than #ifdef 0, the person doing
the #ifdef uses his/her cvs user name appended with "_0". in this case the
guilty party appears to be Marcus.

Posted: Thu Aug 24, 2006 3:29 am
by Ollie Saunders
What for?
Making a private or protected variable public is not the same as getting its value once.

Posted: Thu Aug 24, 2006 1:07 pm
by volka
Yes, but the question remains: why? What is its purpose?
Changing the classes behaviour for testing? Not such a good idea.