2010-01-09

PHP 102: A Portable Apache+PHP Environment for Debian/Ubuntu

The very first thing you need to do to develop for PHP is to setup a development server with PHP installed, of course. But maybe you're somewhat new to PHP and Apache is a huge, mind numbing universe of options. All you really want is the ability to run PHP and map hostnames to code, right? Almost always that's my goal, anyway.

Well, I've done all the work for you. Congrats! Actually, I did it mostly for myself and I'm sharing it with you! Yay for us!

What You Need
  • A computer with Ubuntu installed and working; I'm using 9.10 as I write this
  • An Internet Connection
Scope
We are going to setup a new, portable Apache server instance on Ubuntu 9.10 that listens on all of your computer's IP addresses and maps the hostnames being requested from that computer to directories on your hard disk. This means that you can add websites without restarting or reconfiguring Apache in any way. You just create a new directory and Apache figures it out for you. Brilliant, no?

What do I mean by portable? I mean every configuration file, content and scripts you need to run that entire Apache instance is in one folder that is not part of the operating system. You can copy that folder to any other Ubuntu/Debian box and execute the script to start it without any modifications of any kind. All you need is to install Apache2, PHP5 and some other modules. You aren't even using the init script that Ubuntu provides (because theirs sucks; it even says so in the comments).

Preliminary Setup
You need to install Apache, PHP and xdebug and then configure things. Open a terminal (Applications->Accessories->Terminal) and run these commands in this order:
  • sudo apt-get install php5 libapache2-mod-php5 php5-xdebug # Install Apache/PHP
  • sudo update-rc.d -f apache2 remove # Disable Apache Service
  • sudo /etc/init.d/apache2 stop # Stop Apache Service
  • sudo a2enmod php5 # Enable PHP5
  • sudo a2enmod vhost_alias # Enable Virtualhost Aliasing
Environment Preparation
Now that we have installed, configured and disabled the system-wide Apache instance we need to extract my handy Apache environment and set it up.

Open a terminal (Applications -> Accessories -> Terminal) and run the following commands:
  • # Download the file to your home, extract it to /srv and name it something handy
  • cd
  • wget http://www.webaugur.com/wares/files/apache2-portable-latest.tar.gz
  • pushd /srv
  • sudo tar xvzf ~/apache2-portable-latest.tar.gz
  • sudo mv apache2-portable* development
  • pushd development
  • # Run this script as whichever user account is going to be developing code
  • bin/fixperms
  • # start the service
  • sudo etc/init.d/apache2 start
  • # Next we want to symlink it into the init system so it starts up at boot
  • sudo ln -s /srv/development/etc/init.d/apache2 /etc/rc2.d/S91apache2-development
You should now be able to open http://localhost/ in your browser and see your phpinfo() output.

Adding Content
I have prepopulated a simple test script into www/localhost/public/index.php so you can see if its working right away.

The way it works is very simple. If you point a hostname (either in DNS or in your /etc/hosts file) to any of your computer's IP addresses Apache will serve up content from www/hostname/public. So, lets say you point www.ilikeapache.foo to your computer. You will place your content into www/www.ilikeapache.foo/public for Apache to find it. You can symlink many hostnames to a common directory as needed. You might create a link called www/ilikeapache.foo that points to www.ilikeapache.foo, for example. Thus serving up the same content for both host names. Much less ugly than slaving over hot config files into the wee hours of the morning. Simply amazing if you have hundreds of hosts pointing to one server.

What you Get
Here's where you may get a little lost if you're new to all of this. And I'll show you how to use some of this stuff in future articles. So just hang tight if you're not sure how all of this works.

By default, I have configured full on debugging of every kind. So don't go putting this up on a high traffic site as-is. I've done it and it'll run the box out of RAM and swap in under an hour and come crashing to the ground.

