One man's voice Thoughts, rants and commentary of a simple man

4Apr/099

Getting books details by ISBN in PHP

Sometimes I love being a geek. Today, as I set out to inventory my collection of almost 500 books or so, I wanted to find a fast way to get the information on the book I wanted based on the books ISBN. This was because I was already getting tired of typing five books into the inventory (yes, I am that lazy) and I really did not want to keep typing out the authors name, the title and the subtitle for each book.

So I went to ISBNdb.com and entered an ISBN thinking I would be able to just copy and paste the book information from the output. That turned out to be overwhelmingly difficult since their output of the search is really convoluted. I knew what I wanted to get and they didn't offer that.

But they did offer an API and, after reading their docs, I realized that I could write a script that would take an ISBN and return to me the information I was looking for in a way that I could just copy and paste it. Remarkably, it was faster and easier than I thought it would be. I actually developed this little snippet, freely available for you here, in about 5 minutes. Hope it helps you in some way, if you are looking for such a thing.

NOTE: Before using this code make sure to sign up for your own API key. In order to do that you will need to register for an account with ISBNdb.com and then create a key. But as soon as you do you will have immediate access to their API. The API is simple, responding to the request with a simple XML output. Anyhow, without further ado, here is the little script I put together to fetch me the data I was looking for the way I wanted it.

<?php
/**
 * Set this value to your own API key
 */
$apikey = '12345678';
 
/**
 * Initialize this var for use when forms are not posted
 */
$isbn = null;
 
/**
 * Initialize the result set var
 */
$rs = null;
 
/**
 * See if the form is posted
 */
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  /**
   * Get the ISBN from the form
   * 
   * No, there is no validation on this. This 
   * was for me and I knew I would not be at
   * all trying to trick my own script. USE 
   * THIS AS IS AT YOUR OWN PERIL.
   */
  $isbn = $_POST['isbn'];
 
  /**
   * Get the result as a SimpleXML object
   */
  $rs = simplexml_load_file("http://isbndb.com/api/books.xml?access_key=$apikey&index1=isbn&value1=$isbn");
}
?>
<html>
<head><title>ISBN Check</title></head>
<body>
<form method="post" action="<?php echo basename(__FILE__) ?>">
  <p>
    Enter an ISBN:<br />
    <input type="text" size="40" name="isbn" id="isbn" value="<?php echo $isbn ?>" />
  </p>
  <p>
    <input type="submit" name="submit" value="Search ISBN" />
  </p>
</form>
<?php
/**
 * Only output more stuff if there is a result
 */
if ($rs) : ?>
<hr />
Results for ISBN: <?php echo $isbn ?>:
<?php 
/**
 * Results for a book are in $rs->BookList->BookData
 * 
 * You can see this using var_dump($rs);
 */ 
$book = $rs->BookList->BookData;
?>
<p>
  <strong><?php echo $book->Title; ?></strong><br />
  <?php echo !empty($book->TitleLong) ? $book->TitleLong . '<br />' : null; ?>
  <?php echo $book->AuthorsText; ?><br />
  <?php echo $book->PublisherText; ?><br />
  <?php echo $isbn; ?>
</p>
<?php endif; ?>
</body>
</html>

Typical output might look like (from one of the books I was checking on):


Results for ISBN: 0316116955:

The day the universe changed
James Burke
Boston : Little, Brown, c1985.
0316116955


I hope you enjoy this, if you happen to be looking for an ISBN search tool that you can run on your own. ;)

3Apr/090

A small cache routine in PHP

It has been a long time since I have posted anything PHP related so I decided that to end the week I would post something related to caching data to a file. File based cache's are probably not the sexiest thing around and more than likely not the best way to do things. But in my case I needed something light and small to house a small data set from a database server that was taking upwards of 35 seconds to return 20 rows of data.

I also only needed this cache in one place so in my original code I have it running a small procedural routine within a method. But I figured I would extend that process to be a little more flexible so I objectified it. Here is what I came up with:

NOTES:

  • The backend data is coming from a database in my sample. It could legitimately come from any place that takes a long time to retrieve though.
  • I am using a MySQL database backend in this example, driven by PDO
  • The database handle is assumed to have been created and is retrieved from a registry object in the example
  • The directory where the cache file will live will need to be writable by the web server
  • There is very little error handling in this code. I will leave that up to you.
  • Some of what is being done is tightly coupled internally and requires overwrites to change (path, filename, etc)
  • I have provided precious few examples.
<?php
/**
 * Small abstract class that handles the non-specifics of the cache routine
 */
