Archive for December, 2010

Autoload PhpThumb with Zend Framework

Here’s an easy way to autoload PhpThumb (an excellent & fast image resizing / manipulating PHP library) using Zend Framework without having to modify the PhpThumb source at all. First we need to make our own custom autoloader:

class M_Loader_Autoloader_PhpThumb implements Zend_Loader_Autoloader_Interface {

   static protected $php_thumb_classes = array(
      'PhpThumb'        => 'PhpThumb.inc.php',
      'ThumbBase'       => 'ThumbBase.inc.php',
      'PhpThumbFactory' => 'ThumbLib.inc.php',
      'GdThumb'         => 'GdThumb.inc.php',
      'GdReflectionLib' => 'thumb_plugins/gd_reflection.inc.php',
   );

  /**
   * Autoload a class
   *
   * @param   string $class
   * @return  mixed
   *          False [if unable to load $class]
   *          get_class($class) [if $class is successfully loaded]
   */
   public function autoload($class) {
      $file = APPLICATION_PATH . '/../library/PhpThumb/' . self::$php_thumb_classes[$class];
      if (is_file($file)) {
         require_once($file);
         return $class;
      }
      return false;
   }
}

Then simply put this in your Bootstrap:

Zend_Loader_Autoloader::getInstance()->pushAutoloader(new M_Loader_Autoloader_PhpThumb());

Done. Now you can use PhpThumb in your controllers like this:

class PhotoController extends Zend_Controller_Action {
   public function indexAction() {
      $this->_helper->layout()->disableLayout();
      $this->_helper->viewRenderer->setNoRender(true);
      $thumb = PhpThumbFactory::create("/path/to/image.jpg");
      $thumb->adaptiveResize(250, 250);
      $thumb->show();
   }
}

Script to generate command to push MySQL database changes between enviornments

Got sick of manually doing database dumps between our production, test, and development environments, so I just wrote a very handy script using Zend Framework to generate a command to push changes between environments as you make them.

You will have to tweak ssh_to to suit your needs (our test and production dbs are not separated at the moment, so that removes a layer of complexity for us), but if you have everything setup in application.ini like most people do, this should work almost out of box. You’ll need to setup the autoloader on your own.

usage: php push_db.php --to=dev --from=prod

Enjoy!

#!/usr/bin/env php5
<?php
require_once(getcwd() . '/autoload.php');

$opts = new Zend_Console_Getopt(
   array(
      'from=s'    => 'From environment, with required string parameter',
      'to=s'        => 'To environment, with required string parameter',
   )
);

$from = $opts->getOption('from');
$to = $opts->getOption('to');

foreach (array('from','to') as $env) {
      if (!in_array($$env, array('production','prod','testing','test','development','dev'))) {
         die('Error: invalid parameter ' . $env . " = '{$$env}'" . PHP_EOL);
      }
}

$prod_env = new Zend_Config_Ini(APPLICATION_PATH . '/configs/application.ini', 'production');
$test_env = new Zend_Config_Ini(APPLICATION_PATH . '/configs/application.ini', 'testing');
$dev_env = new Zend_Config_Ini(APPLICATION_PATH . '/configs/application.ini', 'development');

$to_server = array();
$from_server = array();
foreach (array('from','to') as $serv) {
   $server = $serv . '_server';
   switch ($$serv) {
      case 'production':
      case 'prod':
         ${$server}['host'] =                   $prod_env->database->params->host;
         ${$server}['username'] =                   $prod_env->database->params->username;
         ${$server}['password'] =                   $prod_env->database->params->password;
         ${$server}['dbname'] =                   $prod_env->database->params->dbname;
         ${$server}['ssh_to'] = 'me@foobar';
         break;
      case 'testing':
      case 'test':
         ${$server}['host'] = $test_env->database->params->host;
         ${$server}['username'] =                   $test_env->database->params->username;
         ${$server}['password'] =                   $test_env->database->params->password;
         ${$server}['dbname'] =                   $test_env->database->params->dbname;
         ${$server}['ssh_to'] = 'me@foobar';
         break;
      case 'development':
      case 'dev':
         ${$server}['host'] = $dev_env->database->params->host;
         ${$server}['username'] =                   $dev_env->database->params->username;
         ${$server}['password'] =                   $dev_env->database->params->password;
         ${$server}['dbname'] =                   $dev_env->database->params->dbname;
         ${$server}['ssh_to'] = 'me@foobar';
         break;
      }
}

print "Run this command on " . $from . ':' . PHP_EOL;
echo 'mysqldump -u ' . $from_server['username'] . ' -p' . $from_server['password'] . ' ' . $from_server['dbname']
. ' | ssh ' . $from_server['ssh_to'] . ' "mysql -u ' . $to_server['username'] . ' -p' . $to_server['password'] . ' ' . $to_server['dbname'] . '"' . PHP_EOL;


