Tag Archive for programming

Zend Framework and the Zend_Db_Select::where() and ::orWhere() Problem

To say that I dislike Zend Framework, would be an understatement. While it is powerful and capable of quite a bit, I find it to be a bit overkill for many (if not most) tasks and bloated in so many ways. But, I’m not a huge fan of frameworks to begin with. As programmers, we find ourselves having to work with things we dislike all to often. Providing decent documentation and examples allows for this process to be a bit more enjoyable. Zend Framework documentation is, mostly helpful but it definitely has it’s issues.

When using the Zend_Db_Select (I’d rather be using PDO) object, I came across the necessity to execute a MySQL query with some OR conditions. This seems like a simple enough task especially considering the availability of an orWhere() function to use instead of a where() function. Reading the description of the functionality from the manual reveals the following:

If you need to combine terms together using OR, use the orWhere() method. This method is used in the same way as the where() method, except that the term specified is preceded by OR, instead of AND.

Changing AND to OR is the only thing this function provides. Searching around for gripes about the functions yields various complaints regarding the relative uselessness of the orWhere() for many cases.

Let’s say you want to find instances of a record that is active (active = 1) and where the date is set to null (0000-00-00) or in the future (> 2011-08-09). When using the where() and orWhere() functions, it feels somewhat natural to try something like this after reading the documentation:

$select = $db->select()
             ->from("some_table")
             ->where("active = ?", 1)
             ->orWhere("my_date > ?", "2011-08-09");
             ->orWhere("my_date = ?", "0000-00-00")

Unfortunately, this produces a query that looks like this:

SELECT * FROM some_table 
WHERE (active = 1) 
OR (my_date > '2011-08-09') 
OR (my_date = '0000-00-00')

This would return any records where active is 1 or my_date is in the future or my date is null. Not quite what we’re looking for. Progressing from this, one might consider switching the first orWhere() to a where() to add in the appropriate AND:

$select = $db->select()
             ->from("some_table")
             ->where("active = ?", 1)
             ->where("my_date > ?", "2011-08-09");
             ->orWhere("my_date = ?", "0000-00-00")

Trying that doesn’t get us there either:

SELECT * FROM some_table 
WHERE (active = 1) 
AND (my_date > '2011-08-09') 
OR (my_date = '0000-00-00')

Perhaps the documentation from Zend can shed some light on this problem. (A side note to the Zend documentation folks, who still struggle to make documentation that doesn’t suck: it isn’t that hard to create documentation where it’s possible to highlight only code without grabbing the line numbers. Please consider looking into this. Thanks.)

The manual lets us know that the Zend_Db_Select is completely useless for even a moderately useful OR query and tells us to do something like this:

$minimumPrice = 100;
$maximumPrice = 500;
$select = $db->select()
             ->from('products',
             ->where("price < $minimumPrice OR price > $maximumPrice")

This wouldn’t be so bad except for the fact that it doesn’t use query parameterizing and doesn’t take into account the fact there’s a pretty good chance we won’t be dealing with straight integers all the time. I find it to be a weak and lacking example. Trying to rouse someone from the Zend Framework team to understand and possibly address the insufficiencies provides a response just as useless:

The where() and orWhere() methods of Zend_Db_Select support a majority of common queries. This class is not able to implement a method interface to the entire SQL language.

Complex queries that are not supported by Zend_Db_Select must be written in SQL and submitted as a string to the Zend_Db_Adapter_*::query() method.

Translating this, from my perspective, sounds a bit like: “We figured those helper methods would be useful for most things but, since they aren’t, we’re going to blame the complexities of the SQL language and tell you to use the base query method to generate your own queries.”

Adding insult to injury, the codebase I currently work on further abstracts database functionality by providing a search() method to our model’s data adapters to grab things from the database using arbitrary bits of the model or, even other models. Querying simple things such as columns already available could be done by passing an array to the search() method containing a key name for the column and, either the value you wanted it to be equal to, or a different operator with a value associated with it. This would intelligently determine the operator and value to check against the column by providing a simple array like:

array (
    'active' => 1, 
    'my_date' => array (
            'operator' => '>', 
            'value' => '2011-08-09'
        )
    )
);
 
