Very Basic PHP Question

I have a lot of experience coding static HTML and CSS (and enough Javascript to scrape through in a pinch) but I am trying to make the leap to coding dynamically-generated web pages using PHP.

To this end, I’ve read “Kevin Yank’s Build Your Own DB-Driven Website”, and I can follow the train of logic when I see the code and make things work according to the instructions in the book, but when it comes to actually writing code on my own, I have trouble.

So… I thought I would try a really simple exercise: a dummy website that uses one PHP controller (index.php) to pull up different articles depending on what the user clicks. I have posted this site at:

http://www.trilefile.com/fortunecookie/

I guess I got it to work, for the most part, but I can’t help but feel that there is a more eloquent way to code what I am doing. Specifically, there are 3 issues I am wondering about:

  1. Is there a way to include the footer only once rather than over and over again? If I place it below all the “if” statements, it gets “eaten up” by the exit() function.

  2. If the user types in a page that doesn’t exist, I would like for them to be redirected to a custom 404 “page not found” error message. As things stand, they just get redirected to the home page.

  3. Lastly, just wondering… is the phenomenon of the “?+linkname” appearing in the URL unavoidable? Is it OK? Is it bad? What are the current conventions?

Or perhaps there is a sleeker, sexier, more svelte way to structure the whole site, and I’m missing the boat?

I have made available for download a zip file containing all the pages used to build the site (not many) at:

http://www.trilefile.com/fortunecookie/fortunecookie.zip

If you’d rather just see the code, I am copying and pasting the gist of the index.php page below.

Any advice would be greatly appreciated.



<body>
<div id="wrapper">

<?php
include 'header.inc.html.php';
include 'nav.inc.html.php';

if (isset($_GET['home']))
	{
	include 'home.html.php';
	include 'footer.inc.html.php';
	exit();
	}

if (isset($_GET['about']))
	{
	include 'about.html.php';
	include 'footer.inc.html.php';
	exit();
	}

if (isset($_GET['contact']))
	{
	include 'contact.html.php';
	include 'footer.inc.html.php';
	exit();
	}

include 'home.html.php';	
include 'footer.inc.html.php';
?>

</div><!--wrapper-->
</body>

You could use a switch-case statement instead of your approach with the stand-alone if-statements with exit().

Thanks. I was unable to implement the switch statement (I know how to do them in C++, but I’m unfamiliar with the syntax in PHP – particularly when it comes to specifying the links and all that.) I did, however, while looking up the switch, run across the “elseif” format, and I was able to make that work.

