Tudor Barbu's blog

Ramblings about software development

As some of you might already know, I’m working on my latest project, StoreBeez, which is a virtual mall where independent businesses and artisans can open an online store fast and easy, without having to pay any upfront costs.

The products are also displayed on our frontpage. And since I don’t want to promote a given store – all out stores are equal – displaying the products in random order seemed like a good idea. Retrieving random results is simple, just use MySQL’s rand() function and make the queries look like:

SELECT * FROM `table` ORDER BY RAND();

Which works. Every time the user reloads the page, different results are shown. The problem arises when I’m trying to paginate. Every request means a new random order and sometimes rows get displayed multiple times – on different pages – or simply get “lost”.

The solution is to keep the same order between two consecutive requests. To do this, just pass a seed to the random number generator:

SELECT * FROM `table` ORDER BY RAND({seed});

…where {seed} is a number kept in session. This way, the order is kept between requests. Given that I use Zend Framework as my “weapon of choice”, I will post a ZF solution below:

In the controller:

$page = intval($this->_getParam('page'));
$page = $page ?: 1;

if ($page == 1) {
    $namespace = new Zend_Session_Namespace('random_key');
    $namespace->key = time();
}

$productsTable = new My_Table_Products();
$products = $productsTable->fetchPaginator();
$products->setItemCountPerPage(10)
         ->setCurrentPageNumber($page);

And in the table:

class My_Table_Products
{
    // ...

    public function fetchPaginator()
    {
        $namespace = new Zend_Session_Namespace('random_key');
        $select = $this->select()
                       ->order('RAND(' . $namespace->random_key . ')');

        $paginator = Zend_Paginator::factory($select);

        return $paginator;
    }
}
10 Nov

Zend Framework 2.0

Posted by Tudor. Tags: ,

Looks promising

Uploadify is an awesome script and it works like a charm. But – there’s always a but – sometimes it throws a mysterious 302 error. This happened to me all day long and it drove me crazy. Well, not really, I was already crazy :) So, what to do when the HTTP 302 error pops? A quick look over HTTP statuses should point the me in the right direction:

The requested resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. This response is only cacheable if indicated by a Cache-Control or Expires header field.

The temporary URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).

If the 302 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.

from here. In simple English, that means a redirect. So what happens!? For security reasons, I turned on the cookie-httponly setting and the client-side script was unable to access the cookies and pass the session id back to the server-side script, which in term would see this connection as coming from an non-authenticated user and issue a redirect to the login page. Thus the mysterious 302 status.

The problem can be solved really easy, by turning the cookie-httponly setting off for the entire application. If that’s not desirable, there’s a more complicated solution. First, Uploadify must send the session id to the server together with the file:

