RSS
 

Posts Tagged ‘Testing’

CakePHP Tips 2017 Part 1

14 Apr

Whoops as ErrorHandler

I recently switched to Whoops in my apps.
It provides a better exception rendering for development mode for most cases.

Just load the plugin (gourmet/whoops) and modify the bootstrap line:

//(new ErrorHandler(Configure::consume('Error')))->register();
(new \Gourmet\Whoops\Error\WhoopsHandler(Configure::consume('Error')))->register();

If you already have a src/Application.php and run a middleware setup, you also want to make sure you either remove the ErrorHandlerMiddleware there, or provide a custom one for Whoops.

You can test it for example with my sandbox app.
Check it out locally, set it up in minutes and give it a test run.

Jump from browser to IDE

With the Whoops error handler the probably nicest feature is the "click to IDE" one.
The open PR from me enables this.

It allows to click on the class inside the displayed code piece and jump right into the IDE into the exact line of that file displayed in the browser stack trace.

Be more productive leveraging your IDE

Using the new IdeHelper plugin you can now not only bake annotations into new code, but also adjust
your existing code all the time.
The plugin provides a shell to quickly sync the existing annotations with your code.

This way you can maximize IDE compatibility and with that prevent obvious issues and mistakes.
The IDE will usually mark problematic code yellow (missing, wrong method etc).

It can also enable you to click through the classes and object chains which otherwise would just be plain text and have no typehinting.
Finally autocomplete will be available for all the "magically" provided classes and methods this way.

screenshot

This plugin is supposed to work with ANY IDE that supports annotations.
The plugin wiki contains even details and tips/settings for each of those IDEs – collected by the community.

Enable PHPStan in your app and plugins

PHPStan is a super useful static code analysis tool.
It will help you to find issues with your code way beyond what the test suite or IDE syntax checks can do.

For example, if you have two many different return types and only respect one of them, it will mark this as issue.
It will also tell you about interface mismatch or other similar code smells.

Adjust Travis

Add a new matrix row and a script call for that matrix entry:

...
matrix:
  include:
    ...
    - php: 7.0
      env: PHPSTAN=1 DEFAULT=0
...
script:
  ...
  - if [[ $PHPSTAN == 1 ]]; then composer require --dev phpstan/phpstan && vendor/bin/phpstan analyse -l 1 src; fi
...

Level 1 (-l 1) is the lowest level, once everything passes you can go up to level 5 currently. I recommend trying level 3, if possible.

If you need to ignore certain errors, you can use a custom phpstan.neon file in your /tests directory and then append -c tests/phpstan.neon into the command for travis.

vendor/bin/phpstan analyse -c tests/phpstan.neon -l 1 src

For me such a file looked like this, for example:

