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