Overloading in php - php

Can any one tell what does below given lines means?
The lines below are copied from PHP manual:
Note:
It is not possible to use overloaded properties in other language
constructs than isset(). This means if empty() is called on an
overloaded property, the overloaded method is not called.
To workaround that limitation, the overloaded property must be copied
into a local variable in the scope and then be handed to empty().
BUT this is not true that we cant call empty() on overloaded properties, when i called empty() , it triggered __isset()

It is a documentation bug:
<?php
class PropNameReturn {
function __isset($propname){
return true;
}
function __get($propname){
echo 'Yes, it IS called!'.PHP_EOL;
return $propname;
}
}
$o = new PropNameReturn();
var_dump(empty($o->this_prop_name));
//Yes, it IS called!
//bool(false)
$b = new stdClass();
var_dump(empty($b->this_prop_name));
//bool(true)

Looks like the manual is wrong. This also works in PHP 5.2
By the way it seems that __get() is called when used by empty(), but the result also depends on __isset().
empty() returns true only if __isset() returns true and __get() returns an empty value.
See this example code:
class Foo {
function __get($name) {
if ($name == "bar")
return '';
}
function __isset($name) {
if ($name == "bar")
return true;
}
}
$foo = new Foo();
echo $foo->bar . PHP_EOL; // outputs "" - correct
echo var_export(isset($foo->bar)) . PHP_EOL; // outputs "true" - correct
$bar = $foo->bar;
// outputs both "true" -- so the manual is wrong here
echo var_export(empty($foo->bar)) . PHP_EOL;
echo var_export(empty($bar)) . PHP_EOL;
Also see this bug report

Apparently I didn't notice the link you included in your own question, so here's a basic breakdown of overloading:
Overloading in PHP is the concept of dynamically creating methods and parameters. Consider a basic scenario:
class MyClass {
public $foo = 'bar';
}
$Instance = new MyClass();
echo $Instance->foo;
We would all expect that to print "bar," and it would. Here is that same example using overloding:
class MyOverloadedClass {
public function __get($variable) {
return $variable == 'foo' ? 'bar' : '';
}
}
$Instance = new MyOverloadedClass();
echo $Instance->foo;
In this second example, because MyOverloadedClass::$foo does not exist, the __ge method will be executed, and will be passed the variable that the user asked for (in this case, "foo"). My code inside of the __get method would determine that the user is looking for foo, and return "bar"
Language constructs are parts of the PHP language. In other words, the parser interprets them and knows what to do, without having to lookup function references. Some examples are exit, die, print, echo, isset and empty.
isset() is the only language construct to work with overloading. The manual page describes an overloaded method __isset which you can add to your class, and will be executed every time someone calls isset on a property of that class. Calling empty() on an overloaded property, however, will always return true and likely throw an E_NOTICE. Here's why:
Disclaimer: calling empty() on an overloaded property DOES seem to execute __get(), IF AND ONLY IF you have an __isset() method that returns true for the passed variable name:
empty() will not call your overloaded __get() method. It will look in the class definition to see if that parameter exists. If it does, it'll evaluate whether or not it is empty. However, accessing undefined class parameters throws an error. In my first example, I would have thrown an error by asking for $Instance->bar, because it doesn't exist. This is the same error you would get if you asked for
empty( $Instance->foo );
In the second example. I hope this helps.
See the page you mentioned for additional details on how to apply this to functions, or setting variables.

Related

Call a variable function using a class property as function name

