<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Dave Dash</title>
 <link href="http://davedash.com/tag/php/atom.xml" rel="self"/>
 <link href="http://davedash.com/tag/php"/>
 <updated>2012-01-17T21:54:19-08:00</updated>
 <id>http://davedash.com/</id>
 <author>
   <name>Dave Dash</name>
   <email>dd+atom1@davedash.com</email>
 </author>

 
 <entry>
   <title>Versioning Django Models</title>
   <link href="http://davedash.com/2009/02/13/versioning-django-models/"/>
   <updated>2009-02-13T00:00:00-08:00</updated>
   <id>http://davedash.com/2009/02/13/versioning-django-models</id>
   <content type="html">&lt;p&gt;In symfony, versioning a model was not terribly difficult.  I had my own specialized brute-force way of doing this.&lt;/p&gt;

&lt;p&gt;In my experience it's been a lot easier to write python code than PHP code, so naturally I figured this would be an easy task.  It was not.  I suspect that it was not easy because I have a naive understanding of Django and my symfony knowledge was fairly well grounded.&lt;/p&gt;

&lt;p&gt;I tried looking for a generic implementation of django model versioning, but failed.  So I came up with a specific solution.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;How I think of versions&lt;/h3&gt;

&lt;p&gt;For my app I chose to use a sparse versioning system.  I'd have one interface for interacting with a model.  For example, I have a &lt;code&gt;Restaurant&lt;/code&gt; model  and a &lt;code&gt;RestaurantVersion&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Restaurant&lt;/code&gt; would directly store immutable elements like &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;rating&lt;/code&gt; or &lt;code&gt;approved&lt;/code&gt; and it would encapsulate versioned elements like &lt;code&gt;description&lt;/code&gt; by proxying to &lt;code&gt;RestaurantVersion&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This means I only need to interface with one class and let the versioning happen behind the scenes.&lt;/p&gt;

&lt;h3&gt;The Django implementation of &lt;code&gt;RestaurantVersion&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;RestaurantVersion&lt;/code&gt; held the data that I thought might suffer from corruption, and therefore require reversion.  Therefore there's nothing surprising in this table:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
class RestaurantVersion(models.Model):
    restaurant       = models.ForeignKey('Restaurant', null=True, blank=True)
    user             = models.ForeignKey(Profile, null=True, blank=True)
    description      = models.TextField(blank=True)
    html_description = models.TextField(blank=True)
    url              = models.CharField(max_length=765, blank=True)
    created_at       = models.DateTimeField(auto_now_add=True)
    
    def save(self, force_insert=False, force_update=False):
        self.html_description = markdown(self.description)
        super(RestaurantVersion, self).save(force_insert, force_update)
        
    class Meta:
        db_table = u'restaurant_version'

&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The only thing of note is that I'm enforcing a &lt;code&gt;1:M&lt;/code&gt; relation between &lt;code&gt;Restaurant&lt;/code&gt; and &lt;code&gt;RestaurantVersion&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;The Django implementation of &lt;code&gt;Restaurant&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Restaurant&lt;/code&gt; is where the magic happens.  It needs to do a few things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do what a normal object does.&lt;/li&gt;
&lt;li&gt;Proxy attributes to the corresponding attribute of a &lt;code&gt;RestaurantVersion&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Generate a new &lt;code&gt;RestaurantVersion&lt;/code&gt; on demand.&lt;/li&gt;
&lt;li&gt;(optional) Manage which version is &quot;active&quot;.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;I say &quot;4&quot; is optional because I don't do this myself.  I built versioning into a few models because it was easier to do upfront than worry about it down the road.&lt;/p&gt;

&lt;p&gt;Here's the code I use:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;

class Restaurant(models.Model):
    name           = models.CharField(max_length=765, blank=True)
    stripped_title = models.CharField(max_length=384, blank=True)
    approved       = models.IntegerField(null=True, blank=True)
    version        = models.ForeignKey(RestaurantVersion, related_name=&quot;the_restaurant&quot;)
    updated_at     = models.DateTimeField(auto_now=True)
    created_at     = models.DateTimeField(auto_now_add=True)
    new_version    = None
    
    def save(self, force_insert=False, force_update=False):
        if not self.stripped_title:
            self.stripped_title = slugify(self.name)
        
        super(Restaurant, self).save(force_insert, force_update)
        if self.new_version:
            self.new_version.restaurant = self
            self.new_version.save()
            self.version = self.new_version
            super(Restaurant, self).save(force_insert, force_update)        
        
    def __setattr__(self, name, value):
        if name == 'description':
            self.get_new_version().description = value
        
        elif name == 'url':
            self.get_new_version().url = value
        
        else:
            object.__setattr__(self, name, value)

    def __getattr__(self, name):
        try:
            if name == 'description':
                return self.version.description

            elif name == 'url':
                return self.version.url
        except RestaurantVersion.DoesNotExist:
            return ''
            
        models.Model.__getattribute__(self, name)
    
    def get_new_version(self):
        if self.new_version == None:
            try:
                rv    = self.version
                rv.id = None
            except RestaurantVersion.DoesNotExist:
                rv = RestaurantVersion()
            
            self.new_version = rv

        return self.new_version
            
    class Meta:
        db_table     = u'restaurant'
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;It's not rocket science, but it wasn't necessarily easy either.  Let's look at requirements 2 and 4 in a bit more detail.&lt;/p&gt;

&lt;h4&gt;Proxy attributes to the corresponding &lt;code&gt;RestaurantVersion&lt;/code&gt; attributes&lt;/h4&gt;

&lt;p&gt;Proxying attributes is a way of masking the whole versioning infrastructure from the developer.  We do this with &lt;code&gt;__getattr__&lt;/code&gt; and &lt;code&gt;__setattr__&lt;/code&gt; methods.&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
    def __getattr__(self, name):
        try:
            if name == 'description':
                return self.version.description

            elif name == 'url':
                return self.version.url
        except RestaurantVersion.DoesNotExist:
            return ''
            
        models.Model.__getattribute__(self, name)
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;__getattr__&lt;/code&gt; determins if you are looking for &lt;code&gt;description&lt;/code&gt; or &lt;code&gt;url&lt;/code&gt; attributes of a &lt;code&gt;Restaurant&lt;/code&gt;.  If you are then it uses the current &lt;code&gt;RestaurantVersion&lt;/code&gt;'s attribute.&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
    def __setattr__(self, name, value):
        if name == 'description':
            self.get_new_version().description = value
        
        elif name == 'url':
            self.get_new_version().url = value
        
        else:
            object.__setattr__(self, name, value)
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;__setattr__&lt;/code&gt; is very similar, except since we're changing a versionable attribute we're going to make a request to a method, &lt;code&gt;get_new_version()&lt;/code&gt;, which we will detail later.  It does what it says, though, it gets the &quot;new&quot; &lt;code&gt;RestaurantVersion&lt;/code&gt; of the &lt;code&gt;Restaurant&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;Generate a new &lt;code&gt;RestaurantVersion&lt;/code&gt; on demand&lt;/h4&gt;

&lt;p&gt;As you can see from the above code, I am &quot;auto-versioning&quot;.  This is done mostly via &lt;code&gt;get_new_version()&lt;/code&gt;.  We also have to do a few tricks to make sure the bidirectional relationship gets maintained on save.&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
    def get_new_version(self):
        if self.new_version == None:
            try:
                rv    = self.version
                rv.id = None
            except RestaurantVersion.DoesNotExist:
                rv = RestaurantVersion()
            
            self.new_version = rv

        return self.new_version
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;get_new_version()&lt;/code&gt; either returns the current &quot;new version&quot;, a brand new &quot;new version&quot; or a &quot;copy&quot; of the current version.&lt;/p&gt;

&lt;p&gt;The &quot;copy&quot; is done simply by setting the &lt;code&gt;id&lt;/code&gt; attribute of the new version to &lt;code&gt;None&lt;/code&gt;.  Django takes care of assigning it a new &lt;code&gt;id&lt;/code&gt; on save, thus preserving the old version.&lt;/p&gt;