abstract class SmallCache {
  /**
   * Directory path to the cache file 
   * 
   * @access protected
   * @var string
   */
  protected $_cachePath;
 
  /**
   * Name of the cache file
   * 
   * @access protected
   * @var string
   */
  protected $_cacheFile;
 
  /**
   * Flag that checks to see if the file is writable
   * 
   * @access protected
   * @var boolean
   */
  protected $_cacheFileWritable = true;
 
  /**
   * Default expiration time of the cache
   * 
   * @access protected
   * @var int
   */
  protected $_cacheExpiry = 3600;
 
  /**
   * Right now
   * 
   * @access protected
   * @var int
   */
  protected $_currentTime;
 
  /**
   * Object constructor
   * 
   * @access public
   * @param string $cacheFile Name of the file to write to
   * @param string $cachePath Path to the directory where the file will live
   * @param int $cacheExpiry Time in seconds the cache should stay alive
   */
  public function __construct($cacheFile = null, $cachePath = null, $cacheExpiry = null) {
    $this->setPath($cachePath);
    $this->setFile($cacheFile);
    $this->setExpiry($cacheExpiry);
    $this->_currentTime = time();
  }
 
  /**
   * Abstracted method that each child should implement
   */
  abstract public function fetch();
 
  /**
   * Simple sets a path if one is given
   * 
   * NOTE: The path should end with a slash 
   * 
   * @access public
   * @param string $path The path to the directory where the cache files live
   */
  public function setPath($path) {
    $this->_cachePath = $path ? 
      rtrim($path, '\/') . DIRECTORY_SEPARATOR : 
      realpath('.') . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
  }
 
  /**
   * Sets the cache file name
   * 
   * @access public
   * @param string $file Name of the file to write and read the cache
   * @param boolean $useCachePath If true, will prepend the file name with the 
   *                              cache path
   */
  public function setFile($file, $useCachePath = false) {
    // If there is a file name given...
    if ($file) {
      // Prepend the path if it is being asked for
      if ($useCachePath) {
        $file = $this->_cachePath . $file;
      }
 
      // Set the name
      $this->_cacheFile = $file;
    } else {
      // Build a name
      $this->_cacheFile = $this->_cachePath . get_class($this) . '-cache-file.php';
    }
  }
 
  /**
   * Sets the cache file expiration time duration
   * 
   * @access public
   * @param int $expiry Time in seconds to keep the cache before dumping and 
   *                    starting over
   */
  public function setExpiry($expiry) {
    if ($expiry !== null) {
      $this->_cacheExpiry = intval($cacheExpiry);
    }
  }
 
  /**
   * Gets the current contents if there is one
   * 
   * @access public
   * @return mixed Null if there is none, otherwise whatever is in the cache
   */
  public function get() {
    // See if we need to make the temp file
    if (!file_exists($this->_cacheFile)) {
      // This will simply make an empty file for writing. Maybe.
      if (!touch($this->_cacheFile)) {
        $this->_cacheFileWritable = false;
      }
 
      // Send back something to be used by the child
      return null;
    } else {
      // Get the cached data
      return include_once $this->_cacheFile;
    }
  }
 
  /**
   * Writes data to the cache file
   * 
   * @access public 
   * @param string $data The contents of the cache file as a string
   */
  public function put($data) {
    // Write it now if we can
    if ($this->_cacheFileWritable) {
      $fh = fopen($this->_cacheFile, 'w');
      fwrite($fh, $data);
      fclose($fh);
    }
  }
}
 
// Simple sample implementation
class SlowQueryCache extends SmallCache {
  public function fetch() {
    // Get the cache data
    $filedata = $this->get();
 
    // Now see if we have a cache and if the cache is young enough, if not, make the cache and use the data now
    if ( ($filedata && $this->_currentTime - $filedata['timestamp'] > $this->_cacheExpiry) || !$filedata) {
      // Set the query - pretend it is a MySQL query
      $sql = 'call some_proc_that_might_take_long()';
 
      /** 
       * Get the result set
       * 
       * In our case we are pretending to use PDO - substitute your own DB
       * handler code here
       * 
       * We are also pretending to get our DB handle from a registry in 
       * our library
       */
      $db = Libregistry::getInstance()->get('objectdbmysql');
 
      /** 
       * Call the query
       * 
       * I am leaving error handling up to you.
       */
      $rs = $db->query($sql);
 
      // Fileinfo is what is going to go into the file
      $fileinfo  = "<?php\n";
      $fileinfo .= "\$data = array(\n\t'timestamp' => $this->_currentTime,\n\t'rows' => array(\n";
 
      // File data is what would normally come from the cache
      $filedata['timestamp'] = $this->_currentTime;
      $filedata['rows'] = array(); // Clears out any old crap
 
      /**
       * Loop and build
       * 
       * This will be entirely dependent on your data structure and how 
       * you expect to access the data from within the cache
       */ 
      while ($row = $rs->fetch()) {
        $fileinfo .= "\t\t'$row[id]' => array('name' => '$row[name]', 'age' => '$row[age]'),\n";
        $filedata['rows'][$row['id']] = array('name' => $row['name'], 'age' => $row['age']);
      }
      $fileinfo .= "\t),\n);\nreturn \$data;";
 
      // Write it now if we can
      $this->put($fileinfo);
    }
 
    // Send back the data we have
    return $filedata;
  }
}
 
