I would like to know if there is a simple way to use the matched pattern in a preg_replace as an index for the replacement value array.
e.g.
preg_replace("/\{[a-z_]*\}/i", "{$data_array[\1]}", $string);
Search for {xxx} and replace it with the value in $data_array['xxx'], where xxx is a pattern.
But this expression does not work as its invalid php.
I have written the following function, but I'd like to know if it is possible to do it simply. I could use a callback, but how would I pass the $data_array to it too?
function mailmerge($string, $data_array, $tags='{}')
{
$tag_start=$tags[0];
$tag_end =$tags[1];
if( (!stristr($string, $tag_start)) && (!stristr($string, $tag_end)) ) return $string;
while(list($key,$value)=each($data_array))
{
$patterns[$key]="/".preg_quote($tag_start.$key.$tag_end)."/";
}
ksort($patterns);
ksort($data_array);
return preg_replace($patterns, $data_array, $string);
}
From my head:
preg_replace_callback("/\{([a-z_]*)\}/i", function($m) use($data_array){
return $data_array[$m[1]];
}, $string);
Note: The above function requires PHP 5.3+.
Associative Array replacement - keep matched fragments if not found:
$words=array("_saudation_"=>"Hello", "_animal_"=>"cat", "_animal_sound_"=>"MEooow");
$source=" _saudation_! My Animal is a _animal_ and it says _animal_sound_ , _no_match_";
echo (preg_replace_callback("/\b_(\w*)_\b/", function($match) use ($words) { if(isset($words[$match[0]])){
return ($words[$match[0]]);}else{
return($match[0]);}
}, $source));
//returns: Hello! My Animal is a cat and it says MEooow , _no_match_
*Notice, thats although "_no_match_" lacks translation, it will match during regex, but
preserve its key.
you can use preg_replace_callback and write a function where you can use that array index, or else you can use the e modifier to evaluate the replacement string (though note that the e modifier is deprecated, so the callback function is better solution).
Related
I have a little piece of code in PHP that returns certain key-value pairs based upon if a value contains /includes a specific value: Code Runnable number 1
$example = array('1'=>'if you dont','2'=>'if you like','3'=>'if you know');
$searchword = 'like';
$matches = array_filter($example, function($var) use ($searchword) { return preg_match("/\b$searchword\b/i", $var); });
print_r($matches);
The function works perfectly if the array's value's words are separated, like above. The above code will return an array with all the key-value pairs with a value that includes $searchword. However, if the array's value's words aren't separated, like the below, the function will seemingly not detect the word inside the value(that is inside the array), therefore returning a blank array: Code Runnable number 2
$example = array('1'=>'ifyoudont','2'=>'ifyoulike','3'=>'ifyouknow');
$searchword = 'like';
$matches = array_filter($example, function($var) use ($searchword) { return preg_match("/\b$searchword\b/i", $var); });
print_r($matches);
How could I make the code detect if a value inside an array includes a specific string, even if the value's contents aren't spaced? I have tried searching and many other solutions and nothing seems to be working.
REGEX in this scenario are overkill, instead you can use strpos:
$matches = array_filter($example, function($var) use ($searchword) { return strpos($var,$searchword) !== FALSE; });
Incidentally, you don't need to use array_filter. There's already a function that filters an array based on a pattern.
$matches = preg_grep("/$searchword/", $example);
But if you aren't going to be using word boundaries, I agree with the other answer that you don't need to use a regular expression at all.
Just get rid of the word boundary \b from your regex:
return preg_match("/$searchword/i")
I know that the answer is probably simple and I am just not seeing it.
This code gets me an array of "tags" in the layout (eg [[tag]]), and I have an array of replacements that comes in with the request ($this->data). My first inclination was to use preg_match_all to get an array of all the tags and just pass in both arrays:
if(isset($this->layout))
{
ob_start();
include(VIEWS.'layouts/'.$this->layout.'.phtml');
$this->layout = ob_get_contents();
preg_match_all('/\[\[.*\]\]/', $this->layout, $tags);
print preg_replace($tags, $this->data, $this->layout);
}
But the arrays are not the same length (most of the time). The layout might reuse some tags, and the passed in data variables might not include some tags in the layout.
I feel like there must be a more efficient way to do this than doing a foreach and building the output in iterations.
This project is way too small to implement a full templating engine like Smarty or Twig... it is actually just a few pages and a few replacements. My client just wants a simple way to add things like page titles and email recipients, etc.
Any advice would be appreciated. Like I said, I am positive that it is something simple that I am overlooking.
EDIT:
$this->data is an array of replacement text that look like:
tag => replacement_text
EDIT 2:
If I user preg_match_all('/\[\[(.*)\]\]/', $this->layout, $tags); the array includes JUST the tags (no [[]]), I just need a way to match them up with the array of replacement strings in $this->data.
You can simply use str_replace for this job, creating an array of search strings and replacements from $this->data:
$search = array_map(function ($s) { return "[[$s]]"; }, array_keys($this->data));
$replacements = array_values($this->data);
echo str_replace($search, $replacements, $this->layout);
Demo on 3v4l.org
You don't need to get $tags by matching $this->layout, the information is all in the keys of $this->data. You just need to add [[...]] around the keys.
$tags = array_map(function($key) {
return '/\[\[' . preg_quote($key) . '\]\]';
}, array_keys($this->data));
Another solution is to use preg_replace_callback() to look the tag up in $this->data;
echo preg_replace_callback('/\[\[(.*?)\]\]/', function($matches) {
return $this->data[$matches[1]] ?? $matches[0];
}, $this->layout);
Note that I changed the regexp to use a non-greedy quantifier; your regexp will match from the beginning of the first tag to the end of the last tag.
If the tag isn't found in $this->data, ?? $matches[0] leaves it unchanged.
You could make use of preg_replace_callback:
$result = preg_replace_callback('/\[\[(?<tag>.*?)]]/', function ($matches) {
return $this->data[$matches['tag']] ?? $matches[0];
}, $this->layout);
Demo: https://3v4l.org/I9Vvh
Shorter PHP 7.4 version:
$result = preg_replace_callback('/\[\[(?<tag>.*?)]]/', fn($matches) =>
$this->data[$matches['tag']] ?? $matches[0], $this->layout);
Edited with the ?? $matches[0] (courtesy of #Barmar) -- this is basically the same answer, just leaving it in case the PHP 7.4 syntax is useful.
I have two functions in PHP, trimmer($string,$number) and toUrl($string). I want to trim the urls extracted with toUrl(), to 20 characters for example. from https://www.youtube.com/watch?v=HU3GZTNIZ6M to https://www.youtube.com/wa...
function trimmer($string,$number) {
$string = substr ($string, 0, $number);
return $string."...";
}
function toUrl($string) {
$regex="/[^\W ]+[^\s]+[.]+[^\" ]+[^\W ]+/i";
$string= preg_replace($regex, "<a href='\\0'>".trimmer("\\0",20)."</a>",$string);
return $string;
}
But the problem is that the value of the match return \\0 not a variable like $url which could be easily trimmed with the function trimmer().
The Question is how do I apply substr() to \\0 something like this substr("\\0",0,20)?
What you want is preg_replace_callback:
function _toUrl_callback($m) {
return "" . trimmer($m[0], 20) ."";
}
function toUrl($string) {
$regex = "/[^\W ]+[^\s]+[.]+[^\" ]+[^\W ]+/i";
$string = preg_replace_callback($regex, "_toUrl_callback", $string);
return $string;
}
Also note that (side notes wrt your question):
You have a syntax error, '$regex' is not going to work (they don't replace var names in single-quoted strings)
You may want to look for better regexps to match URLs, you'll find plenty of them with a quick search
You may want to run through htmlspecialchars() your matches (mainly problems with "&", but that depends how you escape the rest of the string.
EDIT: Made it more PHP 4 friendly, requested by the asker.
This seems to work ok:
function findImageTags($string) {
$pattern = '/<div(.*?)sourcefile="([^"]+)"(.*?)>(.*?)<\/div>/s';
return preg_replace($pattern, $this->generateImage("$2"), $string);
}
function generateImage($url){
return $url;
}
But when in the generateImage function I try to do something with the argument I can't because the value of the argument is $2 instead of the real value.
So this doesn't work:
function generateImage($url){
$array = explode('.', $url);
return $array;
}
by the way replacing s with e in the pattern doesn't seem to work as I think it's deprecated.
So how can I manipulate the value of the argument in generateImage() ?
What you want is probably preg_replace_callback instead of preg_replace. Here you can use a function which returns the replacement value.
The way you coded it now, the $this->generateImage("$2") code is executed at the moment you call preg_replace. It's not passed as a callback, but instead it's executed first, and the output is passed as the callback.
If you want to execute that function, you have to pass the PHP code as a string, and use the e modifier (http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php for more info).
return preg_replace($pattern, '$this->generateImage("$2")', $string);
Or use preg_replace_callback() of course.
Is it possible use str_replace() and use function in replace?
$value = "gal($data)";
$replace = str_replace($dat, $value, $string);
gal($data) is a function and I need replace one code for this function and show, but the script only give me finally this gal($data), and the function no show nothing
Is it possible use str_replace() for replace code and replace by the function or some similar method?
PHP has a function called preg_replace_callback that does this. When you pass it a callback function, it will pass each match through your function. You can choose to replace, based upon the matched value, or ignore it.
As an example, suppose I have a pattern that matches various strings, such as [a-z]+. I may not want to replace every instance with the same value, so I can call a function upon eat match found, and determine how I ought to respond:
function callback ($match) {
if ($match[0] === "Jonathan")
return "Superman";
return $match[0];
}
$subject = "This is about Jonathan.";
$pattern = "/[a-z]+/i";
$results = preg_replace_callback($pattern, "callback", $subject);
// This is about Superman.
echo $results;
Note in our callback function how I am able to return special values for certain matches, and not all matches.
Expanding Abbreviations
Another example would be a lookup. Suppose we wanted to find abbreviations of programming languages, and replace them with their full titles. We may have an array that has abbreviations as keys, with long-names as values. We could then use our callback ability to lookup the full-length names:
function lookup ($match) {
$langs = Array(
"JS" => "JavaScript",
"CSS" => "Cascading Style Sheets",
"JSP" => "Java Server Pages"
);
return $langs[$match[0]] ?: $match[0];
}
$subject = "Does anybody know JS? Or CSS maybe? What about PHP?";
$pattern = "/(js|css|jsp)/i";
$results = preg_replace_callback($pattern, "lookup", $subject);
// Does anybody know JavaScript? Or Cascading Style Sheets maybe? What about PHP?
echo $results;
So every time our regular expression finds a match, it passes the match through lookup, and we can return the appropriate value, or the original value.