Has the function call_user_func_array() changed in PHP 5.3 ? Because I have module that has several calls of this function, but it doesn't work after upgrade to PHP 5.3. I traced the code and it seems it doesn't call this function.
Should I change it?
Edited:
some cuts of codes:
function complete($message, $endpoint, $return_to)
{
$mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode',
'<no mode set>');
$mode_methods = array(
'cancel' => '_complete_cancel',
'error' => '_complete_error',
'setup_needed' => '_complete_setup_needed',
'id_res' => '_complete_id_res',
);
$method = Auth_OpenID::arrayGet($mode_methods, $mode,
'_completeInvalid');
$method = '_complete_id_res';
return call_user_func_array(array(&$this, $method),
array($message, $endpoint, $return_to));
}
/**
* #access private
*/
function _complete_id_res($message, &$endpoint, $return_to)
{
$user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS,
'user_setup_url');
if ($this->_checkSetupNeeded($message)) {
return new Auth_OpenID_SetupNeededResponse(
$endpoint, $user_setup_url);
} else {
return $this->_doIdRes($message, $endpoint, $return_to);
}
}
if I put die('*'); command in the second function,it doesn't die that show it doesn't enter it.
why not check that on php.net ?
Changelog
Version Description
5.3.0 The interpretation of object oriented keywords like parent and self has
changed. Previously, calling them using the double colon syntax would emit an
E_STRICT warning because they were interpreted as static.
Yes, it has changed.
5.3.0: The interpretation of object oriented keywords like parent and self has changed. Previously, calling them using the double colon
syntax would emit an E_STRICT warning because they were interpreted as
static.
The problem is solved now. It was because of defining function parameters with reference(&) but the calling was with a value. I remove the '&' from function definition and it works well.
It is solved now. The problem was function parameters that were defined by reference (&) in the function but were passed with value. I removed & from them and the problem was resolved.
Related
I'm trying to directly call a function from a static property inside a class.
Here is an extract of my class:
class Uuid {
const VERSION_3 = 3;
const VERSION_5 = 5;
protected static $hash_function = [
self::VERSION_3 => 'md5',
self::VERSION_5 => 'sha1',
];
protected static function get_hash_value($value_to_hash, $version) {
// None of these work:
//$hash = self::$hash_function[$version]($value_to_hash);
//$hash = (self::$hash_function[$version])($value_to_hash);
//$hash = (self::$hash_function)[$version]($value_to_hash);
// Only this works:
$function = self::$hash_function[$version];
$hash = $function($value_to_hash);
return $hash;
}
}
The only way I've found of making it work so far is to store the function name in a temporary variable ($function) before calling it. I've tried wrapping the expression (or bits of the expression) in braces, ({,}), parentheses ((,)), prefixing a $, etc. but so far nothing has worked.
Is there a simple way of doing this without a temporary variable? If so, what is the minimum version of PHP this works for?
Yes, as you've discovered you need to store the function name as a complete string as a simple variable to be able to invoke it. The documentation for this functionality can be found at http://php.net/manual/en/functions.variable-functions.php
http://php.net/manual/en/function.call-user-func.php is another alternative.
call_user_func( static::$hash_function[$version], $value_to_hash );
See also is_callable(), call_user_func(), variable variables and function_exists().
I am experiencing an odd problem with using array_walk with closures within a class. The problem does not arise within my development environment using php version 5.4.7 but it does on my deployment environment 5.3.3.
The following code runs fine on my production box, but crashes on my deployment environment:
<?php
error_reporting(-1);
Class TestArrayWalk
{
/** #var null|array */
protected $userInput = null;
/**
* This expects to be passed an array of the users input from
* the input fields.
*
* #param array $input
* #return void
*/
public function setUserInput( array $input )
{
$this->userInput = $input;
// Lets explode the users input and format it in a way that this class
// will use for marking
array_walk( $this->userInput, function( &$rawValue )
{
$rawValue = array(
'raw' => $rawValue,
'words' => $this->splitIntoKeywordArray( $rawValue ),
'marked' => false,
'matched' => array()
);
}
);
}
public function getUserInput()
{
return $this->userInput;
}
protected function splitIntoKeywordArray( $input )
{
if ( ! is_string( $input )){ return array(); }
return preg_split('/(\s|[\.,\/:;!?])/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
}
}
$testArrayWalk = new TestArrayWalk();
$testArrayWalk->setUserInput(
array(
'This is a test input',
'This is another test input'
)
);
var_dump( $testArrayWalk->getUserInput() );
The error I am getting is: Using $this when not in object context on line 26 which is the only usage of $this within that test class. I am assuming something changed between the versions I am using that has made the above code possible in my development environment.
I also assume that as I cant change the deployment environment (its the clients and they wont change it) that I am going to have to use a foreach rather than array_walk.
My question is this: Given the above, is this possible in 5.3.3 using array_walk if not how do I use foreach in the same way that I am using array_walk ( more specifically the &$rawValue bit)?
My environments are:
My development environment is PHP version 5.4.7
My server (deployment) environment is PHP version 5.3.3
Thanks.
Edit 2
Thanks to everyone who helped. With your help I got this working and have posted my working code to https://gist.github.com/carbontwelve/6727555 for future reference.
This is described in PHP manual:
Version Description
5.4.0 $this can be used in anonymous functions.
Anonymous functions
Possible workaround would be to re-assign this to another variable and pass it via use:
$_this = $this;
function() use($_this) { ... }
but keep in mind you will not be able to access private and protected members so you will have to make splitIntoKeywordArray public
You may also use if you are using PHP < 5.4
public function setUserInput( array $input )
{
$this->userInput = $input;
$userInput_array = &$this->userInput;
array_walk( &$userInput_array, function( &$rawValue ) use (&$userInput_array) {
// use $userInput_array instaed of $this->userInput_array
});
}
I had a similar problem with a lambda function defined in a private function within a class:
MyClass{
private $str="doesn't work :-(";
private function foo(){
$bar=function(){
echo $this->str; // triggers an "Using $this when not in object context" error
};
$bar();
}
}
Solution for PHP 5.3.0: declare a variable $obj=&$this in your parent scope (ie. the private function) and pass it to the anonymouse function by using the use language construct. Also make sure the function/variable that you access has a public visibility (protected|private might not work).
MyClass{
public $str="it just works :-)";
private function foo(){
$obj=&$this;
$bar=function() use(&$obj){
echo $this->str; // it works!
};
$bar();
}
}
When I execute following code I am getting this error. Why is that? What is the proper use of callbacks?
CODE (simplified)
class NODE {
//...some other stuff
function create($tags, $callback=false) {
$temp = new NODE();
//...code and stuff
if($callback) $callback($temp); //fixed (from !$callback)
return $this;
}
}
$document = new NODE();
$document->create("<p>", function($parent) {
$parent->create("<i>");
});
ERROR
Fatal error: Function name must be a string in P:\htdocs\projects\nif\nif.php on line 36
$document->new NODE();
This is not valid syntax. The accepted format would be:
$document = new NODE();
In addition to this, if you use the unary operator (!) on a false, you get true. If you use it on a Callable, you get false. As such, if (!$callback) $callback() will throw the first error of your script.
As a side note, you are reinventing the wheel. I would strongly recommend you take a look at the DOMDocument family of classes, which are doing exactly what you are currently trying to implement, albeit with fewer callbacks.
if(!$callback) $callback($temp);
If $callback is false, for sure you won't be able to call it as a callback.
if(!$callback) $callback($temp);
should probably be
if($callback) $callback($temp);
And the instanciation:
$document = new NODE();
My 2c here, type hinting may be good to use here as well.
Ex: function create($tags, callable $callback = function())
To do such a thing in php you should use function pointers and tell php which function to execute.
Look at this code.
// This function uses a callback function.
function doIt($callback)
{
$data = acquireData();
$callback($data);
}
// This is a sample callback function for doIt().
function myCallback($data)
{
echo 'Data is: ', $data, "\n";
}
// Call doIt() and pass our sample callback function's name.
doIt('myCallback');
So as you seen you can only pass the name to the function and you should predefine the function..
Similar question: How do I implement a callback in PHP?
this time, I'm facing a really weird problem. I've the following code:
$xml = simplexml_load_file($this->interception_file);
foreach($xml->children() as $class) {
$path = str_replace('__CLASS_DIR__',CLASS_DIR,$class['path']);
if(!is_file($path)) {
throw new Exception('Bad configuration: file '.$path.' not found');
}
$className = pathinfo($path,PATHINFO_FILENAME);
foreach($class as $method) {
$method_name = $method['name'];
$obj = new $className();
var_dump(in_array($method_name,get_class_methods($className)));exit;
echo $obj->$method_name();### not a method ???
}
}
As you can see, I get the class name and method name from an XML file.
I can create an instance of the class without any problem. The var_dump at the end returns true, that means $method_name (which has 2 optional parameters) is a method of $className.
BUT, and I am pretty sure the syntax is correct, when I try: $obj->$method_name() I get:
Fatal error: Method name must be a string
If you have any ideas, pleaaaaase tell me :)
Thanks in advance,
Rolf
The issue you are having is probably that $method_name is not a string, but it contains a method to convert it to a string (__toString()).
As in_array by default don't do strict type comparisons you will find that $method_name is probably coveted to a string and then compared with the method names, which would explain why the var_dump outputs true.
You should be able to confirm this by checking the type of $method_name
echo gettype($method_name);
If it isn't a string the solution is to case the variable to a string and then use that to call the function.
$obj->{(string)$method_name}();
It's better to use the call_user_func function instead of $obj->$method_name() to call the method.
echo call_user_func(array($className, $method_name));
How are callbacks written in PHP?
The manual uses the terms "callback" and "callable" interchangeably, however, "callback" traditionally refers to a string or array value that acts like a function pointer, referencing a function or class method for future invocation. This has allowed some elements of functional programming since PHP 4. The flavors are:
$cb1 = 'someGlobalFunction';
$cb2 = ['ClassName', 'someStaticMethod'];
$cb3 = [$object, 'somePublicMethod'];
// this syntax is callable since PHP 5.2.3 but a string containing it
// cannot be called directly
$cb2 = 'ClassName::someStaticMethod';
$cb2(); // fatal error
// legacy syntax for PHP 4
$cb3 = array(&$object, 'somePublicMethod');
This is a safe way to use callable values in general:
if (is_callable($cb2)) {
// Autoloading will be invoked to load the class "ClassName" if it's not
// yet defined, and PHP will check that the class has a method
// "someStaticMethod". Note that is_callable() will NOT verify that the
// method can safely be executed in static context.
$returnValue = call_user_func($cb2, $arg1, $arg2);
}
Modern PHP versions allow the first three formats above to be invoked directly as $cb(). call_user_func and call_user_func_array support all the above.
See: http://php.net/manual/en/language.types.callable.php
Notes/Caveats:
If the function/class is namespaced, the string must contain the fully-qualified name. E.g. ['Vendor\Package\Foo', 'method']
call_user_func does not support passing non-objects by reference, so you can either use call_user_func_array or, in later PHP versions, save the callback to a var and use the direct syntax: $cb();
Objects with an __invoke() method (including anonymous functions) fall under the category "callable" and can be used the same way, but I personally don't associate these with the legacy "callback" term.
The legacy create_function() creates a global function and returns its name. It's a wrapper for eval() and anonymous functions should be used instead.
With PHP 5.3, you can now do this:
function doIt($callback) { $callback(); }
doIt(function() {
// this will be done
});
Finally a nice way to do it. A great addition to PHP, because callbacks are awesome.
Implementation of a callback is done like so
// This function uses a callback function.
function doIt($callback)
{
$data = "this is my data";
$callback($data);
}
// This is a sample callback function for doIt().
function myCallback($data)
{
print 'Data is: ' . $data . "\n";
}
// Call doIt() and pass our sample callback function's name.
doIt('myCallback');
Displays: Data is: this is my data
One nifty trick that I've recently found is to use PHP's create_function() to create an anonymous/lambda function for one-shot use. It's useful for PHP functions like array_map(), preg_replace_callback(), or usort() that use callbacks for custom processing. It looks pretty much like it does an eval() under the covers, but it's still a nice functional-style way to use PHP.
well... with 5.3 on the horizon, all will be better, because with 5.3, we'll get closures and with them anonymous functions
http://wiki.php.net/rfc/closures
You will want to verify whatever your calling is valid. For example, in the case of a specific function, you will want to check and see if the function exists:
function doIt($callback) {
if(function_exists($callback)) {
$callback();
} else {
// some error handling
}
}
create_function did not work for me inside a class. I had to use call_user_func.
<?php
class Dispatcher {
//Added explicit callback declaration.
var $callback;
public function Dispatcher( $callback ){
$this->callback = $callback;
}
public function asynchronous_method(){
//do asynch stuff, like fwrite...then, fire callback.
if ( isset( $this->callback ) ) {
if (function_exists( $this->callback )) call_user_func( $this->callback, "File done!" );
}
}
}
Then, to use:
<?php
include_once('Dispatcher.php');
$d = new Dispatcher( 'do_callback' );
$d->asynchronous_method();
function do_callback( $data ){
print 'Data is: ' . $data . "\n";
}
?>
[Edit]
Added a missing parenthesis.
Also, added the callback declaration, I prefer it that way.
For those who don't care about breaking compatibility with PHP < 5.4, I'd suggest using type hinting to make a cleaner implementation.
function call_with_hello_and_append_world( callable $callback )
{
// No need to check $closure because of the type hint
return $callback( "hello" )."world";
}
function append_space( $string )
{
return $string." ";
}
$output1 = call_with_hello_and_append_world( function( $string ) { return $string." "; } );
var_dump( $output1 ); // string(11) "hello world"
$output2 = call_with_hello_and_append_world( "append_space" );
var_dump( $output2 ); // string(11) "hello world"
$old_lambda = create_function( '$string', 'return $string." ";' );
$output3 = call_with_hello_and_append_world( $old_lambda );
var_dump( $output3 ); // string(11) "hello world"
I cringe every time I use create_function() in php.
Parameters are a coma separated string, the whole function body in a string... Argh... I think they could not have made it uglier even if they tried.
Unfortunately, it is the only choice when creating a named function is not worth the trouble.