$('#fileUpload').uploadify({
    'uploader'   : '/uploadify/uploadify.swf',
    'script'     : '/images/upload/',
    'cancelImg'  : '/uploadify/cancel.png',
    'auto'       : true,
    'fileExt'    : '*.jpg;*.gif;*.png',
    'fileDesc'   : 'Image Files',
    'sizeLimit'  : 2097152,
    'scriptData' : {'sid' : '<?=Zend_Session::getId();?>'},
    onComplete   : function(event, id, fileObj, response, data) {
        // bla bla bla
    }
}

…then, turn off the auto-start in the application.ini file:

phpSettings.session.strict	= "On"

…and in the Bootstrap.php file:

protected function _initSession()
{
	if (isset($_POST['sid'])) {
		Zend_Session::setId($_POST['sid']);
	}

	Zend_Session::start();
}

Of course, there are some security issues with both approaches, but nothing serious. Took me about 2 hours to figure it out :(

One of the strong assets of Zend Framework is its rich built in package of validators. It has validators for just about anything, from simple email addresses and digits to credit card numbers and database rows. Yet, sometimes things go wrong and they need to be hacked back on track. Today’s problem was with the email address validator that sometimes generates some very user-unfriendly error messages. Like such:

‘bad.email’ is no valid hostname for email address ‘testing@bad.email’
‘bad.email’ does not match the expected structure for a DNS hostname
‘bad.email’ appears to be a local network name but local network names are not allowed

For the average user, these messages can be very confusing and sometimes is much better to just show an easy to understand “Please enter a correct email” message. How hard can that be with Zend Framework!?! Well…let’s say it’s not as simple as it looks.

The obvious thing to do is to add custom error messages (basically adding the same message over and over again). Like such:

$email->setOptions(
    array(
        'label'      => 'Email',
        'required'   => true,
        'filters'    => array(
            'StringTrim',
            'StripTags',
        ),
        'validators' => array(
            array(
                'EmailAddress',
                true,
                array(
                    'allow' => Zend_Validate_Hostname::ALLOW_DNS,
                    'domain' => true,
                    'mx' => true,
                    'deep' => true,
                    'messages' => array(
                        Zend_Validate_EmailAddress::INVALID => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_FORMAT => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_HOSTNAME => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_MX_RECORD => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_SEGMENT => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::DOT_ATOM => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::QUOTED_STRING => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_LOCAL_PART => 'Please enter a correct email'
                    ),
                ),
            ),
        ),
    )
);

But it won’t work. That’s because Zend_Validate_EmailAddress calls Zend_Validate_Hostname internally, so custom error messages should be added also for the Zend_Validate_Hostname validator.

$email->setOptions(
    array(
        'label'      => 'Email',
        'required'   => true,
        'filters'    => array(
            'StringTrim',
            'StripTags',
        ),
        'validators' => array(
            array(
                'EmailAddress',
                true,
                array(
                    'allow' => Zend_Validate_Hostname::ALLOW_DNS,
                    'domain' => true,
                    'mx' => true,
                    'deep' => true,
                    'messages' => array(
                        Zend_Validate_EmailAddress::INVALID => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_FORMAT => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_HOSTNAME => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_MX_RECORD => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_SEGMENT => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::DOT_ATOM => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::QUOTED_STRING => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::INVALID_LOCAL_PART => 'Please enter a correct email',
                        Zend_Validate_EmailAddress::LENGTH_EXCEEDED => 'Please enter a correct email',
                        Zend_Validate_Hostname::CANNOT_DECODE_PUNYCODE => 'Please enter a correct email',
                        Zend_Validate_Hostname::INVALID => 'Please enter a correct email',
                        Zend_Validate_Hostname::INVALID_DASH => 'Please enter a correct email',
                        Zend_Validate_Hostname::INVALID_HOSTNAME => 'Please enter a correct email',
                        Zend_Validate_Hostname::INVALID_HOSTNAME_SCHEMA => 'Please enter a correct email',
                        Zend_Validate_Hostname::INVALID_LOCAL_NAME => 'Please enter a correct email',
                        Zend_Validate_Hostname::INVALID_URI => 'Please enter a correct email',
                        Zend_Validate_Hostname::IP_ADDRESS_NOT_ALLOWED => 'Please enter a correct email',
                        Zend_Validate_Hostname::LOCAL_NAME_NOT_ALLOWED => 'Please enter a correct email',
                        Zend_Validate_Hostname::UNDECIPHERABLE_TLD => 'Please enter a correct email',
                        Zend_Validate_Hostname::UNKNOWN_TLD => 'Please enter a correct email',
                    ),
                ),
            ),
        ),
    )
);

Pretty long. Yet still not working. But it is somewhat better – as no creepy error messages are being displayed – instead the same message appears multiple times. This is not a validator chain, so the programmer can’t force the breaking of the chain after the first failed test :(

In the end, after few hours of digging through the code and the docs, I came up with my own solution, which still harnesses the power of the original Zend_Validate_EmailAddress validator while providing a way to add a simple “Please enter a correct email” message. One time :) . It uses the Zend_Validate_Callback class and PHP 5.3 anonymus functions:

$email->setOptions(
    array(
        'label' => 'Email',
        'required' => TRUE,
        'filters' => array(
            'StringTrim',
            'StripTags',
        ),
        'validators' => array(
            array(
                'Callback',
                true,
                array(
                    'callback' => function($value) {
                        $validator = new Zend_Validate_EmailAddress(
                            array(
                                'allow' => Zend_Validate_Hostname::ALLOW_DNS,
                                'domain' => true,
                                'mx' => true,
                                'deep' => true,
                            )
                        );

                        return $validator->isValid($value);
                    },
                    'messages' => array(
                        Zend_Validate_Callback::INVALID_VALUE => 'Please enter a correct email',
                    ),
                ),
            ),
        ),
    )
);

It might seem a little far fetched, but it works :P

This is a post I’ve been wanting to write for a while but I didn’t quite get the time to do it. It’s about me finally taking the ZF certification exam. I wanted to take this certification for over an year and a half, but first I postponed it because I was learning python and working with Django and I didn’t have the time to actually look over Zend Framework’s manual and read it. Then I moved to Spain, got caught up with other problems and so on, and so on…

Thing is that I bought a voucher for this exam about an year ago, and, as most Zend issued vouchers, it only lasts one year. So, few weeks ago I found myself with an already paid non-refundable voucher bound to expire. Since I had nothing to lose, I decided to take my chances and take the exam. And I did :) Hooray for me!

About the exam

The Zend Framework is much more “to the point” than the PHP 5 and it’s a piece of cake for the seasoned Zend Framework developer. No more “which comes first: the needle or the haystack?” questions. Everything is focused on asserting the fact that the candidate has the knowledge required to be productive using ZF. The topics cover a large part of the ZF manual, with focus on commonly used features. This doesn’t mean that you shouldn’t pay attention to more “exotic” topics like Zend_Memory, Zend_TimeSync or Zend_Wildfire. Although the test refers to version 1.5 of Zend Framework – the current version is 1.11 – there’s nothing specific to that version and the questions refer to those features that are common throughout all the versions (MVC, coding standards, plugins, validator chains, etc).

PS: don’t email me asking for the questions to the test! Go study…

01 Jun

Constants for table names

Posted by Tudor. Tags: ,

While working on a PHP project, I had the following idea: wouldn’t be better to use constants for table names? I mean, having a file, let’s say /application/config/tables.php, which would look something like:

define('TABLE_USERS', 'users');

And afterwards, use this value throughout the application:

…in the associated Zend_Db_Table_Abstract

class Users extends Zend_Db_Table_Abstract
{
    protected $_name = TABLE_USERS;
}

…in joins:

$this->select(Zend_Db_Table::SELECT_WITH_FROM_PART)
 ->setIntegrityCheck(false)
 ->join(TABLE_USERS, sprintf('%s.id = %s.user_id', TABLE_USERS, $this->_name));

…in the forms:

$validator = new Zend_Validate_Db_NoRecordExists(
    array(
        'table' => TABLE_USERS,
        'field' => 'email',
    )
);

This makes much more sense to me, since writing a simple ‘users’ string can be interpreted as a “magic number“…well…string and it’s very hard to maintain, should one want to, let’s say, prefix some of the tables in the database with their module’s name.

For example, if there’s already an “users” table in system and, in a new module, there’s the need for another “users” table, it would be less confusing to prefix the old table it’s called “old_module_table” and the call the new one “new_module_table”. Encapsulating the table’s name into a constant makes this really easy. I think I’ll use this in my next project.

Let’s face it. We can’t all buy or rent our own servers. It’s not cost effective. So we turn to hosting companies – always the popular choice. But when you’re using an shared hosting service, you can’t make changes in the server’s configuration file. You usually get a writeable directory in a location like

/home/{your-username}/public_html/

…and a Cpanel account to manage your share of the server, emails, databases and so on. And…that’s about it. You can’t point your webroot to the public directory as taught in Zend Framework’s manual, all your library files will be exposed to the public and so on. What to do then? The answer is simple: .htaccess and mod_rewrite. Usually, hosting companies install mod_rewrite and set AllowOverride to true, so this solution will work in the vast majority of cases.

Upload via FTP or SSH your content into your public_html, wwwroot or www directory (the webroot directory, whatever its name may be). Then add 4 htaccess files, one directly in the web root, one in the public directory and one in all other folders. It should look something like this (without the numbers, of course).

public_html/
     /public
       .htaccess (2)
     /application
        .htaccess (3)
     /library
        .htaccess (3)
    .htaccess (1)

Now, the first htaccess file – #1 – should contain the following:

RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]

