Who we are
Premium PHP Consulting & Training. Worldwide.
Sebastian Arne Stefan
Bergmann Blankerts Priebsch
Code Review
”The best way to prepare [to be a
programmer] is to write programs, and to
study great programs that other people
have written.
In my case, I went to the garbage cans at
the Computer Science Center and fished
out listings of their operating system.”
- Bill Gates
Code Review
Automatic Analysis and Manual Review to improve code quality.
Management vs. Code Reviews
”Stop those code reviews!
They're slowing down
the project.”
This slide contains material by Johanna Rothmann
Management vs. Code Reviews
”But then we won't know where the bugs are.
We need the code reviews.”
This slide contains material by Johanna Rothmann
Management vs. Code Reviews
”Stop them or
I'll fire you.”
This slide contains material by Johanna Rothmann
Management vs. Code Reviews
”You'll fire me for doing the
right thing?”
This slide contains material by Johanna Rothmann
Management vs. Code Reviews
”In this case, the right thing
is to finish the project as
fast as possible.
Stop those code reviews.”
This slide contains material by Johanna Rothmann
You may be wondering ...
… what we will be doing here today in this workshop.
Motivation and Disclaimer
In our daily work as consultants we
see quite a few cases of bad code.
Naturally, we cannot talk about
our customers' code.
Instead, we use examples from
Open Source PHP projects.
We might come across harsh.
But it is not our intention to bash any of
the Open Source projects we will look at.
CakePHP
Release 1.2.5
sb@ubuntu src % phploc --count-tests --exclude cake-1.2.5/vendors cake-1.2.5
phploc 1.3.0 by Sebastian Bergmann.
Directories: 62
Files: 420
Lines of Code (LOC): 162937
Cyclomatic Complexity / Lines of Code: 0.11
Executable Lines of Code (ELOC): 85582
Comment Lines of Code (CLOC): 52689
Non-Comment Lines of Code (NCLOC): 110248
Interfaces: 0
Classes: 614
Abstract Classes: 0
Concrete Classes: 614
Lines of Code / Number of Classes: 265
Methods: 2322
Non-Static Methods: 2322
Static Methods: 0
Lines of Code / Number of Methods: 70
Cyclomatic Complexity / Number of Methods: 4.70
Functions: 62
Constants: 182
Global constants: 182
Class constants: 0
Tests:
Classes: 92
Methods: 1183
CakePHP
cake/lib/class_registry.php (Release 1.2.5)
003 /**
004 * Class collections.
005 *
006 * A repository for class objects, each registered with a key.
CakePHP
cake/lib/class_registry.php (Release 1.2.5)
037 class ClassRegistry {
072 /**
073 * Loads a class, registers the object in the registry
and returns instance of the object.
097 */
352 }
CakePHP
cake/lib/object.php (Release 1.2.5)
037 class Object {
207 /**
208 * Checks for a persistent class file, if found file is opened and
true returned
209 * If file is not found a file is created and false returned
210 * If used in other locations of the model you should choose a unique
name for the persistent file
211 * There are many uses for this method, see manual for examples
212 *
213 * @param string $name name of the class to persist
214 * @param string $object the object to persist
215 * @return boolean Success
216 * @access protected
217 * @todo add examples to manual
218 */
219 function _persist($name, $return = null, &$object, $type = null) {
236 }
297 }
CakePHP
cake/lib/object.php (Release 1.2.5)
037 class Object {
207 /**
208 * Checks for a persistent class file, if found file is opened and
true returned
209 * If file is not found a file is created and false returned
210 * If used in other locations of the model you should choose a unique
name for the persistent file
211 * There are many uses for this method, see manual for examples
212 *
213 * @param string $name name of the class to persist
214 * @param string $object the object to persist
215 * @return boolean Success
216 * @access protected
217 * @todo add examples to manual
218 */
219 function _persist($name, $return = null, &$object, $type = null) {
236 }
297 }
CakePHP
cake/lib/session.php (Release 1.2.5)
049 class CakeSession extends Object {
188 /**
189 * Returns true if given variable is set in session.
190 *
191 * @param string $name Variable name to check for
192 * @return boolean True if variable is there
193 * @access public
194 */
195 function check($name) {
196 $var = $this->__validateKeys($name);
197 if (empty($var)) {
198 return false;
199 }
200 $result = Set::extract($_SESSION, $var);
201 return isset($result);
202 }
778 }
CakePHP
cake/lib/session.php (Release 1.2.5)
049 class CakeSession extends Object {
627 /**
628 * Validate that the $name is in correct dot notation
629 * example: $name = 'ControllerName.key';
630 *
631 * @param string $name Session key names as string.
632 * @return mixed false is $name is not correct format, or $name if it
is correct
633 * @access private
634 */
635 function __validateKeys($name) {
636 if (is_string($name) && preg_match("/^[ 0-9a-zA-Z._-]*$/",
$name)) {
637 return $name;
638 }
639 $this->__setError(3, "$name is not a string");
640 return false;
641 }
778 }
CakePHP
cake/lib/session.php (Release 1.2.5)
049 class CakeSession extends Object {
188 /**
189 * Returns true if given variable is set in session.
190 *
191 * @param string $name Variable name to check for
192 * @return boolean True if variable is there
193 * @access public
194 */
195 function check($name) {
196 $var = $this->__validateKeys($name);
197 if (empty($var)) {
198 return false;
199 }
200 $result = Set::extract($_SESSION, $var);
201 return isset($result);
202 }
778 }
CakePHP
cake/lib/session.php (Release 1.2.5)
049 class CakeSession extends Object {
203 /**
204 * Returns the Session id
205 *
206 * @param id $name string
207 * @return string Session id
208 * @access public
209 */
210 function id($id = null) {
211 if ($id) {
212 $this->id = $id;
213 session_id($this->id);
214 }
215 if (isset($_SESSION)) {
216 return session_id();
217 } else {
218 return $this->id;
219 }
220 }
778 }
Drupal
includes/bootstrap.inc (Release 6.13)
297 function conf_init() {
298 global $base_url, $base_path, $base_root;
299
300 // Export the following settings.php variables to the global namespace
301 global $db_url, $db_prefix, $cookie_domain, $conf, $installed_profile,
$update_free_access;
302 $conf = array();
303
304 if (isset($_SERVER['HTTP_HOST'])) {
305 // As HTTP_HOST is user input, ensure it only contains characters allowed
306 // in hostnames. See RFC 952 (and RFC 2181).
307 // $_SERVER['HTTP_HOST'] is lowercased here per specifications.
308 $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']);
309 if (!drupal_valid_http_host($_SERVER['HTTP_HOST'])) {
310 // HTTP_HOST is invalid, e.g. if containing slashes it may be an attack.
311 header('HTTP/1.1 400 Bad Request');
312 exit;
313 }
314 }
315 else {
316 // Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is
317 // defined for E_ALL compliance.
318 $_SERVER['HTTP_HOST'] = '';
319 }
393 }
Drupal
includes/bootstrap.inc (Release 6.13)
363 // Otherwise use $base_url as session name, without the protocol
364 // to use the same session identifiers across http and https.
371 // To prevent session cookies from being hijacked, a user can configure the
372 // SSL version of their website to only transfer session cookies via SSL by
373 // using PHP's session.cookie_secure setting. The browser will then use two
374 // separate session cookies for the HTTPS and HTTP versions of the site. So we
375 // must use different session identifiers for HTTPS and HTTP to prevent a
376 // cookie collision.
Drupal
includes/bootstrap.inc (Release 6.13)
363 // Otherwise use $base_url as session name, without the protocol
364 // to use the same session identifiers across http and https.
371 // To prevent session cookies from being hijacked, a user can configure the
372 // SSL version of their website to only transfer session cookies via SSL by
373 // using PHP's session.cookie_secure setting. The browser will then use two
374 // separate session cookies for the HTTPS and HTTP versions of the site. So we
375 // must use different session identifiers for HTTPS and HTTP to prevent a
376 // cookie collision.
Drupal
includes/bootstrap.inc (Release 6.13)
423 function drupal_get_filename($type, $name, $filename = NULL) {
424 static $files = array();
425
426 if (!isset($files[$type])) {
427 $files[$type] = array();
428 }
429
430 if (!empty($filename) && file_exists($filename)) {
431 $files[$type][$name] = $filename;
432 }
433 elseif (isset($files[$type][$name])) {
434 // nothing
435 }
436 // Verify that we have an active database connection, before querying
437 // the database. This is required because this function is called both
438 // before we have a database connection (i.e. during installation) and
439 // when a database connection fails.
440 elseif (db_is_active() &&
(($file = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", $name, $type))) &&
file_exists($file))) {
441 $files[$type][$name] = $file;
442 }
443 else {
444 // Fallback to searching the filesystem if the database connection is
445 // not established or the requested file is not found.
446 $config = conf_path();
447 $dir = (($type == 'theme_engine') ? 'themes/engines' : "${type}s");
448 $file = (($type == 'theme_engine') ? "$name.engine" : "$name.$type");
449
450 foreach (array("$config/$dir/$file", "$config/$dir/$name/$file", "$dir/$file", "$dir/$name/$file") as $file) {
451 if (file_exists($file)) {
452 $files[$type][$name] = $file;
453 break;
454 }
455 }
456 }
457
458 if (isset($files[$type][$name])) {
459 return $files[$type][$name];
460 }
461 }
Elgg
Revision 3264
sb@ubuntu src % phploc --count-tests elgg-r3264
phploc 1.3.0 by Sebastian Bergmann.
Directories: 226
Files: 578
Lines of Code (LOC): 55269
Cyclomatic Complexity / Lines of Code: 0.20
Executable Lines of Code (ELOC): 22086
Comment Lines of Code (CLOC): 19054
Non-Comment Lines of Code (NCLOC): 36215
Interfaces: 6
Classes: 83
Abstract Classes: 11
Concrete Classes: 72
Lines of Code / Number of Classes: 665
Methods: 538
Non-Static Methods: 531
Static Methods: 7
Lines of Code / Number of Methods: 102
Cyclomatic Complexity / Number of Methods: 1.91
Functions: 693
Constants: 22
Global constants: 22
Class constants: 0
Tests:
Classes: 0
Methods: 0
Elgg
entities.php (revision 3264)
31 abstract class ElggEntity implements
32 Notable, // Calendar interface
33 Locatable, // Geocoding interface
34 Exportable, // Allow export of data
35 Importable, // Allow import of data
36 Loggable, // Can events related to this object class be logged
37 Iterator, // Override foreach behaviour
38 ArrayAccess // Override for array access
39 {
1033 }
1034
2801 // functions in the global namespace
Elgg
entities.php (revision 3264)
195 /**
196 * Class member get overloading
197 *
198 * @param string $name
199 * @return mixed
200 */
201 function __get($name) { return $this->get($name); }
Elgg
entities.php (revision 3264)
125 public function get($name)
126 {
127 // See if its in our base attribute
128 if (isset($this->attributes[$name])) {
129 return $this->attributes[$name];
130 }
131
132 // No, so see if its in the meta data for this entity
133 $meta = $this->getMetaData($name);
134 if ($meta)
135 return $meta;
136
137 // Can't find it, so return null
138 return null;
139 }
Elgg
users.php (revision 3264)
29 class ElggUser extends ElggEntity
30 implements Friendable
31 {
168 public function ban($reason = "") {
return ban_user($this->guid, $reason);
}
338 }
339
1565 // functions in the global namespace
Habari
Revision 3563
sb@ubuntu src % phploc --count-tests habari-r3563
phploc 1.3.0 by Sebastian Bergmann.
Directories: 25
Files: 255
Lines of Code (LOC): 43975
Cyclomatic Complexity / Lines of Code: 0.18
Executable Lines of Code (ELOC): 21898
Comment Lines of Code (CLOC): 12396
Non-Comment Lines of Code (NCLOC): 31579
Interfaces: 5
Classes: 138
Abstract Classes: 6
Concrete Classes: 132
Lines of Code / Number of Classes: 318
Methods: 1458
Non-Static Methods: 948
Static Methods: 510
Lines of Code / Number of Methods: 30
Cyclomatic Complexity / Number of Methods: 3.35
Functions: 7
Constants: 88
Global constants: 33
Class constants: 55
Tests:
Classes: 14
Methods: 128
Habari
index.php (revision 3563)
192 // If we're doing unit testing, stop here
193 if ( defined( 'UNIT_TEST' ) ) {
194 return;
195 }
Habari
index.php (revision 3563)
52 // Replace all of the $_GET, $_POST and $_SERVER superglobals with object
53 // representations of each. Unset $_REQUEST, which is evil.
54 // $_COOKIE must be set after sessions start
55 SuperGlobal::process_gps();
Habari
superglobal.php (revision 3563)
11 class SuperGlobal extends ArrayIterator
12 {
29 public static function process_gps()
30 {
50 }
263 }
Habari
superglobal.php (revision 3563)
11 class SuperGlobal extends ArrayIterator
12 {
29 public static function process_gps()
30 {
31 /* We should only revert the magic quotes once per page hit */
32 static $revert = true;
33
34 if (!$revert) {
35 // our work has already been done
36 return;
37 }
38
39 if ( get_magic_quotes_gpc() ) {
40 $_GET = Utils::stripslashes($_GET);
41 $_POST = Utils::stripslashes($_POST);
42 }
43
44 $_GET = new SuperGlobal($_GET);
45 $_POST = new SuperGlobal($_POST);
46 $_SERVER = new SuperGlobal($_SERVER);
47 unset($_REQUEST);
48
49 $revert = false;
50 }
263 }
Habari
superglobal.php (revision 3563)
11 class SuperGlobal extends ArrayIterator
12 {
29 public static function process_gps()
30 {
44 $_GET = new SuperGlobal($_GET);
45 $_POST = new SuperGlobal($_POST);
46 $_SERVER = new SuperGlobal($_SERVER);
47 unset($_REQUEST);
50 }
263 }
Habari
superglobal.php (revision 3563)
11 class SuperGlobal extends ArrayIterator
12 {
29 public static function process_gps()
30 {
31 /* We should only revert the magic quotes once per page hit */
32 static $revert = true;
33
34 if (!$revert) {
35 // our work has already been done
36 return;
37 }
38
49 $revert = false;
50 }
263 }
Habari
singleton.php (revision 3563)
13 abstract class Singleton
14 {
24 protected static function instance()
25 {
26 /*
27 * It is important to note that subclasses MUST override this
28 * method, as get_class will ALWAYS return 'Singleton' when
29 * subclasses call this method through inheritance
30 * return self::getInstanceOf( get_class() );
31 */
32 trigger_error(_t('Not implemented: instance'), E_USER_WARNING);
33 return null;
34 }
64 }
Joomla
Release 1.5.14
sb@ubuntu src % phploc --count-tests joomla-1.5.14
phploc 1.3.0 by Sebastian Bergmann.
Directories: 398
Files: 1072
Lines of Code (LOC): 242367
Cyclomatic Complexity / Lines of Code: 0.17
Executable Lines of Code (ELOC): 121338
Comment Lines of Code (CLOC): 81712
Non-Comment Lines of Code (NCLOC): 160655
Interfaces: 0
Classes: 749
Abstract Classes: 0
Concrete Classes: 749
Lines of Code / Number of Classes: 323
Methods: 5066
Non-Static Methods: 5065
Static Methods: 1
Lines of Code / Number of Methods: 47
Cyclomatic Complexity / Number of Methods: 3.95
Functions: 452
Constants: 637
Global constants: 637
Class constants: 0
Tests:
Classes: 0
Methods: 0
Shindig
Revision 772122
sb@ubuntu src % phploc --count-tests --exclude shindig-r772122/php/external
shindig-r772122/php
phploc 1.3.0 by Sebastian Bergmann.
Directories: 21
Files: 182
Lines of Code (LOC): 23266
Cyclomatic Complexity / Lines of Code: 0.14
Executable Lines of Code (ELOC): 10509
Comment Lines of Code (CLOC): 8692
Non-Comment Lines of Code (NCLOC): 14574
Interfaces: 8
Classes: 182
Abstract Classes: 16
Concrete Classes: 166
Lines of Code / Number of Classes: 127
Methods: 1298
Non-Static Methods: 1204
Static Methods: 94
Lines of Code / Number of Methods: 17
Cyclomatic Complexity / Number of Methods: 2.04
Functions: 4
Constants: 16
Global constants: 2
Class constants: 14
Tests:
Classes: 43
Methods: 355
Shindig
index.php (revision 772122)
21 // Some people forget to set their timezone in their php.ini,
22 // this prevents that from generating warnings
23 @date_default_timezone_set(@date_default_timezone_get());
Shindig
index.php (revision 772122)
48 function __autoload($className) {
70 // Check for the presense of this class in our all our directories.
71 $fileName = $className . '.php';
72 foreach ($locations as $path) {
73 if (file_exists("{$path}/$fileName")) {
74 require $path.'/'.$fileName;
75 break;
76 }
77 }
78 }
Shindig
index.php (revision 772122)
113 $class = new $class();
Shindig
index.php (revision 772122)
113 $class = new $class();
114 $method = $_SERVER['REQUEST_METHOD'];
115 // Not all clients support the PUT, HEAD & DELETE http methods,
they depend on the X-HTTP-Method-Override instead
116 if ($method == 'POST' &&
isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
117 $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
118 }
119 $method = 'do' . ucfirst(strtolower($method));
120 if (is_callable(array($class, $method))) {
121 $class->$method();
122 } else {
TikiWiki
Release 3.1
sb@ubuntu src % phploc --count-tests tikiwiki-3.1
phploc 1.3.0 by Sebastian Bergmann.
Directories: 704
Files: 3603
Lines of Code (LOC): 1092274
Cyclomatic Complexity / Lines of Code: 0.15
Executable Lines of Code (ELOC): 478705
Comment Lines of Code (CLOC): 739588
Non-Comment Lines of Code (NCLOC): 352686
Interfaces: 58
Classes: 2547
Abstract Classes: 137
Concrete Classes: 2410
Lines of Code / Number of Classes: 428
Methods: 20075
Non-Static Methods: 19271
Static Methods: 804
Lines of Code / Number of Methods: 54
Cyclomatic Complexity / Number of Methods: 3.61
Functions: 1200
Constants: 4244
Global constants: 950
Class constants: 3294
Tests:
Classes: 26
Methods: 202
In this workshop, three PHP experts with different more
In this workshop, three PHP experts with different software engineering focuses (testing, architecture, and security) will perform an interactive code review together with the audience. Using examples from Open Source projects, attendees of this workshop learn how experts look at code, what tools they use during code reviews, what good code and bad code looks like, and how to avoid the most common gotchas. They are invited to bring their own code for an anonymous code review for an increased benefit from the workshop. Each of the three PHP experts involved in this workshop will present an in-depth workshop for their particular area of expertise in the afternoon that builds upon this one. less
0 comments
Post a comment