PHP: call_user_func_array: pass by reference issue - php

The below function generates error when a function contains referenced arguments eg:
function test(&$arg, &$arg2)
{
// some code
}
Now I can not use call_user_func_array for above function, it will generate an error.
How to solve this problem?
I do need to use call_user_func_array.
Also assume that i don't know beforehand whether they are passed by reference or passed by value.
Thanks

When storing your parameters in the array, make sure you are storing a reference to those parameters, it should work fine.
Ie:
call_user_func_array("test", array(&param1, &param2));

A great workaround was posted on http://www.php.net/manual/de/function.call-user-func-array.php#91503
function executeHook($name, $type='hooks'){
$args = func_get_args();
array_shift($args);
array_shift($args);
//Rather stupid Hack for the call_user_func_array();
$Args = array();
foreach($args as $k => &$arg){
$Args[$k] = &$arg;
}
//End Hack
$hooks = &$this->$type;
if(!isset($hooks[$name])) return false;
$hook = $hooks[$name];
call_user_func_array($hook, $Args);
}
The actual hack is surrounded by comments.

Related

PHP functions that have optional arguments but allow null (and similar) values?

I'm aware that you can have PHP functions with optional arguments like so:
function do_something($argument = null)
{
// argument not provided
if ($argument === null)
........
}
Consider the case that null/false and other values are all valid arguments to my function. How can I determine whether an argument was provided or not?
do_something(null); // argument provided
do_something(false); // argument provided
do_something(0); // argument provided
do_something("test"); // argument provided
do_something(new stdClass()); // argument provided
do_something(); // nothing provided
How can I detect the last case? I have thought about using func_num_args which would work in most cases but it doesn't work if I have several optional arguments.
Is there anything that solves this problem?
func_num_args() should work exactly as you want it to, because you might be assuming something that's actually not the case: You can't have optional arguments left out if they are in the middle of your arguments list.
So let's look at this function:
function test1 ($param1 = null, $param2 = null) {
return func_num_args();
}
If I call that with different parameter combinations I get the following results:
test1() => 0
test1(true) => 1
test1(true, true) => 2
There is just no way to call the function in a way where $param2 would be set while $param1 isn't. So you can map every possible output of func_num_args() to exactly one parameter configuration.
In the example above you can rely on the fact that
if the return value is 1, $param2 definitely hasn't been set, while $param1 has been.
For 0 it's 100% sure that neither one has been given.
And, of course, if it's 2 both are there.
What you actually would need are named parameters, as many other languages have them. PHP doesn't at the moment. NikiC actually wrote an RFC that suggests the addition of named parameters to PHP, but I think that's still way off in the future. You can check that out here: https://wiki.php.net/rfc/named_params
As these are not yet available, here are a few workarounds you can try:
Workaround 1
If you really need to be able to have all the parameters optional, try a parameter array:
function test1 (array $opts) {
if (!isset($opts['opt1'])) { $opts['opt1'] = 'default1'; }
if (!isset($opts['opt2'])) { $opts['opt2'] = 'default2'; }
}
Then you can call it like this:
test1(array('opt2' => true))
It would set the first parameter to "default1" while keeping the second. And there are definitely better and more elegant ways to do this (e.g. using an object instead), but the general idea is the same.
Workaround 2
You could also go with alias functions:
function test ($param1, $patam2) { ... ]
function testNoParam1 ($param2) {
test("default1", $param2);
}
That at least makes it very easy to read, but of course you need to pick the right function depending on the parameters you have.
Workaround 3
By adding a lot of additional code you could get really fancy and use a FactoryObject:
class FunctionExecutor {
private $param1 = "default1";
private $param2 = "default2";
public function param1($val) {
$this->param1 = $val;
return $this;
}
public function param2($val) {
$this->param2 = $val;
return $this;
}
public function execute() {
return yourFunction($this->param1, $this->param2);
}
}
This could be used like this:
$doSomething = new FunctionExecutor();
$returnValue = $doSomething->param2(42)->execute();
In this approach it would probably be a better idea to actually put your function into the object instead of defining it globally. Anyway...this is definitely a possibility, but not the most practical one. Just wanted to add it, because it has some benefits.
perhaps this will help: http://www.php.net//manual/en/function.func-get-args.php
$args = func_get_args();
if(!isset($arg[0])) {
echo 'no argument';
}
or
isset(func_get_arg(0));
Passing "null", "0", or "false" means that you allocate memory to store a variable, regardless it's scope, type, or size. Then it is used as a parameter to a function.
In PHP you cannot override functions by arguments, but you can access them by calling the "func_get_args()", and this is the only way to handle different numbers / types of arguments passed to a function:
function do_something() {
$args = func_get_args();
//do_something(stdClass, 1)
if($args[0] instanceof stdClass && is_numeric($args[1])) {
//handle
//return
//do_something(1, "string")
} else if(is_numeric($args[0]) && is_string($args[1])) {
//handle
//return
}
throw new Exception('invalid arguments');
}
do_something(new StdClass(), 100); //ok
do_something(100, "hell world") // ok
do_someting(); //throws Exception('invalid arguments');
In PHP 7, you can do:
function action(...$args) {
if (count($args) === 0) {
return action_default();
}
$var1 = array_shift($args);
$var2 = array_shift($args);
$var3 = array_shift($args);
// etc.
}

How to addslashes automatically using Codeigniter?

