PHP - Wildcards in URI string - php

I am trying to create an API request handler that can read wildcards in a string. The ideal situation is something like this.
$myClass->httpGet('/account/[account_id]/list-prefs', function ($account_id) {
// Do something with $account_id
});
Where [account_id] is the wild card. The actual URI would look like:
http://api.example.com/account/123456/list-prefs
The actual function looks like...
function httpGet($resource, $callback) {
$URI = urldecode(str_replace('/'.$this->API_VERSION, '', $_SERVER['REQUEST_URI']));
$match = preg_match_all('/\[([a-zA-Z0-9_]+)\]/', $resource, $array);
if ($resource /*matches with wildcards*/ $URI) {
// Do something with it.
}
...
}
My problem is...
I cannot figure out how to match the string within the function with the URI in order to call the callback.
How to parse the string with the values supplied in the URI (replace [account_id] with 123456).

I think you are missing something like:
tokens = array('[account_id]' => '/\[([a-zA-Z0-9_]+)\]/');
Then:
function replaceTokens($resource) {
# get uri with tokens replaced for actual regular expressions and return it
}
function httpGet($resource, $callback) {
$URI = urldecode(str_replace('/'.$this->API_VERSION, '', $_SERVER['REQUEST_URI']));
$uriRegex = replaceTokens($resource);
$match = preg_match_all($uriRegex, $URI, $array);
if ($match) {
// Do something with it.
}
}

Related

How to pass URL parameter into a PHP file?

everyone.
I have created very basic router in PHP and now I am stuck.
The user can navigate to different URLs and pass parameters that can be used to display data for example to get data from an array.
However I am stuck, I do not know how to pass these url parameters so they can be used inside a file.
For example this route
"/user/:id" -> If user navigates to /user/1 -> This executes a callback function and he receives data from an array.
However when the url doesn't have callback function but has a name of a file, the router will load a file, for example the user page.
Router::get("/user/:username", "user.php");
So my question is How can I get the "username" from the route and pass it into the user.php file ?
I have tried using $_GET['username'], however that doesn't work as the url doesn't have ? inside of it.
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']) {
$routeMatch = true;
// 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']);
}
}
}
if(!$routeMatch){
self::notFound();
}
}
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/:username", "user.php");
Router::get("/user/profile/:id", "admin.php");
Router::resolve();
?>
You could pass $matches to the render() method as second optional parameter, and that's it. As well as these variables are accessible in the method scope, they are accessible in all the files included/required from this scope. I.e.:
self::render($route['callback'], $matches);
and in the included file:
print_r($matches);
UPD: In order to IDE not highlighting "unknown" variable, you can add a phpdoc-block somewhere in the included file, like this:
/** #var array $matches */

php Variable for blocking certain URLS

