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