PHP: Specify argument variable names when calling a function? - php

This might seem like an academic or useless topic, but I'm curious.
When developing web pages with PHP, I often need to call functions that take several arguments. I frequently need to look up the spec for the function (on php.net or in my include files, if it's a function I defined) to remind myself what the variables are and what order they're in and what the defaults are, etc. I imagine many of you can relate to this.
A function defined like this:
function do_something_awesome ($people_array, $places_recordset, $num_cycles, $num_frogs,
$url = '?default=yes', $submit_name = 'default_submit_label') {
...
}
when called, might look like this:
$result = do_something_awesome($names, $rsTowns, $c, $f);
My question is this: I'd like to write my code in a way that reminds me of which argument corresponds to each variable, during function calls like this. Is it ever legal to call a function as follows?
$result = do_something_awesome($people_array = $names, $places_recordset = $rsTowns,
$num_cycles = $c, $num_frogs = $f);
If not in PHP, are there other languages where method calls can be made in this way?

To answer your first question:
My question is this: I'd like to write my code in a way that reminds me of which argument corresponds to each variable, during function calls like this.
AFAIK, many PHP coders do it by passing in an associative array as the only argument. However, you'll have to do your own variables checking inside the called function.
$result = do_something_awesome(array(
'people_array' => $names,
'places_recordset' => $rsTowns,
'num_cycles' => $c,
'num_frogs' => $f
));
As for:
Is it ever legal to call a function as follows?
It won't cause any PHP errors, but what you are effectively doing is:
$result = do_something_awesome( expression, expression, expression, expression );
See: PHP Functions arguments
PHP won't know to put $people_array = ... or $num_frogs = ... in their corresponding places when you decide to switch their order around. Furthermore, as DCoder said, these expressions actually take place in the current scope, and will change any pre-existing variables without letting you know.

What about using an object as the only argument:
function my_function($arguments) {
if (!is_object($arguments)) throw new Exception();
$default_values = array('arg1' => 'value1', 'arg2' => 'value2');
foreach ($default_values as $key => $default_value)
if (!isset($arguments->$key)) $arguments->$key = $default_value;
## do the job ##
}
## and then
$my_arguments = new stdClass();
$my_arguments->arg2 = 'some_value';
my_function($my_arguments);

You can try this out:
$bas = 'This is passed to the function.';
$bar = 'This will be modified.';
function foo($bar)
{
echo $bar;
}
foo($bar = $bas);
echo $bar;
The output from this script would be 'This is passed to the function.This is passed to the function.'. So like DCoder said, while you can use them and it's perfectly legal but if you had other variables with the same name as the function arguments, this will overwrite them (in this case the original $bar was overwritten).

Related

PHP. Pass variable by reference vs string. How to works with these two different arguments?

I'm writing my own debug functions and I need some help to fix the code below.
I'm trying to print a variable and its name, the file where the variable and the function was declared and the line of the function call. The first part I did, the variable, the variable name, the file and the line is printed correctly.
At the code, a($variable) works good.
The problem is I'd like this function accepts a string too, out of a variable. But PHP returns with a fatal error (PHP Fatal error: Only variables can be passed by reference in ...). At the code, a('text out').
So, how can I fix this code to accept a variable or a string correctly?
code (edited):
function a(&$var){
$backtrace = debug_backtrace();
$call = array_shift($backtrace);
$line = $call['line'];
$file = $call['file'];
echo name($var)."<br>".$var."<br>".$line."<br>".$file;
}
$variable='text in';
a($variable);
a('text out');
I need pass the variable by reference to use this function below (the function get the variable name correctly, works with arrays too):
function name(&$var, $scope=false, $prefix='unique', $suffix='value'){
if($scope) $vals = $scope;
else $vals = $GLOBALS;
$old = $var;
$var = $new = $prefix.rand().$suffix;
$vname = FALSE;
foreach($vals as $key => $val) {
if($val === $new) $vname = $key;
}
$var = $old;
return $vname;
}
The way your code is currently implementing pass by reference is perfect by design, but also by design cannot be changed to have two a() methods - one accepting a variable by reference and the other as a string-literal.
If the desire to pass a string literal instead of assigning it to a variable first is really needed, I would suggest creating a second convenience method named a_str() that actually accepts a string-literal instead of a variable by reference. This method's sole-purpose would be to relay the variable(s) to the original a() method - thereby declaring a variable to pass by reference.
function a_str($var) {
a($var);
}
The only thing to remember is, use a($variable); when passing by reference and a_str('some text'); when not.
Here is the same convenience-method for your name() function:
function name_str($var, $scope=false, $prefix='unique', $suffix='value'){
return name($var, $scope, $prefix, $suffix);
}
The only way to do what you are asking without writing an additional function like #newfurniturey suggests is plain and simply opening and parsing the file where your function was called as text (e.g. with fopen), using the data from debug_backtrace. This will be expensive in terms of performance, but it might be ok if used only for debugging purposes; and using this method you will no longer need a reference in your function, which means you can freely accept a literal as the parameter.

Setting PHP multidimensional session variable in function

Say I have a function called set_session_variable that looks like:
function set_session_variable($name, $value) {
// ...write value to the specified path
}
How would I write this function (without using an eval) so that I can do something like:
set_session_variable('foo', 'bar'); // Would set $_SESSION['foo'] = 'bar';
set_session_variable('foo[bar][baz]', 'blah'); // Would set $_SESSION['foo']['bar']['baz'] = 'blah';
I highly suggest, that you won't use
set_session_variable('foo[bar][baz]', 'blah');
but instead
set_session_variable('foo', array('bar'=>array('baz' => 'blah')));
Additionally, you don't need a function call for that at all:
$_SESSION['foo']['bar']['baz'] = 'blah';
You can change the implementation of $_SESSION with the session save handler.
If you're only concerned how you could parse a string like 'foo[bar][baz]', this has been asked before, for example use strings to access (potentially large) multidimensional arrays.
A more relevant question is why you need a function at all. Function calls have a cost, and the function doesn't appear to do useful work.
Example assignments:
$_SESSION['foo'] = 'bar';
$_SESSION['foo']['bar']['baz'] = 'blah';
$foo['bar']['baz'] = 'blah';
$_SESSION['foo'] = $foo;
In direct answer to your question: You could parse the value of $name within set_session_variable() using the PCRE module and a regular expression.
Even simpler and faster would be parsing it with sscanf() provided you are able and willing to impose a convention on the naming of array keys.
A cleaner alternative function:
$array['bar']['baz'] = 'blah';
set_session_variable('foo', $array);
function set_session_variable($key, $val) {
$_SESSION[$key] = $val;
}
One way to solve this is to mimic function overloading, example in this post -> PHP function overloading
Another way is to add one string argument to your function, with your array indices delimited.
For example: set_session_variable('foo', 'bar', 'baz;key');
Which saves the value 'bar' into foo['baz']['key'].
All you have to do is tear the 3rd argument apart (i use ; as delimiter here).

Find the name of a calling var

Anyone has an idea if this is at all possible with PHP?
function foo($var) {
// the code here should output the value of the variable
// and the name the variable has when calling this function
}
$hello = "World";
foo($hello);
Would give me this output
varName = $hello
varValue = World
EDIT
Since most people here 'accuse' me of bad practices and global variables stuff i'm going to elaborate a little further on why we are looking for this behaviour.
the reason we are looking at this kind of behaviour is that we want to make assigning variables to our Views easier.
Most of the time we are doing this to assign variables to our view
$this->view->assign('products', $products);
$this->view->assign('members', $members);
While it would be easier and more readable to just be able to do the following and let the view be responsible to determining the variable name the assigned data gets in our views.
$this->view->assign($products);
$this->view->assign($members);
Short answer: impossible.
Long answer: you could dig through apd, bytekit, runkit, the Reflection API and debug_backtrace to see if any obscure combination would allow you to achieve this behavior.
However, the easiest way is to simply pass the variable name along with the actual variable, like you already do. It's short, it's easy to grasp, it's flexible when you need the variable to have a different name and it is way faster than any possible code that might be able to achieve the other desired behavior.
Keep it simple
removed irrelevant parts after OP edited the question
Regardless of my doubt that this is even possible, I think that forcing a programmer on how to name his variables is generally a bad idea. You will have to answer questions like
Why can't I name my variable $arrProducts instead of $products ?
You would also get into serious trouble if you want to put the return value of a function into the view. Imagine the following code in which (for whatever reason) the category needs to be lowercase:
$this->view->assign(strtolower($category));
This would not work with what you're planning.
My answer therefore: Stick to the 'verbose' way you're working, it is a lot easier to read and maintain.
If you can't live with that, you could still add a magic function to the view:
public function __set($name, $value) {
$this->assign($name, $value);
}
Then you can write
$this->view->product = $product;
I don't think there is any language where this is possible. That's simply not how variables work. There is a difference between a variable and the value it holds. Inside the function foo, you have the value, but the variable that held the value is not available. Instead, you have a new variable $var to hold that value.
Look at it like this: a variable is like a bucket with a name on it. The content (value) of the variable is what's inside the bucket. When you call a function, it comes with its own buckets (parameter names), and you pour the content of your bucket into those (well, the metaphor breaks down here because the value is copied and still available outside). Inside the function, there is no way to know about the bucket that used to hold the content.
What you're asking isn't possible. Even if it was, it would likely be considered bad practice as its the sort of thing that could easily get exploited.
If you're determined to achieve something like this, the closest you can get would be to pass the variable name as a string and reference it in the function from the $GLOBALS array.
eg
function this_aint_a_good_idea_really($var) {
print "Variable name: {$var}\n";
print "Variable contents: {$GLOBALS[$var]}\n";
}
$hello="World";
this_aint_a_good_idea_really('hello');
But as I say, that isn't really a good idea, nor is it very useful. (Frankly, almost any time you resort to using global variables, you're probably doing something wrong)
Its not impossible, you can find where a function was invoked from debug_backtrace() then tokenize a copy of the running script to extract the parameter expressions (what if the calling line is foo("hello $user, " . $indirect($user,5))?),
however whatever reason you have for trying to achieve this - its the wrong reason.
C.
Okay, time for some ugly hacks, but this is what I've got so far, I'll try to work on it a little later
<?php
class foo
{
//Public so we can test it later
public $bar;
function foo()
{
//Init the array
$this->bar = array();
}
function assign($__baz)
{
//Try to figure out the context
$context = debug_backtrace();
//assign the local array with the name and the value
//Alternately you can initialize the variable localy
//using $$__baz = $context[1]['object']->$__baz;
$this->bar[$__baz] = $context[1]['object']->$__baz;
}
}
//We need to have a calling context of a class in order for this to work
class a
{
function a()
{
}
function foobar()
{
$s = "testing";
$w = new foo();
//Reassign local variables to the class
foreach(get_defined_vars() as $name => $val)
{
$this->$name = $val;
}
//Assign the variable
$w->assign('s');
//test it
echo $w->bar['s'];
}
}
//Testrun
$a = new a();
$a->foobar();
impossible - the max. ammount of information you can get is what you see when dumping
debug_backtrace();
Maybe what you want to do is the other way around, a hackish solution like this works fine:
<?php
function assign($val)
{
global $$val;
echo $$val;
}
$hello = "Some value";
assign('hello');
Ouputs: Some value
What you wish to do, PHP does not intend for. There is no conventional way to accomplish this. In fact, only quite extravagant solutions are available. One that remains as close to PHP as I can think of is creating a new class.
You could call it NamedVariable, or something, and as its constructor it takes the variable name and the value. You'd initiate it as $products = new NamedVariable('products', $productData); then use it as $this->view->assign($products);. Of course, your declaration line is now quite long, you're involving yet another - and quite obscure - class into your code base, and now the assign method has to know about NamedVariable to extract both the variable name and value.
As most other members have answered, you are better off suffering through this slight lack of syntactic sugar. Mind you, another approach would be to create a script that recognizes instances of assign()'s and rewrites the source code. This would now involve some extra step before you ran your code, though, and for PHP that's silly. You might even configure your IDE to automatically populate the assign()'s. Whatever you choose, PHP natively intends no solution.
This solution uses the GLOBALS variable. To solve scope issues, the variable is passed by reference, and the value modified to be unique.
function get_var_name(&$var, $scope=FALSE) {
if($scope) $vals = $scope;
else $vals = $GLOBALS;
$old = $var;
$var = $new = 'unique'.rand().'value';
$vname = FALSE;
foreach ($vals as $key => $val) {
if($val === $new) $vname = $key;
}
$var = $old;
return $vname;
}
$testvar = "name";
echo get_var_name($testvar); // "testvar"
function testfunction() {
$var_in_function = "variable value";
return get_var_name($var_in_function, get_defined_vars());
}
echo testfunction(); // "var_in_function"
class testclass {
public $testproperty;
public function __constructor() {
$this->testproperty = "property value";
}
}
$testobj = new testclass();
echo get_var_name($testobj->testproperty, $testobj); // "testproperty"

