Archive for the ‘PHP’ Category

DumbledORM version 0.1 released!

Monday, December 20th, 2010

I’m happy to announce the release of DumbledORM 0.1. It’s a major revamp from the initial release. It’s still the same ~200 loc, but now chocked full of comments and under the MIT license.

A few noteworthy changes.

  1. PDO now throws exceptions by default. The standard default in PDO is to fail silently. This was unintended and thanks to @jimplush for reporting it. Be sure to try/catch your code appropriately.
  2. Db::execute() method added. All of the Db methods are publicly accessible and this method is for executing sql that doesn’t require a fetch (inserts, updates, deletes, etc). The requirement for this method became clear after turning on exceptions in PDO.
  3. Better docs. The docs in the README have been updated and reformatted.

DumbledORM received a lot of great feedback and attention after it’s release. Thank you for all the positive response to this little ORM. I hope it continues to grow in use and remains small in size. :)

Download DumbledORM and get started here:
https://github.com/jasonmoo/DumbledORM

MEMP: PHP 5.3 with FPM and nginx via MacPorts

Wednesday, December 1st, 2010

I’ve been considering diving into nginx for a bit now. Rumors of it’s speed and Cyrillic error messages have intrigued me. With recent forays into node.js apps and their requirement for a free port, I wanted to find a nice solution that complimented the evented speed of node.js without exposing a port publicly for each app. As it turns out Nginx is suited quite nicely to this. But that wasn’t where it ended. I found nginx to have a much more interesting and dynamic config language. And even beyond that it gave me a good excuse to learn more about running PHP as a FastCGI process and how PHP-FPM (baked in as of 5.3.3) fits into the picture. Not sure if MEMP is the correct acronym, but since I’ve seen talk of LEMP servers, I’ll run with it.

MacPorts. If you’re not already familiar with it, I recommend reading up before proceeding.

Since I already have MySQL set up and configured I won’t bother covering it here. It’s pretty well documented around the webs. The main focus here is going to be getting nginx installed and setting up PHP as a service to it with an emphasis on local development. No need to tweak this for production on my Macbook Pro.

PHP 5.3.3 introduced PHP-FPM (FastCGI Process Manager) to the codebase. Building it requires a few extra config flags and generates a php-fpm binary. The binary will manage spawning cgi processes and handling the FastCGI passthru from nginx. Currently MacPorts does not have an option to build PHP with PHP-FPM. After some hacking on the current Portfile I arrived at a working solution. It includes the correct flags, a dependency on libevent, and a startup item.

First things first.

sudo port selfupdate

# recommended but probably not a requirement
sudo port upgrade outdated
sudo port uninstall inactive

(If you’ve already got the php5 package installed, uninstall it and it’s cohorts to ensure a clean install process going forward.)

This is a bit of a hack and there’s probably a better way, like submitting a proper patch to MacPorts, but this will get the job done for today.

# replace this file with the Portfile from the gist below
/opt/local/var/macports/sources/rsync.macports.org/release/ports/lang/php5/Portfile

https://gist.github.com/724449

Then run the regular port install.

sudo port install nginx php5 +fastcgi php5-apc php5-mysql +mysqlnd

Next we’ll need to set up the configs. There’s two basic configs that require attention. First is the nginx config. Since this is just for a local development env we can be pretty lean on what we need.

# /opt/local/etc/nginx/nginx.conf

worker_processes  1;

error_log  /var/log/nginx/error.log;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile           on;
    keepalive_timeout  65;

    server {
      listen 80;
      server_name   ~^local\.(?.+?)\.com$;
      root  /Users/jason/Sites/$site/web;
      index  index.php index.html;

      location = /favicon.ico {
        log_not_found off;
      }

      location ~ \.php$ {
         fastcgi_pass   unix:/tmp/php-fpm.sock;
         fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
         include        fastcgi_params;
      }
    }
}

This is the config that I’m currently using as my generic site config. It has a few nice features. First the server name is matched against a regular expression. So any site that matches local.xxxx.com will be parsed and use the xxxx as a sub-directory in my Sites folder. This is nice. So I only need to create a new directory and add my local.xxxx.com domain to /etc/hosts pointed at 127.0.0.1 and I can immediately begin developing. Gone are the days of creating a new v-hosts file, enabling it, and restarting apache every time I want to play with a new site’s code. The other nicety here is we’re using a unix socket for the fastcgi passthru. This is unnoticeably faster on my Mac, but again frees up the need to be concerned with another open port on my system.

It’s worth noting that any fastcgi_param that you define in this config is available within PHP as a $_SERVER variable.

The second config is the FPM config. Copy /opt/local/etc/php-fpm.conf.default to /opt/local/etc/php-fpm.conf and open it up. Here are the important ones to modify:

pid = /opt/local/var/run/php-fpm.pid
error_log = /opt/local/var/log/php-fpm.log
listen = /tmp/php-fpm.sock
listen.owner = _www
listen.group = _www
pm.max_children = 1
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 1
pm.max_requests = 500
slowlog = /opt/local/var/log/php-fpm.log.slow

It’s pretty straight-forward. Lean cus we can. Save and continue.

At this point we’ve got all the pieces in place. We just need a way to start/stop/restart nginx and FPM. I looked for a cleaner way to accomplish this but in the end it came down to a handful of aliases. Add these to your ~/.profile.

# nginx
alias nginx_start='sudo launchctl load /Library/LaunchDaemons/org.macports.nginx.plist' 
alias nginx_stop='sudo launchctl unload /Library/LaunchDaemons/org.macports.nginx.plist' 
alias nginx_restart='nginx_stop; nginx_start;' 

