how would you test a method of a class that has collateral effects via self::? such as:
final class Foo{
private static $effect = false;
public static function doit( $arg ){
if( self::effect ) return;
self::check_args( $arg );
self::$effect = true;
}
private function check_args($arg){
#validate and trhow exception if invalid
}
}
I have to send several args to doit() to test it's validating the values correctly, but after the first run it's just bypassing it. it's basically a initialization method for a singleton that set's a initialized flag.
I can't really afford to mess with the class just yet. Is there any way i can make a copy/instatiate of the object in a way it would work with self::?
The class should be rewritten to:
class Foo{
private $effect = false;
public function doit( $arg ){
if( $this->effect ) return;
$this->check_args( $arg );
$this->effect = true;
}
private function check_args($arg){
#validate and trhow exception if invalid
}
}
self:: is only used for static functions and variables, which you should avoid using in most cases for various reasons.
How about adding to your testing environment a subclass of your class under test, and then using it to manipulate your static variable. In the example below, I modified your code a bit so the value of $effect is easier to play with, and I also had to bump the scope of $effect to protected:
<?php
class Foo{
protected static $effect = "0";
public static function doit( $arg ) {
echo 'I got: ' . $arg . ' and effect is: ' . self::$effect . '<br>';
self::$effect = "1";
}
}
class FooChild extends Foo {
public static function setEffect($newEffect) {
self::$effect = $newEffect;
}
}
Foo::doit('hello');
Foo::doit('world');
FooChild::setEffect('3');
Foo::doit('three');
The output is this:
I got: hello and effect is: 0 (first time through shows the initial value)
I got: world and effect is: 1 (second time through shows the value incremented by Foo in doit()
I got: three and effect is: 3 (third time through, shows that the subclass was able to change the value in the parent)
Related
When you have the following code:
<?php
class Foo { public $attribute; }
$o = new Foo();
$o->bar = true;
?>
PHP automatically creates a dynamic public property to that object.
Is there any possibility to do add dynamic private properties? Or set them to private at runtime with Reflection?
My __set method needs to be called every time someone tries to set any attribute in my class. I could set all my attributes to private, but, I have attributes that are added dynamically in this class, and when an attribute is added automatically, it has a public visibility.
This prevents the __set method from being called. How can I make dynamic properties call __set when receiving a value?
Actually, there's a method on ReflectionProperty class named setAccessible(). I would do the opposite of this method.
This doesn't seem possible in current versions of PHP.
While ReflectionProperty::setAccessible() does take a boolean argument, the change it makes only allows Reflection itself to access / not access the value. It doesn't actually change the accessibility of the actual property.
As a hacky workaround to keep dynamic properties private, consider having your __set store properties that don't actually exist in a dedicated private array. Example code:
class Test {
private $foo;
public $bar;
private $_properties;
public function __get($prop) {
if(property_exists($this, $prop))
return $this->$prop;
if(array_key_exists($prop, $this->_properties))
return $this->_properties[$prop];
}
public function __set($prop, $value) {
if(!property_exists($this, $prop)) {
$this->_properties[$prop] = $value;
echo 'SetDynamic: ', $prop, "\n";
return;
}
$this->$prop = $value;
echo 'Set: ', $prop, "\n";
}
}
Running from the PHP interactive prompt:
php > $t = new Test;
php > $t->foo = 1;
Set: foo
php > $t->foo = 2;
Set: foo
php > $t->bar = 1;
php > $t->testorama = 1;
SetDynamic: testorama
php > $t->testorama = 2;
SetDynamic: testorama
While this will ensure that external access always goes through your __get and __set methods, it presents a problem for internal use, as you're now always given two places to check for dynamic properties. That's why this is a hackish workaround instead of a real solution.
I am using PHP 5.2.x and want to encode objects of my custom PHP classes with only private members. One of the private members is an array of objects of another custom class.
I tried the solution outlined in https://stackoverflow.com/a/7005915/17716, but that obviously does not work recursively. The only solution that I can see is extending the json_encode method somehow ,so that the class' version of the json_encode method is called instead of the default method.
For reference, the code is as follows:
Class A {
private $x;
private $y;
private $z;
private $xy;
private $yz;
private $zx;
public function f1() {
...
}
public function f2() {
...
}
.
.
.
public function getJSONEncode() {
return json_encode(get_object_vars($this));
}
}
class B {
private $p; //This finally stores objects of class A
private $q;
private $r;
public function __construct() {
$this->p = array();
}
public function fn1() {
...
}
public function fn2() {
...
}
.
.
.
public function getJSONEncode() {
return json_encode(get_object_vars($this));
}
}
class C {
private $arr;
public function __construct() {
$this->arr = array();
}
public function fillData($data) {
$obj = new B();
//create objects of class B and fill in values for all variables
array_push($this->arr, $obj)
}
public function returnData() {
echo $this->arr[0]->getJSONEncode(); //Edited to add
}
}
What would be the best way to achieve this nested json encoding?
Edited to Add:
The output I get when the returnData method is executed is:
{"p":[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}],"q":"Lorem Ipsum","r":"Dolor Sit Amet"}
Whilst I believe you would be better off writing a proper export/encode function for each of your classes (which would construct a public object from both private and public values just for encoding - you could be quite clever and use php's reflection ability) - instead - you could make use of the following code:
/// example class B
class TestB {
private $value = 123;
}
/// example class A
class TestA {
private $prop;
public function __construct(){
$this->prop = new TestB();
}
}
function json_encode_private($obj){
/// export the variable to find the privates
$exp = var_export($obj, true);
/// get rid of the __set_state that only works 5.1+
$exp = preg_replace('/[a-z0-9_]+\:\:__set_state\(/i','((object)', $exp);
/// rebuild the object
eval('$enc = json_encode('.$exp.');');
/// return the encoded value
return $enc;
}
echo json_encode_private(new TestA());
/// {"prop":{"value":123}}
So the above should work, but I wouldn't recommend using eval anywhere in php - just because I always hear alarm bells quietly off in the distance :)
update
Just had a thought of what might make this a little safer, rather than using eval you could use create_function which would limit some of its creational powers, or at least the scope of those powers...
function json_encode_private($obj){
$exp = var_export($obj, true);
$exp = preg_replace('/[a-z0-9_]+\:\:__set_state\(/i','((object)', $exp);
$enc = create_function('','return json_encode('.$exp.');');
return $enc();
}
update 2
Had a chance to play around with another way of converting an object with private properties, to an object with public properties - using only a simple function (and no eval). The following would need to be tested on whichever version of PHP you are using as it's behaviour - again - might not be reliable.... due to the weird \0Class Name\0 prefixing in the converted private properties (see comments in code).
For more info on this strange prefixing behaviour:
http://uk3.php.net/language.types.array.php#language.types.array.casting
Anyway, so using a test class:
class RandomClass {
private $var = 123;
private $obj;
public function __construct(){
$this->obj = (object) null;
$this->obj->time = time();
}
}
We can use the following function to convert it to a public object:
function private_to_public( $a ){
/// grab our class, convert our object to array, build our return obj
$c = get_class( $a ); $b = (array) $a; $d = (object) null;
/// step each property in the array and move over to the object
/// usually this would be as simple as casting to an object, however
/// my version of php (5.3) seems to do strange things to private
/// properties when casting to an array... hence the code below:
foreach( $b as $k => $v ){
/// for some reason private methods are prefixed with a \0 character
/// and then the classname, followed by \0 before the actual key value.
/// This must be some kind of internal protection causing private
/// properties to be ignored. \0 is used by some languges to terminate
/// strings (not php though, as far as i'm aware).
if ( ord($k{0}) === 0 ) {
/// trim off the prefixed weirdnesss..?!
$e = substr($k, 1 + strlen($c) + 1);
/// unset the $k var first because it will remember the \0 even
/// if other values are assigned to it later on....?!
unset($k); $k = $e;
}
/// so if we have a key, either public or private - set our value on
/// the destination object.
if ( $k !== '' && $k !== NULL && $k !== FALSE ) {
$d->{$k} = $v;
}
}
return $d;
}
So if we put it all together:
$a = new RandomClass();
echo json_encode( private_to_public( $a ) );
/// {"var":123,"obj":{"time":1349777323}}
Again your best / most reliable bet is to either bespokely code your conversion methods for each class, or create some kind of generalised solution using Class Reflection, but that latter is far more involved, more involved than a StackOverflow answer... at least with the amount of time I have free ;)
further info
The above code will work when trying to access objects from anywhere, the reason for implementing this was because my first attempt was obviously to use the following:
echo json_encode( get_object_vars($a) );
/// you will get {} which isn't what you expect
It seems that if you want to use get_object_vars you have to use it from a context that has access to all the properties, i.e. from inside the class you are exposing:
public function getAllProperties(){
return get_object_vars( $this );
}
So, imagine we'd added the above to the RandomClass definition:
echo json_encode( $a->getAllProperties() );
/// you will get {"var":123,"obj":{"time":1349777323}}
This works because the members of a class have access to all the class's properties public or private.... so as I say, working this way is far far superior; superior, but not always possible.
In a nutshell: a class inherits a function from its parent. This function then gets called on the child, but appears to still have the scope of the parent class. Is this expected behavior?
Consider the following code example:
<?php
class OLTest {
private $var1 = 10;
public function getVar1() {
if (isset($this->var1)) {
return $this->var1;
} else {
return 'undefined';
}
}
public function getVar2() {
if (isset($this->var2)) {
return $this->var2;
} else {
return 'undefined';
}
}
}
class OLTest2 extends OLTest {
private $var1 = 11;
private $var2 = 20;
}
$oltest = new OLTest();
$oltest2 = new OLTest2();
echo "calling parent->getVar1\n";
echo $oltest->getVar1() . "\n";
echo "calling parent->getVar2\n";
echo $oltest->getVar2() . "\n";
echo "calling child->getVar1\n";
echo $oltest2->getVar1() . "\n";
echo "calling child->getVar2\n";
echo $oltest2->getVar2() . "\n";
?>
To my understanding, the output should be:
calling parent->getVar1
10
calling parent->getVar2
undefined
calling child->getVar1
11
calling child->getVar2
20
The actual output on my machine is:
calling parent->getVar1
10
calling parent->getVar2
undefined
calling child->getVar1
10
calling child->getVar2
undefined
To add to the confusion, a print_r($this) within either of the functions, will show that the scope is really set to the subclass, yet it's impossible to access the variable.
Can someone clear this up for me?
EDIT: I am using PHP in version 5.3.3-1ubuntu9.5.
No, the output should definetly be 10, undefined, 10, undefined.
The reason is that the variables that are private are visible only to the class defining them (not super or subclasses).
So, when child is called, its methods are defined in the parent object and when they resolve var1 or var2 they check in the scope definied for OLTest.
var2 is not accessible too because the declared var2 is only visible inside OLTest2.
To get your output, your should declare these variables protected or public.
It seems to be related to the private scope.
Try changing to protected or public anc check the results.
Yes, it is because your properties are private. The methods you are calling still belong to parent class as you haven't redefined them. To enable parent methods accessing child's properties you must make them protected in both parent and child classes:
class OLTest {
protected $var1 = 10;
public function getVar1() {
if (isset($this->var1)) {
return $this->var1;
} else {
return 'undefined';
}
}
public function getVar2() {
if (isset($this->var2)) {
return $this->var2;
} else {
return 'undefined';
}
}
}
class OLTest2 extends OLTest {
protected $var1 = 11;
protected $var2 = 20;
}
If you keep it private in parent class it won't allow child class to override that property, so the function belonging to parent class will be accessing its own private property. And if you make it private in child class it won't allow parent methods to access it.
If you still want to keep them private you'll have to copy-paste code of your methods into child class (yes, return parent::getVar1() won't work either).
You cannot "inject" private $var2 into the parent class, which is what you were trying to do.
So yes, that is normal behavior.
Because you have declared your vars private, they are totally hidden from each other. Thus the $var1 in the parent is actually a different variable from the $var1 in the child
When you call getVar1 on the parent, it uses the $var1 it sees, the one from the parent with the value 10. Similarly the parent cannot see the child's $var2, so to it the property is undefined.
When you call getVar1 and getVar2 on the child, the methods themselves are inherited from the parent class and behave exactly as they would if they were called from an object of the type OLTest
It's the scope of your private variables, try using protected
class OLTest {
protected $var1 = 10;
public function getVar1() {
return isset($this->var1) ? $this->var1:'undefined';
}
public function getVar2() {
return isset($this->var2) ? $this->var2:'undefined';
}
}
class OLTest2 extends OLTest {
protected $var1 = 11;
protected $var2 = 20;
}
$oltest = new OLTest();
$oltest2 = new OLTest2();
echo "calling parent->getVar1\n".$oltest->getVar1()."\n";
echo "calling parent->getVar2\n".$oltest->getVar2()."\n";
echo "calling child->getVar1\n".$oltest2->getVar1()."\n";
echo "calling child->getVar2\n".$oltest2->getVar2()."\n";
Results:
calling parent->getVar1
10
calling parent->getVar2
undefined
calling child->getVar1
11
calling child->getVar2
20
I trying to learn OOP and I've made this class
class boo{
function boo(&another_class, $some_normal_variable){
$some_normal_variable = $another_class->do_something();
}
function do_stuff(){
// how can I access '$another_class' and '$some_normal_variable' here?
return $another_class->get($some_normal_variable);
}
}
and I call this somewhere inside the another_class class like
$bla = new boo($bla, $foo);
echo $bla->do_stuff();
But I don't know how to access $bla, $foo inside the do_stuff function
<?php
class Boo
{
private $bar;
public function setBar( $value )
{
$this->bar = $value;
}
public function getValue()
{
return $this->bar;
}
}
$x = new Boo();
$x->setBar( 15 );
print 'Value of bar: ' . $x->getValue() . PHP_EOL;
Please don't pass by reference in PHP 5, there is no need for it and I've read it's actually slower.
I declared the variable in the class, though you don't have to do that.
Ok, first off, use the newer style constructor __construct instead of a method with the class name.
class boo{
public function __construct($another_class, $some_normal_variable){
Second, to answer your specific question, you need to use member variables/properties:
class boo {
protected $another_class = null;
protected $some_normal_variable = null;
public function __construct($another_class, $some_normal_variable){
$this->another_class = $another_class;
$this->some_normal_variable = $some_normal_variable;
}
function do_stuff(){
return $this->another_class->get($this->some_normal_variable);
}
}
Now, note that for member variables, inside of the class we reference them by prefixing them with $this->. That's because the property is bound to this instance of the class. That's what you're looking for...
In PHP, constructors and destructors are written with special names (__construct() and __destruct(), respectively). Access instance variables using $this->. Here's a rewrite of your class to use this:
class boo{
function __construct(&another_class, $some_normal_variable){
$this->another_class = $another_class;
$this->some_normal_variable = $another_class->do_something();
}
function do_stuff(){
// how can I access '$another_class' and '$some_normal_variable' here?
return $this->another_class->get($this->some_normal_variable);
}
}
You need to capture the values in the class using $this:
$this->foo = $some_normal_variable
I was writing a class that uses __get() and __set() to store and retrieve array elements in a master array. I had a check to make some elements ungettable, basically to re-create private properties.
I noticed that it seemed that __get intercepts all calls to class properties. This sucks for me, because I wanted to have a variable private to the outside world ( unavailable via get ), but I was trying to access it by directly referencing the master array from within the class. Of course, the master array is not in the whitelist of gettable properties :(
Is there a way I can emulate public and private properties in a php class that uses __get() and __set()?
Example:
<?
abstract class abstraction {
private $arrSettables;
private $arrGettables;
private $arrPropertyValues;
private $arrProperties;
private $blnExists = FALSE;
public function __construct( $arrPropertyValues, $arrSettables, $arrGettables ) {
$this->arrProperties = array_keys($arrPropertyValues);
$this->arrPropertyValues = $arrPropertyValues;
$this->arrSettables = $arrSettables;
$this->arrGettables = $arrGettables;
}
public function __get( $var ) {
echo "__get()ing:\n";
if ( ! in_array($var, $this->arrGettables) ) {
throw new Exception("$var is not accessible.");
}
return $this->arrPropertyValues[$var];
}
public function __set( $val, $var ) {
echo "__set()ing:\n";
if ( ! in_array($this->arrSettables, $var) ) {
throw new Exception("$var is not settable.");
}
return $this->arrPropertyValues[$var];
}
} // end class declaration
class concrete extends abstraction {
public function __construct( $arrPropertyValues, $arrSettables, $arrGettables ) {
parent::__construct( $arrPropertyValues, $arrSettables, $arrGettables );
}
public function runTest() {
echo "Accessing array directly:\n";
$this->arrPropertyValues['color'] = "red";
echo "Color is {$this->arrPropertyValues['color']}.\n";
echo "Referencing property:\n";
echo "Color is {$this->color}.\n";
$this->color = "blue";
echo "Color is {$this->color}.\n";
$rand = "a" . mt_rand(0,10000000);
$this->$rand = "Here is a random value";
echo "'$rand' is {$this->$rand}.\n";
}
}
try {
$objBlock = & new concrete( array("color"=>"green"), array("color"), array("color") );
$objBlock->runTest();
} catch ( exception $e ) {
echo "Caught Exeption $e./n/n";
}
// no terminating delimiter
$ php test.php
Accessing array directly:
__get()ing:
Caught Exeption exception 'Exception' with message 'arrPropertyValues is not accessible.' in /var/www/test.php:23
Stack trace:
#0 /var/www/test.php(50): abstraction->__get('arrPropertyValu...')
#1 /var/www//test.php(68): concrete->runTest()
#2 {main}.
Is there a way I can emulate public and private properties in a php class that uses __get() and __set()?
Not directly (if you discount debug_backtrace).
But you can have a private method getPriv that does all the work your current __get does. Then __get would only wrap this private method and check accessibility.
function __get($name) {
if (in_array($name, $this->privateProperties))
throw new Exception("The property ". __CLASS__ . "::$name is private.");
return $this->getPriv($name);
}
Inside your class, you would call getPriv, thus bypassing __get.
Make abstraction::$arrPropertyValues protected or do what Artefacto wrote (if you need additional checks), except that abstraction::getPriv() should be protected.
Rather than manually enlisting private/protected properties, you could use PHPs cumbersome reflection methods:
function __get($name) {
$reflect = new ReflectionObject($this);
$publics = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
if (in_array($name, $publics)) {
return $this->{$name};
}
}