webmaster@1: #!/usr/bin/perl -w webmaster@1: # $Id: code-style.pl,v 1.14 2007/02/15 11:40:19 dries Exp $ webmaster@1: webmaster@1: use Pod::Usage; webmaster@1: use Getopt::Long qw(GetOptions); webmaster@1: Getopt::Long::Configure ("bundling"); webmaster@1: webmaster@1: my %opt = ( "help" => 0, webmaster@1: 'debug' => 0, webmaster@1: ); webmaster@1: webmaster@1: if(!GetOptions(\%opt, webmaster@1: 'help|?', webmaster@1: 'debug', webmaster@1: )) { webmaster@1: pod2usage(-exitval => 1, 'verbose'=>0); webmaster@1: } webmaster@1: webmaster@1: pod2usage(-exitval => 0, -verbose => 2) if($opt{'help'}); webmaster@1: webmaster@1: $debug = $opt{'debug'}; webmaster@1: webmaster@1: $comment = 0; #flag used to signal we're inside /* */ webmaster@1: $program = 0; #flag used to signal we're inside webmaster@1: #read the file webmaster@1: while (<>) { webmaster@1: $org=$_; webmaster@1: s/\\["']//g; webmaster@1: # please don't use nested comments for now... thanks! webmaster@1: # handles comments // style, but don't mess with http:// webmaster@1: s/\/\/[^:].*//; webmaster@1: # handles comments /**/ on a single line webmaster@1: s/\/\*.*\*\///g; webmaster@1: # handles comments /**/ over several lines webmaster@1: if ($comment == 1) { webmaster@1: if (s/.*\*\///) { webmaster@1: $comment = 0; webmaster@1: } webmaster@1: else { webmaster@1: next; webmaster@1: } webmaster@1: } webmaster@1: if (s/\/\*.*//) { webmaster@1: $comment = 1; webmaster@1: } webmaster@1: if (/^\s*#/) { webmaster@1: next; webmaster@1: } webmaster@1: webmaster@1: if (s/<\?php//) { webmaster@1: $program = 1; webmaster@1: } webmaster@1: if (/\?>/) { webmaster@1: $program = 0; webmaster@1: } webmaster@1: webmaster@1: # enforce "bar". foo() ."bar" syntax webmaster@1: if (/^("[^"]*"|[^"])*("[^"]*")\.[^ ]/ && $program) { webmaster@1: $msg = "'\".' -> '\". '"; webmaster@1: } webmaster@1: elsif (/^("[^"]*"|[^"])*("[^"]*")\s+\./ && $program) { webmaster@1: $msg = "'\" .' -> '\".'"; webmaster@1: } webmaster@1: # enforce "bar". foo() ."bar" syntax webmaster@1: elsif (/^("[^"]*"|[^"])*[^ "]\.("[^"]*")/ && $program) { webmaster@1: $msg = "'.\"' -> '.\"'"; webmaster@1: } webmaster@1: elsif (/^("[^"]*"|[^"])*[^ "]\.\s+("[^"]*")/ && $program) { webmaster@1: $msg = "'. \"' -> '.\"'"; webmaster@1: } webmaster@1: # XHTML requires closing tag webmaster@1: elsif (/
/i) { webmaster@1: $msg = "'
' -> '
'"; webmaster@1: } webmaster@1: elsif (/\$REQUEST_URI/i) { webmaster@1: $msg = "the use of REQUEST_URI is prone to XSS exploits and does not work on IIS; use request_uri() instead"; webmaster@1: } webmaster@1: elsif (/\"REQUEST_URI\"/i) { webmaster@1: $msg = "the use of REQUEST_URI is prone to XSS exploits and does not work on IIS; use request_uri() instead"; webmaster@1: } webmaster@1: webmaster@1: # XHTML compatibility mode suggests a blank before / webmaster@1: # i.e.
webmaster@1: elsif (/<[a-z][^>]*[^ >]\/>/i) { webmaster@1: $msg = "'' -> ''"; webmaster@1: } webmaster@1: # we write '{' on the same line, not on the next webmaster@1: elsif (/^\s*{/ && $program) { webmaster@1: $msg = "take '{' to previous line"; webmaster@1: } webmaster@1: elsif (/([a-z])([A-Z])/) { webmaster@1: $msg = "no mixed case function or variable names, use lower case and _"; webmaster@1: } webmaster@1: elsif (/<[\/]*[A-Z]+[^>]*>/) { webmaster@1: $msg = "XHTML demands tags to be lowercase"; webmaster@1: } webmaster@1: webmaster@1: # trying to recognize splitted lines webmaster@1: # there are only a few valid last characters in programming mode, webmaster@1: # only sometimes it is ( if you use if/else with a single statement webmaster@1: webmaster@1: # from here on we need no more strings webmaster@1: while (s/^([^"]*)"[^"]*"/$1#/) {}; webmaster@1: while (s/^([^']*)'[^']*'/$1#/) {}; webmaster@1: webmaster@1: # it should be 'if (' all the time webmaster@1: if (/(^|[^a-zA-Z])(if|else|elseif|while|foreach|switch|return|for)\(/) { webmaster@1: $msg = "'(' -> ' ('"; webmaster@1: } webmaster@1: #elsif (/[^;{}:\s\n]\s*\n*$/ && $program && !/^[\s}]*(if|else)/) { webmaster@1: # $msg = "don't split lines"; webmaster@1: #} webmaster@1: elsif (/\}\s*else/) { webmaster@1: $msg = "'} else' -> '}\\nelse'"; webmaster@1: } webmaster@1: elsif (/[^{\s\n]\s*\n*$/ && $program && /^\s*(if|else)/) { webmaster@1: $msg = "every if/else needs a { at eol"; webmaster@1: } webmaster@1: elsif (/([\(\[]) / && $program) { webmaster@1: $msg = "'$1 ' -> '$1'"; webmaster@1: } webmaster@1: elsif (/\S ([\)\]])/ && $program) { webmaster@1: $msg = "' $1' -> '$1'"; webmaster@1: } webmaster@1: # but no brackets webmaster@1: elsif (/([a-z-A-Z_][a-zA-Z0-9_-]*)\s+\(/ && $program) { webmaster@1: if ($1 ne "switch" and $1 ne "if" and $1 ne "while" and $1 ne "foreach" and $1 ne "return" and $1 ne "for" and $1 ne "elseif") { webmaster@1: $msg = "'$1 (' -> '$1('"; webmaster@1: } webmaster@1: } webmaster@1: # there should be a space before '{' webmaster@1: if (/[^ ]{/ && $program) { webmaster@1: $msg = "missing space before '{'"; webmaster@1: } webmaster@1: # there should be a space after ',' webmaster@1: elsif (/[,][^ \n\r]/ && $program) { webmaster@1: $msg = "missing space after ','"; webmaster@1: } webmaster@1: # spaces before and after, only foreach may use $foo=>bar webmaster@1: elsif (/[^ =|\-|\+](\+|\-)[^ =>|\-|\+]/ && $program && !/foreach/) { webmaster@1: $msg = "'$1' -> ' $1 '"; webmaster@1: } webmaster@1: elsif (/[^ =](\*|==|\.=|=>|=|\|\|)[^ =>]/ && $program && !/foreach/) { webmaster@1: $msg = "'$1' -> ' $1 '"; webmaster@1: } webmaster@1: # ensure $bar["foo"] and $bar[$foo] and $bar[0] webmaster@1: elsif (/\[[^#][^\]]*\]/ && !/\[[0-9\$][^\]]*\]/ && !/\[\]/) { webmaster@1: $msg = "only [\"foo\"], [\$foo] or [0] is allowed"; webmaster@1: } webmaster@1: # first try to find missing quotes after = in (X)HTML tags webmaster@1: elsif (/<[^>]*=[a-zA-Z0-9][^>]*>/) { webmaster@1: $msg = "=... -> =\"...\""; webmaster@1: } webmaster@1: if (defined $msg) { webmaster@1: if ($debug==0) { webmaster@1: print $ARGV .":". $. .": $msg : ". $org; webmaster@1: } webmaster@1: undef $msg; webmaster@1: } webmaster@1: elsif ($debug==1) { webmaster@1: print $org; webmaster@1: } webmaster@1: } continue { webmaster@1: close ARGV if eof; webmaster@1: } webmaster@1: webmaster@1: __END__ webmaster@1: webmaster@1: =head1 NAME webmaster@1: webmaster@1: code-style.pl - Review drupal code for style webmaster@1: webmaster@1: =head1 SYNOPSIS webmaster@1: webmaster@1: code-style.pl [options] webmaster@1: webmaster@1: Options: webmaster@1: webmaster@1: -? --help detailed help message webmaster@1: webmaster@1: =head1 DESCRIPTION webmaster@1: webmaster@1: Originally written for Drupal (http://drupal.org/) to ensure stylish webmaster@1: code. This program reviews PHP code, and tries to show as many code webmaster@1: improvements as possible with no false positives. webmaster@1: webmaster@1: =head1 OPTIONS webmaster@1: webmaster@1: --comment webmaster@1: webmaster@1: =head1 EXAMPLES webmaster@1: webmaster@1: ./code-style.pl ../index.php webmaster@1: webmaster@1: =cut