PHP: __set function behaviour different each time - php

This manages to create a new property on the object. But, can someone explain, with supporting links, why setAttrib behaves in two different ways? Why doesn't it cause a... wait for it... stack overflow!!??
class Test
{
public function setAttrib( $key, $value ) {
echo "setAttrib\n";
// first time: calls $this->__set($key, $value)
// second time: just sets a public property (but, when exactly was it created?)
$this->$key = $value;
}
public function __set( $key, $value ) {
echo "__set\n";
$this->setAttrib($key, $value);
}
}
$test = new Test();
$test->setAttrib('hey', 'It works');
var_dump($test);
produces...
setAttrib
__set
setAttrib
object(Test)#1 (1) {
["hey"]=>
string(8) "It works"
}
Edit: I'm not looking for an alternative. I'm looking for the reason why this works.

You are not the only one who seems to have notice that non-recursive behaviour : this comment on the manual's page states :
2 - PHP will not recursively call one
magic method from within itself (at
least for the same $name).
And, a bit later on the same page, there is this one, which states :
The recursion detection feature can
prove especially perilous when using
__set. When PHP comes across a
statement that would usually call
__set but would lead to recursion,
rather than firing off a warning or
simply not executing the statement it
will act as though there is no __set
method defined at all. The default
behaviour in this instance is to
dynamically add the specified property
to the object thus breaking the
desired functionality of all further
calls to __set or __get for that
property.
And, on PHP's bugtracker, there is #47215 : magic method __set() is bypassed on recursive call, which says :
Magic method __set() is bypassed on
recursive call. PHP automatically
creates a property on instance instead
of recursively calling __set() or
instead of throwing a recursivity
error
And it has been closed as :
Thank you for taking the time to write
to us, but this is not a bug.
That bug-report, itself, points to this blog-post, which ends by this sentence (quoting, emphasis mine) :
After all I think it may not be a bug
but expected behaviour, otherwise we
could not be able to define object
properties from within __set()
method.

__set is only used when writing to inaccessible properties. That is, those who are not accessible (private or protected) or those that aren't set at all. Therefore, __set will only be called once.
Here's what happens:
setAttrib: Attempt to write
class: inaccessible property
__set: Do whatever __set is told to do, which is call setAttrib again.
setAttrib: Attempt to write
class: inaccessible property, but __set can't recurse, and we're already in it, so do it as if __set didn't exist.
See user comments for http://php.net/__set for proof that __set can't recurse.

__set and __get only catch the call for the property if the member is protected. meaning that within the object $this->$key sets the property $key. if called from out of scope then the __set logic is called. One of your calls happens in the object outside the object.

Related

__callStatic(): instantiating objects from static context?

I am confused about how "static" and "dynamic" functions and objects in PHP work together especially with regards to __callStatic().
How __callStatic() works:
You can have a normal class MyClass, where within the class you can
put a static function called __callStatic(), which gets called only
when MyClass doesn't have a static function by the name you want.
i.e. I call MyClass::newFunction();
newFunction() is called statically but MyClass does not
have it declared. So, then __callStatic() gets called and
inside you can say
$myObject=new SomeOtherClass();
$myObject->newFunction();
which calls the function you wanted but on some other object.
Short Version:
In other words, __callStatic() does this:
MyClass::newFunction();
which is hiding this:
(new SomeOtherClass())->newFunction();
Say what now? What looks like code calling a static function from a class, turns out to be calling that function from some other class and calling it via instantiation, and not statically.
Explain this, please!
Why was it done? Can you do anything like this elsewhere, like C++ or Java? I am looking for short & concise, but informative explanation on static and dynamic functions in languages, and in this case whether __callStatic() violates or conforms to the big picture of Language constructs. Or is it a new language construct entirely.
__callStatic() provides developers with possibility to react on static method calls even if that methods don't exist or aren't accessible from outside of the class ( being protected). This is useful for dynamic, generic code generation.
Example: You have this class:
class Test {
protected static function myProtected($test) {
var_dump(__METHOD__, $test);
}
public static function __callStatic($method, $args) {
switch($method) {
case 'foo' :
echo 'You have called foo()';
var_dump($args);
break;
case 'helloWorld':
echo 'Hello ' . $args[0];
break;
case 'myProtected':
return call_user_func_array(
array(get_called_class(), 'myProtected'),
$args
);
break;
}
}
}
Try to call:
// these ones does not *really* exist
Test::foo('bar');
Test::helloWorld('hek2mgl');
// this one wouldn't be accessible
Test::myProtected('foo');
Why was it done?
This is an existential question, but I think that the answer is "because you can". PHP is a dynamic language and these constructs __call and __callStatic() show off its power, and this power can be useful in some situations.
Can you do anything like this elsewhere, like C++ or Java?
No. They have no similar magic methods.
I am looking for short & concise, but informative explanation on static and dynamic functions in languages
Static functions encapsulate code. They exist even when no class has been instantiated. You can call them using scope resolution operator.
Dynamic functions are well, dynamic
.. does __callStatic() violate or conform to the big picture of Language constructs. Or is it a new language construct entirely.
It is a construct of a dynamic language. Not sure if this functionality exists in all dynamic languages, but it does in PHP. It does not violate anything, just introduces new paradigm of fall-through catch-all functions when the function you do call does not exist/not accessible in current scope.
I'm not entirely sure why __callStatic() is relevant here?
I don't quite see the difference between:
class Foo {
static function bar() {
$obj = new Foo();
$obj->quux();
}
function quux() {
return "whatever";
}
}
and your example? In both scenarios you're calling a static method which is calling another method on an object of the same class.
Yeah, that's possible. Actually doing it suggests you might want to refactor your code though. In the example you're instantiating an object with its default state, executing a method on it and then throwing the object away. This suggests whatever the method is doing doesn't actually need the objects state. That means it either doesn't belong in this class or could simply be rewritten to be a static method itself.
And are you aware of __call? It does the same thing as __callStatic but for objects rather than classes. E.g. $foo->myMissingMethod(); would go to __call if such a method exists for the class of which $foo is an instance.

