Redirecting email whilst developing

15th January 2010

One common problem whilst developing is that you don't want to send emails out to the client (or their clients!). Ideally, we want to alter our development environment so that this doesn't happen, but still allows us to test the contents of emails that are sent by our web applications.

Windows

On Windows, the mail() function uses SMTP over port 25. Unless you've changed your php.ini file, then it will try to connect to localhost in order to send an email. On Windows VM, I use Fakemail. This is an SMTP mail server written in perl (or python) that store emails as files into a given directory. When your web application sends an email, you check in the directory and look at the files created. One top tip: alter the script to give each file a .txt extension. Then you can double click :)

Fakemail is also very useful in Linux/Mac if you are using the SMTP transport mail in Zend_Mail or Swiftmailer or whatever.

Linux / OS X

On the *nix based systems, mail() sends email using an application on the system called sendmail (or any number of compatible alternatives). By default it will call the sendmail binary, however you can change this in your php.ini with the sendmail_path setting.

I set my development boxes like this:


sendmail_path = /usr/local/bin/trapmail

Now, mail() will call my trapmail script. This script is trivial:


formail -R cc X-original-cc \
  -R to X-original-to \
  -R bcc X-original-bcc \
  --A"To: rob@akrabat.com" \
| /usr/sbin/sendmail --i

This script causes all emails to be redirect to my email address with the original to, cc and bcc fields renamed in the headers, so they can be checked!. I like this solution even better than Fakemail as it's easier to see exactly what the mail looks like in a mail client, especially for HTML format.

I just wish I had thought of this script myself! However Sean Coates came up with the idea in 2005 and I've been using it every since.

PHP Advent 2009: On deployment

6th December 2009

This year I was asked to write an article for PHP Advent 2009 an it's now been published!

Automate your Deployment is a look at how to automate the process of deploying your application to the web server. At my company we started automating our deployment systems just over a year and the number of issues we have around deployment of new code to a website has dropped considerably and is no longer a stressful event.

If you aren't currently using an automated deployment script, I can't recommend highly enough that you set yourself a New Year's resolution to investigate the options and implement a system for yourself.

Setting up PHP & MySQL on OS X 10.6 Snow Leopard

5th October 2009

(Updated 1st May 2010)

With OS X 10.6, Apple ships PHP 5.3 with PEAR, GD and PDO_MYSQL out of the box. Also, everything is now 64bit. This means that the entire effort required to get a working PHP dev environment for my work is now much easier.

/usr/local

Ensure that the following directories exist:

sudo mkdir /usr/local/include
sudo mkdir /usr/local/bin
sudo mkdir /usr/local/lib
sudo mkdir -p /usr/local/man/man1

MySQL

  1. Download the 64bit DMG version of MySQL 5.1.x (or 5.5.x) for OS X 10.6 from mysql.com and install the pkg, the startup item and the pref pane.
  2. Add /usr/local/mysql/bin to the path: vim ~/.bash_profile and add:
    export PATH=~/bin:/usr/local/bin:/usr/local/mysql/bin:$PATH
    export EDITOR=vim
    

    at top of file. (Note that we set EDITOR whilst we are here so that svn is happy!)

  3. Set up MySQL root password:
    mysqladmin -u root password {new-password}
    mysqladmin -u root -p{new-password} -h localhost password {new-password}
    mysqladmin -u root -p reload
    

    Clear the history file by typing history -c so that {new-password} isn't in plain text on the disk.

Apache

  1. cd /etc/apache2
  2. sudo vim httpd.conf
  3. Find #LoadModule php5_module libexec/apache2/libphp5.so and remove the leading #
  4. Find AllowOverride None within the <Directory "/Library/WebServer/Documents">section and change toAllowOverride All so that .htaccess files will work.
  5. Restart Apache: sudo apachectl restart
  6. Open Finder and navigate to /Library/WebServer/Documents/
  7. Create a new folder called "orig" and place all files currently in the Documents folder into it.
  8. Create a new file called info.php with <?php phpinfo(); inside it.
  9. Use Safari to navigate to http://localhost/info.php and check that the PHP version is displayed (5.3.0 at the time of writing).

php.ini

  1. cd /etc
  2. sudo cp php.ini.default php.ini
  3. sudo chmod ug+w php.ini
  4. sudo chgrp admin php.ini
  5. vim php.ini (assuming your user is a member of the admin group) and change settings appropriately. Change:
    error_reporting  =  E_ALL | E_STRICT
    display_errors = On
    html_errors = On
    extension_dir = "/usr/lib/php/extensions/no-debug-non-zts-20090626"
    

    (I like to see my xdebug errors in bright orange!)
    Also, change all instances of /var/mysql/mysql.sock to /tmp/mysql.sock

Xdebug

Can't have a PHP development environment without xdebug!

  1. sudo pecl channel-update pecl.php.net
  2. sudo pear channel-update pear.php.net
  3. sudo pecl install xdebug
  4. Edit /etc/php.ini and add
    zend_extension="/usr/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so"

    after the other extension lines.

  5. Restart apache: sudo apachectl restart and check in the phpinfo that xdebug is now loaded.