/**
   * Results in:
   *
   * $select->where('active = ?', 1);
   * $select->where('my_date > ?', '2011-08-09');
   */

In the example above, we’d find records where active was equal to 1 and startDate was not equal to ’0000-00-00′. While this worked fine for simple searches, it failed to provide the accessibility to queries where an OR statement was needed.

Supplying a fix that follows a similar style and also takes into account the necessity of correct quotations around values (that weren’t INTs) didn’t prove too hard. Modified search parameter arrays could be provided like so:

$date = date('Y-m-d');
$searchArray = array (
    'active' => 1,    
    'my_date'   => array (
        'operator'  => 'OR',
        'value'     => array(
            array (
                'operator' => '>',
                'value'    => $date
            ),
            array (
                'operator' => '=',
                'value'    => '0000-00-00'
            )
        ),
    )
);

Within our search method, we would check to see if the operator was set to OR. If so, the code would go through a simple construction process to add the appropriate AND query with our parenthesis in the right place and it looked something like this:

if ($operator == 'OR') {
    if (is_array($val)) {
        $queryParts = array();
        foreach ($val as $orArray) {
            $orValue = $orArray['value'];
            $orOperator = $orArray['operator'];
            $queryParts[] = sprintf('%s %s %s', $column, $orOperator, $this->db->quote($orValue));
        }
        $select->where(implode(' OR ', $queryParts));
    }
}

Finally we were able to have our code generate the SQL we were looking for:

SELECT * FROM some_table 
WHERE (active = 1) 
AND (my_date > '2011-08-09' OR my_date = '0000-00-00');

While this doesn’t fix the broken and rather useless Zend_Db_Select::orWhere() issue, it is a useful workaround.

Hopefully others can read and investigate this methodology to find the path towards database adapter enlightenment in the fogginess that is Zend_Db_Select. Personally, I find I always have to keep my flashlight handy when trudging through the long, twisted and dark tunnels that are Zend Framework. Good luck, traveler!

PHP Developers Finally Deprecating ext/mysql in Favor of mysqli or PDO

Perhaps it is because of the recent slew of high profile SQL injection attacks, or perhaps because it was just finally time to bite the bullet and make the switch. PHP internal emails have revealed that there is finally an official movement to deprecate the ext/mysql and mysql_* family of functions from PHP.

For me, this couldn’t have come any sooner. Although the community has been advocating a change for a long time, much of the documentation and tutorials that exist on the Internet still point to using mysql_* functions instead of their more current alternatives. Many times throughout the day, nearly every day, new PHP developers ask questions in the ##php IRC chat on irc.freenode.net and are delivered a swift !+mysql is old indicating that:

mysql_* functions are no longer recommended for new development. For new projects use mysqli (php.net/mysqli) or PDO (php.net/PDO). The MySQL developers developed guidelines and a script for migrating your code http://forge.mysql.com/wiki/Converting_to_MySQLi. Most people here will recommend PDO: http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers

You can find the full email if you want to see the text in it’s entirety but the main points for suggesting things move along are:

  • Softly deprecate ext/mysql with education (docs) starting today
  • Not adding E_DEPRECATED errors in 5.4, but revisit for 5.5/6.0
  • Add pdo_mysql examples within the ext/mysql docs that mimic the current examples, but occasionally introduce features like prepared statements
  • Focus energy on cleaning up the pdo_mysql and mysqli documentation
  • Create a general “The MySQL situation” document that explains the situation

Which should you choose? Should you use PDO or mysqli?

The personal preference of this author is definitely PDO. The possibility of interoperability with other databases is nice, but ability to use exceptions to handle issues with the database is really the selling point for me. Exception handling and other features means that PDO is recommended more often than mysqli_* functions for new development. This is reflected in responses to the mailing list.