parameters:
	autoload_files:
        	- %rootDir%/../../../tests/bootstrap.php
	excludes_analyse:
		- %rootDir%/../../../src/TestSuite/*
	ignoreErrors:
		- '#Call to an undefined method Cake\\Datasource\\EntityInterface\:\:source\(\)#'

Don’t try to ignore too much, though – only "false positives" are recommended to be added here.

You can also write custom PHPStan extensions, see the current CakePHP core /tests/PHPStan folder for details.

Using it locally

I added this to my plugins’s composer.json files:

"scripts": {
    ...,
    "phpstan": "phpstan analyse -l 1 src",
	"phpstan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan && mv composer.backup composer.json"
}

This will allow local testing without messing up your composer file 🙂
For app instead of plugin development you also want to backup-restore the lock file.

Now just run

composer phpstan-setup
composer phpstan

I bet you will find a few useful issues to resolve right on the first run of this tool.

 
No Comments

Posted in CakePHP

 

CakePHP 2.5 arrived + CakePHP Tips 2014

13 May

You probably read my last tip sections.
And I started to move some of them to my sandbox app.

But once in a while it might also be nice to publish a few selected tips, here as well.

Oh, and CakePHP 2.5 is out! Get it – now 🙂
See the last chapter of this post about why you really should upgrade ASAP. And it doesn’t matter if you
are on 1.x or 2.x.

URLs in CLI

In CLI and your shells there is no HTTP HOST and therefore usually no absolute URLs.
But in order to properly work with links/urls (e.g. sending batch emails), we need that to work.
So I use Configure::read(‘Config.fullPageUrl’) here.
My configs.php file then contains:

$config['Config'] = array(
	'fullPageUrl' =>  http://www.myapp.de // The primary URL
)

In case you have a different domain for local development like http://myapp.local and you want that to be the fullPageUrl, make sure you overwrite the default in your configs_private.php file:

Configure::write('Config.fullPageUrl', 'http://myapp.local'); // Usually defaults to the (live) primary URL

And in the end of my bootstrap file (after including all config files), I simply do:

if (php_sapi_name() === 'cli') {
	Configure::write('App.fullBaseUrl', Configure::read('Config.fullPageUrl'));
}

To test the current domain/fullBaseUrl, you can use my Setup plugin command cake Setup.TestCli router.
It will output both relative and absolute URL examples generated by the Router class with your current settings.

So on the live server then it will output http://myapp.local instead of http://localhost when generating Router::url()s in your shells.

Careful with running shells in CLI

Most are probably not aware, but running shells in CLI needs to have a proper user management around them in most cases.
Imagine yourself running your apache as www-data (default) and log in as root or any other user not affiliated with that www-data user/role (bad idea).
Once you execute a shell and tmp cache data are (re)built, your www-data user cannot access them anymore, losing the ability to cache and triggering a lot of errors.
So make sure you only log in with a user that shares the role of www-data at least, so that both can access each others’ generated files.

A popular example is the ClearCache shell which re-builds your cache dirs in debug 0 (when changing files or db schema makes this necessary).

PS: Of course you could also switch to another cache system than the default File engine. But most probably didn’t do that yet, either.

Merging arrays

Ever wondered what Hash::merge(), array_merge and array_merge_recursive have in common – or don’t have in common?
Check out these merge comparison examples.

See what the requirements are – and use the appropriate merge method then.

There is also the + operator, which is quite useful when merging flat arrays and string key based options. This is quite commonly used in the core to merge
options and defaults:

$defaults = array(
	'escape' => true
):
$options += $defaults;

In this case the $defaults are merged on top of $options, but only if that key has not been specified yet.
This kind of merge is really efficient and fast (4-5x faster than array_merge() itself) – but should really only be used if all keys are definitely strings.

Paginating and merging

A propos merging: When setting up paginate settings in your controllers, try to prevent

public function index() {
	$this->paginate = array(...);
}

This way you kill all defaults you might have set via public $paginate or in your extending controllers (AppController’s beforeFilter() callback for example).

So it is better to use:

$this->paginate = array_merge($this->paginate, array(...));
// or
$this->paginate = array(...) + $this->paginate;

In my 2.x code for example I have this snippet in all my AppControllers to have query strings for paginations:

public function beforeFilter() {
	parent::beforeFilter();
	$this->paginate['paramType'] = 'querystring';
}

This will only work with proper merging of defaults and custom settings.
I prefer the latter because the settings are string based and here the + operator is the fastest and easiest way of doing things.
Once the key is already set in your method, the default will be ignored right away (with array_merge() and nullish values this can be different/unexpected).

And remember to not mix controller and component pagination.

Pagination and sort default order

Adjust your bake templates so that some fields like created/modified are ordered DESC instead of ASC per default in the index actions.
For those fields the first click on the header row should display them DESC right away as one would then most likely be interested in the
latest changes. Same goes for most date fields like "published" as well as fields like "priority", "rating", …

That’s how the baked code (or manually adjusted one if done afterwards) could then look like (index.ctp):

<th><?php echo $this->Paginator->sort('name');?></th>
<th><?php echo $this->Paginator->sort('amount');?></th>
<th><?php echo $this->Paginator->sort('priority', null, array('direction' => 'desc'));?></th>
<th><?php echo $this->Paginator->sort('status', null, array('direction' => 'desc'));?></th>
<th><?php echo $this->Paginator->sort('publish_date', null, array('direction' => 'desc'));?></th>
<th><?php echo $this->Paginator->sort('modified', null, array('direction' => 'desc'));?></th>
<th><?php echo $this->Paginator->sort('created', null, array('direction' => 'desc'));?></th>

Using modified model data in the form again

Some of you might have had the wish of posted data that was modified in the model due to beforeValidate/beforeSave to appear modified in the view again (so
that the reason for validation errors might be more clear etc).
So let’s say you have a beforeValidate callback to clean the input of a textarea:

public function beforeValidate($options = array() {
	if (isset($this->data[$this->alias]['comment']) {
		$this->data[$this->alias]['comment'] = $this->_clean($this->data[$this->alias]['comment']);
	}
	return true;
}

So in this case it could easily be that _clean() removes some invalid content and thus the minLength rule is suddenly triggered.
Which is weird, since we posted at least twice the length of text.
To clarify to the user what is going on, one could adjust the error message – but one could additionally return the modified (ready to save) data instead of the
actually posted data.

public function add() {
	if ($this->request->is('post')) {
		$this->{$this->modelClass}->create();
		if ($this->{$this->modelClass}->save($this->request->data)) {
			$this->Session->setFlash(...);
			return $this->redirect(array('action' => 'index'));
		} else {
			// Here we assign the modified model data back to the request
			// object and therefore to the view/form
			$this->request->data = $this->{$this->modelClass}->data;
			$this->Session->setFlash(...);
		}
	} else {
		// Default values for the form
		$this->request->data[$this->modelClass]['status'] = true;
	}
}

The input field will now contain the content that was served to beforeValidate(). And combined with a good error message this will probably clear things up.

PS: You can also directly use the model’s name instead of {$this->modelClass}, e.g. ‘Comment’.

MySQL – MyISAM vs InnoDB

InnoDB is a little bit more robust as it allows transactions. Especially with CakePHP and "multiple queries" most of the time (per default) this can be quite helpful in keeping the DB in a valid state.
Also read myisam-versus-innodb on pros/cons for each.

One additional problem with InnoDB, though: Per default it creates a file that always increases in size, never decreases again. This can soon be a nightmare with backuping when it becomes >> xx GB of size.
See how-to-shrink-purge-ibdata1-file-in-mysql how to avoid that by not using that file, and instead using innodb_file_per_table.

Testing

Testing Controllers

I stumbled upon a few issues with testing controllers – especially plugin controllers.
For plugin controllers to be testable unfortunately you always need to use $uses, even if it wasn’t necessary due to conventions.
So for your Tools.TinyUrls Controller you would need

public $uses = array('Tools.TinyUrl');

Otherwise it would always try to look for the model/fixture in your app dir, which eventually always fails.

Do not forget to use --stderr when testing in CLI. Otherwise all tests that use the session (and which work fine in webtest runner) will fail:

cake test app AllController --stderr

Test Coverage

If you want to generate a HTML overview of all your locale test coverage:

cake test app AllApp --stderr --log-junit tmp/coverage/unitreport.xml --coverage-html tmp/coverage --coverage-clover tmp/coverage/coverage.xml

The report index.html will be in your /tmp/coverage folder.

More on testing – especially controller testing – can be found on the corresponding Tools plugin Wiki page.

Upgrade (deprecated) code

It is always good practice to upgrade to the current master. Not only for CakePHP, but there especially. It will save you a lot of time in the long run, as migration will be easier and faster in small steps instead of one huge step. It will also make it easier to use the new features and more importantly will also come with a lot of fixes and method corrections that the older versions didn’t get anymore. Those outdated versions usually only get security-bugs fixed. So if you look hours for an error that is already fixed in the current master, it was just a huge waste of time. I have seen that a thousands times – on IRC and elsewhere.

So in case you are not using the current master (2.5), do it now. Internally, upgrading 2.x is a "piece of cake".
Upgrading from 1.x is also not that big of a deal – just needs a little bit more manual adjustments. For most things you can use the core UpgradeShell as well as my Upgrade plugin.

In case you are already upgraded to 2.5, you can and should also remove deprecated functionality in favor of the right one.
Those deprecated things will only add to the file of upgrades for the next 2.x release or 3.x. And using the upgrade shell it is usually just one single command to execute.
So for 2.5, you should have removed all the "easy" stuff that will clearly be switched with a different way of doing things as it is mentioned in the migration guide, e.g.

  • loggedIn() in favor of Auth::user(‘id’)
  • CakeRequest::onlyAllow() in favor of CakeRequest::allowMethod()
  • Use first argument (string|array) instead of var args for CakeRequest::allowMethod(), AuthComponent::allow(), etc
  • $title_for_layout in favor of $this->fetch(‘title’) and all other deprecated view vars

From 2.4 and below there are also a few left overs that could easily be corrected:

  • FULL_BASE_URL, DEFAULT_LANGUAGE, IMAGES_URL, JS_URL, CSS_URL to Config variables
  • Remove CAKEPHP_SHELL, IMAGES, JS, CSS usage
  • Simplify HtmlHelper::css()
  • Remove Sanitize class usage in favor of a more sane approach
  • Simplify CakeRequest and PaginatorHelper usage with param() if applicable
  • Don’t use request shortcuts anymore – like $this->action in favor of $this->request->action etc
  • Get rid of Model::read() calls – in 3.x this will be get() – I use my Tools plugin MyModel::get() method here already for years
  • Use the new Model::save() syntax for options
  • Completey get rid of named params in favor query strings (Use my Tools plugin CommonComponent and Configure value App.warnAboutNamedParams to detect left-overs)
  • Replace all Set calls with Hash calls, as Set is deprecated. Make sure it is tested thoroughly (as the functionality of Hash might be slightly different).
  • Prevent using Validation::range() in favor of a custom validation rule for inclusive ranges to easier migrate to 3.x – or simply use my Tools plugin MyModel::rangeInclusive() method.
  • Further deprecations in favor of the 2.5+ way to do things

and so on.
For some details see Tips-Upgrading-to-CakePHP-2.x.

My Tools plugin also contains a few further tweaks that can help ease migration from 2.x to 3.x See the full list on the corresponding Wiki page.

This will help making sure any future upgrade is less painful.
Think about it: When you do that now the remaining TODO list will only be half the size and look a lot less intimidating. When the time comes to upgrade to 3.x it will look quite more doable.

My codez is now officially all 2.5 – and as 3.0 ready as it can get 😛

And what always helps a lot is to code clean and structured. A code mess will always be difficult to maintain. So use coding standards and enforce them. Use best practice approaches. Refactor once in a while to prevent a mess from happening.
Happy coding 🙂

 
1 Comment

Posted in CakePHP

 

Continuous Integration with Jenkins

04 Mar

CI with Jenkins and GitHub is especially interesting for private repositories, as CI with Travis is mainly for free GitHub repos (unless you have Travis Pro, of course).
But since Jenkins is OpenSource and free, it might make sense to set up this on your server and run CI directly in-house.
It integrates nicely with GitHub. So the following tutorial will focus on that example.

So for the beginning we have our example app in GitHub, e.g. a CakePHP app (optionally including composer dependencies).

The main goals are:

  • Continuous test results on each push (especially to master).
  • Automatic test results for each PR (very important when working in teams) – linked inside the PR just as Travis would.
  • Coverage being analysed to see where the weak points can be (classes with < 50% test case coverage).

Set Up Jenkins

This is probably the most difficult part of all.

We first set up Jenkins.

wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | apt-key add -
nano /etc/apt/sources.list
------------------------------
deb http://pkg.jenkins-ci.org/debian binary/
------------------------------
apt-get update
apt-get install jenkins

Check if Jenkins runs properly

http://hostname.domain.local:8080

Install plugins and all their dependencies

Let’s then include the plugins we need to integrate GitHub: "GitHub Pull Request Builder" and "GitHub plugin".
The first is especially interesting as it makes it possible to have Jenkins "build/pass" statuses directly in each PR (similar to Travis).

Access Data -> Global Access Data -> Add Access Data

Create access data for the repository you want to access. Provide username and password or keyfiles.

Manage Jenkins -> Configure System

  • Add your repositories under "Git"
  • Set "Manually manage hook URLs" under "GitHub Web Hook"
  • Generate token (https://github.com/settings/applications) with Github user which has access to the
    private repository and put the token under "GitHub pull requests builder"

Add Job

  1. Select "Free Style" project and provide a jenkins working space name
  2. Put your repository url (e.g. https://github.com/Name/repo) to "GitHub-Project" input field
  3. Select Git under "Source-Code-Management"
    Put your repository url again and select the access data from step 5 of this tutorial.
    Click on advanced settings and set "+refs/pull/:refs/remotes/origin/pr/" for Refspec and "origin" for name.
    Set also the branch specifier to "${sha1}"
  4. Select "Build when a change is pushed to GitHub" and "GitHub pull requests builder" under "Build trigger"
    Add persons or organizations to "GitHub pull requests builder" which are whitelisted for trigger an PR build request
  5. Add shell execution script for your test cases under build proceed (see below)
  6. Add "Set build status on GitHub commit" as post build action.

My shell execution script for the CakePHP apps looks like this:

cd /var/lib/jenkins/jobs/projectname/workspace/app
cp /var/www/projectname/app/Config/database.php Config/
mkdir -p tmp && chmod -R 777 tmp && rm -R tmp/*
cp /var/www/projectname/app/composer.phar ./ && php composer.phar update
Console/cake test app AllApp --stderr

It first navigates into the freshly pulled repo code and into the APP dir.
Then it copies over the database.php from the current staging website (could also be some other place where you store them).
It then creates tmp dirs and sets the correct permissions.
The composer.phar file is also copied over and executed to pull all composer dependencies.
At finally the AllApp (group) tests are executed – with –stderr in order for the session to work properly.
One can then also apply -q for silent (=none) output.

Setup GitHub hooks

To trigger the build process on Jenkins, we need to configure service hook on GitHub. In order to achieve this, navigate to GitHub repository settings and configure Jenkins Hook URL for GitHub plugin. The URL format http://<jenkins-username>:<jenkins-password>@<Elastic-IP-Address>:8080/github-webhook/

Running tests

Run first test manually

Click on "Build with Parameters" and hit "Build" and you will see the first manually triggered build in progress.
If all goes well the icon should be blue (pass) – not red (fail).
In case you need to see what is going on, check the "Console Output" of a specific build.

Check automated tests

Commit to master and see if a build is triggered. Also open a PR and check if there is a badge displayed with the current build status.

Sugar

Badge

To have a small "build status: passing" badge in our Readme, we can install the plugin "Embeddable Build Status".
It will create a new menu entry from where you can select your badges including ready-to-use Markdown syntax if needed.

And if it is a private repo you might also want to htaccess the Jenkins web interface or protect it some other way from the public.

Code Coverage

You can install the plugin "Clover PHP Plugin".
Create a folder "coverage" in your workspace.

Add this to the above command:

--log-junit ../coverage/unitreport.xml --coverage-html ../coverage --coverage-clover ../coverage/coverage.xml

And set up a "Publish Clover PHP Coverage Report" Post Build Action with the absolute path to this coverage.xml file in the workspace.
In my case just coverage/coverage.xml as it can be relative to the workspace, as well.
If you also want to publish the HTML coverage report (which is pretty neat), you can simply put coverage in there then.

Make sure you got Xdebug up and running (apt-get install php5-xdebug). It is a direct dependency here.
Note that using code coverage reports will slow the whole process down by "x3" at least. Usually that shouldn’t really make a difference, though.

In most cases you want to exclude few dirs. In case you are already using a phpunit.xml file which resides in your APP dir, you can easily add

<phpunit>
	<filter>
		<blacklist>
			<directory suffix=".php">../lib</directory>
			<directory suffix=".ctp">../lib</directory>
			<directory suffix=".php">*/Vendor</directory>
			<directory suffix=".php">*/tmp</directory>
			<directory suffix=".php">*/Config/Migration</directory>
			<directory suffix=".php">*/Config/Schema</directory>
			<directory suffix=".php">*/Console/Templates</directory>
			<directory suffix=".php">*/Test</directory>            
			<directory suffix=".php">*/Plugin</directory>
		</blacklist>
	</filter>
