I have a problem with the strpos & substr function on PHP - php

I have a problem with the strpos & substr function, thank you for your help:
$temp = "U:hhp|E:123#gmail.com,P:h123";
$find_or = strpos($temp,"|");
$find_and = strpos($temp,",");
$find_user = substr($temp,2,$find_or-2);
$find_email = substr($temp,$find_or+3,$find_and);
$find_passeord = substr($temp,$find_and+3,strlen($temp));
echo("$find_user+$find_email+$find_passeord<br/>");
/************************************/
Why is the output like this ??
hhp+123#gmail.com,P:h123 +h123
but i want this:
hhp+123#gmail.com,h123

The problem is that $find_and is the index of ,, but the third argument to substr() needs to be the length of the substring, not the ending index. So
$find_email = substr($temp,$find_or+3,$find_and);
should be
$find_email = substr($temp,$find_or+3,$find_and-$find_or-3);
For $find_passeord you can omit the 3rd argument, since the default is the end of the string.
However, this would be simpler with a regular expression:
if (preg_match('/^U:(.*?)\|E:(.*?),P:(.*)/', $temp, $match)) {
list($whole, $user, $email, $password) = $match;
}

if you have control over the input I would suggest
$temp = "U:hhp|E:123#gmail.com|P:h123";
list($user, $email, $password) = explode("|",$temp);
$user = explode(":",$user)[1];
$email = explode(":",$email)[1];
$password = explode(":",$password)[1];
if not then I still recommend exploding the string into parts and work your way down to what you need . https://3v4l.org/ is a great site for testing php code ... here is an example of this working https://3v4l.org/upEGG

Echoing what Barmar just said in a comment, regular expressions are definitely the best way to "break up a string." (It is quite-literally much of what they are for.) This is the preg_ family of PHP functions. (e.g. preg_match, preg_match_all, preg_replace.)
The million-dollar idea behind a "regular expression" is that it is a string-matching pattern. If the string "matches" that pattern, you can easily extract the exact substrings which matched portions of it.
In short, all of the strpos/substr logic that you are right now wrestling with ... "goes away!" Poof.
For example, this pattern: ^(.*)|(.*),(.*)$ ...
It says: "Anchored at the beginning of the string ^, capture () a pattern consisting of "zero or more occurrences of any character (.*), until you encounter a literal |. Now, for the second group, proceed until you find a ,. Then, for the third group, proceed to take any character until the end of the string $."
You can "match" that regular expression and simply be handed all three of these groups! (As well as "the total string that matched.") And you didn't have to "write" a thing!
There are thousands of web pages by now which discuss this "remarkable 'programming language' within a single cryptic string." But it just might be the most pragmatically-useful technology for any practitioner to know, and every programming language somehow implements it, more-or-less following the precedent first set by the (still active) programming language, Perl.

Related

Using different names for subpatterns of the same number with preg_replace_callback

