Problem
I am currently doing a preg_match on an url. This url has a certain id in the second parameter or the third parameter. However I don't know how I could get this more efficiently.
Code
preg_match('~http://www.example.com/some/(.+?)/~is', $url, $id);
if (!isset($id[1])) {
preg_match('~http://www.example.com/some/thing/(.+?)/~is', $url, $id);
if (!isset($id[1])) {
preg_match('~http://www.example.com/some/other/(.+?)/~is', $url, $id);
if (!isset($id[1])) {
preg_match('~http://www.example.com/some/thingelse/(.+?)/~is', $url, $id);
if (!isset($id[1])) {
return false
}
}
}
}
What I would like to do
if (preg_match('~http://www.example.com/some/(.+?)/~is', $url, $id)) {
$id = $id[1];
} else if (preg_match('~http://www.example.com/some/(.+?)/(.+?)/~is', $url, $id)) {
$id = $id[1];
} else {
return false;
}
However, this doesn't seem to work.
If the following regular expressions in fact did work as you wanted them to
if (preg_match('~http://www.example.com/some/(.+?)/~is', $url, $id)) {
$id = $id[1];
} else if (preg_match('~http://www.example.com/some/(.+?)/(.+?)/~is', $url, $id)) {
$id = $id[1];
} else {
return false;
}
... then you would never reach the second case anyway. The match will already be made in the first RegEx, as the beginning or the second expression is identical to the first expression. And even if you turned them around you would always get the id from the first parameter/path part, as you set $id = $id[1] on both results.
As stated in the comments, you probably would be better off using parse_url for this instead:
$urls = [
'http://www.example.com/some/thingelse/foo/bar/baz/',
'http://www.example.com/some/foo/bar/baz/',
];
foreach ($urls as $url) {
echo "Checking $url", PHP_EOL;
$path = parse_url($url, PHP_URL_PATH);
$parts = explode('/', $path);
echo "Second parameter: ", $parts[2], PHP_EOL;
echo "Third parameter: ", $parts[3], PHP_EOL;
}
Output:
Checking http://www.example.com/some/thingelse/foo/bar/baz/
Second parameter: thingelse
Third parameter: foo
Checking http://www.example.com/some/foo/bar/baz/
Second parameter: foo
Third parameter: bar
Related
everyone.
I have a basic router created in PHP.
I can redirect to any page I want, if there is a callback function the callback function gets executed and if there is a page (String instead of a function) the page loads the correct file. However I can't figure out how to implement 404 page on non-existing route.
I tried to reuse the preg_match() function, but that gave me no results and if I place the notFound() (404 page) in the else block, it always gets executed regardless of the correct url or not.
if(preg_match($pattern, $path, $matches) && $httpMethod === $route['method']) {
}else{
self::notFound(); //THIS GETS EXECUTED ON EVERY ROUTE
}
This is my Code.
<?php
class Router{
public static $routes = [];
public static function get($route, $callback){
self::$routes[] = [
'route' => $route,
'callback' => $callback,
'method' => 'GET'
];
}
public static function resolve(){
$path = $_SERVER['REQUEST_URI'];
$httpMethod = $_SERVER['REQUEST_METHOD'];
$methodMatch = false;
$routeMatch = false;
foreach(self::$routes as $route){
// convert urls like '/users/:uid/posts/:pid' to regular expression
$pattern = "#^" . preg_replace('/\\\:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', preg_quote($route['route'])) . "$#D";
$matches = Array();
// check if the current request matches the expression
if(preg_match($pattern, $path, $matches) && $httpMethod === $route['method']) {
// remove the first match
array_shift($matches);
// call the callback with the matched positions as params
if(is_callable($route['callback'])){
call_user_func_array($route['callback'], $matches);
}else{
self::render($route['callback']);
}
}
}
}
public static function render($file, $viewsFolder='./views/'){
include($viewsFolder . $file);
}
public static function notFound(){
http_response_code(400);
include('./views/404.php');
exit();
}
}
Router::get("/", "home.php");
Router::get("/user/:id", function($val1) {
$data = array(
"Nicole",
"Sarah",
"Jinx",
"Sarai"
);
echo $data[$val1] ?? "No data";
});
Router::get("/user/profile/:id", "admin.php");
Router::resolve();
?>
You can add notFound() at the very end of resolve() method, and a return when you hit a match:
public static function resolve(){
$path = $_SERVER['REQUEST_URI'];
$httpMethod = $_SERVER['REQUEST_METHOD'];
$methodMatch = false;
$routeMatch = false;
foreach(self::$routes as $route){
$pattern = "#^" . preg_replace('/\\\:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', preg_quote($route['route'])) . "$#D";
$matches = Array();
if(preg_match($pattern, $path, $matches) && $httpMethod === $route['method']) {
array_shift($matches);
if(is_callable($route['callback'])){
call_user_func_array($route['callback'], $matches);
}else{
self::render($route['callback']);
}
return;
}
}
notFound();
}
I have the following array with urls
$data = Array ( 'http://localhost/my_system/users',
'http://localhost/my_system/users/add_user',
'http://localhost/my_system/users/groups',
'http://localhost/my_system/users/add_group' );
Then I have a variable
$url = 'http://localhost/my_system/users/by_letter/s';
I need a function that will return the closest url from the array if $url does not exist. Something like
function get_closest_url($url,$data){
}
get_closest_url($url,$data); //returns 'http://localhost/my_system/users/'
$url2 = 'http://localhost/my_system/users/groups/ungrouped';
get_closest_url($url2,$data); //returns 'http://localhost/my_system/users/groups/'
$url3 = 'http://localhost/my_system/users/groups/add_group/x/y/z';
get_closest_url($url3,$data); //returns 'http://localhost/my_system/users/groups/add_group/'
You can explode both the current URL and each of the URLs in $data, intersect the arrays, then return the array with the most elements (best match). If there's no matches, return false:
<?php
$data = [ "localhost/my_system/users",
"localhost/my_system/users/add_user",
"localhost/my_system/users/by_letter/groups",
"localhost/my_system/users/add_group"];
$url = "localhost/my_system/users/by_letter/s";
function getClosestURL($url, $data) {
$matches = [];
$explodedURL = explode("/", $url);
foreach ($data as $match) {
$explodedMatch = explode("/", $match);
$matches[] = array_intersect($explodedMatch, $explodedURL);
}
$bestMatch = max($matches);
return count($bestMatch) > 0 ? implode("/", $bestMatch) : false; // only return the path if there are matches, otherwise false
}
var_dump(getClosestURL($url, $data)); //returns localhost/my_system/users/by_letter
var_dump(getClosestURL("local/no/match", $data)); //returns false
Demo
You don't mention how you want to specifically check if the URL exists. If it needs to be "live", you can use get_headers() and check the first item for the HTTP status. If it's not 200, you can then go ahead with the URL intersection.
$headers = get_headers($url);
$httpStatus = substr($headers[0], 9, 3);
if ($httpStatus === "200") {
return $url; // $url is OK
}
// else, keep going with the previous function
function get_closest_url($item,$possibilities){
$result = [];
foreach($possibilities as $possibility){
$lev = levenshtein($possibility, $item);
if($lev === 0){
#### we have got an exact match
return $possibility;
}
#### if two possibilities have the same lev we return only one
$result[$lev] = $possibility;
}
#### return the highest
return $result[min(array_keys($result))];
}
That should do it.
With $_SERVER['REQUEST_URI'], I get a URL that could be:
index.php
or
index.php?id=x&etc..
I'd like to do two things:
Find if there is a ?something after index.php name with regular expression.
If there is in the url a specific var (id=x) and delete it from the url.
For example:
index.php?id=x => index.php
index.php?a=11&id=x => index.php?a=11
How can I do this?
To check if there is a ?something after index.php, you could use the built-in function parse_url(), like so:
if (parse_url($url, PHP_URL_QUERY)) {
// ?something exists
}
To remove the id, you could use parse_str(), get the query parameters, store them in an array, and unset the particular id.
And since you also want to re-create the URL after the particular element is deleted from the query part of the URL, then you could use http_build_query().
Here's a function for that:
function removeQueryString($url, $toBeRemoved, $match)
{
// check if url has query part
if (parse_url($url, PHP_URL_QUERY)) {
// parse_url and store the values
$parts = parse_url($url);
$scriptname = $parts['path'];
$query_part = $parts['query'];
// parse the query parameters from the url and store it in $arr
$query = parse_str($query_part, $arr);
// if id == x, unset it
if (isset($arr[$toBeRemoved]) && $arr[$toBeRemoved] == $match) {
unset($arr[$toBeRemoved]);
// if there less than 1 query parameter, don't add '?'
if (count($arr) < 1) {
$query = $scriptname . http_build_query($arr);
} else {
$query = $scriptname . '?' . http_build_query($arr);
}
} else {
// no matches found, so return the url
return $url;
}
return $query;
} else {
return $url;
}
}
Test cases:
echo removeQueryString('index.php', 'id', 'x');
echo removeQueryString('index.php?a=11&id=x', 'id', 'x');
echo removeQueryString('index.php?a=11&id=x&qid=51', 'id', 'x');
echo removeQueryString('index.php?a=11&foo=bar&id=x', 'id', 'x');
Output:
index.php
index.php?a=11
index.php?a=11&qid=51
index.php?a=11&foo=bar
Demo!
If it must be a regular expression :
$url='index.php?a=11&id=1234';
$pattern = '#\id=\d+#';
$url = preg_replace($pattern, '', $url);
echo $url;
output
index.php?a=11&
There is a trailing &, but the above removes any id=xxxxxxxx
I want to read one parameter from a specific URl :
Like : http://www.youtube.com/watch?v=MrOiL74P-9E&feature=watch
Output should be : MrOiL74P-9E
I try to search and I found this function :
function remove_query_part($url, $term)
{
$query_str = parse_url($url, PHP_URL_QUERY);
if ($frag = parse_url($url, PHP_URL_FRAGMENT)) {
$frag = '#' . $frag;
}
parse_str($query_str, $query_arr);
unset($query_arr[$term]);
$new = '?' . http_build_query($query_arr) . $frag;
return str_replace(strstr($url, '?'), $new, $url);
}
This function just remove one parameter and return the rest. Can anyone edit this function to return just the video ID and Ignore whatever else in the URL.
$query_str = parse_url($url, PHP_URL_QUERY);
parse_str($query_str, $args);
print $args['v']; // <- MrOiL74P-9E
I think you know how to put this into a function...
Quick and dirty:
$url='http://www.youtube.com/watch?v=MrOiL74P-9E&feature=watch';
function test($url)
{
$data=parse_url($url);
if(!isset($data['query']))
{
return null;
}
else
{
$ex=explode('&', $data['query']);
foreach($ex as $key => $val)
{
$param=explode('=', $val);
if($param[0]=='v')
{
return $param[1];
break;
}
}
}
}
echo test($url);
Not tested but you could try....
function remove_query_part($url, $term) {
// get the query part of the string (i.e. after the '?')
$query_str = parse_url($url, PHP_URL_QUERY);
$queryItems = explode('&', $query_str);
$item = array();
$itemArray = array();
foreach ($queryItems as $item) {
$itemArray = explode('=', $item);
if($item[0] == $term) {
return $item[1];
}
}
return false;
}
Want to remove p2variable from url string, below are 3 cases if case 3 also remove ? sign.
case 1: http://www.domain.com/myscript.php?p1=xyz&p2=10&p3=ghj
result: http://www.domain.com/myscript.php?p1=xyz&p3=ghj
case 2: http://www.domain.com/myscript.php?p2=10&p3=ghj
result: http://www.domain.com/myscript.php?p3=ghj
case 3: http://www.domain.com/myscript.php?p2=10
result: http://www.domain.com/myscript.php
Want to achieve result with single preg_replace expression.
Don't use regular expressions when dealing with URL values. It's much easier (and safer) to handle them as a URL instead of plain text.
This could be one way to do it:
Split the url first and parse the query string
Take the parameter out
Rebuild the url
The below code is an example of such an algorithm:
// remove $qs_key from query string of $url
// return modified url value
function clean_url_qs($url, $qs_key)
{
// first split the url in two parts (at most)
$parts = explode('?', $url, 2);
// check whether query string is passed
if (isset($parts[1])) {
// parse the query string into $params
parse_str($parts[1], $params);
// unset if $params contains $qs_key
if (array_key_exists($qs_key, $params)) {
// remove key
unset($params[$qs_key]);
// rebuild the url
return $parts[0] .
(count($params) ? '?' . http_build_query($params) : '');
}
}
// no change required
return $url;
}
Test code:
echo clean_url('http://www.domain.com/myscript.php?p1=xyz&p2=10&p3=ghj', 'p2'), "\n";
echo clean_url('http://www.domain.com/myscript.php?p2=10&p3=ghj', 'p2'), "\n";
echo clean_url('http://www.domain.com/myscript.php?p2=10', 'p2'), "\n";
Found this in one of my old projects (a bit of shitcode, but...), may help you:
$unwanted_param = 'p2';
$s = 'http://www.domain.com/myscript.php?p1=xyz&p2=10&p3=ghj';
$s = parse_url($s);
$params = explode('&', $s['query']);
$out_params = array();
foreach ($params as $key => &$param) {
list($name, $value) = explode('=', $param);
if ($unwanted_param == $name) {
unset($params[$key]);
} else {
$out_params[$name] = $value;
}
}
$query = '?' . http_build_query($out_params);
$result = $s['scheme'] . '://' . $s['host'] . $s['path'] . $query;
var_dump($result);
Using preg_replace, something like
$url = preg_replace('!([\?&]p2=[^&\?$]+)!i', '', $url);
However, personally I'd do the following
if (strpos($url, '?') !== false) {
list($domain, $qstring) = explode('?', $url, 2);
parse_str($qstring, $params);
if (isset($params['p2'])) {
unset($params['p2']);
}
$qstring = !empty($params) ? '?' . http_build_query($params) : '';
$url = $domain . $qstring;
}