I am writing a function in php for blocking some urls like Scam & Fraud or profanity. It should be act like a filter or validation rule. I am just confusing which variable is best for this purpose.
1. trim()
2. strippos ()
or do you have any better solutions then this .?
Function would be like this
function validation($data) {
$data = trim(https://www.url.com, https://www.url1.com ...);
return data;
}
OR
function validation($data) {
$data = stripos(https://www.url.com, https://www.url1.com ...);
return data;
}
That ain't gonna work. I'd list all the url's you want to check in an array and search in there;
function is_url_allowed($url) {
$not_allowed = array(
'google.com',
'facebook.com',
'usa.gov'
);
$parsed_url = parse_url($url, PHP_URL_HOST);
return !in_array($parsed_url, $not_allowed);
}

Get id from url in a variable PHP

Have a problem to get the id from the URL in a variable!
The Url is like this domain.com/article/1123/
and its like dynamic with many id's
I want to save the 1123 in a variable please help!
a tried it with this
if(isset($_GET['id']) && !preg_match('/[0-9]{4}[a-zA-Z]{0,2}/', $_GET['id'], $id)) {
require_once('404.php');
} else {
$id = $_GET['id'];
}
The absolute simplest way to accomplish this, is with basename()
echo basename('domain.com/article/1123');
Which will print
1123
the reference url click hear
I would do in this way:
Explode the string using /.
Get the length of the exploded array.
Get the last element, which will be the ID.
Code
$url = $_SERVER[REQUEST_URI];
$url = explode("/", $url);
$id = $url[count($url) - 1];
You should definitely be using parse_url to select the correct portion of the URL – just in case a ?query or #fragment exists on the URL
$parts = explode('/', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
$parts[0]; // 'domain.com'
$parts[1]; // 'article'
$parts[2]; // '1123'
You'll probably want to reference these as names too. You can do that elegantly with array_combine
$params = array_combine(['domain', 'resource', 'id'], $parts);
$params['domain']; // 'domain.com'
$params['resource']; // 'article'
$params['id']; // '1123'
I'm really feeling like a procrastinator right now so I made you a little router. You don't have to bother dissecting this too much right now; first learn how to just use it, then you can pick it apart later.
function makeRouter ($routes, callable $else) {
return function ($url) use ($routes, $else) {
foreach ($routes as $route => $handler) {
if (preg_match(makeRouteMatcher($route), $url, $values)) {
call_user_func_array($handler, array_slice($values, 1));
return;
}
}
call_user_func($else, $url);
};
}
function makeRouteMatcher ($route) {
return sprintf('#^%s$#', preg_replace('#:([^/]+)#', '([^/]+)', $route));
}
function route404 ($url) {
echo "No matching route: $url";
}
OK, so here we'll define our routes and what's supposed to happen on each route
// create a router instance with your route patterns and handlers
$router = makeRouter([
'/:domain/:resource/:id' => function ($domain, $resource, $id) {
echo "domain:$domain, resource:$resource, id:$id", PHP_EOL;
},
'/public/:filename' => function ($filename) {
echo "serving up file: $filename", PHP_EOL;
},
'/' => function () {
echo "root url!", PHP_EOL;
}
], 'route404');
Now let's see it do our bidding ...
$router('/domain.com/article/1123');
// domain:domain.com, resource:article, id:1123
$router('/public/cats.jpg');
// serving up file: cats.jpg
$router('/');
// root url!
$router('what?');
// No matching route: what?
Yeah, I was really that bored with my current work task ...
That can be done quite simple. First of all, you should create a variable with a string that contains your URL. That can be done with the $_SERVER array. This contains information about your server, also the URL you're actually at.
Second point is to split the URL. This can be done by different ways, I like to use the p_reg function to split it. In your case, you want to split after every / because this way you'll have an array with every single "directory" of your URL.
After that, its simply choosing the right position in the array.
$path = $_SERVER['REQUEST_URI']; // /article/1123/
$folders = preg_split('/', $path); // splits folders in array
$your_id = $folders[1];
To be thorough, you'll want to start with parse_url().
$parts=parse_url("domain.com/article/1123/");
That will give you an array with a handful of keys. The one you are looking for is path.
Split the path on / and take the last one.
$path_parts=explode('/', $parts['path']);
Your ID is now in $path_parts[count($path_parts)-1];

replace a wildcard for url comparison

I need to check valid routes from a route files where i want to put a wildcard (or placeholder) for url part that is dynamic.
The router read all routes in that json format:
{"action" : "BlogController#showPost", "method" : "GET", "url" : "showPost/id/{}"}
I need when the comparsion occurs to change the holder {any} with the current value and maybe allow to put regex expression inside the {any} tag.
An url like this:
showPost/id/211 have to be compared with showPost/id/{} and should return true. If possible i would like to allow putting {'[0-9]\'} as optional param to ensure that the real value match a regex expression.
What best solution to do this?
The comparsison method is this:
public static function findAction($query) {
foreach (Router::getInstance()->routes as $route) {
if ($route->url == $query) {
return $route;
}
}
}
The $query contains /showPost/id/221 and the Router::getInstance()->routes->route->url contains showPost/id/{}
The post is related to this auto-solved question:
how to make nice rewrited urls from a router
I don't re-post router code in order to avoid duplication.
Thanks in advance
I found a solution using "?" as a wildcard for routes json file. Its not maybe the best way but actually works.
The method now replace (and try to check) the real path queries with ? and check the routes each cycle.
public static function findAction($query) {
//d($query);
$queryArray = explode("/", $query);
//d($queryArray);
foreach (Router::getInstance()->routes as $route) {
if ($route->url == $query) {
// replace current routes url with incoming url
$route->url = $query;
return $route;
} else {
$queryReplace = null;
foreach ($queryArray as $key => $value) {
if (strpos($route->url,"?")) {
$queryReplace = str_replace("?", $value, $route->url);
if($queryReplace == $query) {
$route->url = $query;
return $route;
}
}
}
}
I still would like to put {any or regex} but atm i did not found a solution to this.

Using Regex to match url for route function. Can't get Regex to work

I am trying to build a router function to properly match incoming URI's and match them to an array of stored system URI's. I also have wildcards '(:any)' and '(:num)' similar to CodeIgniter.
Basically, I am trying to get the 'admin/stats/(:num)' entry to match on both 'admin/stats' and admin/stats/1'.
While the script is starting I grab all paths from a separate array and use a foreach to save each path:
route('admin/stats/(:num)', array('#title' => 'Statistics',...));
The function is:
function route($path = NULL, $options = NULL) {
static $routes;
//If no arguments are supplied, return all routes stored.
if(!isset($path) && !isset($options)) {
return $routes;
}
//return options for path if $path is set.
if(isset($path) && !isset($options)) {
//If we have an exact match, return it.
if(array_key_exists($path, $routes)) {
return $routes[$path];
}
//Else, we need to use RegEx to find the correct route options.
else {
$regex = str_replace('/', '\/', $path);
$regex = '#^' . $regex . '\/?$#';
//I am trying to get the array key for $route[$path], but it isn't working.
// route_replace('admin/stats/(:num)') = 'admin/stats/([0-9]+)'.
$uri_path = route_replace(key($routes[$path])); //route_replace replaces wildcards for regex.
if(preg_match($regex, $uri_path)) {
return $routes[$path];
}
}
}
$routes[$path] = $options;
return $routes;
}
Route replace function:
function route_replace($path) {
return str_replace(':any', '.+', str_replace(':num', '[0-9]+', $path));
}
A key/value pair in the $routes array looks like:
[admin/stats/(:num)] => Array
(
[#title] => Statistics //Page title
[#access] => user_access //function to check if user is authorized
[#content] => html_stats //function that returns HTML for the page
[#form_submit] => form_stats //Function to handle POST submits.
)
Thanks for the help. This is my first router and I am not that familiar in making proper Regex's.
'admin/stats/(:num)' will never match 'admin/stats' as in your "pattern" the slash is required. In pseduo-regex you need to do something like 'admin/stats(/:num)'.
There does also seem to be a few bugs in your code. This line
$uri_path = route_replace(key($routes[$path]));
is in the block that is executed when $path is not a key that exists in $routes.
I've tried to rewrite it and this seems to work (this is just the else clause):
foreach( array_keys( $routes ) as $route ) {
$regex = '#^' . $route . '?$#';
//I am trying to get the array key for $route'$path', but it isn't working.
// route_replace('admin/stats/(:num)') = 'admin/stats/('0-9'+)'.
$uri_path = route_replace($regex); //route_replace replaces wildcards for regex.
if(preg_match($uri_path,$path)) {
return $routes[$route];
}
}
But this requires 'admin/stats/(:num)' to be 'admin/stats(/:num)'.
btw if you don't have one already, you should get a debugger (Zend and xDebug are two of the most common ones for PHP). They can be invaluable in solving problems like this.
Also, ask yourself if you need to write a router, or whether you can't just use one of the perfectly good ones out there already...

Categories