Access local variable from other function PHP - php

I have a private function which has lots of variables, let's say these: $foo, $bar, $baz.
Inside the function, I call parent::_setViewVars(array('foo','bar','baz'));
This _setViewVars looks so:
protected function _setViewVars($a){
foreach($a as $v){
global $$v;
$this->set($v, $$v);
}
}
I just want to get rid off cakephp's shit like 80 lines of $this->set('selected_areas',$selected_areas);, that's what this function is for.
_setViewVars cannot access $$v, because it's not a global variable. Given that this function is being called from the context that has these variables, can I reach them somehow?
(by the way if there is a cakephp way of bulk setting view variables with the same names, tell me how -- I haven't found one)
PS: packing everything into an array is NOT a solution, we're talking about thousands of unneccessary square brackets here.

What you're looking for is actually pretty standard PHP:
compact() - Create array containing variables and their values
Example showing it's use in a CakePHP Action:
$var1 = "I";
$var2 = "love";
$var3 = "pizza!";
$this->set(compact('var1', 'var2', 'var3'));
(might want to try to tone down the hatred a bit - you're more likely to get help that way)

Related

Is there a convenient function or PHP function for converting associative array values into individual variables? [duplicate]

One thing I find myself often wanting to do is convert a large associative array, usually from a POST request (a registration form, for example, with many inputs) into local variables based on the key from the array. If you are using the values from the array often, your code quickly becomes littered with long variable names and quotes.
To prevent this for small associative arrays, it is acceptable to just do something like this:
$username = $_POST['username'];
$password = $_POST['password'];
I'm not escaping them for this example to keep everything as uncluttered as possible, so relax.
You can also do this:
list($username, $password) = $_POST;
But what if the $_POST array is larger? Then it becomes tedious to do both of those methods. To fix that, you can run a loop like this:
foreach($arr as $key => $value)
{
${$key} = $value;
}
The problem with this method is that it assigns local variables. It would be nice if it were possible to call a function in a parent class that runs this loop and those local variables were accessible within the calling class. Imagine an MVC setup where every controller is derived from a Controller class, and likewise for the models. It would be nice to do:
$this->localize($_POST);
doWhatever($username) // $username works! Assuming $_POST['username'] is defined
Creating such a method causes the local variables to only remain within the scope of the localize() function in the parent class, so this won't work. What I have been doing is running the same loop with one modification:
foreach($arr as $key => $value)
{
$this->{$key} = $value;
}
This works and all, but doesn't really solve the initial problem. Instead of code that is cluttered with brackets and quotes, it has $this-> all over the place, not to mention it is assigning variables that were never formally defined within the class.
So finally, my question: is it possible to create a function such as the localize() I described, such that it can be inherited from a parent class but create local variables relative to the child class ($username instead of $this->username).
Also, whether you can or not, is this considered bad practice? It seems a bit hacky to me, and you are ignoring some principles of OOP. If it is, do you use a solution to fix the ugliness and clutter of large associative arrays, or do you just deal with it?
The PHP extract function does exactly this. Extracts a hash into the local namespace:
http://us3.php.net/extract
An important addendum:
Be sure to use the EXTR_SKIP option for extract() if the function is acting on user-supplied variable names (such as provided by $_POST) to prevent redefining of existing variables:
$myvar = 'abc';
extract($_POST, EXTR_SKIP); // $_POST['myvar'] won't overwrite $myvar.
It's a bad practice, users will be able to redefine any of your variables just by passing key in POST array, it's pretty simple and it's how register_globals works, and it's why this ability is deprecated in 5.3 and removed in 5.4.

PHP: query all variables defined inside function scope

This is just out of curiosity, but I was wondering if there was a way to query all the variables defined inside the scope of a function (exclusively within this scope, and from within that function) and put them in an associative array. Something like an extended get_defined_vars function.
The reason is that it would be nice to be able to save the 'state' of an execution at any point in the program, for instance to debug, log, handle exceptions, or even pass the entire scope of a function to another one. If I'm not mistaken, I think get_object_vars allows doing this with objects.
From the comments of PHP.net
// The very top of your php script
$vars = get_defined_vars();
// Now do your stuff
$foo = 'foo';
$bar = 'bar';
// Get all the variables defined in current scope
$vars = array_diff(get_defined_vars(),$vars);
echo '<pre>';
print_r($vars);
echo '</pre>';
PHP doesn't really have scopes, they were added later, that's why you need $this and global to access a global variable. Objects are implemented basically as arrays because they came later, that's why equality tests on two objects work member by member.
You can try var_dump of course.
Ignore the var_dump bit, you've found get_defined_vars and get_object_vars they are as close as you will get I'm sorry to say.
Another difficulty in telling you about what's in a function is how it does variables, remember PHP is a dynamic language if we have:
$x = "hello";
$$x = "world"
we now have a variable called $hello with value "world", if the $x variable came from some complicated nondeterministic code, it'd be impossible to tell without running. So you can only inspect the current variables, when you're in it, and given how scopes are implemented, get_defined_vars is as close as you will get sorry.

PHP - Passing a variable to a function without passing variables asked before it?

Is it possible to pass a variable to function without passing the variables that come before it in the function definition?
For example, if my function looks like this:
function doThis( $v1, $v2, $v3 )
{
echo $v1.$v2.$v3;
}
Can I then call the function with only some of those variables, like doThis("v1",null,"v3") or some other way?
you can use predefined arguments:
function doThis( $v1, $v2 = "hello", $v3 = 1 ) {}
call to this function only with the first argument, the 2nd and 3rd will be "hello",1 by defualt:
doThis(10);
+1 on #Alon post
You can also use an array ..
$array['var1'] = null;
$array['var2'] = "foo";
$array['var3'] = "bar";
doThis($array);
function doThis($theArray){
var_dump($theArray);
}
This way, you can use empty values and N amout of variables.
Yes, you can do doThis("v1",null,"v3"), if you prepare your function to handle that null argument.
For example your example will work just fine.
Or, if it makes sense you can change the order of the arguments so you can set defaults.
You can specify default values in the function definition like so:
function doThis($a, $b=null, $c=null) {
echo $a . $b . $c;
}
doThis("hello", " world"); // yields: hello world
In this function, $a is a required parameter. The other two values are optional.
To appease the commenter below, you can of course still call this function like so:
doThis("hello", null, "world");
and of course, you do not have to provide defaults (but it is a good idea to do so)
The answer is: rework your workflow.
I know you don't like that answer, but honestly, if you are passing more than 3 parameters or if sometimes you don't want to pass part of the beginning parameters, you probably want to rethink what you're doing. Function and method calls should be simple. You are probably doing too much in your function.
General tips:
Use classes if appropriate. This way you can avoid passing some information around because it's stored in the class and your methods have access to it.
Keep methods cohesive. That means your methods and functions should do one thing and do it well.
Put variables you want to pass only sometimes at the back. This allows you to simply not pass them if they have $var=null after it. You then check to see if $var===null before doing something with it.
Much cleaner way is passing an array of variables, as demonstrated by #jflaflamme.
Another way is allowing an arbitrary number of variables like this:
function foo()
{
$arguments = func_get_args();
if(sizeof($arguments))
{
echo implode('', $arguments);
}
}
foo(1, 2, 3, $some_var);
You can also loop trough the array you obtain via func_get_args, check them for their type (if it's a null, array, object - you name it) and then handle them in a way you deem appropriate.

Function to create new, previously undefined variable

Sorry for such a lame title, but I just had no idea what to put there, hope you understand it. Plus, I have no idea if similar question has been asked before, because I don't know the proper keywords for it - therefore couldn't google it too.
Basicly... When looking at preg_match_all();, they got this matches parameter that will create new array defined in function, and give us the ability to access it after function execution.
And the question is.. How can I implement such a feature in my own function? But that it could create single variable and/or array.
Thanks in advance!
preg_match_all() accepts a reference to an array, which in its own scope is called $matches. As seen in the function prototype:
array &$matches
If you call the function and pass in a variable, if it does not already exist in the calling scope it will be created. So in your user-defined function, you accept a parameter by reference using &, then work with it inside your function. Create your outer-scope variable by simply declaring it in your function call, like you the way you call preg_match_all() with $matches.
An example:
function foo(&$bar) {
$bar = 'baz';
}
// Declare a variable and pass it to foo()
foo($variable);
echo $variable; // baz
I think you are referring to function parameters passed by reference, are you not?
function putValInVar(&$myVar, $myVal){
$myVar = $myVal;
}
$myVar = 1;
putValInVar($myVar, 2);
echo $myVar; // outputs '2', but will output '1' if we remove the '&' //
By default function arguments in PHP are passed by value. This means that new variables are created at each function call and those variables will exist only inside the function, not affecting anything outside it.
To specify that an argument should be used by reference the syntax is to append an & before declaring it in the function header. This will instruct PHP to use the passed variable inside the function rather than creating a copy of it.
Exception: Objects are always passed by reference. (Well... Not really, but it's complicated. See the comment thread for more info.)
I think what you are asking for is passing-by-reference. What preg_match_all basically does to "create" an array variable outside its scope is:
function preg_match_all($foo, $bar, & $new_var) {
$new_var = array(1,2,3);
}
The crucial point here is & in the function definition. This allows you to overwrite variables in the outer scope when passed.
Stylistically this should be used with care. Try to return arrays or results instead of doing it via reference passing.
Like this:
$myvariable = runfunction();
function runfunction() {
//do some code assign result to variable (ie $result)
return $result;
}
Or
global $result;
function runfunction() {
global $result;
$result = 'something';
}

How to set a variable in the caller scope, like the extract() function

I know that directly setting a variable in the scope of caller is probably not a good idea.
However, the PHP extract() function does exactly that! I would like to write my own version of extract() but cannot figure out how to actually go about setting the variables in the caller. Any ideas?
The closest I have come is modifying the caller's args using debug_backtrace(), but this is not exactly the same thing...
You can't modify local variables in a parent scope - the method which extract() uses is not exposed by PHP.
Also, what you get back from debug_stacktrace() isn't magically linked to the real stack. You can't modify it and hope your modifications are live!
You could only do it in a PHP extension. If you call an internal PHP function, it will not run in a new PHP scope (i.e., no new symbol table will be created). Therefore, you can modify the "parent scope" by changing the global EG(active_symbol_table).
Basically, the core of the function would do something like extract does, the core of which is:
if (!EG(active_symbol_table)) {
zend_rebuild_symbol_table(TSRMLS_C);
}
//loop through the given array
ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table),
Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, data, 1, 0);
There are, however, a few nuances. See the implementation of extract, but keep in mind a function that did what you wanted wouldn't need to be as complex; most of the code in extract is there to deal with the several options it accepts.
You can abuse the $GLOBALS scope to read and write variables from the caller of your function. See below sample function, which reads and write variables from the caller scope.
And yes, I know its dirty to abuse the $GLOBAL scope, but hey, we're here to fix problems ain't we? :)
function set_first_name($firstname) {
/* check if $firstname is defined in caller */
if(array_key_exists('firstname', $GLOBALS)) {
$firstname_was = $GLOBALS['firstname'];
} else {
$firstname_was = 'undefined';
}
/* set $firstname in caller */
$GLOBALS['firstname'] = $firstname;
/* show onscreen confirmation for debugging */
echo '<br>firstname was ' . $firstname_was . ' and now is: ' . $firstname;
}
set_first_name('John');
set_first_name('Michael');
The function returns the following output:
<br>firstname was undefined and now is: John
<br>firstname was John and now is: Michael
It depends on how badly you need to do this. If it's only for source beauty, find another way. If, for some reason, you really need to mess with parent scope, there's always a way.
SOLUTION 1
The safest method would be to actually use extract itself for this job, since it knows the trick. Say you want to make a function that extracts elements of an array but with all the names backwards - pretty weird! -, let's do this with a simple array-to-array transformation:
function backwardNames($x) {
$out = [];
foreach($x as $key=>$val) {
$rev = strrev($key);
$out[$rev] = $val;
}
return $out;
}
extract(backwardNames($myArray));
No magic here.
SOLUTION 2
If you need more than what extract does, use eval and var_export. YES I KNOW I KNOW everybody calm down please. No, eval is not evil. Eval is a power tool and it can be dangerous if you use it without care - so use it with care. (There is no way to go wrong if you only eval something that's been generated by var_export - it doesn't give any way to intrusions even if you put values in your array from an untrusted source. Array elements behave well.)
function makeMyVariables() {
$vars = [
"a" => 4,
"b" => 5,
];
$out = var_export($vars,1);
$out = "extract(".$out.");";
return $out;
}
eval(makeMyVariables()); // this is how you call it
// now $a is 4, $b is 5
This is almost the same, except that you can do a lot more in eval. And it's significantly slower, of course.
However, indeed, there is no way to do it with a single call.

Categories