PHPUnit

  1. sudo pear channel-update pear.php.net
  2. sudo pear upgrade-all
  3. sudo pear channel-discover pear.phpunit.de
  4. sudo pear install phpunit/PHPUnit

It all works on this machine, anyway :)

Other options:

Some notes on Zend Server CE for Mac OS X

22nd June 2009

I've installed Zend Server CE on my Mac to see where it's got to and it's looking quite usable. The installation puts everything into the usr/local/zend directory which is fairly well laid out so that you can find what you are looking for. There's also a a nice admin system at http://localhost:10081 which allows you to restart PHP, view phpinfo(), configure extensions and php.ini. There's also a phpMyAdmin to help administer the bundled MySQL server.

For Mac, this is now one of the better one stop shops for easy PHP & MySQL installation.

Obviously, some things need configuration:

Set up paths

You need access to the command line zendctl.sh and mysql tools:

  • Edit ~/.bash_profile and add:
        PATH=$PATH:/usr/local/zend/bin:/usr/local/zend/mysql/bin
        LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/zend/lib
    
  • Close Terminal and restart it so that your change takes effect.

Change to port 80

The Apache in Zend Server is configured for 10088 to avoid conflicting with Apple's Web Sharing I suppose. The choice of using port 80 would have been nice as an installation option though.

To use port 80 is easy enough:

  • Stop Apache: sudo zendctl.sh stop-apache
  • Edit /usr/local/zend/apache2/conf/httpd.conf and replace Listen 10088 with Listen 80
  • Edit /usr/local/zend/apache2/bin/apachectl and change STATUSURL="http://localhost:10088/server-status" to STATUSURL="http://localhost:80/server-status"
  • If you are using vhosts, then edit /usr/local/zend/apaches/conf/httpd.conf and replace all instances 10088 with 80
  • Restart Apache:sudo zendctl.sh start-apache

Installing PHPUnit

Update PEAR first:

    sudo pear channel-update pear.php.net
    sudo pear upgrade-all

Install PHPUnit:

    sudo pear channel-discover pear.phpunit.de
    sudo pear install phpunit/PHPUnit

Installing Xdebug

  • Install Xcode so you have a compiler!
  • Stop Apache: sudo zendctl.sh stop-apache
  • Go to http://localhost:10081/ and pick the Server Setup tab. Turn off the Zend Debugger and Zend Data Cache and restart PHP
  • sudo pecl install xdebug
  • Edit /usr/local/zend/etc/php.ini and add above the [zend] section near the bottom:
    zend_extension="/usr/local/zend/lib/php_extensions/xdebug.so" 
    
    [xdebug]
    xdebug.remote_enable=1
    xdebug.remote_host="localhost"
    xdebug.remote_port=9000
    xdebug.show_local_vars=On
    xdebug.var_display_max_data=10000
    xdebug.var_display_max_depth=20
    

    (you should set up your xdebug settings as you require!)

  • Restart Apache:sudo zendctl.sh start-apache
  • The Server Setup->Extensions section of the admin interface should now show xdebug.

All in all, it's remarkably easy to set up Zend Server using PEAR and PECL is which how it should be.

The only other gotcha I noticed is that my.cnf is in /usr/local/zend/mysql/data whereas I would have thought that /usr/local/zend/etc would have been more logical.

UTF8, PHP and MySQL

18th March 2009

Everyone else probably already knows this stuff, but I hit an issue today to that took a while to sort out. Fortunately, some kind folks on IRC helped me, but as it's embarrassing to ask for help on the same issue twice, I'm writing down what I've learned!

The problem

Get a £ character stored to MySQL, retrieved and then displayed without any weird characters in front of it using UTF8.

The solution

Make sure that you are using UTF8 everywhere!

The browser:

<?php header("Content-type: text/html; charset=utf-8"); ?>

You can also use a meta tag that is redundant in theory:


<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

Also, note that the the <form> element has an 'accept-charset' attribute which should also be set:


<form accept-charset="utf-8" ...>

MySQL:

Make sure that your table's collation is utf8_general_ci and that all string fields within the table also have the utf8_general_ci collation.

And here's the really important bit: make sure your client connection is also using UTF-8:

For mysql:


mysql_set_charset('utf8');

or for mysqli:


mysqli_set_charset('utf8');

or execute the SQL immediately after connection:


SET NAMES UTF8;

or for PDO:


$handle = new PDO("mysql:host=localhost;dbname=dbname",
    'username''password', 
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

or for Zend_Db:


$params = array(
    'host' => 'localhost',
    'username' => 'username',
    'password' => 'password',
    'dbname' => 'dbname',
    'driver_options' => array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8;');
);
$db Zend_Db::factory('PDO_MYSQL'$params);

Note that in PHP 5.3.0 and 5.3.1, you cannot use the PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8;' option for PDO as it doesn't work! See bug 47224 for details.

For a Zend Framework application that uses Zend_Application, add this to your ini file:

resources.db.params.charset utf8

Now everything works as expected!

(as long as you don't have an output filter on your view that's too clever for its own good...)

Also, read About using UTF-8 fields in MySQL by Joshua Thijssen.