// Tests
$cache = new SlowQueryCache('cache/slowquery.php');
$data = $cache->fetch();
var_dump($data);
?>

Basically what this does is check to see if there is a cache file. If there is, it gets it and reads it and looks at the time written into the cache file to compare the current time minus the cache time against the expiry limit. If the cache is expired, the data is collected from the database, a result array is build and the result array is then written, as a string of PHP code to the cache file while the array data is returned.

If the cache has not yet expired then the original cache data is returned.

Keep in mind that this is still very tailored to my personal needs. But it should be easily enough transitioned to something more useful with a little tweaking. And yes, I know that this can be done in a better, more robust and sexier way. But this worked for me and I figured I'd share it in case it might work for you.

19Mar/090

Microsoft Expression Web Super Preview

I just read an article on ZDNet that mentioned a recent "showing off" of Expression Web SuperPreview by Microsoft. SuperPreview allows web developers to view and debug web output and rendering as it would appear in Internet Explorer 6, 7 and 8. And I can't help but wonder... why would we need a tool to check rendering if Microsoft actually handled rendering properly to begin with?

I am glad they have introduced this tool. Before this you had to install an application that would literally install multiple versions of IE on your machine. Then you would need to load your page in a new instance of whatever version you wanted to test. That really sucked. So having the option to inspect your rendering in one app across multiple versions of IE is definitely a boon. But still, why should you have to do that?

And speaking of browsers, a friend of mine the a few weeks back brought up a point that I still enjoy... why are we still coding for browser based output anyway? The web as we know it is totally breaking out of the browser. Why are there still cross-browser compatibility issues and rendering problems?

While I applaud Microsoft on their efforts to help developers code for their broken, crappy, waste-of-software browser, I think I am leaning more towards my friend's way of thinking. Maybe it is time to start thinking outside the browser box?

Regardless, if you are a developer developing on Windows and you need a tool that will help you see how things are going to work for you in IE 6, 7 and 8, this might be the tool for you.

4Mar/0922

PHP operators: double and single arrow

Someone recently asked a question on the Professional PHP Programmers Google Group that I thought was interesting. This question interested me for a few reasons:

  1. The PHP manual is a very good manual and usually covers things like this; and,
  2. I remember asking this very question when I first started developing.

Hi all, I’m new to the mailing list, and new to php, and new on programming.

Here I go:

I have a question but I was unable to find an answer for this on the web. Because I have no Idea how to ask this on a search engine.

What’s the meaning of -> and => on php?

As good as the PHP manual is, and as well documented as the operators in PHP are, the documentation on these two operators is lacking severely. Even naming is horribly lacking for these two operators, as a recent Twitter chat shows:

More on the operators used in PHP can be found at http://us3.php.net/manual/en/language.operators.php. Unfortunately, these operators will not be found there. So to assist in explaining what these operators are and what they do, lets dig a little deeper in the world of cryptic symbols that remain unnamed in PHP. Shall we?

The double arrow operator, "=>", is used as an access mechanism for arrays. This means that what is on the left side of it will have a corresponding value of what is on the right side of it in array context. This can be used to set values of any acceptable type into a corresponding index of an array. The index can be associative (string based) or numeric. So if I have an array like:

<?php
$myArray = array('Big', 'Small', 'Up', 'Down');
?>

Then in effect the code is saying:

<?php
$myArray = array(
  0 => 'Big',
  1 => 'Small',
  2 => 'Up',
  3 => 'Down'
);
?>

This is not a real surprise seeing as arrays in PHP are numerically indexed, zero-based arrays by default. But what if you wanted a hash of keys and values? Like when passing something as a form or in a querystring or reading results from a database? Well then, you would want to associate a key to a value in the array, or something like:

<?php
$myArray = array(
  'Robert' => 'Big',
  'Bobby' => 'Smart',
  'PHP' => 'Rocks'
);
?>

