PHP router with switch statement and regular expressions not working - php

I have this code in php:
$route = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
switch ($route) {
case '':
echo "root";
break;
case '/':
echo "root";
break;
case (preg_match("/^[a-zA-Z0-9-_]+$/", $route) ? true : false):
echo "db";
break;
default:
header("HTTP/1.0 404 Not Found");
http_response_code(404);
print(http_response_code());
break;
}
The regexp has to match all routes containing alphanumeric characters and - and _. But it doesn't, instead all slips to the default option, 404.
Probably it is something wrong with the pregmatch inside the switch.
Please help.

There are few issues:
REQUEST_URI will return string with leading /, so you must add it in your regex: /^\/[a-zA-Z0-9-_]+$/
switch checks if case value matches with provided value, so you should compare it with true instead of $route:
switch (true) {
case $route == '':
...
case $route == '/':
...
case preg_match("/^\/[a-zA-Z0-9-_]+$/", $route):
...
}
But in that case it's better to use simple if conditions:
if (empty($route) || $route == '/') {
echo 'root';
return; // Use early return in order not to jump to other cases
}
if (preg_match("/^\/[a-zA-Z0-9-_]+$/", $route)) {
echo 'db';
return;
}
header("HTTP/1.0 404 Not Found");
http_response_code(404);
print(http_response_code());

Related

PHP switch statement with preg_match

I have some problem to create a preg_match() inside my switch statement.
I want to write preg_match that match /oop/page/view/[some-number].
For now its working like:
If I run in my browser http://example.com/oop/page/view/1 its shows '404 page'.
And when I run some address for example http://example.com/oop/page/view/test or even /oop/test its run 2nd case and dont know yet how. For sure something is wrong in my regex expresion..
public function check(){
$url = filter_input(INPUT_GET, 'url');
switch ($url) {
case '':
echo 'HomePage';
break;
case preg_match('#^/oop/page/view/\d+$#', $url):
echo $url;
break;
default:
echo '404 page';
break;
}
}
What you should do instead is something like this:
switch (true) {
case preg_match(...):
I don't remember if switch in PHP is strict or loose comparison, but if it's strict, just put a !! in front of each case to convert it to boolean.
A switch statement compares each case expression to the original expression in the switch(). So
case preg_match('#^/oop/page/view/\d+$#', $url):
is analogous to:
if ($url == preg_match('#^/oop/page/view/\d+$#', $url))
This is clearly not what you want. If you want to test different kinds of expressions, don't use switch(), use if/elseif/else:
if ($url == '') {
echo 'Homepage';
} elseif (preg_match('#^/oop/page/view/\d+$#', $url)) {
echo $url;
} else {
echo '404 page';
}

Sanitise host name into required format

I am working on a script that calls a third party api, which needs a valid server hostname. Any of these format should be allowed, for example:
server.domain.com
server1.domain.com
something.domain.com
123456x.domain.tld
etc...
So, I have put together the following script to sanitize the server's hostname (in the event user inputs an invalid entry):
$server_hostname = 'test';
if (IsValidHostname($server_hostname))
{
switch (substr_count($server_hostname, '.'))
{
case 1:
$server_hostname = 'server.'. $server_hostname;
break;
case 0:
$server_hostname = 'server'. time() .'.default-domain.com';
break;
}
}
else
{
$server_hostname = 'server'. time() .'.default-domain.com';
}
var_dump($server_hostname);
function IsValidHostname($hostname)
{
// Src: http://stackoverflow.com/a/4694816
return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $hostname)
&& preg_match("/^.{1,253}$/", $hostname)
&& preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $hostname)
);
}
The script appears to work. If an invalid hostname is supplied, it auto-generates a random one. Here are few test cases:
test -> server1451385708.default-domain.com
test.com -> server.test.com
123-test.com -> server.123-test.com
adam.test.com -> adam.test.com
e-v-e.test.com -> e-v-e.test.com
server12.test.co.uk -> server12.test.co.uk
However, I am not sure this is quite perfect yet. Here's a test that failed:
test.co.uk -> test.co.uk
I would prefer the outcome to be the following, when tld has 2 parts (e.g. co.uk):
test.co.uk -> server.test.co.uk
Any ideas on how I can achieve this?
Like this, this just covers the case that there are two ".":
if (IsValidHostname($server_hostname))
{
switch (substr_count($server_hostname, '.'))
{
case 1:
case 2:
$server_hostname = 'server.'. $server_hostname;
break;
case 0:
$server_hostname = 'server'. time() .'.default-domain.com';
break;
}
}
else
{
$server_hostname = 'server'. time() .'.default-domain.com';
}
or like this, which adds the info regardless of the number of ".":
if (IsValidHostname($server_hostname))
{
switch (substr_count($server_hostname, '.'))
{
case 0:
$server_hostname = 'server'. time() .'.default-domain.com';
break;
default:
$server_hostname = 'server.'. $server_hostname;
break;
}
}
else
{
$server_hostname = 'server'. time() .'.default-domain.com';
}
This is what I have settled on for now, let me know if there is a better way of doing it:
// Sanitize server's hostname
$serverHostname = strtolower(trim($server['hostname']));
if ($this->IsValidHostname($serverHostname)) {
switch (substr_count($serverHostname, '.')) {
case 1:
case 2:
if (substr($serverHostname, 0, 7) !== 'server.')
$serverHostname = uniqid() .'.'. $serverHostname;
break;
case 0:
$serverHostname = uniqid() .'.default-domain.com';
break;
}
} else {
$serverHostname = uniqid() .'.default-domain.com';
}