I'm having a hard time getting my head around what exactly is being numbered in my regex subpatterns. I'm being given the PHP warning:
PHP Warning: preg_replace_callback(): Compilation failed: different names for subpatterns of the same number are not allowed
When attempting the following:
$input = "A string that contains [link-ssec-34] and a [i]word[/i] here";
$matchLink = "\[link-ssec-(0?[1-9]|[1-9][0-9]|100)\]";
$matchItalic = "\[i](.+)\[\/i]";
$output = preg_replace_callback(
"/(?|(?<link>$matchLink)|(?<italic>$matchItalic))/",
function($m) {
if(isset($m['link'])){
$matchedLink = substr($m['link'][0], 1, -1);
//error_log('m is: ' . $matchedLink);
$linkIDExplode = explode("-",$matchedLink);
$linkHTML = createSubSectionLink($linkIDExplode[2]);
return $linkHTML;
} else if(isset($m['italic'])){
// TO DO
}
},
$input);
If I remove the named capture groups, like so:
"/(?|(?:$matchLink)|(?:$matchItalic))/"
There's no warnings, and I get matches fine but can't target them conditionally in my function. I believe I'm following correct procedure for naming capture groups, but PHP is saying they're using the same subpattern number, which is where I'm lost as I'm not sure what's being numbered. I'm familiar with addressing subpatterns using $1, $2, etc. but don't see the relevancy here when used with named groups.
Goal
Incase I'm using completely the wrong technique, I should include my goal. I was originally using preg_replace_callback() to replace tagged strings that matched a pattern like so :
$output = preg_replace_callback(
"/\[link-ssec-(0?[1-9]|[1-9][0-9]|100)\]/",
function($m) {
$matchedLink = substr($m[0], 1, -1);
$linkIDExplode = explode("-",$matchedLink);
$linkHTML = createSubSectionLink($linkIDExplode[2]);
return $linkHTML;
},
$input);
The requirement has grown to needing to match multiple tags in the same paragraph (My original example included the next one [i]word[/i]. Rather than parsing the entire string from scratch for each pattern, I'm trying to look for all the patterns in a single sweep of the paragraph/string in the belief that it will be less taxing on the system. Researching it led me to believe that using named capture groups in a branch reset was the best means of being able to target matches with conditional statements. Perhaps I'm walking down the wrong trail with this one but would appreciate being directed to a better method.
Result Desired
$input = "A string that contains [link-ssec-34] and a [i]word[/i] here";
$output = "A string that contains <a href='linkfromdb.php'>Link from Database</a> and a <span class='italic'>word</span> here."
With the potential to add further patterns as needed in the format of square brackets encompassing a word or being self-contained.
To answer your question about the warning:
PHP Warning: preg_replace_callback(): Compilation failed: different names for subpatterns of the same number are not allowed
Your pattern defines named matchgroups. But your pattern is using alternations (|) as well, meaning a whole part of the pattern does not need to be matched as all.
That means, that the named pattern link can appear with the match-number 1, but italic can also appear with match-number 1.
Since there is an alternation BOTH the matches can only be the same "number", hence they are only allowed to have the same NAME:
#(?|(?<first>one)|(?<first>two))#
would be allowed.
#(?|(?<first>one)|(?<second>two))#
throws this warning.
Without fully understand what I've done (but will look into it now) I did some trial and error on #bobblebubble comment and got the following to produce the desired result. I can now use conditional statements targeting named capture groups to decide what action to take with matches.
I changed the regex to the following:
$matchLink = "\[link-ssec-(0?[1-9]|[1-9][0-9]|100)\]"; // matches [link-ssec-N]
$matchItalic = "\[i](.+)\[\/i]"; // matches [i]word[/i]
$output = preg_replace_callback(
"/(?<link>$matchLink)|(?<italic>$matchItalic)/",
function($m) { etc...
Hopefully it's also an efficient way, in terms of overhead, of matching multiple regex patterns with callbacks in the same string.

How to get a number from a html source page?

I'm trying to retrieve the followed by count on my instagram page. I can't seem to get the Regex right and would very much appreciate some help.
Here's what I'm looking for:
y":{"count":
That's the beginning of the string, and I want the 4 numbers after that.
$string = preg_replace("{y"\"count":([0-9]+)\}","",$code);
Someone suggested this ^ but I can't get the formatting right...
You haven't posted your strings so it is a guess to what the regex should be... so I'll answer on why your codes fail.
preg_replace('"followed_by":{"count":\d')
This is very far from the correct preg_replace usage. You need to give it the replacement string and the string to search on. See http://php.net/manual/en/function.preg-replace.php
Your second usage:
$string = preg_replace(/^y":{"count[0-9]/","",$code);
Is closer but preg_replace is global so this is searching your whole file (or it would if not for the anchor) and will replace the found value with nothing. What your really want (I think) is to use preg_match.
$string = preg_match('/y":\{"count(\d{4})/"', $code, $match);
$counted = $match[1];
This presumes your regex was kind of correct already.
Per your update:
Demo: https://regex101.com/r/aR2iU2/1
$code = 'y":{"count:1234';
$string = preg_match('/y":\{"count:(\d{4})/', $code, $match);
$counted = $match[1];
echo $counted;
PHP Demo: https://eval.in/489436
I removed the ^ which requires the regex starts at the start of your string, escaped the { and made the\d be 4 characters long. The () is a capture group and stores whatever is found inside of it, in this case the 4 numbers.
Also if this isn't just for learning you should be prepared for this to stop working at some point as the service provider may change the format. The API is a safer route to go.
This regexp should capture value you're looking for in the first group:
\{"count":([0-9]+)\}
Use it with preg_match_all function to easily capture what you want into array (you're using preg_replace which isn't for retrieving data but for... well replacing it).
Your regexp isn't working because you didn't escaped curly brackets. And also you didn't put count quantifier (plus sign in my example) so it would only capture first digit anyway.

regex to find all text after delimited string

I have some content that contains a token string in the form
$string_text = '[widget_abc]This is some text. This is some text, etc...';
And I want to pull all the text after the first ']' character
So the returned value I'm looking for in this example is:
This is some text. This is some text, etc...
preg_match("/^.+?\](.+)$/is" , $string_text, $match);
echo trim($match[1]);
Edit
As per author's request - added explanation:
preg_match(param1, param2, param3) is a function that allows you to match a single case scenario of a regular expression that you're looking for
param1 = "/^.+?](.+?)$/is"
"//" is what you put on the outside of your regular expression in param1
the i at the end represents case insensitive (it doesn't care if your letters are 'a' or 'A')
s - allows your script to go over multiple lines
^ - start the check from the beginning of the string
$ - go all the way to end of the string
. - represents any character
.+ - at least one or more characters of anything
.+? - at least one more more characters of anything until you reach
.+?] - at least one or more characters of anything until you reach ] (there is a backslash before ] because it represents something in regular expressions - look it up)
(.+)$ - capture everything after ] and store it as a seperate element in the array defined in param3
param2 = the string that you created.
I tried to simplify the explanations, I might be off, but I think I'm right for the most part.
The regex (?<=]).* will solve this problem if you can guarantee that there are no other square brackets on the line. In PHP the code will be:
if (preg_match('/(?<=\]).*/', $input, $group)) {
$match = $group[0];
}
This will transform [widget_abc]This is some text. This is some text, etc... into This is some text. This is some text, etc.... It matches everything that follows the ].
$output = preg_replace('/^[^\]]*\]/', '', $string_text);
Is there any particular reason why a regex is wanted here?
echo substr(strstr($string_text, ']'), 1);
A regex is definitely overkill for this instance.
Here is a nice one-liner :
list(, $result) = explode(']', $inputText, 2);
It does the job and is way less expensive than using regular expressions.