Now you have a hash of values assigned to indeces using the double arrow, or array value assignment, operator ("=>").

The object operator, "->", is used in object scope to access methods and properties of an object. It's meaning is to say that what is on the right of the operator is a member of the object instantiated into the variable on the left side of the operator. Instantiated is the key term here. We'll talk about that a little more in just a bit.

If you come from a background in object oriented programming then you know that every object has a way to access the methods and properties of the instantiated object. In most languages that is the dot operator ("."). In PHP the dot operator has completely different meaning (it is used to concatenate strings) so don't use that for object scope resolution. Instead, after instantiating an object, access its methods and properties using the object operator, like this:

<?php
$obj = new MyObject(); // Create a new instance of MyObject into $obj
$obj->thisProperty = 'Fred'; // Set a property in the $obj object called thisProperty
$obj->getProperty(); // Call a method of the $obj object named getProperty
?>

Now, getting back to object basics, remember I said that instantiated was a key term? The reason for that term is because there is another operator related to scope resolution than can be used but it is neither an arrow nor can it be used on objects. It can only be used on classes (this is as of PHP 5 - since PHP4 has already been fed to the dogs we are only going to talk about PHP 5). Class scope resolution is identified by the Paamayim Nekudotayim, or double colon, operator ("::").

If you use a static class or static methods or properties within a class, you would not use the object operator ("->") but would instead use the scope resolution, or double colon, operator, like so:

<?php
MyClass::$staticProperty = 'Fred'; // Sets a values into the static class
MyClass::getStaticProperty(); // Gets a value from a static method
?>

Now that you have the power of operators at your fingertips, even if in a very basic sense, you can get busy with assigning and accessing goodies in arrays, objects and classes. And remember as you use these that nothing at all can replace the learning you would get from trying them out, especially when you try to use them the wrong way (error messages can be quite educational).

If I could leave you with one bit of advice it would be to get your hands dirty on some of them and see what the parser tells you when it chokes on one (or more) of them.

And have fun. Stepping in to object oriented programming can be a frustrating, painful experience for some. But it is definitely a rewarding one.

18Feb/0924

Building the PHP MS SQL Server extension from source on Ubuntu 8.10

Yesterday I wrote about how I got an enhanced Sybase CT extension for PHP built on Ubuntu 8.10 from source files and from Red Hat RPM archives. Today I wanted to write about installing the SQL Server extension from source on Ubuntu 8.10.

I have written about this once before but it was for a Red Hat environment. Seeing as we are converting all of our system at work to Ubuntu I had to do the same thing for the Ubuntu platform. And wouldn't you know it, Debian and Red Hat are vastly different from each other.

Aside from the stupidity that is the Ubuntu package manager's rendition of the SQL Server extension for PHP the fact remained for me that I had to be able to integrate database communication from LAMP to a SQL Server on Windows as well to a Sybase server on Unix. The Sybase bit was taken care of in yesterday's write up. But how do we get PHP to talk to SQL Server on Ubuntu 8.10?

Throw out the notion that you can use the php-sybase package from the package manager. It munges stuff up pretty badly and at the same time does things to the Sybase extension that make it unusable for my needs. Not to mention that if their is a Sybase extension already installed then it really pukes because the php-sybase extension that installs the SQL Server functions actually installs the mssql_* functions then maps the sybase_* functions to them, which makes PHP squawk since those functions are already defined.

So the only way to make this happen is to build the SQL Server extension from source.

Building and installing the mssql extension from source

