PHP switch statement with preg_match - php

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';
}

Related

PHP router with switch statement and regular expressions not working

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());

Switch/Case without a `break`, doesn't check the cases properly

I'm having trouble with a switch case conidtion.
Why in the following scenario:
$category = "A";
$offer = "none";
$discount = "none";
For the following code:
switch (TRUE) {
case ($category == 'A') : / #1
$msg = "hello";
case ($offer == 'special') : / #2
$id = "123";
case ($discount == '50D') : / #3
$id = "999";
break;
echo $id;
}
I get output of 999 for id, even though #2 and #3 are not fullfilled?
EDIT:
Cases such as $offer == 'special' are private cases of the general case which is $category == 'A'.
That's why I want the function to go in this order.
if switch/case is not appropriate, what shall I use?
Once switch finds a matching case, it just executes all the remaining code until it gets to a break statement. None of the following case expressions are tested, so you can't have dependencies like this. To implement sub-cases, you should use nested switch or if statements.
switch ($category) {
case 'A':
$msg = 'hello';
if ($offer == 'special') {
$id = '123';
} elseif ($discount == '50D') {
$id = '999';
}
break;
...
}
echo $id;
The fallthrough feature of case without break is most often used when you have two cases that should do exactly the same thing. So the first one has an empty code with no break, and it just falls through.
switch ($val) {
case 'AAA':
case 'bbb':
// some code
break;
...
}
It can also be used when two cases are similar, but one of them needs some extra code run first:
switch ($val) {
case 'xxx':
echo 'xxx is obsolete, please switch to yyy';
case 'yyy':
// more code
break;
...
}
This is how a switch-case statement works. It goes through all the casees once one condition was met, as long as there is no break. You can read up on it in the manual here: PHP switch.
If you want to stop the execution of the switch at the end of an case, just add a break; statement.

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.

switch case isset triggered when case is -not- set

--Let me add this. This code works for me the way it is. I just do not know why it works.--
I can't figure this out.
switch ($_SERVER['QUERY_STRING']) {
case isset($_GET['test0']):
echo "test0<br>";
break;
case isset($_GET['test1']):
echo "test1<br>";
break;
case isset($_GET['test2']):
echo "test2<br>";
break;
case isset($_GET['test3']):
echo "test3<br>";
break;
case isset($_GET['test4']):
echo "test4<br>";
break;
default:
echo "no test<br>";
break;
}
When the url is index.php?test0, "test0" is shown.
When the url is index.php?test4, "test4" is shown.
When the url is index.php?test999, "no test" is shown.
When the url is index.php?tes, "no test" is shown.
When the url is index.php?, or index.php, "test0" is shown.
Why is this? The condition is not met, so should the default not be shown?
switch can't be used this way. isset() returns true or false, not something (a string, an int, etc) you can match against. What you are basically doing is:
switch ($_SERVER['QUERY_STRING']) {
case true:
echo "test0<br>";
break;
case true:
echo "test1<br>";
break;
case false:
echo "test2<br>";
break;
case false:
echo "test3<br>";
break;
case true:
echo "test4<br>";
break;
default:
echo "no test<br>";
break;
}
cases are considered from top to bottom. In this case, $_SERVER["QUERY_STRING"] is automatically type-converted to bool (which will return true in this case). The first case it sees would be test0, so it echos that. If you do that for test0-4, it will give you the false illusion that this code is working as intended, while it's not considering the edge cases.
The only way you can achieve what you want is by using multiple ifs, or by redesigning your application.
When the url is index.php?, or index.php, "test0" is shown.
Why is this? The condition is not met, so should the default not be shown?
Like a good question, your question as well contains the answer already.
You already have realized that the condition must be met even you think it is not met. Therefore you ask. So let's see which condition is met:
case isset($_GET['test0']):
echo "test0<br>";
break;
This is a test for isset($_GET['test0']) and we know with the request that this is FALSE. So this test tests for FALSE.
Now let's see against what this tests:
switch ($_SERVER['QUERY_STRING']) {
That is $_SERVER['QUERY_STRING']. So if $_SERVER['QUERY_STRING'] is FALSE the test0 will be output.
Because switch { case:} in PHP does loose comparison, the empty string $_SERVER['QUERY_STRING'] is FALSE. This is why you see the output.
Easy if you know why, right? And all so logical.
And what you wanted to test against was not $_SERVER['QUERY_STRING'] but just TRUE:
switch (TRUE)
{
case isset($_GET['test0']) :
...
}
This gets the job done, too.
<?php
$q = $_SERVER['QUERY_STRING'];
if(!empty($q) && isset($q) && strlen($q) >0 ){
$url = $q;
switch ($url){
case true;
echo $url;
break;
}
}
else {
echo "no test<br>";
}
what about
$found = false;
for($i=0;$i <=4; $i++){
if( isset($_GET['test'.$i]) ){
echo "test".$i;
$found = true;
}
}
if(!$found){
echo "no test";
}

PHP Regex for Filtering Domain Names in _POST Username

I have some hardcoded if/else statements to set $page variable - (for later use in header: ".page") - to a given website based on the $_POST["username"] input.
CODE:
if ($_POST["username"] == "username1#domain1.com") {
$page = "http://www.google.com";
}
else if ($_POST["username"] == "username2#domain1.com"){
$page = "http://www.yahoo.com";
}
else if ($_POST["username"] == "username1#domain2.com"){
$page = "http://www.stackoverflow.com";
}
else if ($_POST["username"] == "username2#domain2.com"){
$page = "http://www.serverfault.com";
}
else if ($_POST["username"] == "username#domain3.com"){
$page = "http://www.superuser.com";
}
else if (!preg_match($domain2.com, $_POST["username"])) { //THIS IS VERY WRONG
$page = "http://www.backblaze.com";
}
else{
$page = "DefaultBackupPage.php";
}
I am trying to say if your username has "#domain.com" at the end of it, set the $page to, in this case backblaze.com, but could be anything.
I am aware it is messy, I don't actually like this implementation. It just has to fit in this schema and I need a quick fix!
The current error I am receiving is that the regular expression is empty. I am hoping this is a no brainer for someone who knows PHP - which I have been hastily trying to learn!
if (preg_match('/#domain2\.com$/i',$_POST['username']))
will catch username ending with domain2.com. Please note the escaping backward slash for the dot. If you want to test against the opposite (aka. does NOT end with domain2.com) then use an exclamation mark before the preg_match() function.
Is this what you were asking?
EDIT 1: I added the i flag to the pattern to make it look for a case-insensitive match.
EDIT 2: For the sake of readability and control I would wrap this in a function instead, but that's my own preference so it's not the suggested approach or anything. In case your code is long and complicated…
function get_page($username) {
$username = strtolower($username);
switch ($username) {
case "username1#domain1.com" : return "http://www.google.com";
case "username2#domain1.com" : return "http://www.yahoo.com";
case "username1#domain2.com" : return "http://www.stackoverflow.com";
case "username2#domain2.com" : return "http://www.serverfault.com";
case "username#domain3.com" : return "http://www.superuser.com";
}
return preg_match('/#domain2\.com$/',$username) ?
"http://www.backblaze.com" : "DefaultBackupPage.php";
}
$page = get_page($_POST['username']);
The line:
else if(!preg_match($domain2.com, $_POST["username"]))
has to be:
else if(!preg_match("/domain2\.com/", $_POST["username"]))

Categories