PHP URL Param redirects - with wildcards/regex - php

I recently found this solution for doing php url variable to header location redirects.
It's so much more manageable compared to htaccess for mass redirects, however one thing I want to next work out, how I can use regex to achieve what you can do with htaccess where request/(.*) goes to destination/$1.
My grasping is on that you use preg_match or preg_replace or something. How can I achieve something like the below, preferably keeping it short like this if possible. (I know this is wrong btw, just for example sake).
preg_match($redirects['request(.*)'] = "$domain/destination/\1");
Basically to break it down, say I want to redirect doma.in/pics to domain.tld/pictures, I have htaccess redirect that to doma.in/index?req=pics, where index is the script file and req is the parameter used.
And then using the script I have a line like $redirects['pics'] = "$domain/pictures";, where $domain variable is tied to http://domain.tld.
That works great, however I want to take this a step further with regex and send anything pics/*stuff*, aka doma.in/index?req=pics/*stuff* to $domain/pictures/*stuff*.
Here's an example of how things look using this script for doing many redirects.
$redirects['request'] = "$domain/dest";
$redirects['request2'] = "$domain/dest2";
$redirects['request3'] = "$domain/dest3";
Even though I've linked the post at the top that I got the script I'm using, here's the code:
if(isset($_GET['req']) && isset($redirects[$_GET['req']])) {
$loc = htmlspecialchars($redirects[$_GET['req']]);
header("Location: " . $loc);
exit();
}
header("Location: $domain");
With the $redirects lines being included above this, which I have in included files.

This is a long read, plus it is messy way of doing this. See my accepted answer for a much better way to do this.
I thought that ltrim() was what I wanted, seeing on other answers that if for example I specify 0 as what to remove, 01 will become 1, 001 will become 01, and 10 will be left as 10, 100 as 100 and so on. However this was not turning out to be the case. and instead it would strip all instances of the stated characters. Though it wasn't doing it with the slash, so confused.
This however does it correctly:
if (strpos($get, $req) === 0) {
$get = substr($get, strlen($req));
}
return $get;
Thanks to this answer for this one liner.
All I'm doing here with this script is just assigning $redirects['request'] to the associated value, like with any other variable value assignments. And the $_GET['req'] already does the job to get well whatever the parameter is, so no complicated preg or regex or anything.
So with that substr(), we can take the $_GET['req'] and do the following:
$req = "pics/";
$get = $_GET['req'];
$wild = strpos($get, $req) === 0
? substr($get, strlen($req))
: $get;
$redirects["pics/$wild"] = "$domain/pictures/$wild";
This takes pics/*stuff* and removes the pics/ so the value of $wild equals just *stuff*, and so I just use that in the redirect to make a wildcard and taadaa.
This is completely functional, but let's make this even better to save remembering this code each time which is a fair bit.
Create a function like this above the redirects:
function wildcard($req) {
$get = $_GET['req'];
return strpos($get, $req) === 0
? substr($get, strlen($req))
: $get;
}
By calling wildcard('pics/');, the $req equals pics/.
We can use this in redirects like:
$req = "pics/";
$wild = wildcard($req);
$redirects[$req.$wild] = "$domain/pictures/$wild";
It's still a bit more than I hoped for, so the idea I've had is to call $req as a global in the function, like this:
function wild() {
$get = $_GET['req']; global $req;
return strpos($get, $req) === 0
? substr($get, strlen($req))
: $get;
}
And then do the redirect like:
$req = "pics/";
$redirects[$req.wild()] = "$domain/pictures/".wild();
That becomes a much shorter single line. Though with the conflict around using globals, I've just put it back to as before but instead of repeatedly assigning $wild, just put $req back inside wild() and have it be like:
$req = "pics/"; $redirects[$req.wild($req)] = "$domain/pictures/".wild($req);
It's still shorter anyway and isn't much to it over the brackets being empty.
P.S, This method, you want to include the trailing slash on the parameter so results don't get messy.
In order to achieve to be able to send pics to the $domain/pictures, we want to have a trailing slash at the end of the parameter. In your redirect rule in htaccess to send requests as a parameter to the script, add a trailing slash on the end. So if you're using Apache or Litespeed, you can do the following in htaccess to send all requests to your script as a parameter with the trailing slash like:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?req=$1/ [R=301,L]
Make sure this is at the bottom so it doesn't take priority over other rules.
Also added a precautionary rtrim() to the script to remove the trailing slash in the header location, so if you want to link anything that doesn't remove trailing slashes on file links, it doesn't go to a dead link. As again the slashes weren't being effected by what behaviour I discovered as mentioned at top, this is fine here.
Here is how you can have things now.
function wild($req) {
$get = $_GET['req'];
return strpos($get, $req) === 0
? substr($get, strlen($req))
: $get;
}
$domain = "http://domain.tld";
// Redirects
$req = "request1/"; $redirects[$req.wild($req)] = "$domain/dest1/".wild($req);
$req = "request2/"; $redirects[$req.wild($req)] = "$domain/dest2/".wild($req);
$req = "request3/"; $redirects[$req.wild($req)] = "$domain/dest3/".wild($req);
// Run Script
if (isset($_GET['req'], $redirects[$_GET['req']])) {
$loc = htmlspecialchars($redirects[$_GET['req']]);
header("Location: " . rtrim($loc,"/"));
exit();
}
// If no match in the redirects, redirect to this location.
header("Location: $domain");
Now, this has one flaw if the destination is sending non existent requests to the script, if a destination, which is going to be guaranteed with wildcards, is non existent for the request, well.back it goes to the script and bam you have a redirect loop.
My way of solving this is to add ?referer=doma.in to the end of the header location, and in the htaccess on domain.tld, exclude non existent requests with that query string from redirecting back to the script.
So that looks like:
$loc = htmlspecialchars($redirects[$_GET['req']]).'?referer=doma.in';
And in the htaccess of domain.tld, place a rewritecond above the existing rule like so to exclude the query string:
# Ignore these referer queries
RewriteCond %{QUERY_STRING} !referer=doma.in [NC]
# Send dead requests to doma.in with uri as query
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ http://doma.in/?referer=domain.tld&req=$1/ [R=301,L]
For good measure I also added a referer on the redirect for the domain.tld.
Now, as a bonus, to hide the refer query on requests to tidy things up, let's add below:
# Send dead requests with referer query to home (or 404 or wherever)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{QUERY_STRING} "referer=" [NC]
RewriteRule (.*) /?req=$1 [R=301,L]
# Remove referer query from requests
RewriteCond %{QUERY_STRING} "referer=" [NC]
RewriteRule (.*) /$1/? [R=301,L]
We need to send dead referer query requests somewhere before we remove the query, otherwise we'd be back at step one. I have the dead requests sent to my homepage with the request uri as a parameter so that can still know what the request url was.
And job done. But as an extra bonus, let's make external/non wildcard redirects not have a query. So back in the script, change the script to be like so:
$get = $_GET['req'];
$loc = $redirects[$get];
$wildloc = $wildcards[$get];
// Run Script
if(isset($get) && isset($loc) || isset($wildloc)) {
if(isset($wildcards[$get])) {
$loc = rtrim($wildloc,'/').'?referer=hwl.li'; }
$loc = rtrim(htmlspecialchars($loc),'/');
header("Location: ".$loc);
exit();
}
Here, I've moved things about with the $_GET['req'] assigned to $get, $redirects[$get] assigned as $loc, $wildcards[$get] assigned to $wildloc, and call them in the issets, along with an extra isset after an :, aka OR for $wildloc.
And then have an if statement so $wildcards redirects use $loc assigned to $wildloc, and $redirects ones use the above one.
This way, we can have tidy redirects.
So now things look like:
// Wildcard function
function wild($req) {
$get = $_GET['req'];
return strpos($get, $req) === 0
? substr($get, strlen($req))
: $get;
}
$domain = "http://domain.tld";
// Redirects
$req = "request1/"; $wildcards[$req.wild($req)] = "$domain/dest1/".wild($req); // A wildcard redirect
$req = "request2/"; $wildcards[$req.wild($req)] = "$domain/dest2/".wild($req); // A wildcard redirect
$redirects['request3/'] = "$domain/dest3/"; // Not a wildcard redirect
$get = $_GET['req'];
$loc = $redirects[$get];
$wildloc = $wildcards[$get];
// Run Script
if(isset($get) && isset($loc) || isset($wildloc)) {
if(isset($wildcards[$get])) {
$loc = rtrim($wildloc,'/').'?referer=hwl.li';}
$loc = rtrim(htmlspecialchars($loc),'/');
header("Location: ".$loc);
exit();
}
// If no match in the redirects, redirect to this location.
header("Location: $domain/?req=$get");
This improves things so much and solves the redirect loop.
Edited this again slightly as what I did here with the query string being appended.. the rtrim() therefore was looking for a non existent trailing slash after that, not where we wanted it to be doing it, before. So now the rtrim() comes before. Doubles it up which is slightly annoying but at least it does the job right now.

Updated script
I've completely overhauled this script, now I have realised a lot more that this is all an array that we're doing here with each redirect just adding to it. Also I've learnt a lot more about functions, and it's now extremely portable while also doing a lot more.
Here is the full new script. It's a beast compared to the previous, but the previous was so messy.
function redirects($config) { // Create a redirects(); function with the inputted array set as $config
// Config Variables
$req = $config['param']; // Assign $req to the $config param value
$dir = $config['dir']; // Assign $dir to the $config dir value
$domain = $config['domain']; // Assign $domain to the $config domain value
$_404 = $config['404']; // Assign $_404 to this $config 404 value
$_referer = $config['referer']; // Assign $referer_ to the referer value
$referer = 'referer='.$_referer; // Assign $referer to the full referer param
if (isset($_GET[$_referer])) {
if ($_GET[$_referer] == $_referer) { // If this script's referer exists,
echo "Do not loop back to the script!"; // Throw a warning
exit(); // & exit
} // Otherwise continue
}
if (isset($_GET[$req]) && !empty($_GET[$req])) { // If a req parameter exists & isn't empty, continue
$req = $_GET[$req]; // Assign $req to $_GET[$req]
// Create the arrays
$redirects = $wildcards = $halfwilds = array(); // Create the arrays needed, so if there's not at least one redirect done, which would be what creates the array otherwise, there won't be a 500 error due to it.
// Redirect includes
foreach (glob($dir) as $filename) { // Search for all redirect php files at $dir location
include $filename; // Include those files
}
// Leading & Trailing Slashes
$req = '/'.trim($req, '/').'/'; // Add a leading & trailing slash to $req param if non existent
function redir_editkeys($array) { // Create an editkeys(); function and pass the specified array as $array
$keys = array_keys($array); // Extract the keys of the array as values of $keys array
foreach ($keys as &$value) {
$value = '/'.trim($value, '/').'/'; // Add a leading & trailing slash to $keys values if non existent
}
return array_combine($keys, $array); // Replace the original array's keys with the modified keys & return
}
// Referer
function referer($redir, $referer, $domain) { // Create a referer(); function and pass to it $redir, $referer, & $domain.
if (strpos($redir, $domain) !==false) { // Using $domain, check $redir for a match.
$redir = $redir.'?'.$referer; // If there's a match, add the referer param
} return $redir; // Return the edited $redir, or if no match, return without editing
}
// Redirects
$redirects = redir_editkeys($redirects); // Edit $redirects keys using editkeys();
if (isset($redirects[$req])) { // Check that $redirects[$req] exists
$redir = $redirects[$req]; // Assign $redir to $redirects[$req];
$redir = referer($redir, $referer, $domain); // If it does, run referer();
header("Location: $redir"); // & Redirect
exit();
}
// Wildcards
$wildcards = redir_editkeys($wildcards); // Edit $wildcards keys using editkeys();
foreach ($wildcards as $key => $value) { // Assign variables to $wildcards keys & values
if (strpos($req, $key) !== false) { // Using $key, check $req for a match
$wild = substr($req, strlen($key)); // Extract everything after the match as $wild
$req = substr($req, 0, strlen($key)); // Extract the matching part at the beginning as $req
if (isset($wildcards[$req])) { // Check that $wildcards[$req] exists
$redir = $wildcards[$req]; // Assign $redir to $wildcards[$req]
$redir = $redir.$wild; // Attach $wild onto $redir
$redir = referer($redir, $referer, $domain); // If it does, run referer();
header("Location: $redir"); // & Redirect
exit();
}
}
}
// Half Wilds
$halfwilds = redir_editkeys($halfwilds); // Edit $halfwilds keys using editkeys();
foreach ($halfwilds as $key => $value) { // Assign variables to $halfwilds keys & values
if (strpos($req, $key) !== false) { // Using $key, check $req for a match
$req = substr($req, 0, strlen($key)); // Extract the matching part at the beginning as $req
if (isset($halfcards[$req])) { // Check that $halfwilds[$req] exists
$redir = $halfwilds[$req]; // Assign $redir to $halfwilds[$req]
$redir = referer($redir, $referer, $domain); // If it does, run referer();
header("Location: $redir"); // & Redirect
exit();
}
}
}
// 404
$req = "req=".trim($req,'/')."&"; // Attach the param name to the $req value for the 404 redirect if it's not empty and remove the slashes.
}
else { $req = ''; } // If no req param, or if empty, unset $req
header("Location: ".$domain.$_404."?".$req.$referer); // If there's no match, redirect to this location.
}
We call this script by:
redirects(
array( // Config
'param' => 'req', // Param name || Set the name of the url param. Here it is "req".
'dir' => 'redirects/*/*.php', // Redirect file(s) location || Set the location to look for the file(s) you store your redirects. Here I have my files in sub folders of a rediects folder. Do not put a leading slash infront, use "../" etc as it must be relative, not absolute..
'domain' => 'http://domain.tld', // Your domain || Set your website's domain name here. This'll be used to check whether redirects go to it and if the referer is needed.
'404' => '/', // 404 location || Set the location 404s will go to. Hereit is just my homepage, so I've put "/".
'referer' => 'redirector' // Referer param || Set the value of the referer param that will be used in redirects to the same site so we can stop 404s resulting in a loop.
)
);
To do simple redirects, we can do:
$redirects['request1'] = "$domain/dest1";
$redirects['request2'] = "$domain/dest2";
To do wildcards, we can do: (make sure to add trailing slashes on the end unless it's a destination such as as a query string)
$wildcards['request3'] = "$domain/dest3/";
$wildcards['request4'] = "$domain/dest4/";
And I also have added a half wildcards, so if you want to send request5/anypath to just $domain/dest5, and not dest5/anypath, we can do:
$halfwilds['request5'] = "$domain/dest5";
$halfwilds['request6'] = "$domain/dest6";
Let's split this into chunks and take a look at what each part is doing:
Configuration:
First we start the redirects(); function with the $config array as the argument. Then we assign variables to each setting.
function redirects($config) { // Create a redirects(); function with the inputted array set as $config
// Config Variables
$req = $config['param']; // Assign $req to the $config param value
$dir = $config['dir']; // Assign $dir to the $config dir value
$domain = $config['domain']; // Assign $domain to the $config domain value
$_404 = $config['404']; // Assign $_404 to this $config 404 value
$_referer = $config['referer']; // Assign $referer_ to the referer value
$referer = 'referer='.$_referer; // Assign $referer to the full referer param
We have two variables for the referer, one for the referer value, and the second to join up as a full param for use in the redirects.
Referer check
I've firstly included a check for if this script's referer param exists in the request, and just simply say "Do not loop back to the script!", and then exit(); the script.
if (isset($_GET[$_referer])) {
if ($_GET[$_referer] == $_referer) { // If this script's referer exists,
echo "Do not loop back to the script!"; // Throw a warning
exit(); // & exit
} // Otherwise continue
}
The arrays and includes
Next up, if there is no match for the referer param in the request, we open an if for when the req param exists and is not empty, create the empty arrays, and then include where we have stated in the config we're putting our redirects.
if (isset($_GET[$req]) && !empty($_GET[$req])) { // If a req parameter exists & isn't empty, continue
$req = $_GET[$req]; // Assign $req to $_GET[$req]
// Create the arrays
$redirects = $wildcards = $halfwilds = array(); // Create the arrays needed, so if there's not at least one redirect done, which would be what creates the array otherwise, there won't be a 500 error due to it.
// Redirect includes
foreach (glob($dir) as $filename) { // Search for all redirect php files at $dir location
include $filename; // Include those files
}
From my understanding with the way php works, this actually has a benefit putting the includes inside the if, that if the condition isn't met, which would be the case when the request has no req param, the includes won't even get processed.
Leading & Trailing slashes
With how things were before with the previous script, we had to make sure we added slashes at the end of the redirect keys, and send requests to the script with trailing slashes at the end of the param. Let's take the need away from doing this by using trim(); for if it exists mixed with adding it back on, plus a leading slash too to ensure strpos(); won't match 'cat-dog/' when we wanted to look for 'dog/'.
// Leading & Trailing Slashes
$req = '/'.trim($req, '/').'/'; // Add a leading & trailing slash to $req param if non existent
function redir_editkeys($array) { // Create an editkeys(); function and pass the specified array as $array
$keys = array_keys($array); // Extract the keys of the array as values of $keys array
foreach ($keys as &$value) {
$value = '/'.trim($value, '/').'/'; // Add a leading & trailing slash to $keys values if non existent
}
return array_combine($keys, $array); // Replace the original array's keys with the modified keys & return
}
Here we've created a function called editkeys(); which makes a new array of the keys of whatever array you pass to it, does a foreach to modify those keys and then put them back into the original array, replacing the original keys.
Destination domain check for if referer param is needed
If we are sending the destination's 404 requests to the script, we want to stop redirect loops for redirects to that destination that end in 404s. We could just have the referer param to always be added, but that could risk bugging something up depending on that destination's website configuration's handling of query strings. So let's do a function to check for whether the passed destination goes to $domain, add the referer to it if true and return it back.
// Referer
function referer($redir, $referer, $domain) { // Create a referer(); function and pass to it $redir, $referer, & $domain.
if (strpos($redir, $domain) !==false) { // Using $domain, check $redir for a match.
$redir = $redir.'?'.$referer; // If there's a match, add the referer param
} return $redir; // Return the edited $redir, or if no match, return without editing
}
Redirecting
Next we have the part that puts together the redirects. Firstly we have the simple redirects. Firstly, we run the $redirects array though the editkeys(); function, then it checks if the matching key is in the array, run the referer(); function above on the destination and then redirect to it.
// Redirects
$redirects = redir_editkeys($redirects); // Edit $redirects keys using editkeys();
if (isset($redirects[$req])) { // Check that $redirects[$req] exists
$redir = $redirects[$req]; // Assign $redir to $redirects[$req];
$redir = referer($redir, $referer, $domain); // If it does, run referer();
header("Location: $redir"); // & Redirect
exit();
}
Secondly is the wildcards. We run the $wildcards array through the editkeys(); function, run a foreach(); on the array to look for a matching key, get everything after the match as $wild, get only the match as $req, check the $req value exists as a key in the array, get that key's destination, attach the $wild on, then run the check for if referer is needed, redirect and bam.
// Wildcards
$wildcards = redir_editkeys($wildcards); // Edit $wildcards keys using editkeys();
foreach ($wildcards as $key => $value) { // Assign variables to $wildcards keys & values
if (strpos($req, $key) !== false) { // Using $key, check $req for a match
$wild = substr($req, strlen($key)); // Extract everything after the match as $wild
$req = substr($req, 0, strlen($key)); // Extract the matching part at the beginning as $req
if (isset($wildcards[$req])) { // Check that $wildcards[$req] exists
$redir = $wildcards[$req]; // Assign $redir to $wildcards[$req]
$redir = $redir.$wild; // Attach $wild onto $redir
$redir = referer($redir, $referer, $domain); // If it does, run referer();
header("Location: $redir"); // & Redirect
exit();
}
}
}
Then we have half wildcards, which does more or less the same. I've not really got any real reason for this one myself, but there are reasons for it for sure, such as that you've got rid of a whole portfolio gallery and want to send requests to that and sub requests to the images etc back to the portfolio, this will do the job. So it's an included part of the script.
// Half Wilds
$halfwilds = redir_editkeys($halfwilds); // Edit $halfwilds keys using editkeys();
foreach ($halfwilds as $key => $value) { // Assign variables to $halfwilds keys & values
if (strpos($req, $key) !== false) { // Using $key, check $req for a match
$req = substr($req, 0, strlen($key)); // Extract the matching part at the beginning as $req
if (isset($halfcards[$req])) { // Check that $halfwilds[$req] exists
$redir = $halfwilds[$req]; // Assign $redir to $halfwilds[$req]
$redir = referer($redir, $referer, $domain); // If it does, run referer();
header("Location: $redir"); // & Redirect
exit();
}
}
}
I did want to try to do a function to save this largely duplicated code, but foreach();'s are darn awkward.
404
Then we have the 404 redirect that sends back home whatever requests to the script that either have no match with the request as a req param, or just with the referer param if there was no req param or if it is empty.
// 404
$req = "req=".trim($req,'/')."&"; // Attach the param name to the $req value for the 404 redirect if it's not empty and remove the slashes.
}
else { $req = ''; } // If no req param, or if empty, unset $req
header("Location: ".$domain.$_404."?".$req.$referer); // If there's no match, redirect to this location.
}
And that's the script.
Now, to send dead requests to the script.
Apache / Litespeed / Openlitespeed (htaccess)
At the bottom of your htaccess, add this:
############# Rewrite dead requests to redirector with uri as query
RewriteCond %{QUERY_STRING} !referer=redirector [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /redirect.php?req=$1 [R=301,L]
#############
Nginx
Add this in the server's (vhost) conf:
############# Send dead requests to redirector with uri as query
error_page 404 = #redirects;
location #redirects {
if ($query_string !~ referer=redirector {
rewrite ^/(.*)(.php?) /redirect.php?req=$1 redirect;
}
}
#############
I'm using Nginx now, moving away from OpenLitespeed since a weird issue came up that Nginx didn't have at all. So been some fun figuring out Nginx conf way of doing things. One thing I noticed was that by rewriting extension requests to no extensions, requests that get sent else where you'll notice have .php added on the end. So I've got it to exclude the extension in the rewrite here. Also doing these wildcard redirects in a location for error_page 404, is a tonne lot easier than trying to set in an if to ignore existent files/folders.
Removing the referer param on requests at the destination.
While trying to do the same with nginx what I'd done with htaccess, remove the referer param, which I was having fun trying to do, I realised that was a bit of a flawed solution anyway because it removes the whole query.
In comes using PHP instead, this function to achieve just that, removing a single param:
function strip_param($url,$param) {
if (isset($_GET[$param])) { // Check that param exists
$base_url = strtok($url,'?'); // Get the base url
$parsed_url = parse_url($url); // Parse it
$query = $parsed_url['query']; // Get the query string
parse_str($query,$params); // Convert parameters into array
unset($params[$param]); // Delete the one you want
$new_query = http_build_query($params); // Rebuild query string
$new_url = $base_url.'?'.$new_query; // Rebuild the url
$url = rtrim($new_url,'?'); // If now no query, remove ending ?
return header("Location: $url"); // Redirect
}
}
// How to use
//
// Place the above function anywhere you include above content of where you need it, such as a functions file.
// The below, put this somewhere at the top before content.
// Strip params from requests
strip_param($webpage_URI,'referer'); // The $webpage_URI value is: "https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
This function we can give props to this person for.
This is what lead me to just start cloning the way it was commented. It is such a tidy, readable way to do things. And even though my script before was short, it wasn't tidy, and it wasn't exactly sweet. But oh I'm so glad to get functions now. Well recommended!!
Thanks Mick for again the few tips for improving that bit more.
Had a bit of fun with 500 errors because I botched up when I decided to name the half wildcards $half_wildcards instead of $wildcards_half, I didn't update all of them lol. I've changed this now to $halfwilds, as I suddenly thought of it, which is the same amount of characters to $wildcards and $redirects so that's sweet consistency there. It differentiates it more too, which in turn just makes things less confusing.
Also this brought up the one problem that would arise if someone used this just say for $wildcards, or $redirects, or in my case, both, but not $halfwilds.. the associated array doesn't get created if no redirects for it exist, which would cause a 500. So I've created the empty arrays so they will always exist and not cause an error.
Updated the script a slight moving the array key variable assignations around so they don't spam the logs if you don't have them set to ignore warnings for undefined arrays and variables etc. I've got those logs now set to critical, because it is annoying but not here to be lazy with this script. Also edited the editkeys function name to redir_editkeys upon realising nested functions still darn well act as global defining them wise. grr php has it's quirks.

Related

PHP check in a function to redirect a non-trailing slash URL to trailing slash URL

i've a function that control paginated Wordpress content and redirect numbered URLs to its parent URL.
The function is working perfectly but i want that the redirect 301 for numbered URLs that don't have a final trailing slash, fires directly to the trailing slash URL. For example:
https://www.example.com/how-to-do-something/1111
should redirect immediately to
https://www.example.com/how-to-do-something/
At the moment, instead the redirect 301 is working but pass for https://www.example.com/how-to-do-something and then to https://www.example.com/how-to-do-something/.
But, at the same time, this check should not invalidate the numbered URLs with final trailing slash, that are already good, for example:
https://www.example.com/how-to-do-something/1111/ redirect perfectly to https://www.example.com/how-to-do-something/ in one shot. So there is to do nothing for those.
the function is the following:
global $posts, $numpages;
$request_uri = $_SERVER['REQUEST_URI'];
$result = preg_match('%\/(\d)+(\/)?$%', $request_uri, $matches);
$ordinal = $result ? intval($matches[1]) : FALSE;
if(is_numeric($ordinal)) {
// a numbered page was requested: validate it
// look-ahead: initialises the global $numpages
setup_postdata($posts[0]); // yes, hack
$redirect_to = isset($ordinal) ? '/': (($ordinal > $numpages) ? "/$numpages/" : FALSE);
if(is_string($redirect_to)) {
// we got us a phantom
$redirect_url = get_option('home') . preg_replace('%'.$matches[0].'%', $redirect_to, $request_uri);
// redirect to it's parent 301
header($_SERVER['SERVER_PROTOCOL'] . ' 301 Moved Permanently');
header("Location: $redirect_url");
exit();
}
}
How can i achieve this PHP check from non-trailing slash URL directly to trailing slash without invoke the htaccess rule that i have to force the trailing slash? Thanks for your patience and time.
Wordpress has a function that adds a trailing slash:
trailingslashit($string);
Looking again at your code there are some things that don't add up:
The line $redirect_to = isset($ordinal) ? '/': (($ordinal > $numpages) ? "/$numpages/" : FALSE); will always return '/' because $ordinal is always set within the if statement.
Does the 'home' option return a url with a trailing slash? to make sure you need the 'trailingslashit' function, i.e., trailingslashit(get_option('home'))
Overall I would approach this a bit differently. This is what I would do (fill free to change it to your needs):
$request_uri = $_SERVER['REQUEST_URI'];
$uriParts = explode('/', trim($request_uri, '/'));
$ordinal = array_pop($uriParts);
if (is_numeric($ordinal)) {
setup_postdata($posts[0]);
$redirect_url = trailingslashit(get_option('home')) . implode('/', $uriParts) . '/';
header($_SERVER['SERVER_PROTOCOL'] . ' 301 Moved Permanently');
header("Location: $redirect_url");
exit();
}
Hope this helps.

Redirection HTTP/1.1 301 Moved Permanently

I have the following files. The objective of this is to redirect to the correct news. For example:
localhost/tostadotv/esto-es-una-noticia-28.html
If I intentionally modify the url, for example:
localhost/tostadotv/esto-es-una-noticia-modificada-incorrecta-28.html
I should redirect myself to the correct news:
localhost/tostadotv/esto-es-una-noticia-28.html
However, it redirects me to this:
http://localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/localhost/tostadotv/esto-es-una-noticia-28.html
Where this error? Could you please help me thanks. Excuse my english I'm from Argentina I do not speak English
.htaccess
RewriteEngine On
RewriteRule ^.*-([0-9]+)\.html$ noticia.php?id_not=$1 [L]
noticia.php
<?php require_once("lib/connection.php"); ?>
<?php require_once("lib/functions.php"); ?>
<?php
fix_category_product_url();
?>
functions.php
function fix_category_product_url() {
$proper_url = get_proper_category_product_url(1);
if ( SITE_DOMAIN.$_SERVER['REQUEST_URI'] != $proper_url) {
header('HTTP/1.1 301 Moved Permanently');
header('Location: '.$proper_url);
exit();
}
}
function get_proper_category_product_url($id) {
$product_id = $_GET['id_not'];
$query = sprintf('SELECT titulo FROM noticias WHERE id_not = "%d"', mysqli_real_escape_string($GLOBALS['DB'], $product_id));
$restit = mysqli_query($GLOBALS['DB'], $query);
$noticia = mysqli_fetch_array($restit);
$proper_url = make_category_product_url($noticia['titulo'], $product_id, $id);
return $proper_url;
}
define('SITE_DOMAIN', 'localhost');
function _prepare_url_text($string) {
$NOT_acceptable_characters_regex = '#[^-a-zA-Z0-9_ ]#';
$string = iconv('UTF-8','ASCII//TRANSLIT',$string);
$string = preg_replace($NOT_acceptable_characters_regex, '', $string);
$string = trim($string);
$string = preg_replace('#[-_ ]+#', '-', $string);
return $string;
}
function make_category_product_url($product_name, $product_id, $ido) {
$clean_product_name = _prepare_url_text($product_name);
if ($ido == 0)
$url = strtolower($clean_product_name).'-'.$product_id.'.html';
else
$url = SITE_DOMAIN.'/tostadotv/'.strtolower($clean_product_name).'-'.$product_id.'.html';
return $url;
}
As said in the comments, the final solution for the asker was to add http:// to the defined SITE_DOMAIN constant.
Before
define('SITE_DOMAIN', 'localhost');
After
define('SITE_DOMAIN', 'http://localhost');
But there's more to it than just that. Let's focus on the following two functions:
function fix_category_product_url(){
$proper_url = get_proper_category_product_url(1);
if(SITE_DOMAIN.$_SERVER['REQUEST_URI'] != $proper_url){
header('HTTP/1.1 301 Moved Permanently');
header('Location: '.$proper_url);
exit();
}
}
function make_category_product_url($product_name, $product_id, $ido) {
$clean_product_name = _prepare_url_text($product_name);
if($ido == 0)
$url = strtolower($clean_product_name).'-'.$product_id.'.html';
else
$url = SITE_DOMAIN.'/tostadotv/'.strtolower($clean_product_name).'-'.$product_id.'.html';
return $url;
}
The idea here is that $proper_url actually ends up getting a value from make_category_product_url() because its result is returned by get_proper_category_product_url(). It makes sense because make_category_product_url() has more parameters and uses the other to get their values.
What's funny about this is that the else block of the second function doesn't always return a path, but rather a URL. The problem here is that such URL is given without a defined protocol, but starts with the domain name instead. This value is therefore mistaken as a path.
Now take a look at the first function: it ultimately redirects the user using header('Location: '.$proper_url);. As we discussed earlier, $proper_url is not always a path, so the protocol should be added somewhere in the code whenever a URL takes place instead of a path. That's where the actual solution comes in: adding http:// where SITE_DOMAIN is defined is one way to do this, because this constant is only used when a URL takes place. There are many other ways to do this, but this one is completely valid.

Change URL Extension from given URL [duplicate]

This question already has answers here:
How to get host name from this kind of URL?
(2 answers)
Closed 8 years ago.
Is there any way to accept a URL and change it's domain to .com ?
For example if a user were to submit www.example.in, I want to check if the URL is valid, and change that to www.example.com. I have built a regex checker that can check if the URL is valid, but I'm not entirely sure how to check if the given extension is valid, and then to change it to .com
EDIT : To be clear I am not actually going to these URL's. I am getting them submitted as user input in a form, and am simply storing them. These are functions I want to do to the URL before storing, that is all.
Edit 2 : An example to make this clearer -
$url = 'www.example.co.uk'
$newurl = function($url);
echo $newurl
which would yield the output
www.example.com
Are you looking for something like this on the server side to replace a list of selected TLDs to be translated to .coms?
<?php
$url = "www.example.in";
$replacement_tld = "com";
# array of all TLDs you wish to support
$valid_tlds = array("in","co.uk");
# possible TLD source lists
# http://data.iana.org/TLD/tlds-alpha-by-domain.txt
# https://wiki.mozilla.org/TLD_List
# from http://stackoverflow.com/a/10473026/723139
function endsWith($haystack, $needle)
{
$haystack = strtolower($haystack);
$needle = strtolower($needle);
return $needle === "" || substr($haystack, -strlen($needle)) === $needle;
}
foreach($valid_tlds as $tld){
if(endsWith($url, $tld))
{
echo substr($url, 0, -strlen($tld)) . $replacement_tld . "\n";
break;
}
}
?>
Create an empty text file using a text editor such as notepad, and save it as htaccess.txt.
301 (Permanent) Redirect: Point an entire site to a different URL on a permanent basis. This is the most common type of redirect and is useful in most situations. In this example, we are redirecting to the "mt-example.com" domain:
# This allows you to redirect your entire website to any other domain
Redirect 301 / http://mt-example.com/
302 (Temporary) Redirect: Point an entire site to a different temporary URL. This is useful for SEO purposes when you have a temporary landing page and plan to switch back to your main landing page at a later date:
# This allows you to redirect your entire website to any other domain
Redirect 302 / http://mt-example.com/
For more details : http://kb.mediatemple.net/questions/242/How+do+I+redirect+my+site+using+a+.htaccess+file%3F
The question is not entirely clear, I'm assuming you wish to make this logic on PHP part.
Here's useful function to parse such strings:
function parseUrl ( $url )
{
$r = "^(?:(?P<scheme>\w+)://)?";
$r .= "(?:(?P<login>\w+):(?P<pass>\w+)#)?";
$r .= "(?P<host>(?:(?P<subdomain>[\w\.\-]+)\.)?" . "(?P<domain>\w+\.(?P<extension>\w+)))";
$r .= "(?::(?P<port>\d+))?";
$r .= "(?P<path>[\w/]*/(?P<file>\w+(?:\.\w+)?)?)?";
$r .= "(?:\?(?P<arg>[\w=&]+))?";
$r .= "(?:#(?P<anchor>\w+))?";
$r = "!$r!";
preg_match( $r, $url, $out );
return $out;
}
You can parse URL, validate it, and then recreate from resulting array replacing anything you want.
If you want to practice regexp and create own patterns - this site will be best place to do it.
If your goal to route users from one url to another or change URI style, then you need to use mod rewrite.
Actually in this case you will end up configuring your web server, probably virtual host, because it will route only listed domains (those being parked at the server).
To validate a URL in PHP You can use filter_var() .
filter_var($url, FILTER_VALIDATE_URL))
and then to get Top Level Domain (TLD) and replace the it with .com , you can use following function :
$url="http://www.dslreports.in";
$ext="com";
function change_url($url,$ext)
{
if(filter_var($url, FILTER_VALIDATE_URL)) {
$tld = '';
$url_parts = parse_url( (string) $url );
if( is_array( $url_parts ) && isset( $url_parts[ 'host' ] ) )
{
$host_parts = explode( '.', $url_parts[ 'host' ] );
if( is_array( $host_parts ) && count( $host_parts ) > 0 )
{
$tld = array_pop( $host_parts );
}
}
$new_url= str_replace($tld,$ext,$url);
return $new_url;
}else{
return "Not a valid URl";
}
}
echo change_url($url,$ext);
Hope this helps!

How to improve the speed of a redirect to a subdomain depending on user language?

I'm optimizing our webpage, and we have notice something we want to dramatically improve. We use Symfony 1.3.
When the user loads example.com, the filters (rendering, security and remember) are executed. Then we execute our subdomain filter. If it's the first time the user is here, we get the preferred language of his browser and we redirect the webpage to en.example.com or es.example.com. If the user has a session, we get the language from its session; and we redirect to the subdomain. Then the en.example.com page loads again.
We lose around 1.5 seconds on that redirect. The en.example.com loads sometimes faster than that. How we can get rid of that delay? Changing the index.php and doing the browser-memcache-or-db queries directly without loading symfony?
thanks a lot!
You could use mod_rewrite like this
RewriteCond %{HTTP:Accept-Language} ^es [NC]
RewriteRule ^(.*)$ http://es\.example\.com/$1 [R=301,L]
for every supported language. No PHP involved, no session required, just fast.
I finally made the redirection on index.php. From 1 to 1.5 seconds to 40ms. Something like:
<?php
$host = $_SERVER['HTTP_HOST'];
$host_a = explode('.', $host);
// if subdomain is not in the supported langs
$langs = array('en', 'es');
if( !in_array($host_a[0], $langs) ){
// try to get the cookie and the user culture
$cookie = $_COOKIE['symfony'];
list($id, $signature) = explode(':', $cookie, 2);
if( $signature == sha1($id.':'.'secret_cookie') )
{
// get cookie data from memcache
$memcache_obj = memcache_connect('localhost', 11211);
// the cookie is built with two parts separated by :
// - md5 of the sfCache directory
// - $id from the user
$md5_dir = md5(dirname(dirname(dirname(__FILE__)).'/lib/vendor/symfony/lib/cache/sfCache.class.php'));
$session = memcache_get($memcache_obj, $md5_dir.':'.$id);
$user_lang = $session['symfony/user/sfUser/culture'];
if( !in_array($user_lang, $langs) ) $user_lang = 'en';
// if not, get browser lang
}else{
$user_lang = prefered_language($langs);
}
// build url
$url = 'http://'.$user_lang.'.'.str_replace('www.', '', $_SERVER['HTTP_HOST']).$_SERVER['REQUEST_URI'];
// and redirect
Header( "HTTP/1.1 301 Moved Permanently" );
Header( "Location: ".$url);
die();
}
require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);
sfContext::createInstance($configuration)->dispatch();

