Unable to download the code block as the article you requested is currently pending approval for public viewing.

CodeCheck Plugin

By Gordon Pettey (petteyg)
A plugin to check whether your code follows Cake conventions.
It is unlikely that I will keep the code blocks below up to date. Please obtain the plugin from http://github.com/petteyg/code_check.

Following coding conventions makes it easier for everyone to read code. CakePHP has a set of coding standards, available at http://book.cakephp.org/view/509/Coding-Standards.

If you've written lots of code and aren't sure it follows the conventions, it can be hard to go back and read through every file to fix errors. This plugin includes a task to check for errors, and shows what the code should be. Make sure that the correction is correct, and it will rewrite your file for you.

To check your app code, run: cake code convention
I've included the whitespace shell as a task, to check your app: cake code whitespace

plugins/code_check/vendors/shells/code.php

Download code
<?php
class CodeShell extends Shell {

    
/**
    * Shell tasks
    *
    * @var array
    */
    
public $tasks = array(
        
'CodeConvention',
        
'CodeWhitespace'
    
);

    
/**
    * Models used by shell
    *
    * @var array
    */
    
public $uses = array();

    
/**
    * Main execution function
    *
    * @return void
    */
    
public function main()  {
        if (!empty(
$this->args)) {
            if (!empty(
$this->args[1])) {
                
$this->args[1] = constant($this->args[1]);
            } else {
                
$this->args[1] = APP;
            }
            
$this->{'Code'.ucfirst($this->args[0])}->execute($this->args[1]);
        } else {
            
$this->out('Usage: cake code type');
            
$this->out('');
            
$this->out('type should be space-separated');
            
$this->out('list of any combination of:');
            
$this->out('');
            
$this->out('convention');
            
$this->out('whitespace');
        }
    }
}
?>

plugins/code_check/vendors/shells/tasks/code_convention.php

Download code
<?php
class CodeConventionTask extends Shell {

    
/**
    * Models used by shell
    *
    * @var array
    */
    
public $uses = array();

    
/**
    * Main execution function
    *
    * @return void
    */
    
public function execute($root APP)  {
        
$Folder = new Folder($root);
        
$files $Folder->findRecursive('.*\.php');
        
$files array_diff($files, array(__FILE__));
        
$this->out("Checking *.php in ".$root);
        
$grep 'grep -RPnh "%s" %s';
        
$regex = array();

        
$regex['array']['find'] = array('(^\s)=>(^\s)''(^\s)=>''=>(^\s)');
        
$regex['array']['replace'] = array('$1 => $2''$1 =>''=> $1');
        
$regex['control']['find'] = array('if\(''foreach\(''for\(''while\(''switch\(''\)\{');
        
$regex['control']['replace'] = array('if (''foreach (''for (''while (''switch ('') {');
        
$regex['function']['find'] = array('(function [a-zA-Z_\x7f\xff][a-zA-Z0-9_\x7f\xff]+) \(');
        
$regex['function']['replace'] = array('$1(');

        
$types array_keys($regex);

        foreach (
$files as $file) {
            
$contents file_get_contents($file);
            foreach (
$types as $t) {
                for (
$i 0$i count($regex[$t]['find']); $i++) {
                    
$f $regex[$t]['find'][$i];
                    
$grepd exec(sprintf($grep$f$file), $output);
                    if (!empty(
$grepd)) {
                        foreach (
$output as $line) {
                            
$this->out('');
                            
$this->out('');
                            
$this->out($this->shortPath($file));
                            
preg_match('/[0-9]+/'$line$linenumber);
                            
preg_match('/(?<=:)\s+(.*)/'$line$linecode);
                            
$this->out('Line '.str_pad($linenumber[0], 4"0"STR_PAD_LEFT).': '.$linecode[1]);
                            
$r $regex[$t]['replace'][$i];
                            
$replace preg_replace('/'.$f.'/'$r$linecode[1]);
                            
$this->out('Change to: '.$replace);
                            
$fix $this->in('Fix it?', array('y''n'), 'y');
                            if (
$fix) {
                                
$contents preg_replace('/'.$f.'/'$r$contents);
                                
file_put_contents($file$contents);
                            }
                        }
                    }
                }
            }
        }
    }

}
?>

plugins/code_check/vendors/shells/tasks/code_whitespace.php

Download code
<?php
class CodeWhitespaceTask extends Shell {

    
/**
    * Models used by shell
    *
    * @var array
    */
    
public $uses = array();

    
/**
    * Main execution function
    *
    * @return void
    */
    
public function execute($root APP) {
        
$Folder = new Folder($root);
        
$files $Folder->findRecursive('.*\.php');
        
$this->out("Checking *.php in ".$root);
        foreach (
$files as $file) {
                
$contents file_get_contents($file);
                if (
preg_match('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/'$contents)) {
                        
$this->out('!!!contains leading whitespaces: '$this->shortPath($file));
                }
                if (
preg_match('/\?\>[\n\r|\n\r|\n|\r|\s]+$/'$contents)) {
                        
$this->out('!!!contains trailing whitespaces: '$this->shortPath($file));
                }
        }
    }

}
?>

 

Comments 1316

CakePHP Team Comments Author Comments
 

Comment

1 Constructive criticism

Regarding code_convention.php:
grep -T doesn't work on my system, but the intent seems preserved without it. Since I couldn't figure out why it was there, I removed it, and it seemed to work as intended.

In your for loop, you need to reset $output before running exec again, or the results are concatenated. This produces much redundant output.

The Folder class is very slow. Since you were already using exec, I took the liberty of replacing it with exec("find $root -name '*.php'", $files);
The preg_match on line 45 fails when an if starts against the gutter - the fix is to replace the + with a *, and it works as intended in all cases (a potentially dangerous situation given the replacement function).

Some things that I think would be neat to see are:
  • Optional chomping of closing newline in code_whitespace.php
  • Option for automatic replacement for a whole app (running without asking if I'm sure) / or saying yes just once per file.
  • A "virtual mode", where instead of actually writing the file, it dumps a diff of the changes and moves on.

Some nitpicking because I also like to code:
  • That for could just as well have been a foreach, and no need to manage $i yourself that way.
  • I'd prefer the file_put_contents() call be performed only once per file_get_contents() - a dirty flag would serve to track if it should be written at the end.
  • Ignoring the vendors directory, as that code does not need to follow Cake standards (hint, pretty easy with the find call - add "-not -wholename '*/vendors/*'" after the name check)
  • despite find using an odd order, piping the output through sort to process the files in a more logical order
Posted Nov 5, 2009 by Michael Clark
 

Comment

2 Removed -T

I've removed the -T option in latest commit. Will make your suggested changes shortly.
Posted Nov 5, 2009 by Gordon Pettey
 

Comment

3 php tags

A) changing the deprecated stuff as well would be great, too


although this is difficult if you have a newline after it:
echo ...
?>
otherwise it is very simple:
and can both be found easily


B) all the old cake basics
am() => array_merge()
low()
up()
...
Posted Nov 7, 2009 by Mark
 

Comment

4 php tags

A) changing the deprecated stuff as well would be great, too


although this is difficult if you have a newline after it:
echo ...
?>
otherwise it is very simple:
and can both be found easily


B) all the old cake basics
am() => array_merge()
low()
up()
...
Posted Nov 7, 2009 by Mark