PHP_CodeSniffer ruleset.xml support available in SVN
10 FebThe current SVN checkout of PHP_CodeSniffer contains some really big changes to the way custom standards are written and used. The changes make it much easier to create and distribute your own coding standards, giving you the flexibility to pick and choose from the available sniffs and override some of the internal rules the sniffs use, like line lengths and messages.
Creating a custom coding standard
Before ruleset.xml support was added to PHP_CodeSniffer, you needed to write a PHP class for all custom standards. This class had to reside at the top level of your standard's directory structure and extend PHP_CodeSniffer_Standards_CodingStandard. This class was used to both indicate that you have a custom standard and provide a list of sniffs or standards you want to include or exclude.
This file is no longer used. Instead, you must provide a ruleset.xml file (called ruleset.xml) if you wish to distribute a custom standard with your own sniffs. If you do not have any sniffs to distribute and you simply want to use some of the sniffs that are distributed with PHP_CodeSniffer, you only need a single XML file, named whatever you want.
The format is really easy. Here is a very simple ruleset.xml file for a custom standard for PHP_CodeSniffer itself. It uses the whole PEAR standard but also grabs some of the Squiz sniffs. It does this by including the whole Squiz standard and then excluding some specific sniffs from it.
<?xml version="1.0"?>
<ruleset name="PHP_CodeSniffer">
<description>The coding standard for PHP_CodeSniffer.</description>
<!-- Include the whole PEAR standard -->
<rule ref="PEAR"/>
<!-- Include most of the Squiz standard -->
<rule ref="Squiz">
<exclude name="Squiz.Classes.ClassFileName"/>
<exclude name="Squiz.Classes.ValidClassName"/>
<exclude name="Squiz.Commenting.ClassComment"/>
<exclude name="Squiz.Commenting.FileComment"/>
<exclude name="Squiz.Commenting.FunctionComment"/>
<exclude name="Squiz.Commenting.VariableComment"/>
<exclude name="Squiz.ControlStructures.SwitchDeclaration"/>
<exclude name="Squiz.Files.FileExtension"/>
<exclude name="Squiz.NamingConventions.ConstantCase"/>
<exclude name="Squiz.WhiteSpace.ScopeIndent"/>
</rule>
</ruleset>
If we assume this file is called phpcs.xml, to use this standard with PHP_CodeSniffer you simply run the command:
phpcs --standard=/path/to/phpcs.xml /path/to/code
No need for a directory structure or special naming. And obviously this single file is easy to distribute, maintain and document.
Overriding sniffs options
One of the biggest frustrations when building a custom standard, or even just using an existing one, has been the inability to easily override some of those sniff-specific options. For example, when the Line Length sniff should generate a warning; at 80 chars, 85 chars, 90 chars or another length.
Previously, you had to create your own sniff class that extended the main Line Length sniff and overrode the protected member var. Now, you can do it directly from your ruleset.xml file without needing to create any PHP classes.
Suppose you love the PEAR standard, but you hate the fact that it generates a warning when your line is 85 characters long because you would rather have it happen at 90 characters. Now you can create your own ruleset.xml file, mypear.xml:
<?xml version="1.0"?>
<ruleset name="My PEAR">
<description>The PEAR standard, but warnings at 90 chars.</description>
<!-- Include the whole PEAR standard -->
<rule ref="PEAR"/>
<!-- Lines can be no longer than 90 chars -->
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="90"/>
</properties>
</rule>
</ruleset>
Now run PHP_CodeSniffer like this:
phpcs --standard=/path/to/mypear.xml /path/to/code
Changing sniff messages
The new ruleset.xml file also allows you to customise the messages that sniffs produce. For those messages with variables inside them, you use a sprintf format. Just add this to your ruleset.xml file to change the TODO sniff's warning message:
<rule ref="Generic.Commenting.Todo.CommentFound">
<message>Please review this comment: %s</message>
</rule>
Or even use this feature to provide translations:
<rule ref="Generic.Commenting.Todo.CommentFound">
<message>Lütfen bu yorumu gözden geçirin: %s</message>
</rule>
Changing sniff severity levels
Another new feature available in SVN is the ability for PHP_CodeSniffer to filter out errors and warnings based on a severity level. By default, all errors and warnings have a severity of 5 and PHP_CodeSniffer will show them all. This feature needs its own blog post, but it revolves around the ruleset.xml file as well so I wanted to include it briefly here.
Here is an example of making the TODO sniff less severe. You may want to do this so it does not appear when running PHP_CodeSniffer normally, but you can do a full run to get all warnings during a manual code review:
<rule ref="Generic.Commenting.Todo.CommentFound">
<severity>3</severity>
</rule>
A complete example
The following sample file does pretty much everything you can do with this new ruleset.xml file format:
<?xml version="1.0"?>
<ruleset name="Sample">
<description>A sample coding standard</description>
<!-- Include all sniffs in the PEAR standard -->
<rule ref="PEAR"/>
<!-- Include all sniffs in an external standard directory -->
<rule ref="/home/gsherwood/standards/mystandard"/>
<!-- Include everything in another ruleset.xml file -->
<rule ref="/home/gsherwood/standards/custom.xml"/>
<!-- Include all sniffs in the MySource standard except one -->
<rule ref="MySource">
<exclude name="MySource.Channels.DisallowSelfActions"/>
</rule>
<!-- Include some specific sniffs -->
<rule ref="Generic.CodeAnalysis.UnusedFunctionParameter"/>
<rule ref="Generic.Commenting.Todo"/>
<rule ref="Generic.ControlStructures.InlineControlStructure"/>
<!-- Drop the severity of the todo sniff and change the message -->
<rule ref="Generic.Commenting.Todo.CommentFound">
<message>Please review this TODO comment: %s</message>
<severity>3</severity>
</rule>
<!-- Change the settings of the Line Length sniff -->
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="90"/>
<property name="absoluteLineLimit" value="100"/>
</properties>
</rule>
<!-- Change both Line Length messages -->
<rule ref="Generic.Files.LineLength.MaxExceeded">
<message>Line contains %s chars, longer than the max of %s</message>
</rule>
<rule ref="Generic.Files.LineLength.TooLong">
<message>Line longer than %s chars; has %s chars</message>
</rule>
<!-- Disable internal message for missing code and short open tags -->
<rule ref="Internal.NoCodeFound">
<severity>0</severity>
</rule>
</ruleset>
An alpha release of PHP_CodeSniffer 1.3.0 will happen this week or next. So you can grab the code from SVN directly or wait for this alpha release. Developers of existing custom standards should use this long alpha/RC release process to convert their PHP classes to the new ruleset.xml format.
Lost in SWITCH-lation
09 FebCorny title, but a serious issue.
Recently, a simple change made to one of our functions broke a unit test, but it was not obvious how it could have broken when the only thing that was being tested was a PHP SWITCH statement. The code being tested looked something like this:
function test($var)
{
switch ($var) {
case 'abc':
echo 'abc';
break;
case 'def':
echo 'def';
break;
default:
echo 'default';
break;
}
}//end test()
A quick investigation found that when the value int(0) is passed into the SWITCH statement, "abc" is printed instead of "default". We had incorrectly assumed that the SWITCH was doing an identical check rather than just equality.
What actually happens is that PHP casts the CASE conditions to the same type as the input to the SWITCH statement. So in the case above, string(abc) is cast to an integer, becoming int(0), which then matches our input.
A more obvious way of doing the comparison would be to cast the input to the same type as the CASE comparison value. If this was done, int(0) would become string(0) and it would not match string(abc).
To be fair, the SWITCH documentation does say that it does a loose comparison, but it doesn't mention that the CASE values that the developer explicitly coded would be cast based on the type of the input. Internally, PHP probably just takes the two values and passes them to a comparison function that uses the first argument to determine the type. But still, the logic is a bit backwards. But PHP can't be changed now as we are too far down the road.
So how do you get around this?
In our case, we now cast the input to a string before passing it to the SWITCH to be evaluated:
function test($var)How many developers out there cast SWITCH input before evaluating it?
{
$var = (string) $var;
switch ($var) {
...
}
}//end test()
It's not the sort of thing you would normally think about, but it is not enough to use the DEFAULT case as a catch-all for both value and type comparisons. You need to be positive you know the type of a variable before sending it to a SWITCH or you may get unexpected results.
So now we have a decision to make. Do we enforce casting for all SWITCH input in our coding standard (or at least flag it for code review) or do we put this down to a once-off considering this is the first time our team has encountered the issue in all our years of product development?
Progress report: Browsing MySource Matrix within MySource Mini
29 JanWe are currently developing a bridge between MySource Mini and MySource Matrix so that MySource Matrix content can be brought into a MySource Mini system. When this development is complete you will not only be able to browse your MySource Matrix system from MySource Mini, but also:
- Link to Matrix pages from within MySource Mini WYSIWYG content
- Create a new link between a Matrix page and a MySource Mini page, giving the Matrix page a MySource Mini URL
- Edit Matrix pages within the MySource Mini interface via a new Matrix simple edit interface
- Apply MySource Mini metadata, designs, permissions etc. to a Matrix page through the standard MySource Mini interface
To create a connection between MySource Matrix and MySource Mini, you will first need to configure the MySource Matrix SOAP interface. Once you have the URL of the SOAP server, you can use a new Connection wizard within MySource Mini to bridge the two systems.
Along with the standard fields, the required fields for this connection are:
- The URL of the SOAP server you configured within MySource Matrix
- A username and password to use for connecting to the SOAP server
- The ID of an asset within MySource Matrix to use as the root node of the connection
Currently, development has been completed to allow you to browse your MySource Matrix asset tree from within MySource Mini. Work is now starting on the remaining features around linking and editing of MySource Matrix content.
But you want some proof of course.
This image shows a simple MySource Matrix asset tree with a number of different asset types.
Note that the number in brackets is the asset ID and not the number of children.
This image shows the asset tree being browsed from within MySource Mini, using the Site asset as the root node of the connection.
To achieve this, we've made an important addition to the MySource Matrix SOAP API. You are currently able to retrieve all the information you require to build an asset tree, but we've added a new API functionGetAssetsInfo() that allows you to pull out this information in one call to significantly improve performance.
This is an example of how you use the new API function:
<?php
$soap_url = "http://[SYSTEM_ROOT_URL]/_web_services/soap-server?WSDL";
$authen_info = array (
'login' => root,
'password' => root,
);
$client = new SoapClient($soap_url, $authen_info);
$request = array(
'AssetIDs' => [Root AssetID],
'FinderAttributes' => array(
'Children',
'type',
'name'
)
);
$assetsInfoResult = $client->GetAssetsInfo($request);
?>
This is what the returned result looks like:
Array (
[0] => Array (
'AssetID' => '90',
'AssetType' => 'backend_user',
'AssetTypeAncestor' => 'user',
'AssetName' => 'Dan Green',
'AssetChildren' =>
array (
0 => '91',
1 => '92',
),
[1] ......
)
Please note that this new SOAP API function is not yet available in a stable release of MySource Matrix, but will be in a future 3.26 release.
PHP_CodeSniffer 1.2.2 released
27 JanPHP_CodeSniffer version 1.2.2 has just been uploaded to PEAR and is now available to install. Most of the changes are behind-the-scenes, but some of the highlights are:
- An interactive reporting mode to check large code bases and make changes as you go
- Closure support added into the core so sniffs can now check rules for PHP closures
- A completely rewritten reporting system to make everything much easier to extend
- Sniff violation codes that will be used to allow overriding of error messages in the future
All new features have been fully documented and are available on the PHP_CodeSniffer documentation page.
You can view the full changelog on the PHP_CodeSniffer download page.
MySource Mini patch 9049 goes RC for personal users
22 JanWe pushed MySource Mini patch 9049 from beta to RC late yesterday, so personal sites should have received the update overnight.
The patch is later than expected as we found a few issues during our beta testing phase (thanks beta testers!). We also held it back to fix up a couple of issues found during the change of year from 2009 to 2010 with date formats, and the Christmas / New Year break didn't help the timeline either. But it is available now and highly recommended.
We've also updated our MySource Mini download page so you can grab a free VM of the new version to play around with.
In addition to the normal features and bug fixes, this patch brings a completely minimised JavaScript interface to MySource Mini, greatly improving interface performance. We've also improved the caching of the editing interface images to cut down the number of requests that MySource Mini makes while editing. This should really help remote content authors, especially those working internationally.
For a full list of changes, view the patch notes for patch 9049.
Updating multiple metadata values in a single JavaScript API call
19 JanThe MySource Matrix JavaScript API now has a new function, setMetadataAllFields(), which accepts an array of metadata values and sets them on a single asset in one call. This function will be made available in version 3.26.1, due out on the 8th of Feb 2010.
/**The JavaScript API could always be used to set a single metadata value for an asset, but we found ourselves sending many requests to the API to set up to 15 different metadata values from our custom frontend interfaces. The overhead of creating connections for 15 requests, and the overhead of performing the same action 15 times within MySource Matrix, left us searching for a new solution.
* Set metadata values of multiple fields for an asset
*
* @param string asset_id Id of asset to set metadata for
* @param array field_info Field Ids and their values
* @param function dataCallback Custom callback function
*
* @version $Revision: 0.1
*/
function setMetadataAllFields(asset_id, field_info, dataCallback)
With this new function call, all metadata fields and their corresponding values are provided in one go, so that there is only a single connection no matter how many metadata values you have to set. Internally, MySource Matrix only needs to acquire and release metadata locks once, reducing the number of DB queries being executed. An even bigger saving is made by not having to regenerate the metadata content file each time a metadata value is set, saving DB queries, disk I/O and PHP processing time.
If you've got a few metadata values to set on an asset, all these savings will add up to a significant performance improvement. Even just setting 5 metadata values could take almost 10 seconds (depending on a lot of factors) while this new function call can return in less than 2.
Like the call that is made to update a single metadata field, this function will return an error if it fails to update any of the fields.
Updating your code to use this new function is pretty easy. The following two code blocks show the old and new way of setting multiple metadata values for an asset.
The first shows the existing way you would be setting 3 metadata values for an asset:
<script type="text/javascript">
window.api_key = 'XXXXXXXXXXXX';
// Asset ID of the asset we are updating
var assetid = "123";
// Update the metadata of the asset
// Metadata field 1000 = Title
// 1001 = Description
// 1002 = Keywords
setMetadata(assetid, "1000", "Test page");
setMetadata(assetid, "1001", "Just a simple test page");
setMetadata(assetid, "1002", "test, page");
</script>
And this is the new way of setting these values:
<script type="text/javascript">
window.api_key = 'XXXXXXXXXXXX';
// Asset ID of the asset we are updating
var assetid = "123";
var field_info = new Array();
// Update the metadata of the asset
// Metadata field 1000 = Title
// 1001 = Description
// 1002 = Keywords
var field_info = new Array();
field_info["1000"] = "Test page";
field_info["1001"] = "Just a simple test page";
field_info["1002"] = "test page";
setMetadataAllFields(assetid, field_info);
</script>
Rest assured that the setMetadata() function will continue to work as normal, so upgrading will not cause your API calls to break. But you may want to consider rewriting any parts of your code that set multiple metadata values to get a performance improvement.
Introducing workflow streams
18 JanIf you've been checking out version 3.26.0 of MySource Matrix [download] [changelog], which was released last week, you may have noticed a subtle change when you tried to create new Workflow Schema assets.
Starting from version 3.26, system administrators can create workflow schemas that contain multiple approval paths, known as Workflow Streams. Schemas will always contain a default approval stream but can then have one or more optional "alternate" streams. This allows you to create approval paths more suited to certain scenarios. For instance, you can create an "urgent" workflow stream to bypass a usual multiple-step approval path and request approval directly from a manager.

You may have noticed a similar feature is provided as part of the workflow system of our next-generation CMS product, MySource Mini.
Consistent alternative approval paths
These alternate streams can be linked under more than one workflow schema and can be moved or deleted freely. The ability to re-use alternate streams allows you to provide a consistent approval path for these scenarios, in line with business or other policies. Note that the default streams are tied to their schemas and cannot be shared or moved in this way.
Alternatively, if different workflows require different "urgent" approval paths (for instance), then alternate streams will be grouped by name. Where an asset has multiple workflow schemas applied, any selected stream name will apply for all workflows. Any workflow that does not use that stream name will use its default stream instead.
Which users can use alternate workflow streams?
The ability to use alternate streams will be restricted to those with effective Administrator permissions on the asset. For them, a second drop-down will appear next to the Status Change box (on the Details screen) when alternate streams exist, allowing a stream to be chosen when starting workflow. Other users will always use the default workflow stream and will see no changes to their interface.

Migration of workflow schemas
Migration of existing Workflow Schemas from earlier versions of MySource Matrix to the new workflow stream system in version 3.26.0 is painless as the upgrade will occur during the installation of the Workflow Stream asset. A "default" stream will be automatically created for the existing schemas in your system, and the existing approval steps will be moved underneath it.
In summary...
- Workflow streams provide the ability to specify alternative approval paths for various scenarios (such as urgent approvals).
- They can be re-used across multiple workflow schemas to create a consistent approval path for non-standard workflows across your site.
- They can only be activated by asset administrators. All other users can only use the default workflow.
- Workflow streams were released in MySource Matrix 3.26.0 on the 15th January 2010.
How usable is MySource Mini?
15 JanAccording to the book Usability Engineering, by Jakob Nielsen, usability is made up of five attributes:
- Learnability: how easy is it to learn how to use the system
- Efficiency: once you learn how to use the system, it should be quick and easy to get things done
- Memorability: for a user who doesn't use the system to often, they are able to remember how to use the system
- Errors: the number of errors that a user makes to complete a task should be low and there are no catastrophic bugs
- Satisfaction: users like using the system.
Based on these five attributes, we have asked ourselves a not so simple question:
How usable is MySource Mini?
We conduct regular usability sessions with users who are not familiar with our product, so we have a bit of past data that we can use to try and answer this question.
Is MySource Mini easy to learn?
At the start of each usability session, participants are given a very brief introduction to MySource Mini. After this they are asked to complete a minimum of 6 everyday scenarios. All participants were able to complete these scenarios with little or no help.
Once they had completed the testing, participants were asked to rate the statement "MySource Mini is difficult to learn". Nobody agreed with this statement. In fact, 50% disagreed with the statement and 50% strongly disagreed.
Based on this information, we could say that yes, MySource Mini is easy to learn.
Are users efficient when using MySource Mini?
Participants were initially hesitant and unsure of what they were doing within the system. Some users took some time to complete the first steps of the first scenario while they were familiarising themselves with the system. Once they started learning how to use the system, however, their pace increased and they completed all scenarios within 30 minutes.
Based on this, we could say that yes, users are efficient when using MySource Mini once they learn how to use the system. And we have already established that users find the system easy to learn, meaning they become efficient quickly.
Are users able to remember how to use MySource Mini?
At this stage, the memorability of MySource Mini has not really been tested. In the book, Jakob Nielsen suggests to conduct a memory test once the testing has been completed to see if participants remember how to use the system. This type of test will be added for the next round of testing - so watch this space.
Do users make errors when using MySource Mini and do catastrophic errors occur?
Considering that this was the first time that the participants were using MySource Mini, errors were to be expected. All participants made at least one mistake during each scenario. However, no participant made the same error twice and all were able to recover and go on to complete the scenario with little or no help. No catastrophic errors occurred.
From our point of view, we consider the error rate to be low. If we see that an error is re-occurring between participants, we endeavour to try and make that part of the system more usable.
Do users like the system?
From the feedback that has been received from the participants, users like MySource Mini and are very impressed by its features. Some existing MySource Matrix users are even looking forward to the day it has enough features to allow them to upgrade to MySource Mini.
So how usable is MySource Mini?
For the parts of the system that have been tested, MySource Mini has a high level of usability. Users are able to complete tasks efficiently, with very little training and only a small number of errors. At the same time, they also feel satisfied when using the system.
PHP_CodeSniffer interactive mode
14 JanPHP_CodeSniffer is traditionally run over a set of files and a report printed after the run. A developer then uses the report to fix the errors and rechecks the files manually. This can be quite time consuming for large code bases because the error list may be long or may change as other errors are fixed. To make this process easier, a new interactive mode has just been committed to the PHP_CodeSniffer SVN repository.
When PHP_CodeSniffer is asked to run interactively, it will stop checking files as soon as it finds one with errors. It will then print the error report for this file and wait for user input. The idea here is that you go away, fix the errors in the file (focusing on one file at a time) and then ask PHP_CodeSniffer to recheck the file. If the errors are now gone, PHP_CodeSniffer will continue checking the code base from where it left off. If more errors are found, the new error report is shown for the file. You also have to option of skipping the file if you would prefer to fix the errors later.
The following example shows some sample output from interactive mode. This mode is enabled using the -a command line argument.
$ phpcs -a temp.php
FILE: /Users/gsherwood/PHP_CodeSniffer/trunk/temp.php
--------------------------------------------------------------------------
FOUND 2 ERROR(S) AND 1 WARNING(S) AFFECTING 1 LINE(S)
--------------------------------------------------------------------------
2 | WARNING | Inline control structures are discouraged
2 | ERROR | Missing file doc comment
2 | ERROR | TRUE, FALSE and NULL must be lowercase; expected "true" but
| | found "TRUE"
--------------------------------------------------------------------------
<ENTER> to recheck, [s] to skip or [q] to quit :
Interactive mode also works with all other PHP_CodeSniffer command line options. So you can set the report format and standard to use, if you want to hide warnings, if you'd like to display verbose output, which files to ignore, etc.
So grab a copy of PHP_CodeSniffer from the SVN repository and give it a go. Just add -a to your normal PHP_CodeSniffer command. If you prefer to wait until the release, this feature will be in version 1.2.3 and released as stable in a few weeks time.