All posts by shannah

Steve Hannah is a 28-year-old software developer currently studying and working at Simon Fraser University in beautiful Vancouver British Columbia. He specializes in web information systems and prefers Java, PHP, and Python as programming languages. He is a Christian and worships at Christ Church of China in Vancouver.

Smarty Layered Skinning

The default install of smarty is great but simple. I wanted to be able to have multiple template directories so that Smarty would search all of the directories for the requested template, choosing the first one found to be displayed (similar to a UNIX-style PATH environment variable).

This functionality is built-in to smarty (simply specify the template_dir attribute as an array), but only a single compile directory is specifiable, so compiled versions of templates in different directories will conflict. I created a SkinTool as a subclass of smarty to provide skinning. Its code is as follows:

class SkinTool extends Smarty{

         /**
          * Array of registered skins..
          */
	var $skins = array();
	
	/**
           * Overrides smarty method.  This will place compiled version of the template
           * in a subfolder of the compile_dir named after the skin where the template
           * resides.  If this subfolder does not exist, it is created.
           *
           * @param string $resource_name
           * @return string results of {@link _get_auto_filename()}
           */
         function _get_compile_path($resource_name)
         {
    	    $params = array('resource_name'=>$resource_name, 'resource_base_path' => $this->template_dir);
    	    $name = $this->_parse_resource_name($params);
    	    $template_dir = dirname($params['resource_name']);
    	    $skin = $this->skins[$template_dir];
    	    if ( strlen($skin) > 0 and preg_match('/^[0-9a-zA-Z_]+$/', $skin) ){
    		$compile_dir = $this->compile_dir.'/'.$skin;
    		if ( !file_exists($compile_dir) ){
    			mkdir($compile_dir);
    		}
    		if ( !file_exists($compile_dir) ){
    			trigger_error("Failed to create compile directory '$compile_dir'", E_USER_ERROR);
    		}
    	    } else {
    		$compile_dir = $this->compile_dir;
    	    }
             return $this->_get_auto_filename($compile_dir, $resource_name,
                                         $this->_compile_id) . '.php';
        }

	
	/**
	 * Registers a skin to be used as the default skin.  This skin is added to 
	 * the top of the stack so it has the highest priority.  If a template is
	 * requested and this skin does not contain that template, then the SkinTool
	 * will check the next skin in the stack. And so on...
	 *
          * @param $name The name of the skin
	 * @param $template_dir The directory to the templates for this skin.
	 */
	function register_skin( $name, $template_dir){
		if ( !is_array($this->template_dir) ){
			if ( strlen($this->template_dir) > 0 ){
				$this->template_dir = array($this->template_dir);
			} else {
				$this->template_dir = array();
			}
		}
		array_unshift($this->template_dir, $template_dir);
		$this->skins[$template_dir] = $name;
	
	}

Jakarta Commons FTP Client Deadlock problems

I have been experimenting with Java FTP components in my effort to create a JFileChooser component that will work over FTP. Common concensus seems to be that the best FTP component is the Jakarta Commons FTPClient class. It seems to have all of the necessary functionality, however seems to have multithreading issues. The component continually deadlocks, and I have narrowed it down to the FTPClient class. I found a post at http://www.kaffe.org/pipermail/kaffe/2005-August/103033.html that also talks about deadlocking in the FTPClient. One suggested solution is to disable the separate reader thread by calling setReaderThread(false). This helps me get a little further to the point where it usually works, but nonetheless deadlocking still occurs.

I will keep on searching for another FTP component but in the mean time I have successfully adapted JFileChooser to use SFTP (Secure FTP) using the SSH Tools libraries.

References

Hessian Web Services

I just ran across a great little library for web services, called Hessian. It is a binary web service (ie not xml) so its messages are much more compact and easier to transfer. In addition, the client and server libraries don’t require nearly as large a framework. It looks really easy and there are libraries to support Java, Python, and PHP — and maybe a few others that slipped my mind.

http://hessianphp.sourceforge.net/quickstart.html

Extracting Tar files in php in safe mode with no shell access

I have just about had it with web servers that give no shell access and operate in safe mode. I wanted to install Dataface on my webspace here at sjhannah.com. The full installation is about 2.6 megs as a compressed tar file (much much bigger uncompressed) so it is kind of unfeasible to upload the uncompressed files to the server. Given the tendency of ftp to hang before during and after each file is uploaded, it would likely take 5 to 6 hours to upload the conventional way using an FTP client.

What I really needed to be able to do was upload the compressed tar archive to the server, then extract its contents on the server. Sounds easy, right. Well not when your server doesn’t provide shell access and PHP is executed in safe mode. Okay, Okay, normally you could create a PHP script like the following:

< ?
system("tar -xvfz filename.tar.gz");

?>

Except for the fact that my server doesn’t even provide ‘tar’ in the path – so even this command won’t work on my server.

PEAR to the rescue!!!

PEAR is a repository for reusable PHP classses. When I need to get something done in PHP, PEAR is usually the first place where I look. Luckily I found a great class called Achive_Tar that provides a pure PHP solution for Tar file manipulation.

After downloading and installing the class in my web space I wrote a new script to take care of the extraction:

< ?
require_once 'Archive/Tar.php';
$archive = new Archive_Tar($_GET['filename']);

$res = $archive->extract();
if ( PEAR::isError($res) ){
	echo $res->toString();
}

?>

This script takes a get parameter ‘filename’ to specify the name of the file to be extracted. It then extracts that file into the same directory!! And voila!! we can run this script and extract our file.

Dataface Code Tip #1: Relationships the easy way

This is just a sample of how to deal with related records using the dataface library.

< ?
    require_once '/path/to/dataface/init.php';
    init(__FILE__, 'http://mydomain.com/path/to/dataface');
    require_once 'Dataface/Application.php';
    $app =& Dataface_Application::getInstance();

