Serious PHP, part 1

I used PHP 3 a lot back in the day, and I built some seriously interesting stuff with PHP 4 before moving on to what was then consider more “serious” technology. Recently I’ve been looking at PHP again, just to figure out how the famous LAMP stack works these days. Care to follow along?

In the below I assume you’re on Mac OS X and you sort-of know your way around unix.

Installing a bunch of packages under Mac OS X

We’re going to use a whole bunch of stuff…let’s get the package in one go (hint: you can install multiple ports at a time, see man port)

  • Install MacPorts
  • Install apache2:
    sudo port install apache2
  • Install MySQL 5:
    sudo port install mysql5
    (You will need to follow some on-screen instructions to finish configuration)
  • Install SQLite:
    sudo port install sqlite3
  • Install Perl 5.8:
    sudo port install perl5.8
  • Install Apache-Test:
    sudo port install p5-apache-test
  • Install PHP 5:
    sudo port install php5 +apache2+mysql5+pear+sqlite
    (you may want to add some more options, see what’s available using port variants php5; You may also need to follow some on-screen instructions to enable the extension within apache)
    sudo cp /opt/local/etc/php.ini-recommended /opt/local/etc/php.ini
  • Configure pear:
    sudo pear config-set php_ini /opt/local/etc/php.ini
    sudo pear upgrade-all
    sudo pear channel-update pear.php.net
    sudo pear channel-update pecl.php.net
  • Install APC:
    sudo pear install APC
  • Install Memcached:
    sudo port install memcached
  • Install memcache for python:
    sudo port install php5-memcache
    (You will need to follow some on-screen instructions to enable the extension)

Setting up MySQL replication

MySQL replication is a technique that helps with scaling up your application. Sooner or later your database tends to become a bottleneck, and in that case what you do is make sure your SELECT statements go to database slaves. It is easier to get this right from the start, rather than re-write your application later. So, set up some replication locally…Buy and read High Performance MySQL to get a good grasp for the details; it’s the best book on the subject.

After you’ve RTFMed a bunch, assuming you have a basic mysql install set up…here’s the magic…

# set the root password
mysqladmin -u root password 'blaat'

# shortcut
my="mysql -uroot -pblaat"

# db we'll be using
echo "DROP DATABASE IF EXISTS test;" | $my
echo "CREATE DATABASE test;" | $my
echo "GRANT ALL
  ON test.* TO 'test'@'%'
  IDENTIFIED BY 'test'" | $my

# prep for replication
echo "GRANT
    REPLICATION SLAVE,
    REPLICATION CLIENT
  ON *.* TO 'repl'@'%'
  IDENTIFIED BY 'blaat'" | $my

# I always manage to forget this one...
echo "FLUSH PRIVILEGES;" | $my

# enable binary logging and set server-id on to-be-master
sudo cat > /opt/local/etc/mysql5/my.cnf <<END
[mysqld]
log-bin
server-id = 1
END

# create config for first slave to run on port 3307
sudo cat > /opt/local/etc/mysql5/my_3307.cnf <<END
[mysqld]
server-id = 2
master-host = 127.0.0.1
master-user = repl
master-password = blaat
master-port = 3306
port = 3307
socket = /opt/local/var/run/mysql5/mysqld_3307.sock
pid-file = /opt/local/var/db/mysql5_3307/my.pid
datadir = /opt/local/var/db/mysql5_3307
log-error = /opt/local/var/db/mysql5_3307/my.error.log
END

# create config for second slave to run on port 3308
sudo cat > /opt/local/etc/mysql5/my_3307.cnf <<END
[mysqld]
server-id = 2
master-host = 127.0.0.1
master-user = repl
master-password = blaat
master-port = 3306
port = 3308
socket = /opt/local/var/run/mysql5/mysqld_3308.sock
pid-file = /opt/local/var/db/mysql5_3308/my.pid
datadir = /opt/local/var/db/mysql5_3308
log-error = /opt/local/var/db/mysql5_3308/my.error.log
END