The follow steps will guide you through how I got the mssql extension built and installed on Ubuntu 8.10:

  • Build the FreeTDS library
    FreeTDS is a free, open source library of clients that allow Linux machines to talk to SQL servers and Sybase servers. Download the latest stable version of the FreeTDS library (as of this writing it was 0.82) and unpack the source to a directory on your machine. Once the source is unpacked, change to the directory where the source is located and configure and make the library:
    $ ./configure --prefix=/usr/local/freetds --enable-msdblib
    $ make
    $ sudo make install

    DO NOT MAKE CLEAN AT THIS POINT.

    Before you do anything else you must make sure that you have copied over all the necessary files from FreeTDS. "Wait, didn't the installation routine do that?" you might ask. No, it doesn't, because of a change made on the part of the FreeTDS developers. There are two files that are needed in order to build the mssql extension. Those files are among the make files and must be copied over to the freetds install directory.

    From inside the source directory of freetds, where you built it from, enter:
    $ sudo cp include/tds.h /usr/local/freetds/include
    $ sudo cp src/tds/.libs/libtds.a /usr/local/freetds/lib

  • Get the PHP source code
    Change to a directory where you wouldn't mind having the entirety of the PHP source stashed. From the command line type:
    $ apt-get source php5
  • Copy the mssql extension files to a directory you can build from
    My preference is to leave source alone and work from copies. So I always copy the files I need to a different location and build from the copies. To that end, I created a directory at ~/SourceCode/php-mssql and copied the following files from the original PHP source directory:
    $ cp ext/mssql/config.m4 ~/SourceCode/php-mssql
    $ cp ext/mssql/php_mssql.c ~/SourceCode/php-mssql
    $ cp ext/mssql/php_mssql.h ~/SourceCode/php-mssql
  • Make and install the extension
    Make sure you have the php5-dev package installed on your system so that you can build PHP extensions from source. From inside the directory where your mssql extension source code is, at the command line, enter:
    $ phpize
    $ ./configure --with-mssql=/usr/local/freetds
    $ make
    $ make install

    Find out where your extensions directory is on your machine and quickly check it to make sure there is a php_mssql.so file living in it. On my machine the extension directory is /usr/lib/php5/20060613+lfs/. Yours may be different.

  • Configure PHP to use the new extension we just made
    Now we need to tell PHP to use the new extension we just built. To do that we need to create an ini file for the extension and put it inside of the extensions directory where PHP can find it. On my machine PHP looks for ini files to parse in /etc/php5/conf.d/ so naturally that is where I am going to go to to tell PHP to use this extension.

    $ cd /etc/php5/conf.d/

    Now we need to create an ini file and put into a directive to load the extension. You can use whatever editor you like. I prefer to use vim:
    $ sudo vim mssql.ini

    Inside this file place the following two lines:
    ; Enable the mssql extension
    extension=mssql.so

  • Configure your environment to load a much needed environment variable whenever the machine starts
    Much like the Sybase extension we did yesterday the mssql extension needs an environment variable in order to function properly. Again, this one caused me fits for a long time in Ubuntu. To be sure you can use the SQL Server extension from both the CLI and the web server you will need to add an environment variable to both the /etc/profile startup script AND the web servers environment variable setting script.

    $ sudo vim /etc/profile

    At the end of the file add:
    export FREETDSCONF=/etc/freetds/freetds.conf

    Now add this same entry into your web server’s environment variables. I am using apache and assuming you are to. If not, consult your web server’s documentation for how to do this:
    $ sudo vim /etc/apache2/envvars

    Now add these entries to the end of the file:
    export FREETDSCONF=/etc/freetds/freetds.conf

  • Configure FreeTDS
    In order for FreeTDS to communicate properly with the SQL server a DSN of sorts needs to be created. To make a DSN you need to edit the freetds.conf file:
    $ sudo vim /etc/freetds/freetds.conf

    Go to the end of the file and add the following lines (MAKE SURE TO INDENT ALL OF THE CONFIG VALUES FOR THE SERVERNAME):
    ;--- Custom MSSQL server name ---
    ; THIS CAN BE ANY NAMED SERVER REFERENCE YOU WANT TO CREATE
    [SERVERNAME]
      ; This is the host name of the MSSQL server
      host=HOSTNAME
      ; This is the port number to use
      port=PORTNUMBER
      ; This is the TDS version to use for anything over Server 2000
      tds version=8.0

    Now we need to add the freetds library to the load library stack.
    $ sudo vi /etc/ld.so.conf

    Go to the end of the file and add the following line:
    /usr/local/freetds/lib

  • Restart your web server
    Like everything that involves a change to the PHP environment or configuration on your machine, restart the web server. I am assuming this is being built upon an apache server. If not, you will need to know how to stop and start your web server or, at the very least, know how to reboot your machine:
    $ sudo apache2ctl stop
    $ sudo apache2ctl start

    Run a PHP info page or CLI call to see if it is loaded:
    $ php -m

    You should now see mssql.

Again, I hope this was helpful to you. If it was, please leave a comment and let me know. It something went haywire for you, leave me a comment, too. Systems being what they are, it is never unheard of for two almost identical systems to have vastly different experiences with building software from source.

17Feb/090

Handling multiple Sybase result sets in PHP on Ubuntu 8.10

A long time ago I was tasked with creating a PHP application that would talk to our Sybase database that drives our entire enterprise at work. This is not normally a big thing to do as one can simply enable the Sybase extension for PHP and have a database connection generally within minutes. However there was one thing that was an absolute necessity in this application that made it significantly more difficult to get it working: some parts of the application would be reliant upon stored procedures that returned multiple result sets.

