Archive

Php

Since as long as I have used Php, I have carried around some code that I use for quick shell scripts and the occasional data mangling. It’s not particularly great code, in fact I would say its pretty bad but it was never really used for anything other than the occasional quick hack (I use Propel, RedBean, or PDO for other Php projects). I have been using this code copy+paste style for over 5 years and it has remain mostly unchanged:

if (!$link = @mysql_connect('localhost', 'user', 'pass')) {
  die('Could not connect: ' . mysql_error());
}

@mysql_select_db('database', $link) or die('Could not select database.');

function query($query) {
  $rows = array();
  $result = @mysql_query($query);
  if ($result && mysql_num_rows($result)) {
    while ($row = mysql_fetch_assoc($result)) {
      $rows[] = $row;
    }
  }
  return $rows;
}

foreach(query("select * from foo") as $row => $record) {

  // do stuff with $record

}

Yeah… I can see you cringing right now. But for banging out the occasional script it usually worked (as long as the result fit into memory). You may also notice the use of error suppression with @. That’s because I’ve been using this code for so long that the mysql_* functions have been deprecated (as they should be) and they will throw warnings on newer versions of Php when you use them… ouch.

As we all know too well, sometimes our little hacks and kludges work so well that they end up in production, get scheduled as cronjobs, get rolled into internal web pages and the like. Recently this code put me in a bit of a bind. It had found it’s way into a very complex data-mangling cronjob that was needed to generate a table which was used for reporting. The table stopped being generated when the result set had grown too large to fit into memory and the script was crashing.

I realized I had to fix this script but the query function and foreach loops were peppered throughout the code, sometimes nested, sometimes used in recursive functions. I didn’t have time to re-write a large portion of the code so I came up with this backwards compatible version which limited the amount of re-writing I needed to do. I could use it in such a way that when I knew the result set would be small I could use the old in-memory query (no changes were needed), but when I knew it would be large result set, I could use a callback that was called for each row.

function query($query, $callback = FALSE) {
  $rows = array();
  $result = @mysql_query($query);
  if ($result && mysql_num_rows($result)) {
    while ($row = mysql_fetch_assoc($result)) {
      if ($callback === FALSE) {
        $rows[] = $row;
      } else {
        call_user_func($callback, $row);
      }
    }
  }
  return $rows;
}

// new use, with callback
query("select * from foo", function ($record) {

  // do stuff with each $record

});

I realize this code is STILL not ideal, but it is a backwards compatible fix and got the job done.

Advertisements

Today I decided to try out  memcached for php sessions instead of files.

Memcached is a server, that runs on port 11211 (by default). By installing a Php extensions and configuring php.ini you can save session data in RAM instead of on disk. 

First, you will need to grab some dependencies:

yum install memcached php-pecl-memcached phpMemcachedAdmin

You will need the EPEL repo to install the php extension and the option web interface to memcached.

Start up memcached and enable on boot:

service memcached start
chkconfig memcached on

Give yourself access to phpMemcachedAdmin by editing /etc/httpd/conf.d/phpMemcachedAdmin.conf:

<IfModule !mod_authz_core.c>
    # Apache 2.2
    Order Deny,Allow
    Deny from All 
    Allow from 127.0.0.1
    Allow from <YOUR IP HERE>
    Allow from ::1 
 </IfModule>

You can access the interface at http://<SERVER IP>/phpMemcachedAdmin

Update the following in php.ini:

session.save_handler = memcached
session.save_path = "127.0.0.1:11211"

Restart apache and you should be all set

service httpd restart

Watch the phpMemcachedAdmin interface as your application starts using sessions.

Now to run some benchmarks and see if I get any performance gains, if not than at least I could use memcached to host sessions for multiple app servers.