Setting up PHP & MySQL on OS X 10.7 Lion

25th July 2011

With OS X 10.7, Apple continues to ship PHP 5.3 with PEAR, GD and PDO_MYSQL out of the box. This is how to set it up from a clean install of 10.7.

/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{new-password} reload
    

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

  4. Now ensure that the mysql.sock file can be found by PHP:
    1. Ensure that MySQL is running
    2. sudo mkdir /var/mysql
    3. sudo ln -s /tmp/mysql.sock /var/mysql/mysql.sock

Apache

  1. cd /etc/apache2
  2. Give write permission the config file to root: sudo chmod u+w httpd.conf
  3. sudo vim httpd.conf
  4. Find #LoadModule php5_module libexec/apache2/libphp5.so and remove the leading #
  5. Find AllowOverride None within the <Directory "/Library/WebServer/Documents">section and change toAllowOverride All so that .htaccess files will work.
  6. Change permissions back: sudo chmod u-w httpd.conf
  7. Restart Apache by unticking and then ticking again the Web Sharing checkbox in System Preferences -> Sharing
  8. Open Finder and navigate to /Library/WebServer/Documents/ using shift+cmd+g
  9. Create a new folder called "orig" and place all files currently in the Documents folder into it. (note that it will ask for your password as the Documents folder is only writable by root.
  10. Create a new file called info.php with <?php phpinfo(); inside it.
  11. Use Safari to navigate to http://localhost/info.php and check that the PHP version is displayed (5.3.6 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! Apple appears to agree as Lion ships with it.

  1. vim /etc/php.ini
  2. Find the line:
    ;zend_extension="/usr/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so"
    and remove the semicolon at the start
  3. If you want to configure your xdebug settings, then scroll to the end of the file and look for the [xdebug] section. I like these settings:
    xdebug.var_display_max_children = 999
    xdebug.var_display_max_data = 99999
    xdebug.var_display_max_depth = 100
    

    (use with caution…)

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

PEAR

We need PEAR! For some reason, it's not set up ready to on Lion, but the install phar file is here, so we just need to run it.

  1. cd /usr/lib/php
  2. sudo php install-pear-nozlib.phar
  3. Edit/etc/php.ini and find the line: ;include_path = ".:/php/includes" and change it to:
    include_path = ".:/usr/lib/php/pear"
  4. sudo pear channel-update pear.php.net
  5. sudo pecl channel-update pecl.php.net
  6. sudo pear upgrade-all

PHPUnit and friends

I assume that everyone needs these…

  1. sudo pear channel-discover pear.phpunit.de
  2. sudo pear channel-discover components.ez.no
  3. sudo pear channel-discover pear.symfony-project.com
  4. sudo pear install phpunit/PHPUnit
  5. sudo pear install phpunit/phpcpd
  6. sudo pear install PHP_CodeSniffer

PECL OAuth

A couple of projects I work on use the PECL OAuth component:

  1. Ensure you have installed Xcode from the Mac App Store
  2. Download the latest PCRE source code from http://sourceforge.net/projects/pcre/files/pcre/ and unzip to a folder on your desktop
  3. cd ~/Desktop/pcre-8.12
  4. ./configure
  5. sudo cp pcre.h /usr/include/
  6. Remove the pcre folder on your desktop as you don't need it any more
  7. sudo pecl install oauth
  8. Edit/etc/php.ini add these lines to the end of the file:
    [oauth]
    extension="/usr/lib/php/extensions/no-debug-non-zts-20090626/oauth.so"
  9. Restart apache: sudo apachectl restart and check in the phpinfo that OAuth is now loaded.

mcrypt

This is useful! Follow the installation details by Michale Gracie here: http://michaelgracie.com/2011/07/21/plugging-mcrypt-into-php-on-mac-os-x-lion-10-7/)

It all works on this machine, anyway :)

Other options

If you'd rather use a packaged version, then these are two alternatives:

Great PHP developer required in Central Birmingham

23rd March 2011

Update: This position has now been filled.