&lt;p&gt;Note that when we create a brand new &lt;code&gt;RestaurantVersion&lt;/code&gt;, we don't immediately set it's &lt;code&gt;restaurant&lt;/code&gt; attribute.  That's because we haven't saved the current &lt;code&gt;restaurant&lt;/code&gt; yet.  It's a &quot;chicken and the egg&quot; problem that gets solved in our &lt;code&gt;save()&lt;/code&gt; method:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
    def save(self, force_insert=False, force_update=False):
        if not self.stripped_title:
            self.stripped_title = slugify(self.name)
        
        super(Restaurant, self).save(force_insert, force_update)
        if self.new_version:
            self.new_version.restaurant = self
            self.new_version.save()
            self.version = self.new_version
            super(Restaurant, self).save(force_insert, force_update)
        
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We first save the &lt;code&gt;Restaurant&lt;/code&gt;, then we save a new version if there is one.  If we save a new version we want to update the &lt;code&gt;Restaurant&lt;/code&gt; a second time.  This ensures that there's a &lt;code&gt;1:1&lt;/code&gt; relationship between &lt;code&gt;Restaurant&lt;/code&gt; and the current &lt;code&gt;RestaurantVersion&lt;/code&gt; and it also establishes a &lt;code&gt;1:M&lt;/code&gt; relationship between &lt;code&gt;Restaurant&lt;/code&gt; and &lt;em&gt;all&lt;/em&gt; &lt;code&gt;RestaurantVersion&lt;/code&gt;s.&lt;/p&gt;

&lt;h3&gt;Conclusion and Drawbacks&lt;/h3&gt;

&lt;p&gt;While, I think that this setup works in my particular situation, I feel like it has some major flaws.&lt;/p&gt;

&lt;p&gt;The code is quite messy and very specific to this model.  My goal was to take care of this quickly, not necessarily in a reusable manner.  I also was not familiar enough with the Django model to create some sort of extension.&lt;/p&gt;

&lt;p&gt;This way of versioning does not allow for versioned attributes to be set upon instantiation of the model:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;r=Restaurant(description=&quot;Great place&quot;) 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;won't work.  You'll have to do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;r=Restaurant()
r.description=&quot;Great place&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I figured this was acceptable.&lt;/p&gt;

&lt;p&gt;Lastly I'm not entirely happy with having to explicitly save the &lt;code&gt;Restaurant&lt;/code&gt; model twice, but I think my bidirectional relation requires this.&lt;/p&gt;

&lt;p&gt;All in all this works in my particular situation.  I'm hoping that this can be simplified.  I'm curious to hear about other versioning schemes.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>PHPs strengths: array_count_values</title>
   <link href="http://davedash.com/2008/12/07/phps-strengths-array_count_values/"/>
   <updated>2008-12-07T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/12/07/phps-strengths-array_count_values</id>
   <content type="html">&lt;p&gt;I always like to think of what different interpreted programming languages bring to the table.&lt;/p&gt;

&lt;p&gt;Perl is great with string manipulation.  I tend to use perl if I need to rewrite/reformat text.&lt;/p&gt;

&lt;p&gt;Python makes it easy to write clean, organized code and has a lot of syntactical sugar like list comprehensions.&lt;/p&gt;

&lt;p&gt;For PHP one of my coworkers suggested that maybe it's ease is what it brings to the table.  I've accepted that wryly at first, but after some thought it's actually a real strength.&lt;/p&gt;

&lt;p&gt;PHP has positioned itself to be easily adoptable.  When I learned PHP/FI 2.0 it was because I had exhausted server-side includes and needed something better.  PHP/FI was a great solution because it was easy to install (my ISP had it) and documented easy things that I could do.  After all it was just some additional changes to my templates.&lt;/p&gt;

&lt;p&gt;I never for once thought that I'd be paid at a company to code in it... but then the internet just got big.&lt;/p&gt;

&lt;p&gt;The other way php positioned itself is the library of functions it has out of the box.  Today I found myself looking for a python equivalent to &lt;a href=&quot;http://www.php.net/array_count_values&quot;&gt;&lt;code&gt;array_count_values&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Its a function I wouldn't use every day, but there are rare instances where having a set of data aggregated by count would be very useful.  That's exactly what this function does.&lt;/p&gt;

&lt;p&gt;Even python's lists, sets and dictionaries, which can do with simple operations what PHP needs an army of &lt;code&gt;array_&lt;/code&gt; functions to do, didn't reveal anything useful.  So I will use the following:&lt;/p&gt;

&lt;div&gt;
&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
def list_count_values(l):
    d = {}
    
    for item in l:
        if item in d:
            d[item] += 1
        else:
            d[item] = 1
    
    return d
&lt;/textarea&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Optimization heuristics</title>
   <link href="http://davedash.com/2008/02/26/optimization-heuristics/"/>
   <updated>2008-02-26T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/02/26/optimization-heuristics</id>
   <content type="html">&lt;p&gt;I decided to play version control private-eye today when my coworker mentioned that we make a system call to check the time several times per request on a few specific pages.&lt;/p&gt;

&lt;p&gt;My analysis was we didn't need to have the calls but it had me thinking...&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;The micro-caching heuristic&lt;/h3&gt;

&lt;p&gt;One way of optimizing code is by calculating expensive operations once and storing them in a specific location.  It's a micro-cache if you will of data.&lt;/p&gt;

&lt;p&gt;One obvious example ends up being loops:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
// inefficient
for ($i = 0; $i &lt; count($arr); $i++)
{
// magic
}

// efficient
$arr_size = count($arr);
for ($i = 0; $i &lt; $arr_size; $i++)
{
// magic
}
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The inefficient example computes &lt;code&gt;count($arr)&lt;/code&gt; &lt;em&gt;n&lt;/em&gt; times where &lt;em&gt;n&lt;/em&gt; is the size of the array &lt;code&gt;$arr&lt;/code&gt;.  The latter computes it once.&lt;/p&gt;

&lt;p&gt;When we write and review code we should look out for loops and repeated code.  These elements can be pulled out.&lt;/p&gt;

&lt;p&gt;With some data lookups like system calls or queries we can apply a request-wide caching.  In a symfony app, for example, we might need to calculate the time stamp in several locations throughout the code.  Rather than calling &lt;code&gt;time()&lt;/code&gt; throughout the code, we can create a site wide accessible attribute that has the same value.&lt;/p&gt;

&lt;p&gt;For example, we can subclass &lt;code&gt;sfContext&lt;/code&gt; and add a method called &lt;code&gt;getTime()&lt;/code&gt; which runs &lt;code&gt;time()&lt;/code&gt; once and only once per request and stores it internally.  Whenever part of the app needs the time stamp, &lt;code&gt;myContext::getInstance()-&amp;gt;getTime()&lt;/code&gt; (or an equivalent shortcut) can be called.  Of course, I haven't tested this, and &lt;code&gt;myContext&lt;/code&gt; might not be the best place for this, but a similar strategy will work.&lt;/p&gt;

&lt;p&gt;-dd&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>py vs php: stemming</title>
   <link href="http://davedash.com/2008/02/16/py-vs-php-stemming/"/>
   <updated>2008-02-16T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/02/16/py-vs-php-stemming</id>
   <content type="html">&lt;p&gt;I've been porting some PHP to python during SuperHappyDevHouse and was amazed at how little code I needed to write since python makes list manipulation a breeze.&lt;/p&gt;

