Unit testing Laravel 4 Auth extension

Laravel 4 is a great framework, it has all sorts of good stuff like IoC, automatic injection of Models into Controllers based on routes.

However, if you try and unit test a custom extension to the Auth package, you will find that some of the tests fail.

Here’s the code:

/**
 * ----------------------------------------------------
 * Extend the Auth functionality
 * to allow admin user to switch users
 * ----------------------------------------------------
 */
Auth::extend('eloquent', function($app) {
    $hasher = new \Illuminate\Hashing\BcryptHasher();
    $user_class = '\Repository\User\EloquentUserRepository';
    $provider = new \Illuminate\Auth\EloquentUserProvider($hasher, $user_class);
    return new CustomAuthGuard($provider,  App::make('session.store'));
});

This works fine if you test it by clicking through but this unit test failed

public function testLoginAsUser()
{
     $login_user = EloquentUserRepository::find($this->admin_user_id);
     $this->be($login_user);
     $this->assertEquals(Auth::user()->id, $this->admin_user_id);
}

The problem turned out to be in the way that the ‘be’ function set the logged in user

public function be(UserInterface $user, $driver = null)
{
     $this->app['auth']->driver($driver)->setUser($user);
}

The Auth::user() function finds the user by checking the session for the user id and then querying that user against the UserProvider. It relies on a session key ‘login_’+md5(class name). In testing, this is not being picked up. The solution I found was to use this instead.

public function testLoginAsUser()
{
     $login_user = EloquentUserRepository::find($this->admin_user_id);
     Auth::login($login_user);
     $this->assertEquals(Auth::user()->id, $this->admin_user_id);
}

Zend Framework: Adding XHTML within a form

I recently needed to add some custom HTML within a form. It proved to be easy but I did need to write some custom code to do it.

First, I had to create a HTML form element by subclassing Zend_Form_Element