…this will redirect the traffic from this folder to the public/ sub-folder. The second .htaccess – the one in the public folder – should be the .htaccess file shipped with Zend Framework. It usually looks like this:

SetEnv APPLICATION_ENV production 

Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]

And the .htaccess files located in all other directories – #3 – should simply forbid access to their respective folders, like such:

deny from all

Worked for me :)

firephpYou know the saying: if debugging means taking the bugs out, then programming means putting them in. Yes. We all have bugs in our code. And since not all of them can be marketed as “undocumented features”, from time to time we have to debug our applications.

The best debugger for PHP I’ve used so far is Zend’s. Zend Platform together with Zend Studio constitutes a very good development environment and a great debugging environment. Due to the fact that Zend Studio is a little pricey, I don’t use it any more, instead I’m using a highly customised vim. This makes a great development environment, but unfortunately isn’t not that great when debugging. I know you can use vim with Xdebug, but it’s quite a chore, and I don’t like it. Since old school debugging with var_dump() or print_r() is out of the question, I was looking for another way to debug my applications. And I’ve found just the thing: FirePHP. It’s a Firefox extension, just like FireBug – that can receive debug information from the server.

Since I do most of my bugging programming on Zend Framework, I also need debugging for this platform. I use the OOP based bootstraping method, where you extend your Bootstrap class from Zend_Application_Bootstrap_Bootstrap. And in the .htaccess file of the /public/ directory, I have an envelope with the current state of application (usually on of development/staging/production):

