I made this awesome plugin for wordpress to easily add references to blog posts using latex-like tags. It works really well, but there's one nasty detail: I'm using global variables, as I need to change a variable in an anonymous function of which I can't change the passed parameters (it's a callback function).
I tried the use syntax and it works but the variable gets copied into the anonymous function.
Here's the code, shortened to give a quick overview of what I want to do:
// Global variables, ugh...
// I don't want to declare $reflist here!
$reflist = array();
// Implementation of reference parsing.
function parse_references( $str ) {
// Clear array
$reflist = array();
// I want to declare $reflist here!
// Replace all tags
$newstr = preg_replace_callback( "/{.*}/",
function ( $matches ) {
// Find out the tag number to substitute
$tag_number = 5;
// Add to $reflist array
global $reflist;
// I don't want to have to use a global here!
$reflist[] = $tag_number;
return "[$tag_number]";
}, $str );
return $newstr;
}
So does anyone know how to solve this elegantly?
Pass the variable by reference with the use construct. This way, modifying the value of $reflist inside the anonymous function does have an external effect, meaning the original variable's value changes.
$newstr = preg_replace_callback("/{.*}/", function($matches) use (&$reflist) {
$tag_number = 5; // important ----^
$reflist[] = $tag_number;
return "[$tag_number]";
}, $a);
Related
This question already has answers here:
PHP variables in anonymous functions
(2 answers)
Closed 6 years ago.
I have an issue using this code in order to get the appropriate result.
My code:
function check_txt($url, $content) {
global $content;
$result = get_file_contents($url);
$arr = array_filter($result, function($ar) {
return ($ar['txt'] == $content);
});
return $arr[0];
}
I got this error when I execute the code:
Notice: Undefined variable: content in myfile.php
My question is how to pass content variable to function($ar) ? already tried function($ar, $content) and too global $content; like the code I posted.
You need to USE the $content variable assuming it is actually available, to make it available in your annonymous function
function check_txt($url, $content) {
$result = get_file_contents($url);
$arr = array_filter($result, function($ar) use ($content) {
return ($ar['txt'] == $content);
});
return $arr[0];
}
The Manual has more details and examples
Either
A) if you have $content previously defined in global scope, use the global you have and remove the $content parameter.
or,
B) remove the global declaration and pass something in using the $content parameter.
PS: As others have pointed out, you need to use a use() on the anon function... but also pass param or go global, don't do both.
The array_filter() function filters the values of an array using a callback function.
This function passes each value of the input array to the callback function. If the callback function returns true, the current value from input is returned into the result array. Array keys are preserved.
http://www.w3schools.com/php/func_array_filter.asp
Hope this helps :)
Here is my anonymous function:
$step = function() use ($text,$count,$new_text) {
$new_text .= $text[$count];
$count++;
I'm reading a long text value and scanning for bad characters. If the value of $text[$count] is ok, I want to add it to the new text variable and increase the count by calling $step(). Sure, I could just repeat the two lines over and over in my code, but using an anonymous function seemed so much simpler. The only problem is that it doesn't work. The variables aren't changing in the outer function.
What am I doing wrong. Alternatively, what's a different way to do this if there is one? There has to be a way to abstract a few lines of repeated code throughout a function.
You MUST pass by reference if want a modified version of variable after function is performed, like this:
<?php
$text = 'Some text';
$anon = function() use (&$text) {
$text .= ' and more...' ;
};
$anon();
print $text; // returns: Some text and more...
The use statement just inherit variables from the parent scope.
I have used PHP for a very long time, but not really used callbacks very much until quite recently. In the following code, the callback (the example is QueryPath, in case you're wondering, but it could be anything that accepts a callback) will add some link to an array:
// parse any product links out of the html
$aProducts = array();
qp( $html, 'a' )->each(function($index, $element){
global $aProducts;
$link = qp($element)->attr('href');
$pregMatch = preg_match('#(.*)-p-(.*)\.html#i', $link, $matches);
if( $pregMatch ) {
$product_id = (int)$matches[2];
if( !in_array($product_id, $aProducts) ) {
$aProducts[] = $product_id;
}
}
});
// print out our product array
print_r( $aProducts );
What's the alternative to using global $aProducts (if there is one)?
use use:
qp( $html, 'a' )->each(function($index, $element) use(&$aProducts) {
Note the &. This is needed, for otherwise you would be using a copy of the array. You can also use multiply values, just list them separated with a ,. E.g: use(&$aProducts, $someObj, &$someInt)
PHP.net: http://www.php.net/manual/en/language.namespaces.importing.php
I Recommend you To Don't Use global variable, instead put your code on a Class and Use $this instead global variable. it must Work
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.
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).