RSS
 

Archive for December, 2011

Traits – PHP5.4

31 Dec

Since it is new years eve, I don’t want to start a complete new chapter. So I will write one last post this year with some more general topic. And yes – I find it so interesting that I have to mention it in an own post :)

Traits

With PHP5.4 approaching fast, it will probably be the standard sometime in 2012. It will have one major feature that we will all going to love: oop5.traits.

What are they? The above link describes it pretty good. They are some kind of behavior – like the one we attach to models right now. Only way more flexible and powerful. Every class can then use those behaviors.

trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}
 
trait World {
    public function sayWorld() {
        echo 'World!';
    }
}
 
trait HelloWorld {
    use Hello, World;
}
 
class MyHelloWorld {
    use HelloWorld;
}
 
$Foo = new MyHelloWorld();
$Foo->sayHello();
$Foo->sayWorld();
The result would be Hello World!

Imagine what would be possible with Controllers, Components, Shells, and other classes that can now not only extend parent classes but also use traits.

Cool things you can do with them

The above link contains a good set of examples. I will outline the most interesting ones:

public function sayWhere() {
    echo __CLASS__; // same with __FILE__ etc
}
As of right now objects that are extended by some subclass would always return the wrong class, because inside a class those magic constants always relate to the current file (and therefore often the parent class defining it). With traits the magic is endless – they will make it possible to inject functionality that overcomes that deficiency:
trait sayWhere {
    public function whereAmI() {
        echo __CLASS__;
    }
}
 
class Hello {
    use sayWHere;
}
 
class World {
    use sayWHere;
}
 
$One = new Hello;
$One->whereAmI(); //Hello
 
$Two = new World;
$Two->whereAmI(); //World

Another neat feature is, that we can override specific methods if needed – both ways:

class Base {
    use SayBye;
 
    // we want this method to be overridden by the trait to do some extra stuff with it
    // can only be done in the inheriting class, though
    public function sayHello() {
        echo 'Hello ';
    }
}
 
trait SayWorld {
    public function sayHello() {
        parent::sayHello();
        echo 'World!';
    }
}
 
trait SayBye {
    // this method will be be overridden by the class method to do some extra stuff with it
    // can only be done in the inheriting class, though
    public function sayBye() {
        echo 'Bye ';
    }
}
 
class MyHelloWorld extends Base {
    use SayWorld;
 
    public function sayBye() {
        echo parent::sayBye();
        echo 'World!';
    }
}
 
$Foo = new MyHelloWorld();
$Foo->sayHello(); //Hello World!
$Foo->sayBye(); //Bye World!

Update 2012-02-24: Interesting Link

There is an interesting page about the potential dangers of those new PHP5.4 features you might want to read before starting to write cutting-edge code :)

 
No Comments

Posted in PHP

 

Tools Plugin – Part 2: Contact Form

15 Dec

I want to show how easy it is to make a solid and universal contact form in Cake(2). The full code can be found in the github rep of the Tools plugin.

Model

The most important part first: We need a solid validation for the form. How many forms are out there that do not have any validation. The first thing I always do on about any public contact form: Hit the submit button and smile it if tells me that my empty form “has just been successfully sent”. We can do better than that :)

class ContactForm extends ToolsAppModel {
 
    protected $_schema = array(
        'name' => array('type' => 'string' , 'null' => false, 'default' => '', 'length' => '30'),
        'email' => array('type' => 'string' , 'null' => false, 'default' => '', 'length' => '60'),
        'subject' => array('type' => 'string' , 'null' => false, 'default' => '', 'length' => '60'),
        'message' => array('type' => 'text' , 'null' => false, 'default' => ''),
    );
 
    public $useTable = false;
 
    public $validate = array(
        'name' => array(
            'notEmpty' => array(
                'rule' => array('notEmpty'),
                'message' => 'valErrMandatoryField',
                'last' => true
            )
        ),
        ...
    );
The _schema var mocks a database table so that we don’t really need one. This helps the FormHelper to generate the inputs (maxlength, type, …). The validation rules will make sure the email is valid and the user actually entered some text.

Controller

The logic cakes care of the validation on POST. /Controller/ContactController.php

<?php
class ContactController extends AppController {
 
    public $uses = array('Tools.ContactForm');
 