I think it’s an improvement, because the “footer” include only gets stated once now, instead of repeatedly, and we avoid all of the exit() functions. But if an invalid URL is typed in, it still takes the user back to the home page instead of the 404 error page as I would like. I guess for that to happen, the “error” include should appear in the “else” clause, but if I do that, then the error page would come up when you’d type in the directory’s URL (i.e. http://www.trilefile.com/fortunecookie/)

So I guess it boils down to: is there a way to specify an if-clause that would direct the user to the home page if no particular URL directory is specified? – meaning, if the user just types in http://www.trilefile.com/fortunecookie/

The improved code:


<body>
<div id="wrapper">

<?php
include 'header.inc.html.php';
include 'nav.inc.html.php';

if (isset($_GET['home']))
	{
	include 'home.html.php';
	}

elseif (isset($_GET['about']))
	{
	include 'about.html.php';
	}

elseif (isset($_GET['contact']))
	{
	include 'contact.html.php';
	}

else
	{
	include 'home.html.php';
/*I would like for this to be include 'error.html.php';
but as things stand, that would make the URL
http://www.trilefile.com/fortunecookie/ lead to
the error page instead of the home page*/
	}

include 'footer.inc.html.php';
?>

</div><!--wrapper-->
</body>

Try this:

http://johns-jokes.com/downloads/sp/fortune-cookie/

.

Wow. That was amazing.

Yes, that works. (The only thing was that when I tested the code, somehow I wound up with double the amount of div’s because they were both in the includes and in the index file, so I edited them out, but that’s peripheral.)

So the fully functional code is below. Thanks ever so much.

Just out of curiosity, I understand the switch statement, and the setting of default to “home”, but what’s all that stuff in the beginning? Lines 2 & 3, I assume, are a built-in error reporting function, but what are array_keys and how does that $mode variable get set up?


<?php    
        error_reporting(E_ALL);
        ini_set('display_errors', 'on');
        
        $mode = 'home'; // default
        if (! empty($_GET))
        {
            $mode =  array_keys($_GET);
            $mode = $mode[0];
        }        
        switch($mode)
        {
            case 'home'            : $blurb = 'home.html.php';
            break;
        
            case 'about'        : $blurb = 'about.html.php';
            break;
        
            case 'contact'    :    $blurb =  'contact.html.php';
            break;
            
            default                    : $blurb = 'error.html.php';
        }//endswitch
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
<title>Fortune Cookie Fan Club</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="style1.css" />
</head>

<body>
    <div id="wrapper">
        
        <?php include 'header.inc.html.php'; ?>
        <?php include 'nav.inc.html.php'; ?>
        <?php include $blurb; ?>
        <?php include 'footer.inc.html.php'; ?>
    
    </div><!--wrapper-->       
</body>
</html>

From there you can assign ‘error.html.php’ as the blurb, then use file_exists to check if the name of the provided file exists.

If it does exist, assign that page name instead of the error one, and then load up the page.

So, instead of the switch, you could have this:


...
$blurb = 'error.html.php';
if (file_exist($mode . '.html.php')) {
    $blurb = $mode . '.html.php';
}
...

Did you scroll to the bottom and see the source code?

Your DIVs were duplicated in your include files so I removed then and placed a single reference in the index.php file. It will be a lot easier in the future to make a single change that affects the whole site.

The $mode is tested to see if the URI parameter $_GET array() is passed else use the default value. If it is set the $mode is extracted. Download the following PHP document help file and lookup the functions that I use to parse the $mode variable. Lookup “Array functions”.

http://php.net/download-docs.php

Did you check the 404 File not found?

A good way of viewing the contents of an array:



//==============================================
//
//  usage:
//             show_array($array_value);
//
//   Result:
//           displays formatted contents of $array_value
//
//==============================================
function show_array($value = array('Red','Yellow','Blue', 'Green'))
{
  if (is_array($value))
  {
     echo '<pre>';
        print_r($value);
     echo '</pre>';
  }else{
  echo 'The vaule you passed is not an array';
 }//endif
}//endfunc


I programmed in C and C++ a long time ago and found it is very similar syntax to PHP. Beware of the PHP non-stict types. No doubt you will be caught out and the PHP documentation is essential. :slight_smile:

.

I like your cryptic alternative to using the switch statement but must admit that I prefer the simpler to read and debug switch statement.

.

Hey, look at this –

using that new php code, i was able to implement contextual navigation with a $bodyid variable. So that a little border lights up around whatever page you’re on at the moment.

You just have to stick the following stuff in your stylesheet…


#home .home,
#about .about,
#contact .contact {
border: 1px solid darkslategray;
padding: 0 .5em;
}

and in the nav:


<div id="nav">
<ul>
<li><a href="?home" class="home">Home</a></li>
<li><a href="?about" class="about">About</a></li>
<li><a href="?contact" class="contact">Contact</a></li>
</ul>
</div><!--nav-->

This PHP stuff is really useful.


<?php    
        error_reporting(E_ALL);
        ini_set('display_errors', 'on');
        
        $mode = 'home'; // default
        if (! empty($_GET))
        {
            $bodyid = 'home';
			$mode =  array_keys($_GET);
            $mode = $mode[0];
        }        
        switch($mode)
        {
            case 'home':
			$blurb = 'home.html.php';
			$bodyid = 'home';
            break;
        
            case 'about':
			$blurb = 'about.html.php';
			$bodyid = 'about';
            break;
        
            case 'contact':
			$blurb =  'contact.html.php';			
            $bodyid = 'contact';
			break;
            
            default:
			$blurb = 'error.html.php';
			$bodyid = '';
        }//endswitch
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
<title>Fortune Cookie Fan Club</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="style1.css" />
</head>

<body id="<?php echo $bodyid; ?>">
    <div id="wrapper">
        
        <?php include 'header.inc.html.php'; ?>
        <?php include 'nav.inc.html.php'; ?>
        <?php include $blurb; ?>
        <?php include 'footer.inc.html.php'; ?>
    
    </div><!--wrapper-->       
</body>
</html>

I did scroll down to the bottom, but only saw the source code for the index, not the includes, which is why I was confused about the DIVs at first. But I get it now. And I checked the 404 File not found page; it works fine.

Many thanks for all your help and explanations, and I’ll be sure to check out the documentation.

This is a very clever solution: I tried it out in my code. But, since I’m just starting out, the switch statement is I perhaps easier to wrap my mind around. Also, it allowed me to easily implement contextual navigation, which works really nicely.