Spotadoink

As small after-work project inspired by FitFinder (which, unfortunately, has since been taken down per the demands of the UCL), several months ago Montmere Limited launched spotadoink, the 21st century catalyst to flirting.

It was hastily thrown together in just a few short hours, but the concept is fun: when you see someone who is attractive, tell the world where!

Unfortunately it has not seen much use since its launch, but as with anything else, who knows what the future holds.

Facebook Developer Subscriptions – Part 1

So Facebook has an API.  It’s pretty full featured although the documentation seems to be a little on the weak side.

After a little glancing, I thought “I can do this” and followed the sample php code to create a “Login” page for my test site.  I then spent all kinds of time and notebook paper coming up with an elaborate series of cron jobs to use the Access Token to make requests on the user’s behalf so I can periodically check for updates while they’re away.

Then I read the manual.

Facebook offers a subscription service via Real-Time Updates where you can request to be notified of certain changes as they happen.  Facebook doesn’t actually tell you what the changes are, but they notify you that a certain type of change has occurred (new friends, activities, etc.) Cool.

Great.  So all I need to do is get a user authenticated to my application using some example code from the php SDK, let Facebook know what kinds of updates I’m interested in, and set up an endpoint to get that data.  Easy enough, right? Ha.

So the way the Facebook API works is that you request a user’s data using their Access Token (that the developer gets when the user allows a certain application access).  The main problem with using this vanilla approach with subscriptions is that the access token given when using the example code in the php SDK expires after a little while.  Well, I want to get user updates all the time so I need a token that doesn’t expire.  This requires using Extended Permissions.  A quick look through the list tells me I need the “offline_access” permission.

Unfortunately, this is where I realized that the example code in the php SDK starts to lack.  The login URL built into the SDK $facebook->getLoginURL() doesn’t allow you add your requested permission in.  We’ll need to take a few extra steps to get what we’re looking for.

First, we need to construct our own login URL. The format will look something like this:

https://graph.facebook.com/oauth/authorize/client_id={your application ID}&redirect_URL={where to send the user after they do OR don’t authenticate}&scope=offline_access

Using this in place of the function call will cause the user to see something like this when they click on “Login”:

Request for Permissions

When your user clicks “Allow”, they will be redirected back to your redirect_url with an additional parameter called “code”.  Our neverending access token! Well, almost.  Now we need to make another request, server side time, to Facebook to turn this code into an actual access token.  This is done by requesting from a URL like this:

https://graph.facebook.com/oauth/access_token/client_id={your application ID}&redirect_URL={where to send the user after they do OR don’t authenticate}&client_secret={your secret code made when setting up developer account}&code={the code you just got in your return URL from Facebook}

Getting this URL returns the following data:

access_token=178416385509226|bbf513b2bf0c667340e80b40-15123454|9-ha-O6KUwHVtY51PtNxUR_Fkz4

So you’re application will have to parse our your actual access token from the string, but that should be a good start.

Update: after playing with this for a little bit, I found that the facebook session will return the access_token with extended permissions to you when your user is redirected back to your page.  This means you can do something like access $facebook->session->[‘access_token’] and save that instead of having to use curl for that second URL.

Next up: setting up your subscriptions and endpoint.

The ANTI JOIN – all values from table1 where not in table2

One of the less intuitive concepts I come across regularly in SQL is that of the ANTI JOIN. That is, requesting data from a table where some value is not in another table.

The wrong way of doing it is this:

SELECT
*
FROM table1 t1
WHERE t1.id NOT IN (SELECT id FROM table2)

This is very slow and inefficient, because the RDBMS has to compare each row with an entire table of values. The following is much much faster, as it matches up each row once, then simply eliminates the results that are not in the second table:

SELECT
*
FROM table1 t1
LEFT JOIN table2 t2 ON t1.id = t2.id
WHERE t2.id IS NULL

This is what is known as an ANTI JOIN.. And I really wish it were part of standard SQL.. Be careful, though,if table2.id has any NULL rows, you will experience unexpected results. Here’s my proposal, and the following should throw a fatal error if table2.id is not a NOT NULL column:

SELECT
*
FROM table1 t1
ANTI JOIN table2 t2 ON t1.id = t2.id

Too bad I don’t make the rules.

Get the average value from the nearest date in MS SQL

Needed to get the average exchange rate for the nearest invoice to a given date, and I found a quick and easy way to accomplish this in SQL Server:

SELECT TOP 1
   AVG(i.exchrate) / 100 AS exchrate
   , MIN(DATEDIFF(DAY, '" . $date . "', i.invoicedate)) AS accuracy
FROM InvoiceTable i
WHERE
   i.currencycode = '" . $currency_code . "'
   AND DATEDIFF(DAY, '" . $date . "', i.invoicedate) &gt; 0
ORDER BY MIN(DATEDIFF(DAY, '" . $date . "', i.invoicedate))