Replacing Tags with Includes in PHP with RegExps

I need to read a string, detect a {VAR}, and then do a file_get_contents('VAR.php') in place of {VAR}. The "VAR" can be named anything, like TEST, or CONTACT-FORM, etc. I don't want to know what VAR is -- not to do a hard-coded condition, but to just see an uppercase alphanumeric tag surrounded by curly braces and just do a file_get_contents() to load it.
I know I need to use preg_match and preg_replace, but I'm stumbling through the RegExps on this.
How is this useful? It's useful in hooking WordPress.
Orion above has a right solution, but it's not really necessary to use a callback function in your simple case.
Assuming that the filenames are A-Z + hyphens you can do it in 1 line using PHP's /e flag in the regex:
$str = preg_replace('/{([-A-Z]+)}/e', 'file_get_contents(\'$1.html\')', $str);
This'll replace any instance of {VAR} with the contents of VAR.html. You could prefix a path into the second term if you need to specify a particular directory.
There are the same vague security worries as outlined above, but I can't think of anything specific.
You'll need to do a number of things. I'm assuming you can do the legwork to get the page data you want to preprocess into a string.
First, you'll need the regular expression to match correctly. That should be fairly easy with something like /{\w+}/.
Next you'll need to use all of the flags to preg_match to get the offset location in the page data. This offset will let you divide the string into the before, matching, and after parts of the match.
Once you have the 3 parts, you'll need to run your include, and stick them back together.
Lather, rinse, repeat.
Stop when you find no more variables.
This isn't terribly efficient, and there are probably better ways. You may wish to consider doing a preg_split instead, splitting on /[{}]/. No matter how you slice it you're assuming that you can trust your incoming data, and this will simplify the whole process a lot. To do this, I'd lay out the code like so:
Take your content and split it like so: $parts = preg_split('/[{}]/', $page_string);
Write a recursive function over the parts with the following criteria:
Halt when length of arg is < 3
Else, return a new array composed of
$arg[0] . load_data($arg[1]) . $arg[2]
plus whatever is left in $argv[3...]
Run your function over $parts.
You can do it without regexes (god forbid), something like:
//return true if $str ends with $sub
function endsWith($str,$sub) {
return ( substr( $str, strlen( $str ) - strlen( $sub ) ) === $sub );
}
$theStringWithVars = "blah.php cool.php awesome.php";
$sub = '.php';
$splitStr = split(" ", $theStringWithVars);
for($i=0;$i<count($splitStr);$i++) {
if(endsWith(trim($splitStr[$i]),$sub)) {
//file_get_contents($splitStr[$i]) etc...
}
}
Off the top of my head, you want this:
// load the "template" file
$input = file_get_contents($template_file_name);
// define a callback. Each time the regex matches something, it will call this function.
// whatever this function returns will be inserted as the replacement
function replaceCallback($matches){
// match zero will be the entire match - eg {FOO}.
// match 1 will be just the bits inside the curly braces because of the grouping parens in the regex - eg FOO
// convert it to lowercase and append ".html", so you're loading foo.html
// then return the contents of that file.
// BEWARE. GIANT MASSIVE SECURITY HOLES ABOUND. DO NOT DO THIS
return file_get_contents( strtolower($matches[1]) . ".html" );
};
// run the actual replace method giving it our pattern, the callback, and the input file contents
$output = preg_replace_callback("\{([-A-Z]+)\}", replaceCallback, $input);
// todo: print the output
Now I'll explain the regex
\{([-A-Z]+)\}
The \{ and \} just tell it to match the curly braces. You need the slashes, as { and } are special characters, so they need escaping.
The ( and ) create a grouping. Basically this lets you extract particular parts of the match. I use it in the function above to just match the things inside the braces, without matching the braces themselves. If I didn't do this, then I'd need to strip the { and } out of the match, which would be annoying
The [-A-Z] says "match any uppercase character, or a -
The + after the [-A-Z] means we need to have at least 1 character, but we can have up to any number.
Comparatively speaking, regular expression are expensive. While you may need them to figure out which files to load, you certainly don't need them for doing the replace, and probably shouldn't use regular expressions. After all, you know exactly what you are replacing so why do you need fuzzy search?
Use an associative array and str_replace to do your replacements. str_replace supports arrays for doing multiple substitutions at once. One line substitution, no loops.
For example:
$substitutions = array('{VAR}'=>file_get_contents('VAR.php'),
'{TEST}'=>file_get_contents('TEST.php'),
...
);
$outputContents = str_replace( array_keys($substitutions), $substitutions, $outputContents);