The following code uses the string "rand" stored in the property $prop to call rand() as a variable function, by using $function as a temporary local variable.
class C
{
private $prop = "rand";
public function execute()
{
$function = $this->prop;
echo $function();
}
}
$c = new C();
$c->execute();
This works, but I need to call the variable function stored in $this->prop using only one statement and avoiding the temporary variable.
I had no luck with
echo $this->prop();
because it actually calls the method prop() which does not exist and in any case it is not what I want to do.
As $this->prop is actually a string, I tried the following, but it produces a syntax error:
echo ($this->prop)();
I also tried
echo call_user_func($this->prop);
Although it does the work, it is not an option for me because it is not a variable function.
It seems like variable functions only work using local variables as function name.
Does anybody know a way to call directly a variable function using a class property as function name, avoiding the local temporary variable and the usage of call_user_func()?
Edit:
I understand your perplexity, therefore I'm going to explain what's wrong with using call_user_func.
I'm just exploring the opportunities offered by variable functions, which seems to be less then those offered by variable variables.
Let's try using variable variables feature it its simplest form.
Suppose we have a function f() which returns the string "something"
function f() {
return "something";
}
then a class property containing the string "something"
$this->prop = "something";
$something is a local variable
$something = "I am a local variable";
Then all the following statements will work:
$r = ${"something"};
$r = ${$this->prop};
$r = ${f()};
My personal conclusion: No matter how the string "something" has been obtained; just surround it with braces {} and prepend a dollar symbol $ to consider it a variable.
Pretty flessibe.
Let's try the same for variable functions
Now we have a function f() which returns the string "rand"
function f() {
return "rand";
}
then a class property containing the string "rand"
$this->prop = "rand";
Variable functions on the other hand, does not allow a string followed by parenthesis () to be considered a function call.
$r = "rand"(); // Produces a syntax error, unexpected '()' after a string
$r = $this->prop(); // Calls the 'prop()' method, which does not exist
$r = f()(); // Again a syntax error, unexpected '()' after the function f()
I have to conclude that variable functions always require a local variable to be run :(
You need to implement a magic __call method, like this:
class C
{
private $prop = "execute";
public function __call($method, $args)
{
if($method == "prop") // just for this prop name
{
if(method_exists($this, $this->prop))
return call_user_func_array([$this, $this->prop], $args);
}
}
public function execute ($s){
echo '>>'.$s.'<<';
}
}
$c = new C;
$c->prop(123);
It certainly does feel like a glaring omission in PHP's syntax. (Although taken literally I guess they are variable functions, not property functions!?) I would have perhaps expected the following "curly brace" syntax to work, but it doesn't, Parse error: syntax error, unexpected '{' in ....
echo {$this->prop}();
However, there are significant benefits to using variable function syntax over other methods. Variable functions are quicker than call_user_func() / call_user_func_array() and natively support pass-by-reference, rather than the "special-case" call-time pass-by-reference with call_user_func_array() (which is deprecated in all other cases).
An alternative to the __call magic method (above), which is going to be relatively slow, is to simply use a wrapper method, to which you pass the function/method name and use variable functions inside that wrapper method.
In its most simplest form:
function callUserFunc($callable) {
return $callable();
}
Because of the performance benefit (over using call_user_func_array()) several frameworks implement a similar "helper" method, allowing for a variable number of arguments. This other question/answer goes into more depth and covers some performance benchmarks: Calling a function with explicit parameters vs. call_user_func_array()
In case anyone is wondering, since PHP 7 we get immedietally invoked function expressions.
While this particular case is undocumented it actually works in the following example:
class Test {
private $func = "strtolower";
public function testFunc() {
return ($this->func)("ALPHABET");
}
}
$t = new Test();
echo $t->testFunc(); //echoes alphabet in PHP 7+ error in anything below
This can be seen in https://3v4l.org/JiuIF

PHP Magic Methods with unexpected behaviour

<?php
class MyClass{
private $props;
public function __constructor(){
$this->props = array();
}
public function __get($field) {
return $this->props[$field];
}
public function __set($field, $value) {
$this->props[$field] = $value;
}
}
$myInstance = new MyClass();
$myInstance->a = "Some string";
echo "Property 'a' has value ",$myInstance->a,"<br />"; // First echo
echo "Is property 'a' empty? ",(empty($myInstance->a) ? "Yes, but it shouldn't!" : "No, as expected"); // Second echo
// Output:
// Property 'a' has value Some string
// Is property 'a' empty? Yes, but it shouldn't!
$temp = $myInstance->a;
echo 'Variable $temp has value ',$temp,"<br />";
echo 'Is variable $temp empty? ',(empty($temp) ? "Yes, but it shouldn't!" : "No, as expected");
// Output:
// Variable $temp has value Some string
// Is variable $temp empty? No, as expected
?>
I've made this small class and code to ilustrate exactly what is my problem.
As you can see, my class has one property called $props. This property will be a array with all other properties.
My array $props will be populated with Magic Method __set, when a instance of this class has an atribution at a nonexistent property (http://php.net/manual/en/language.oop5.magic.php).
This part works properly.
When I want to read a nonexistent property, PHP should call MyClass's __get method and return the value stored in $props property.
It works fine in first echo (value is correctly returned).
But, if I use this reading action when I'm sending its return to one empty() function, it always returns TRUE, as you can see in second echo's return (in other words, php seems can't read $props property properly)
If I store property's value in a $temp variable and use this variable, my problem is solved, BUT I don't want to just solve this problem. I want to understand what is happening here.
Is it a PHP bug on Magic Methods?
Is it a empty funcion bug?
Is my code doing something meaningless? (Most probable answer)
Before you ask: I'm running my php server on a Windows machine and my php version is 5.3.8
Thanks in advance.
Because empty check if a variable exists and then if it is empty. If it doesn't exists (in your case because you're not passing it a variable), then it returns true
Checkout the doc page: http://php.net/manual/en/function.empty.php

Passing a function by reference

Is it possible to pass a function by reference? So everytime the reference variable is called the function will be called aswell. Take a look at my code.
<?php
class Documents {
private static $docs = array(
'She went to the toilet and on her way back, opened the wrong door',
'She had found something that would mean she\'d never be poor again - but there was a catch',
'It was just for one night',
'He watched, helpless, as the door closed behind her'
);
public static function get() {
return self::$docs[array_rand(self::$docs)];
}
}
class Printer {
public static $content;
}
Printer::$content = &Documents::get();
echo Printer::$content;
echo "\n" . Printer::$content;
echo "\n" . Printer::$content;
Right now it'll print 3 similar lines but i would like it to call Documents::get() everytime Printer::$content is printed because Printer::$content = **&**Documents::get(); it is by reference.
No, you cannot have a variable which you treat as a variable which nonetheless runs code behind the scenes. If you write $foo, that's using the value of a variable. Only of you write $foo() are you explicitly executing a function.
Having said that, there are some situations in which object methods will be called implicitly. For instance, if $foo is an object and you use it in a string context:
echo $foo;
This will (try to) implicitly call $foo->__toString().
Please do not get the idea to somehow abuse this implied method call to do anything fancy. If you want to call functions, call functions. You can even return functions from other functions, so there's no lack of possibility to pass around functions. You will have to call them explicitly with () however.
There are variable functions:
php > function foo($bar) { echo $bar; }
php > $baz = 'foo';
php > $baz('qux');
qux
But you cannot have PHP automatically execute that "referenced" function when the variable is simply accessed, e.g:
php > $baz;
php >
As you can see, the foo function was not called, and no output was performed. Without the () to signify a function call, that variable is like any other - it's just a string whose contents happen to be the same as a particular functions. It's the () that makes the string "executable".
Note that variable functions, while useful in some limited circumstances, should be avoided as they can lead to spaghetti code and difficult-to-debug bugs.
You can use the magic method __get().
class Printer {
public function __get($name)
{
switch($name) {
case "content":
return Documents::get();
break;
default:
return $this->$name;
break;
}
}
}
But this cannot be done in static context. So you would have to have an instance of Printer. (Perhaps use a singleton?)

Variable assignment in function call

I inherited a php codebase that contains some variable assignments in function calls:
<?php
function some_func($foo, $state) {
....
}
some_func("random stuff", $state = true);
...
some_func("other stuff", $state = false);
...
?>
I did some research and some tests, but I can't find out what the defined behaviour for this code is in PHP.
How is the value of the second argument to some_func() computed? The content of the 4state variable (true on first call, false on second)? Or is it the outcome of the assignment (i.e. assigning true/false to the variable $state was successful, so some_func received true?
What is the value of the $state variable in the global scope? The result of the assignment, i.e. true after the first call, false after the second?
I too had to work with a codebase that had function calls similar to this. Luckily, I had access to developers that wrote the code. Here is what I learned.
Scenario 1:
Simply a way to document the code. You know the variable name that you are passing into the function.
Scenario 2:
Here is a link: http://www.php.net/manual/en/language.references.pass.php
If you see, they do specifically call out your case:
foo($a = 5); // Expression, not variable
A 'dummy' pass-by-ref. Depending on your version of PHP, it may throw a warning. I was getting this: Strict Standards: Only variables should be passed by reference in ...
Now let me go into detail of what is happening in this situation.
The dangerous thing is that your example that you have provided wont display the "gotcha!" behavior. In a case like this, your $arg2 that you are echoing outside of the function will always be what the expression in the function call set it to be. Furthermore, the function that is being called will also be sent a "copy" of that value, and work with that. I say "copy" because even though the function is requiring a pass-by-ref, it is actually getting a copy, similar to what a normal function parameter would get.
If you modify the $arg2 that is inside of the function it WILL NOT modify the $arg2 that is outside of the function, as you would expect from a function that is pass-by-ref.
To assign a variable at function call time, you have to pass it as a reference (&$var):
function my_function($arg1, &$arg2) {
if ($arg1 == true) {
$arg2 = true;
}
}
my_function(true, $arg2 = false);
echo $arg2;
outputs 1 (true)
my_function(false, $arg2 = false);
echo $arg2;
outputs 0 (false)
How is the value of the second argument to some_func() computed?
It's not "computed" but explicitly setup : $state = true / false and then passed as argument to some_func().
What is the value of the $state variable in the global scope?
$state does not exist in the global scope.

PHP returning by reference not working with normal functions but working with OOP

If I try this code :
<?php
class ref
{
public $reff = "original ";
public function &get_reff()
{
return $this->reff;
}
public function get_reff2()
{
return $this->reff;
}
}
$thereffc = new ref;
$aa =& $thereffc->get_reff();
echo $aa;
$aa = " the changed value ";
echo $thereffc->get_reff(); // says "the changed value "
echo $thereffc->reff; // same thing
?>
Then returning by reference works and the value of the object property $reff gets changed as the variable $aa that references it changes too.
However, when I try this on a normal function that is not inside a class, it won't work !!
I tried this code :
<?php
function &foo()
{
$param = " the first <br>";
return $param;
}
$a = & foo();
$a = " the second <br>";
echo foo(); // just says "the first" !!!
it looks like the function foo() wont recognize it returns by reference and stubbornly returns what it wants !!!
Does returning by reference work only in OOP context ??
That is because a function's scope collapses when the function call completes and the function local reference to the variable is unset. Any subsequent calls to the function create a new $param variable.
Even if that where not the case in the function you are reassigning the variable to the first <br> with each invocation of the function.
If you want proof that the return by reference works use the static keyword to give the function variable a persistent state.
See this example
function &test(){
static $param = "Hello\n";
return $param;
}
$a = &test();
echo $a;
$a = "Goodbye\n";
echo test();
Echo's
Hello
Goodbye
Does returning by reference work only in OOP context ??
No. PHP makes no difference if that is a function or a class method, returning by reference always works.
That you ask indicates you might have not have understood fully what references in PHP are, which - as we all know - can happen. I suggest you read the whole topic in the PHP manual and at least two more sources by different authors. It's a complicated topic.
In your example, take care which reference you return here btw. You set $param to that value - always - when you call the function, so the function returns a reference to that newly set variable.
So this is more a question of variable scope you ask here:
Variable scope

Categories