SetEnv APPLICATION_ENV development

Normally, I need the debug information only when the application is in the “development” state, so I’m using this method in the Bootstrap class.

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
    // Bootstrap other components

    /**
     * inits FirePHP for debugging
     *
     * @return void
     */
    protected function _initFirebugDebugger() {
        if(APPLICATION_ENV == 'development') {
            // don't debug while not in "development"
            $logger = new Zend_Log();
            $writer = new Zend_Log_Writer_Firebug();
            $logger->addWriter($writer);

            Zend_Registry::set('logger',$logger);
        }
    }
}

I also like to have some syntactic sugar when developing, so I’ve define this function in the Bootstrap.php file. Yes, I know that this might be perceived as a blasphemy by some of the OOP purists out there, but I really don’t care. If you don’t want non-OOP “stains” on your code, simply create a YourApplication_Utility_Firebug class or whatever with a static debug() method and paste the code in it.

/**
 * syntactic sugar for logging errors
 * and debug messages to FireBug
 *
 * @param string $message
 * @param int $label
 * @return void
 */
function fb($message, $label = null) {
    if($label != null) {
    	$message = array($label, $message);
    }

    if(Zend_Registry::isRegistered('logger')) {
    	Zend_Registry::get('logger')->log($message);
    }
}

And now, any time you need to debug something, simply type:

fb($variable);
fb($_POST); // and so on

And all these variables will be sent to Firefox’s FirePHP toolbar and you can inspect them from there. For even better results, you can also send debug information from your ErrorController to FirePHP (comes in handy when using Ajax).

PS: I’ll have a look into FireLogger for PHP. It also look pretty interesting, although it’s only in Beta.

While working on a Zend Framework project, I’ve found myself in need of a “same as” validator. If you’re familiar with Zend Framework’s form system, then you know what a validator is, if not, enhance your knowledge on the topic by reading this page from ZF’s manual.

My validator was supposed to check if the same value was entered into two password fields, to avoid the user’s typos in passwords. You know, those annoying “please re-type your password” fields. At the time, I couldn’t find a good validator for this purpose, so I’ve decided to write my own. The source code is available here:

/**
 * Checks if a field has the same value as another,
 * very useful in validating passwords
 *
 * @package Generic Validate
 * @author Tudor Barbu <miau@motane.lu>
 * @copyright MIT
 */

class Generic_Validate_SameAs extends Zend_Validate_Abstract {
    /**
     * Validation failure message key for when the values are not
     * the same
     */
    const NOT_THE_SAME = 'not_the_same';

    /**
     * the external element that we check the value against
     *
     * @var Zend_Form_Element
     */
    protected $_element;

    /**
     * Validation failure message template definitions
     *
     * @var array
     */
    protected $_messageTemplates = array(
        self::NOT_THE_SAME => 'The two values are not identical',
    );

