If you had the string
'Old string Old more string Old some more string'
and you wanted to get
'New1 string New2 more string New3 some more string'
how would you do it?
In other words, you need to replace all instances of 'Old' with variable string 'New'.$i. How can it be done?
An iterative solution that doesn't need regular expressions:
$str = 'Old string Old more string Old some more string';
$old = 'Old';
$new = 'New';
$i = 1;
$tmpOldStrLength = strlen($old);
while (($offset = strpos($str, $old, $offset)) !== false) {
$str = substr_replace($str, $new . ($i++), $offset, $tmpOldStrLength);
}
$offset in strpos() is just a little bit micro-optimization. I don't know, if it's worth it (in fact I don't even know, if it changes anything), but the idea is that we don't need to search for $old in the substring that is already processed.
See Demo
Old string Old more string Old some more string
New1 string New2 more string New3 some more string
Use preg_replace_callback.
$count = 0;
$str = preg_replace_callback(
'~Old~',
create_function('$matches', 'return "New".$count++;'),
$str
);
From the PHP manual on str_replace:
Replace all occurrences of the search string with the replacement string
mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )
search
The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles.
replace
The replacement value that replaces found search values. An array may be used to designate multiple replacements.
subject
The string or array being searched and replaced on, otherwise known as the haystack.
If subject is an array, then the search and replace is performed with every entry of subject, and the return value is an array as well.
count
If passed, this will be set to the number of replacements performed.
Use:
$str = 'Old string Old more string Old some more string';
$i = 1;
while (preg_match('/Old/', $str)) {
$str = preg_replace('/Old/', 'New'.$i++, $str, 1);
}
echo $str,"\n";
Output:
New1 string New2 more string New3 some more string
I had some similar solution like KingCrunch's, but as he already answered it, I was wondering about a str_replace variant with a callback for replacements and came up with this (Demo):
$subject = array('OldOldOld', 'Old string Old more string Old some more string');
$search = array('Old', 'string');
$replace = array(
function($found, $count) {return 'New'.$count;},
function($found, $count) {static $c=0; return 'String'.(++$c);}
);
$replace = array();
print_r(str_ureplace($search, $replace, $subject));
/**
* str_ureplace
*
* str_replace like function with callback
*
* #param string|array search
* #param callback|array $replace
* #param string|array $subject
* #param int $replace_count
* #return string|array subject with replaces, FALSE on error.
*/
function str_ureplace($search, $replace, $subject, &$replace_count = null) {
$replace_count = 0;
// Validate input
$search = array_values((array) $search);
$searchCount = count($search);
if (!$searchCount) {
return $subject;
}
foreach($search as &$v) {
$v = (string) $v;
}
unset($v);
$replaceSingle = is_callable($replace);
$replace = $replaceSingle ? array($replace) : array_values((array) $replace);
foreach($replace as $index=>$callback) {
if (!is_callable($callback)) {
throw new Exception(sprintf('Unable to use %s (#%d) as a callback', gettype($callback), $index));
}
}
// Search and replace
$subjectIsString = is_string($subject);
$subject = (array) $subject;
foreach($subject as &$haystack) {
if (!is_string($haystack)) continue;
foreach($search as $key => $needle) {
if (!$len = strlen($needle))
continue;
$replaceSingle && $key = 0;
$pos = 0;
while(false !== $pos = strpos($haystack, $needle, $pos)) {
$replaceWith = isset($replace[$key]) ? call_user_func($replace[$key], $needle, ++$replace_count) : '';
$haystack = substr_replace($haystack, $replaceWith, $pos, $len);
}
}
}
unset($haystack);
return $subjectIsString ? reset($subject) : $subject;
}
Related
I have string like this in database (the actual string contains 100s of word and 10s of variable):
I am a {$club} fan
I echo this string like this:
$club = "Barcelona";
echo $data_base[0]['body'];
My output is I am a {$club} fan. I want I am a Barcelona fan. How can I do this?
Use strtr. It will translate parts of a string.
$club = "Barcelona";
echo strtr($data_base[0]['body'], array('{$club}' => $club));
For multiple values (demo):
$data_base[0]['body'] = 'I am a {$club} fan.'; // Tests
$vars = array(
'{$club}' => 'Barcelona',
'{$tag}' => 'sometext',
'{$anothertag}' => 'someothertext'
);
echo strtr($data_base[0]['body'], $vars);
Program Output:
I am a Barcelona fan.
I would suggest the sprintf() function.
Instead of storing I am a {$club} fan, use I am a %s fan, so your echo command would go like:
$club = "Barcelona";
echo sprintf($data_base[0]['body'],$club);
Output: I am a Barcelona fan
That would give you the freedom of use that same code with any other variable (and you don't even have to remember the variable name).
So this code is also valid with the same string:
$food = "French fries";
echo sprintf($data_base[0]['body'], $food);
Output: I am a French fries fan
$language = "PHP";
echo sprintf($data_base[0]['body'], $language);
Output: I am a PHP fan
/**
* A function to fill the template with variables, returns filled template.
*
* #param string $template A template with variables placeholders {$variable}.
* #param array $variables A key => value store of variable names and values.
*
* #return string
*/
public function replaceVariablesInTemplate($template, array $variables){
return preg_replace_callback('#{(.*?)}#',
function($match) use ($variables){
$match[1] = trim($match[1], '$');
return $variables[$match[1]];
},
' ' . $template . ' ');
}
Edit: This answer still gets upvotes, so people need to be aware that there's a security vulnerability in the naive interpolation technique present in the below code snippets. An adversary could include arbitrary variables in the input string which would reveal information about the server or other data in the runtime variable register. This is due to the way the general expression search is performed in that it finds any arbitrary variable name pattern, and then uses those variable names verbatim in the subsequent compact call. This causes clients to control server-side behavior similar to eval. I'm leaving this answer for posterity.
You are looking for nested string interpolation. A theory can be read in the blog post Wanted: PHP core function for dynamically performing double-quoted string variable interpolation.
The major problem is that you don't really know all of the variables available, or there may be too many to list.
Consider the following tested code snippet. I stole the regex from Mohammad Mohsenipur.
$testA = '123';
$testB = '456';
$testC = '789';
$t = '{$testA} adsf {$testB}adf 32{$testC} fddd{$testA}';
echo 'before: ' . $t . "\n";
preg_match_all('~\{\$(.*?)\}~si', $t, $matches);
if ( isset($matches[1])) {
$r = compact($matches[1]);
foreach ( $r as $var => $value ) {
$t = str_replace('{$' . $var . '}', $value, $t);
}
}
echo 'after: ' . $t . "\n";
Your code may be:
$club = 'Barcelona';
$tmp = $data_base[0]['body'];
preg_match_all('~\{\$(.*?)\}~si', $tmp, $matches);
if ( isset($matches[1])) {
$r = compact($matches[1]);
foreach ( $r as $var => $value ) {
$tmp = str_replace('{$' . $var . '}', $value, $tmp);
}
}
echo $tmp;
if (preg_match_all('#\$([a-zA-Z0-9]+)#', $q, $matches, PREG_SET_ORDER));
{
foreach ($matches as $m)
{
eval('$q = str_replace(\'' . $m[0] . '\', $' . $m[1] . ', $q);');
}
}
This matches all $variables and replaces them with the value.
I didn't include the {}'s, but it shouldn't be too hard to add them something like this...
if (preg_match_all('#\{\$([a-zA-Z0-9]+)\}#', $q, $matches, PREG_SET_ORDER));
{
foreach ($matches as $m)
{
eval('$q = str_replace(\'' . $m[0] . '\', $' . $m[1] . ', $q);');
}
}
Though it seems a bit slower than hard coding each variable. And it introduces a security hole with eval. That is why my regular expression is so limited. To limit the scope of what eval can grab.
I wrote my own regular expression tester with Ajax, so I could see, as I type, if my expression is going to work. I have variables I like to use in my expressions so that I don't need to retype the same bit for each expression.
I've found these approaches useful at times:
$name = 'Groot';
$string = 'I am {$name}';
echo eval('return "' . $string . '";');
$data = array('name' => 'Groot');
$string = 'I am {$data[name]}';
echo eval('return "' . $string . '";');
$name = 'Groot';
$data = (object)get_defined_vars();
$string = 'I am {$data->name}';
echo eval('return "' . $string . '";');
Here is my solution:
$club = "Barcelona";
$string = 'I am a {$club} fan';
preg_match_all("/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", $string, $matches);
foreach ($matches[0] as $key => $var_name) {
if (!isset($GLOBALS[$matches[1][$key]]))
$GLOBALS[$matches[1][$key]] = 'default value';
$string = str_replace($var_name, $GLOBALS[$matches[1][$key]], $string);
}
You can use a simple parser that replaces {$key} with a value from a map if it exists.
Use it like:
$text = templateWith('hello $item}', array('item' => 'world'...));`
My first version is:
/**
* Template with a string and simple map.
* #param string $template
* #param array $substitutions map of substitutions.
* #return string with substitutions applied.
*/
function templateWith(string $template, array $substitutions) {
$state = 0; // forwarding
$charIn = preg_split('//u', $template, -1, PREG_SPLIT_NO_EMPTY);
$charOut = array();
$count = count($charIn);
$key = array();
$i = 0;
while ($i < $count) {
$char = $charIn[$i];
switch ($char) {
case '{':
if ($state === 0) {
$state = 1;
}
break;
case '}':
if ($state === 2) {
$ks = join('', $key);
if (array_key_exists($ks, $substitutions)) {
$charOut[] = $substitutions[$ks];
}
$key = array();
$state = 0;
}
break;
case '$': if ($state === 1) {
$state = 2;
}
break;
case '\\': if ($state === 0) {
$i++;
$charOut[] = $charIn[$i];
}
continue;
default:
switch ($state) {
default:
case 0: $charOut[] = $char;
break;
case 2: $key[] = $char;
break;
}
}
$i++;
}
return join('', $charOut);
}
Maybe the following snippet is (partly) usefull for someone.
/**
* Access an object property using "dot" notation
*
* #param object $object
* #param string|null $path
* #param mixed $default
* #return mixed
*/
function xobject_get(object $object, $path, $default = null) {
return array_reduce(explode('.', $path), function ($o, $p) use ($default) {
return is_numeric($p) ? $o[$p] ?? $default : $o->$p ?? $default;
}, $object);
}
/**
* Access an array's property using "dot" notation
*
* #param array $array
* #param string|null $path
* #param mixed $default
* #return mixed
*/
function xarray_get(array $array, $path, $default = null) {
return array_reduce(explode('.', $path), function ($a, $p) use ($default) {
return $a[$p] ?? $default;
}, $array);
}
/**
* Replaces placeholders from a string with object or array values using "dot" notation
*
* Example:
* "The book {title} was written by {author.name}" becomes "The book Harry Potter was written by J.K. Rowling"
*
* #param array|object $data
* #param string $template
* #return string
*/
function render_template($data, string $template) {
preg_match_all("/\{([^\}]*)\}/", $template, $matches);
$replace = [];
foreach ($matches[1] as $param) {
$replace['{'.$param.'}'] = is_object($data) ? xobject_get($data, $param) : xarray_get($data, $param);
}
return strtr($template, $replace);
}
Try the preg_replace PHP function.
<?php
$club = "Barcelona";
echo $string = preg_replace('#\{.*?\}#si', $club, 'I am a {$club} fan');
?>
You can use preg_replace_callback for getting a variable name like:
$data_base[0]['body'] = preg_replace_callback(
'#{(.*?)}#',
function($m) {
$m[1] = trim($m[1], '$');
return $this->$m[1];
},
' ' . $data_base[0]['body'] . ' '
);
Attention: This code I wrote is for class($this);. You can declare a variable into the class. Then use this code for detecting the variables and replace them like:
<?php
class a {
function __construct($array) {
foreach($array as $key => $val) {
$this->$key = $val;
}
}
function replace($str){
return preg_replace_callback(
'#{(.*?)}#', function($m) {$m[1] = trim($m[1], '$'); return $this->$m[1];},
' ' . $str . ' ');
}
}
$obj = new a(array('club' => 3523));
echo $obj->replace('I am a {$club} fan');
Output:
I am a 3523 fan
For your case, honestly, I do not see a reason not to use eval :)
Here is some extra way to define your variables if they are too into your database:
$my_variable_name = 'club'; //coming from database
$my_value = 'Barcelona'; //coming from database
$my_msg= 'I am a {$club} fan'; //coming from database
$$my_variable_name = $my_value; // creating variable $club dinamically
$my_msg = eval("return \"$my_msg\";"); // eating the forbidden fruit
echo $my_msg; // prints 'I am Barcelona fan'
This code is fully tested and working with php 7.
But if you allow your users to define such strings into your database, better don't do it.
You should run eval only with trusted data.
Something like this should solve your problem:
$club = "Barcelona";
$var = 'I am a {$club} fan';
$res = preg_replace('/\{\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/e', "$$1", $var);
echo "$res\n";
It's a one-line preg_replace.
With PHP 5.5, /e modifier is deprecated. You can use a callback instead:
$club = "Barcelona";
$var = 'I am a {$club} fan';
$res = preg_replace_callback('/\{\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/',
create_function(
'$matches',
'extract($GLOBALS, EXTR_REFS | EXTR_SKIP); return $$matches[1];'),
$var);
echo "$res\n";
Note that this uses a hack of importing all global variables. This may not be exactly what you want. Possibly using closures would be a better idea.
I have string like this in database (the actual string contains 100s of word and 10s of variable):
I am a {$club} fan
I echo this string like this:
$club = "Barcelona";
echo $data_base[0]['body'];
My output is I am a {$club} fan. I want I am a Barcelona fan. How can I do this?
Use strtr. It will translate parts of a string.
$club = "Barcelona";
echo strtr($data_base[0]['body'], array('{$club}' => $club));
For multiple values (demo):
$data_base[0]['body'] = 'I am a {$club} fan.'; // Tests
$vars = array(
'{$club}' => 'Barcelona',
'{$tag}' => 'sometext',
'{$anothertag}' => 'someothertext'
);
echo strtr($data_base[0]['body'], $vars);
Program Output:
I am a Barcelona fan.
I would suggest the sprintf() function.
Instead of storing I am a {$club} fan, use I am a %s fan, so your echo command would go like:
$club = "Barcelona";
echo sprintf($data_base[0]['body'],$club);
Output: I am a Barcelona fan
That would give you the freedom of use that same code with any other variable (and you don't even have to remember the variable name).
So this code is also valid with the same string:
$food = "French fries";
echo sprintf($data_base[0]['body'], $food);
Output: I am a French fries fan
$language = "PHP";
echo sprintf($data_base[0]['body'], $language);
Output: I am a PHP fan
/**
* A function to fill the template with variables, returns filled template.
*
* #param string $template A template with variables placeholders {$variable}.
* #param array $variables A key => value store of variable names and values.
*
* #return string
*/
public function replaceVariablesInTemplate($template, array $variables){
return preg_replace_callback('#{(.*?)}#',
function($match) use ($variables){
$match[1] = trim($match[1], '$');
return $variables[$match[1]];
},
' ' . $template . ' ');
}
Edit: This answer still gets upvotes, so people need to be aware that there's a security vulnerability in the naive interpolation technique present in the below code snippets. An adversary could include arbitrary variables in the input string which would reveal information about the server or other data in the runtime variable register. This is due to the way the general expression search is performed in that it finds any arbitrary variable name pattern, and then uses those variable names verbatim in the subsequent compact call. This causes clients to control server-side behavior similar to eval. I'm leaving this answer for posterity.
You are looking for nested string interpolation. A theory can be read in the blog post Wanted: PHP core function for dynamically performing double-quoted string variable interpolation.
The major problem is that you don't really know all of the variables available, or there may be too many to list.
Consider the following tested code snippet. I stole the regex from Mohammad Mohsenipur.
$testA = '123';
$testB = '456';
$testC = '789';
$t = '{$testA} adsf {$testB}adf 32{$testC} fddd{$testA}';
echo 'before: ' . $t . "\n";
preg_match_all('~\{\$(.*?)\}~si', $t, $matches);
if ( isset($matches[1])) {
$r = compact($matches[1]);
foreach ( $r as $var => $value ) {
$t = str_replace('{$' . $var . '}', $value, $t);
}
}
echo 'after: ' . $t . "\n";
Your code may be:
$club = 'Barcelona';
$tmp = $data_base[0]['body'];
preg_match_all('~\{\$(.*?)\}~si', $tmp, $matches);
if ( isset($matches[1])) {
$r = compact($matches[1]);
foreach ( $r as $var => $value ) {
$tmp = str_replace('{$' . $var . '}', $value, $tmp);
}
}
echo $tmp;
if (preg_match_all('#\$([a-zA-Z0-9]+)#', $q, $matches, PREG_SET_ORDER));
{
foreach ($matches as $m)
{
eval('$q = str_replace(\'' . $m[0] . '\', $' . $m[1] . ', $q);');
}
}
This matches all $variables and replaces them with the value.
I didn't include the {}'s, but it shouldn't be too hard to add them something like this...
if (preg_match_all('#\{\$([a-zA-Z0-9]+)\}#', $q, $matches, PREG_SET_ORDER));
{
foreach ($matches as $m)
{
eval('$q = str_replace(\'' . $m[0] . '\', $' . $m[1] . ', $q);');
}
}
Though it seems a bit slower than hard coding each variable. And it introduces a security hole with eval. That is why my regular expression is so limited. To limit the scope of what eval can grab.
I wrote my own regular expression tester with Ajax, so I could see, as I type, if my expression is going to work. I have variables I like to use in my expressions so that I don't need to retype the same bit for each expression.
I've found these approaches useful at times:
$name = 'Groot';
$string = 'I am {$name}';
echo eval('return "' . $string . '";');
$data = array('name' => 'Groot');
$string = 'I am {$data[name]}';
echo eval('return "' . $string . '";');
$name = 'Groot';
$data = (object)get_defined_vars();
$string = 'I am {$data->name}';
echo eval('return "' . $string . '";');
Here is my solution:
$club = "Barcelona";
$string = 'I am a {$club} fan';
preg_match_all("/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", $string, $matches);
foreach ($matches[0] as $key => $var_name) {
if (!isset($GLOBALS[$matches[1][$key]]))
$GLOBALS[$matches[1][$key]] = 'default value';
$string = str_replace($var_name, $GLOBALS[$matches[1][$key]], $string);
}
You can use a simple parser that replaces {$key} with a value from a map if it exists.
Use it like:
$text = templateWith('hello $item}', array('item' => 'world'...));`
My first version is:
/**
* Template with a string and simple map.
* #param string $template
* #param array $substitutions map of substitutions.
* #return string with substitutions applied.
*/
function templateWith(string $template, array $substitutions) {
$state = 0; // forwarding
$charIn = preg_split('//u', $template, -1, PREG_SPLIT_NO_EMPTY);
$charOut = array();
$count = count($charIn);
$key = array();
$i = 0;
while ($i < $count) {
$char = $charIn[$i];
switch ($char) {
case '{':
if ($state === 0) {
$state = 1;
}
break;
case '}':
if ($state === 2) {
$ks = join('', $key);
if (array_key_exists($ks, $substitutions)) {
$charOut[] = $substitutions[$ks];
}
$key = array();
$state = 0;
}
break;
case '$': if ($state === 1) {
$state = 2;
}
break;
case '\\': if ($state === 0) {
$i++;
$charOut[] = $charIn[$i];
}
continue;
default:
switch ($state) {
default:
case 0: $charOut[] = $char;
break;
case 2: $key[] = $char;
break;
}
}
$i++;
}
return join('', $charOut);
}
Maybe the following snippet is (partly) usefull for someone.
/**
* Access an object property using "dot" notation
*
* #param object $object
* #param string|null $path
* #param mixed $default
* #return mixed
*/
function xobject_get(object $object, $path, $default = null) {
return array_reduce(explode('.', $path), function ($o, $p) use ($default) {
return is_numeric($p) ? $o[$p] ?? $default : $o->$p ?? $default;
}, $object);
}
/**
* Access an array's property using "dot" notation
*
* #param array $array
* #param string|null $path
* #param mixed $default
* #return mixed
*/
function xarray_get(array $array, $path, $default = null) {
return array_reduce(explode('.', $path), function ($a, $p) use ($default) {
return $a[$p] ?? $default;
}, $array);
}
/**
* Replaces placeholders from a string with object or array values using "dot" notation
*
* Example:
* "The book {title} was written by {author.name}" becomes "The book Harry Potter was written by J.K. Rowling"
*
* #param array|object $data
* #param string $template
* #return string
*/
function render_template($data, string $template) {
preg_match_all("/\{([^\}]*)\}/", $template, $matches);
$replace = [];
foreach ($matches[1] as $param) {
$replace['{'.$param.'}'] = is_object($data) ? xobject_get($data, $param) : xarray_get($data, $param);
}
return strtr($template, $replace);
}
Try the preg_replace PHP function.
<?php
$club = "Barcelona";
echo $string = preg_replace('#\{.*?\}#si', $club, 'I am a {$club} fan');
?>
You can use preg_replace_callback for getting a variable name like:
$data_base[0]['body'] = preg_replace_callback(
'#{(.*?)}#',
function($m) {
$m[1] = trim($m[1], '$');
return $this->$m[1];
},
' ' . $data_base[0]['body'] . ' '
);
Attention: This code I wrote is for class($this);. You can declare a variable into the class. Then use this code for detecting the variables and replace them like:
<?php
class a {
function __construct($array) {
foreach($array as $key => $val) {
$this->$key = $val;
}
}
function replace($str){
return preg_replace_callback(
'#{(.*?)}#', function($m) {$m[1] = trim($m[1], '$'); return $this->$m[1];},
' ' . $str . ' ');
}
}
$obj = new a(array('club' => 3523));
echo $obj->replace('I am a {$club} fan');
Output:
I am a 3523 fan
For your case, honestly, I do not see a reason not to use eval :)
Here is some extra way to define your variables if they are too into your database:
$my_variable_name = 'club'; //coming from database
$my_value = 'Barcelona'; //coming from database
$my_msg= 'I am a {$club} fan'; //coming from database
$$my_variable_name = $my_value; // creating variable $club dinamically
$my_msg = eval("return \"$my_msg\";"); // eating the forbidden fruit
echo $my_msg; // prints 'I am Barcelona fan'
This code is fully tested and working with php 7.
But if you allow your users to define such strings into your database, better don't do it.
You should run eval only with trusted data.
Something like this should solve your problem:
$club = "Barcelona";
$var = 'I am a {$club} fan';
$res = preg_replace('/\{\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/e', "$$1", $var);
echo "$res\n";
It's a one-line preg_replace.
With PHP 5.5, /e modifier is deprecated. You can use a callback instead:
$club = "Barcelona";
$var = 'I am a {$club} fan';
$res = preg_replace_callback('/\{\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/',
create_function(
'$matches',
'extract($GLOBALS, EXTR_REFS | EXTR_SKIP); return $$matches[1];'),
$var);
echo "$res\n";
Note that this uses a hack of importing all global variables. This may not be exactly what you want. Possibly using closures would be a better idea.
Given
$str = "asd/fgh/jkl/123
If we want to get string piece after last slash , we can use function strrchr() right?
In php not function, to get string piece, before last slah, that is asd/fgh/jkl ?
I know this can make via regex or other way, I am asking about internal function?
You can use
$str = "asd/fgh/jkl/123";
echo substr($str, 0,strrpos($str, '/'));
Output
asd/fgh/jkl
$str = "asd/fgh/jkl/123";
$lastPiece = end(explode("/", $str));
echo $lastPiece;
output: 123;
explode() converts the string into an array using "/" as a separator (you can pick the separator)
end() returns the last item of the array
You can do this by:
explode — Split a string by string (Documentation)
$pieces = explode("/", $str );
example
$str = "asd/fgh/jkl/123";
$pieces = explode("/", $str );
print_r($pieces);
$count= count($pieces);
echo $pieces[$count-1]; //or
echo end($pieces);
Codepad
Use this powerful custom function
/* $position = false and $sub = false show result of before first occurance of $needle */
/* $position = true and $sub false show result of before last occurance of $needle */
/* $position = false and $sub = true show result of after first occurance of $needle */
/* $position = true and $sub true show result of after last occurance of $needle */
function CustomStrStr($str,$needle,$position = false,$sub = false)
{
$Isneedle = strpos($str,$needle);
if ($Isneedle === false)
return false;
$needlePos =0;
$return;
if ( $position === false )
$needlePos = strpos($str,$needle);
else
$needlePos = strrpos($str,$needle);
if ($sub === false)
$return = substr($str,0,$needlePos);
else
$return = substr($str,$needlePos+strlen($needle));
return $return;
}
I want an if statement that uses same thingy like mysql something LIKE '%something%'
I want to build an if statement in php.
if ($something is like %$somethingother%)
Is it possible?
The reason for me asking this question is that I don't want to change the MySQL command, it's a long page with many stuff on it, I don't want to build a different function for this.
Let me know if this is possible, if possible then how to do it .
if ($something is like %$somethingother%)
Is it possible?
no.
I don't want to change the MySQL command, it's a long page with many stuff on it
Use some good editor, that supports regular expressions in find & replace, and turn it to something like:
if(stripos($something, $somethingother) !== FALSE){
}
I know, this question isn't actual but I've solved similar problem :)
My solution:
/**
* SQL Like operator in PHP.
* Returns TRUE if match else FALSE.
* #param string $pattern
* #param string $subject
* #return bool
*/
function like_match($pattern, $subject)
{
$pattern = str_replace('%', '.*', preg_quote($pattern, '/'));
return (bool) preg_match("/^{$pattern}$/i", $subject);
}
Examples:
like_match('%uc%','Lucy'); //TRUE
like_match('%cy', 'Lucy'); //TRUE
like_match('lu%', 'Lucy'); //TRUE
like_match('%lu', 'Lucy'); //FALSE
like_match('cy%', 'Lucy'); //FALSE
look on strstr function
Use this function which works same like SQL LIKE operator but it will return boolean value and you can make your own condition with one more if statement
function like($str, $searchTerm) {
$searchTerm = strtolower($searchTerm);
$str = strtolower($str);
$pos = strpos($str, $searchTerm);
if ($pos === false)
return false;
else
return true;
}
$found = like('Apple', 'app'); //returns true
$notFound = like('Apple', 'lep'); //returns false
if($found){
// This will execute only when the text is like the desired string
}
Use function, that search string in another string like: strstr, strpos, substr_count.
strpos() is not working for so i have to use this preg_match()
$a = 'How are you?';
if (preg_match('/\bare\b/', $a)) {
echo 'true';
}
like in this e.g i am matching with word "are"
hope for someone it will be helpful
But you will have to give lowercase string then it will work fine.
Example of strstr function:
$myString = "Hello, world!";
echo strstr( $myString, "wor" ); // Displays 'world!'
echo ( strstr( $myString, "xyz" ) ? "Yes" : "No" ); // Displays 'No'
If you have access to a MySQL server, send a query like this with MySQLi:
$SQL="select case when '$Value' like '$Pattern' then 'True' else 'False' end as Result";
$Result=$MySQLi->query($SQL)->fetch_all(MYSQLI_ASSOC)[0]['Result'];
Result will be a string containing True or False. Let PHP do what it's good for and use SQL for likes.
I came across this requirement recently and came up with this:
/**
* Removes the diacritical marks from a string.
*
* Diacritical marks: {#link https://unicode-table.com/blocks/combining-diacritical-marks/}
*
* #param string $string The string from which to strip the diacritical marks.
* #return string Stripped string.
*/
function stripDiacriticalMarks(string $string): string
{
return preg_replace('/[\x{0300}-\x{036f}]/u', '', \Normalizer::normalize($string , \Normalizer::FORM_KD));
}
/**
* Checks if the string $haystack is like $needle, $needle can contain '%' and '_'
* characters which will behave as if used in a SQL LIKE condition. Character escaping
* is supported with '\'.
*
* #param string $haystack The string to check if it is like $needle.
* #param string $needle The string used to check if $haystack is like it.
* #param bool $ai Whether to check likeness in an accent-insensitive manner.
* #param bool $ci Whether to check likeness in a case-insensitive manner.
* #return bool True if $haystack is like $needle, otherwise, false.
*/
function like(string $haystack, string $needle, bool $ai = true, bool $ci = true): bool
{
if ($ai) {
$haystack = stripDiacriticalMarks($haystack);
$needle = stripDiacriticalMarks($needle);
}
$needle = preg_quote($needle, '/');
$tokens = [];
$needleLength = strlen($needle);
for ($i = 0; $i < $needleLength;) {
if ($needle[$i] === '\\') {
$i += 2;
if ($i < $needleLength) {
if ($needle[$i] === '\\') {
$tokens[] = '\\\\';
$i += 2;
} else {
$tokens[] = $needle[$i];
++$i;
}
} else {
$tokens[] = '\\\\';
}
} else {
switch ($needle[$i]) {
case '_':
$tokens[] = '.';
break;
case '%':
$tokens[] = '.*';
break;
default:
$tokens[] = $needle[$i];
break;
}
++$i;
}
}
return preg_match('/^' . implode($tokens) . '$/u' . ($ci ? 'i' : ''), $haystack) === 1;
}
/**
* Escapes a string in a way that `UString::like` will match it as-is, thus '%' and '_'
* would match a literal '%' and '_' respectively (and not behave as in a SQL LIKE
* condition).
*
* #param string $str The string to escape.
* #return string The escaped string.
*/
function escapeLike(string $str): string
{
return strtr($str, ['\\' => '\\\\', '%' => '\%', '_' => '\_']);
}
The code above is unicode aware to be able to catch cases like:
like('Hello 🙃', 'Hello _'); // true
like('Hello 🙃', '_e%o__'); // true
like('asdfas \\🙃H\\\\%🙃É\\l\\_🙃\\l\\o asdfasf', '%' . escapeLike('\\🙃h\\\\%🙃e\\l\\_🙃\\l\\o') . '%'); // true
You can try all of this on https://3v4l.org/O9LX0
I think it's worth mentioning the str_contains() function available in PHP 8 which performs a case-sensitive check indicating whether a string is contained within another string, returning true or false.
Example taken from the documentation:
$string = 'The lazy fox jumped over the fence';
if (str_contains($string, 'lazy')) {
echo "The string 'lazy' was found in the string\n";
}
if (str_contains($string, 'Lazy')) {
echo 'The string "Lazy" was found in the string';
} else {
echo '"Lazy" was not found because the case does not match';
}
//The above will output:
//The string 'lazy' was found in the string
//"Lazy" was not found because the case does not match
See the full documentation here.
like_match() example is the best
this one witch SQL reqest is simple (I used it before), but works slowly then like_match() and exost database server resources when you iterate by array keys and every round hit db server with request usually not necessery. I made it faster ferst cutting / shrink array by pattern elements but regexp on array works always faster.
I like like_match() :)
I want to make sure some string replacement's I'm running are multi byte safe. I've found a few mb_str_replace functions around the net but they're slow. I'm talking 20% increase after passing maybe 500-900 bytes through it.
Any recommendations? I'm thinking about using preg_replace as it's native and compiled in so it might be faster. Any thoughts would be appreciated.
As said there, str_replace is safe to use in utf-8 contexts, as long as all parameters are utf-8 valid, because it won't be any ambiguous match between both multibyte encoded strings. If you check the validity of your input, then you have no need to look for a different function.
As encoding is a real challenge when there are inputs from everywhere (utf8 or others), I prefer using only multibyte-safe functions. For str_replace, I am using this one which is fast enough.
if (!function_exists('mb_str_replace'))
{
function mb_str_replace($search, $replace, $subject, &$count = 0)
{
if (!is_array($subject))
{
$searches = is_array($search) ? array_values($search) : array($search);
$replacements = is_array($replace) ? array_values($replace) : array($replace);
$replacements = array_pad($replacements, count($searches), '');
foreach ($searches as $key => $search)
{
$parts = mb_split(preg_quote($search), $subject);
$count += count($parts) - 1;
$subject = implode($replacements[$key], $parts);
}
}
else
{
foreach ($subject as $key => $value)
{
$subject[$key] = mb_str_replace($search, $replace, $value, $count);
}
}
return $subject;
}
}
Here's my implementation, based off Alain's answer:
/**
* Replace all occurrences of the search string with the replacement string. Multibyte safe.
*
* #param string|array $search The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles.
* #param string|array $replace The replacement value that replaces found search values. An array may be used to designate multiple replacements.
* #param string|array $subject The string or array being searched and replaced on, otherwise known as the haystack.
* If subject is an array, then the search and replace is performed with every entry of subject, and the return value is an array as well.
* #param string $encoding The encoding parameter is the character encoding. If it is omitted, the internal character encoding value will be used.
* #param int $count If passed, this will be set to the number of replacements performed.
* #return array|string
*/
public static function mbReplace($search, $replace, $subject, $encoding = 'auto', &$count=0) {
if(!is_array($subject)) {
$searches = is_array($search) ? array_values($search) : [$search];
$replacements = is_array($replace) ? array_values($replace) : [$replace];
$replacements = array_pad($replacements, count($searches), '');
foreach($searches as $key => $search) {
$replace = $replacements[$key];
$search_len = mb_strlen($search, $encoding);
$sb = [];
while(($offset = mb_strpos($subject, $search, 0, $encoding)) !== false) {
$sb[] = mb_substr($subject, 0, $offset, $encoding);
$subject = mb_substr($subject, $offset + $search_len, null, $encoding);
++$count;
}
$sb[] = $subject;
$subject = implode($replace, $sb);
}
} else {
foreach($subject as $key => $value) {
$subject[$key] = self::mbReplace($search, $replace, $value, $encoding, $count);
}
}
return $subject;
}
His doesn't accept a character encoding, although I suppose you could set it via mb_regex_encoding.
My unit tests pass:
function testMbReplace() {
$this->assertSame('bbb',Str::mbReplace('a','b','aaa','auto',$count1));
$this->assertSame(3,$count1);
$this->assertSame('ccc',Str::mbReplace(['a','b'],['b','c'],'aaa','auto',$count2));
$this->assertSame(6,$count2);
$this->assertSame("\xbf\x5c\x27",Str::mbReplace("\x27","\x5c\x27","\xbf\x27",'iso-8859-1'));
$this->assertSame("\xbf\x27",Str::mbReplace("\x27","\x5c\x27","\xbf\x27",'gbk'));
}
Top rated note on http://php.net/manual/en/ref.mbstring.php#109937 says str_replace works for multibyte strings.