Structured handling of form data (Zend Framework)

Another gem from the book – Practical Web 2.0 Applications with PHP

The rule of thumb in processing webforms is to sanitize ALL input. At a minimum, strip tags and remove spaces (Unless you are accepting HTML/javascript input to allow users to construct pages).

From a good software engineering perspective, it behooves us to create a class that takes care of this for every single form in our app. The requirements are:

  1. Sanitize, sanitize, sanitize!
  2. Collect form values – so they can be re-displayed to the appropriate form fields in case of an error.
  3. Collect all the errors – it would be handy to pass this to the view to display appropriate error messages

With these requirements in mind, we can develop an abstract base class for form processing that looks like:

<?php
abstract class FormProcessor
{
    protected $errors=null;
    protected $vals=null;
    private $appliedFilter=null;
    abstract function process(Zend_Controller_Request_Abstract $request);
    public function sanitize($value)
    {
        $this->appliedFilter=new Zend_Filter();
        $this->appliedFilter->addFilter(new Zend_Filter_StripTags());
        $this->appliedFilter->addFilter(new Zend_Filter_StringTrim());
        return $this->appliedFilter->filter($value);
    }
    public function addError($key, $val)
    {
        $this->errors[$key]=$val;
    }
    public function getError($key)
    {
        return $this->errors[$key];
    }
    public function hasError($key=null)
    {
        if (strlen($key)==0)
            return (count($this->errors)>0);
        return array_key_exists($key,$this->errors);
    }
    public function __set($name, $value)
    {
        $this->vals[$name]=$value;
    }
    public function __get($name)
    {
        $ret=array_key_exists($name,$this->vals)?$this->vals[$name]:null;
        return $ret;
    }
}
?>

The Zend Framework provides various classes to sanitize user input. The sanitize() method above makes use of two of them –Zend_Filter_StripTags() and Zend_Filter_StringTrim(). Take a look at the ZF documentation for a whole list of filters that you can use.

Now, we can go ahead and create concrete classes from this abstract class. For instance, the login form that we just developed.
Note that the ‘process’ method is marked abstract in FormProcessor and therefore an implementation for the ‘process’ method MUST be found in all our concrete class.

<?php
class LoginProcess extends FormProcessor
{
    private $username=null;
    private $password=null;
    public function process(Zend_Controller_Request_Abstract $request)
    {
        $this->username = $this->sanitize($request->getPost(‘username’));
        if (strlen($this->username)==0)
            $this->addError(‘username’,’Please enter username’);
        $this->password=$this->sanitize($request->getPost(‘password’));
        if (strlen($this->password)==0)
            $this->addError(‘password’,’Please enter password’);
        return (!$this->hasError());
    }
}
?>

 

The class implements abstract function process(). It returns a boolean – true if the form has errors and false otherwise.

Here is the new AuthController.php class using our LoginProcess class:

<?php
class AuthController extends BaseController
{
    private $authProcess=null;
    public function indexAction()
    {
        $this->_forward(‘login’);
    }
    public function logoutAction()
    {
        $id = Zend_Auth::getInstance();
        $id->clearIdentity();
        $this->_redirect("/");
    }
    public function loginAction()
    {
        $request = $this->getRequest();
        $validate = $request->isXmlHttpRequest();
        $this->authProcess = new LoginProcess();
        if ($request->isPost())
        {
            $username = $request->getPost(‘username’);
            $password = $request->getPost(‘password’);
           if ($this->authProcess->process($request))
            {
                //no errors with form.. process
                $authAdapter = $this->_getAuthAdapter($username,$password);
                $auth=Zend_Auth::getInstance();
                $result = $auth->authenticate($authAdapter);
                if ($result->isValid())
                {
                    //success.. store row
                    $data = $authAdapter->getResultRowObject();
                    $auth->getStorage()->write($data);
                    $this->_redirect(‘/’);
                }
            }
        }
        if ($validate)
        {
            $this->sendJson(array(‘errors’=>$this->authProcess->getErrors()));
        }
        else
        {
            $this->view->authProcess=$this->authProcess;
        }
    }
    private function _getAuthAdapter($username, $password)
    {
        $dbAdapter = self::$db;
        $authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter,’users’,’username’,’password’);
        $authAdapter->setIdentity($username);
        $authAdapter->setCredential($password);
        return $authAdapter;
    }
}
?>

Notice how the entire authProcess object is passed onto the view for easy processing. Finally, the modified login.phtml is :

<h1>Login</h1>
<p>Please log in here</p>
<form method="post" id="login" action='<?php echo $this->baseUrl?>/auth/login’>
<div>
<label>Username</label>
<input type="text" name="username" value=""/>
<div class="error"> <?php 
echo $this->authProcess->getError(‘username’);
?>
</div>
</div>
<div>
<label>Password</label>
<input type="password" name="password" value="" />
<div class="error"> <?php
echo $this->authProcess->getError(‘password’);
?> </div>

</div>
<div>
<input type="submit" name="login" value="Login" />
</div>
</form>
<script type="text/javascript" src="<?php echo $this->baseUrl?>/js/ValidateAjax.js"></script>
<script type="text/javascript">
    new ValidateAjax(‘logi
n’);
</script>

There.. now you have it.. a well engineered ZF with Ajax solution for authentication.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s