In the code below I would expect that real_change and change methods to change the variable $n passed by reference in the constructor.
abstract class y {
private $x;
function __construct(&$x) {
$this->x = &$x;
}
function real_change() {
$this->x = 'real change';
}
}
class x extends y {
function change() {
$this->x = 'changed';
}
}
$n = 'intact';
$c = new x($n);
$c->change();
echo $n.PHP_EOL; // prints "intact"
$c->real_change();
echo $n.PHP_EOL; // prints "real change"
Why is this happening?
How could I achieve to create a method in an abstract class that modifies a variable referenced in a property?
Many thanks.
In your abstract class, $x is marked as private, so it's not available to your extended class. $this->x will essentially just create a new public $x variable in class x.
If you set the variable to protected it will give access to all extended classes, and allow you to set it properly.
See updated playground here.
More information on visibility via the PHP docs, can be found here.
Related
Given:
class X extends A
class Y extends A
I have an Object of Class X and want to create an Object of Class Y that shares all fields with the Object of Class X (no matter if deep or shallow copy) that they have in common through Class A.
Can I cast Objects of Class X to Class Y or somehow else copy all attributes they have in common?
If you would have simply asked: how can I easily create a variable of class Y that has all the common contents of class A of a variable of class X? Then I think you would have gotten a quicker answer. However you asked a question about casting.
Casting is a way of 'tricking' the compiler (or in this case the interpreter) to interpret a variable of one type as a variable of another type. If you know what you are doing this can come in handy sometimes. Most of the time however, it is a bad idea.
Casting to objects is not possible in PHP, but if it were and you could write:
class A {
public $foo = 1;
}
class X extends A {
public $bar = "2";
}
class Y extends A {
public $bom = 3;
}
$x = new X();
$y = (Y) x;
Would you want PHP to think variable $y is of type Y or would you want it to actually be of type Y? The above would achieve the first, not the latter. As soon as you would access $y->bom your program's execution would run into a problem, since no memory was allocated for $bom nor was it initialized. In languages that do allow t his, like c++, this would likely cause a GPF.
Therefore you don't need to cast, you don't want to cast and you can't cast to begin with. Forget casting :)
What you want to do is create some method that copies the fields of another object to your object instance. Here is an example:
class A {
public $foo;
public function copy($cA) {
$this->foo = $cA->foo;
}
}
class X extends A {
public $bar;
public function copy($cX) {
if ($cX instanceof A) parent::copy($cX);
if ($cX instanceof X) {
$this->bar = $cX->bar;
}
}
}
class Y extends A {
public $bom;
public function copy($cY) {
if ($cY instanceof A) parent::copy($cY);
if ($cY instanceof Y) {
$this->bom = $cY->bom;
}
}
}
$x = new X();
$x->foo = "hello";
$x->bar = "world";
$otherX = new X();
$otherX->copy($x);
echo "$otherX->foo $otherX->bar \n"; // hello world
$y = new Y();
$y->copy($otherX);
echo "$y->foo \n"; // hello
The method of copying in this example is by assignment. This is not very flexible though, since this means everytime you add a property or rename a property in one of these classes you'd have to update the copy method to reflect the change.
Here is a more flexible way of achieving the same thing:
class A {
protected $foo;
public function copy($cA) {
foreach (get_object_vars($cA) as $key => $value) {
$this->$key = $value;
}
}
public function setFoo($foo) {
$this->foo = $foo;
}
}
class X extends A {
public $bar;
public function out() {
echo "$this->foo $this->bar \n";
}
}
class Y extends A {
public $bom;
public function out() {
echo "$this->foo $this->bom \n";
}
}
// setup x
$x = new X();
$x->setFoo("hello");
$x->bar = "world";
// setup otherX
$otherX = new X();
$otherX->copy($x);
$x->setFoo("hi");
$otherX->out(); // -> hello world
// setup Y
$y = new Y();
$y->copy($x);
$y->bom = "php";
$y->out(); // -> hi php
Though this method is a lot more flexible it is also somewhat crude.
echo $y->bar; // has become "world"
This happens because get_object_vars doesn't look at what variables each class type can have. You could work around this to some extent using ReflectionClass, but I am guessing by this time you've gotten the answer you needed.
Hope this helps.
Good luck!
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 have two classes A and B, both inheriting from the same parent. In PHP, is there a way to make sure that class B cannot be instantiated except from within class A?
(Class B is not a child of A.)
Using debug_backtrace:
class Ancestor{}
class A extends Ancestor{
public function buildB()
{
return new B;
}
}
class B extends Ancestor{
public function __construct(){
$backtrace = debug_backtrace();
if( $backtrace[1]['class'] !== 'A' )
throw new Exception("Don't you dare!");
echo "Built successful!\n";
}
}
Try it:
//Everything ok this way:
$a = new A;
$a -> buildB();
// You will have an exception in any other case:
try{
$b = new B;
}catch(Exception $e){
echo $e -> getMessage();
}
EDIT: if you want to be sure to create B just inside A's code, you can do as well - just make buildBprivate ^^.
Yes.
How you can go about achieving it is, make the class B's constructor accept one argument. And define a method for class A that makes objects of B.
Die or throw execption if $argument == null || !($argument instanceof A).
Example code:
class X {
public $i = 0;
public function getI() {
return $i;
}
public function setI($x) {
$i = $x;
}
}
class A extends X {
public function setI($x) {
$i = $x * 2;
}
public function makeB($var){
$b = new B($var);
}
}
class B extends X {
public function __construct($a) {
if (null == $a) {
echo "no arguments given!\r\n";
//exit;
}else if (!($a instanceof A)) {
echo "disallowed\r\n";
//exit;
}else{
echo "initialized b\r\n";
}
}
public function setI($x) {
$i = $x * 3;
}
}
$a = new A();
$a->makeB();
$a->makeB(new X());
$a->makeB(&$a);
Output:
Warning: Missing argument 1 for A::makeB(), called in file.php
no arguments given!
disallowed
initialized b
You can see a demo here.
I'm not thinking of this from a php perspective, but more from the oop side...think the only way you could accomplish it is if you made B's constructor private, then exposed a static method accepting a parameter of A and an out parameter of B, then that method could privately instantiate B and return it to A through the out parameter.
//Pseudocode, language-indifferent
class A{
var _B;
public B GetMeAnInstanceOfB(){
_B=B.CreateInstanceOfB(this);
}
//alternate
public B GetMeAnotherInstanceOfB(){
_B=new B(this);
}
}
class B{
private B();
//alternate
private B(A);
static B CreateInstanceOfB(A){
return new(b);
}
}
That's really crude and probably full of potholes, but there's a stab at it. Technically, subclasses of A could still get a B, so if you sealed the class (prevented subclasses), that would close that door.
Interesting question, to be sure...
EDIT: This mod really doesn't fix the problem, but maybe(?) it's better - I've created a public constructor for B that takes an A as a parameter, and the instantiation of B now takes place only in A. The only problem is that it persists with the same problem JDelage pointed out - if I instantiate A, I can build a B...
I need to construct a class with alot of variables directly from the Database, For simplicity we'll name them 'userX', I've looked into ORM just a little, but its way over my head.
Essentially I thought I could use my procedural code
for ($i=0; $i<100; $i++) {
public ${'user'.$i};
}
But, in a class
class test() {
private $var1;
for ($i=0; $i<10000; $i++) {
public ${'user'.$i};
}
function __constructor .....
}
Obviously not.. but it leaves me with the same problem, how can I add $user0, $user1, $user2, etc etc, without having to type all 10k of them in..
Obviously, it would be 1000x easier to just grab the names from the Database, but again, that looks even harder to code. Should I buckle down and grab them all ORM style?
You could simply use the magic accessors to have as many instance attributes as you wish :
class test{
private $data;
public function __get($varName){
if (!array_key_exists($varName,$this->data)){
//this attribute is not defined!
throw new Exception('.....');
}
else return $this->data[$varName];
}
public function __set($varName,$value){
$this->data[$varName] = $value;
}
}
Then you could use your instance like this :
$t = new test();
$t->var1 = 'value';
$t->foo = 1;
$t->bar = 555;
//this should throw an exception as "someVarname" is not defined
$t->someVarname;
And to add a lot of attributes :
for ($i=0;$i<100;$i++) $t->{'var'.$i} = 'somevalue';
You could also initialize a newly created instance with a given set of attributes
//$values is an associative array
public function __construct($values){
$this->data = $values;
}
Try $this->{$varname}
class test
{
function __construct(){
for($i=0;$i<100;$i++)
{
$varname='var'.$i;
$this->{$varname}=$i;
}
}
}
You can use variable variables ($$var) - content of one variable is used as a name for other variable (double $$)
Therefore not $this->varname but $this->$varname.
class test
{
for($i=0;$i<100;$i++)
{
$varname='var'.$i;
$this->$varname=$i;
}
}
This will dynamically create 100 variables with names $var0, $var1 ...
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