How to handle file includes properly

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
User avatar
Mr Tech
Forum Contributor
Posts: 424
Joined: Tue Aug 10, 2004 3:08 am

How to handle file includes properly

Post by Mr Tech »

I'm developing a script that uses modules for different sections of the website... basically the PHP code is laid out like this:

Code: Select all

include("header.php");

include("includes/".$_GET[module]."/index.php");

include("footer.php");
The problem with this method (i guess) is that it will cause "warning: headers already sent" messages" because the included module file uses the header() function a lot. There may be more reasons this is not a good idea but I have no idea what.

Is there a better approach to this? What do you do when you include files in this manner?
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Take a look at the output buffering functions.
(#10850)
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post by Kieran Huggins »

sadly, output buffering doesn't buffer headers.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Post by alex.barylski »

Good point.

In my opinion, when you start having issues with headers already sent...thats a good time to invest some time in a simple template engine.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Kieran Huggins wrote:sadly, output buffering doesn't buffer headers.
I'm not sure it needs to. I think the issue Mr Tech was referring to was that of including the header before including the bulk of the page which includes such controller logic (sessions, redirects etc). Output buffering will help in this case.

Though Hockey is on track too, but it's not really about template engines, it's about code organization... effectively you want to run all the code which processes user input, handles sessions/redirects etc (the controller) *before* including header & footer. Obviously you need to split your "index.php" file into two for that. You're slowly refactoring towards something like MVC this way.

Code: Select all

include('controllers/' . $_GET['page'] . '.php');

include('templates/header.php');
include('templates/' . $_GET['page'] . '.php');
include('templates/footer.php');
I'd be a bit wary about directly using the GET variable in the include() however since you can pass "../../../some/secret/folder" in input like that ;)

If your main include previously had something like the following:

Code: Select all

<table>
  <tr>
    <th>Name</th>
    <th>Email</th>
  </tr>
<?php

$query = "select name, email from users";
$result = mysql_query($query);
while ($row = mysql_fetch_assoc($result))
{
  echo '<tr><td>';
  echo $row['name'];
  echo '</td><td>';
  echo $row['email'];
  echo '</td></tr>';
}

?>
</table>
Then you'd split it something like this:

controllers/pageName.php

Code: Select all

<?php

$users = array();

$query = "select name, email from users";
$result = mysql_query($query);
while ($row = mysql_fetch_assoc($result))
{
  $users[] = $row;
}

?>
templates/pageName.php

Code: Select all

<table>
  <tr>
    <th>Name</th>
    <th>Email</th>
  </tr>
  <?php foreach ($users as $user): ?>
  <tr>
    <td><?php echo $user['name']; ?></td>
    <td><?php echo $user['email']; ?></td>
  </tr>
  <?php endforeach; ?>
</table>
All I've done is separated my business logic from my presentation logic.

For the sake of brevity I've not escaped data I'm echo'ing into the HTML which is *bad* -- use htmlspecialchars() or such like in real code.

In this case too you really only got two layers: View and Controller-Model.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Kieran Huggins wrote:sadly, output buffering doesn't buffer headers.
That is the whole point. Headers are sent and output is held in a string to be echoed later. Doing that is just making PHP templates function the same as templates where parsing/replacement is done. They all share the same idea -- gather output in strings and sent them last.

I think a Response object is the real solution, then you "buffer" headers as well as control doctype, etc.
(#10850)
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post by Kieran Huggins »

it just occurred to me that it doesn't matter that headers aren't buffered. The problem lies in the fact that there's output BEFORE some of the headers he wants to send. Ergo, start output buffering before the includes and flush at the end of the script; all headers (including his) will be sent before the output. Bingo!
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Kieran Huggins wrote:it just occurred to me that it doesn't matter that headers aren't buffered. The problem lies in the fact that there's output BEFORE some of the headers he wants to send. Ergo, start output buffering before the includes and flush at the end of the script; all headers (including his) will be sent before the output. Bingo!
Did someone say coffee? :wink:
Post Reply