PHP: Variable in a function name

I want to trigger a function based on a variable.
function sound_dog() { return 'woof'; }
function sound_cow() { return 'moo'; }
$animal = 'cow';
print sound_{$animal}(); *
The * line is the line that's not correct.
I've done this before, but I can't find it. I'm aware of the potential security problems, etc.
Anyone? Many thanks.
You can do that, but not without interpolating the string first:
$animfunc = 'sound_' . $animal;
print $animfunc();
Or, skip the temporary variable with call_user_func():
call_user_func('sound_' . $animal);
You can do it like this:
$animal = 'cow';
$sounder = "sound_$animal";
print ${sounder}();
However, a much better way would be to use an array:
$sounds = array('dog' => sound_dog, 'cow' => sound_cow);
$animal = 'cow';
print $sounds[$animal]();
One of the advantages of the array method is that when you come back to your code six months later and wonder "gee, where is this sound_cow function used?" you can answer that question with a simple text search instead of having to follow all the logic that creates variable function names on the fly.
http://php.net/manual/en/functions.variable-functions.php
To do your example, you'd do
$animal_function = "sound_$animal";
$animal_function();
You can use curly brackets to build your function name. Not sure of backwards compatibility, but at least PHP 7+ can do it.
Here is my code when using Carbon to add or subtract time based on user chosen type (of 'add' or 'sub'):
$type = $this->date->calculation_type; // 'add' or 'sub'
$result = $this->contactFields[$this->date->{'base_date_field'}]
->{$type.'Years'}( $this->date->{'calculation_years'} )
->{$type.'Months'}( $this->date->{'calculation_months'} )
->{$type.'Weeks'}( $this->date->{'calculation_weeks'} )
->{$type.'Days'}( $this->date->{'calculation_days'} );
The important part here is the {$type.'someString'} sections. This will generate the function name before executing it. So in the first case if the user has chosen 'add', {$type.'Years'} becomes addYears.
For PHP >= 7 you can use this way:
function sound_dog() { return 'woof'; }
function sound_cow() { return 'moo'; }
$animal = 'cow';
print ("sound_$animal")();
You should ask yourself why you need to be doing this, perhaps you need to refactor your code to something like the following:
function animal_sound($type){
$animals=array();
$animals['dog'] = "woof";
$animals['cow'] = "moo";
return $animals[$type];
}
$animal = "cow";
print animal_sound($animal);
You can use $this-> and self:: for class-functions. Example provided below with a function input-parameter.
$var = 'some_class_function';
call_user_func(array($this, $var), $inputValue);
// equivalent to: $this->some_class_function($inputValue);
And yet another solution to what I like to call the dog-cow problem. This will spare a lot of superfluous function names and definitions and is perfect PHP syntax and probably future proof:
$animal = 'cow';
$sounds = [
'dog' => function() { return 'woof'; },
'cow' => function() { return 'moo'; }
];
print ($sounds[$animal])();
and looks a little bit less like trickery as the "string to function names" versions.
JavaScript devs might prefer this one for obvious reasons.
(tested on Windows, PHP 7.4.0 Apache 2.4)