__call catches static method calls

I'm using both the magic methods _call and _callStatic for my own implementation of something like an ORM/Activerow. They're mainly meant for catching certain function calls: __call is responsible for getters and setters, and __callStatic for findBy methods (e.g. findById).
To map foreign keys, i'm trying to convert calls to e.g. getArticle to return the value of Article::findById(). To do that, i'm using this case inside my __call:
if (strstr($property, "_id")) {
return $foreignClass::findById($this->getId());
}
where $property is the substring after set or get in __call, and $foreignClass the rest of the string. So, in the case of the call getArticle, $property would be get and $foreignClass would be Article.
I've placed some echoes to ensure that the values are correct. However, my __call method gets called instead of my __callStatic. If i make an implicit static method findById, it does get called (so it does recognize it as a static call). If i specifically call Article::findById(), __call also catches it.
Is this an error with the relatively new __callStatic, or am i doing something wrong?
EDIT:
The problem seems to reside in this part:
_call() is triggered when invoking inaccessible methods in an object context.
__callStatic() is triggered when invoking inaccessible methods in a static context.
Though i am calling it on a class, i am calling it from an object context. Is there a way to get into the static context in this case?
Since the code you give runs in the context of an Activity object and since the value of $foreignClas is Article, which is an ancestor of Activity, PHP assumes that you are intending to call an ancestor's implementation of the method.
To break out of the object context there is AFAIK no option other than this absolutely hideous technique:
$id = $this->getById();
return call_user_func(
function() use($foreignClass, $id) {
return call_user_func("$foreignClass::findById", $id);
}
);
The __callStatic magic method was only introduced in PHP 5.3. Prior to that, I believe static calls were routed through __call just like normal method calls. My guess would be that you are using a PHP version that is < 5.3. What is the output of php -v on the command line?

prevents a class unsetting in php