class Misc_FormHtml extends Zend_Form_Element_Xhtml{
 /**
     * Load default decorators
     *
     * @return Zend_Form_Element
     */
    public function loadDefaultDecorators()
    {
        if ($this->loadDefaultDecoratorsIsDisabled()) {
            return $this;
        }

        $decorators = $this->getDecorators();
        if (empty($decorators)) {
            $getId = create_function('$decorator',
                'return $decorator->getElement()->getId()
                        . "-element";');
            $this ->addDecorator(new Decorators_Html())
                ->addDecorator('HtmlTag', array('tag' => 'div',
                    'id'  => array('callback' => $getId)))
           ;
        }
        return $this;
    }
}

Zend loads the decorators from the inside out so in order to wrap my HTML in a div tag, I need to add a custom decorator (new Decorators_Html() ) first and then add the HtmlTag decorator second

I wrote a very simple decorator.

class Decorators_Html  extends Zend_Form_Decorator_Abstract
{
    public function render($content)
    {
       //return $content;
        $element = $this->getElement();
        $value = $element->getValue();
        return $content.$value ;
    }

}

Then I set it up to be used:

... form code
 $html = new Misc_FormHtml();
        $html->setValue('

Here is some text that we are really hoping will appear on the page without being messed up by Zend

') ->setAttrib('id','top_form_text'); $category_sub_form->addElement($html);

Tomcat 6 slow shutdown problems

I’m running Tomcat 6 for a client and the server has had a history of really slow shutdowns. The server admin people couldn’t find a reason but finally, I tracked down the problem

The Ubuntu syslog in /var/log/syslog was showing the following entry:

org.apache.catalina.connector.Connector pause SEVERE: Protocol handler pause failed java.net.ConnectException: Connection timed out

The cause of this was apparent when I ran the command line

hostname

The result was “www“, not very helpful

I updated the /etc/hostname file and then ran

/etc/init.d/hostname.sh start

The stop time dropped from around 3 minutes to 30 seconds.

Zend Framework: Uploading and resizing an image

I’ve needed to upload and resize an image on a Zend Framework. Not especially difficult but it does take a little setting up.

First, I need to set up some upload locations for the final files. This was in my application.ini file like this:

paths.images.thumbs=APPLICATION_PATH "/../public/photos/thumbs"
paths.images.web=APPLICATION_PATH "/../public/photos/web"
paths.images.original=APPLICATION_PATH "/../public/photos/original"

Then I needed to set up the form

class Application_Form_Photos extends Zend_Form
{

    public function init()
    {
        $decorator = new Decorators_Inputs();

        // Add a name element
        $this->addElement('text', 'name', array(
            'label' => 'Photo Name:',
            'required' => true,
            'style' => 'width:200px;',
            'filters' => array('StringTrim'),
            'validators' => array(
                'NotEmpty'
            ),
            'decorators' => array($decorator)
        ));

        $photo = new Zend_Form_Element_File('photo');
        $photo->setLabel('Photo (jpg only)')
            ->setDestination(Zend_Registry::get('config')->paths->images->original);
        // ensure only one file
        $photo->addValidator('Count', false, 1);
        // max 10MB
        $photo->addValidator('Size', false, 10097152)
            ->setMaxFileSize(10097152);
        // only JPEG, PNG, or GIF
        $photo->addValidator('Extension', false, 'jpg,png,gif');
        $photo->setValueDisabled(true);
        $photo->setDecorators(array('File', new Decorators_Files()));
        $this->addElement($photo, 'photo');

        $submit = new Zend_Form_Element_Submit('submit');
        $submit->setValue('Upload');
        $submit->setName('Upload');
        $submit->setDecorators(array(new Decorators_Submit()));
        $this->addElement($submit);

    }

}

The Decorators_Files class is a decorator (See here for how to set that up)

class Decorators_Files extends Zend_Form_Decorator_Abstract
{
    protected $_format = '
    
%s 

'; public function render($content) { $element = $this->getElement(); $name = htmlentities($element->getFullyQualifiedName()); $label = htmlentities($element->getLabel()); $id = htmlentities($element->getId()); $value = htmlentities($element->getValue()); $style = htmlentities($element->getAttrib('style')); $class = htmlentities($element->getAttrib('class')); $error = ""; $errors = $element->getErrors(); if($errors){ $error = 'Please enter a value'; $class = 'error'; } $markup = sprintf($this->_format, $name, $label, $id, $name, $value,$class, $style,$error); return $markup; } }

Now, I need the code to handle the upload

/**
     * Handle the image upload
     * @param Application_Form_Photos $form, the form that contains the image
     * @param Zend_Controller_Request_Abstract $request, the request
     * @param Application_Model_MediaMapper $media_mapper, the Model class which handles saving the file details
     */
    private function handle_image_upload(Application_Form_Photos $form, Zend_Controller_Request_Abstract $request, Application_Model_MediaMapper $media_mapper)
    {
       //Create an entry in the DB
        $extension = pathinfo($form->getElement('photo')->getValue(), PATHINFO_EXTENSION);
        $media_model = new Application_Model_Media();
        $media_model->setName($request->getParam('name'))
            ->setThumb('')
            ->setWeb('')
            ->setOriginal('');
        $media_mapper->save($media_model);

       //Rename the file
        $form->getElement('photo')->addFilter('Rename', array(
            'target' => $media_model->getId() . '.' . $extension,
            'overwrite' => true
        ));

        //Add the filter to resize to web image
        $form->getElement('photo')->addFilter(new Filters_File_Resize(array(
            'width' => 800,
            'height' => 600,
            'keepRatio' => true,
            'directory' => Zend_Registry::get('config')->paths->images->web
        )));

        //Add the filter to resize to thumb, note it can't be the same filter as above or the web image action will fail
        $form->getElement('photo')->addFilter(new Filters_File_Thumb(array(
            'width' => 200,
            'height' => 133,
            'keepRatio' => false,
            'directory' => Zend_Registry::get('config')->paths->images->thumbs
        )));

        //Upload the photo
        if ($form->getElement('photo')->receive()) {
            $media_model->setThumb('photos/thumbs/' . $media_model->getId() . '.' . $extension);
            $media_model->setWeb('photos/web/' . $media_model->getId() . '.' . $extension);
            $media_model->setOriginal('photos/original/' . $media_model->getId() . '.' . $extension);
            $media_mapper->save($media_model);
        }
    }

Finally, I need some filters that will handle the image resize.

I initially used the filter written by Stefan Koch. See
http://eliteinformatiker.de/2011/09/02/thumbnails-upload-and-resize-images-with-zend_form_element_file/

Moodle unit testing through simple test

I’ve used the simpletest PHP unit test framework and as Moodle uses the simpletest, I thought that setting up unit tests and running them would be straightforward. As with a lot of things Moodle, there are various gotchas which are not documented and it was only by searching through the source code that I was able to get them running.

I set up a unit test class and ran it through Site Administration -> Development -> Unit Tests. The first thing that went wrong was it complained that unittestprefix had not been set. A search through the source revealed that this variable refers the prefix you need to use on the test tables in the Moodle database. In my config.php, the setting was

//Unit testing. 
//This refers to the prefix for the test tables in the database
$CFG->unittestprefix = 'testing_';

The next problem was that Moodle couldn’t find my unit tests. It took a bit of digging but I found that I had prefix the name the php file with the unit test with ‘test_’. After that, my tests worked OK.

if (!defined('MOODLE_INTERNAL')) {
   ///  It must be included from a Moodle page
    die('Direct access to this script is forbidden.');    
}

global $DB, $CFG;

class test_block_rollover_clone_test extends UnitTestCase
{
    public function testInitialTesting()
    {
        $this->assertFalse(true);
    }

}

This resulted in the following:

Moodle form validation tips

There are a number of gotchas with Moodle Quick Form and it’s only through trial and error that I’ve discovered how they work.

The first is that when you implement any sort of validation, the form will only return data when the validation passes.

The second is that you don’t call any custom validation directly, you need to check that is_validated() is returning true.

class block_rollover_form extends moodleform
{

    function definition()
    {
        $form = $this->_form;
        //add form definitions here
        $form->addElement('date_selector', 'livedate', get_string('livedate', 'block_rollover'), array(
            'startyear' => 2012,
            'stopyear' => 2020
        ));

        $form->addElement('date_selector', 'archivedate', get_string('archivedate', 'block_rollover'), array(
            'startyear' => 2012,
            'stopyear' => 2020
        ));
        
    }

    /**
     * Custom validation to check that the start date and archive date are in future
     *
     * @param array $data
     * @param array $files
     * @return array
     */
    function validation($data, $files)
    {
        $errors = parent::validation($data, $files);
        $now = time();
        $live_date = $data['livedate'];
        $archive_date = $data['archivedate'];
        if ($live_date <= $now) {
            $errors['livedate'] = 'Please ensure that the start date for the new course is after today';
        }

        if ($archive_date <= $now) {
            $errors['archivedate'] = 'Please ensure that the archive date for the old course is after today';
        }
        return $errors;
    }


    /**
     * This function handles the form submission
     */
    public function handle()
    {
        //Do nothing if submitted or cancelled.
        if (!$this->is_submitted() || $this->is_cancelled()) {
            return;
        }

        //If the validation fails, it will print out the error messages on the form
        //note that $this->get_data() will return empty until the validation passes.
        if (!$this->is_validated()) {
            return;
        }

        //Form has been submitted and it validates, now we can get the data in the form
        $data = $this->get_data();

    }
}

Tomcat6 on Ubuntu, heap size and perm gen size

I’ve been having some trouble with a server running Tomcat6 on Ubuntu with a Spring/Hibernate set up. It also does some image resizing via Image Magick.

The problems were that the perm gen size for the Hibernate objects was too small and it ended up running out of memory.

No problem, in Ubuntu, the fix was going /etc/default/tomcat6 and editing the JAVA_OPTS variable.

I started off with

JAVA_OPTS="-XX:MaxPermSize=512m"

That solved the Hibernate problem but I was soon getting out of memory errors for image uploads. I had to add this into the file

JAVA_OPTS="-XX:MaxPermSize=512m -Xms256m -Xmx512m"

Running this gave me some useful feedback about the memory that Java was using.

jmap -heap <pid>

There are some useful resources out there for anyone interested in reading more

http://diegobenna.blogspot.co.uk/2011/02/how-to-increase-heap-size-in-tomcat-6.html

http://www.yaronco.com/tomcat6-and-java-heap-size/

PHP upload limits

Every now and then I need to re-set the PHP upload limits in the PHP ini file.

The obvious setting is

upload_max_filesize = 1024M

The less obvious settings are:

file_uploads = On
max_file_uploads=20
post_max_size=1024M

I’ve found it easy to forget to change post_max_size which need to be the same or greater than the upload_max_filesize.

Zend Framework, styling and decorating a form

Although extending Zend_Form produces a really easy form, the layout that results is less than perfect. This is where subclassing Zend_Form_Decorator_Abstract comes in. Here’s the implementation for a text field.

class Decorators_Inputs extends Zend_Form_Decorator_Abstract
{
    protected $_format = '
    <div class="element">
        <div class="label_col"
            <label for="%s">%s</label>
        </div>
         <div class="input_col">
            <input id="%s" name="%s" type="text" value="%s" class="%s" style="%s"/>
         </div>
         <div class="error_col">
            %s 
         </div>
     </div>
     <br clear="left" />
     ';

    public function render($content)
    {
        $element = $this->getElement();
        $name    = htmlentities($element->getFullyQualifiedName());
        $label   = htmlentities($element->getLabel());
        $id      = htmlentities($element->getId());
        $value   = htmlentities($element->getValue());
        $style   = htmlentities($element->getAttrib('style'));

        $class = "";
        $error = "";
        $errors = $element->getErrors();
        if($errors){
            $error = 'Please enter a value';
            $class = 'error';
        }

        $markup  = sprintf($this->_format, $name, $label, $id, $name, $value,$class, $style,$error);
        return $markup;
    }
}

Zend framework, sending an email

Sending an email in the Zend Framework turns out to be reasonably easy. This example uses the contact form for a web site

public function contactAction()
    {
        $request = $this->getRequest();
        $form = new Application_Form_Contact();

       //Check if form has been submitted
        if ($this->getRequest()->isPost()) {
            if ($form->isValid($request->getPost())
              && $this->send($this->getRequest())) {

                $this->_forward('thanks');
            }
        }

        $this->view->form = $form;
    }

    private function send(Zend_Controller_Request_Abstract $request)
    {

        $session = new Zend_Session_Namespace();

        if (isset($session->numberOfPageRequests)) {
            $session->numberOfPageRequests++;
        } else {
            $session->numberOfPageRequests = 1;
        }

        //pretend mail has been sent for people repeatedly hitting refresh
        if ($session->numberOfPageRequests > 2) {
            return true;
        }

        $email_mapper = new Application_Model_EmailMapper();
        //Get the email text from the database.
        $email = $email_mapper->getEmailByName('ContactEmail', $request);

        $mail = new Zend_Mail();
        $mail->setBodyText($email->getText());
        $mail->setBodyHtml($email->getHtml());
        $mail->setFrom('somebody@example.com', 'Some Sender');
        $mail->addTo('somebody_else@example.com', 'Some Recipient');
        $mail->setSubject('TestSubject');
        $mail->send();

        return true;

    }

This is the result of sending the email

I’m using Toolheap’s Test email SMTP server to send test emails during development. It’s proven to be a very useful tool for verifying designs and content of the emails before they are put live.