</phpunit>

You can also just create this file and also copy it over to the jenkins workspace.

Memory Limit

If you have a project of medium to large size you will soon see the memory usage for the test coverage of 50%+ way above 200MB.
With more coverage and the project getter bigger, or some coverage report generated you will run out of memory with 256MB (which one would think should suffice).
Raise your limit to at least 512 for the PHPUnit testing. You can use the phpunit.xml file:

<phpunit>
	<php>
		<ini name="memory_limit" value="512M" />
	</php>
	...
</phpunit>

Cleanup

Use the plugin "Discard Old Build plugin" to keep your diskspace reasonable. Without it (especially with CodeCoverage) the disk space
will soon exceed several GB. We usually keep the last 40 builds.

More

This article goes more into detail about the installation of Jenkins and also how to use additional tools like CodeSniffer, MessDetector and CO using a build file and Ant. But I didn’t check that out yet.
There is also jenkins-php.org if you want to go all in 🙂

 
No Comments

Posted in Testing

 

CakePHP Tips – Summer 2013

30 Jul

While CakePHP 2.4 is now just around the corner (already beta), a few tips gathered the last few days.

Don’t mix controller and component pagination

Currently, if you mix controller pagination setup and call the Pagination component then, it can easily backfire as the two different
settings are not always merged as you would expect it to. So don’t mix em.