&lt;p&gt;Today I was working on stemming (ala &lt;a href=&quot;http://tartarus.org/martin/PorterStemmer/&quot;&gt;Porter Stemming algorithm&lt;/a&gt;).  &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; uses stemming in the search engine to make queries:&lt;/p&gt;

&lt;p&gt;Stemming turns &lt;code&gt;hello everybody how are you guy's&lt;/code&gt; into a collection &lt;code&gt;'everybodi', 'gui', 'hello'&lt;/code&gt;.  To produce this in php I do the following:&lt;/p&gt;

&lt;!--more--&gt;




&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public static function stemPhrase($phrase)
    {
        // remove apostrophe's and periods
        $phrase = strtolower(str_replace(array('\'', '.'), null, $phrase));
        
        // split into words
        $words = str_word_count($phrase, 1);

        // ignore stop words
        $words = array_diff($words, STOP_WORDS_ARRAY);

        // stem words
        $stemmed_words = array();

        foreach ($words as $word)
        {
            $stemmed_words[] = PorterStemmer::stem($word, true);
        }

        return $stemmed_words;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;With some magic python:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
def stem_phrase(phrase):
    words = phrase.lower().replace('.','').replace(&quot;'&quot;,'').split()

    # ignore stop words
    words = list(set(words)-set(STOP_WORDS))

    p = PorterStemmer()
    
    return [p.stem(word,0,len(word)-1) for word in words]

&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The magic here is list mappings.  Learning about them, they don't seem that great, but as soon as you start coding you stop using a lot of for loops.&lt;/p&gt;

&lt;p&gt;I'm sure my PHP can be cleaned up and reduced as well, but its fun exploiting the magic of languages.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>the magic of django: get_callable</title>
   <link href="http://davedash.com/2008/02/08/the-magic-of-django-get_callable/"/>
   <updated>2008-02-08T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/02/08/the-magic-of-django-get_callable</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; is an amazing framework simply because of the uphill battle it has
with PHP.  There's a lot of behind the scenes magic that ameliorates the
frustrations one might have with PHPs lack of syntactical magic.&lt;/p&gt;

&lt;p&gt;A lot of work is done to make sure certain things are autoloaded and that
certain things behave the way you expect.&lt;/p&gt;

&lt;p&gt;I hadn't had a chance to peek under Django's hood too much myself, but it
appears that despite the inherent magic of python, there were some much needed
additions.  My savior for today was &lt;code&gt;get_callable&lt;/code&gt;.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;I have a very particular vision of interacting with OpenID.  Basically I like
to decouple as much of the OpenID interactions away from the inner workings of
my web site.  I do, however, want a hook where I can supply some of my own
logic after OpenID has been verified.&lt;/p&gt;

&lt;p&gt;It's roughly how I implimented the sfOpenIDPlugin.   Luckily this time, I was
able to rely on the &lt;a href=&quot;http://www.openidenabled.com/openid/libraries/python/&quot;&gt;OpenID 2.1
libraries&lt;/a&gt; from
OpenIDEnabled.&lt;/p&gt;

&lt;p&gt;I did ditch their example code, because I wanted a sexy package.  The package
isn't quite ready for primetime, but you can &lt;a href=&quot;http://svn.spindrop.us/django/trunk/&quot;&gt;have a
gander&lt;/a&gt; (and yes, I do welcome
collaborators and am open to merging my work with the myriad of work that's out
there).&lt;/p&gt;

&lt;h3&gt;Getting to the point&lt;/h3&gt;

&lt;p&gt;Anyway, &lt;code&gt;get_callable&lt;/code&gt; is what allowed me to write my &quot;hook&quot; into my OpenID
logic.&lt;/p&gt;

&lt;p&gt;In my &lt;code&gt;settings.py&lt;/code&gt; I defined:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;OPENID_SUCCESS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;myproject.myapp.views.openid_handler&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The way my OpenID library works, is it does it's business and when it verifies
a url belongs to someone it then delivers he request to the &lt;code&gt;OPENID_SUCCESS&lt;/code&gt;
hook.  Here's how:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OPENID_SUCCESS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_callable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OPENID_SUCCESS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;get_callable&lt;/code&gt; works some python magic by splitting
&lt;code&gt;myproject.myapp.views.openid_handler&lt;/code&gt; into the module: &lt;code&gt;myproject.myapp.views&lt;/code&gt;
and the attribute &lt;code&gt;openid_handler&lt;/code&gt;.  It uses this to produce a callable object.
In the example above I gave it parameters of &lt;code&gt;request&lt;/code&gt; and &lt;code&gt;result&lt;/code&gt; as they
were required by my app.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why Django Templating is awesome and why I get smarty again</title>
   <link href="http://davedash.com/2008/02/01/why-django-templating-is-awesome-and-why-i-get-smarty-again/"/>
   <updated>2008-02-01T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/02/01/why-django-templating-is-awesome-and-why-i-get-smarty-again</id>
   <content type="html">&lt;p&gt;I get Smarty thanks to django... yeah, it's weird.&lt;/p&gt;

&lt;p&gt;Back to my &lt;a href=&quot;http://spindrop.us/2008/01/28/templating/&quot;&gt;original comment about templating&lt;/a&gt;, smarty really is trying to limit the scope of PHP in a good way.  Too often I see a lot of heavy-lifting in the templates.  It's so bad it makes my MVC'd brain explode.&lt;/p&gt;

&lt;p&gt;Django templates are very limited, based on a philosophy of keeping view logic and only view logic in the templates.&lt;/p&gt;

&lt;p&gt;This is what smart tries to do and it's a reasonable solution to the fact that PHP is a templating language with a kitchen sink.  It's saying, okay... well let's treat PHP as a programming language, and keep Smarty as the template.&lt;/p&gt;

&lt;p&gt;symfony of course says (well not really, or in any official capacity, but would you believe... frameworks talk to me), some PHP is view and some is model/controller.  We'll suggest which ones are which, but really we're not getting in the way of making your life a living hell by sticking complicated business logic inline.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Templating</title>
   <link href="http://davedash.com/2008/01/28/templating/"/>
   <updated>2008-01-28T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/28/templating</id>
   <content type="html">&lt;p&gt;[tags]smarty, symfony, php, python, django[/tags]&lt;/p&gt;

&lt;p&gt;I used to use the Smarty templating system quite heavily in my PHP apps.  Then after switching over to &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, I broke that habit.&lt;/p&gt;

&lt;p&gt;PHP &lt;em&gt;is&lt;/em&gt; a templating language for better or worse.  Smarty was a way of saying that &quot;well things could be easier&quot;, but that's against the grain of what PHP fundamentally is.&lt;/p&gt;

&lt;p&gt;One frustration of PHP is this.  A &quot;pure&quot; PHP file with no HTML looks like this:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    &lt;?php
    
    // code goes here
    // ...
    
    // end of file
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;PHP is an HTML templating system, by default everything in PHP is just stuff waiting to be sent untouched to the browser.  So in essence, PHP web apps aren't really web apps... they are very complicated templates all talking to each other.&lt;/p&gt;

&lt;p&gt;So you'll note in the above example, even a pure PHP file must begin with &lt;code&gt;&amp;lt;?php&lt;/code&gt;, and signal that we're actually doing code.  In fact, to prevent any unintended whitespace being sent to the browser, a PHP only file will by convention omit the closing &lt;code&gt;?&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So my shiny new Django framework is a breath of fresh air.  There's no &lt;code&gt;&amp;lt;?py&lt;/code&gt; tags, it's all just python.  And template files are explicitly template files parsed by a template engine built on top of a scripting language.&lt;/p&gt;

&lt;p&gt;I can easily see PHP evolving into something similar one day.  Where all PHP files are just PHP by default.  It would be more in line with the way MVC development is going.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>python: relative paths</title>
   <link href="http://davedash.com/2008/01/27/python-relative-paths/"/>
   <updated>2008-01-27T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/27/python-relative-paths</id>
   <content type="html">&lt;p&gt;So I started yesterday with Django, and I decided I didn't want to futz with creating another mysql database that I'd need to manage, etc.  Instead I'll just use &lt;code&gt;sqlite&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I wanted to keep my &lt;code&gt;sqlite&lt;/code&gt; database within my project regardless of where I might move my project later.  So I did this:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
    import os        
    DATABASE_NAME = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/db.sqlite')        # Or path to database file if using sqlite3.
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;I confused a lot of people on IRC, but it's really quite easy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;__file__&lt;/code&gt; is the filename of the current script, very similar to PHP's &lt;code&gt;__FILE__&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;os.path.abspath&lt;/code&gt; calculates the absolute path, hence the absolute path of the current file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;os.path.join&lt;/code&gt; does all the nasty business of joining paths together and figuring out what type of slashes are needed, etc.&lt;/li&gt;
&lt;li&gt;'data/db.sqlite' is a string&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;So really all we were doing is creating a relative path, but setting it absolutely.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>python: it begins</title>
   <link href="http://davedash.com/2008/01/26/python-it-begins/"/>
   <updated>2008-01-26T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/26/python-it-begins</id>
   <content type="html">&lt;p&gt;[tags]php, python, symfony, frameworks, programming[/tags]&lt;/p&gt;

&lt;p&gt;The problem I have with python, is I really like what I can do with it, and it makes me hate that I have to use PHP.&lt;/p&gt;

&lt;p&gt;PHP works.  I've used it for over 10 years without problem.  My web development methodology was this: I want to do &lt;code&gt;x&lt;/code&gt;, but I need to first learn &lt;code&gt;y&lt;/code&gt;.  Well one day &lt;code&gt;y&lt;/code&gt; became PHP/FI and then PHP 3 and then PHP 4 and then PHP5.  It was a natural progression.&lt;/p&gt;

&lt;p&gt;I came from a (what was at the time) traditional C/C++/Java background and decided that PHP while amateurish was still a better solution for web development.  A few years later I was proven right by companies like Yahoo! and Facebook.&lt;/p&gt;

&lt;p&gt;I did have my issues, and I worked through them, symfony and other frameworks like it, have made PHP development so much better, but eventually you get let in on little secrets.  Secrets like other scripting languages.&lt;/p&gt;

&lt;p&gt;In the last year I dabbled with ruby, but decided I didn't want to look at rails.  A few people pointed me in the direction of python and Django as an alternative to PHP alternatives.  So I've spent time learning python.&lt;/p&gt;

&lt;p&gt;Yesterday and today I gave myself a real project.  I started hacking away at del.icio.us for a Winter Hack Day at Yahoo!  I would write a few lines of code, and then realize that I can write it a little better a little cleaner using some neat little python constructs.  It's enjoyable in the way that PHP is not.&lt;/p&gt;

&lt;p&gt;It might just be a phase, but I sincerely doubt it.  A lot of people I know who use python love it.  Many have been using it for years (even while I secretly thought, &quot;python? really?&quot;  Well, I'm starting to think they've been onto something.  We'll see where I end up.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Fixing broken PATH_INFO</title>
   <link href="http://davedash.com/2008/01/23/fixing-broken-path_info/"/>
   <updated>2008-01-23T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/23/fixing-broken-path_info</id>
   <content type="html">&lt;p&gt;[tags]php, cgi, path_info, nginx, symfony[/tags]&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; and other applications rely on the server's &lt;code&gt;PATH_INFO&lt;/code&gt; being set properly.  According to &lt;a href=&quot;http://hoohoo.ncsa.uiuc.edu/cgi/env.html&quot;&gt;NCSA&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;The extra path information, as given by the client. In other words, scripts can be accessed by their virtual pathname, followed by extra information at the end of this path. The extra information is sent as `PATH_INFO`. This information *should be decoded by the server* if it comes from a URL before it is passed to the CGI script.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Unfortunately, I use a nonstandard server that doesn't natively support CGI, so everything sent to the FastCGI server is done so via parameters that are usually obtained from the HTTP request, but I can't figure out how to do a &lt;code&gt;urldecode&lt;/code&gt; in my configuration.&lt;/p&gt;

&lt;p&gt;So to workaround this I used the &lt;code&gt;auto_prepend_file&lt;/code&gt; directive in &lt;code&gt;php.ini&lt;/code&gt;.  With OP code caching this shouldn't hurt too much:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;auto_prepend_file = /var/www/pathinfofix.php
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I then added the following script:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php 
$_SERVER['PATH_INFO'] = urldecode($_SERVER['ORIG_PATH_INFO']);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Voila, the &lt;code&gt;PATH_INFO&lt;/code&gt; is in a format that symfony (and any other PHP script that depends on &lt;code&gt;PATH_INFO&lt;/code&gt;) needs.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>doctine and getState()</title>
   <link href="http://davedash.com/2007/07/10/doctine-and-getstate/"/>
   <updated>2007-07-10T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/07/10/doctine-and-getstate</id>
   <content type="html">&lt;p&gt;[tags]doctrine, php, symfony, sfDoctrine, database,errors[/tags]&lt;/p&gt;

&lt;p&gt;I tend to have models with a field called &lt;code&gt;state&lt;/code&gt;.  Doctrine offers a few ways of getting to the &lt;code&gt;state&lt;/code&gt; field:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$obj-&amp;gt;get('state');
$obj['state'];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;$obj-&amp;gt;getState()&lt;/code&gt; however conflicts with &lt;code&gt;Doctrine_Record::getState()&lt;/code&gt; from which all objects inherit.  Use one of the above alternatives.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Using sfDoctrine to match allowed email domains</title>
   <link href="http://davedash.com/2007/07/09/using-sfdoctrine-to-match-allowed-email-domains/"/>
   <updated>2007-07-09T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/07/09/using-sfdoctrine-to-match-allowed-email-domains</id>
   <content type="html">&lt;p&gt;[tags]php, propel, doctrine, validators, symfony, optiopt, startup[/tags]&lt;/p&gt;

&lt;p&gt;I'm a co-founder at an online finance web site and I'm in charge with building out the site.  Our rollout strategy is to let a a handful of companies at a time, so we're limiting registration based on your company's email address.&lt;/p&gt;

&lt;p&gt;I decided to follow the bandwagon and use PHP Doctrine.  We'll define two objects, &lt;code&gt;Company&lt;/code&gt; and &lt;code&gt;CompanyDomain&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Company:
  tableName: company
  columns:
    name: {type: string(100)}
    created_at: timestamp
    updated_at: timestamp

CompanyDomain:
  tableName: company_domain
  columns:
    company_id:
      foreignClass: Company
      cascadeDelete: true
    pattern: {type: string(100)}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Where a &lt;code&gt;CompanyDomain&lt;/code&gt; represents a domain name for a company.  E.g. Motorola might have both &lt;code&gt;motorola.com&lt;/code&gt; and &lt;code&gt;mot.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can validate a signup form and see if we've got an email address from a domain we recognize.  I like using the &lt;code&gt;validationXXX&lt;/code&gt; methods in an action class for specific validation.  I made one called &lt;code&gt;validateSignup&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function validateSignup()
{
    if ($this-&amp;gt;isPost())
    {
        $email = $this-&amp;gt;getRequestParameter('company_email');
        if (!$company = CompanyTable::match($email))
        {
            $this-&amp;gt;getRequest()-&amp;gt;setError('company_email', 'Your work email doesn\'t appear to belong to any of the registered companies.');
            return false;
        }
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In our &lt;code&gt;CompanyTable&lt;/code&gt; we create a static function &lt;code&gt;CompanyTable::match&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;static public function match($email)
{
    $company = Doctrine_Query::create()-&amp;gt;from('Company c, c.CompanyDomains d')-&amp;gt;where('? LIKE CONCAT(\'%\', pattern)', $email)-&amp;gt;execute()-&amp;gt;getFirst();

    return $company;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that we transparently do a join in the &lt;code&gt;-&amp;gt;from()&lt;/code&gt; statement.  If there's a match we get a company object (which we can associate with the new user) otherwise we get a &lt;code&gt;null&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Enjoy.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Boosting terms in  Zend Search Lucene</title>
   <link href="http://davedash.com/2007/05/29/boosting-terms-in-zend-search-lucene/"/>
   <updated>2007-05-29T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/05/29/boosting-terms-in-zend-search-lucene</id>
   <content type="html">&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl[/tags]&lt;/p&gt;

&lt;h3&gt;Boosting terms &amp;mdash; some fields are better than others&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Lucene&lt;/a&gt; supports boosting or weighting terms.  For example, if I search for members of a web site, and I type in &lt;q&gt;Dash&lt;/q&gt;, I want people with the name &lt;q&gt;Dash&lt;/q&gt; to take precendence over somebody who has a hobby of running the 50-yard Dash.&lt;/p&gt;

&lt;p&gt;If we look at our &lt;code&gt;generateZSLDocument()&lt;/code&gt; method we defined we just need to adjust a few lines:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;

        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $this-&gt;getFirstname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $this-&gt;getLastname()));
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Should be turned into:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;

        $field = Zend_Search_Lucene_Field::Text('firstname', $this-&gt;getFirstname());
        $field-&gt;boost = 1.5;
        $doc-&gt;addField($field);
        $field = Zend_Search_Lucene_Field::Text('lastname', $this-&gt;getLastname());
        $field-&gt;boost = 1.5;
        $doc-&gt;addField($field);

&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;This is pretty straight forward way to add weight (1.5 times the weight of a normal term) and you can customize it to the needs of your site.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Finding things using Zend Search Lucene in symfony</title>
   <link href="http://davedash.com/2007/05/23/finding-things-using-zend-search-lucene-in-symfony/"/>
   <updated>2007-05-23T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/05/23/finding-things-using-zend-search-lucene-in-symfony</id>
   <content type="html">&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl[/tags]&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;notice&quot;&gt;This is part of an &lt;a href=&quot;http://spindrop.us/tag/zsl&quot;&gt;on going series&lt;/a&gt; about the Zend Search Lucene libraries and symfony.  We'll pretty everything up when we're done =)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;We now know how to &lt;a href=&quot;http://spindrop.us/2007/04/24/creating-updating-deleting-documents-in-a-lucene-index-with-symfony/&quot;&gt;manipulate the index via our model classes&lt;/a&gt;.  But let's actually do something useful with our search engine... let's search!&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl[/tags]&lt;/p&gt;

&lt;p&gt;At the time of this writing we're dealing with Propel which uses &lt;code&gt;Peer&lt;/code&gt; classes which are meant for dealing with multiple objects&lt;sup id=&quot;#fnr_1&quot;&gt;&lt;a href=&quot;#fn_1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.  This is the perfect place for a &lt;code&gt;::search()&lt;/code&gt; method.  In otherwords, &lt;code&gt;UserPeer::search('dave');&lt;/code&gt; should query Lucene for users matching &quot;dave&quot;.  Let's make that happen:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;

    public static function search($query)
    {
        $index = self::getLuceneIndex();
        
        $hits = $index-&gt;find(strtolower($query));
        $pks = array();
    
        foreach($hits AS $hit)
        {
            $pks[] = $hit-&gt;user_id;
        }
        
        return self::retrieveByPks($pks);
    }

&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;What we're doing is retrieving our Lucene index.  Somewhere between tutorials we wrote this &lt;code&gt;Peer&lt;/code&gt; function to handle that:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public static function getLuceneIndex($autoIndex = true)
    {
        try 
        {
            return $index = Zend_Search_Lucene::open(sfConfig::get(self::$luceneIndex));
        } 
        catch (Exception $e) 
        {
            $index = $autoIndex ? self::reindex() : null;
            return $index;
        }
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;If our index is missing we'll conveniently create it on the fly.  We then use the Zend Search Lucene API to retrieve the matching hits in this index and then use some Propel trickery to retrieve by an array of primary keys.&lt;/p&gt;

&lt;p&gt;It's now simple to use &lt;code&gt;::search()&lt;/code&gt; functions in the same manner as you use &lt;code&gt;::doSelect()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point you should be able to create a basic symfony app that can utilize a Lucene index.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn_1&quot;&gt;The examples refer to using Propel, but it's trivial to adapt this to sfDoctrine &lt;a href=&quot;#fnr_1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote  in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Creating, Updating, Deleting documents in a Lucene Index with symfony</title>
   <link href="http://davedash.com/2007/04/24/creating-updating-deleting-documents-in-a-lucene-index-with-symfony/"/>
   <updated>2007-04-24T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/24/creating-updating-deleting-documents-in-a-lucene-index-with-symfony</id>
   <content type="html">&lt;p&gt;Previously we covered &lt;a href=&quot;http://spindrop.us/2007/04/23/the-lucene-search-index-and-symfony/&quot;&gt;an all-at-once approach&lt;/a&gt; to indexing objects in your symfony app.  But for some reason, people find the need to allow users to sign up, or change their email addresses and then all of a sudden our wonderful Lucene index is out of date.&lt;/p&gt;

&lt;p&gt;Here lies the strength of using &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Zend Search Lucene&lt;/a&gt; in your app, you can now get the flexibility of interacting with a Lucene index, no matter how it was created and add, update and delete documents to it.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;The last thing you want to do is have a cron job in charge of making sure your index is always up to date by reindexing regularly.  This is an inelegant and inefficient process.&lt;/p&gt;

&lt;p&gt;A smarter method would be to trigger an update of the index each time you update your database.  Luckily the &lt;acronym title=&quot;Object Relational Mapping&quot;&gt;ORM&lt;/acronym&gt; layer allows us to do this using objects (in our case Propel objects).&lt;/p&gt;

&lt;p&gt;If we look at our &lt;a href=&quot;http://spindrop.us/2007/04/23/the-lucene-search-index-and-symfony/&quot;&gt;user example from before&lt;/a&gt;, we did set ourselves up to easily do this using our &lt;code&gt;User::generateZSLDocument()&lt;/code&gt; function, which did most of the heavy lifting.&lt;/p&gt;

&lt;p&gt;We can make a few small changes to the &lt;code&gt;User&lt;/code&gt; class:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    var $reindex = false;
    public function setUsername ( $v )
    {
        parent::setUsername($v);
        $this-&gt;reindex = true;
    }
    public function setFirstname ( $v )
    {
        parent::setFirstname($v);
        $this-&gt;reindex = true;
    }
    public function setLastname ( $v )
    {
        parent::setLastname($v);
        $this-&gt;reindex = true;
    }
    public function setEmail ( $v )
    {
        parent::setEmail($v);
        $this-&gt;reindex = true;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We have an attribute called &lt;code&gt;$reindex&lt;/code&gt;.  When it is false we don't need to worry about the index.  When something significant changes, like an update to your name or email address, then we set &lt;code&gt;$reindex&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.  Then when we save with an overridden save method:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function save ($con = null)
    {
        parent::save($con);
      
        if ($this-&gt;reindex) 
        {
            $index = $this-&gt;removeFromIndex();
            $doc   = $this-&gt;generateZSLDocument();
            $index-&gt;addDocument($doc);
        }
    }

    public function removeFromIndex() 
    {
        $index = Zend_Search_Lucene::open(sfConfig::get('app_search_user_index'));  

        // remove old documents
        $term  = new Zend_Search_Lucene_Index_Term($this-&gt;getId(), 'userid');
        $query = new Zend_Search_Lucene_Search_Query_Term($term);
        $hits  = array();
        $hits  = $index-&gt;find($query);

        foreach ($hits AS $hit) 
        {  
            $index-&gt;delete($hit-&gt;id);  
        }

        return $index;      
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Now we've got the &lt;em&gt;exact&lt;/em&gt; same data that we created during &lt;a href=&quot;http://spindrop.us/2007/04/23/the-lucene-search-index-and-symfony/&quot;&gt;our original indexing&lt;/a&gt;.  This handled creating and updating object, but we miss updating the index when deleting objects.&lt;/p&gt;

&lt;p&gt;Luckily we already made a function &lt;code&gt;User::removeFromIndex()&lt;/code&gt; to remove any related documents from the index, so our delete function can be pretty simple:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function delete($con = null)
    {
        parent::delete($con);
        $this-&gt;removeFromIndex();
    }
&lt;/textarea&gt;&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>The Lucene Search Index and symfony</title>
   <link href="http://davedash.com/2007/04/23/the-lucene-search-index-and-symfony/"/>
   <updated>2007-04-23T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/23/the-lucene-search-index-and-symfony</id>
   <content type="html">&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl, index[/tags]&lt;/p&gt;

&lt;p&gt;This article is meant to followup &lt;a href=&quot;http://spindrop.us/2007/04/10/sfzendplugin/&quot;&gt;sfZendPlugin&lt;/a&gt; where we learn a newer way of obtaining the &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this tutorial we're going to delve into the Lucene index.  &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Zend Search Lucene&lt;/a&gt; relies on building a Lucene index.  This is a directory that contains files that can be indexed and queried by Lucene or other ports.  In our example we'll be creating a search for user profiles.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;We'll want to store in our &lt;code&gt;app.yml&lt;/code&gt; the precise location of this index file so we can refer to it in our app&lt;sup id=&quot;#fnr_lucene_index1&quot;&gt;&lt;a href=&quot;#fn_lucene_index1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;all:
  search:
    user_index: /tmp/myapp.user.lucene.index
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now when we need to refer to the index we can do &lt;code&gt;sfConfig::get('app_search_user_index')&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Index Something&lt;/h3&gt;

&lt;p&gt;Let's try a user search where we can find a user by their name or email address.  It's fairly simple to accomplish, and hardly requires the use of &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;&lt;/a&gt;, but by using &lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt; we can easily extend it to do a full-text search of a user's profile or any other textual data.&lt;/p&gt;

&lt;p&gt;Each &quot;thing&quot; stored in the index is a Lucene &quot;document&quot;.  Each document then consists of several &quot;fields&quot; (&lt;code&gt;Zend_Search_Lucene_Field&lt;/code&gt; objects).  In our example, each document will be an individual user and the fields will be relevant attributes of the user (username, first name, last name, email, the text of their profile).&lt;/p&gt;

&lt;p&gt;Initially we'll want to populate our index.  We may also want to regularly reindex all the users at once to optimize the search performance.  Since reindexing involves multiple users it would make sense to have a static &lt;code&gt;reindex&lt;/code&gt; method in our &lt;code&gt;UserPeer&lt;/code&gt; class&lt;sup id=&quot;#fnr_lucene_index2&quot;&gt;&lt;a href=&quot;#fn_fn_lucene_index2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
class UserPeer extends BaseUserPeer
{
    public static function reindex()
    {
        $index = Zend_Search_Lucene::create(sfConfig::get('app_search_user_index'));

        $user = UserPeer::doSelect(new Criteria());
        foreach ($users AS $user)
        {
            $index-&gt;addDocument($user-&gt;generateZSLDocument());
        }

        return $index;
    }
}
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Very simply, we're creating a new index, getting all the users, adding a document to the index and then committing the index (to disk).  You might have noticed that there's a strange function, &lt;code&gt;User::generateZSLDocument()&lt;/code&gt;.  This function contains all the magic.  In order to not repeat ourselves we keep the internals of making a document for the Lucene index in the &lt;code&gt;User&lt;/code&gt; class itself.  Let's look at it:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function generateZSLDocument()
    {
        $doc = new Zend_Search_Lucene_Document();
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('uid', $this-&gt;getId()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('username', $this-&gt;getUsername()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('email', $this-&gt;getEmail()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $this-&gt;getFirstname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $this-&gt;getLastname()));
        /* An unstored contents field as an aggregate 
          * of all data is no longer needed in *ZEND* Lucene 
          * But it's here.
          */
        $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', implode(' ', array($this-&gt;getEmail(), $this-&gt;getFirstname(), $this-&gt;getLastname(), $this-&gt;getUsername())));
        return $doc;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We're really just dumping the relevant search terms into this document.  The beauty of keeping this code internalized in the &lt;code&gt;User&lt;/code&gt; class is we can reuse it later if we need to index a single &lt;code&gt;User&lt;/code&gt; at a time.&lt;/p&gt;

&lt;p&gt;A couple things to note.  &lt;code&gt;Zend_Search_Lucene_Field::Keyword&lt;/code&gt; allows us to store data that we can lookup later.  We store the &lt;code&gt;User::id&lt;/code&gt; in a field called &lt;code&gt;uid&lt;/code&gt; since &lt;code&gt;id&lt;/code&gt; is a reserved word for the index and we can't access it from &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Zend Search Lucene&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a batch script or a reindex action we can now just call &lt;code&gt;UserPeer::reindex()&lt;/code&gt; and have a working search index for our users.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn_lucene_index1&quot;&gt;Storing things in &lt;code&gt;app.yml&lt;/code&gt; is great for indexes that don't need to be searched in multiple applications. &lt;a href=&quot;#fnr_lucene_index1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
        &lt;li id=&quot;fn_lucene_index2&quot;&gt;
Since we're using a Lucene index, which has an open documented structure, we aren't limited to just using Zend Search Lucene or Apache Lucene (java).  We can mix and match and read and write to the same index file.  For very large indexes (65,000+ documents), I rewrote a Java application to index all the documents at once as PHP would time out during such a task.
&lt;a href=&quot;#fnr_lucene_index2&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 2 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>MinneBar</title>
   <link href="http://davedash.com/2007/04/21/minnebar/"/>
   <updated>2007-04-21T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/21/minnebar</id>
   <content type="html">&lt;p&gt;[tags]Minnebar, symfony, barcamp, frameworks, php, php5[/tags]&lt;/p&gt;

&lt;p&gt;I gave a quick overview of the symfony framework for PHP5 at &lt;a href=&quot;http://barcamp.org/MinneBar&quot;&gt;MinneBar&lt;/a&gt;.  If you have questions or comments about symfony or my talk, feel free to leave them here.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>sfZendPlugin</title>
   <link href="http://davedash.com/2007/04/10/sfzendplugin/"/>
   <updated>2007-04-10T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/10/sfzendplugin</id>
   <content type="html">&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl, plugins[/tags]&lt;/p&gt;

&lt;p&gt;I originally intended to rewrite &lt;a href=&quot;http://spindrop.us/2006/08/25/using-zend-search-lucene-in-a-symfony-app/&quot;&gt;my Zend Search Lucene tutorial&lt;/a&gt;, but &lt;a href=&quot;http://archivemati.ca/2007/03/08/zend-search-lucene-symfony-and-the-ica-atom-application/&quot;&gt;Peter Van Garderen&lt;/a&gt; covered the bulk of what's changed and I was too busy developing search functionality for &lt;a href=&quot;http://lyro.com/&quot;&gt;lyro.com&lt;/a&gt; (not to mention finding inconsistencies with the Zend Search Lucene port and Lucene) to finish the tutorial.  So I broke it up into smaller pieces.&lt;/p&gt;

&lt;p&gt;I packaged &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt; into a &lt;a href=&quot;http://www.symfony-project.com/trac/browser/plugins/sfZendPlugin&quot;&gt;symfony plugin&lt;/a&gt;.  &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; is easily extended using plugins.&lt;/p&gt;

&lt;p&gt;You can obtain this from subversion with the following command (from your &lt;code&gt;/plugins&lt;/code&gt; directory):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;svn export http://svn.symfony-project.com/plugins/sfZendPlugin
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; has a &lt;a href=&quot;http://www.symfony-project.com/book/trunk/17-Extending-Symfony#Bridges%20to%20Other%20Framework%20Components&quot;&gt;Zend Framework Bridge&lt;/a&gt; which let's us autoload the framework by adding the following to &lt;code&gt;settings.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;.settings:
  zend_lib_dir:   %SF_ROOT_DIR%/plugins/sfZendPlugin/lib
  autoloading_functions:
    - [sfZendFrameworkBridge, autoload]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;First we define &lt;code&gt;sf_zend_lib_dir&lt;/code&gt; to be in our plugin's &lt;code&gt;lib&lt;/code&gt; directory.  Then we autoload the bridge framework.&lt;/p&gt;

&lt;p&gt;After setting this up, all the Zend classes will be available and auto-loaded from elsewhere in your code.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Caching REST with sfFunctionCache</title>
   <link href="http://davedash.com/2007/04/07/caching-rest-with-sffunctioncache/"/>
   <updated>2007-04-07T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/07/caching-rest-with-sffunctioncache</id>
   <content type="html">&lt;p&gt;[tags]geocoding, caching, REST, symfony, Cache_Lite, php, cache, sfFunctionCache[/tags]&lt;/p&gt;

&lt;p&gt;For &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; we do a lot of geocoding.  To facilitate we use &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Yahoo! Geocoding API&lt;/a&gt;.  This helps us normalize data, obtain latitude and longitude, interpret location specific searches.&lt;/p&gt;

&lt;p&gt;These &lt;acronym title=&quot;REpresentational State Transfer&quot;&gt;REST&lt;/acronym&gt; queries happen a lot and will continue to happen, but this data that Yahoo! provides is fairly static.  We're basically querying a database of sorts.  So it makes sense that we should cache this data.&lt;/p&gt;

&lt;p&gt;We'll demonstrate how to cache these queries using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;'s &lt;a href=&quot;http://www.symfony-project.com/book/trunk/18-Performance#Caching%20the%20Result%20of%20a%20Function%20Call&quot;&gt;&lt;code&gt;sfFunctionCache&lt;/code&gt;&lt;/a&gt; class.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;I wrote a wrapper (I'll release it as a plugin if requested) for the &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Geocoding API&lt;/a&gt;, the bulk of the work (the &lt;acronym title=&quot;REpresentational State Transfer&quot;&gt;REST&lt;/acronym&gt; call) occurs in a function called &lt;code&gt;doQueryGIS&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
        public static function doQueryGIS($location)
        {
            $url               = sfConfig::get('app_yahoo_geocode');

            $query             = array();
            $query['appid']    = sfConfig::get('app_yahoo_app_id');
            $query['location'] = $location;
            $query['output']   = 'php';

            $url .= '?' . http_build_query($query,null,'&amp;');    
              $curl = curl_init($url);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exe c($curl); 
              // note there should be no space between curl_exe and c($curl) 
              // wordpress is just dumb
        

            if ($response != 'Array') 
            {
                return unserialize($response);
            } 
            else 
            {
                throw new sfException('Yahoo! GeoCoder does not understand: &quot;'. $location . &quot;\&quot;\n&quot;);
            }
            return false;   
            
            
        }

&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The call to this function is always wrapped with &lt;code&gt;queryGIS&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
      protected function queryGIS()
        {
            $function_cache_dir = sfConfig::get('sf_cache_dir').'/function';
            $cache = new sfFunctionCache($function_cache_dir);
            $this-&gt;rawData = $cache-&gt;call(array('YahooGeo','doQueryGIS'), $this-&gt;rawLocation);
            return $this-&gt;rawData;
            
        }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;This wrapper creates a &lt;code&gt;sfFunctionCache&lt;/code&gt; objet and calls the function and caches it for subsequent queries.&lt;/p&gt;

&lt;p&gt;What this means is once Yahoo! teaches &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; that India is located at (25.42&amp;deg;, 77.830002&amp;deg;) and that the precision is 'country' we remember it in the future.&lt;/p&gt;

&lt;p&gt;These features will be incorporated into future versions of &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>PHP double versus single quotes</title>
   <link href="http://davedash.com/2007/03/03/php-double-versus-single-quotes/"/>
   <updated>2007-03-03T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/03/03/php-double-versus-single-quotes</id>
   <content type="html">&lt;p&gt;[tags]best practices, php[/tags]&lt;/p&gt;

&lt;p&gt;If you're familiar with PHP, the difference between double and single quotes is obvious.  Double quotes evaluate variables and control characters (e.g. &lt;code&gt;\n&lt;/code&gt; or &lt;code&gt;\r&lt;/code&gt;), whereas single quotes do not.&lt;/p&gt;

&lt;p&gt;I've been indoctrinated with the &quot;use single quotes whenever possible&quot; methodology, but I never really put it to the test.  Is it really worth it for me to go back and look at old code that uses double quotes and change them?  Like all best practices, the answer is &quot;maybe.&quot;&lt;/p&gt;

&lt;p&gt;So I wrote a simple test harness (there's a lot of room for error with these, but I tried my best).&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
&lt;?php

define ('MAX',2000000);

function f1()
{
    for($i=0;$i&lt;MAX; $i++)
    {
        $c = &quot;test &quot; . $i;
    }
}


function f2()
{
    for($i=0;$i&lt;MAX; $i++)
    {
        $c = &quot;test $i&quot;;
    }
}


function f3()
{
    for($i=0;$i&lt;MAX; $i++)
    {
        $c = 'test ' . $i;
    }
}



$t1 = microtime(true);
f1();
echo 'Time 1: ' , (microtime(true) - $t1) , &quot;\n&quot;;

$t2 = microtime(true);
f2();
echo 'Time 2: ' , (microtime(true) - $t2) , &quot;\n&quot;;

$t3 = microtime(true);
f3();
echo 'Time 3: ' , (microtime(true) - $t3) , &quot;\n&quot;;



&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;I tried different permutations of which order to run &lt;code&gt;f1()&lt;/code&gt;, &lt;code&gt;f2()&lt;/code&gt; and &lt;code&gt;f3()&lt;/code&gt; they seemed to give similar results no matter which order they were run.&lt;/p&gt;

&lt;p&gt;My results were:&lt;/p&gt;

&lt;pre&gt;
Time 1: 2.898087978363
Time 2: 3.5480048656464
Time 3: 2.8503499031067
&lt;/pre&gt;


&lt;p&gt;My interpretation is that quotes versus double quotes isn't as big of a deal as concatenation versus putting variables within double quotes.  My guess is that PHP 5.2.0 (which is what I used for the tests) is smarter with strings than older versions.&lt;/p&gt;

&lt;p&gt;So really if you look at the test harness there isn't any discernible differences until you hit 2 million iterations and even then nobody gets fired over 0.6 seconds of performance.  Chances are it doesn't matter too much, but over time you can write enough code in the right spots or shared in the right open source projects and that few seconds will snowball.  After all, I'm not so much concerned about how optimized a script is that I write, but rather how optimized is a script that everyone ends up using.  But rather than do mental calculations about whether or not to optimize something, let's just assume that everything should be as optimal as we can stand to write it.&lt;/p&gt;

&lt;p&gt;Update: 6 March 2006, I updated the test harness to reflect my intended tests.
Update: 7 March 2006, I updated the results to be more clear.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Parsing a list of Key:Value pairs</title>
   <link href="http://davedash.com/2007/02/24/parsing-a-list-of-kv-pairs/"/>
   <updated>2007-02-24T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/02/24/parsing-a-list-of-kv-pairs</id>
   <content type="html">&lt;p&gt;[tags]best practices,php,openID[/tags]
I'm working on implementing &lt;a href=&quot;http://openid.net/&quot;&gt;openID&lt;/a&gt; for &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; and for use in &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; apps.  One thing I was having trouble with was parsing key value pairs, which is one of the requirements to reading responses.  It's a fairly easy task, but &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; offers so many ways to do this.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://openid.net/&quot;&gt;openID&lt;/a&gt; calls for the following &lt;a href=&quot;http://openid.net/specs/openid-authentication-1_1.html#anchor32&quot;&gt;Key-Value format&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Lines of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; some_key:some value&lt;/li&gt;
&lt;li&gt; There MUST NOT be a space before or after the colon.&lt;/li&gt;
&lt;li&gt; Newline characters MUST be Unix-style, just ASCII character 10 (&quot;\n&quot;).&lt;/li&gt;
&lt;li&gt; Newlines MUST BE at end of each line as well as between lines.&lt;/li&gt;
&lt;li&gt; MIME type is unspecified, but text/plain is RECOMMENDED.&lt;/li&gt;
&lt;li&gt; Character encoding MUST BE UTF-8.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;So here is my attempt at parsing something like this as efficiently and error free as possible:&lt;/p&gt;

&lt;!--more--&gt;


&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
function splitKV($response) 
{
    $r = array();
    preg_match_all('|^\s*([^:]+):([^:\n]+)[ ]*$|m', $kvs, $matches);
    for($i = 0; $i &lt; count($matches[0]); $i++) {
        $r[$matches[1][$i]] = $matches[2][$i];
    }
    return $r;
}
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;I wrote this function as I was writing the post... and it came out way faster than my previous implementations using &lt;code&gt;strtok&lt;/code&gt; or a combination of &lt;code&gt;explode&lt;/code&gt; and &lt;code&gt;trim&lt;/code&gt;.  Which is good to hear since I do use &lt;code&gt;preg_&lt;/code&gt; functions quite a bit in PHP and they definitely have their place.&lt;/p&gt;

&lt;p&gt;I'm curious if people have found a faster way of parsing through a string of Key-Value pairs.  I'll run it in a test harness and stand corrected if someone comes through with something faster ;).&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Coming soon to reviewsby.us</title>
   <link href="http://davedash.com/2006/10/02/coming-soon-to-reviewsbyus/"/>
   <updated>2006-10-02T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/10/02/coming-soon-to-reviewsbyus</id>
   <content type="html">&lt;p&gt;In August I took a break from &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; only to be plagued by spam.  In September, I relinquished portions of the project planning to my wife.  We haven't released anything publicly, yet, but there's a lot in development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I updated the development framework to &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; 1.0 alpha and took care of a whole slew of bugs.&lt;/li&gt;
&lt;li&gt;Katie and I came up with a &lt;a href=&quot;http://flickr.com/photos/davedash/251518309/&quot;&gt;wireframe&lt;/a&gt; that details some of the upcoming changes.&lt;/li&gt;
&lt;li&gt;I upgraded the user logic to take advantage of sfGuardUser, a user management plugin for symfony.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I'm in progress of writing location specific searches.  I'm slow to implement.  It seems that this month is far busier than I'd like, and I can rarely get in a block of enough time to just crank this out.  The problem with geographic-specific searches is mySQL supports those types of queries, but it's not as easy as I'd like.  Zend Search Lucene with some support from PHP, however, may yield some promising results.  As always, I'll share my findings in a forthcoming tutorial.&lt;/p&gt;

&lt;p&gt;Anyway, no visible updates on the actual site, since I didn't want to put alpha software on the live site.  I'm sure by next month symfony will be ready.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Using Zend Search Lucene in a symfony app</title>
   <link href="http://davedash.com/2006/08/25/using-zend-search-lucene-in-a-symfony-app/"/>
   <updated>2006-08-25T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/08/25/using-zend-search-lucene-in-a-symfony-app</id>
   <content type="html">&lt;p&gt;[tags]zend, search, lucene, zend search lucene, zsl, symfony,php[/tags]&lt;/p&gt;

&lt;p&gt;If you're like me you've probably followed the &lt;a href=&quot;http://symfony-project.com/askeet/21&quot;&gt;Askeet tutorial on Search&lt;/a&gt; in order to create a decent search engine for your web app.  It's fairly straight forward, but they hinted that when &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Zend Search Lucene&lt;/a&gt; (&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;) is released, that might be the way to go.  Well we are in luck, &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;&lt;/a&gt; is available, so let's just dive right in.&lt;/p&gt;

&lt;!--more--&gt;


&lt;p&gt;If you aren't using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; have a look at &lt;a href=&quot;http://devzone.zend.com/node/view/id/91&quot; title=&quot;Roll Your Own Search Engine with Zend_Search_Lucene&quot;&gt;this article&lt;/a&gt; from the &lt;a href=&quot;http://devzone.zend.com/&quot;&gt;Zend Developer Zone&lt;/a&gt;.  It covers just enough to get you started.  If you are using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, just follow along and we'll get you where you need to go.&lt;/p&gt;

&lt;h3&gt;Obtaining Zend Search Lucene&lt;/h3&gt;

&lt;p&gt;First &lt;a href=&quot;http://framework.zend.com/download&quot; title=&quot;Zend Framework Download&quot;&gt;download&lt;/a&gt; the &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt; (&lt;acronym title=&quot;Zend Developer Framework&quot;&gt;ZF&lt;/acronym&gt;).  The &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt;  is supposed to be fairly &quot;easy&quot; in terms of installation.  So let's put that to the test.  Open your &lt;a href=&quot;http://framework.zend.com/&quot;&gt;&lt;acronym title=&quot;Zend Developer Framework&quot;&gt;ZF&lt;/acronym&gt;&lt;/a&gt; archive.  Copy &lt;code&gt;Zend.php&lt;/code&gt; and &lt;code&gt;Zend/Search&lt;/code&gt; to your &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; project's library folder:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cp Zend.php $SF_PROJECT/lib              
mkdir $SF_PROJECT/lib/Zend
cp -r Zend/Search $SF_PROJECT/lib/Zend
cp Zend/Exception.php $SF_PROJECT/lib/Zend                 
chmod -R a+r $SF_PROJECT/lib/Zend*
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Index Something&lt;/h3&gt;

&lt;p&gt;We'll deviate slightly from &lt;a href=&quot;http://spindrop.us/category/reviewsbyus&quot; title=&quot;ReviewsBy.Us category of Spindrop&quot;&gt;food themed&lt;/a&gt; tutorials and do something generic.  Let's try a user search where we can find a user by their name or email address.  It's fairly simple to accomplish, and hardly requires the use of &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;&lt;/a&gt;, but by using &lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt; we can easily extend it to do a full-text search of a user's profile or any other textual data.&lt;/p&gt;

&lt;p&gt;Each &quot;thing&quot; stored in the index is a &quot;document&quot; in &lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;, specifically a &lt;code&gt;Zend_Search_Lucene_Document&lt;/code&gt;.  Each document then consists of several &quot;fields&quot; (&lt;code&gt;Zend_Search_Lucene_Field&lt;/code&gt; objects).  In our example, our document will be an individual user and the fields will be relevant attributes of the user (username, first name, last name, email, the text of their profile).&lt;/p&gt;

&lt;p&gt;We're going to write a general re-indexing tool.  Something that will index all users.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;userActions&lt;/code&gt; class let's add the following action:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function executeReindex()
    {
        require_once 'Zend/Search/Lucene.php';
        $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'),true);
        
        $users = UserPeer::doSelect(new Criteria());
        foreach ($users AS $user)
        {
            $doc = new Zend_Search_Lucene_Document();
            $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('id', $user-&gt;getId()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('username', $user-&gt;getUsername()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('email', $user-&gt;getEmail()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $user-&gt;getFirstname()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $user-&gt;getLastname()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', &quot;{$user-&gt;getEmail()} {$user-&gt;getFirstname()} {$user-&gt;getLastname()} {$user-&gt;getUsername()}&quot;));
            $index-&gt;addDocument($doc);
        }
        
        $index-&gt;commit();
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The code should be fairly easy to follow.  First of all we're requiring the necessary libraries for Lucene.  The next line we are creating the index:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'),true);
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;app_search_user_index_file&lt;/code&gt; is a symfony configuration that you define in your &lt;code&gt;app.yml&lt;/code&gt;.  It defines which file you want to use for your index.  &lt;code&gt;/tmp/lucene.user.index&lt;/code&gt; works for our purposes.   The second parameter tells Lucene we are creating a new index.&lt;/p&gt;

&lt;p&gt;We then loop through all the users and for each user create a document.  For all the search relevant attributes that a user might have we add a field into the document.  Note the last field:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', &quot;{$user-&gt;getEmail()} {$user-&gt;getFirstname()} {$user-&gt;getLastname()} {$user-&gt;getUsername()}&quot;));
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;By default search is made for the &quot;contents&quot; field.  So in this example we want people to be able to type in someone's name, email, username without having to specify what field we're searching for.&lt;/p&gt;

&lt;h3&gt;Find those users&lt;/h3&gt;

&lt;p&gt;Finding the user's is equally as straight-forward.  We make a new action called &lt;code&gt;search&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function executeSearch()
    {
        require_once('Zend/Search/Lucene.php');
        $query = $this-&gt;getRequestParameter('q');
    
        $this-&gt;getResponse()-&gt;setTitle('Search for \'' . $query . '\' &amp;laquo; ' . sfConfig::get('app_title'), true);
    
        $hits = array();
    
        if ($query)
        {
            $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'));
            $hits = $index-&gt;find(strtolower($query));
        }
        $this-&gt;hits = $hits;
    }

The magic happens in our `if` statement:

    if ($query)
    {
        $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'));
        $hits = $index-&gt;find(strtolower($query));
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;If we have a query, open the &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;ZSL&lt;/a&gt; index (note that we only have one parameter here).  Run the &lt;code&gt;find&lt;/code&gt; method to find our query and store it to the &lt;code&gt;$hits&lt;/code&gt; array.  Note that our query was cleaned with &lt;code&gt;strtolower&lt;/code&gt;, since &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;ZSL&lt;/a&gt; is case sensitive.&lt;/p&gt;

&lt;p&gt;The template takes care of the rest:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    &lt;?php use_helper('Form');?&gt;
    &lt;?php echo form_tag('@search_users') ?&gt;
    &lt;?php echo input_tag('q'); ?&gt;
    &lt;?php echo submit_tag() ?&gt;
    &lt;/form&gt;
    &lt;?php foreach ($hits as $hit): ?&gt;
      &lt;?php echo $hit-&gt;score ?&gt;
      &lt;?php echo $hit-&gt;firstname ?&gt;
      &lt;?php echo $hit-&gt;lastname ?&gt;
      &lt;?php echo $hit-&gt;email ?&gt;
    &lt;?php endforeach ?&gt;
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Fairly simple... but it could use some cleaning up (enjoy).&lt;/p&gt;

&lt;h3&gt;What about new users?&lt;/h3&gt;

&lt;p&gt;Regularly reindexing might be nice in terms of having an optimized search index, but its lousy if you want to be able to search the network immediately when new people join on.  So why not automatically re-index each user every time they are created or everytime one of their indexed components is summoned?&lt;/p&gt;

&lt;p&gt;This should be fairly simple by adding to the &lt;code&gt;User&lt;/code&gt; class:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    var $reindex = false;
    public function setUsername ( $v )
    {
        parent::setUsername($v);
        $this-&gt;reindex = true;
    }
    public function setFirstname ( $v )
    {
        parent::setFirstname($v);
        $this-&gt;reindex = true;
    }
    public function setLastname ( $v )
    {
        parent::setLastname($v);
        $this-&gt;reindex = true;
    }
    public function setEmail ( $v )
    {
        parent::setEmail($v);
        $this-&gt;reindex = true;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We have an attribute called &lt;code&gt;$reindex&lt;/code&gt;.  When it is false we don't need to worry about indexes.  When something significant changes, like an update to your name or email address, then we set &lt;code&gt;$reindex&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.  Then when we save:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function save ($con = null)
    {
        parent::save($con);
        if ($this-&gt;reindex) {
            require_once 'Zend/Search/Lucene.php';
            $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'));
            // first find any references to this user and delete them
            $hits = $index-&gt;find('id:'. $this-&gt;getId());
            foreach ($hits AS $hit) {
                $index-&gt;delete($hit-&gt;id);
            }
        
            $doc = $this-&gt;generateZSLDocument();
            $index-&gt;addDocument($doc);
            $index-&gt;commit();
        }
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We're calling a new function called &lt;code&gt;generateZSLDocument&lt;/code&gt;.  It might look familiar:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function generateZSLDocument()
    {
    
        require_once 'Zend/Search/Lucene.php';
        $doc = new Zend_Search_Lucene_Document();
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('id', $this-&gt;getId()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('username', $this-&gt;getUsername()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('email', $this-&gt;getEmail()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $this-&gt;getFirstname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $this-&gt;getLastname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', &quot;{$this-&gt;getEmail()} {$this-&gt;getFirstname()} {$this-&gt;getLastname()} {$this-&gt;getUsername()}&quot;));
        return $doc;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Now, whenever a user is updated, so is our index.  Additionally we can modify our reindex action:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function executeReindex()
    {
        require_once('Zend/Search/Lucene.php');
        $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'),true);
        
        $users = UserPeer::doSelect(new Criteria());
        foreach ($users AS $user)
        {
            
            $index-&gt;addDocument($user-&gt;generateZSLDocument);
        }
        
        $index-&gt;commit();
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;That's a &lt;strong&gt;lot&lt;/strong&gt; easier to deal with.&lt;/p&gt;

&lt;h3&gt;...and beyond&lt;/h3&gt;

&lt;p&gt;Hope this article helps some of you jumpstart your &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; apps.  Really cool, easy to implement search is here.  We no longer have to stick with shoddy solutions like HT://Dig or spend time rolling our own full text search, as the &lt;a href=&quot;http://symfony-project.com/askeet/21&quot;&gt;symfony team diligently showed us we could&lt;/a&gt;.  But there is a lot more ground to cover.  Including optimization techniques and best practices.&lt;/p&gt;

&lt;p&gt;Let me know what you think, and if you use this in any of your apps.&lt;/p&gt;
</content>
 </entry>
 

</feed>

