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']);
Related
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
Hi everybody and tks in advance for your help!
I have a multilengual site made in a similar way to this example. Everything is working just fine, but now I want to make that the URL change according to the languague selected. For example, if my page is called perfil.php when I select english languague, should be profile.php, and all the links in the web should translate to english too. I was surfing another questions but the majority offers a solution through htaccess. This should work, but I need to store that configuration (or translations) into my database so the user can change it when they want to.
Any ideas?
Thank you again!
You can use constants in some files called language files and then require/include them by language selected:
english.php
const PROFILE = "profile";
spanish.php
const PROFILE = "perfil";
main file:
require $language_selected . ".php";
echo '<a href="' . PROFILE . '.php">';
This part should be a comment (but its a bit long)
The method described in the link you provided is a reasonable way to implement the choice of language, but a poor way to detect the choice.
Your browser already tells servers what language(s) it thinks they should respond in. And most webservers have a mechanism for multiplexing different language content. However the latter means hard-wiring the choice of the browser without providing an easy means for overriding the behaviour.
The approach I have used before is something like this:
$use_lang='en-GB';
if (isset($_COOKIE['userlang'])
&& is_language_supported($_COOKIE['userlang'])) {
$use_lang=$_COOKIE['userlang'];
} else if ($proposedlang=supported_lang_in($_SERVER['Accept-Language'])) {
$use_lang=$proposedlang;
}
function supported_lang_in($str)
{
$l=array();
$opts=explode(',', $str);
foreach ($opts as $v) {
list($p, $weight)=explode(';', $v);
if ($weight) {
list($dummy, $weight)=explode('=', $weight);
$weight=float($weight);
}
if (!$weight) {
$weight=1.0;
}
if (isset($l[$weight])) {
$weight-=0.001;
}
$l[$weight]=$p;
}
krsort($p); // preferred first
foreach ($p as $proposed) {
if ('*'==$proposed) {
return false;
}
if (is_language_supported($proposed)) {
return $lang;
}
}
return false;
}
Now on to the problem you asked about....
Maintaining different URLs to reference the same content then dereference the language within the content seems a very byzantine solution to the problem. Not only do you have to map the input to the URL but you need to rewrite any URLs in the output to the appropriate representation.
While having semantically meaningful URLs is a definite bonus, going to great length to tailor these dynamically is not perhaps not the best use of your time.
i have alot of get values that define the page the user gonna see , for example for "profile" i will show the profile page and so on..
to find out what page to display i tried to do something like that:
switch ($_GET) {
case 'profile':
require_once('function/profile.php');
break;
case 'team':
require_once('function/team.php');
break;
but it shows no result..
i send the GET request like that : index.php?profile , for example..
what is the problem here and how can i can manage to do something similar that work as well. thank you in advance!
To make your example work, you could replace $_GET with key($_GET)
be aware though that key() will return the first key of an array, so if you change your URL's variable order, this line'll stop functioning.
$_GET is an array from the key value pairs found in the query string (part of the url after the script name and a question mark).
For example, the query string test=1&foo=bar will translate to:
Array(
test => 1
foo => 'bar'
)
In the OP example, index.php?profile, you will end up with a $_GET array like:
Array(
profile => null
)
Problem with doing urls like this is that it is non-standard. When you do things in a non-standard way, you have to come up with non-standard solutions to fix the problems.
Here are a few options along with issues that each has:
You can use $_SERVER['QUERY_STRING'] which will get you everything after the ? in the url. This is fine if the only thing passed in the url is just profile (or some other single value). In that case, $_SERVER['QUERY_STRING'] will have nothing but profile in it. But you also then lose the ability to pass additional parameters in the get string.
You can go with the method described by #stewe. The php key function will return the key from the current position in the array passed in. If you haven't done any looping, the current position is the first element. This will work fine with multiple get parameters as well. Your query string will just look like index.php?profile&test=1&foo=bar. The problem is that profile (or whatever page) has to be the first or else key will return whatever the key is for the first parameter passed.
Another option is to just go with the standard method of using a key and value. Regardless of page, you use the same key and just the value changes. You then have urls that look like index.php?page=profile and you can always access the page using $_GET['page'].
You can use mod_rewrite. It is simple to setup, most hosts support it (or some other similar) and there are millions of tutorials and examples on how to get it to work. You end up with the cleanest urls and it works with query string parameters. For example, /profile/ can be rewritten to point to /index.php?page=profile. The user sees /profile/ and php sees the standard. This allows you to use $_GET['page'] to get the requested page and not have to do extra parsing to get other values inside php.
$_GET is an array or variables that are populated based on the URL's query string. You need to do something like:
switch ($_GET['myVar']) { ... }
where your URL would look like:
http://www.domain.com/index.php?myVar=value
For more information, see the PHP Manual for $_GET.
$_GET by itself is not very useful to you. I suppose you are looking for a key, like 'page', right ? Remember to declare a default value as well.
so..
$page = $_GET['page'];
switch ($page) {
case 'profile':
require_once('function/profile.php');
break;
case 'team':
require_once('function/team.php');
break;
default:
require_once('function/page-not-found.php');
}
$_GET is a super global variable, where the data are sent as stored as array. So you have to
access it using Index
Assuming you page you are trying to include a page when the data are sent like this:
domain.com?page=product
Then you have to use switch like this
switch($_GET['page']) {
....
}
Note: May be I dont have to remind you how vulnerable this code towards injection.
It's an array, so you need to use a loop to iterate on the values:
foreach($_GET as $key => $val){
switch ($key) {
case 'profile':
require_once('function/profile.php');
break;
case 'team':
require_once('function/team.php');
break;
}
}
with foreach you can get the key and value pair
foreach ($_GET as $switchkey => $switchval) {
switch ($switchkey) {
case 'profile':
require_once('function/profile.php');
break;
case 'team':
require_once('function/team.php');
break;
}
}
i actually use the following after a normal get resulting in lfi/rfi vulnerabilities. the following solution was submitted to me via a bug bounty program and works great. notice the inclusion of the default attribute. very important. the following should be the only acceptable answer. Credit to the flying spaghetti monster(His Noodly Appendage)
switch($_GET['page']) {
case 'foo':
include('pages/foo.php');
break;
case 'bar':
include('pages/bar.php');
break;
default:
include('pages/home.php');
}
As mentioned before, the first thing that seems to come to mind is the non standard way of passing the information. which will generate some difficulties when you parse the values. Although, for me, the main problem is not checking/sanitazing/cleaning the data on $_GET. May be it's too obvious and since almost all the answers have been given by people who seem to know what are they doing, I'll assume they just didn't mention it because of that
But remember that if you don't check it, you are vulnerable to attacks and malfunction of your script. The extent of the damage depends on your own application, so it's not easy to predict.
In any case, this is what I'll do, including the html
<?php
// initialize variables
$variable_1 = false; // assume this is the page you want to load
$variable_2 = false;
$default = 'index.php'; // the idea is to load something controlled by you. index, error, 404, etc.
// process $_GET, check, clean and assign values
if ( isset( $_GET ) !== false ) {
foreach ( $_GET as $keys => $values ) {
// check both, $keys and $values for; character set, length, validity against a white list, content
// using an if to match the $keys garantees that regardless of the order, you will get what you want
if ( $keys === 'field_1' ) {
// do what you have to do with this, for instance ...
$variable_1 = $values;
}
if ( $keys === 'field_2' ) {
// do what you have to do with this, for instance ...
$variable_2 = $values;
}
unset( $_GET[$keys] );
}
unset ( $keys, $values );
}
// check there are no surprises on $_GET. Load and study anything here
if ( empty( $_GET ) === false ) {
// it should be empty, so log what is in here and prepare your code for that
unset( $_GET );
} else {
unset( $_GET );
}
// process the variables according to what you want to do
// if there are just a few options, and they are not going to change often
// use a switch, otherwise, use a method to check if a file/content exists
// for the request and load it. If it doesn't exist, inform the user
// with out giving away internals and suggest a new destination
// process other variables, here or before this part, wherever makes sense
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>testing get</title>
</head>
<body>
<form method="get" action="test_get_00.php" accept-charset="utf-8">
<p><label for="field_1">write something<input type="text" id="field_1" name="field_1" /></label></p>
<p><label for="field_2">write something<input type="text" id="field_2" name="field_2" /></label></p>
<p><button type="submit">send</button></p>
</form>
</body>
</html>
Of course you can do a few more things, but if you prepare your form properly, including the character set, you have less worries, or at least a few more known elements. It's not failproof, but it helps.
Also, the mechanics I mention above work on a white list mindset, that is the idea of the foreach, to check that you get what you expect and discard the rest, after logging it.
You are trying to archive SEO urls in the wrong way.
I see that index.php?profile is better then index.php?page=profile but it's the wrong way of act in this case.
You should use index.php?page=profile and then apply a rewrite rule to create SEO urls, like this one:
RewriteEngine On
RewriteRule ^(.*)$ index.php?page=$1
In this way your users will use:
http://example.com/profile
and the page displayed will be:
http://example.com/index.php?page=profile
instead of switching on the keys (as proposed by key($_GET)) you could define one variable, in $_GET (url) named for example 'action' which would containt 'profile', or 'team' or whatever you wish in the future. then your switch, simply, will be :
switch ($_GET['action'])
so whatever action you assign to this key, you can use as a case in your switch
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.