Google Style Regular Expression Search

It's been several years since I have used regular expressions, and I was hoping I could get some help on something I'm working on. You know how google's search is quite powerful and will take stuff inside quotes as a literal phrase and things with a minus sign in front of them as not included.
Example: "this is literal" -donotfindme site:examplesite.com
This example would search for the phrase "this is literal" in sites that don't include the word donotfindme on the webiste examplesite.com.
Obviously I'm not looking for something as complex as Google I just wanted to reference where my project is heading.
Anyway, I first wanted to start with the basics which is the literal phrases inside quotes. With the help of another question on this site I was able to do the following:
(this is php)
$search = 'hello "this" is regular expressions';
$pattern = '/".*"/';
$regex = preg_match($pattern, $search, $matches);
print_r($matches);
But this outputs "this" instead of the desired this, and doesn't work at all for multiple phrases in quotes. Could someone lead me in the right direction?
I don't necessarily need code even a real nice place with tutorials would probably do the job.
Thanks!
Well, for this example at least, if you want to match only the text inside the quotes you'll need to use a capturing group. Write it like this:
$pattern = '/"(.*)"/';
and then $matches will be an array of length 2 that contains the text between the quotes in element 1. (It'll still contain the full text matched in element 0) In general, you can have more than one set of these parentheses; they're numbered from the left starting at 1, and there will be a corresponding element in $matches for the text that each group matched. Example:
$pattern = '/"([a-z]+) ([a-z]+) (.*)"/';
will select all quoted strings which have two lowercase words separated by a single space, followed by anything. Then $matches[1] will be the first word, $matches[2] the second word, and $matches[3] the "anything".
For finding multiple phrases, you'll need to pick out one at a time with preg_match(). There's an optional "offset" parameter you can pass, which indicates where in the string it should start searching, and to find multiple matches you should give the position right after the previous match as the offset. See the documentation for details.
You could also try searching Google for "regular expression tutorial" or something like that, there are plenty of good ones out there.
Sorry, but my php is a bit rusty, but this code will probably do what you request:
$search = 'hello "this" is regular expressions';
$pattern = '/"(.*)"/';
$regex = preg_match($pattern, $search, $matches);
print_r($matches[1]);
$matches1 will contain the 1st captured subexpression; $matches or $matches[0] contains the full matched patterns.
See preg_match in the PHP documentation for specifics about subexpressions.
I'm not quite sure what you mean by "multiple phrases in quotes", but if you're trying to match balanced quotes, it's a bit more involved and tricky to understand. I'd pick up a reference manual. I highly recommend Mastering Regular Expressions, by Jeffrey E. F. Friedl. It is, by far, the best aid to understanding and using regular expressions. It's also an excellent reference.
Here is the complete answer for all the sort of search terms (literal, minus, quotes,..) WITH replacements . (For google visitors at the least).
But maybe it should not be done with only regular expressions though.
Not only will it be hard for yourself or other developers to work and add functionality on what would be a huge and super complex regular expression otherwise
it might even be that it is faster with this approach.
It might still need a lot of improvement but at least here is a working complete solution in a class. There is a bit more in here than asked in the question, but it illustrates some reasons behind some choices.
class mySearchToSql extends mysqli {
protected function filter($what) {
if (isset(what) {
//echo '<pre>Search string: '.var_export($what,1).'</pre>';//debug
//Split into different desires
preg_match_all('/([^"\-\s]+)|(?:"([^"]+)")|-(\S+)/i',$what,$split);
//echo '<pre>'.var_export($split,1).'</pre>';//debug
//Surround with SQL
array_walk($split[1],'self::sur',array('`Field` LIKE "%','%"'));
array_walk($split[2],'self::sur',array('`Desc` REGEXP "[[:<:]]','[[:>:]]"'));
array_walk($split[3],'self::sur',array('`Desc` NOT LIKE "%','%"'));
//echo '<pre>'.var_export($split,1).'</pre>';//debug
//Add AND or OR
$this ->where($split[3])
->where(array_merge($split[1],$split[2]), true);
}
}
protected function sur(&$v,$k,$sur) {
if (!empty($v))
$v=$sur[0].$this->real_escape_string($v).$sur[1];
}
function where($s,$OR=false) {
if (empty($s)) return $this;
if (is_array($s)) {
$s=(array_filter($s));
if (empty($s)) return $this;
if($OR==true)
$this->W[]='('.implode(' OR ',$s).')';
else
$this->W[]='('.implode(' AND ',$s).')';
} else
$this->W[]=$s;
return $this;
}
function showSQL() {
echo $this->W? 'WHERE '. implode(L.' AND ',$this->W).L:'';
}
Thanks for all stackoverflow answers to get here!
You're in luck because I asked a similar question regarding string literals recently. You can find it here: Regex for managing escaped characters for items like string literals
I ended up using the following for searching for them and it worked perfectly:
(?<!\\)(?:\\\\)*(\"|')((?:\\.|(?!\1)[^\\])*)\1
This regex differs from the others as it properly handles escaped quotation marks inside the string.

Categories