    /**
     * Can receive a Zend_Form_Element parameter that will be used
     * into the validation process
     *
     * @param Zend_Form_Element
     */
    public function __construct(Zend_Form_Element $element = null) {
        if(null !== $element) {
            $this->setElement($element);
        }
    }

    /**
     * Set the element
     *
     * @param Zend_Form_Element $element
     * @return void
     */
    public function setElement(Zend_Form_Element $element) {
        $this->_element = $element;
    }

    /**
     * gets the element
     *
     * @return Zend_Form_Element
     */
    public function getElement() {
        return $this->_element;
    }

    /**
     * overrides isValid from Zend_Validate_Interface
     *
     * @param string $value
     * @return bool
     */
    public function isValid($value) {
        if(null === $this->_element) {
            require_once 'Zend/Exception.php';
            throw new Zend_Exception('You must add a Zend_Form_Element to the SameAs validator prior to calling the isValid() method');
        }

        if($value != $this->_element->getValue()) {
            $this->_error(self::NOT_THE_SAME);
            return false;
        }

        return true;
    }
}

// EOF

PS: The formatting plugin I’m using might not display everything correctly :( Aside from that, It’s quite easy to use:

// create some filters
$stringTrimFilter = new Zend_Filter_StringTrim();
$stripTagsFilter = new Zend_Filter_StripTags();

// other validators
$notEmptyValidator = new Zend_Validate_NotEmpty();
$notEmptyValidator->setMessage('This field is required');

// create the SameAs validator
$sameAsValidator = new Generic_Validate_SameAs();
$sameAsValidator->setMessage('The 2 fields do not coincide', Generic_Validate_SameAs::NOT_THE_SAME);

// create the main password field
$password = new Zend_Form_Element_Password('password');
$password->setOptions(
    array(
        'label'       => 'Password',
        'required'   => true,
        'filters'      => array($stringTrimFilter, $stripTagsFilter),
        'validators' => array($notEmptyValidator),
    )
);    

// add this field to the SameAs validator
$sameAsValidator->setElement($password);

// create the new
$validateNewPassword = new Zend_Form_Element_Password('validateNewPassword');
$validateNewPassword->setOptions(
    array(
        'label'        => 'Re-type the password',
        'required'   => true,
        'filters'      => array($stringTrimFilter, $stripTagsFilter),
        'validators' => array($notEmptyValidator, $sameAsValidator), // add it as a validator to the second field
    )
);

If the value entered in the second field isn’t the same as the value entered in the first field, the validator will signal an error. Simple as that.

Zend Framework has some weird “features” so to say, which pop out from time to time and get the programmers really annoyed. The latest thing quoteInto() method of Zend_Db_Adapter_Abstract class. This is supposed to quote input variables prior to entering them into a SQL command to avoid SQL injection attacks. Sounds cool, doesn’t it? Well…not quite. Because, because it can only quote one argument.

You can’t write:

// note: I usually extend my models from the Zend_Db_Table_Abstract class
// this code should be executed in a model of a such model
$select = new Zend_Db_Select($this->_db);
$select->from($this->_name);
$select->where($this->_db->quoteInto('(a = ? AND b = ?) OR (c != ?)', $a, $b, $c);

…as this won’t work. Of course, there’s a really lame approach, to concatenate the output from multiple quoteInto() calls. Like such:

$select->where(
    $this->_db->quoteInto('(a =? AND ', $a) .
    $this->_db->quoteInfo('b = >)', $b) .
    $this->_db->quoteInfo(' OR ( c != ?)', $c)
);

As I said, extremely lame. What’s to do then. Well, when I’ve hit this roadblock, I’ve opted for prepared statements. Of course, they brake the OOP encapsulation of SQL queries but if you don’t need to alter the query later it’s okay. So:

$stmt = $this->_db->prepare( 'SELECT * FROM ' . $this->_name . ' WHERE (a = ? AND b = ?) OR (c != ?)');
$stmt->execute(array($a, $b, $c));

This also works when you need to write custom sql queries:

$stmt = $this->_db->prepare('INSERT INTO ' . $this->_name . ' (first, second) VALUES (?, ?) ON DUPLICATE KEY UPDATE first = first + 1');
$stmt->execute(array($first, $second));

A decent workaround to the quoteInto() problem…