I get this error "Function name must be a string" - php

I have a PHP class where one of the private member is a callback to my log function (i.e. in PHP land, a function pointer is simply a string containing the name of the function to call).
self::$logferr = "msgfunc";
self::$logferr($res);
I get this error:
Fatal error: Function name must be a string
self::$logferr is equal to "msgfunc" which is my log function.
If I rewrite the code like this (on the same very class method):
$tmp = "msgfunc";
$tmp($res);
It works, the log function get called

Just wrap your variable in parenthesis, let PHP resolve the value first:
(self::$logferr)($res);
Proof of concept

You can use call_user_func. ref: this
call_user_func(self::$logferr, $res);

You should call it by using
self::{self::$logferr}($req)
Working example : https://3v4l.org/CYURS

Let's build a reproducible example:
class Foo {
private static $_loggerCallback;
static function setLogCallback(callable $loggerCallback) {
self::$_loggerCallback = $loggerCallback;
}
static function log(...$arguments) {
if (NULL !== self::$_loggerCallback) {
return self::$_loggerCallback(...$arguments);
}
return NULL;
}
}
Foo::setLogCallback(function() { echo 'success'; } );
Foo::log();
Output:
Notice: Undefined variable: _loggerCallback in /in/f3stL on line 13
Fatal error: Uncaught Error: Function name must be a string in /in/f3stL:13
The notice reports the actual mistake in this case. If you do not get something like it, you should check your error reporting configuration.
The notice shows that PHP looks for a local variable $_loggerCallback. It tries to execute $_loggerCallback(...$arguments). Here are different possibilities to make the call explicit.
Use parenthesis (PHP >= 7.0):
return (self::$_loggerCallback)(...$arguments);
Use a local variable (as you did):
$callback = self::$_loggerCallback;
return $callback(...$arguments);
A small advise. PHP support anonymous functions. You do not need a (global) function for a callback. This avoids calling to the function by name as well.

Related

"PHP Fatal error: Constant expression contains invalid operations", when initializing a static inline?

I am wondering why is it that my static variable initialization does not work with the following declaration:
function validate()
{
static $timezones = DateTimeZone::listIdentifiers(); // Error here
...
}
The line with static ... generates the error:
PHP Fatal error: Constant expression contains invalid operations
If I do the following, though, it works as expected:
function validate()
{
static $timezones = null;
if(!isset($timezones))
{
$timezones = DateTimeZone::listIdentifiers();
}
...
}
So I have a way around the problem, but I am wondering why is it that the first method fails?
Based on php documentation; You cannot initialize static variable with another non constant expression or variable.
Which means if you want to assign a value to static variable this value should be a an integer, string etc.
What you did here is against static word rule in PHP you are assigning a dynamic value to $timezones variable
static $timezones = DateTimeZone::listIdentifiers(); // Error here
Check for detailed information.
http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static

Not defined Substr in PHP

I tried substr() method in PHP 5.6 to get some part of String,
first,
<?php
echo substr("Avicienna", 0,3);
and save it to a file.
second one,
<?php
class Test{
public function index(){
$name = "Hasan";
var_dump(subtr($name,0,3));
}
}
$test = new Test();
$test->index();
and save it to another file.
the first one without class, return correct string parts, while, the second one return PHP 500 error :
Call to undefined method Coba::subtr() in /var/www/html/koper/coba.php on line 5
is there any limitation to call substr() or others php function inside a class ?
Typo error subtr() . Its substr().

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

attempt to use array_map in a class is resulting in a warning

A new class has been created for a PHP project as follows:
class Cleanse
{
# trims leading and trailing spaces
public static function trimmer($values)
{
return is_array($values) ?
array_map('trimmer', $values) :
trim($values);
}
}
However, when trying to use this functionality like so:
$values = Cleanse::trimmer($_POST);
the following warning message is returned:
Warning: array_map() expects parameter 1 to be a valid callback, function 'trimmer' not found or invalid function name in (class file path) on line 41.
What is wrong with this code and/or this approach?
As trimmer is a static method of Cleanse, it should be
array_map('Cleanse::trimmer', $values) // PHP >= 5.2.3
or
array_map(array('Cleanse', 'trimmer'), $values) // PHP < 5.2.3
See Callbacks for the correct syntax to use for callbacks.

Only variables should be passed by reference

I have a class:
class Validator {
private $validationArray;
private $cleanedValues;
public function __construct($arg1, $arg2=NULL) {
if(empty($arg2)) {
$this->LoadValidatorByName($arg1);
} else {
$this->LoadValidatorFromLeadType($arg1, $arg2);
}
}
private function LoadValidatorFromLeadType($lead_type, $vocabulary) {
$ErrorReporter = new ErrorReporter;
$taxonomy_term = reset(taxonomy_get_term_by_name($lead_type, $vocabulary));
...some more stuff
The function taxonomy_get_term_by_name is a Drupal function but the issue I am experiencing is a PHP one.
When this method is called PHP complains with:
Strict warning: Only variables should be passed by reference in Validator->LoadValidatorFromLeadType() (line 32 of [path to my file])
Line 32 is ths line with:
$taxonomy_term = reset(taxonomy_get_term_by_name($lead_type, $vocabulary));
I've looked in to the error and I'm pretty sure I know what it means, but I can't understand what is wrong with my code that causes this warning.
reset is waiting for a variable reference. You are passing it a function result...
$taxonomy_term = taxonomy_get_term_by_name($lead_type, $vocabulary);
$taxonomy_term = reset($taxonomy_term );
This mean that only variable should be passed by reference, not an expression.
reset($array_variable); // correct
and
reset(some_function_that_returns_array()); // incorrect
If you take a second and think about it more - you would find that reset() with expression (not a variable) makes no sense, because you've rewind the array pointer to the beginning, but you don't have the ability to access that array anymore.
You should only reset a variable (which is passed by reference), not a return value of a function.
see: http://www.php.net/reset

Categories