I currently have this setup
<?php
$nav = filter_input(INPUT_GET, 'nav', FILTER_SANITIZE_STRING);
$Working_On = true;
//$Working_On = false;
if ($Working_On == true) {
$title = '- Under Construction';
$error_msg = "Website Under Construction.";
include('404.php');
exit;
} else {
switch ($nav) {
case "":
include('main.php');
break;
default:
$title = '- 404 Page Not Found';
include('404.php');
break;
}
}
I'd love to know if there is a better way more efficent way of orginising this type of setup so i can easily add more options ?
While accessing a page with
example.com/?nav=examplepage
is a valid approach (that I also used in past projects), today's standard is to use something like
example.com/examplepage
This creates URLs that are easier to understand by humans and more semantic than query parameters. Doing so will also prevent you from running into possible caching issues with URLs defined by query parameters.
It also brings you the benefit of including even more information in the URL without the need for query parameters, for example
example.com/posts/view/20
Such URLs are usually parsed by a "Router". There are many reference and open source implementations for PHP routing on the web, just search for "PHP Router", take a look and decide what best fits your needs.
Side note:
use type-safe checks, aka === instead of == to save you a lot of headache and follow security best practices; if you're still at the beginning of your project, you might want to take a look at https://phptherightway.com/
you exit() if the site is on maintenance. In that case you don't need to put the switch statement inside else{}, you can just put it after the if clause
Related
I'm designing a semi-basic tool in PHP, I have more experience server side, and not in PHP.
My tool contains 10-15 pages, and I am doing the navigation between them with the $_GET parameter.
In my code I have many if statements that look like:
if(isset[param1] && !isset[param2] && .....&& !isset[paramN]){
// code
}
You will agree with me, it's ugly, right?
Is it "how we do it" in PHP? or is there some kind of design pattern / functions for navigation in a PHP website?
Edit: To be clearer, what I want to know is: Is the proper way to design the navigation is with plenty of $_GET variables?
To avoid a huge if else statement try a switch statement:
$param1 = 'something';
switch (true){
case isset($param1):
echo "PARAM 1 is set";
break;
case isset($param2):
echo "PARAM 2 is set";
break;
default:
echo "None set";
}
And to answer your edit - Yes, you can use _GET variables for navigation and it is normal.
Actually the solution to this problem is a little bit complicated, than it might seem at first glance. And you need to implement a handler for that, since you're looking a cleanest way of handling GET parameters and invoking a code fragment.
In your case, your final API, should look like this (since you were asking for the design, not the for implementation):
$nav = new Nav($_GET);
$nav->whenSet('param1', 'param2')->andNotSet('param3')->then(function(){
});
$nav->whenSet('param3')->andNotSet('param1', 'param2')->then(function(){
});
// Or a simpler and shorter way
$nav->register($existingKeys, $nonExistingKeys, function(){
});
Another option you have is to use a routing mechanism. Most PHP frameworks provide this functionality. They all support optional route fragments, so you can handle parameters only defining one route path, like this /page/? (? - means optional).
This is my suggestion add conditions in an array and than use in_array()
echo in_array($yourVar, array('abc', 'def', 'hij', 'klm', 'nop'))
? True
: false;
UPDATE:
$get = "abc";
$navigation = array(
"abc"=>"yourneed",
"def"=>"yourneed2",
"ghi"=>"yourneed3",
);
foreach($navigation as $key => $value){
if($key == $get){
echo $value;
}
}
One more solution you can define routes also.
Same as CI and YII.
(I'm not using switch but you should definitely try that, and try using break between cases)
I'm using this at the moment, it seems to avoid mass-IFfing:
if (!$_GET) {
homePageFunction();
}elseif($_GET) {
contentPagesFunction($_GET['p']);
}
I have a menuer() function that outputs rel paths to content 'things' in links like:
<a href='?p=$relPath'>$name</a>
Although I could probably use different notation to tidy it further... Untested but like this:
if (!$_GET) homePageFunction();
elseif ($_GET) contentPagesFunction($_GET['p']);
I have pages in my website with URLs like this:
http://example.com/index.php?page=about
http://example.com/index.php?page=portofolio
http://example.com/index.php?page=location
http://example.com/index.php?page=mission
http://example.com/index.php?page=contact
http://example.com/index.php?page=register
http://example.com/index.php?page=login
Now, the objective is to include be it a simple about.html page containing a simple text about the company, or a form processor file called register.php when ?page=register is called.
This is how I am handling the contents.
$id= isset($_GET && !empty($_GET['page'])) ? htmlspecialchars($_GET['page']) : false;
switch($id){
case 'about':
include 'about.html';
break;
case 'porofolio':
include 'portofolio.html';
break;
case 'contact':
include 'contact_form.html';
include 'contact_process.php';
break;
case 'login':
if(isset($_SESSION['user_login'])){
echo 'You are already logged in';
}else{
include 'login_form.html';
include 'login_process.php';
}
default:
die('Page not found');
break;
}
Yes, I know It is horrible. I know trust me. That is why, I need better maintainable solution to this.
Now, if you are just curious what could be inside process_login.php It looks like:
if($_POST){
if(!empty($_POST['username']) && !empty($_POST['password'])){
try{
$pdo = new PDO(...);
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ? AND password = ?");
if($stmt->rowCount(){
$_SESSION['user_login'] = $_POST['username'];
header('location: success.php'); exit;
}
}
}
}
This is just an example I quickly wrote, so there may be some typo or another thing I've left. What I am interested to know is how to better create/emulate a practice of handling the switch statements, as it does not seem to me a good oop-oriented approach for such task. I want something maintainable, extensible approach.
If you are wandering why I have only have one page index.php, it is because I don't want to create static pages for all the pages, as they have the same layout, making the HTML improvement easier. Instead of one day, opening 7 files to change/add a single tag, it would make sense to do it only once.
second, if you wandering why I chose to include the about.html page instead of simple storing the plain text in database, is because I didn't want to create a field/table/connection just for that 10 line simple text, as I would be saving some overhead performance. (Although I am sure, you may not agree on this)
So, the general question is, how to get rid of that complexity with switch statement, which makes me end up creating files like login_process.php instead of dealing with it, in some OOP way that I am not aware of.
Sorry for the wall of text :( and thanks in advance.
The solution that you're looking for is really MVC and templating your views. Further, you're essentially creating your own router, your router is pretty simple compared to some of the complex routing schemes found in some of the major frameworks, so if you're looking to simplify further, that's going to be kinda difficult.
If you want to see how this is all done in a very OOP manner, checkout something like the Zend Framework 2, CakePHP, etc.
The code below ensures that when a user accesses control panel, they are ran through a quick verification process to validate what their entities are. For instance, if a user is level 1 they are only given access to video feed which means nothing else is available to them.
When I look at the code though, I can see video feed being called when case 1 and 3 are called. I would possibly enjoy an alternative to make the code more efficient.
I was told a possible array could make things a little easier but then again this is faster.
switch ($_SESSION['permission']) {
case 1: // Level 1: Video Feed
include ("include/panels/videofeed.index.php");
break;
case 2: // Level 2: Announcements / Courses / Teachers
include ("include/panels/announcements.index.php");
include ("include/panels/courses.index.php");
include ("include/panels/teachers.index.php");
break;
case 3: // Level 3: Announcements / Video Feed / Courses / Teachers / Accounts / Logs
include ("include/panels/announcements.index.php");
include ("include/panels/videofeed.index.php");
include ("include/panels/courses.index.php");
include ("include/panels/teachers.index.php");
include ("include/panels/accounts.index.php");
include ("include/panels/log.index.php");
break;
case 4: // Level 4: Teachers
include ("include/panels/teachers.index.php");
}
It's fine the way it is. I think you don't mean "efficiency" when you refer to the "repeated" includes. You mean you could compact your code by using the switch fall-through.
While this might make your code smaller, it has no significant impact of efficiency (the time the script takes to run) and it will actually make the code harder to read. Leave it be.
Frist you may run better if you use require_once if its possible.
The second point is to shorten the url it seems that its every include the same.
Maybe try to use it in a function for example:
$permission_id = $_SESSION['permission']
function requireModule($moduleName, $path = 'include/panels/') {
$moduleName .= '.index.php';
require_once($path . $moduleName);
}
// if you want you can add an arry for it:
$permissionModules = array(
array('videofeed'),
array('announcements', 'courses', 'teachers'),
array('announcements', 'courses', 'teachers', 'accounts', 'log', 'videofeed'),
array('teachers')
);
// now we can put it in a more effectiv way
if(array_key_exists($permission_id, $permissionModules)) {
foreach($permissionModules[$permission_id] as $includes) {
requireModule($includes);
}
}
Wrong ? Correct me!
So basically i am struggling with the need of optimizing my code for some project of mine.
Currently i have pages like add_company.php, view_company.php, edit_company.php.
What i would like to do is to serve all content from just 1 PHP file company.php.
So to speak company.php?action=view, company.php?action=edit etc. Is the only way to do this with massive if else statements? That would make my company.php like mega huge.
Or maybe even better how could I serve all my pages just using index.php ?
So what would be the best way to accomplish this? I am not php guru and i don't have much experience with MVC or any other pattern.
Thanks.
You could make company.php
<?php
$allowed = array('add', 'view', 'edit');
if ( ! isset($_GET['action'])) {
die('missing param');
}
$action = $_GET['action'];
if ( ! in_array($action, $allowed)) {
die('invalid');
}
require $action . '_' . __FILE__;
Quick and dirty, but should work :) You could place this code in any file, and it will work straight away.
With a bit of modification, you could make this your front controller with index.php.
I always prefer using a switch statement for the $_GET variable. Its my personal preference to put all the logic pertaining to one entity (in this case company) in a single PHP file because I generally deal with tons of entities. If you want a MVC model, this might not be what you are looking for. Just my 2 cents.
// Common page header
// Other stuff common in the pages
$page_to_load = $_GET[view];
switch($page_to_load) {
case 'view':
//Logic to view or HTML for view
break;
case 'add':
//Logic to add or HTML for add
break;
}
// Common footer etc..
This is pretty simple stuff. It should look something like this:
//index.php
if (!isset($_GET['page']))
{
require('index_contents.php');
}
else if ($_GET['page'] == 'company')
{
if (!isset($_GET['action']))
{
require('company_contents.php');
}
else if ($_GET['action'] == 'edit')
{
require('edit_company.php');
}
else if ($_GET['action'] == 'add')
{
require('add_company.php');
}
else
{
require('company_contents.php');
}
}
else
{
require('index_contents.php');
}
A very easy solution would be to just use includes.
if ($_GET["action"] == "view") {
require("action_view.php");
} else if ...
(of course, you might want to use a switch statement, if you have lots of different page types)
Then action_view.php contains just the code for the specific page.
Better, but also more complicated solutions, would be an object oriented approach (abstract Page class with Factory Pattern) or forthright a good framework and template engine.
You could use POST instead of GET with index.php:
<?php
require($_POST['action'] . ".php");
?>
This would hide the action type from the user, acting as though it is a single page. However, it may require using a form in your navigation as opposed to linking direct to "company.php?action=edit".
I want url's like index.php?showuser=512, index.php?shownews=317 for pages i get content from db... and for regular pages index.php?page=about and so on WITHOUT mod-rewrite.
Invision Power Board has urls like this. I have looked through their code but I can't figure out how they do it.
I could do it like this:
if (ctype_digit($_GET['shownews'])) include('shownews.php');
elseif (ctype_digit($_GET['showuser'])) include('showuser.php');
// regular pages
elseif ($_GET['page'] == 'about') include('about.php');
elseif ($_GET['page'] == 'help') include('help.php');
elseif ($_GET['page'] == 'login') include('login.php');
But this feels too messy.
Just curious how IPB does this. Is there a better way do to this? WITHOUT any mod-rewrite. Any one know? I doubt they do it like the above.
I can't do:
if (preg_match('/^[a-z0-9]+$/', $_GET['page'])) include('$_GET['page']');
Then I would get links like index.php?showuser&id=512 and that I dont like. (i know its not safe just showing the princip)
I like it this way, it's not the best but i like it so please be quiet about template engines, frameworks etc. Just be kind and answer my question... I just want to know how IPB does this.
Thanks
Tomek
I don't know how IPB does this, let's get that out of the way. But, this is how I would approach this problem:
First, I recognize that there are two kinds of GET parameters: page/identifier, and just page. These would get tested separately.
Second, I recognize that all all get parameters match their filenames sans the php-suffix, so we can use this to our advantage.
One of the most important things to remember is to never let GET-parameters affect our code unsanitized. In this case, we know which types of pages we can and want to show, so we can create a white-list out of these.
So, onto the pseudo-y dispatcher code:
$pagesWithId = array("shownews", "showuser", "showwhatever");
$justPages = array("about", "help", "login");
foreach ($pagesWithId as $page) {
if (isset($_GET[$page])) {
$id = (int)$_GET[$page];
include($page.'.php');
die();
}
}
if (in_array($_GET['page'], $justPages)) {
include($_GET['page'].'.php');
die();
}
// page not found
show404OrHandleOtherwise();
For pages you just use a simple array.
if (isset($pages[$_GET['page']])) include $pages[$_GET['page']];
For shownews=317 You could make a simple conversion in your app. Depending on how you want to prioritize page or shownews etc:
if (isset($pages[$_GET['page']])) {
include $pages[$_GET['page']];
} else {
$possiblePages = array_filter(array_intersect_key($_GET, $pagesWithId), 'ctype_digit');
if (!empty($possiblePages)) {
$id = reset($possiblePages);
$pageName = key($possiblePages);
$page = $pagesWithId[$pageName];
include $page;
} else {
//no valid pages
}
}
Note: page "names" are array keys, and the value is the path, file and extension to include. More customizable.