matching url to another url - php

I am faced with rather unusual situation
I will have url in any of the 3 formats:
http://example.com/?p=12
http://example.com/a-b/
http://example.com/a.html
Now, I need to match with a url like
http://example.com/?p=12&t=1
http://example.com/a-b/?t=1
http://example.com/a.html?t=1
How can I achieve this? Please help
I know I can use like:
stristr('http://example.com/?p=12','http://example.com/?p=12&t=1')
but this will also match when
http://example.com/?p=123 (as it matches p=12)
Help guys, please.

A simple way to accomplish this would be to use PHP's parse_url() and parse_str().
http://www.php.net/manual/en/function.parse-url.php
http://www.php.net/manual/en/function.parse-str.php
Take your urls and run them through parse_url(), and take the resulting $result['query']. Run these through parse_str() and you'll end up with two associative arrays of the variable names and their values.
Basically, you'll want to return true if the $result['path']s match, and if any keys which are in both $result['query'] contain the same values.
code example:
function urlMatch($url1, $url2)
{
// parse the urls
$r1 = parse_url($url1);
$r2 = parse_url($url2);
// get the variables out of the queries
parse_str($r1['query'], $v1);
parse_str($r2['query'], $v2);
// match the domains and paths
if ($r1['host'] != $r2['host'] || $r1['path'] != $r2['path'])
return false;
// match the arrays
foreach ($v1 as $key => $value)
if (array_key_exists($key, $v2) && $value != $v2[$key])
return false;
// if we haven't returned already, then the queries match
return true;
}

