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.
Related
I'm pretty sure this is a very basic question to all of you, but i'm new with php, and i don't really get it...
basically i've created a function in which i need to pass two parameters.
My functions is this:
function displayRoomDetails($customerRooms, $test)
{
foreach ($customerRooms as $room) {
$test.= $room->name;
};
}
it is a very basic function, but will do for this example.
Now, i'm creating templates to display several information, and i have 3 different layout where i need to display the same info but styled differently, so my approach was:
template1 .= '<span>';
if (!$customerRooms == "") {
displayRoomDetails($customerRooms,"template1");
};
template1 .= '</span>';
It should be pretty easy to understand, basically i'm calling the same functions in all the different templates passing as a parameter the template name and trying to append the results to the right template.
The problem i've got is this:
According to this example here ->
http://www.w3schools.com/php/showphp.asp?filename=demo_function3
i should be able to do this exactly like i did, but when i try, when i debug my function, $template doesn't take the passed value as i though it would, but it is still called $test and not $template1...
What am i doing wrong?
Thanks
Try these changes:
function displayRoomDetails($customerRooms, &$test)
And
$template1 .= '<span>';
if ($customerRooms != "") {
displayRoomDetails($customerRooms, $template1);
};
$template1 .= '</span>';
From what I understand you want to append some text to the template1 variable using the displayRoomDetailsFunction
Some things to fix:
template1 should be $template1
You should be passing the $template1 not the "template1" (i.e. the variable itself not its name).
If you want to modify this variable you need to either:
pass it as reference, which you can do by changing the function's declaration to: function displayRoomDetails($customerRooms, &$test)
return new string from function and assign it to the $template1 by adding return $test; just after your foreach block and changing the call to $template1 .= displayRoomDetails($customerRooms,$template1);
Additional note: if $customerRooms is an array, it'd be better to check if it's not empty using count() than !$customerRooms == "", see #andrew's comment for details
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);
I need to search through some content and enclose certain words with HTML. The words to search for are in an array, and I want to only replace the 2nd to last occurrence of each set of matches. So, as an example, if there are 5 matches, I want to replace the 4th match. If there are 3 matches, replace the 2nd. If there are 2 matches, replace the 2nd/last match, and if only one match then use that one.
The callback function is external to the function using the callback.
I'm thinking I need an array count -- minus one -- and then pass that number to the callback function and use it there, but maybe there's an easier way to do this?
Simplified functions are:
function repWords($content) {
foreach ($words_array as $w) {
$content = preg_replace_callback('/\b('.$w.'[s]?)\b/S', array(&$this,'addHtml'), $content);
}
return $content;
}
function addHtml($format){
return '<span>' . $format[1] . '</span>'
}
This works as desired to replaced all words it finds, and I can set it up to randomly replace some, but I want to either replace the nth occurrence and/or replace only one occurrence (not always the first occurrence).
Based on your requirements, here is what I came up with:
// needs to be global given that you are using non-anonymous function syntax
$count = 0;
function repWords($content) {
global $count;
$words_array = array('some','content','foobar');
foreach ($words_array as $w) {
$pattern = '~\b('.$w.'s?)\b~';
$count=0;
preg_match_all($pattern,$content,$matches);
$count=count($matches[0]);
if ($count>0)
$content = preg_replace_callback($pattern,'addHtml',$content);
}
return $content;
}
function addHtml ($format) {
global $count;
static $cc=1;
static $pf='';
$val=$format[1];
$cc = ($pf!=$val) ? 1 : ++$cc;
if ((1==$count)||($cc==($count-1)))
$format[1]= '<span>' . $val . '</span>';
$pf=$val;
return $format[1];
}
$content = <<<EOC
this is some content
some more content
this content foobar
EOC;
echo repWords($content);
output
this is <span>some</span> content
some more <span>content</span>
this content <span>foobar</span>
the first "some" is wrapped because there are 2 instances of it
the 2nd "content" is wrapped because there are 3 instances of it
"foobar" is wrapped because there is only 1 instance of it
Note: this utilizes a global variable $count. In general, it's a bad idea to use global variables. But this is one of the few examples where it's grudgingly accepted, due to limitation of php and preg_replace_callback (or any function that you can specify a callback) when you opt to define the callback separately instead of use an anonymous function. If you are on php5.3+ and are willing to make addHtml an anonymous function instead, $count can be passed to the anonymous function with use. Alternatively, if all this is actually in a class, make it a class property and use $this->count instead.
Another note: The very last thing you said was "(not always the first occurrence)" I was somewhat confused by this and took it to possibly mean that you want to somehow be able to specify the nth* instead of it always being "2nd to last (or first if there's only 1" e.g. in my code example perhaps you want to change it to be every 3rd to last word instead of 2nd to last. I asked you to clarify but you didn't respond by the time I posted this solution, so I did not write the code to be flexible for that. However, hopefully you should be able to take this and alter it to suit you if that's what you want. Basically it will involve passing another arg to repWords and it will also involve using another global variable to use in addHtml, where $cc==($count-1) is used.
My code:
$myText = "";
function addText($textString){
$myText .= $textString;
}
addText("Hello there...");
echo $myText;
Expected output:
Hello There...
$myText was empty.
Why does this happen?
You need to tell the function to use the global variable $myText
function addText($textString){
global $myText;
$myText .= $textString;
}
Though using global variables within functions is considered harmful.
try this:
$myText = "";
function addText($textString){
global $myText;
$myText .= $textString;
}
addText("Hello there...");
echo $myText;
the reason you have to do the is is bc $myText is not in the scope on the function. so to put it in scope, you have to tell the function which global variables to use
here is the demo: http://codepad.org/DeH89No6
Ok let me actually help you as opposed to just telling you how to get your current example to work. Basically, variables defined outside a custom function are not visible inside it. In your example your function has no idea what $myText is because it was declared out side of the functions scope. To pass variables to a function you will need to add them as parameters - the variables you pass with a function when you define and then when you call it, eg: function addText($param1, $param2).
As other have mentioned, you can allow functions to see certain variables by using the global keyword, but only really use this for abstract things like database connections, not the actually data you want to configure, do all that inside your function.
Hopefully this will help you understand PHP a bit more.
This is what you call variable scope. That's the part of the code where your variable is known as explained here.
To make a short story even shorter, even though your $myText inside the function shares the name with the one outside, they're completely different. They're different boxes, one in your room (the one inside your function) and one outside (the other one). Even if they're labeled the same, things you put into one of them wouldn't show up inside the other one as well.
There are two ways to do what you want to do.
First the easy but bad way: Make one big all including box by adding the global keyword to the one inside the function like posted before. This is like saying "Look outside for a box with this label and use this one."
But remember: Global variables are BAD.
Tempting as the dark side may be there is an other way...
Taking your box with you...
$myText = "";
function addText($existingText, $textToAdd) {
return $existingText . $textToAdd;
}
addText($myText, "Look, it's there!");
echo $myText;
May the source be with you.
get the function to return the string
function addText($textString){
$myText .= $textString;
return $myText
}
$myText = addText('string');
It has to do with variable scope. Basically $myText outside of addText is different from $myText inside the function. You could use this to set the variable inside the function to the one outside of it:
function addText($textString){
global $myText;
$myText .= $textString;
}
Just another take on it...
$myText = "The quick brown fox";
echo addText(" jumped over the lazy dog.", $myText);
function addText($addString,$toVar) {
$newString = $toVar.$addString;
return $newString;
}
the scope (life time of variables) of a function doesn't reach outside the function. To this simple task I don't see why you make a function. However, a variable passed into a function is copied into the functions scope, even if you name the variable the same it will not be the same. To be able to retrieve something outside the function you have to use a return statement.
(with C++, your approach would have been possible if the parameter to the function was passed as a reference)
But, a possible solution to your problem may be:
function addText($text){
return $text;
}
$myText .= addText("Hello there");
echo $myText;
But again, I don't see the reason to make a function for this simple task
PHP variable scope is a little different than scope in other languages that you might be used to. In order for the variable $myText to be visible inside the function addText, you must use the global keyword:
$myText = "";
function addText($textString){
global $myText; //added this line
$myText .= $textString;
}
addText("Hello there...");
echo $myText;
Alternatively, if the code is inside a class, you could make $myText a class-level variable using the keyword $this:
class SomeClass{
function __init__(){
$this->myText = "";
}
function addText($textString){
$this->myText .= $textString;
}
function someFunction(){
$this->addText("Hello there...");
echo $this->myText;
}
}
I have a function that searches for a string inside a text file. I want to use the same function to assign all lines to an array in case I am going to replace that string. So I will read the input file only once.
I have the search function working but I do not know how to deal with the array thing.
the code is something like that (I made the code sample much simpler,so please ignore the search function that actually isn't below)
function read_ini($config_file_name,$string){
$config_file = file($config_file_name);
foreach($config_file as $line) {
return_string = trim(substr($line,0,15));
$some_global_array = $line'
}
}
echo read_ini('config.ini','database.db')
if ($replaced) {file_put_contents('config.ini', $some_global_array);}
http://php.net/parse_ini_file
I know it doesn't answer the question, but it quite possibly removes the need for even having to ask.
The deal with globals, though, is that they must be defined at the top of the function as globals, or else they're considered part of the function's scope.
function write_global() {
global $foo;
$foo = 'bar';
}
write_global();
echo $foo; // bar