To educate yourself on the issue, you can check out additional information about the differences between the options in the PHP manual. For a quick overview, the table near the bottom of the page in the manual is a nice, quick reference:

  PHP's mysqli Extension PDO (Using PDO MySQL Driver and MySQL Native Driver) PHP's MySQL Extension
PHP version introduced 5.0 5.0 Prior to 3.0
Included with PHP 5.x yes yes Yes
MySQL development status Active development Active development as of PHP 5.3 Maintenance only
Recommended by MySQL for new projects Yes – preferred option Yes No
API supports Charsets Yes Yes No
API supports server-side Prepared Statements Yes Yes No
API supports client-side Prepared Statements No Yes No
API supports Stored Procedures Yes Yes No
API supports Multiple Statements Yes Most No
Supports all MySQL 4.1+ functionality Yes Most No

However, my opinion and the opinion of other PHP developers on the matter, may not make a huge difference when it comes to future development. Johannes Schlüter, an engineer on the MySQL/Oracle team, responded to the PHP mailing list with some interesting information about which direction their team is headed:

I’m not sure the current PDO is “the” alternative. We (= MySQL/ORACLE) focus mostly on mysqli, that’s the extension providing access to all current and future features of MySQL. True, many features could be added to PDO but there are two design decision in PDO which make this bad:

  • The parser used for identifying statement place holders is very basic, as it is implemented in PDO core, not the drivers, which leads to FRs like #54929 or the famous LIKE issue.
  • driver-specific functions are implemented by using __call() which means there is no good introspection mechanism to check whether a feature is available or not in the current setup.

Besides these two items there are every now and then reports on PDO_mysql which in fact are caused by limitations in the PDO design which can’t be bypassed by the driver implementation.

A good abstraction layer would certainly be good for the language but for now we (=MySQL/ORACLE) consider mysqli the preference.

It sounds like we will have to wait and see which will be the accepted alternative for future development (if any is ever considered accepted). A huge tipping point would be adding exception handling in the mysqli_* family of functions but that doesn’t appear to be an important development direction at the moment. For now, I will continue to happily use PDO and await the day for users to start wondering why most of the MySQL/PHP tutorials on the web are throwing E_DEPRECATED warnings.

For more information about converting your code to use the new libraries or to learn about them, consider the following links:

It’s about time.

Moving Right Along

Well, it was time for a change. This blog has transitioned to it’s new permanent location here at Random Drake. There are events in our lives which cannot be foretold and thus, we move on and on in an effort to keep up with the inevitable force that drives us known as: change.

Moving the blog, setting up the new design and going through old content, allowed me to realize that it had been an entire year since I had the opportunity to publish something. I was saddened to know that a blog, I worked so hard to get up and going, was not getting the attention it deserved. Life is unfolding faster and faster in front of me and so I felt it necessary to bring this back to life. I enjoy the writings and pleasure that I received in sharing programming, news and events with the rest of the world. That being the case: you can expect a revival of content and stories from this particular blogger. I have personalized the blog in an effort to make it a part of my life and who I am instead of simply a place where I posted things occasionally.

My personal and professional lives have had a great deal of happenings in the last year. Moving to another part of the world has introduced me to a new job where I toil along an immense project that keeps me constantly busy. The database seems to practically be busting at the seams, compared to other projects, as it grows near 400GB in size. The project rewards me by allowing me to write some good PHP and increase my SQL skills tremendously.

The ongoing side project, that has been in development for a few years, has seen a revival since receiving a makeover. Surpassing half a million hits in a month was a pretty neat thing to see and allowed me to know it was something that should become more of a priority. Continuing to gain popularity, the site has a few more big steps going for it before my trip to California where I hope to start spreading the good word about it.

Continuing to work on getting this blog more accessible, interesting and connected is something I have a goal for. Hopefully I will be able to find some additional WordPress plugins to aid me in the task.

We shall see what happens as time goes on.