    public function index() {
        if ($this->request->is('post') || $this->request->is('put')) {
            if (!$this->Session->check('Auth.User.id')) {
                $this->ContactForm->Behaviors->attach('Tools.Captcha');
            }
            $this->ContactForm->set($this->request->data);
            if ($this->ContactForm->validates()) {
                $name = $this->request->data['ContactForm']['name'];
                $email = $this->request->data['ContactForm']['email'];
                $message = $this->request->data['ContactForm']['message'];
 
                //send email with CakeEmail
            } else {
                $this->Common->flashMessage(__('formContainsErrors'), 'error');
            }
 
        }
        $this->helpers = array_merge($this->helpers, array('Tools.Captcha'));
    }
}
As you can see it only sends emails after successfully validating. For public contact forms I usually like some easy captcha behavior attached so that spam doesnt reach me. You can omit that, of course. Also note: you should use your own setFlash() method instead of mine!

Last but not least: View

/View/Contact/index.ctp

<?php echo $this->Form->create('ContactForm');?>
    <fieldset>
        <legend><?php echo __('contactLegend');?></legend>
    <?php
        echo $this->Form->input('name');
        echo $this->Form->input('email');
 
        echo $this->Form->input('subject');
        echo $this->Form->input('message', array('rows'=>15));
 
        if (!$this->Session->read('Auth.User.id')) {
            echo $this->Captcha->input('ContactForm');
        }
    ?>
    </fieldset>
<?php echo $this->Form->submit(__('Submit')); ?>
<?php echo $this->Form->end();?>

Result

Browse to /contact/ That should display the form right away. After the successful POST you should redirect back to the contact form (emptied then) or to another custom page.

Final notes

Currently, the Model is in the Tools.Plugin. You could put it into your normal app model folder, as well. But I use it in many projects and therefore I want to keep it dry. Feel free to adjust any of the code to your own needs. Same goes for my own custom methods like $this->Common->flashMessage() etc.

Some might wonder why “ContactController” and not “ContactsController”. The latter would be cake conventions. But there are situations where you can and should diverge form those. As in this case where “contacts” would mean more like sth to manage your addressbook. It also makes the url more meaningful out of the box (without any custom routes). With cake2 this all works without any additional customization.

The other thing: I didn’t name the Model “Contact” but “ContactForm” in order to not create possible conflicts which exactly such contacts/contact mangement MVCs (as I did in a contact management suite).

 
No Comments

Posted in CakePHP

 

Unit-Testing Tips for 2.0 and PHPUnit

04 Dec

Quite some time ago I wrote about Unit testing. But that was still in 1.3 and with SimpleTest. A lot has changed since then.

Execution Order

The documentation wasn’t all that clear about it. So I tried it with the following test file:

App::uses('MyCakeTestCase', 'Tools.Lib');
 
class TestCaseExecutionOrderTest extends MyCakeTestCase {
 
    public function setUp() {
        parent::setUp();
 
        $this->out('setUp');
    }
 
    public function tearDown() {
        $this->out('tearDown'); 
 
        parent::tearDown();
    }
 
    public function startTest() {
        $this->out('startTest');
    }
 
    public function endTest() {
        $this->out('endTest'); 
    }
 
 
    public function testFoo() {
        $this->out('* foo *');
    }
 
    public function testBar() {
        $this->out('* bar *'); 
    }
 
}
Note the parent calls for setUp and tearDown. Those need to be set. The other 2 methods don’t require this.

The result was pretty obvious:

setUp
startTest
* foo *
endTest
tearDown
 
setUp
startTest
* bar *
endTest
tearDown

Debug Output with PHPUnit >= 3.6

On the release notes of CakePHP2.0.3 you could find the note

A big difference people will notice when writing unit tests is that all output is swallowed by PHPUnit and not presented in either the web tester page nor in the CLI tester. To overcome this annoyance use the--debug modifier if you are using the CLI interface
Well, that broke a lot of existing debug code, of course. So I tried to come up with a solution. The Shell uses a specific output(). Why not use sth like that in test cases, too?

/**
 * outputs debug information during a web tester (browser) test case
 * since PHPUnit>=3.6 swallowes all output by default 
 * this is a convenience output handler since debug() or pr() have no effect
 * @param mixed $data
 * @param bool $pre should a pre tag be enclosed around the output
 * @return void
 * 2011-12-04 ms
 */
public function out($data, $pre = true) {
    if ($pre) {
        $data = pre($data);
    }
    echo $data;
    if (empty($_SERVER['HTTP_HOST'])) {
        # cli mode / shell access: use the --debug modifier if you are using the CLI interface
     return;
    }
    ob_flush();
}
As you probably noticed, I use it in the above “execution tryout”. You can make yourself a custom “MyCakeTestCase” class with then extends the core test case class and put it in there.

UPDATE January 2012

The core (>=2.0) now contains a native way to enable debugging output for your tests. You need to append &debug=1 to the url. Not ideal, but it works.