    require_once 'Dataface/Table.php';
    $studentsTable =& Dataface_Table::loadTable('Students');

    $studentsTable->addRelationship(
	    'courses', 		// the name of the relationship to add
	    array(
		    'Courses.CourseID' => 'StudentCourses.CourseID',
		    'StudentCourses.StudentID' => '$StudentID'
		    )
    );

    require_once 'dataface-public-api.php';

    $student =& df_get_record('Students', array('StudentID'=>1000));

    foreach ( $s tudent->getRelatedRecords('courses') as $course ){
	    echo $course['Name']."\n";
    }
    ?>

 This was pretty easy, especially since most of this code would be tucked away in a configuration file leaving you with 2 nice lines to get the student’s courses:


$student =& df_get_record('Students', array('StudentID'=>1000));
$courses = $student->getRelatedRecords('courses');

Code Tip #1: PEAR SQL Parser

In developing Dataface, I have had to use a number of existing libraries to make my life easier.  One such library is the SQL Parser library in PEAR.  This class will parse an SQL query into a data structure that can be manipulated quite easily.  The library also comes with the complementary SQL_Compiler class that can compile these data structures back into an SQL statement.  This can be handy if you want to be able to add filters, column lists, or sort orders to sql queries, or if you want to break a query down into its component parts.

 Simple Usage

require_once ‘SQL/Parser.php’;
$parser = new SQL_Parser();
$struct = $parser->parse("SELECT a,b,c FROM Foo");
print_r($struct);

The above code sample will output the following:

Array
(
    [command] => select
    [column_tables] => Array
        (
            [0] =>
            [1] =>
            [2] =>
        )

    [column_names] => Array
        (
            [0] => a
            [1] => b
            [2] => c
        )

    [column_aliases] => Array
        (
            [0] =>
            [1] =>
            [2] =>
        )

    [table_names] => Array
        (
            [0] => foo
        )

    [table_aliases] => Array
        (
            [0] =>
        )

    [table_join_clause] => Array
        (
            [0] =>
        )

)

 

This is a fairly simple representation and it should be straight forward what all this means, but I’ll go over it here.  The statement SELECT a, b, c FROM Foo is parsed into the above array with the following keys:

