|
1 #!/usr/bin/perl -w |
|
2 # $Id: code-style.pl,v 1.14 2007/02/15 11:40:19 dries Exp $ |
|
3 |
|
4 use Pod::Usage; |
|
5 use Getopt::Long qw(GetOptions); |
|
6 Getopt::Long::Configure ("bundling"); |
|
7 |
|
8 my %opt = ( "help" => 0, |
|
9 'debug' => 0, |
|
10 ); |
|
11 |
|
12 if(!GetOptions(\%opt, |
|
13 'help|?', |
|
14 'debug', |
|
15 )) { |
|
16 pod2usage(-exitval => 1, 'verbose'=>0); |
|
17 } |
|
18 |
|
19 pod2usage(-exitval => 0, -verbose => 2) if($opt{'help'}); |
|
20 |
|
21 $debug = $opt{'debug'}; |
|
22 |
|
23 $comment = 0; #flag used to signal we're inside /* */ |
|
24 $program = 0; #flag used to signal we're inside <?php ?> |
|
25 #read the file |
|
26 while (<>) { |
|
27 $org=$_; |
|
28 s/\\["']//g; |
|
29 # please don't use nested comments for now... thanks! |
|
30 # handles comments // style, but don't mess with http:// |
|
31 s/\/\/[^:].*//; |
|
32 # handles comments /**/ on a single line |
|
33 s/\/\*.*\*\///g; |
|
34 # handles comments /**/ over several lines |
|
35 if ($comment == 1) { |
|
36 if (s/.*\*\///) { |
|
37 $comment = 0; |
|
38 } |
|
39 else { |
|
40 next; |
|
41 } |
|
42 } |
|
43 if (s/\/\*.*//) { |
|
44 $comment = 1; |
|
45 } |
|
46 if (/^\s*#/) { |
|
47 next; |
|
48 } |
|
49 |
|
50 if (s/<\?php//) { |
|
51 $program = 1; |
|
52 } |
|
53 if (/\?>/) { |
|
54 $program = 0; |
|
55 } |
|
56 |
|
57 # enforce "bar". foo() ."bar" syntax |
|
58 if (/^("[^"]*"|[^"])*("[^"]*")\.[^ ]/ && $program) { |
|
59 $msg = "'\".' -> '\". '"; |
|
60 } |
|
61 elsif (/^("[^"]*"|[^"])*("[^"]*")\s+\./ && $program) { |
|
62 $msg = "'\" .' -> '\".'"; |
|
63 } |
|
64 # enforce "bar". foo() ."bar" syntax |
|
65 elsif (/^("[^"]*"|[^"])*[^ "]\.("[^"]*")/ && $program) { |
|
66 $msg = "'.\"' -> '.\"'"; |
|
67 } |
|
68 elsif (/^("[^"]*"|[^"])*[^ "]\.\s+("[^"]*")/ && $program) { |
|
69 $msg = "'. \"' -> '.\"'"; |
|
70 } |
|
71 # XHTML requires closing tag |
|
72 elsif (/<br>/i) { |
|
73 $msg = "'<br>' -> '<br />'"; |
|
74 } |
|
75 elsif (/\$REQUEST_URI/i) { |
|
76 $msg = "the use of REQUEST_URI is prone to XSS exploits and does not work on IIS; use request_uri() instead"; |
|
77 } |
|
78 elsif (/\"REQUEST_URI\"/i) { |
|
79 $msg = "the use of REQUEST_URI is prone to XSS exploits and does not work on IIS; use request_uri() instead"; |
|
80 } |
|
81 |
|
82 # XHTML compatibility mode suggests a blank before / |
|
83 # i.e. <br /> |
|
84 elsif (/<[a-z][^>]*[^ >]\/>/i) { |
|
85 $msg = "'<foo/".">' -> '<foo />'"; |
|
86 } |
|
87 # we write '{' on the same line, not on the next |
|
88 elsif (/^\s*{/ && $program) { |
|
89 $msg = "take '{' to previous line"; |
|
90 } |
|
91 elsif (/([a-z])([A-Z])/) { |
|
92 $msg = "no mixed case function or variable names, use lower case and _"; |
|
93 } |
|
94 elsif (/<[\/]*[A-Z]+[^>]*>/) { |
|
95 $msg = "XHTML demands tags to be lowercase"; |
|
96 } |
|
97 |
|
98 # trying to recognize splitted lines |
|
99 # there are only a few valid last characters in programming mode, |
|
100 # only sometimes it is ( if you use if/else with a single statement |
|
101 |
|
102 # from here on we need no more strings |
|
103 while (s/^([^"]*)"[^"]*"/$1#/) {}; |
|
104 while (s/^([^']*)'[^']*'/$1#/) {}; |
|
105 |
|
106 # it should be 'if (' all the time |
|
107 if (/(^|[^a-zA-Z])(if|else|elseif|while|foreach|switch|return|for)\(/) { |
|
108 $msg = "'(' -> ' ('"; |
|
109 } |
|
110 #elsif (/[^;{}:\s\n]\s*\n*$/ && $program && !/^[\s}]*(if|else)/) { |
|
111 # $msg = "don't split lines"; |
|
112 #} |
|
113 elsif (/\}\s*else/) { |
|
114 $msg = "'} else' -> '}\\nelse'"; |
|
115 } |
|
116 elsif (/[^{\s\n]\s*\n*$/ && $program && /^\s*(if|else)/) { |
|
117 $msg = "every if/else needs a { at eol"; |
|
118 } |
|
119 elsif (/([\(\[]) / && $program) { |
|
120 $msg = "'$1 ' -> '$1'"; |
|
121 } |
|
122 elsif (/\S ([\)\]])/ && $program) { |
|
123 $msg = "' $1' -> '$1'"; |
|
124 } |
|
125 # but no brackets |
|
126 elsif (/([a-z-A-Z_][a-zA-Z0-9_-]*)\s+\(/ && $program) { |
|
127 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") { |
|
128 $msg = "'$1 (' -> '$1('"; |
|
129 } |
|
130 } |
|
131 # there should be a space before '{' |
|
132 if (/[^ ]{/ && $program) { |
|
133 $msg = "missing space before '{'"; |
|
134 } |
|
135 # there should be a space after ',' |
|
136 elsif (/[,][^ \n\r]/ && $program) { |
|
137 $msg = "missing space after ','"; |
|
138 } |
|
139 # spaces before and after, only foreach may use $foo=>bar |
|
140 elsif (/[^ =|\-|\+](\+|\-)[^ =>|\-|\+]/ && $program && !/foreach/) { |
|
141 $msg = "'$1' -> ' $1 '"; |
|
142 } |
|
143 elsif (/[^ =](\*|==|\.=|=>|=|\|\|)[^ =>]/ && $program && !/foreach/) { |
|
144 $msg = "'$1' -> ' $1 '"; |
|
145 } |
|
146 # ensure $bar["foo"] and $bar[$foo] and $bar[0] |
|
147 elsif (/\[[^#][^\]]*\]/ && !/\[[0-9\$][^\]]*\]/ && !/\[\]/) { |
|
148 $msg = "only [\"foo\"], [\$foo] or [0] is allowed"; |
|
149 } |
|
150 # first try to find missing quotes after = in (X)HTML tags |
|
151 elsif (/<[^>]*=[a-zA-Z0-9][^>]*>/) { |
|
152 $msg = "=... -> =\"...\""; |
|
153 } |
|
154 if (defined $msg) { |
|
155 if ($debug==0) { |
|
156 print $ARGV .":". $. .": $msg : ". $org; |
|
157 } |
|
158 undef $msg; |
|
159 } |
|
160 elsif ($debug==1) { |
|
161 print $org; |
|
162 } |
|
163 } continue { |
|
164 close ARGV if eof; |
|
165 } |
|
166 |
|
167 __END__ |
|
168 |
|
169 =head1 NAME |
|
170 |
|
171 code-style.pl - Review drupal code for style |
|
172 |
|
173 =head1 SYNOPSIS |
|
174 |
|
175 code-style.pl [options] <filename> |
|
176 |
|
177 Options: |
|
178 |
|
179 -? --help detailed help message |
|
180 |
|
181 =head1 DESCRIPTION |
|
182 |
|
183 Originally written for Drupal (http://drupal.org/) to ensure stylish |
|
184 code. This program reviews PHP code, and tries to show as many code |
|
185 improvements as possible with no false positives. |
|
186 |
|
187 =head1 OPTIONS |
|
188 |
|
189 --comment |
|
190 |
|
191 =head1 EXAMPLES |
|
192 |
|
193 ./code-style.pl ../index.php |
|
194 |
|
195 =cut |