A very quick (and somewhat dirty) way to achieve this is via the following regex:
$regex = '#^' . preg_quote($url, '#') . '[?&$]#';
Where $url is the URL you need to search for. In the above, we look for the URL in the beginning of whatever the regex is matched upon, followed by either a ?, a & or the end-of-line anchor. This is not bullet-proof but may be sufficient (#Mala already posted the "right" approach).
Below, I've posted an example of use (and the result):
$urls = array(
'http://example.com/?p=12',
'http://example.com/a-b/',
'http://example.com/a.html'
);
$tests = array(
'http://example.com/?p=12&t=1',
'http://example.com/a-b/?t=1',
'http://example.com/a.html?t=1',
'http://example.com/?p=123'
);
foreach ($urls as $url) {
$regex = '#^' . preg_quote($url, '#') . '[?&$]#';
print $url . ' - ' . $regex . "\n";
foreach ($tests as $test) {
$match = preg_match($regex, $test);
print ' ' . ($match ? '+' : '-') . ' ' . $test . "\n";
}
}
Result:
http://example.com/?p=12 - #^http\://example\.com/\?p\=12[?&$]#
+ http://example.com/?p=12&t=1
- http://example.com/a-b/?t=1
- http://example.com/a.html?t=1
- http://example.com/?p=123
http://example.com/a-b/ - #^http\://example\.com/a-b/[?&$]#
- http://example.com/?p=12&t=1
+ http://example.com/a-b/?t=1
- http://example.com/a.html?t=1
- http://example.com/?p=123
http://example.com/a.html - #^http\://example\.com/a\.html[?&$]#
- http://example.com/?p=12&t=1
- http://example.com/a-b/?t=1
+ http://example.com/a.html?t=1
- http://example.com/?p=123

Related

How to find ALL substrings in string using starting and ending words arrays PHP

I've spent my last 4 hours figuring out how to ... I got to ask for your help now.
I'm trying to extract from a text multiple substring match my starting_words_array and ending_words_array.
$str = "Do you see that ? Indeed, I can see that, as well as this." ;
$starting_words_array = array('do','I');
$ending_words_array = array('?',',');
expected output : array ([0] => 'Do you see that ?' [1] => 'I can see that,')
I manage to write a first function that can find the first substring matching one of both arrays items. But i'm not able to find how to loop it in order to get all the substring matching my requirement.
function SearchString($str, $starting_words_array, $ending_words_array ) {
forEach($starting_words_array as $test) {
$pos = strpos($str, $test);
if ($pos===false) continue;
$found = [];
forEach($ending_words_array as $test2) {
$posStart = $pos+strlen($test);
$pos2 = strpos($str, $test2, $posStart);
$found[] = ($pos2!==false) ? $pos2 : INF;
}
$min = min($found);
if ($min !== INF)
return substr($str,$pos,$min-$pos) .$str[$min];
}
return '';
}
Do you guys have any idea about how to achieve such thing ?
I use preg_match for my solution. However, the start and end strings must be escaped with preg_quote. Without that, the solution will be wrong.
function searchString($str, $starting_words_array, $ending_words_array ) {
$resArr = [];
forEach($starting_words_array as $i => $start) {
$end = $ending_words_array[$i] ?? "";
$regEx = '~'.preg_quote($start,"~").".*".preg_quote($end,"~").'~iu';
if(preg_match_all($regEx,$str,$match)){
$resArr[] = $match[0];
}
}
return $resArr;
}
The result is what the questioner expects.
If the expressions can occur more than once, preg_match_all must also be used. The regex must be modify.
function searchString($str, $starting_words_array, $ending_words_array ) {
$resArr = [];
forEach($starting_words_array as $i => $start) {
$end = $ending_words_array[$i] ?? "";
$regEx = '~'.preg_quote($start,"~").".*?".preg_quote($end,"~").'~iu';
if(preg_match_all($regEx,$str,$match)){
$resArr = array_merge($resArr,$match[0]);
}
}
return $resArr;
}
The resut for the second variant:
array (
0 => "Do you see that ?",
1 => "Indeed,",
2 => "I can see that,",
)
I would definitely use regex and preg_match_all(). I won't give you a full working code example here but I will outline the necessary steps.
First, build a regex from your start-end-pairs like that:
$parts = array_map(
function($start, $end) {
return $start . '.+' . $end;
},
$starting_words_array,
$ending_words_array
);
$regex = '/' . join('|', $parts) . '/i';
The /i part means case insensitive search. Some characters like the ? have a special purpose in regex, so you need to extend above function in order to escape it properly.
You can test your final regex here
Then use preg_match_all() to extract your substrings:
preg_match_all($regex, $str, $matches); // $matches is passed by reference, no need to declare it first
print_r($matches);
The exact structure of your $matches array will be slightly different from what you asked for but you will be able to extract your desired data from it
Benni answer is best way to go - but let just point out the problem in your code if you want to fix those:
strpos is not case sensitive and find also part of words so you need to changes your $starting_words_array = array('do','I'); to $starting_words_array = array('Do','I ');
When finding a substring you use return which exit the function so you want find any other substring. In order to fix that you can define $res = []; at the beginning of the function and replace return substr($str,$pos,... with $res[] = substr($str,$pos,... and at the end return the $res var.
You can see example in 3v4l - in that example you get the output you wanted

PHP: Deal with delimiters in dynamic regular expression

Let's say I want to create a PHP tool to dynamically check a string against regular expression pattern. There is one problem with that: delimiters.
I would like to be able to do the following (simplified example):
$pattern = $_POST['pattern'];
$matched = (preg_match($pattern, $_POST['string']) === 1);
I don't want the users to put delimiters in the input, just a pure pattern, like ^a[bc]+d. How to deal with delimiters? I could do this:
$pattern = '/' . $_POST['pattern'] . '/';
Or with any other possible delimiter, but what about escaping? Is placing \ before each character in the pattern, being the same one as the delimiter of my choice, enough? Like this:
$pattern = '/' . str_replace('/', '\\/', $_POST['pattern']) . '/';
What is a neat way to deal with delimiters?
You have to check the input to identify the delimiter if there is any and remove it. This way if the user follows the rules, you don't have to worry, but if they don't the delimiter is removed anyway. The delimiter can be identified by comparing the first and last character.
// with incorrect input.
$input = "/^a[bc]+d/"; // from $_POST['pattern']
$delim = "/";
if ($input[0] === $input[strlen($input) - 1]) {
$delim = $input[0];
}
$sInput = str_replace($delim,"",$input);
echo $sInput; // ^a[bc]+d
With correct input, you don't have to worry.
$input = "^a[bc]+d"; // from $_POST['pattern']
$delim = "/";
if ($input[0] === $input[strlen($input) - 1]) {
$delim = $input[0];
}
$sInput = str_replace($delim,"",$input);
echo $sInput; // ^a[bc]+d
$sInput is your sanitized pattern. You can use it directly to test your string.
$matched = (preg_match($sInput, $_POST['string']) === 1);

If $_POST value has the word AND, then insert word ARE

I have a typical question, I'm not sure whether it is possible or not. I have a form in which there is a field namely Producer. How do I make it possible, if user use the word and in the field then insert the word are in the result, and if user don't use the word and in the field then insert the word is in the result. Let me explain you with an example.
Example (the word and is in the field) then generate the following result:
ABC and DEF are the producers of the movie.
Example (the word and is NOT in the field) then generate the following result:
XYZ is the producer of the movie.
I have the following code:
if(!empty($_POST['Producer'])) {
$description .= ' ' . $_POST["Producer"] . ' is/are the producer(s) of the movie';
}
Kindly tell me if anyone have the idea.
Simply call strpos with $_POST['Producer'] as haystack and and as needle. If the return value is false, the string doesn't contain and.
Now you can depending on the return value create your output.
http://php.net/manual/en/function.strpos.php
if(!empty($_POST['Producer']))
{
if(stripos($_POST['Producer'], ' and ') != false) // ' and ' is found
$producers = $_POST['Producer'] .' are the producers ';
else
$producers = $_POST['Producer'] .' is the producer ';
$description = $producers .'of the movie';
}
I put ' and ' instead of 'and' (with whitespaces) because some names contains the word 'are', so it would return true even if there's only one name.
Below code should work (not tested).
if(!empty($_POST['Producer'])) {
$producer = $_POST["Producer"]; // CONSIDER SANITIZING
$pos = stripos($_POST['Producer'], ' and ');
list($verb, $pl) = $pos ? array('are', 's') : array('is', '');
$description .= " $producer $verb the producer$pl of the movie";
}
As noted you should also consider sanitizing the incoming value of $_POST["Producer"], depending on how you intend to use the formatted string.
I have not tested this but something along the lines of this should work.
$string = $_POST['Producer'];
//This is the case if the user used and.
$start = strstr($string, 'and');
if($start != null)
{
$newString = substr($string, 0, $start) . "are" . substr($string, $start+3, strlen($string))
}

PHP Divide variable into two different variables

I have this variable : $lang['_city']
I need to divide it in two portions.
One portion will be $key == lang and second portion will be $ind == _city
I think I can do it with some sort of regexp but I am not good with it.
Can you help me?
P.S. The above example is only a sample. I need an abstract function that will do it with any passed variable.. So for example, get whatever is before [ and cut the $, and then get whatever is inside [' '].. I know the logic but not the way to do it :)
Also the new values will have to be assigned each one to a variable as explained before.
Thanks
$string = "\$foo['bar']";
preg_match('~^\$(?\'key\'[^\[]+)\[\'(?\'ind\'[^\']+)~', $string, $matches);
var_dump($matches['key'], $matches['ind']);
http://ideone.com/XaopX
Regexp for preg_match would be: ~^\$([^\[]+)\[\s*("[^"]+"|'[^']+')\s*\]$~ (you'll need to escape ' or " when moving to string).
Than:
$key = $match[1];
$ind = substr( $match[2], 1, -1);
You do not even need regexp here :)
$str = "\$lang['city']";
$tmp = explode('[', $str);
$key = ltrim($tmp[0], '$');
$ind = trim($tmp[1], ']\'"');
echo $key . '=>' . $ind;
Starting from PHP 5.4 you could do it even shorter
$str = "\$lang['city']";
$key = ltrim(explode('[', $str)[0], '$');
$ind = trim(explode('[', $str)[1], ']\'"');
echo $key . '=>' . $ind;

Fastest way of deleting a value in a comma separated list

I've got a list of names separated by commas (they may contain other characters), or be empty, but generally looking like this:
NameA,NameB,NameC
I need to create a function to delete a name if its present in the list and which restores the comma separated structure.
eg: if NameA is to be deleted, I should end up with:
NameB,NameC
NOT
,NameB,NameC
Similarly for the rest.
This is what I came up with, is there a better solution?
$pieces = explode(",", $list);
$key=array_search($deleteuser, $pieces);
if(FALSE !== $key)
{
unset($pieces[$key]);
}
$list = implode(",", $pieces);
That should work pretty well. You may also be interested in PHP's fgetcsv function.
Doc: http://php.net/manual/en/function.fgetcsv.php
You could use the array_splice function to delete from the array. With offset = array_search($deleteuser, $pieces) and length = 1.
If your application data is so large that you are experiencing a crippling amount of lag, then you may have bigger issues that this snippet of code. It might be time to rethink your data storage and processing from the ground up.
In the meantime, here are some benchmarks when imploding a 5001 element array with commas, then using different techniques to remove value 4999. (speeds actually include the generation of the comma-separate string, but all benchmarks are identical in this regard)
explode() + array_search() + unset() + implode() (demo)System time: ~.009 - .011s
$pieces = explode(",", $list);
if (($key = array_search($deleteuser, $pieces)) !== false) {
unset($pieces[$key]);
}
echo implode(",", $pieces);
explode() + array_search() + array_splice() + implode() (demo)System time: ~.010 - .012s
$pieces = explode(",", $list);
if (($key = array_search($deleteuser, $pieces)) !== false) {
array_splice($pieces, $key, 1);
}
echo implode(",", $pieces);
explode() + foreach() + if() + unset() + break + implode() (demo)System time: ~.011 - .012s
$pieces = explode(",", $list);
foreach ($pieces as $key => $value) {
if ($value == $deleteuser) {
unset($pieces[$key]);
break;
}
}
echo implode(",", $pieces);
(note: if you remove the break, the loop can remove multiple occurrences of the needle)
explode() + array_diff() + implode() (demo)System time: ~.010 - .011s
$pieces = explode(",", $list);
$pieces = array_diff($pieces, [$deleteuser]);
echo implode(",", $pieces);
// or just: echo implode(',', array_diff(explode(",", $list), [$deleteuser]);
explode() + array_filter() + implode() (demo)System time: ~.010 - .013s
$pieces = explode(",", $list);
$pieces = array_filter($pieces, function($v) use ($deleteuser) {
return $v != $deleteuser;
});
echo implode(",", $pieces);
preg_quote() + preg_replace() (demo) (regex demo)System time: ~.007 - .010s
$needle = preg_quote($deleteuser, '/');
echo preg_replace('/,' . $needle . '(?=,|$)|^' . $needle . ',/', '', $list);
// if replacing only one value, declare preg_replace's 4th parameter as 1
Note that if you are using a delimiting character that has a special meaning to the regex engine (like +), then you will need to add a slash before it \+ to escape it and make it literal. This would make the pattern: '/\+' . $needle . '(?=\+|$)|^' . $needle . '\+/'
So while the regex-based snippet proved to be slightly (unnoticeably) faster for my arbitrarily conjured string length, you will need to make your own benchmarks to be sure which is the best performer for your application.
That said, and don't get me wrong I love regex, but the regular expression is going to be the easiest for developers to "get wrong" when it is time to modify the pattern AND I am confident that most devs will agree it has the worst overall comprehensibility.
You could also try a regular expression like this (maybe it can be optimized):
$search = 'NameB';
$list = 'NameA,NameB,NameC';
$list = preg_match('/(^' . $search . ',)|(,' . $search. ')/', '', $list);

Categories