# stop the current mysql database
sudo killall mysqld_safe

# remove any previous/old binary log files
rm /opt/local/var/db/mysql5/*-bin*

# initialize data directories for the slaves
mkdir /opt/local/var/db/mysql5_3307
chown mysql:mysql /opt/local/var/db/mysql5_3307
rsync -av /opt/local/var/db/mysql5/ /opt/local/var/db/mysql5_3307/
mkdir /opt/local/var/db/mysql5_3308
chown mysql:mysql /opt/local/var/db/mysql5_3308
rsync -av /opt/local/var/db/mysql5/ /opt/local/var/db/mysql5_3308/

Pfoeey. Let’s see if that worked (save the below to a script file):

#!/usr/bin/env bash
# starts 3 mysql servers
sudo mysqld_safe >/opt/local/var/db/mysql5/my.out 2>&1 &
sudo mysqld_safe --defaults-file=/opt/local/etc/mysql5/my_3307.cnf \
  >/opt/local/var/db/mysql5_3307/my.out 2>&1 &
sudo mysqld_safe --defaults-file=/opt/local/etc/mysql5/my_3308.cnf \
  >/opt/local/var/db/mysql5_3308/my.out 2>&1 &

Let’s see if we got replication going:

echo "CREATE TABLE repl_test ( id int(11) PRIMARY KEY );" \
    | $my test
tail /opt/local/var/db/mysql5_3307/my.error.log
tail /opt/local/var/db/mysql5_3308/my.error.log

Setting up Memcached

Memcached needs essentially no setup. Try this (save the below to a script file):

#!/usr/bin/env bash
# starts 2 memcached servers
hostname=127.0.0.1
memperdaemon=128
daemonport1=11211
daemonport2=11212

memcached -d -l $hostname -m $memperdaemon -p $daemonport1
memcached -d -l $hostname -m $memperdaemon -p $daemonport2

Setting up Apache to use virtual hosting

We’ll have apache listen on a separate port where our app can be all by itself without getting disturbed by your other dev projects that invade its URL space.

  • mkdir ~/doodle
  • mkdir ~/doodle/htdocs
  • Edit
    /opt/local/apache2/conf/httpd.conf
    look for the line
    #Include conf/extra/httpd-vhosts.conf
    and un-comment it.
  • Then change that file:
sudo cat >/opt/local/apache2/conf/extra/httpd-vhosts.conf <<END
NameVirtualHost *:80
<VirtualHost *:80>
DocumentRoot "/opt/local/apache2/htdocs"
</VirtualHost>

Listen 8810
NameVirtualHost *:8810
<VirtualHost *:8810>
    ServerName localhost:8810
    DocumentRoot $HOME/doodle/htdocs
    <Directory "$HOME/doodle/htdocs">
      Options Indexes FollowSymLinks
      AllowOverride None
      Order allow,deny
      Allow from all
    </Directory>
</VirtualHost>
  • Test the config:
    sudo /opt/local/apache2/bin/apachectl configtest

    then start (restart if started)
    sudo /opt/local/apache2/bin/apachectl stop

    sudo /opt/local/apache2/bin/apachectl start

Make pecl/filter more pendantic

This is a pretty awkward PHP extension that implements user-submitted variable sanitization rather efficiently. Take cross-site scripting seriously, and protect against it!

  • Edit
    /opt/local/etc/php.ini
    look for the line
    [filter]

    and add
    filter.default = "special_chars"

    filter.flags = FILTER_FLAG_ENCODE_HIGH
  • Restart apache.

When are we going to do any PHP?

Let’s see if part 2 has more actual PHP in it. The above list of dependencies should give you some idea of where I’m going :-). For now try this:

cat > ~/doodle/htdocs/phpinfo.php <<END
<?php
    phpinfo();
?>
END

open http://localhost:8810/phpinfo.php