Either use the controller only:

public $paginate = array(...);
// and
$this->paginate = array(...) + $this->paginate;
$results = $this->paginate();

or the component only:

$this->Paginator->settings(array(...));
$results = $this->Paginator->paginate();

My recommendation: Stick to controller pagination for now (as we all have the last years) as it seems it won’t be deprecated after all. It is a nice
convenience access to the component anyway.

PS: If sticking to controller pagination, add this rejected PR snippet to your AppController to avoid the notice thrown.

Test your code

Even if you don’t assert the outcome.

"But that is time consuming. I dont have time!"

Yeah, sometimes you are in a hurry and can’t properly assert all tests. Still no excuse.
Just be running the code via "dummy call" and having the test not through any notices/warnings/errors you already have a very basic level of testing. And it really doesn’t need time to set such a basic test run up.
As soon as there is time you can then still address the proper result. But until then you at least know that no line of code is suddenly gone missing or variables have
accidentally been renamed etc.
Such a basic "run-through" test is still better than no test at all:

/**
 * Test myMethod()
 * 
 * @return void
 */
public function testMyMethod() {
	$this->MyClass->myMethod();
	//TODO: assert return value etc
}

Return your redirects

I always used to bake my templates with simple redirect() calls:

if ($this->Model->save()) {
	$this->redirect(...);
}