So... we seem to be in the same position as everyone else and are looking to hire a new, great, PHP developer!

From the spec:

Big Room Internet are looking for a great PHP software engineer to bring depth to the team and spearhead our future development. You will be working across a range of projects, ideally with experience in seeing through a project from start to finish. You will also interact with customers from taking work requests through to working with them to resolve an issue and get new software approved. You must be self motivated and be able to show that you can work on your own initiative.

Ideally you'll have several years of PHP website experience, but also the knowledge of best practices when working as a team on projects. You will be able to take the lead on technical decisions for projects along with documenting them and proving that they work.

This position is based in our office in central Birmingham.

Obviously, we do a fair amount of our work using Zend Framework :)

If you're interested or if you know someone who is, please get them to contact me :)

Displaying an RSS feed in WordPress

3rd January 2011

My wife decided that she wanted to display a list of her latest AudioBoos in the sidebar of her blog. She looked at the AudioBoo JavaScript widget but decided it wasn't subtle enough and so she enlisted me to solve her problem.

It turns out that AudioBoo has an RSS feed, so a simple plugin was required. I had a quick look on the extension site, but most are now "widgets" which her theme isn't set up for or didn't provide an unsigned list. Hence, I whipped up a small extension for her.

It turns out that WordPress ships with MagpieRSS baked in, so the work to get the feed is trivial:

include_once(ABSPATH WPINC '/rss.php');
$messages fetch_rss($url);

The rest of the work is simply formatting the output. The key requirement that she had was that it should provide an unsigned list with title, date and optionally the summary. Along with providing some customisation for her, this is what I came up with:

function akrabat_simple_rss($options = array()) 
{
    $defaults = array(
        'url' => '', 
        'number_of_items' => 5,
        'display_date' => true,
        'date_format' => 'd M Y at H:i', 
        'display_summary' => true,
        'number_of_summary_chars' => 100,
        'link_on_title' => true,
        'link_on_date' => false,
        'css_class' => 'akrabat-simple-rss',
    );
    
    extract (array_merge($defaults$options));
    
    $output '';
    if (!empty($url)) {
        include_once(ABSPATH WPINC '/rss.php');
        $messages fetch_rss($url);
        if(count($messages->items) == 0){
            return '';
        }
        
        if($number_of_items count($messages->items)) {
            $number_of_items count($messages->items);
        }
    
        $output '<ul class="'.$css_class.'">';
        for($i 0$i $number_of_items$i++){
            $message $messages->items[$i];
        
            $link $message['link'];
            $title $message['title'];
            $date null;
            if (isset($message['published'])) {
                $date $message['published'];
            }
            if (!$date && isset($message['pubdate'])) {
                $date $message['pubdate'];
            }
            $summary null;
            if (isset($message['summary'])) {
                $summary $message['summary'];
            }
            if (!$summary && isset($message['description'])) {
                $summary $message['description'];
            }
            
            $output .= "<li>";
            $title_string htmlspecialchars($title_string);
            if ($link_on_title) {
                $output .= '<a href="'.$link.'">'.$title_string.'</a>';
            }
            $output .= '<div class="akrabat-rss-title">'.$title_string.'</div>';
            
            if ($date && $display_date) {
                $dateString date($date_formatstrtotime($date));
                if ($link_on_date) {
                    $dateString '<a href="'.$link.'">'.$dateString.'</a>';
                }
                $output .= '<div class="date">'.$dateString.'</div>';
            }
            if ($summary && $display_summary) {
                $summary_string substr($summary0$number_of_summary_chars);
                if (count(summary) > $number_of_summary_chars) {
                    $summary_string substr(summary_string0, -3) . '...';
                }
                $summary_string htmlspecialchars($summary_string);
                $output .= '<div class="summary">'.$summary_string.'</div>';
            }
            $output .= "</li>";
        }
        $output .= "</ul>";
    }
        
    return $output;
}

Maybe it's useful to someone else too, and I've documented it somewhere!

PHP 5.3 is quicker than PHP 5.2

6th December 2010

I know that everyone already knows this, but I happened to find out for myself recently!