I created a class implementing ArrayAccess and I added it a function to prevents WRITE actions:
$Obj->Add("key","something");
$Obj->Add("key2","something2");
$Obj->SetReadOnly(); // sets read only property
unset($Obj["key2"]); // throws error, object is readonly
But, i want to prevent unsetting object too:
unset($Obj);
I can do it?I hear suggestions.
Thanks for help!.
I can't imagine any situation where you would really want to do this. I can also imagine doing this will cause you serious problems at script termination when all objects are destroyed. The PHP manual says the following on throwing exceptions in destructors:
Note:
Attempting to throw an exception from a destructor (called in the time
of script termination) causes a fatal error.
The above statement implies that you can throw an exception if you're not in the script termination phase, so maybe the following is possible.
public function __destruct ()
{
if ($this -> isReadOnly ())
{
throw new Exception ('Class is read-only');
}
}
However, as the manual points out, this will trigger a fatal error during script shutdown.
I honestly can't see any point to wanting to prevent object destruction. It should be up to the programmer to manage object lifetimes.
unset() does not actually destruct an object, if that's what you're trying to prevent.
An object will only be destructed when all references to it have been unset or are no longer in scope. Even then it won't happen until the garbage collector runs.
So if you have some code that you are worried will molest your object, you've already done a good job of making it immutable with your read-only logic.
Let's say you have
$Obj = gotMyObjectSomehow();
and you need to pass it to a some other code you don't want to unset $Obj. As long as that code is called inside a function, there's nothing to be concerned about. If you call
someFunction($Obj);
and let's say that function unsets the parameter it's passed in
function someFunction($anObj) {
unset($anObj);
}
then your original $Obj variable will still be set.
The function creates a second variable referencing the original object and uses that in its own scope.
You can't control unsetting variable names, because those names are not technically a part of the object referenced. Consider the following:
$a = new MyObject();
$b = $a;
Now you have two references to the same object. There is no difference between using $a and $b, because in PHP objects are always used by reference (i.e. you don't have to do $b =& $a in the second line). So both $a and $b are essentially the same object; unsetting $a will not destroy the object, as well as unsetting $b won't destroy it; all references need to be unset before the object is destroyed.
I don't think you can do what you're asking for - it's not possible to prevent a variable being unset like that.
However, a comment of yours above set me thinking. You said:
.... idea if you want to prevent unsets system variables in a thirdparty extensions
So if I understand you right, your aim here is to ensure that while the thirdparty code (ie your software) is in use, all the variables associated with it remain in place?
Now you haven't specified much about what variables there are in this system. We see one object in the question, but presumably there must be more than that? I'm guessing you've got a bunch of things that tie together, right? It would help in these sorts of questions to provide a bit more context; the actual thing that you're asking for isn't possible, but with a bit of understanding about what you want to achieve, we could come up with alternatives.
Okay. So my suggestion: create your objects as Singletons. This is often frowned on by purists, but might work well for this situation, depending on exactly what you're doing. The beauty here is that you can encapsulate all access to the object inside class methods, meaning that the developer using your code doesn't have access to the master copy of the object in order to unset it.
A singleton works like this:
<?php
class mySingletonClass {
private static $masterObject=null;
public static function getInstance() {
if(!isset(self::$masterObject)) {
self::$masterObject = new self;
}
return self::$masterObject;
}
private function __construct() {
//your existing constructor, as it already exists, but marked as private.
}
//...and all the other methods as you already have them.
}
The class constructor method is private, so can only be accessed from methods within the class. Therefore, you can no longer do new classname(). The only way you can get an object of this class is to get it from the static getInstance() method. And the key thing here is that this method always returns the same copy of the object.
$obj = mySingletonClass::getInstance();
unset($obj);
$obj = mySingletonClass::getInstance(); //will give the exact same object as the first time.
You can unset it if you want, but the original object is still there and can still be accessed. Any of your other classes can use that getInstance() method to get the same copy of the same object from anywhere in the program. It's an indestructible global variable.
Singletons are often used for a program's main database connection object, but it might be a useful pattern for you here.
I hope that helps. It's about the only way I can think of to get close to what you want.

__get() example via Zandstra

Matt Zandstra gives the following example in his text "PHP Objects Patterns and Practice" to illustrate the __get() method:
class Person {
function __get( $property ) {
$method = "get{$property}";
if ( method_exists( $this, $method ) ) {
return $this->$method();
}
}
function getName() {
return "Bob";
}
function getAge() {
return 44;
}
}
In reality, we know we would never actually create such methods (getName and getAge) to return such static values, but instead - we would create name and age properties in the object and return those using the $this operator.
My question is whether this example actually shows utility. And if it does not, could somebody provide a better example of why one would use __get() for the same sort of purpose?
Justification for asking
If we were to use name and age properties in the object, then the __get() function would not be fired anyway, because attempting to get these properties with:
$person = new Person();
$name = $person->name;
would cause either the name property to actually be returned if it were public, or cause a visibility error if it were private or protected. The __get() function would not be executed in either of these 'real' cases... am i missing something?
I'm fully aware that the above code works.
I am not convinced that it is a practical example.
You are absolutely right - I am impressed that you are quoting from a book, that example just plain sucks.
Using the magic __get method to call methods is just wrong, there are other magic methods just for that kind of usage:
__call()
__callStatic()
__invoke()
__get() and __set() should be used to read and write non declared object properties.
The actual functionality of magical methods is totally up to the developer to handle in any way they see fit. The difference is in how they are invoked.
$obj->property; // invokes __get(), if defined
$obj->method(); // invokes __call(), if defined
$obj(); // invokes __invoke() if defined (php 5.3+)
etc. So if you want to have __get() call a method internally, you can. How you want to handle it depends entirely on how you want to design your application.
About __get(), if you have a public member and try to call it with ->, the __get() will not fire. However, if you try to access a non-accessible member (either protected, private, or nonexistent) __get() will fire and you can handle it how you like.
It is my understanding that the main purpose of __get() at least according to php is to eliminate the need to write getters for every member. Compare with __set() for setters. I don't want to get into this here, but I think the use of getters/setters should raise some red flags about design. However, __get()'s intended purpose does not have to be adhered to except for the standards of those developing the application.
Two examples of how __get() might be used are to initialize a property that may not be needed upon instantiation (e.g. by a DB query) thus reducing overhead. Another may be if you have properties stored in an array, magical get may return the value mapped to the key of the requested property.
With the php magic functions, you can do some very cool things and you can write code that is more extensible by using these special invocations. Something I like to do:
class application {
public function __call($_, $_) {
throw new Exception("PUT and DELETE not supported");
}
public function POST() {
//update data
header("Location: $_SERVER[PHP_SELF]", true, 303);
}
public function GET() {
//display data
}
public static function inst() {
return new self;
}
}
application::inst()->$_SERVER['REQUEST_METHOD']();
Of course, this is using __call() rather than __get(), but the principle's the same. In this case it's just a sanity check.
As php.net states
"__get() is utilized for reading data
from inaccessible properties."
This means that __get() (if it's defined) is called whenever you try to access a property that does not exist or is private / protected. The above example shows a way for getting the values of those private properties by calling their accessors (getters) whenever someone tries to get the values.
For example, calling
echo $a -> publicPropery or echo $a -> getPublicProperty()
would result in the same thing. However if you call $a -> privateProperty you would get an error, and
$a -> getPrivateProperty()
would be OK.
But, if you defined __get() whenever you call $a -> privateProperty, __get() is executed and redirected to $a -> getPrivateProperty(). In this case you would always succeed in getting the value and still keeping security in mind. Even more, since you check for the existance of the property getter in __get() you could show an appropriate error or throw an exception if someone tries to access unexisting properties, which will override php's visibility errors.
<?php
class data {
public $name="Ankur DalsaniYa"; \\ I wont to change name
public function __get($var) {
print "Attempted to retrieve '<b>$var</b>' and failed...\n";
//$var is print the Unknown and Not Define but call variable print
}
}
$data = new data;
echo "<br> Not Define Variable Call : ";
print $data->title; // Not Defined variable Call then call __get function
echo "<br><br> Define Variable Call : ";
print $data->name; // Define variable Call then Not call __get function
?>

Is there a better way to block property overloading?

<?php
class Item {
public function __set($name, $value){
throw new Exception('Overloading is forbidden.');
}
public function __get($name){
throw new Exception('Overloading is forbidden.');
}
}
I want to make sure I set all required object properties, but if I mistype a property name PHP just adds a new property and the prop I wanted to initialize remains null.
From your comment:
I want to make sure I set all required object properties, but if I mistype a property name PHP just adds a new property and the prop I wanted to initialize remains null.
If you want to make sure you have set all required object properties, consider making them required arguments to the constructor, e.g.
class Item
{
public function __construct($required, $alsoRequired, $moreRequired)
{
// assign to corresponding properties
}
}
This way you can be sure the object is in valid state.
If you cannot set them at object creation, consider adding a validator method to your object. Call this method before doing any critical things with your object to make sure all required properties are set. This would be a much more useful addition to your API than implementing a typo safeguard.
In addition, you should not have an issue with this if you use proper setters. Access your object through an interface instead of assigning properties directly. If you mistype a method name, PHP will tell you (unless you implemented __call).
On a sidenote, __get and __set are not lazy replacements for proper Getters and Setters. They are interceptors that will be triggered when trying to access a non accessible property. This is much more related to error handling. Also note that they are much slower than proper Getter and Setter.
Just using the constructor will not solve the problem.
The problem he describes is very common and throwing an exception if __get or __set are called is a nice idea.
If you want to, you can make those final but I see no reason why you should do this. Only classes that require dynamic propertys will override the methods.
The only improvement I can suggest is enclosing the method definitions in
//DEBUG
and
///
When you are finished debugging you can then easily run a replace(//*DEBUG with /*DEBUG) on all your code files and comment out __get and __set
Using http://php.net/manual/en/function.get-class-vars.php you can check for existence of the variable and for example throw exception when fails

Categories