First off let me say that any time you have the option to build stored procedures for your database quieries, do it. They are faster, safer, compiled and centralized and they keep your code from being littered with query builders all over the place. That said, this is neither the time nor the place to talk about the merits of stored procedures in application development. Just know that where I work there are no direct queries ever allowed to touch our database servers.

Knowing that I had to work under the requirement of handling multiple Sybase result sets I set out to find out how to do it and what I found was alarming. Few database extensions in PHP handle multiple result sets. In fact, as I was looking around it seemed that only the MySQLi extension and the SQL Server extension handled multiple result sets. But after a little searching around I found something called the "PHP Sybase CT driver enhancements" project on Sourceforge that essentially provided prepared statement handling and multiple result set handling for the PHP Sybase Extension. However, after trying to install it it became evident that the extension, as it was written, was still not usable in the state it was offered up for download in.

Not to worry, I have modified the package for compiling within a PHP 5 environment and have made a few changes to it that should allow it to be used as is. The source code archive file can be downloaded (in tar.gz format) by clicking here.

Please read the notes on this source code before installing it!

Before installation

Before we get started we need to cover a few assumptions:

  • You have a licensed copy of Sybase Adaptive Server
    I have not tried this against any Sybase database server other than Adaptive server.
  • You have the Sybase client installed or at the very least have the installer available
    For this exercise we are using a Red Hat RPM of the Sybase 12.5 client. Yes, we are installing on Ubuntu, which is essentially Debian and therefore does not use RPM files, but we will burn that bridge when we come to it.
  • You are comfortable building PHP extensions from source
    There is no way around this. In order to get this to work you will need to be able to build from source.
  • You have the php-devel package installed on the machine you building this extension for
    If you don't have it, a quick sudo apt-get install php5-dev should do the trick. This will be necessary to build the Sybase extension from source.

Installation instructions