I was looking at the way view helpers work in ZF2 and thought it would be more convenient if we altered the syntax a little. A side-effect of the change was that we'd have to use call_user_func_array, which is perceived as slow. I thought I'd whip up a simple test to find out how much slower it would be over a direct method call.

That is, how much slower is this code:

$a = new A();
call_user_func_array(array($a'b'), array(1));

than this code:

$a = new A();
$a->b(1);

Bear in mind that I don't do formal benchmarks and have no clue on methodology. Paul Jones is your man for proper testing of performance.

With that caveat out of the way this is the code I wrote to test:

benchmark.php

<?php

class A
{
    function b($a)
    {
        return;
    }
}

define ('ITERATIONS'10000000);
$start microtime(true);
$a = new A();
for ($i 0$i ITERATIONS; ++$i) {
   $a->b(1);
}
$stop microtime(true);
echo  'Direct method call: ' . ($stop $start) . ' seconds' PHP_EOL$start microtime(true);
for ($i 0$i ITERATIONS; ++$i) {
   call_user_func_array(array($a'b'), array(1));
}
$stop microtime(true);
echo  'call_user_func_array call: ' . ($stop $start) . ' seconds' PHP_EOL

This was the simplest possible scenario I could imagine and so would show call_user_func_array in the best possible light.

I ran the test on my laptop (using the command line) and got these results:


Direct method call21.155211925507 seconds
call_user_func_array call72.147792100906 seconds

i.e. call_user_func_array is around 3.5 times slower over 10 million iterations.

Nobody uses their laptop to serve a website though! Being the curious sort, I thought I'd test the difference of this script between PHP 5.2 and PHP 5.3 in a completely unscientific way as I'm lazy! I happen to have a server with both PHP 5.2 and PHP 5.3. on it, so I used that:

PHP 5.2:


Direct method call8.3424661159515 seconds
call_user_func_array call26.904649972916 seconds

PHP 5.3:


Direct method call3.35181903839 seconds
call_user_func_array call11.5989868641 seconds

As you can see, my server is much faster than my laptop :) Also, the relative difference is the same. i.e. PHP 5.3's call_user_func_array is as slow as it is in PHP 5.2.

I think I'll move everything to PHP 5.3 for the free performance gain! Of course, The question remains as to whether we should avoid call_user_func_array or not.

I would however recommend testing your own apps and see if it makes any difference and please don't take this post as anything other than something interesting I found out!

Some notes on SQL Server blobs with sqlsrv

22nd November 2010

I recently updated my use of SQL Server with Zend_Db_Adapter_Sqlsrv to use UTF-8 throughout. This turned out to be easy enough:

  • Use ntext, nvarchar types in the database
  • add:
    resources.db.params.driver_options.CharacterSet "UTF-8"
    

    to your application.ini

I subsequently noticed a problem with storing binary data to a varbinary(max) field. The error was:

An error occurred translating string for input param 2 to UCS-2: No mapping for the Unicode character exists in the target multi-byte code page.

urgh!

The code looked something like this:

$data['filename'] = 'test.gif';
$data["file_contents"] = $binaryData;
$db->insert($data);

Fortunately, my friend Elizabeth Smith, pointed me in the right direction by suggesting I find out about bindings. So I did some research and it turns out that I just need to use an array for the parameter that I pass into the update() or insert() method of Zend_Db_Adapter_Sqlsrv.

It turns out that all you need to do is change the 'file_contents' element of the array to an array that also specifies the data type. The code I ended up with now looks something like this:

$data['filename'] = 'test.gif';
$data["file_contents"] = $binaryData;
if ($adapter == 'Zend_Db_Adapter_Sqlsrv') {
    $data["file_contents"] = array($binaryDataSQLSRV_PARAM_IN, 
        SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY), SQLSRV_SQLTYPE_VARBINARY('max'));
}
$db->insert($data);

And all is fine.

The list of constants for the Sql Server Driver for PHP is helpfully available on MSDN and the documentation for sqlsrv_query() is worth reading too!