issues with friendly urls using htaccess and php

i have a problem, but may be easier if i put my code. I have this:
<?php
$view = "";
if(isset($_REQUEST["view"]) != "" && $_REQUEST["view"]) {
$view = $_REQUEST["view"];
} else {
$view = "";
}
#View Handler
$factory = new \API\Factory();
$factory->Template('header');
switch($view) {
#Home
case '':
$factory->View("home/index");
break;
case 'about-us':
$factory->View("home/about-us");
break;
case 'contact':
$factory->View("home/contact");
break;
#Products
case 'products':
$factory->View("products/index");
break;
case 'details':
$factory->View("products/details");
break;
#How To
case 'how-to':
$factory->View("how-to/index");
break;
#Tech Documents
case 'tech-docs':
$factory->View("tech-docs/index");
break;
#Virtual Home
case 'virtual-home':
$factory->View("virtual-home/index");
break;
#Shopping Cart
case 'shopping-cart':
$factory->View("cart/shopping-cart");
break;
case 'checkout':
$factory->View("cart/checkout");
break;
#Client
case 'client-dashboard':
$factory->View("client/client-dashboard");
break;
case 'client-profile':
$factory->View("client/client-profile");
break;
case 'logout':
ClientLogin::doLogout();
header("Location: index.php");
break;
}
$factory->Template('footer');
DonĀ“t ask why, lol. So my problem is this, when user navigates through website he see url's like this: www.mysite.com?view=products, but i want that they can see something like this: www.mysite.com/products/ but i want to manage it using .htaccess how can i implement that .htaccess to rewrite those ugly url's into nice url's. And asuming that all view requests are handled through that index.php
Also want to see it in my localhost, http://localhost:8080/index.php?view=products to http://localhost:8080/products/
Thanks in advance.
you can do this (replaced $_REQUEST["view"] by $_SERVER['PATH_INFO']):
if(isset($_SERVER['PATH_INFO']) != "" && $_SERVER['PATH_INFO']) {
$view = $_SERVER['PATH_INFO'];
} else {
$view = "";
}

Convert elses into switch with boolean return value