# php-fpm
alias fpm_start='sudo launchctl load /Library/LaunchDaemons/org.macports.php-fpm.plist'
alias fpm_stop='sudo launchctl unload /Library/LaunchDaemons/org.macports.php-fpm.plist'
alias fpm_restart='fpm_stop; fpm_start'

Reopen the Terminal window to enable the new aliases. Start or restart nginx and FPM as the case may be.

At this point you should be able to create a site directory, add a domain to /etc/hosts and drop in an index.php with phpinfo(); to verify that PHP is working.

Hopefully this will be of some use to other aspiring MEMP devs out there. I found it intensely fun. :)

Introducing DumbledORM a novelty ORM

Thursday, October 28th, 2010

I’m proud to release DumbledORM today. It is the product of a late night hack sesh with an idea to build a PHP ORM in less than 100 lines. After it’s all said and done I arrived at 200 lines with lots of features. The decision to call it DumbledORM came after realizing there was more casting and magic in it than anything out there. :)

Some examples of what it can do:

// load record with id 13
$user = new User(13);  
$user->setName('Jason')->save();

// create new record
$user = new User(array(
  'name' => 'Jason', 
  'email' => 'jasonmoo@me.com', 
));
$user->save();

// find single record
User::one(array('name' => 'Jason'))->delete();

// apply object methods to entire set at once
PhoneNumber::select('`number` like "607%"')
  ->setLocation('Ithaca, NY')
  ->save();

// relations between objects
$user->create(new PhoneNumber(array(
  'type' => 'home', 
  'number' => '607-333-2840', 
)))->save();

// update all user phone numbers matching $type
$user->getPhoneNumber('`type` = ?',$type)
  ->setType($new_type)
  ->save();

Applying model object methods to entire sets of objects, jQuery style, is made possible by this incantation:

final class ResultSet extends ArrayIterator {
  public function __call($method,$params=array()) {
    foreach ($this as $obj) {
      call_user_func_array(array($obj,$method),$params);
    }
    return $this;
  }
}

So proud. I hope you find it fun and maybe even useful. Although at this point all I can say is that it’s passing it’s little tests.

Constant time string comparison

Friday, October 22nd, 2010

I recently spotted @fabpot‘s tweet about his Symfony2 commit to prevent timing attacks. It’s a pretty simple change. Basically it just makes every password string comparison run through every character no matter if the match fails on any character. I don’t use bitwise operators much so it took a sec to spot how it works.

The comparison code is:

$result = 0;
for ($i = 0; $i < strlen($password1); $i++) {
  $result |= ord($password1[$i]) ^ ord($password2[$i]);
}
return 0 === $result;

The key components are:

ord()  // translate a character into it's ASCII value (an 8 bit integer)
^       // bitwise xor
|=     // short for left assign value bitwise or'd with new value

Just for clarity, bitwise xor evaluates to 1 for 1/0 or 0/1 or 0/0 but evaluates to 0 for 1/1. So when comparing two integers by xor'ing the bits a perfect match will evaluate to 0. Anything but an exact match will evaluate to 1.

01110000 xor 
01110000
00000000  // match

01110011 xor 
01110010
00000011  // nope

This means that if we xor each integer value of each character and or the produced values together it will evaluate to 0 when the two strings match. And this is always accomplished in string length time.

00000000  or
00000000  or
...
00000000
00000000  // match

As an aside, bitwise operators can do some neat stuff. This one is a solution that my Pascal instructor in college mentioned as a fast cheap way to swap screen buffers. In this example we're just swapping variable values without an intermediate.

$a = 'a';
$b = 'b';

$a ^= $b; 
$b ^= $a; 
$a ^= $b;

echo $a;  // b
echo $b;  // a

PHP cast as array

Tuesday, July 27th, 2010

PHP is full of shenanigans.  If you know them you can code more efficiently.  If you don’t it can make for a mess.  One of the lesser used, but useful tricks is casting as array.

If you’re not familiar with casting, check the docs:  http://php.net/manual/en/language.types.type-juggling.php

Here’s a quick breakdown of what happens when you cast to array:

php > var_dump((array)false);
array(1) {
  [0]=>
  bool(false)
}
php > var_dump((array)null);
array(0) {
}
php > var_dump((array)0);
array(1) {
  [0]=>
  int(0)
}
php > var_dump((array)"");
array(1) {
  [0]=>
  string(0) ""
}
php > var_dump((array)array());
array(0) {
}
php > var_dump((array)new StdClass);
array(0) {
}
php > class TestIterator implements IteratorAggregate {
php >   public function getIterator() {
php >     return new ArrayIterator(array(1,2,3));
php >   }
php > }
php > $a = new TestIterator;
php > var_dump((array)$a);
array(0) {
}
php > foreach ($a as $num) { echo $num,"\n"; }
1
2
3

The interesting bits here are that null, empty array and empty object all cast to empty array. Even an Iterator. Since the Iterator object is empty is casts to an empty array, even though iterating through it will produce 3 elements. Any other value casts to an array with a single element that is the value.

There’s a couple use cases I’d like to highlight from this list. Flexibility in signature params, and easier iteration:

function string_replace($string,$elements) {
  foreach((array)$elements as $element) {
    $pos = strpos($string,'?');
    if ($pos !== false) {
      $string = substr_replace($string,$element,$pos,1);
    }
  }
  return $string;
}

This is just a simple function for replacing question marks in a string with supplied string(s). By casting $elements as an array we can take a single param or an array of params without checking for it in our code. We can also passively return the string untouched if null params are supplied.

php > echo string_replace('the name is ?','jason');
the name is jason
php > echo string_replace('? ? ? at swingers',array('in','the','evening'));
in the evening at swingers