I want to apply addslashes() to all the post elements got through
$this->input->post('my_var');
How can I do that ? Is there any feature like filters under wordpress for this ?
I think you want something global. My idea is to edit the global post function in the codeigniter to use addslashes on everything. You can find that function in:
/yourfolder/system/core/Input.php
You can escape it by setting it global.
function post($index = NULL, $xss_clean = FALSE)
{
// Check if a field has been provided
if ($index === NULL AND ! empty($_POST))
{
$post = array();
// Loop through the full _POST array and return it
foreach (array_keys($_POST) as $key)
{
$post[$key] = addslashes($this->_fetch_from_array($_POST, $key, $xss_clean));
}
return $post;
}
return addslashes($this->_fetch_from_array($_POST, $index, $xss_clean));
}
Although I don't really find it as good solution to modify the global functions this should do the trick in your case.
Edit: I see that input->post already does that and you would not need to add that function additionally.

Match function parameters from an associative array and function call in PHP

I have an associative array in the following form:
$params = array(
'paramName_4'=>'param_4',
'paramName_2'=>'param_2',
// ...,
'paramName_6'=>'param_6',
);
and I also have a function myFunction defined as:
public function myFunction($paramName_1, $paramName_2, $paramName_3, ....);
Does a a "parsing" function exist in PHP so that I can call function myFunction by matching the parameters (even if they are not sorted wrt the myFunction's parameter sequence)? In other words, can I do
my_magic(__NAMESPACE__.'\\myFunction', $params);
Does this "magic" function does exist? If not, how can I implement it?
You can implement it using reflection. Here's how:
// The input is the array of arguments and the function name
$arguments = array(....);
$functionName = __NAMESPACE__.'\\myFunction';
$reflector = new \ReflectionFunction($functioName);
$params = $reflector->getParameters();
$values = array();
foreach ($params as $param) {
$name = $param->getName();
$isArgumentGiven = array_key_exists($name, $arguments);
if (!$isArgumentGiven && !$param->isDefaultValueAvailable() {
die ("Parameter $name is mandatory but was not provided");
}
$values[$param->getPosition()] =
$isArgumentGiven ? $arguments[$name] : $param->getDefaultValue();
}
// You can now call the function:
call_user_func($functionName, $values);
Yes, you can use Reflection as per #Jon's example, but if the problem is just that the params aren't in the right order, why not just use ksort() or uksort() to put them in the right order.
Then you can use call_user_func_array(). Problem solved.
$params = array(....);
uksort($params, function($a,$b) {
//sort the params into the known order....
$sortOrder = array('param1','param2','param3','param4');
return (array_search($a, $sortOrder) > array_search($b, $sortOrder)) ? -1 : 1;
});
//now that $params is in the right order we can do this....
$retVal = call_user_func_array($func, $params);
I've hard-coded the param order here, because it's the most efficient way. If you are calling a function where you don't know the correct param order in advance, then yes, you'll need to use reflection. But I would think that's fairly unlikely (passing an unknown params list into an unknown function sounds like a goldmine for hackers)
Here is a library which does the argument resolving for a given function/method: ArgumentsResolver.

Can I pass variables to PHP GD imagefilter?

I know the imagefilter function expects a long but is there a way to cast a variable to a long or am I forced to simply create separate functions for each filter. My thought is this:
public function ImgFilter($filter, $arg1=null, $arg2=null){
$this->lazyLoad();
if($this->_cache_skip) return;
if(isset($this->_image_resource)){
imagefilter($this->_image_resource, $filter);
}
}
It's complaining about my $filter variable. For this example my $filter value is: IMG_FILTER_GRAYSCALE
Is this possible?
Provided:
$filter = "IMG_FILTER_GRAYSCALE"
You should be able to use the function constant:
imagefilter($this->_image_resource, constant($filter));
However note that the following will also work just fine:
$filter = IMG_FILTER_GRAYSCALE
imagefilter($this->_image_resource, $filter);
You can pass around the constant as an argument without a problem if you need to do so. The former is only useful if you really need the constant name to be dynamic.
Casting is made this way:
<holder> = (<type>) <expression>
$var = (int) "123";
The following function would do what you need:
public function ImgFilter($filter, $arguments = array())
{
$this->lazyLoad();
if ($this->_cache_skip) {
return;
}
if (isset($this->_image_resource)) {
$params = array($this->_image_resource, $filter);
if (!empty($arguments)) {
$params = array_merge($params, $arguments);
}
call_user_func_array('imagefilter', $params);
}
}
Then use it like this:
$this->ImgFilter(IMG_FILTER_GRAYSCALE);
$this->ImgFilter(IMG_FILTER_COLORIZE, array(0, 255, 0));

Forward undefined number of arguments to another function

I will explain the question with a simple function accepting any number of function
function abc() {
$args = func_get_args();
//Now lets use the first parameter in something...... In this case a simple echo
echo $args[0];
//Lets remove this first parameter
unset($args[0]);
//Now I want to send the remaining arguments to different function, in the same way as it received
.. . ...... BUT NO IDEA HOW TO . ..................
//tried doing something like this, for a work around
$newargs = implode(",", $args);
//Call Another Function
anotherFUnction($newargs); //This function is however a constructor function of a class
// ^ This is regarded as one arguments, not mutliple arguments....
}
I hope the question is clear now, what is the work around for this situation?
Update
I forgot to mention that the next function I am calling is a constructor class of another class.
Something like
$newclass = new class($newarguments);
for simple function calls
use call_user_func_array, but do not implode the args, just pass the array of remaining args to call_user_func_array
call_user_func_array('anotherFunction', $args);
for object creation
use: ReflectionClass::newInstanceArgs
$refClass = new ReflectionClass('yourClassName');
$obj = $refClass->newInstanceArgs($yourConstructorArgs);
or: ReflectionClass::newinstance
$refClass = new ReflectionClass('yourClassName');
$obj = call_user_func_array(array($refClass, 'newInstance'), $yourConstructorArgs);

Categories