Ok, these are the steps I took in order to build the enhanced Sybase extension for PHP on my Ubuntu 8.10 desktop machine starting with Sybase ASE client and common packages in Red Hat RPM format.

  • Convert the Sybase 12.5 OpenClient and Sybase 12.5 Common RPM archives to DEB packages and install them
    To do this I used the alien package converter tool and, while inside the directory where the RPMs were living, issued the following command:
    $ alien -k sybase-common-12.5.0.1de-1.i386.rpm
    $ alien -k sybase-openclient-12.5.0.1esd-1.i386.rpm

    This created two new archives, in the directory I was in, in debian format named

    1. sybase-common-12.5.0.1DE-1_i386.deb
    2. sybase-openclient-12.5.0.1ESD-1_i386.deb

    I then issued the following commands to install them:
    $ sudo dpkg --install sybase-common-12.5.0.1DE-1_i386.deb
    $ sudo dpkg --install sybase-openclient-12.5.0.1ESD-1_i386.deb

    This made a directory named sybase-12.5 in the /opt directory.

  • Prepare your environment to build the extension
    To make things easier (and a little more compatible with the Red Hat way of doing things) I created a link inside of /top called sybase and pointed it to the sybase-12.5 directory.
    $ cd /opt
    $ ln -s -T /opt/sybase-12.5 sybase

    I then had to export some environment vars that are needed by the extension and the web server in order to handle communication with the Sybase server:
    $ export SYBASE=/opt/sybase
    $ export SYBASE_OCS=OCS-12_5
    $ export PATH=/opt/sybase/OCS-12_5/bin:$PATH
    $ export LD_LIBRARY_PATH=/opt/sybase/OCS-12_5/lib:/opt/sybase/OCS-12_5/lib3p:$LD_LIBRARY_PATH
    $ export INCLUDE=/opt/sybase/OCS-12_5/include:$INCLUDE
    $ export LIB=/opt/sybase/OCS-12_5/lib:$LIB

    Yes, I know that each one of these commands could have been placed in a file and sourced, but for some reason source is not available to my installation of Ubuntu so it was in fact easier and faster for me to do it this way. Do this how you will, but remember the values because you will need these later.

  • Build and install the php-sybase-ct extension from source against the Sybase client you just installed
    Remember that package I told you about earlier? The one that I said you could download? If not, get it now and unpack it to a directory somewhere where you have permission to unpack stuff on your system. For me, it was ~/Temp.

    Change to the directory you unpacked the source code to and configure it for make and installation using the php-devel package:
    $ cd ~/Temp
    $ phpize
    $ ./configure --with-sybase-ct=$SYBASE/$SYBASE_OCS
    $ make
    $ sudo make install

    Find out where your extensions directory is on your machine and quickly check it to make sure there is a php_sybase_ct.so file living in it. On my machine the extension directory is /usr/lib/php5/20060613+lfs/. Yours may be different.

    At this point you can safely do a sudo make clean but you might want to hold off on that until it is all working in case you need to rebuild at all. It does happen from time to time. Just sayin'.

  • Configure PHP to use the new extension
    Now we need to tell PHP to use the new extension we just built. To do that we need to create an ini file for the extension and put it inside of the extensions directory where PHP can find it. On my machine PHP looks for ini files to parse in /etc/php5/conf.d/ so naturally that is where I am going to go to to tell PHP to use this extension.

    $ cd /etc/php5/conf.d/

    Now we need to create an ini file and put into a directive to load the extension. You can use whatever editor you like. I prefer to use vim:
    $ sudo vim sybase_ct.ini

    Inside this file place the following two lines:
    ; Enable the sybase extension
    extension=sybase_ct.so

  • Configure your environment to load the correct environment variables whenever the machine starts
    This one caused me fits for a long time in Ubuntu. In order to ensure that you can use the Sybase extension from both the CLI and the web server you will need to take all of the environment variables that exported prior to building the extension and place them in both the /etc/profile startup script AND the web servers environment variable setting script. This caused countless hours of frustration and anger for me and I hope I can save you some angst with this little snippet.

    Add the environment vars to /etc/profile. You can use any editor you like. I like vim:
    $ sudo vim /etc/profile

    At the end of the file add the environment vars:
    export SYBASE=/opt/sybase
    export SYBASE_OCS=OCS-12_5
    export PATH=/opt/sybase/OCS-12_5/bin:$PATH
    export LD_LIBRARY_PATH=/opt/sybase/OCS-12_5/lib:/opt/sybase/OCS-12_5/lib3p:$LD_LIBRARY_PATH
    export INCLUDE=/opt/sybase/OCS-12_5/include:$INCLUDE
    export LIB=/opt/sybase/OCS-12_5/lib:$LIB

    Now add these same entries into your web server's environment variables. I am using apache and assuming you are to. If not, consult your web server's documentation for how to do this:
    $ sudo vim /etc/apache2/envvars

    Now add these entries to the end of the file:
    export SYBASE=/opt/sybase
    export SYBASE_OCS=OCS-12_5
    export PATH=/opt/sybase/OCS-12_5/bin:$PATH
    export LD_LIBRARY_PATH=/opt/sybase/OCS-12_5/lib:/opt/sybase/OCS-12_5/lib3p:$LD_LIBRARY_PATH
    export INCLUDE=/opt/sybase/OCS-12_5/include:$INCLUDE
    export LIB=/opt/sybase/OCS-12_5/lib:$LIB

  • Restart your web server
    Like everything that involves a change to the PHP environment or configuration on your machine, restart the web server. I am assuming this is being built upon an apache server. If not, you will need to know how to stop and start your web server or, at the very least, know how to reboot your machine:
    $ sudo apache2ctl stop
    $ sudo apache2ctl start

    Run a PHP info page or CLI call to see if it is loaded:
    $ php -m

    You should see sybase_ct. If not, something went wrong. If so, you are now golden.

At this point you should be able to run queries against your Sybase server AND handle multiple result sets using the sybase_next_result() function. There is no documentation for this function, but a quick read through of the mssql_next_result() function will tell pretty well how to use it.

Enjoy! And if this was at all helpful please leave a comment to let me know.

Notes on the PHP Sybase CT Enhanced extension

Some things to keep in mind when using this extension:

  • This extension is coded in PHP 4 source.
  • When I got my hands on it the code was still much in development phase. Not much has changed about that other than I removed debug output from it.
  • It does not handle connection failure well when the server does not respond. So unwell in fact that internally it causes segmentation faults and results in blank web pages upon failure. I do not know how to fix that.
  • If you know how to program in c or want to make it better, please feel free to do so. I have contacted the original authors of this extension and of the three only one has done any real work on it and none have worked on it in the last five years and really do not intend to. I was given permission to distribute it and modify it. But I really do not know what I am doing in c. Yet. ;)
31Jan/090

Coffee, WiFi and coding

Today I had the pleasure of working. I know, that sounds really weird, but it is true. It was a pleasure. The reason it was pleasurable is because I was able to work outside of the home in a fairly quiet place that served all the coffee I could drink for $1.50.

Thanks to Mission Coffee and their free WiFi internet connection I was able to spend about three and half hours working on a project that I have spent entirely too much time on already but seriously needs to get done. It was clean, quiet, conducive to programming work and, well, it had coffee. Lots of it. And it was only $1.50 for never ending cup.