  • command – The SQL command (eg: SELECT, UPDATE, INSERT, etc..)
  • column_names – The names of the columns that are included in the query.
  • column_aliases – If you have a column name of the form foo as bar, indicating that you are aliasing that column, this will be an array of the aliases for each column. 
  • column_tables – If you have a column name of the form Foo.bar (indicating column bar from table Foo) then this is an array of the table names.
  • table_names – The names of the tables in the FROM clause.
  • table_aliases – The aliases of the tables in the FROM clause.  This array has corresponding indices to the table_names array.
  • table_join_clause – A structure to represent the join clause of the query.
  • where_clause – A structure to represent the where clause of the query.
  • sort_order – A structure to represent the ORDER BY clause of the query.

How about a slightly more complex query as an example to see how these structures work.

require_once ‘SQL/Parser.php’;
$parser = new SQL_Parser(null, ‘MySQL’);
$struct = $parser->parse("SELECT F.a as column1, B.b as column2 FROM Foo F inner join Bar B on F.c=B.c where column1 = ‘val1’ and column2 = ‘val2’");
print_r($struct);

This would output:

Array
(
    [command] => select
    [column_tables] => Array
        (
            [0] => F
            [1] => B
        )

    [column_names] => Array
        (
            [0] => a
            [1] => b
        )

    [column_aliases] => Array
        (
            [0] => column1
            [1] => column2
        )

    [table_names] => Array
        (
            [0] => Foo
            [1] => Bar
        )

    [table_aliases] => Array
        (
            [0] => F
            [1] => B
        )

    [table_join_clause] => Array
        (
            [0] =>
            [1] => Array
                (
                    [arg_1] => Array
                        (
                            [value] => F.c
                            [type] => ident
                        )

                    [op] => =
                    [arg_2] => Array
                        (
                            [value] => B.c
                            [type] => ident
                        )

                )

        )

    [table_join] => Array
        (
            [0] => inner join
        )

    [where_clause] => Array
        (
            [arg_1] => Array
                (
                    [arg_1] => Array
                        (
                            [value] => column1
                            [type] => ident
                        )

                    [op] => =
                    [arg_2] => Array
                        (
                            [value] => val1
                            [type] => text_val
                        )

                )

            [op] => and
            [arg_2] => Array
                (
                    [arg_1] => Array
                        (
                            [value] => column2
                            [type] => ident
                        )

                    [op] => =
                    [arg_2] => Array
                        (
                            [value] => val2
                            [type] => text_val
                        )

                )

        )

)

Comments

As you can see the column_names, column_aliases, and column_tables arrays all have corresponding indicesi.e. $struct[‘column_aliases’][$i] contains the alias (if it is specified) for the column whose name is stored in $struct[‘column_names’][$i] and the table for the column can be found in $struct[‘column_tables’][$i].

There is a similar correspondence between the table_names and table_aliases arrays.

Dataface Coming Along

Dataface is coming along slowing but surely.  It is still only publicly available via sourceforge CVS but it is growing in features.  I played around with soap a little over the holidays and have begun to build a Web service interface.  Next on the slate is Dataface Desktop Edition !!  Watch out filemaker, open source just got a whole lot better!

Happy New Year

OK.. so I haven’t been keeping up with my blog posts.  Here’s one for the new year 2006.  I spent boxing day through January 1st in Calgary with my girlfriend’s family.  It was nice and relaxing.  Didn’t really even notice the cold.  I did, have to endure the airplane from hell, however.  (A short ‘aside’ – I this was my very first plane travel experience). — on the last 20 minutes or so of the flight home was like riding a roller coaster blind. 

By God’s grace I survived and did not throw up (although my breakfast was beginning to surface in the back of my throat.  At one point I even yelled out "Holy f**k!" when the plane hit some major turbulence.   From now on, call me BA Baracas and tranquilize me before stepping on the plane.

Back at work now for another semester of web development and mathematics studies.  I will be taking Math 443 (Combinatorial Theory) and Math 439 (Algebraic Systems) this semester.  Looks interesting …

ClassHoles : Top quality comedy

I went to a fundraiser last night for a local comedy troup called Classholes.  A good friend of mine, Mike Berdan, is part of this troup so I may be a little biased in my opinions of the troup.  All I can say is that if you are looking for top quality live entertainment in Vancouver, it doesn’t get any better than Classholes.  They do a mixture of live comedy sketches and recorded sketches similar to the Saturday Night Live format.  In fact, the comedy is generally of higher calibre than SNL.  In fairness to SNL, they have to produce something once per week, and Classholes takes months to perfect a show.  Anyways, their next show is in February 2006.  Check it out.  For more info check out http://www.classholes.com