Is it possible to pass parameters by reference using call_user_func_array()?

When using call_user_func_array() I want to pass a parameter by reference. How would I do this. For example
function toBeCalled( &$parameter ) {
//...Do Something...
}
$changingVar = 'passThis';
$parameters = array( $changingVar );
call_user_func_array( 'toBeCalled', $parameters );
To pass by reference using call_user_func_array(), the parameter in the array must be a reference - it does not depend on the function definition whether or not it is passed by reference. For example, this would work:
function toBeCalled( &$parameter ) {
//...Do Something...
}
$changingVar = 'passThis';
$parameters = array( &$changingVar );
call_user_func_array( 'toBeCalled', $parameters );
See the notes on the call_user_func_array() function documentation for more information.
Directly, it may be impossible -- however, if you have control both over the function you are implementing and of the code that calls it - then there is one work-around that you might find suitable.
Would you be okay with having to embed the variable in question into an object? The code would look (somewhat) like this if you did so.
function toBeCalled( $par_ref ) {
$parameter = $par_ref->parameter;
//...Do Something...
$par_ref->parameter = $parameter;
}
$changingVar = 'passThis';
$parembed = new stdClass; // Creates an empty object
$parembed->parameter = array( $changingVar );
call_user_func_array( 'toBeCalled', $parembed );
You see, an object variable in PHP is merely a reference to the contents of the object --- so if you pass an object to a function, any changes that the function makes to the content of the object will be reflected in what the calling function has access to as well.
Just make sure that the calling function never does an assignment to the object variable itself - or that will cause the function to, basically, lose the reference. Any assignment statement the function makes must be strictly to the contents of the object.
This works by double referencing,the original variable is modified when the $parameter variable is modified.
$a = 2;
$a = toBeCalled($a);
echo $a //50
function toBeCalled( &$par_ref ) {
$parameter = &$par_ref;
$parameter = $parameter*25;
}
Except you are using deprecated functionality here. You'll generate a warning in PHP5 making it less than perfect.
Warning: Call-time pass-by-reference has been deprecated; If you would like to pass it by reference, modify the declaration of runtime function name. If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file in ...
Unfortunately, there doesn't appear to be any other option as far as I can discover.

Categories