But, especially with testing your code, this might have unexpected results, as there you cannot exit, but need to return early (by overwriting the redirect method in your test case file).

The cookbook now states this, as well:

When testing actions that contain redirect() and other code following the redirect it is generally a good idea to return when redirecting. The reason for this, is that redirect() is mocked in testing, and does not exit like normal. And instead of your code exiting, it will continue to run code following the redirect.

So it’s wiser to cleanly return here, as well:

// in your controller action
if ($this->Model->save()) {
	return $this->redirect(...);
}

Even if for your productive environment the return will be superfluous, the test cases will not return wrong results.

I just recently adjusted my bake templates and it has also been changed in the 2.4 branch of the Cake core. I advice you to update your files, as well. You can use my Upgrade shell task "controllers" to do that as a batch job for your app and plugins:

// for your app
cake Upgrade.Upgrade controllers
// for a plugin FooBar
cake Upgrade.Upgrade controllers -p FooBar

Return your shell errors

Same applies here: Testing does not halt the code. Therefore you also need to return early here:

public function some_command() {
	if (!$this->foo()) {
		$this->error('Abort');
	}
	...

becomes

public function some_command() {
	if (!$this->foo()) {
		return $this->error('Abort');
	}
	...

This is also covered by my Upgrade shell now.

Debug SQL queries by throwing exceptions

Ever worked on an ajax, csv or even a redirect action where there will be no debug SQL bar at the bottom etc?
You can easily display your SQL queries up to a specific point by throwing an exception:

$someRecord = $this->Model->find(...);
 // We just add this snippet here to halt and see the actual SQL query before we continue:
throw new Exception();
if (!$someRecord) {
	return $this->redirect(...);
}
return $this->redirect(...);

This halts the code, shows the error layout with all the queries listed below. Neat, isn’t it? And it is quite fast and works anywhere in your application.

Switch from constants to a more flexible way of configuration

In our case we can leverage the Configure class and its read() and write() methods to do so. Better than constants anyway as they can only be defined once and not changed anymore.
So it’s only reasonable that the 2.4 core deprecated quite a few of those constants like FULL_BASE_URL, DEFAULT LANGUAGE, CAKE_SHELL etc.
Using Configure we can store a default value and overwrite it any time after that with a different value if we need/want to.

In the past I also caught myself using constants where Configure would have sufficed. Partly to make myself believe my app will be faster this way.
The speed advantage is so minimal though that it does not outweigh the reduced flexibility that comes with it.

So instead of using define('FULL_BASE_URL', 'http://example.com') you can now use Configure::write('App.fullBaseURL', 'http://example.com'); and overwrite it again later on.

Same goes for any settings you might want to set. Chose a meaningful namespace to group them, though:

Configure::write('Config.someKey', 'somevalue'); // with Config as namespace

You can read it anywhere in your app as always, of course:

$key = Configure::read('Config.someKey'); // in this case probably boolean (1/0 alias true/false)
if ($key) {
	// some code here
}

Change your default validation error message

It will be changed in 3.x from "This field cannot be left empty" to "The provided value is invalid".
This makes sense as in most cases it would display this error message when a value is provided but does not pass the specific validation rule (like numeric or email).
This is highly confusing then.

'email' => array(
	'notEmpty' => array(
		'rule' => array('email')))

This example would just fallback to "This field cannot be left empty" here – both on empty submit and if you input some invalid email.

So you should already address that in 2.x by always defining the error message for your rules with the above wording. Clearer validation error messages will help your users to correct their mistakes and submit the form properly. Only use "This field cannot be left empty" for actual "notEmpty" cases then:

// in your model
public $validate = array(
	'email' => array(
		'notEmpty' => array(
			'rule' => array('email'),
			'message' => 'The provided value is invalid' // or even 'This is not a valid email address'
		)
	),
	'postal_code' => array(
		'notEmpty' => array(
			'rule' => array('notEmpty'),
			'message' => 'This field cannot be left empty',
			'last' => true
		),
		'exists' => array(
			'rule' => array('validatePostalCodeExists'), // custom validation rule
			'message' => 'The provided value is invalid', // or even 'This postal code is not valid'
		)
	)
);

Also try to use the verbose array declaration and don’t forget 'last' => true for consecutive validation rules (as in postal_code above).

Text(area) to paragraphs

Use the new Cake2.4 method TextHelper::autoParagraph() to display your textarea input. It will automatically format single line breaks to <br /> and double ones to <p> blocks.
If you are still on 2.3, you can copy this method over into a TextExt helper, for example, and use aliasing to map it back to "Text" helper alias. When upgrading you won’t have to change your code then a bit 🙂

Important: Do not forget that this method does not escape your output. Thus you need to use h() for (potentially unsafe) user input:

echo $this->Text->autoParagraph(h($text));

h() will effectively remove any XSS dangers as well as defuse any HTML chars like < or > that could otherwise destroy the layout (but are totally valid chars for quoting in some languages).

Read more about output and security in CakePHP here.

Check if you need the hardcoding of your core lib path

By default CakePHP "bake project" for new apps will hardcode the paths in your index.php and test.php – if you don’t have CakePHP in your include_path. Which most don’t, especially when maintaining multiple different Cake versions and apps.
So you might want to (I do anyway) comment those lines out again after baking the project:

define('CAKE_CORE_INCLUDE_PATH', '/some/hardcoded/path');
// to
//define('CAKE_CORE_INCLUDE_PATH', '/some/hardcoded/path');

You will stumple upon that when deploying your scripts. So better take care of it asap.

Check out the 2.4 core

While it is still beta, you can test it already, though. I even use it for new apps (Disclaimer: don’t do that if you are new to CakePHP!).
You can test the new features or how your 2.3 app would run with it. You can realize quite early this way where potential pitfalls are.
In the past I often discovered some "bugs" this way that then could easily be fixed or adjusted before your code actually some day depends on it.
While still in beta you might also be able to add some additions to new features before you will have to wait for 2.5 again or something.
So it’s worth to at least play around with it now.

Also keep an eye on the 2.4 migration guide and maybe even 3.x migration guide as well as 3.x chatter and how your code would be affected or would have to be modified. It might save you some time in the upgrade process if you can already eliminate some of the necessary changes in advance.

A very good example is the removal of the named params in favor of query strings. Don’t start to use those for new apps. Use the future proof query strings even for your 2.x apps.

One example for 3.x, that is not yet officially mentioned yet, is the possible deprecation and removal of Model::read(). It is a method I have always been preaching to avoid. Especially if you are a beginner, but also if you can (and you always do) use find() instead. The cookbook now also states this in a warning.

I never used it once and it seems that there is very high possibility that it will be removed in 3.x. So if you catch yourself using it today, think about this and if you might be able to replace it with the find() wrapper from here on 🙂 To be on the safe side.

 
 

Continuous integration with Travis and CakePHP

03 Apr

I must admit that I only recently started to take testing more serious.
In the past I just created tests if too much time was available.

But the larger projects get, the more just minor changes will most likely break other pieces in them, as well.
Most of the time you don’t even realize it until it is too late, and people report it broken after deployment.
To avoid this, it is wise to have a good test coverage of your project.
Also, it helps to have some software that tests your projects continuously and automatically. This is where Jenkins or Travis come into play.

In the following tutorial I want to focus on how use the free Travis CI to automatically test your github repositories automatically after every commit.
Jenkins you would have to manually set up on your server. Travis is already available with only some minor configuration (for free repos anyway).

Note: For Jenkins there is some documentation in the cookbook.

Travis Setup

You need to get a Travis account first. The easierst way would be to sign in with your github account as this automatically sets everything up. Then you just need to enable the github repositories you want to be tested.

Github Setup

On github you also need to enable the corresponding "Travis hook" for your repositories. It will also need the token you can get from the travis settings page.

Then we need to make our ".travis.yml" configuration file. The documentation is pretty good, but I will still outline the major pitfalls.
A basic version to test your "YourPluginRepository" plugin code for PHP5.3 and PHP5.4 would be sth. like this:

language: php
 
php:
  - 5.3
  - 5.4
before_script:
  - git clone --depth 1 git://github.com/cakephp/cakephp ../cakephp && cd ../cakephp
  - mv ../YourPluginRepository plugins/YourPlugin
  - sh -c "mysql -e 'CREATE DATABASE cakephp_test;'"
  - chmod -R 777 ../cakephp/app/tmp
  - echo "<?php
    class DATABASE_CONFIG {
    public \$test = array(
      'datasource' => 'Database/Mysql',
      'database' => 'cakephp_test',
      'host' => '0.0.0.0',
      'login' => 'travis',
      'persistent' => false,
    );
    }" > ../cakephp/app/Config/database.php
 
script:
  - ./lib/Cake/Console/cake test PluginName AllTests --stderr
  
notifications:
  email: false

Save this file to your repository root.
After the commit and push Travis should be notified via hook and your first test build should already be in the queue there.
You can find it at

https://travis-ci.org/[YOUR_GITHUB_USERNAME]/[YOUR_PROJECT_NAME]/

If you want to take a look at some more complex travis setups, take a look at my tools plugin or the cakephp repository. Both also use different CakePHP versions or database types.

Hooks

If you take a look at the Build-Lifecycle you can see that you have the possibility to install additional software/packages and configure your build to your liking prior to executing the tests.

Status image for your readme or website:

https://travis-ci.org/[YOUR_GITHUB_USERNAME]/[YOUR_PROJECT_NAME].png

Current status for my tools plugin

That was actually the main reason I started to try out Travis. The plugin got larger and more and more apps started to use it. I had to start thinking about how to make it more stable and reliable, especially after modifying parts of it. Regression tests really help here a lot!

Just as an example image:
Build Status

I noticed, however, that this image does not change very often. So if your build does not pass anymore, don’t rely on this image. Better use the notification hooks to get alerted 🙂

Sugar: Coverage via Coveralls

You can include coveralls.io coverage results and also get a detailed report for each class.
It is easiest to use the travis plugin then, as the coveralls setup is quite complicated otherwise.
See my Tools plugin on how to get it working.

You can show a neat little batch in your readme as well:

Note

It seems I wasn’t the only one writing something about this. Independently, Mark Story wrote an alternative article about the the same topic.