<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tower Of Power &#187; Development</title>
	<atom:link href="http://www.toosweettobesour.com/category/development/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.toosweettobesour.com</link>
	<description>Far Too Sweet To Be Sour</description>
	<lastBuildDate>Wed, 07 Dec 2011 23:22:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Flex Box: Prevent Children From Stretching</title>
		<link>http://www.toosweettobesour.com/2011/07/14/flex-box-prevent-children-from-stretching/</link>
		<comments>http://www.toosweettobesour.com/2011/07/14/flex-box-prevent-children-from-stretching/#comments</comments>
		<pubDate>Thu, 14 Jul 2011 21:47:57 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[columns]]></category>
		<category><![CDATA[css3]]></category>
		<category><![CDATA[flex box]]></category>
		<category><![CDATA[moz-box-flex]]></category>
		<category><![CDATA[webkit-box-flex]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=497</guid>
		<description><![CDATA[So, flex boxes are a glorious addition to CSS3 to make advanced layouts rapidly. No more crazy floats and nested divs and weird percentage values for columns! Just setup your orientation to horizontal and toss a box-flex: 1 in for kicks. However, just a tip: In the course of working with flex boxes, you will [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2011/07/14/flex-box-prevent-children-from-stretching/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>So, <a href="http://www.the-haystack.com/2010/01/23/css3-flexbox-part-1/">flex boxes</a> are a glorious addition to CSS3 to make advanced layouts rapidly. No more crazy floats and nested divs and weird percentage values for columns! Just setup your orientation to horizontal and toss a <code>box-flex: 1</code> in for kicks.</p>
<p>However, just a tip: In the course of working with flex boxes, you will find that if you given a parent container a flex value, the children can still stretch it out. For example, a long paragraph will push its parent wider and wider to accommodate unless you give it a static width (which kinda defeats the purpose of using flex boxes really).</p>
<p>A nifty trick, however, is if you give an element with a defined <code>box-flex</code> a <code>width: 0px;</code>, the sizing algorithm will ignore said element&#8217;s children when sizing the element.</p>
<p>Here&#8217;s an example of automatic vertical columns:</p>
<pre name="code" class="html">
&lt;div id="wall"&gt;
    &lt;div id="col1" class="column top"&gt;

        &lt;div class="entry"&gt;
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam eget tristique velit. In ut ligula nibh, a pulvinar lorem. Nam sed elit eget tellus vestibulum
        &lt;/div&gt;

    &lt;/div&gt;
    &lt;div id="col2" class="column bottom"&gt;

        &lt;div class="entry"&gt;
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam eget tristique velit. In ut ligula nibh, a pulvinar lorem. Nam sed elit eget tellus vestibulum
        &lt;/div&gt;

    &lt;/div&gt;
    &lt;div id="col3" class="column top"&gt;&lt;/div&gt;
    &lt;div id="col4" class="column bottom"&gt;&lt;/div&gt;
&lt;/div&gt;
</pre>
<pre name="code" class="css">
#wall {
    display: -webkit-box;
    -webkit-box-orient: horizontal;

    position: absolute !important;
    top:0;
    right:0;
    bottom:0;
    left:0;

    overflow: hidden;
}

#wall .column {
    display: -webkit-box;
    -webkit-box-sizing: border-box;

    -webkit-box-orient: vertical;

    -webkit-box-flex: 1;
    width:0px;

    border-right: 1px solid #333;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2011/07/14/flex-box-prevent-children-from-stretching/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Doctrine 1.2 MSSQL Alternative LIMIT/Paging</title>
		<link>http://www.toosweettobesour.com/2010/09/16/doctrine-1-2-mssql-alternative-limitpaging/</link>
		<comments>http://www.toosweettobesour.com/2010/09/16/doctrine-1-2-mssql-alternative-limitpaging/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 22:11:47 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[mssql]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=477</guid>
		<description><![CDATA[At work I had been having all sorts of issues with Doctrine_Connection_Mssql&#8216;s LIMIT alteration, based on Zend_Db&#8216;s code. The code used the more-compatible-with-SQL-Server-2000 technique of modifying the query to SELECT TOP (offset + limit), reverse the ORDER BY clause and SELECT TOP (limit), then finally reversing the returned dataset. As ugly as this technique is, [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2010/09/16/doctrine-1-2-mssql-alternative-limitpaging/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>At work I had been having all sorts of issues with <code>Doctrine_Connection_Mssql</code>&#8216;s <code>LIMIT</code> alteration, based on <code>Zend_Db</code>&#8216;s code.</p>
<p>The code used the more-compatible-with-SQL-Server-2000 technique of modifying the query to <code>SELECT TOP (offset + limit)</code>, reverse the <code>ORDER BY</code> clause and <code>SELECT TOP (limit)</code>, then finally reversing the returned dataset.</p>
<p>As ugly as this technique is, it works. The problem is it requires an extreme amount of intelligence or an extreme amount of simplicity in the query in order for an automated system like Doctrine to be usable. The biggest caveat with this technique is good goddamned luck paging your query if it doesn&#8217;t have an <code>ORDER BY</code>. And sometimes queries that are complex enough break the modified <code>Zend_Db</code> code.</p>
<p>There exists an <a href="http://varjabedian.net/archive/2008/04/09/paging-made-easy-in-ms-sql-server-2005.aspx">easier MSSQL paging technique</a>. Using features first available in SQL Server 2005, with only 1 subquery you can mimic MySQL&#8217;s <code>LIMIT</code> clause with ease.</p>
<p>Basically, Microsoft provided the following special feature to determine the row number in your final resultset:</p>
<pre name="code" class="sql">SELECT Row_Number() OVER (ORDER BY column) AS RowIndex FROM table</pre>
<p>Notice the <code>OVER (ORDER BY column)</code> segment? This is provided as this query will most often be used in a subquery. Given that MSSQL does not allow <code>ORDER BY</code> statements in subqueries, just move them into the <code>OVER (...)</code> section.</p>
<p>To borrow Ralph Varjabedian&#8217;s example, the following MySQL query:</p>
<pre name="code" class="sql">SELECT * FROM users LIMIT 15, 15</pre>
<p>Becomes functionally equivalent to the following MSSQL query:</p>
<pre name="code" class="sql">SELECT
    *
FROM
    (
    SELECT
        Row_Number() OVER (ORDER BY userID) AS RowIndex,
        *
    FROM
        users
    ) AS sub
WHERE
    sub.RowIndex > 15
    AND sub.RowIndex <= 30</pre>
<p>Now all I needed to do was add the ability for Doctrine to parse a generated MSSQL query and reform it like the one above!</p>
<p>I've provided a copy of the Doctrine connection adapter I wrote. Simply add the following line to wherever you setup Doctrine:</p>
<p><strong>Please note: This was the result of about 6 hours of hacking today. There are certainly places in the code where it can be more robust or improved, especially in my little parser. Use at your own risk (though I have yet to encounter any errors in my application).</strong></p>
<pre name="code" class="php">&lt;?php
class CaseMan_Doctrine_Connection_Mssql extends Doctrine_Connection_Mssql
{
    /**
     * @var string $driverName                  the name of this connection driver
     */
    protected $driverName = 'Mssql';

    /**
     *
     * @var boolean $is2005OrBetter             cached result determining if server is SQL Server 2005 or better
     */
    protected $is2005OrBetter;

    /**
     * the constructor
     *
     * @param Doctrine_Manager $manager
     * @param PDO $pdo                          database handle
     */
    public function __construct(Doctrine_Manager $manager, $adapter)
    {
        parent::__construct($manager, $adapter);
    }

    /**
     * Adds an adapter-specific LIMIT clause to the SELECT statement.
     *
     * @param string $query
     * @param mixed $limit
     * @param mixed $offset
     * @return string
     */
    public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false)
    {
        if ($limit &lt;= 0 &#038;&#038; $offset &lt;= 0)
            return $query;

        if( !$this-&gt;is2005OrBetter() ) //Not at least 2005
            return parent::modifyLimitQuery ($query, $limit, $offset, $isManip);

        //SETUP FIELD ALIASES
        $inner_query_name = '_inner_query_';
        $row_count_name = '_inner_query_row_count_';

        //PREPARE TOKENIZER REGEXES
        $escaped = "\\\.";
        $sections = "SELECT|FROM|WHERE|GROUP[ ]+BY|HAVING|ORDER[ ]+BY";
        $open_delimiters = "[\[\(]";
        $close_delimiters = "[\]\)]";
        $string_delimiters = "[\"\']";

        //TOKENIZE QUERY
        $_split_query = preg_split(
            "#({$escaped}|{$sections}|{$open_delimiters}|{$close_delimiters}|{$string_delimiters})#i",
            trim($query),
            -1,
            PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
        );

        $query_parts = array();
        $_state = 'BEGIN';
        $_current_part = 'SELECT';
        $_stack = array();
        while( $_token = array_shift($_split_query) )
        {
            switch( $_state )
            {
                case 'BEGIN':

                    if( trim(strtoupper($_token)) == 'SELECT' )
                    {
                        $query_parts['SELECT'] = '';
                        $_current_part = 'SELECT';
                        $_state = 'SECTION';
                    }
                    else
                    {
                        throw new Doctrine_Exception("Invalid query passed to modifyLimitQuery, must begin with SELECT");
                    }

                    break;
                case 'SECTION':

                    if( preg_match("#^({$sections})$#i", trim($_token)) )
                    {
                        $_section = strtoupper(trim($_token));

                        $query_parts[$_section] = '';
                        $_current_part = $_section;
                        $_state = 'SECTION';
                    }
                    else
                    {
                        $query_parts[$_current_part] .= $_token;

                        if( preg_match("#^({$string_delimiters})$#i", trim($_token)) )
                        {
                            array_push($_stack, trim($_token));
                            $_state = 'STRING';
                        } else if( preg_match("#^({$open_delimiters})$#i", trim($_token)) )
                        {
                            array_push($_stack, trim($_token));
                            $_state = 'DELIMITED';
                        }
                    }

                    break;
                case 'DELIMITED':

                    $query_parts[$_current_part] .= $_token;

                    if( preg_match("#^({$close_delimiters})$#i", trim($_token)) )
                    {
                        $_prev_delimiter = array_pop($_stack);
                        switch( trim($_token) )
                        {
                            case ']':
                                if( $_prev_delimiter != '[' )
                                    throw new Doctrine_Exception("Mismatched ]");
                                break;
                            case ')':
                                if( $_prev_delimiter != '(' )
                                    throw new Doctrine_Excpetion("Mismatched )");
                                break;
                            default:
                                trigger_error("FATAL ERROR: UNRECOGNIZED CLOSE DELIMITER TOKEN '{$_token}' IN " . __CLASS__ . '::' . __METHOD__, E_USER_ERROR);
                        }

                        if( count($_stack) == 0 )
                            $_state = 'SECTION';
                    }
                    elseif( preg_match("#^({$open_delimiters})$#i", trim($_token)) )
                    {
                        array_push($_stack, trim($_token));
                    }

                    break;
                case 'STRING':

                    $query_parts[$_current_part] .= $_token;

                    if( preg_match("#^({$string_delimiters})$#i", trim($_token)) )
                    {
                        if( trim($_token) == end($_stack) )
                        {
                            array_pop($_stack);
                            $_state = 'SECTION';
                        }
                    }

                    break;
            }
        }
        unset($item, $_current_part, $_token, $_stack, $_state, $_section);

        //DIVIDE UP THE SELECT STATEMENT TO PREPARE TO INSERT THE ROW_NUMBER() SELECT FIELD
        $_select_split = array_map('trim', preg_split(
            "#^(DISTINCT|)[ ]*(TOP[ ]+[0-9]+|)#i",
            trim($query_parts['SELECT']),
            -1,
            PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
        ));

        $_select_details = array_pop($_select_split);

        $query_parts['SELECT'] = array_merge((array)implode(' ', $_select_split), (array)$_select_details);
        unset($_select_split, $_select_details);

        //SETUP OUTER QUERY SELECT STATEMENT
        $outer_select = array();
        foreach( array_map('trim', explode(',', end($query_parts['SELECT']))) as $_select )
        {
            $matches = array();
            if( preg_match('#AS[ ]+(?&lt;alias&gt;.*)$#i', $_select, $matches) )
            {
                $outer_select[] = "[{$inner_query_name}]." . trim($matches['alias']);
            }
            else if( preg_match('#^(?&lt;table&gt;(\[[^\]]+\]|[^\.]+)\.|)(?&lt;field&gt;.*)#i', $_select, $matches) )
            {
                $outer_select[] = "[{$inner_query_name}]." . trim($matches['field']);
            }
        }

        //SETUP ROW_COUNT OVER() SEGMENT
        if( isset($query_parts['ORDER BY']) )
        {
            $row_count_select = "Row_Number() OVER (ORDER BY " . $query_parts['ORDER BY'] . ") AS [{$row_count_name}]";
            unset($query_parts['ORDER BY']); //ORDER BY NOT ALLOWED IN SUBQUERY, OVER(...) TAKES ITS PLACE
        }
        else
        {
            $row_count_select = "Row_Number() OVER (ORDER BY (SELECT 0)) AS [{$row_count_name}]";
        }

        $query = implode(' ', array(
            'SELECT',
            count($query_parts['SELECT']) &gt; 1 ?
                $query_parts['SELECT'][0] . ' ' . implode(', ', array(
                    $row_count_select,
                    $query_parts['SELECT'][1]
                )) :
                implode(', ', array(
                    $row_count_select,
                    $query_parts['SELECT'][0]
                )),
        ));

        unset($query_parts['SELECT']);
        foreach( $query_parts as $section =&gt; $parameters )
            $query .= ' ' . $section . ' ' . $parameters;

        $outer_query = "SELECT " . implode(', ', $outer_select) . " FROM (" . $query . ") AS [{$inner_query_name}]";

        if( $limit || $offset )
        {
            $outer_where = array();
            if( $limit )
                $outer_where[] = "[{$inner_query_name}].[{$row_count_name}] &lt;= " . ($limit + $offset);
            if( $offset )
                $outer_where[] = "[{$inner_query_name}].[{$row_count_name}] &gt; " . $offset;

            $outer_query .= ' WHERE ' . implode(' AND ', $outer_where);
        }

        return $outer_query;
    }

    public function is2005OrBetter()
    {
        if( !isset($this-&gt;is2005OrBetter) )
        {
            $version = $this-&gt;getServerVersion();

            if( $version['major'] &gt;= 9 )
                $this-&gt;is2005OrBetter = true;
            else
                $this-&gt;is2005OrBetter = false;
        }

        return $this-&gt;is2005OrBetter;
    }

    /**
     * return version information about the server
     *
     * @param bool   $native  determines if the raw version string should be returned
     * @return array    version information
     */
    public function getServerVersion($native = false)
    {
        if ($this-&gt;serverInfo) {
            $serverInfo = $this-&gt;serverInfo;
        } else {
            $query      = 'SELECT @@VERSION';
            $serverInfo = $this-&gt;fetchOne($query);
        }
        // cache server_info
        $this-&gt;serverInfo = $serverInfo;
        if ( ! $native) {
            if (preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $serverInfo, $tmp)) {
                $serverInfo = array(
                    'major' =&gt; $tmp[1],
                    'minor' =&gt; $tmp[2],
                    'patch' =&gt; $tmp[3],
                    'extra' =&gt; null,
                    'native' =&gt; $serverInfo,
                );
            } else {
                $serverInfo = array(
                    'major' =&gt; null,
                    'minor' =&gt; null,
                    'patch' =&gt; null,
                    'extra' =&gt; null,
                    'native' =&gt; $serverInfo,
                );
            }
        }
        return $serverInfo;
    }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2010/09/16/doctrine-1-2-mssql-alternative-limitpaging/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Netbeans Code Completion and your Zend_View</title>
		<link>http://www.toosweettobesour.com/2010/05/11/netbeans-code-completion-and-your-zend_view/</link>
		<comments>http://www.toosweettobesour.com/2010/05/11/netbeans-code-completion-and-your-zend_view/#comments</comments>
		<pubDate>Tue, 11 May 2010 16:18:38 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Netbeans]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend_View]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=472</guid>
		<description><![CDATA[Oh, look at me, blogging again! I definitely have a lot to blog about as I get the time, I&#8217;m coming off of a really involved project and learned a lot of tips I&#8217;d like to share about the Zend Framework. In the mean time I thought I&#8217;d share something I had a helluva time [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2010/05/11/netbeans-code-completion-and-your-zend_view/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>Oh, look at me, blogging again! I definitely have a lot to blog about as I get the time, I&#8217;m coming off of a really involved project and learned a lot of tips I&#8217;d like to share about the Zend Framework. In the mean time I thought I&#8217;d share something I had a helluva time figuring out.</p>
<p>If you&#8217;re using Zend Framework and NetBeans, you may be like me and bemoaning the lack of code completion in your Zend Views. As you may know, essentially what Zend_View does is includes your view within a method that belongs to a Zend_View object. This gives your view some nice variable encapsulation as well as access to the <code>$this</code> object (which is how Zend_View provides access to all the ViewHelpers and other functions).</p>
<p>Unfortunately NetBeans can&#8217;t figures this out (such is the problem with static analysis on a dynamic language) without help. If you&#8217;ve been using NetBeans and its code completion you&#8217;ll have already noticed that the PHPDocumentor syntax for <code>@var</code> or <code>@return</code> is how NetBeans figures out much of its code completion information, but that syntax doesn&#8217;t work in a view script.</p>
<p>Thanks to <a href="http://www.tiplite.com/useful-netbeans-6-8-php-tips/">Mystic at tiplite.com</a> I now know that to have code completion in your Zend_View scripts in NetBeans, add the following to the top of your view script:</p>
<pre name="code" class="php">&lt;?php
/* @var $this Zend_View */
?&gt;
</pre>
<p>Obviously if you&#8217;re using custom Zend_View objects you can pass in their class name instead.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2010/05/11/netbeans-code-completion-and-your-zend_view/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Python PHPSerialize Posted To Googlecode</title>
		<link>http://www.toosweettobesour.com/2009/10/20/python-phpserialize-posted-to-googlecode/</link>
		<comments>http://www.toosweettobesour.com/2009/10/20/python-phpserialize-posted-to-googlecode/#comments</comments>
		<pubDate>Tue, 20 Oct 2009 23:36:51 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Serialization]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=464</guid>
		<description><![CDATA[A while back I posted a Python snippet that I noticed could be used in a few other side projects. I have since posted it to Google Code so more than just myself will know it exists: http://code.google.com/p/phpserialize/.]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2009/10/20/python-phpserialize-posted-to-googlecode/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>A while back I posted a Python snippet that I noticed could be used in a few other side projects. I have since posted it to Google Code so more than just myself will know it exists: <a href="http://code.google.com/p/phpserialize/">http://code.google.com/p/phpserialize/</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2009/10/20/python-phpserialize-posted-to-googlecode/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Doctrine Migrations Proper</title>
		<link>http://www.toosweettobesour.com/2009/10/20/doctrine-migrations-proper/</link>
		<comments>http://www.toosweettobesour.com/2009/10/20/doctrine-migrations-proper/#comments</comments>
		<pubDate>Tue, 20 Oct 2009 23:32:40 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[zendcon09]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=462</guid>
		<description><![CDATA[I was talking with someone (I will edit this post when I find her card and remember her name) here at ZendCon and discovered that they were having trouble with migrations in Doctrine. Having gone through the same issues of Doctrine seemingly not being able to figure out your changes and generate migration classes, I [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2009/10/20/doctrine-migrations-proper/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>I was talking with someone (I will edit this post when I find her card and remember her name) here at ZendCon and discovered that they were having trouble with migrations in Doctrine. Having gone through the same issues of Doctrine seemingly not being able to figure out your changes and generate migration classes, I thought I&#8217;d post the solution here for future reference.</p>
<p>Assuming you have access to the Doctrine CLI tool, the sequence you need is:</p>
<ol>
<li>Make the changes to your YAML schema files</li>
<li>Run <code>$ doctrine generate-migrations-diff</code> to generate the migration deltas</li>
<li>Run <code>$ doctrine migrate</code> to determine if your migration deltas are working (a concern if you&#8217;re using SQL Server)</li>
<li>Run <code>$ doctrine generate-models-yaml</code> to update your models to reflect the status of your database</li>
</ol>
<p>The reasoning behind this, and if I&#8217;m wrong someone can correct me, is the &#8220;generate-migrations-diff&#8221; task compares the differences between your YAML schema files and your current <strong>models</strong>. If you have updated your models before generating the diff, obviously Doctrine will find no difference and generate no migration delta.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2009/10/20/doctrine-migrations-proper/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Solve A Sliding Puzzle With JavaScript And Your AI Course: Part 1</title>
		<link>http://www.toosweettobesour.com/2009/10/13/solve-a-sliding-puzzle-with-javascript-and-your-ai-course-part-1/</link>
		<comments>http://www.toosweettobesour.com/2009/10/13/solve-a-sliding-puzzle-with-javascript-and-your-ai-course-part-1/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 22:06:27 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=456</guid>
		<description><![CDATA[In all my years of web development and formal computer science training, you know I never really got around to truly sitting down and learning JavaScript. Sure I knew the syntax (C based, not terribly hard), understood closures (LISP will do that to you), understood the prototype approach to Object Orientation (oddly enough playing with [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2009/10/13/solve-a-sliding-puzzle-with-javascript-and-your-ai-course-part-1/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>In all my years of web development and formal computer science training, you know I never really got around to truly sitting down and learning JavaScript.</p>
<p>Sure I knew the syntax (C based, not terribly hard), understood closures (LISP will do that to you), understood the prototype approach to Object Orientation (oddly enough playing with Python caused it to finally click).</p>
<p>Well <a href="http://thedailywtf.com/Articles/Sliding-Around.aspx">around a month ago</a> <a href="http://thedailywtf.com">The Daily WTF</a> posted one of their weekly programming puzzles.</p>
<p>Lo and behold, it&#8217;s the classic &#8220;Eight Puzzle&#8221; that I had to solve in LISP using several different algorithms!</p>
<p>Feeling particular motivated I decided to finally get around to really learning JavaScript (by learning I mean really knowing the language inside and out, like I know PHP and to a lesser extent Python).</p>
<p>The first thing I had to tackle was: Which algorithm would I choose? There are several to choose from, DFS (depth-first search), BFS (breadth-first search), Greedy, what have you. Well, a testament to my schooling, I do remember the most optimal I could remember was the <strong>A*</strong> algorithm.</p>
<p>The A* not terribly complex once you look at it properly. Most examples discuss the A*, like most navigation algorithms, as a tree structure which, while academically correct, never actually touches a tree data structure.</p>
<p>The A* algorithm works by examining the current node, the possible exit directions, and how much closer the new states after exit bring you to your goal. It arranges these new nodes in a priority queue and recurses on all the new nodes until such time that the current node is the goal node.</p>
<p>In simpler terms: examine the paths that bring you closer to the goal first.</p>
<p>The Eight-Puzzle is an excellent situation in which to first play with this algorithm as all the pieces we need for the algorithm are easy to conceptualize. It&#8217;s just a very simple navigation puzzle: How do we move the blank spot to it&#8217;s final destination (and making sure the other pieces are set too).</p>
<p>By pieces we need for the A* algorithm, I mean:</p>
<ul>
<li><strong>Heuristic</strong> (or &#8220;weight&#8221; of a current path, how far are we from the goal, etc.)</li>
<li><strong>Expansion Function</strong> (from our current node, what are the paths we can take?)</li>
</ul>
<p>The Expansion function is easy. It&#8217;s literally which way can you move the blank spot. If you&#8217;re in the exact center of the board, the expansion function will return UP, RIGHT, DOWN, LEFT. If you&#8217;re in the top left hand corner, the expansion function will return RIGHT, DOWN.</p>
<p>The Heuristic is a little trickier. If we knew the exact distance (number of moves) a certain state in the puzzle is to the finish, we would need a searching algorithm. Instead we estimate (hence this value being called a heuristic).</p>
<p>2 decent estimations would be counting the number of tiles that are not in their final position as well as calculating the <a href="http://en.wikipedia.org/wiki/Taxicab_geometry">manhattan distance</a> between the blank space and it&#8217;s final spot. The manhattan distance is the actual &#8220;walking&#8221; distance as opposed to the straight line distance. Think of the grid street system that is famously used in New York City. While drawing a line from point A to point B may be 2.5<em>ish</em> miles, in reality you walk maybe 4 miles as you must walk north about 2 miles and east about 2 miles.</p>
<p>Even better is combining the two values. As excellent as manhattan distance sounds for an estimation, you run into inconsistencies like an unsolved board where the blank spot is in the correct position being given more weight in searching than a board 1 step away from being solved with the blank spot only 1 block away from the correct (and final) position.</p>
<p>In Part 2 I will step away from the algorithm and step into JavaScript to build our basic utility function (namely creating and managing a board).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2009/10/13/solve-a-sliding-puzzle-with-javascript-and-your-ai-course-part-1/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Development Environment Help! Setting Up PHP mail() On Windows, Or: Where Is My Windows sendmail.exe?</title>
		<link>http://www.toosweettobesour.com/2009/08/21/development-environment-help-setting-up-php-mail-on-windows-or-where-is-my-windows-sendmail-exe/</link>
		<comments>http://www.toosweettobesour.com/2009/08/21/development-environment-help-setting-up-php-mail-on-windows-or-where-is-my-windows-sendmail-exe/#comments</comments>
		<pubDate>Fri, 21 Aug 2009 16:39:00 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[hMailServer]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[php.ini]]></category>
		<category><![CDATA[SMTP]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=439</guid>
		<description><![CDATA[Lately at work I&#8217;ve been having the worst of trouble getting PHP on my workstation to send mail. Previously it was a simple trek to a website to download a Windows build of sendmail.exe, but for some reason I cannot get access to it, so I have to go another route. Enter hMailServer, a free, [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2009/08/21/development-environment-help-setting-up-php-mail-on-windows-or-where-is-my-windows-sendmail-exe/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>Lately at work I&#8217;ve been having the worst of trouble getting PHP on my workstation to send mail. Previously it was a simple trek to a website to download a Windows build of sendmail.exe, but for some reason I cannot get access to it, so I have to go another route.</p>
<p>Enter <a href="http://www.hmailserver.com/">hMailServer</a>, a free, full-featured SMTP/POP/IMAP server for Windows with a pretty, shiny GUI configuration interface. All I really want to do is setup SMTP for localhost only but one could easily use hMailServer as a production mail server.</p>
<p>To start things off, lets make sure our php.ini directives are setup correctly. By correct, I mean PHP needs to be looking for our SMTP server on my local machine (localhost) at the default SMTP port (25), and, just in case, set a default from address:</p>
<pre name="code" class="ini">
[mail function]
; For Win32 only.
smtp = localhost
smtp_port = 25

; For Win32 only.
sendmail_from = user@domain.tld
</pre>
<p>Looks good. Second step is to download and install hMailServer. This tutorial is working from <a href="http://www.hmailserver.com/?page=download_mirrors&#038;downloadid=185">version 5.2 build 356</a>, but you should be able to use the latest stable or unstable version that you might desire.</p>
<p>Once you&#8217;re installed and configured, run the administrative application. If you click over to status you will notice a message to the effect of &#8220;You haven&#8217;t specified the public host name for this computer in the SMTP settings.&#8221; That means we have some more configuration to do!</p>
<p><img src="http://www.toosweettobesour.com/wp-content/uploads/2009/08/hmailserver-1.png" alt="hMailServer Screenshot 1" title="hMailServer Screenshot 1" width="818" height="600" class="aligncenter size-full wp-image-440" /></p>
<p>Now mosey on over to Settings &raquo; Protocols &raquo; SMTP, navigate to the &#8220;Delivery of e-mail&#8221; tab. We set our &#8220;Local host name&#8221; to &#8220;localhost&#8221; and click &#8220;Save&#8221;.</p>
<p><img src="http://www.toosweettobesour.com/wp-content/uploads/2009/08/hmailserver-2.png" alt="hMailServer Screenshot 2" title="hMailServer Screenshot 2" width="818" height="600" class="aligncenter size-full wp-image-442" /></p>
<p>Next, for securities sake (as well as to ensure we don&#8217;t have to waste time configuring account as this is only a dev machine), go to Settings &raquo; Advanced &raquo; IP Ranges &raquo; My Computer. Un-check the POP3 and IMAP checkboxes under the &#8220;Allow Connections&#8221; as we don&#8217;t intend to receive mail, nor use the IMAP protocol. Then, un-check everything under &#8220;Requires SMTP authentication&#8221; as we want our PHP applications to have full reign. Finally, click &#8220;Save&#8221;. Remember, this is a dev box.</p>
<p><img src="http://www.toosweettobesour.com/wp-content/uploads/2009/08/hmailserver-3.PNG" alt="hMailServer Screenshot 3" title="hMailServer Screenshot 3" width="818" height="600" class="aligncenter size-full wp-image-443" /></p>
<p>UNDER NO CIRCUMSTANCES SHOULD YOU USE THESE SETTINGS ON A BOX THAT WILL BE EXPOSING ITSELF TO THE INTERNET OR ON A PRODUCTION SERVER! However, since our local machine will obviously have internet connectivity, lets remove all access from outsiders!</p>
<p>Navigate to Settings &raquo; Advanced &raquo; IP Ranges &raquo; Internet. Un-check EVERYTHING from &#8220;Allow connections&#8221; to deny outsiders any services, and, just in case, check EVERYTHING under &#8220;Require SMTP authentication&#8221;  and click &#8220;Save&#8221;.</p>
<p><img src="http://www.toosweettobesour.com/wp-content/uploads/2009/08/hmailserver-4.PNG" alt="hMailServer Screenshot 4" title="hMailServer Screenshot 4" width="818" height="600" class="aligncenter size-full wp-image-444" /></p>
<p>And <em>voilà</em>! We have a fully functioning SMTP server that our local PHP environment can use to send test email messages.</p>
<p>The nice thing about our administrative interface is if we go to Status, navigate to the &#8220;Logging&#8221; tab, and click &#8220;Start,&#8221; we can capture the dialogue between hMailServer and your PHP app when sending an email address (very useful for debugging).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2009/08/21/development-environment-help-setting-up-php-mail-on-windows-or-where-is-my-windows-sendmail-exe/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Calculating Daylight Savings Time Boundary In PHP</title>
		<link>http://www.toosweettobesour.com/2009/03/10/calculating-daylight-savings-time-boundary-in-php/</link>
		<comments>http://www.toosweettobesour.com/2009/03/10/calculating-daylight-savings-time-boundary-in-php/#comments</comments>
		<pubDate>Tue, 10 Mar 2009 14:30:23 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Daylight Savings Time]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[strtotime]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=397</guid>
		<description><![CDATA[I had an issue recently where I needed to calculate the Unix timestamp for the daylight savings time boundaries. According to the United States Naval Observatory, daylight savings time begins the Second Sunday of March and ends on the First Sunday of November. Awkward date calculations if you don&#8217;t have the magical strtotime() function in [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2009/03/10/calculating-daylight-savings-time-boundary-in-php/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>I had an issue recently where I needed to calculate the Unix timestamp for the daylight savings time boundaries. According to the <a href="http://aa.usno.navy.mil/faq/docs/daylight_time.php">United States Naval Observatory</a>, daylight savings time begins the <strong>Second Sunday of March</strong> and ends on the <strong>First Sunday of November</strong>.</p>
<p>Awkward date calculations if you don&#8217;t have the magical <a href="http://php.net/strtotime"><code>strtotime()</code></a> function in PHP. <code>strtotime()</code> is able to do relativistic time conversions from the common &#8220;+1 hours&#8221; to the more complex (and more relevant) &#8220;Second Sunday March 0&#8243;.</p>
<p>Why do we do the &#8216;March 0&#8242; in the above string to calculate the start boundary? Doing some tests with <code>strtotime()</code> reveals some behavior you should be aware of.</p>
<p><code>strtotime("March");</code> doesn&#8217;t actually give you the first of March, it gives you the current day in the month specified (<code>date('D, F j, Y, g:i a', strtotime("March"));</code> returns <code>Tue, March 10, 2009, 12:00 am</code> at the time of this post). Doing a similar test but substituting April for March results in the 10th of April (at 12 am).</p>
<p>Now in most cases doing a <code>strtotime("March 1")</code> will suffice (<code>date('D, F j, Y, g:i a', strtotime("March 1"));</code> results in <code>Sun, March 1, 2009, 12:00 am</code>). However, as you can tell, this month is going to be awkward because the first day of the month is a Sunday. <code>strtotime()</code>, when calculating the fuzzy &#8220;Second Sunday,&#8221; doesn&#8217;t include the current day. So calculating <code>date('D, F j, Y, g:i a', strtotime("Second Sunday March 1"));</code> will actually return the 3rd Sunday (<code>Sun, March 15, 2009, 12:00 am</code>).</p>
<p>So our solution is actually to take a step back and return the <strong>PREVIOUS</strong> day to the first day of March and then calculate the Second Sunday from there (since we know that starting from the next day will account for the first). Normally, dealing with the last day in February is a headache, BUT again thanks to PHP magic we don&#8217;t have to figure out if it&#8217;s February 28th or 29th, we merely do a &#8220;March 0&#8243; which steps us back a month (<code>date('D, F j, Y, g:i a', strtotime("March 0"));</code> returns <code>Sat, February 28, 2009, 12:00 am</code>). From there we can calculate the Second Sunday with ease and eventually determine that the second sunday is March 8th, which is confirmed by the above USNO website.</p>
<p>Applying these lessons to calculating the first Sunday in November we come up with the following snippit:</p>
<pre name="code" class="php">&lt;?php
$remove_hour = strtotime("Second Sunday March 0");
$add_hour = strtotime("First Sunday November 0");

$time  = time();

if( $time &gt;= $remove_hour &#038;&#038; $time &lt; $add_hour )
{
    var_dump("Lost an hour");
}
else
{
    var_dump("Gained an hour");
}</pre>
<p>Cheers!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2009/03/10/calculating-daylight-savings-time-boundary-in-php/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>PHP File Uploads And Background Conversion: Oopsie</title>
		<link>http://www.toosweettobesour.com/2009/02/26/php-file-uploads-and-background-conversion-oopsie/</link>
		<comments>http://www.toosweettobesour.com/2009/02/26/php-file-uploads-and-background-conversion-oopsie/#comments</comments>
		<pubDate>Thu, 26 Feb 2009 21:53:19 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[File Upload]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=393</guid>
		<description><![CDATA[So I spent an inordinate amount of time tracking down a bug recently for a former employer in a system that accepts media file uploads and converts them on the fly. The system spawns off a small script that manages mplayer/ffmpeg/lame into the background and holds on to the PID to track the conversion process. [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2009/02/26/php-file-uploads-and-background-conversion-oopsie/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>So I spent an inordinate amount of time tracking down a bug recently for a former employer in a system that accepts media file uploads and converts them on the fly.</p>
<p>The system spawns off a small script that manages mplayer/ffmpeg/lame into the background and holds on to the PID to track the conversion process. HOWEVER, there was a problem in the system where large files were being ignored and no error messages were popping up.</p>
<p>What ended up happening was I forgot to take into account PHP&#8217;s file upload behavior in the conversion process.</p>
<p>When PHP accepts a file upload it creates a temporary file (usually in <code>/tmp/</code>). The caveat is PHP frees (read: deletes) the temporary uploaded file at the end of script execution.</p>
<p>So, while the converters could convert a small file in the same encoding as the desired output, a large file (say, a 10mb MPEG) that needed to be converted to a different format (say, a FLV), the converter would begin working in the background but when the PHP script finished executing, PHP would delete the file right out from under the converter&#8217;s feet.</p>
<p>So, a tip to anyone doing anything similar, go ahead and at least rename/move the file (I created a unique MD5 hash from the current time plus a few other things and use that as the new file name) and pass that to any background processing functions so PHP won&#8217;t delete the file at the end of processing.</p>
<p>It will save a WHOLE lot of headaches for you in the future.</p>
<p>Also, if anyone is interested, I will probably write up a tutorial on how to write a PHP script that will convert your MP3&#8242;s in the background.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2009/02/26/php-file-uploads-and-background-conversion-oopsie/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PHP, Mumbles (Growl), and DBus: Sweeet</title>
		<link>http://www.toosweettobesour.com/2009/02/13/php-mumbles-growl-and-dbus-sweeet/</link>
		<comments>http://www.toosweettobesour.com/2009/02/13/php-mumbles-growl-and-dbus-sweeet/#comments</comments>
		<pubDate>Fri, 13 Feb 2009 07:58:30 +0000</pubDate>
		<dc:creator>Daniel Cousineau</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[DBus]]></category>
		<category><![CDATA[Growl]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mumbles]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.toosweettobesour.com/?p=372</guid>
		<description><![CDATA[So, after reading Mark Shuttlework&#8217;s blog on ideas for notifications in Ubuntu (basically mimicking Growl notifications for the Mac), I decided I wanted that kind of functionality, but&#8230; you know, NOW! There are a multitude of options available but currently I&#8217;m liking Mumbles. Unlike, say, Specto, which does the monitoring itself, Mumbles provides a DBus [...]]]></description>
			<content:encoded><![CDATA[<div style="float: right; width: 66px; height: 66px; overflow: hidden; position: relative; left: 8px;"><script>//<![CDATA[
reddit_url="http://www.toosweettobesour.com/2009/02/13/php-mumbles-growl-and-dbus-sweeet/";
//]]&gt;
</script><script language="javascript" src="http://reddit.com/button.js?t=3"></script></div><p>So, after reading <a href="http://www.markshuttleworth.com/archives/253">Mark Shuttlework&#8217;s blog on ideas for notifications in Ubuntu</a> (basically mimicking <a href="http://growl.info/">Growl</a> notifications for the Mac), I decided I wanted that kind of functionality, but&#8230; you know, NOW!</p>
<p>There are a multitude of options available but currently I&#8217;m liking <a href="http://www.mumbles-project.org/">Mumbles</a>. <del datetime="2009-02-17T07:13:24+00:00">Unlike, say, <a href="http://specto.sourceforge.net/">Specto</a>, which does the monitoring itself, Mumbles provides a DBus interface, a command-line app named <code>mumbles-send</code>, and (I&#8217;m not sure if it&#8217;s implemented in the current stable download) libnotify support.</del> Woutc, from the Specto project, commented below explaining that Specto is not intended to be a Mumbles competitor, but a package to easily monitor system internals (I quote <em>&#8220;&#8230;the purposes from mumbles and specto are different…specto is monitoring what happens outside your desktop, mumbles monitors what happens on your desktop (or in your network)&#8230;&#8221;</em>) Apparently he has plans to build a Mumbles plugin so that one can optionally have Specto send its messages to Mumbles for display.</p>
<p>I decided the <del>best</del> easiest route is to access the internal DBus API, however the forums and other resources on the Mumbles site&#8230; well&#8230; just plain suck. And by suck I mean tell you that something exists and&#8230; thaaats about it.</p>
<div id="attachment_375" class="wp-caption alignright" style="width: 310px"><a href="http://www.toosweettobesour.com/wp-content/uploads/2009/02/d-feet.png" rel="lightbox[372]"><img src="http://www.toosweettobesour.com/wp-content/uploads/2009/02/d-feet-300x242.png" alt="D-Feet browsing Mumble&#039;s Growl interface" title="D-Feet Growl Interface Screenshot" width="300" height="242" class="size-medium wp-image-375" /></a><p class="wp-caption-text">D-Feet browsing Mumble's Growl interface</p></div>
<p>Well in my Google quest I discovered the existence of <a href="https://fedorahosted.org/d-feet/">D-Feet</a>, a DBus debugging tool (on <a href="http://arstechnica.com/open-source/news/2008/01/learning-from-d-feet-a-quick-look-at-a-new-d-bus-debugger.ars">Ars</a> of all places). Thanks to a quick <code>sudo apt-get install d-feet</code> I found the existence of a public interface in <code>info.growl.Growl</code> that allows for a <code>Notify(title, message)</code> signal to be passed.</p>
<p>Well, with a place to access all I needed was DBus integration with PHP (because I want to start sending debug notification via Growl/Mumbles like <a href="http://framework.zend.com/wiki/pages/viewpage.action?pageId=8454257">this idea</a> on the Zend Framework incubator).</p>
<p>Luckily, GREE Labs provides a <a href="http://labs.gree.jp/Top/OpenSource/DBus-en.html">DBus C extension for PHP</a> that was easily downloaded and installed. Once installed I read <a href="http://labs.gree.jp/Top/OpenSource/DBus/Document-en.html">the documentation</a> to get an idea on how to use the API (which is a 1:1 mapping to the <a href="http://dbus.freedesktop.org/doc/api/html/">DBus API</a>).</p>
<p>After a bit of hacking I finally came up with an alpha product:</p>
<pre name="code" class="php">&lt;?php
function null_callback()
{
        var_dump(func_get_args());
}

$dbus = dbus_bus_get(DBUS_BUS_SESSION);

$m = new DBusMessage(DBUS_MESSAGE_TYPE_SIGNAL);

$m->setPath('/info/growl/Growl');
$m->setInterface('info.growl.Growl');
$m->setMember('Notify');
$m->appendArgs('Hello World!');
$m->appendArgs('Lorem Ipsum Dolor Sit Amet. (' . time() . ')');

$r= $dbus->send($m);
</pre>
<p>After running the script in the console, did I see success?</p>
<div id="attachment_378" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.toosweettobesour.com/wp-content/uploads/2009/02/mumbles-php.png" rel="lightbox[372]"><img src="http://www.toosweettobesour.com/wp-content/uploads/2009/02/mumbles-php-300x67.png" alt="Mumbles and PHP Test: Success!" title="Mumbles and PHP Test: Success!" width="300" height="67" class="size-medium wp-image-378" /></a><p class="wp-caption-text">Mumbles and PHP Test: Success!</p></div>
<p>Damn right I did!</p>
<p><del datetime="2009-02-17T07:17:02+00:00">Though with a caveat:</p>
<blockquote><p><code>Warning: dbusconnection::sendwithreplyandblock(): dbus_connection_send_with_reply_and_block() failed (Traceback (most recent call last):<br />
  File "/var/lib/python-support/python2.5/dbus/service.py", line 643, in _message_cb<br />
    (candidate_method, parent_method) = _method_lookup(self, method_name, interface_name)<br />
  File "/var/lib/python-support/python2.5/dbus/service.py", line 244, in _method_lookup<br />
    raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name, dbus_interface))<br />
UnknownMethodException: org.freedesktop.DBus.Error.UnknownMethod: Unknown method: Notify is not a valid method of interface info.growl.Growl<br />
) in /home/daniel/test.php on line 21</code></p>
<p><code>Call Stack:<br />
    0.0002      64496   1. {main}() /home/daniel/test.php:0<br />
    0.0024      66372   2. dbusconnection->sendwithreplyandblock() /home/daniel/test.php:21<br />
</code></p></blockquote>
<p>Apparently Mumbles seems to be throwing an Exception that it&#8217;s failing to to find the Notify signal (though everything works correctly). I guess I could ignore this, use the error suppressor (<code>@</code>), or even yell at GREE Labs to have the objects throw exceptions so I can catch them&#8230; I guess I&#8217;ll hack on it some more and report back. I should probably spend more time learning the DBus specs since this is my first project playing with DBus&#8230;</del></p>
<p><strong>Update:</strong> Though it&#8217;s not listed on the API page for PHP DBus&#8217;s API, there is a method <code>send()</code> that takes only a single argument (the message object).</p>
<p><strong>Edit:</strong></p>
<p>I saw a question on Reddit asking what is the use of this technique if it&#8217;s limited to the desktop it was called on and PHP is primarily a server side language. Why not do this in Python or Perl?</p>
<p>Well, Mumbles was written in Python so there&#8217;s no point in me doing this in Python: it&#8217;s already been done.</p>
<p>However, the primary use case of a technique like this is having a web app post notifications and errors for the developer. When I work on a site I have a local copy running on my desktop and/or laptop, so when it posts Growl/Mumbles notifications, I get them on my desktop. It&#8217;s great for situations where, say, I have a page in my PHP app that never displays its contents because it processes data then redirects to another page. If a warning or other non-fatal error that I should really fix occurs, then I would normally have to dig through the system wide PHP error log (if you even have error logging enabled). However, if I wrote a custom error handler that posts errors to Growl/Mumbles as they happen, when I visit a transitory page like I described I get little Growl/Mumbles notifications showing that I screwed up!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.toosweettobesour.com/2009/02/13/php-mumbles-growl-and-dbus-sweeet/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>