It was a nice feeling to get out of the house today. Usually as I am at home working I get derailed by the screams of children fighting or someone needing something. Being out meant that the distractions that I am faced with at home were virtually non-existent (save for guy that sat behind me and talked to himself in a quasi-farci-spanish mix). I had an entire table to myself, off in a little corner, where I wasn't messed with by anyone. Mostly.

After about three and half hours I was asked to move because the wall I was sitting next to was getting artwork hung on it. As soon as I stood up I realized that it was time to get home. It just seemed right to be done at the time. And I have no regrets. The day was productive, the environment was nice and the coffee was great.

So if you are ever in Fremont and need a coffee joint to sit at and spend some time on the Internet, hit up Mission Coffee. You'll be glad you did.

Tagged as: , No Comments
8Jan/093

Security basics – lock the front door

A recent attack on the popular microblogging site Twitter has produced a great deal of buzz around the social mediasphere and the developer community because of the nature of the attacks. Apparently the attack was a pretty basic dictionary attack against the front door of the Twitter site.

Jeff Atwood reports on his blog how the attack was carried out and how you, as a developer, can defend against dictionary attacks.

He provides a lot of information to assist you in protecting your data, or, as is often the case, your users data. One of the things that Jeff mentions is throttling failed login attempts against a wait time to deter the basic repetitive failed login attempt. He also mentions requiring a capcha/recapcha after a certain number of failures to make sure you are dealing with a human as opposed to a bot.

Google employs this principle. I have actually tested this (actually, I have had to use this as I forgot a Google password once). This is perhaps the cleanest validating type of login failure handler as it allows the user to make mistakes without penalizing them for not remembering. Believe it or not, there are some people that forget their passwords and choose to actually try to remember it instead of resetting it. I know, it seems archaic, but it does happen.

Another way to makes things better for your users is to not only allow a mix of numbers, letters - both upper and lower case, and special characters but to actually requre a mix of those types of characters. It may seem inconvenient at the time, it will always be less convenient than a security breach because of a password like "happiness".

Regardless of what you choose to emply in terms of front door security I would suggest you read Jeff's article. It has plenty of useful information about security and protecting against dictionary attacks.

21Sep/080

Little Bobby Tables? Or do I prefer Robert?

Way back when I ran across a comic at xkcd.com that outlined the funny things that can happen when developers forget to escape input:

Little Bobby Tables strikes hard

This brought a tremendous amount of laughter to everyone that works in my department (since many are database people and a few are developers) and from that point on I had the lovely nickname "Little Bobby Tables".

In an effort to try to rectify that I found a LOLCAT picture that sums up a little better what my position is on the name "Little Bobby Tables":

Bobcat prefers to be called Robert

Little Bobby Details has spoken.

Tagged as: No Comments
15Sep/080

ZendCon 2008 – Day 1

Today marked a new experience for me as a PHP developer: I got to go my first PHP developer's conference, ZendCon 2008. I have wanted to come to one of these conferences for a long time now so this is a real boon for me as a tech head and professional developer.

I gotta tell you I am really freaking stoked at my experience today. Although I didn't attend any tutorials today I did spend the day in Christian Wenz's ZCE Crash Course breakout session in preparation for the PHP 5 ZCE exam I am planning on taking tomorrow. This session was worth the price of admission all by itself.

Christian just knows how to teach. I had taken a ZCE training course before over the course of four weeks and 24 hours and managed to learn a pretty decent amount. I took a few ZCE practice exams after that and managed to score a PASS and a PASS. But they were not really string passes as I failed in a few of the 12 sections, got a pass in most of them and an excellent in a few others.

After taking Christian's course today I came home and took another practice exam and managed to get a pass in which 10 of the 12 sections were scored as excellent and two of the sections were pass. So I am feeling a little better about taking the ZCE exam tomorrow as I think I might actually be able to pass this thing knowing a little more about streams and XML.

But getting back to the conference, I was pretty excited today to be able to meet some folks that I have always wanted to meet (or at least as long as I have been a developer and have known some of these names).  I was able to meet Christian Wenz, Wez Furlong, Elizabeth Smith, Ben Ramsey, Matthew Turland, Lorna Mitchell and Christian Flickinger. I was like a kid in a candy store meeting names of people that I look up to in the PHP development community.

Overall today I had a great time. Jason, my coworker, and I took in a lot of good info from the day and from the breakout session. I am really looking forward to meeting some other folks and mixing it up  bit tomorrow. And I am also looking forward to passing my ZCE first thing in the morning. Wish me luck. Tomorrow I plan on being a ZCE.