Extract part from URL for a query string

I need a certain part of a URL extracted.
Example:
http://www.domain.com/blog/entry-title/?standalone=1 is the given URL.
blog/entry-title should be extracted.
However, the extraction should also work with http://www.domain.com/index.php/blog/[…]as the given URL.
This code is for a Content Management System.
What I've already come up with is this:
function getPathUrl() {
$folder = explode('/', $_SERVER['SCRIPT_NAME']);
$script_filename = pathinfo($_SERVER['SCRIPT_NAME']); // supposed to be 'index.php'
$request = explode('/', $_SERVER['REQUEST_URI']);
// first element is always ""
array_shift($folder);
array_shift($request);
// now it's only the request url. filtered out containing folders and 'index.php'.
$final_request = array_diff($request, array_intersect($folder, $request));
// the indexes are mangled up in a strange way. turn 'em back
$final_request = array_values($final_request);
// remove empty elements in array (caused by superfluent slashes, for instance)
array_clean($final_request);
// make a string out of the array
$final_request = implode('/', $final_request);
if ($_SERVER['QUERY_STRING'] || substr($final_request, -1) == '?') {
$final_request = substr($final_request, 0, - strlen($_SERVER['QUERY_STRING']) - 1);
}
return $final_request;
}
However, this code does not take care of the arguments at the end of the URL (like ?standalone=1). It works for anchors (#read-more), though.
Thanks a ton guys and have fun twisting your brains. Maybe we can do this shit with a regular expression.
There's many examples and info for what you want at:
http://php.net/manual/en/function.parse-url.php
That should do what you need:
<?php
function getPath($url)
{
$path = parse_url($url,PHP_URL_PATH);
$lastSlash = strrpos($path,"/");
return substr($path,1,$lastSlash-1);
}
echo getPath("http://www.domain.com/blog/entry-title/?standalone=1");
?>

Categories