What is the correct way to use boolean operators to have multiple conditions in a while loop?
I have this script here that I feel SHOULD work, however it comes up blank when I run it.
$string = '"""JOHN"" <31255555656>","DAHDI/1-1",3948723,,"",';
for ($i=0; $i<strlen($string); $i++) {
if ($string[$i] == chr(34)) {
// chr 34 is ascii value for double quote chr 44 is for comma
while (($string[$i] != chr(34)) && ($string[$i+1] != chr(44))) {
echo $string[$i];
$i++;
}
}
}
Ideally what i want is for the script to echo the the given string up until the pointer ($i) reaches a double quote and one ahead of it is a comma. I feel like this is very simple and I am clearly missing something obvious.
if ($string[$i] == chr(34)) {
while (($string[$i] != chr(34)) && ($string[$i+1] != chr(44)))
Your if statement will run if this character is a quote. The while statement will run for as long as this character isn't a quote (and isn't a comma)
The two are exclusive. I think you might be able to get around it by incrementing $i after the if statement, but there's almost certainly an easier way of doing this.
The if and the while says the opposite condition... which means the while will never have a true. I guess you wanted to check the next character ?
$string = '"""JOHN"" <31255555656>","DAHDI/1-1",3948723,,"",';
for ($i=0; $i<strlen($string); $i++)
{
if ($string[$i] == chr(34))
{
while (($string[$i+1] != chr(34)) && ($string[$i+1] != chr(44))) // notice the $i+1
// chr 34 is ascii value for double quote chr 44 is for comma
{
echo $string[$i];
$i++;
}
}
}
The inner loop will not end at the end of the string.
Manipulation the loop control variable ($i) inside the loop leads to difficult code.
It makes it much harder to reason about it, and check if it is correct or not.
You should avoid it.
You get a better structure if you have only one loop that move through the string one char after another. Than use a variable to remember a state, for example $inside_quotes .
You have a logical flaw inside your loop which will cause it to iterate over the whole string but without doing any output.
Let's say:
$a = 1;
$b = 1;
while ($a == 1)
{
while ($a != 1 && $b == 1)
{
# will never come here.
}
$a++;
}
Because $a can never be 1 and !1 at the same time the condition you have put inside each other are blocking themselves.
One common way to solve this is to tokenize the string (scan the string) and then work on the tokens:
$string = '"""JOHN"" <31255555656>","DAHDI/1-1",3948723,,"",';
$token = strtok($string, '"');
while ($token !== false)
{
$token[0] !== ',' && (print "$token\n");
$token = strtok('"');
}
This produces (Demo) the following output:
JOHN
<31255555656>
DAHDI/1-1
Which might or might not be what you are looking for. A more detailed variant of this is to have a tokenizer/scanner that has state, one that can do more (but still has the shortcoming that the escapes of the quotation marks by doubling ("") them). As this is pretty bogus, I only link it. It is bogus because:
The actual function you're looking for is called str_getcsv:
$string = '"""JOHN"" <31255555656>","DAHDI/1-1",3948723,,"",';
echo implode("\n", str_getcsv($string));
Output (Demo):
"JOHN" <31255555656>
DAHDI/1-1
3948723
If you have a whole file, take a look at the SplFileObject.
Since you already located the first quote with the for statement you don't need to locate the same quote again with the for statement.
remove this: (($string[$i] != chr(34)) && along with the extra ) and the code will produce the following:
"""JOHN"" <31255555656>"DAHDI/1-1"
<?php
$string = '"""JOHN"" <31255555656>","DAHDI/1-1",3948723,,"",';
for ($i=0; $i<strlen($string); $i++) {
if ($string[$i] == chr(34)) {
while ($string[$i+1] != chr(44)) {
echo $string[$i];
$i++;
}
}
}
?>
Related
I'm trying to get the value of if condition that make the condition TRUE, I have the following example:
(code.php)
<?php
$a = 'USERNAME';
$b = 'LASTNAME';
if(substr($a, 0, strlen("<SCRIPT"))=== "<SCRIPT Abdalla $a " ) {
}
?>
in the above example, I'm trying to get the value <SCRIPT without double quotations. I tried some steps, but it still get the double quotation with the value (<SCRIPT). I plan then to assign the value <SCRIPT to another variable. Next code shows my code to get the result, where it still not working properly:
(test.php)
<?php
$file='';
$handle = fopen('code.php','r+');
if ($handle) {
while (($buffer=fgets($handle, 4096)) !== false) {
$file.=$buffer."\n";
}
if (!feof($handle)) {
die('error');
}
fclose($handle);
}
//take note of the use of single quotes to wrap this regex, as we do not want PHP parser to eval the $ in the regex string
preg_match_all('/\s*?(\$[\S]+?)\s*?\=(\"?[\S]+?\"?);/',$file,$matches, PREG_SET_ORDER);
$varval=array();
foreach($matches as $match){
$tmp=$match[2];
if(substr($tmp,0,1)=='"' && substr($tmp,-1,1)=='"'){
//evaluate variables in string. we can evaluate with the current object as the variable should be declared first as of proper PHP syntax
$tmp=substr($tmp, 1,-1); //remove quotes
$tmp=replaceFromObject($tmp, $varval);
}
$varval[$match[1]]=$tmp; //we do not need to check if exists, because we should replace with the latest value.
}
/*
The below stores all quoted text and replace them with our own special variable %var0%, %var1%, %var2%.... %varN%.
This is because there could be cases like if("test == 3" == $a) or even worse if("if(test=3)" == $a), which will make regex/parsing tricky if we do not substitute these quoted text out just like a parser/compiler would.
Note: Something I didn't do. We should really first scan the whole text using preg_match to find if there are any text using this format of variables as well, to prevent collisions!
If there are, we can set a loop to check and append some character to our own special variable like %varrN%, %varrrN%, %varrrrN%...
until we find no collisions and it is safe to use. I will leave this simple regex exercise for you to do on your own. Create the regex to find the file for %varN%...
*/
preg_match_all("/\"([\s\S]*?)\"/", $file, $matches, PREG_SET_ORDER);
$stringvars=array();
$key="%var";
$count=0;
foreach($matches as $match){
if(!in_array($match[1], $stringvars)){
$stringvars[$key.$count.'%']=$match[1];
$file=preg_replace("/\"".preg_quote($match[1])."\"/", $key.$count.'%', $file); //take note of the change to preg_quote
$count++;
}
}
// now we parse the whole text for if(subject anycomparator value)
preg_match_all("/if\s*?\(([\s\S]*?)([\=|\>|\<][\=]{0,2})\s*?([\S\s]*?)\s*?\)/", $file, $matches,PREG_SET_ORDER);
$conditionals=array();
foreach($matches as $match){
$conditionals[]=array(
'subject'=>replaceFromObject(replaceFromObject(trim($match[1]),$stringvars),$varval),
//the order does matter, we replace the string first, then the variables, as there might be variables in the double quoted strings which we should evaluate
'comparator'=>$match[2],
'value'=>replaceFromObject(replaceFromObject(trim($match[3]),$stringvars),$varval),
);
}
foreach ($conditionals as $c){
// echo htmlspecialchars($c['subject']).' ';
// echo htmlspecialchars($c['comparator']).' ';
echo htmlspecialchars($c['value']);
echo "<br/>";
}
/* now this function can be used to replace both the quoted strings AND the variables */
function replaceFromObject($s, $obj){
foreach($obj as $key=>$value){
$s=preg_replace('/'.preg_quote($key).'/', $value, $s); //take note of the preg_quote as the string may contain regex keywords
}
return $s;
}
?>
I want a way to print the value that make the if condition be TRUE, which is (
Note: I know the condition here is not TRUE, so that I want to get the value that make the condition TRUE which is <SCRIPT
The purpose is I want to know what is the value that make the condition TRUE, then later will use this value to do test cases on the code based on this value.
Your goal seems oddly specific, but here's a go at addressing your question.
You should not be using file_get_contents to read the actual source code of the php file. You will be getting the generated html equivalent instead. If you are getting the source code, it means you have your allow_url_fopen set to true. This opens your web application up to potential pretty damaging script injection possibilities, unless you know what you are doing and are careful and consistent with your code. You should try using fgets instead. This code will basically do the same as your file_get_contents.
$file='';
$handle = fopen('code.php','r+');
if ($handle) {
while (($buffer=fgets($handle, 4096)) !== false) {
$file.=$buffer."\n";
}
if (!feof($handle)) {
die('error');
}
fclose($handle);
}
There is a bug in your code. if(strpos($value, 'if') != false) will render position 0 as false, which should be true in your case as if is at position 0. The comparator should be strict if(strpos($value, 'if') !== false).
It is returning the quotation because that is what your code is telling it to. $pos1 = strpos($value, '==') + 2 is pointing to 2 characters after the first =, which is technically your last = in your ===. In fact, with your current code, you would be getting = "<SCRIPT" as the result.
Now for the Solution
To solve it just for your case, you can just adjust the position to substr with. After making the amendments mentioned above (especially 2), you will get your condition without the quotes by changing these 2 lines
$pos1 = strpos($value, '===') + 5; // 5 includes the 3 equal signs, a space and the quote
$pos2 = strrpos($value, ')') - 2; // 2 includes the space and the quote
Although this will get the result you want, it might not work for all use cases, as sometimes you might add a space, sometimes you might not. A more robust solution will be to use regex. In your case, it will be something along this line:
foreach ($lines as $line) {
if(preg_match("/if\s*?\([\s\S]*?\=\=\s*?[\"|']([\S\s]*?)[\"|']\s*?\)/", $line, $matches)){
echo htmlspecialchars($matches[1]);
}
}
This regex string /if\s*?\([\s\S]*?\=\=\s*?[\"|']([\S\s]*?)[\"|']\s*?\)/ does what your code aims to do, but in a more robust manner = finds a string with if possibly a space, a (, some text, a comparator == and whatever is in between the quotes - either ' or ".
To have it even more robust to pick up other conditions, strict equal, < or > or <=, >=, you can do something like this.
$conditionals=array();
foreach ($lines as $line) {
if(preg_match("/if\s*?\([\s\S]*?([\=|\>|\<][\=]{0,2})\s*?[\"|']([\S\s]*?)[\"|']\s*?\)/", $line, $matches)){
$conditionals[]=array(
'comparator'=>$matches[1],
'value'=>$matches[2]
);
}
}
foreach ($conditionals as $c){
echo htmlspecialchars($c['comparator']).' ';
echo htmlspecialchars($c['value']);
echo "<br/>";
}
This will work for a file of code.php that might look like this:
//code.php
<?php
$a = 'Data';
if(substr($a, 0, strlen("<SCRIPT"))=== "<SCRIPT" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))== "equal" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))<= "lesser or equal" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))>= "greater or equal" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))< "lesser" ) {
echo TRUE;
}
if(substr($a, 0, strlen("<SCRIPT"))>"greater" ) {
echo TRUE;
}
and will return
=== <SCRIPT
== equal
<= lesser or equal
>= greater or equal
< lesser
> greater
Edit to provide even more robust code for non-quoted values...
Another edit to capture variable values and dump them back in.
Another edit for single quotes variables and space between variables
Note:
The order of capturing strings or variables first matters, as if we are not careful we might be caught in a viscous cycle -> there is string in the variable, there is variable in the string, there is a string in the variable in the string of a variable, etc...
The idea is we should capture the variable first, evaluate whatever variables within these variables (if they are a double quoted string), and we do not need to worry about that string-var inception problem.
We then capture the strings -> and then replace the strings THEN replace the variables.
More Notes
What we should really also check for:
Since PHP only evaluates variables in double quotes, we should check if the string was enclosed in double and single quotes before making the decision to evaluate. I have done it for the variables. It could be easily translated to the strings as well - by capturing the quotes as well, then testing the quotes if it is single or double (or any at all). I will leave this to you as a regex exercise.
The other note is that I did this on purpose. :p The current regex for capturing variables works for $a=3;, $a =3; but not $a = 3; or $a = 3 ; or $a= 3 ;, so on. It is easy to add it in, I left it this way so you have a chance to practice your regex skills by adding this simple conditions in. (Edited to add this in) Hope this helps..
$file='';
$handle = fopen('code.php','r+');
if ($handle) {
while (($buffer=fgets($handle, 4096)) !== false) {
$file.=$buffer."\n";
}
if (!feof($handle)) {
die('error');
}
fclose($handle);
}
//take note of the use of single quotes to wrap this regex, as we do not want PHP parser to eval the $ in the regex string
preg_match_all('/\s*?(\$[\S]+?)\s*?\=\s*?(\"?[\S]+?\"?);/',$file,$matches, PREG_SET_ORDER);
$varval=array();
foreach($matches as $match){
$tmp=trim($match[2]);
if(substr($tmp,0,1)=='"' && substr($tmp,-1,1)=='"'){
//evaluate variables in string. we can evaluate with the current object as the variable should be declared first as of proper PHP syntax
$tmp=substr($tmp, 1,-1); //remove quotes
$tmp=replaceFromObject($tmp, $varval);
}else if(substr($tmp,0,1)=='\'' && substr($tmp,-1,1)=='\''){ // remove single quotes
$tmp=substr($tmp, 1,-1);
//no substitution of variables in single quotes just as PHP syntax
}
$varval[$match[1]]=$tmp; //we do not need to check if exists, because we should replace with the latest value.
}
/*
The below stores all quoted text and replace them with our own special variable %var0%, %var1%, %var2%.... %varN%.
This is because there could be cases like if("test == 3" == $a) or even worse if("if(test=3)" == $a), which will make regex/parsing tricky if we do not substitute these quoted text out just like a parser/compiler would.
Note: Something I didn't do. We should really first scan the whole text using preg_match to find if there are any text using this format of variables as well, to prevent collisions!
If there are, we can set a loop to check and append some character to our own special variable like %varrN%, %varrrN%, %varrrrN%...
until we find no collisions and it is safe to use. I will leave this simple regex exercise for you to do on your own. Create the regex to find the file for %varN%...
*/
preg_match_all("/\"([\s\S]*?)\"/", $file, $matches, PREG_SET_ORDER);
$stringvars=array();
$key="%var";
$count=0;
foreach($matches as $match){
if(!in_array($match[1], $stringvars)){
$stringvars[$key.$count.'%']=$match[1];
$file=preg_replace("/\"".preg_quote($match[1])."\"/", $key.$count.'%', $file); //take note of the change to preg_quote
$count++;
}
}
// now we parse the whole text for if(subject anycomparator value)
preg_match_all("/if\s*?\(([\s\S]*?)([\=|\>|\<][\=]{0,2})\s*?([\S\s]*?)\s*?\)/", $file, $matches,PREG_SET_ORDER);
$conditionals=array();
foreach($matches as $match){
$conditionals[]=array(
'subject'=>replaceFromObject(replaceFromObject(trim($match[1]),$stringvars),$varval),
//the order does matter, we replace the string first, then the variables, as there might be variables in the double quoted strings which we should evaluate
'comparator'=>$match[2],
'value'=>replaceFromObject(replaceFromObject(trim($match[3]),$stringvars),$varval),
);
}
foreach ($conditionals as $c){
echo htmlspecialchars($c['subject']).' ';
echo htmlspecialchars($c['comparator']).' ';
echo htmlspecialchars($c['value']);
echo "<br/>";
}
/* now this function can be used to replace both the quoted strings AND the variables */
function replaceFromObject($s, $obj){
foreach($obj as $key=>$value){
$s=preg_replace('/'.preg_quote($key).'/', $value, $s); //take note of the preg_quote as the string may contain regex keywords
}
return $s;
}
So actually your code doesn't output anything (test.php) on my server. Therefore I've tried another approach which at least works for your code.php, but however, it is not very stable like your own code since it doesn't take a few things into account which would be the following.
Logical operators which weren't thought of:
<, <=, >, >=, ===
No multiline if support
My approch actually doesn't split the string nor does it strip anything, the only thing which it is doing however, is to replace the string literal.
<?php
$file = file_get_contents("test.php");
$curIdx = 0;
while($curIdx < strlen($file))
{
$ifIndex = strpos($file,"if", $curIdx);
if($ifIndex === false)
{
break;
}
$curIdx = $ifIndex+1;
$equalsIdx = strpos($file,"=",$curIdx);
if($equalsIdx === false)
{
break;
}
$curIdx = $equalsIdx+1;
for($i = $curIdx; $i<strlen($file); $i++)
{
if($file[$i] !== "=" && $file[$i] !== " ")
{
$curIdx = $i;
break;
}
}
$condition = substr($file,$curIdx,strpos($file,")",$curIdx)-$curIdx);
$condition = str_replace('"','',$condition);
echo htmlspecialchars($condition);
}
This outputs: <SCRIPT
For your script, I found several issues:
if(strpos($value, 'if') !== false)
has to have a double ==
with that out of the way, you're getting the following output:
= "<SCRIPT"
To get rid of this, simply check if the char after pos1 is another =
if($value[$pos1] === "=")
{
$pos1++;
}
Then we can actually remove the call to sanitize_recursive completely, because substr never returns an array.
Now to get rid, simply call str_replace on the between string:
$between = substr($value, $startIndex, $length);
$between = str_replace('"','',$between);
echo htmlspecialchars($between);
And we're having an output of <SCRIPT- the call to htmlspecialchars is needed because otherwise your browser would start to interpret this tag.
This leaves us with the following file:
<?php
$file = file_get_contents("test.php"); // Code.php the page that include the if condition
$lines = explode("\n", $file); // get each line of source code and store it in array ($lines)
foreach ($lines as $key => &$value) {
if(strpos($value, 'if') !== false) // check if the line have if statement
{
if(strpos($value, '==') !== false ) // check if the line compare two values
{
$pos1 = strpos($value, '==') + 2; // get the existence position of '==' + 2
if($value[$pos1] === "=")
{
$pos1++;
}
$pos2 = strrpos($value, ')'); // get the position of last ) in the line
$startIndex = min($pos1, $pos2);
$length = abs($pos1 - $pos2);
$between = substr($value, $startIndex, $length);
$between = str_replace('"','',$between);
echo htmlspecialchars($between); // will print: "<SCRIPT" with double quotation
}
}
}
I want to check if a string contains a character repeated zero or more times, for example:
If my string is aaaaaa, bbbb, c or ***** it must return true.
If it contains aaab, cd, or **%*** it must return false.
In other words, if the string has 2 or more unique characters, it must return false.
How to go about this in PHP?
PS: Is there a way to do it without RegEx?
You could split on every character then count the array for unique values.
if(count(array_count_values(str_split('abaaaa'))) == 1) {
echo 'True';
} else {
echo 'false';
}
Demo: https://eval.in/760293
count(array_unique(explode('', string)) == 1) ? true : false;
You can use a regular expression with a back-reference:
if (preg_match('/^(.)\1*$/', $string)) {
echo "Same characters";
}
Or a simple loop:
$same = true;
$firstchar = $string[0];
for ($i = 1; $i < strlen($string); $i++) {
if ($string[$i] != $firstchar) {
$same = false;
break;
}
}
For the fun of it:
<?php
function str2Dec($string) {
$hexstr = unpack('H*', $string);
$hex = array_shift($hexstr);
return hexdec($hex);
}
function isBoring($string) {
return str2Dec($string) % str2Dec(substr($string, 0, 1)) === 0;
}
$string1 = 'tttttt';
$string2 = 'ttattt';
var_dump(isBoring($string1)); // => true
var_dump(isBoring($string2)); // => false
Obviously this works only in small strings because once it gets big enough, the INT will overflow and the mod will not produce the correct value. So, don't use this :) - posting it just to show a different idea from the usual ones.
strlen(str_replace($string[0], '', $string)) ? false : true;
You can check that the number of unique characters is greater than 1. This will perform well even if the input string is empty: (Demo)
$string = 'aaaba';
var_export(
strlen(count_chars($string, 3)) < 2 // false
);
Alternatively, you can trim the string by its first character, but this will generate warnings/notices if the input string has no length. (Demo)
$string = 'aaaba';
var_export(
!strlen(trim($string, $string[0])) // false
);
p.s. Yes, you could use !strlen(trim($string, #$string[0])) to prevent warnings/notices caused by a zero-length string, but I avoid error suppression like the plague because it generally gives code a bad smell.
Regex: ^(.)\1{1,}
^: Starting of string
(.): Match and capture single characted.
\1{1,}: using captured character one or more than once.
For this you can use regex
OR:
PHP code demo
$string="bbbb";
if($length=strlen($string))
{
substr_count($string,$string[0]);
if($length==substr_count($string,$string[0]))
{
echo "Do something";
}
}
I have a string that contains 2 informations (1.A Boolean/2.Something(could be number, letter, special chars and could be of any length)).
2 is a user input.
Exemples:
(part 1)"true".(part 2)"321654987" => "true321654987"
Could also be
"false321654987" or "trueweiufv2345fewv"
What I need is a way of parsing trought the string to check first if 1 is true (if its false do nothing), if it is true I need to check if to following part is a positive number higher than 0 (must accepte any number higher than 0 even decimal BUT not bin or hex (well... could be 10 but its would mean ten not two)).
Here is what I tried:
//This part is'nt important it work as it should....
if(isset($_POST['validate']) && $_POST['validate'] == "divSystemePositionnement")
{
$array = json_decode($_POST['array'], true);
foreach($array as $key=>$value)
{
switch($key)
{
case "txtFSPLongRuban":
//This is the important stuff HERE.....
if(preg_match('#^false.*$#', $value))//If false do nothing
{}
else if(!preg_match('#^true[1-9][0-9]*$#', $value))//Check if true and if number higher than 0.
{
//Do stuff,
//Some more stuff
//Just a bit more stuff...
//Done! No more stuff to do.
}
break;
//Many more cases...
}
}
}
As you can see I use regEx to parse trought to string. But it does'nt match decimal number.
I know how to do a regEx to parse decimal this is'nt the question.
The question is:
is there already a function in php that match the parsing I need?
If not, do any of you know a more efficient way to do the parsing or should I just add to my regEx the decimal part?
I was thinking something like :
test = str_split($value, "true")
if(isNumeric(test[1]) && test[1] > 0)
//problem is that isNumeric accepte hex and a cant have letter in there only straight out int or decimal number higher than 0.
Any idea??
Thank you so much for the help!
Use substr : documentation
if(substr($value, 0, 4) == "true"){
$number_part = substr($value, 5);
if(((int) $number == $number) || ((float) $number == $number)){
//do something...
}
}
You can do this:
case "txtFSPLongRuban":
if (preg_match('~^true(?=.*[^0.])([0-9]+(?:\.[0-9]+)?)$~', $value, $match))
{
// do what you want with $match[1] that contains the not null number.
}
break;
The lookahead (?=.*[^0.]) checks if there is somewhere a character that is not a 0 or a .
This oughta do the trick, and handle both types of values:
preg_match('/^(true|false)(.*)$/', $value, $matches);
$real_val = $matches[2];
if ($matches[1] == 'true') {
... true stuff ...
} else if ($matches[1] == 'false') {
... false stuff ...
} else {
... file not found stuff ...
}
Have a try with:
else if(!preg_match('#^true([1-9][0-9]*(?:\.[0-9]*)?$#', $value))
Have a look at ctype_digit:
Checks if all of the characters in the provided string, text, are numerical.
To check for decimals you can use filter_var:
if (filter_var('123.45', FILTER_VALIDATE_FLOAT) !== false) {
echo 'Number';
}
A simple substr call is not functioning properly. I want to grab only the strings which end with a forward slash. Here are seven strings.
HELLO/NN, SMILE/JJ, JUMP/, GOOD/RB, GREAT/JJ, HAPPY/NNP, SEAPORT/
$m = substr($string, -1);
if ($m = "/") {
echo $string;
}
This code somehow returns true every time. All seven words are printed.
I've tried strrev and many other string functions. It doesn't seem to
matter. I can literally print $m and see that it's "/" but the if statement
decides that every word meets the $m = "/" criteria. Even when $m is not a "/"
The comparison operator is ==, not =:
if ($m == "/") {
echo $string;
}
Shouldn't that be == rather than =?
if ($m = "/") {
You are assigning the value "/" to $m and that evaluation returns true. You want to compare and should use
if ($m == "/") {
The standard PHP way to test whether a string $str ends with a substring $test is:
$endsWith = substr( $str, -strlen( $test ) ) == $test
Is this the fastest way?
What Assaf said is correct. There is a built in function in PHP to do exactly that.
substr_compare($str, $test, strlen($str)-strlen($test), strlen($test)) === 0;
If $test is longer than $str PHP will give a warning, so you need to check for that first.
function endswith($string, $test) {
$strlen = strlen($string);
$testlen = strlen($test);
if ($testlen > $strlen) return false;
return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0;
}
This method is a tiny bit more memory-expensive, but it is faster:
stripos(strrev($haystack), $reversed_needle) === 0;
This is best when you know exactly what the needle is, so you can hard-code it reversed. If you reverse the needle programmatically, it becomes slower than the earlier method.
Edit (12 years later): LOL, this is a super-old answer that I wrote when I didn't know what I was actually talking about. I'd like the think I've grown since then. #DavidHarkness is right, it is not very efficient in the negative case. Probably much faster to just iterate in reverse and bail early if you really need as much perf as possible. Also, php probably has better ways to do this now. Honestly, I haven't written php in nearly a decade, so I'll leave it up to others now.
$endsWith = substr_compare( $str, $test, -strlen( $test ) ) === 0
Negative offset "starts counting from the end of the string".
Here’s a simple way to check whether one string ends with another, by giving strpos an offset right where the string should be found:
function stringEndsWith($whole, $end)
{
return (strpos($whole, $end, strlen($whole) - strlen($end)) !== false);
}
Straightforward, and I think this’ll work in PHP 4.
It depends on which sort of efficiency you care about.
Your version uses more memory due to the extra copy from the use of substr.
An alternative version might search the original string for the last occurrence of the substring without making a copy, but would probably be slower due to more testing.
Probably the most efficient way is to do loop char-by-char from the -sterlen(test) position till the end of the string and compare. That's the minimal amount of comparisons you can hope to do and there's hardly any extra memory used.
In PHP 8:
str_ends_with('haystack', 'stack'); // true
str_ends_with('haystack', 'K'); // false
and also:
str_starts_with('haystack', 'hay'); // true
PHP RFC: Add str_starts_with(), str_ends_with() and related functions
Another way would be to use the strrpos function:
strrpos($str, $test) == strlen($str) - strlen($test)
But that’s not faster.
I hope that the below answer may be efficient and also simple:
$content = "The main string to search";
$search = "search";
//For compare the begining string with case insensitive.
if(stripos($content, $search) === 0) echo 'Yes';
else echo 'No';
//For compare the begining string with case sensitive.
if(strpos($content, $search) === 0) echo 'Yes';
else echo 'No';
//For compare the ending string with case insensitive.
if(stripos(strrev($content), strrev($search)) === 0) echo 'Yes';
else echo 'No';
//For compare the ending string with case sensitive.
if(strpos(strrev($content), strrev($search)) === 0) echo 'Yes';
else echo 'No';
Don't know if this is fast or not but for a single character test, these work, too:
(array_pop(str_split($string)) === $test) ? true : false;
($string[strlen($string)-1] === $test) ? true : false;
(strrev($string)[0] === $test) ? true : false;
easiest way to check it via regular expression
for example to check if the mail given is gmail:
echo (preg_match("/#gmail\.com$/","example-email#gmail.com"))?'true':'false';
I'm thinking the reverse functions like strrchr() would help you match the end of the string the fastest.
This is pure PHP, without calling external functions, except for strlen.
function endsWith ($ends, $string)
{
$strLength = strlen ($string);
$endsLength = strlen ($ends);
for ($i = 0; $i < $endsLength; $i++)
{
if ($string [$strLength - $i - 1] !== $ends [$i])
return false;
}
return true;
}
for single-char needle:
if (#strrev($haystack)[0] == $needle) {
// yes, it ends...
}