Thank you for your help.

The negative side of your solution is, that you’ll have to change the switch statement each time you want to add a page to your website. While pmw57’s solution is flexible.

You can also add contextual navigation:


...
$blurb = 'error.html.php';
if (file_exist($mode . '.html.php')) {
    $blurb = $mode . '.html.php';
    $bodyid = $mode;
}
...

Untested but I would do something like this for flexibility and ease of expansion.


<?php

/*
* 1.) Default mode
* 2.) Error mode
* 3.) $_GET mode key
*/
$default_mode = 'home';
$error_mode = 'error';
$mode_flag = 'mode';

/*
* Route configuration
*/
$routes = array(
	'home'=>array(
		'body_id'=>'home'
		,'blurb'=>'home.html.php'
	)
	,'about'=>array(
		'body_id'=>'about'
		,'blurb'=>'about.html.php'
	)
	,'contact'=>array(
		'body_id'=>'contact'
		,'blurb'=>'contact.html.php'
	)
	,'error'=>array(
		'body_id'=>'error'
		,'blurb'=>'error.html.php'
	)
);

/*
* Get requested route from mode
*/
$route = isset($_GET[$mode_flag])?array_key_exists($_GET[$mode_flag],$routes)?:$routes[$_GET[$mod_flag]]:$routes[$error_mode]:$routes[$default_mode];

/*
* Set-up template data
*/
$header = 'header.inc.html.php';
$nav = 'nav.inc.html.php';
$blurb = $route['blurb'];
$footer = 'footer.inc.html.php';

/*
* Dump template
*/
include($header);
include($nav);
include($blurb);
include($footer);
?>

In every one of those route arrays you can now define data that changes but is present on every page such as; the title,body id, etc etc w/o it being bound to the mode name. You can also build your navigation dynamically using the route configuration. Meaning if you ever need to add a page you won’t have to touch the template code just the route configuration.

I don’t recommend people use this method. Unless some robust validation is performed on the value of the $mode variable, it’s a security issue. null bytes are the problem here. It lets an attacker make php ignore the ‘.html.php’ suffix, and with that, they can pretty much provide the name and path to any file on the system.

You’re right. I totally see the advantage of it now. But apparently there are security issues with this one, so that sucks.

I tested your method out but couldn’t get it to work. I keep getting syntax errors; I think stemming from the following section:


/*
* Get requested route from mode
*/
$route = isset($_GET[$mode_flag])?array_key_exists($_GET[$mode_flag],$routes)?:$routes[$_GET[$mod_flag]]:$routes[$error_mode]:$routes[$default_mode];

Hi again –

Sorry to keep bugging you, but I was wondering: how is it an advantage to place the DIVs in the index.php file rather than the includes? Don’t get me wrong: I believe you wholeheartedly, and I definitely want to make things easier in the future, but I just can’t follow through the thinking process that far ahead.

I just must be missing my ticket for the logic train on this one.

Should work now.


<?php

/*
* 1.) Default mode
* 2.) Error mode
* 3.) $_GET mode key
*/
$default_mode = 'home';
$error_mode = 'error';
$mode_flag = 'mode';

/*
* Route configuration
*/
$routes = array(
	'home'=>array(
		'body_id'=>'home'
		,'blurb'=>'home.html.php'
	)
	,'about'=>array(
		'body_id'=>'about'
		,'blurb'=>'about.html.php'
	)
	,'contact'=>array(
		'body_id'=>'contact'
		,'blurb'=>'contact.html.php'
	)
	,'error'=>array(
		'body_id'=>'error'
		,'blurb'=>'error.html.php'
	)
);

/*
* Get requested route from mode
*/
$route = isset($_GET[$mode_flag])?array_key_exists($_GET[$mode_flag],$routes)?$routes[$_GET[$mode_flag]]:$routes[$error_mode]:$routes[$default_mode];

/*
* Set-up template data
*/
$header = 'header.inc.html.php';
$nav = 'nav.inc.html.php';
$blurb = $route['blurb'];
$footer = 'footer.inc.html.php';

/*
* Dump template
*/
include($header);
include($nav);
include($blurb);
include($footer);
?>

cool – thank you.

I think that it is easier to read and understand the script flow. Also debugging is easier because each include can be remmed and you will still have the basice outline.

Try remming each include statement and use the debug style div {outline: dotted 1px #f00}.

Also the CSS article unique headers are duplicated which I find confusing.

The code you supplied is difficult to understand and especially once you return to debug after about a month.

I try to follow the KISS style of programming.

.