Here's a breakdown of useful directories and files in the default config:
  • var/log/apache2/access_log - Access log with hostname in the first column
  • var/log/apache2/error_log - Regular error log file
  • var/log/php.log - PHP Error Log (tail -f var/log/php.log to see errors in realtime)
  • var/log/xdebug - PHP xdebug stack traces and cachegrind (memory profiling) dumps
  • var/spool/mail/ - All outgoing email is intercepted (using bin/mailtrap as the "sendmail" program) and dropped here, one email per (timestamped) file. Files are stored in a format that can be piped directly into sendmail for real delivery.
  • lib/cgi-bin - If you create this directory it will be a common /cgi-bin for all hostnames on this Apache instance.
  • etc/apache2/include/php5.conf - a symlink that can pointed between debug and production PHP5 config files for quickly moving an instance between production and development.
  • etc/apache2/sites-templates/ - Examples of how to manually configure a hostname in this setup; both for HTTP and HTTPS.
  • etc/apache2/run - PID and lock files for this Apache instance
Why Oh Why
Some questions and answers I expect you'll have.
  1. Q: Why do I place content in a hostname/public subdirectory?
    A: You should never put your application code in a public directory. Zend Framework uses the convention of having a directory named "public" that maps URLs into your application code. I use ZF quite a lot and therefore followed this convention.
  2. Q: Can I run more than one instance of Apache like this?
    A: Absolutely, this is why I wrote my own init script. You can have as many Apache instances as you have IP addresses to give them. Modify etc/apache2/sites-available/mass-vhost.conf to Listen on a specific IP address instead of all IPs. You can have one instance on multiple IPs, as well. Just make sure none of the instances are listening on all IPs (or each other's IPs) or you'll have port conflicts.
  3. Q: How do I move my code and apache instance to another server?
    A: Run the setup and preparation steps above on the new host and instead of downloading my copy of the apache-portable just copy your entire directory containing your instance and content from /srv on the current machine to /srv on the new machine.
Troubleshooting and Corrections
If you run into troubles check the var/log/apache2/error_log for relevant problems. If you get completely stumped maybe I missed a step. Feel free to email me with a description of your problem and I'll try to take a look.

Background
I've been using web servers since 1994. I started using NCSA HTTPD and later switched to Apache as it become popular. I was even once part of the first team to port Apache to Windows back in the late 90s. Neat, huh? (No, Windows sucks. It was anything other than neat but it was a challenge.) I've written modules in C and I even modified the indexer module to support XML back when the XML specs were first being drafted (but my changes admittedly weren't generic enough and someone else did a better job later on). I've been using Apache since the beginning and I'm continually amazed at its capabilities.

Labels: , ,

2009-11-23

PHP 102: Whitespace Matters!

Lets start at the beginning. Lets start with creating a blank PHP script. What's one of the most important things to remember when creating a new PHP script? I'll tell ya: Whitespace Matters!

You must keep track of your whitespace in PHP scripts. Why? HTTP headers cannot be set if you have already "printed" any characters. Stray spaces before and after segments will, among other things, prevent you from setting HTTP headers (e.g. session cookies, redirects, and so on).

This is something I run into over and over and over and over in modifying carelessly written code. Sometimes this is the work of a well intentioned developer trying to make code "more readable". Worse, this is a problem you won't catch until its too late. You can code for months or years on a project and never notice any problems. Until one day everything explodes for no obvious reason. There are huge, massive projects out there that still haven't completely learned this lesson.

Opening Demarcation
If your script contains only PHP code then it must begin with <?php at the very beginning of the file. No spaces. No new lines. <?php should be the very first characters in your new file. Don't use asptags <%, don't use shorttags <? or any other cutesy business.

Closing Demarcation
If your script contains only code and no embedded HTML markup then it must never include a ?> closing tag. Never. Ever. Yes, of course it'll work. Don't do it. Omitting the closing ?> ensures that all trailing whitespace will be in a code segment and not your output buffer.

Good Example
<?php
function helloWorld(){
return 'Hello World!';
}

echo helloWorld();



Bad Example



<?php
function helloWorld(){
return 'Hello World!';
}

echo helloWorld();

?>

Labels: , ,