I am trying to convert this simple set of else statement into a more easily readable switch;
$parts_arr = explode('.', $_SERVER['SERVER_NAME']);
if (in_array('dev', $parts_arr)) {
DEFINE('APP_ENV', "dev");
} else if (in_array('staging', $parts_arr)) {
DEFINE('APP_ENV', "staging");
} else if (in_array('local', $parts_arr)) {
DEFINE('APP_ENV', "local");
} else {
DEFINE('APP_ENV', "live");
}
However I have completely drawn a blank. I can't use a foreach loop and use the string as the case as APP_ENV cannot be redefined.
You may only check the whole server name.
switch($_SERVER['SERVER_NAME']) {
case 'mysite.com':
case 'www.mysite.com':
DEFINE('APP_ENV', "live");
break;
case 'dev.mysite.com':
DEFINE('APP_ENV', "dev");
break;
case 'staging.mysite.com':
DEFINE('APP_ENV', "staging");
break;
case 'mylocalhost.local':
DEFINE('APP_ENV', "local");
break;
default:
exit;
}
You can't turn it into switch-case structure unless you're doing string comparaison.
Maybe something like this :
$str = array_pop(explode('.', $_SERVER['SERVER_NAME']));
switch($str)
{
case 'dev' :
DEFINE('APP_ENV', "dev");
break;
// en so on
}
Sunil Pachlangia's solution won't work because he is comparing an array and a string
<?php
$parts_arr = explode('.', $_SERVER['SERVER_NAME']);
switch (true) {
case in_array('dev', $parts_arr):
DEFINE('APP_ENV', "dev");
break;
case in_array('staging', $parts_arr):
DEFINE('APP_ENV', "staging");
break;
case in_array('local', $parts_arr):
DEFINE('APP_ENV', "local");
break;
default:
DEFINE('APP_ENV', "live");
break;
}
I think I'd tend to shy away from splitting strings and analysing array elements, as #Almo-Do commented Be specific in some kind of config file.
$environments = array(
'localhost' = > 'local'
, 'staging.mysite.com' > 'staging'
// etc - see? now you can comment some out
//, 'mysite.com' => 'live'
);
Then simply
define ('APP_ENV', $environments[$_SERVER['SERVER_NAME']]);
Or even be a bit more defensive prior to that, something like :
if (!array_key_exists($environments[$_SERVER['SERVER_NAME'])) die('suitable msg');

Switch statement using strstr always validates as true?

I have the following switch statement.
The URL contains a referral ID e.g twitter, facebook or an email e.g mail#mail.com. This is stored as $ref
I have the following switch statement:
switch ($ref) {
case "twitter":
echo "twitter";
break;
case "facebook":
echo "facbeook";
break;
case "blog":
echo "blog";
break;
case strstr($ref,'#'):
echo "email = ".$ref;
default:
echo "no referral found";
break;
}
However if URL is passed with nothing (e.g just www.mything.co.uk) then I wish to go to the default case.
Instead, I get the following output:
email = no referral found
Why does the default also include the text I set for case strstr($ref,'#') ?
OP question: "Why does the default also include the text I set for case strstr($ref,'#') ?"
Answer: there's no break; following the output, and thus falls through to the default case.
UPDATE: Addressing the issue of putting a statement within a case, I'm also including an easy work-around:
switch ($ref) {
case "twitter":
echo "twitter";
break;
case "facebook":
echo "facbeook";
break;
case "blog":
echo "blog";
break;
default:
if (strstr($ref,'#')) {
echo "email = ".$ref;
} else {
echo "no referral found";
}
break;
}
When $ref is an empty String, then strstr($ref,'#'); returns an empty string too, this is why the case strstr($ref,'#'): matches the switch input $ref.
The problem is, you can't even use a email validation function like
filter_var($ref, FILTER_VALIDATE_EMAIL)
That would return false in case of an empty input instead of an empty string, but switch does loose comparison, meaning that an "" == false would return true:
http://php.net/manual/en/types.comparisons.php#types.comparisions-loose
Thus the only solution I see is to use an if statement using the === operator:
if($ref == 'twitter') {
echo "twitter";
} else if($ref == 'facebook') {
echo "facbeook";
} else if($ref == 'blog') {
echo "blog";
} else if($ref === filter_var($ref, FILTER_VALIDATE_EMAIL)) {
echo "email = ".$ref;
} else {
echo "no referral found";
}
That's because your test is performed like if ($ref == strstr($ref, '#')), where strstr returns false which equals an empty string. You cannot really use dynamic comparisons in switch statements. Use if..else if you need that. Alternatively, abuse switch a bit:
switch (true) {
case $ref == 'twitter':
..
case strstr($ref, '#'):
..
}
That will work:
case (strstr($ref, '#') ? true : false):
But it's not really good of practice.

Categories