1. STRIVING TOWARD BETTER CODE WITH PHP
http://joind.in/talk/view/1493
Stefano "Steve" Maraspin
E-Mail: steve [AT] maraspin [DOT] net
LinkedIn: http://it.linkedin.com/in/maraspin PHPDAY
Twitter: http://twitter.com/maraspin Corropoli, TE - ITALY
Website: http://www.maraspin.net May, 15th 2010
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
2. About Me
STEFANO "STEVE" MARASPIN
● PHP user since PHP3
● Zend Certified Engineer
● PHP CLI User (Unix platforms)
● Consultant & Managing Partner at
http://www.mvassociati.it
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 2 / 52
3. Why am I here?
● Not to preach anything
● Rather, to share a few practices which either myself or
other colleagues I've been working with (in different
projects and environments) have found to be useful
● (well deserved) emphasis always given to logical
structuring of applications; but syntax and proper
construct usage can also help writing better programs
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 3 / 52
4. Messages I'd like to convey today
● maintainance consitutes an (often forgotten) integral part of the
software lifecycle
● the adoption of consistent coding standards and good practices
can ease software mainteinance
● PHP offers a few constructs and tools to improve code quality and
avoid common pitfalls
● it's dangerous to always rely on clichées; instead it's better to try
being analytical; most likely it's not a construct itself to be the root
of all evil in our code, but rather the use we make out of it
● There's no “Silver Bullet”; each specific development context
differs from others, so that it's dangerous to blindly apply
commonly accepted good practices without first making sure they
suit the specific context we're dealing with
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 4 / 52
6. What's our real goal?
● Most likely to build successful software products
● Software that does what it is supposed to do (meets expectations)
● Software that is reliable (low number of defects)
● Software that is easily maintainable (mainteinance do cost!)
● Software that talks for you (about you) even after you've left a
working place... (do not understimate the effects of your traces for
the future of your career!)
● A good software is not an output we can expect from an
algorithm; we have to choose Heuristics wisely and
depending on our specific context
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 6 / 52
7. Code quality and software success
● We spend more time reading than writing software
● Source code is often the ONLY documentation
available to development & mainteinance teams
● When not the ONLY documentation available, it's
usually the ONLY up to date
● Re-use code across projects (different people
working on it)
● In agile environments code is now designed to
change (modified more frequently)
● Let's Avoid the "Broken Window Principle"
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 7 / 52
8. Underlying Principles
● Consistency, Conceptual Integrity
● Don't Repeat Yourself (DRY)
● Don't rely upon chance
● Detect Errors Early
● Be consistent with abstraction levels
● Simplify the problem; do the possible to maximize the portion
of a program that you can ignore while working on any section of
code [McConnell]
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 8 / 52
10. No Standards, uh?
www.flickr.com/photos/european_community/529820772/sizes/l
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 10 / 52
11. Translating this pic into poor code
Script Invocation HTTP GET request: ./page.php?month=MONTH
<?
// =====================================================*
// Cool Code by John Doe *
// =====================================================*
if ($month=='may' || $month == 'jun' || $month=='july' || $month == 'augst')
{ echo 'Hot!';
}
elseif ($month=='feb' || $month==”december” || $month == 'janur') {
// ON DAY 1 I GO SKYING!
if ($dayofweek == 1) { echo 'Gone Skying for the whole week-end. Will be [...] } else {
print 'Cold!';
}}
?>
OMG - WHAT A MESS!
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 11 / 52
12. A first attempt to improve code
<?php
/**
* John Doe Yearly Feelings
● Sure enough, this is still far from perfect
* @author John Doe code – in fact the poor design/logic still
* This snippet tells users how John Doe
* feels through the year
remains the same ...let's see what's been
*/ already improved though:
$month = $_GET['month'];
const SUNDAY = 1; ● We do not rely on register_globals
if ('may' == $month || ● We do not use php short_tags
'jun' == $month || ● Our naming convention is uniform
'jul' == $month ||
● We have no magic numbers
'aug' == $month) {
echo 'Hot!'; ● Code is reasonably indented
} elseif ('feb' == $month || ● Line length is limited
'dec' == $month ||
● Only one from print & echo is used
'jan' == $month) {
// When skying I'm excited ● Header comment is easier to maintain
// otherwise I feel cold! ● There are better (although still imperfect)
if (SUNDAY == $dayOfWeek) {
echo 'Gone Skying for the whole'. comments
'week-end. Will be back on'. ● Closing tag is omitted
'monday!';
} else {
echo 'Cold!'; There road towards good code is not a
}
} nicely paved one...
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 12 / 52
13. Code Formatting and Layout
● Hard Tabs vs Soft Tabs debate (Religion War)
● Standards (PEAR, Zend, Linux, others...)
Zend FW: Spaces only; no tabs
Four (4) spaces per level of indentation
Purpose is consistency of viewing
● A possible alternative, viable solution:
● Hard Tabs for indentation
● Soft Tabs for alignment
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 13 / 52
14. Braces
● Better to avoid situations such as:
if ($condition) call_function();
● Use braces, and do it consistently
BSD Style GNU Style K&R Style
if ($condition) if ($condition) if ($condition){
{ { // statement
// statement // statement }
} }
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 14 / 52
15. About Names
● Match with level of abstraction
● Avoid No-Sense names (Foo, bar, person2, etc)
$person>eat($apple);
● Single letter variables ($x, $y, $i), are usually ok for loops only
● Here too, establish a standard (name language, notations, short
forms) – camelCase vs Simonyi vs Petzold vs ...
vUsing adjHungarian nnotation vmakes nreading ncode adjdifficult.
● yet, it makes wrong code look wrong (let's think about situations
where unit testing is not an option and code inspection has to be
performed)
$count == $people
$i_count == $as_people
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 15 / 52
16. Function/Methods
● “Functions should be short and sweet,
and do just one thing” L. Torvalds
● Limit number of parameters (possibly to no more than 7)
● Always remove unused parameters
● Use Type Hinting
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 16 / 52
17. Alignment Styles
STYLE A STYLE B
$person->firstname = 'John'; $person->firstname = 'John';
$person->lastname = 'Doe'; $person->lastname = 'Doe';
$person->birthdate = '1980-01-01'; $person->birthdate = '1980-01-01';
$car->color = colors::RED; $car->color = colors::RED;
$car->owner = $person; $car->owner = $person;
Style B is “nicer” to your eyes, but also more difficult to maintain. What happens if you add
a member variable which name is longer than all the others? Either you re-allign all
assignments or you use an abbreviation for the newly created variable. But is it the
abbreviation consistent with all other abbreviations you've used? Does it still allow you to
easily understand what that variable contains?
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 17 / 52
18. Coding Standard Guidelines
● PEAR Coding Standards
http://pear.php.net/manual/en/standards.php
● Zend Framework Coding Standards
http://framework.zend.com/manual/en/coding-standard.html
● eZComponents Implementation guidelines
http://www.ezcomponents.org/contributing/coding_standards
Enforcing Coding Standards:
http://pear.php.net/package/PHP_CodeSniffer
Much more valuable than choosing the perfect style
is having a consistent style across all your code
[G. Schlossnagle]
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 18 / 52
20. Comment Basics
Should clarify the code intent (higher level of abstraction),
not how things do get done (why, not how)
If a snippet of code is really hard to understand, you might
want, at least, start to think about rewriting the code, not just
about adding more comments to it...
It's better to always avoid redundancy in code/comments
PHP Supported Document Formats
● C Style /* */
● C++ Style //
● Shell/Perl style #
● PHPDocumentor
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 20 / 52
21. PHPDocumentor Class Example
/**
* studentClass – Represents a student
*
* This class represents a student in our Campus application
*
* @package Campus
* @author AUTHOR <EMAIL>
* @copyright 2010 NOTICE
* @license LICENSE NOTICE
* @version VERSION
*/
Class Student {
/**
* The Student Roommate
*
* @var Student
*/
private $roomMate = null;
[...]
}
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 21 / 52
22. PHPDocumentor Method Example
/**
* hasRoomMate – Tells us whether student has a roommate
*
* @return bool true if student has a roommate
*/
public function hasRoomMate() {
return (null != $this->roomMate);
}
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 22 / 52
24. Agenda
● Why worry about Clean Code
● Key Principles
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 24 / 52
25. Introducing Namespaces
● Class names have to be unique per running script
● Namespaces help us with classes which name is vague
(possibly same name, different domain/levels of
abstraction) IE “message” or third party code (possibility of
naming collisions are diminished)
● Namespaces (PHP 5.3) help avoiding class names like:
company_library_component_classofitems_item_element
(which BTW also affect code autocomplete in editor)
● No (measurable) impact on the runtime performance
● Namespace declaration has to be at the beginning of the
file (...multiple namespaces can actually be present in a
single file but the aforementioned fact must hold true).
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 25 / 52
26. Namespace Example
<?php
namespace Phpday;
const ANSWER = 'PHP';
class C { /* ... */ }
function bestLanguage() { return ANSWER; }
?>
<?php
use PhpdayC;
use Phpday as Fun;
echo PhpdayANSWER;
new PhpdayC();
PhpdaybestLanguage();
echo FunANSWER;
?>
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 26 / 52
27. Another Namespace Example
<?php
namepace Phpdaysteve;
class Presentation { /* ... */ } The compile translates this to
?> StevephpdayPresentation
<?php
class odpPresentation extends
PhpdaystevePresentation { /* ... */ }
?>
We can use the full name from
within another file
<?php
namepace Phpdaysteve;
echo strlen();
echo Phpdayotherspeakerstrlen();
?>
We can solve ambiguities
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 27 / 52
29. What is Type Hinting?
Signature example:
public function giveTalk(Topic $talkTopic)
{ /* … */ }
Benefits
● Early error detection
● More readable code
Limitations
● Not allowed for primitives (only objects and arrays)
● Passing null cause exception
Type Hinting Patch (Ilia Alshanetsky):
http://ilia.ws/archives/205-Type-hinting-for-PHP-5.3.html
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 29 / 52
30. Type Hinting Example cont.d
Alternatively, there is someone who suggest to encapsulate all
base data types in objects
Somewhere I've recently read:
“Paradigm conflict here: On the one hand, we are usually open for
most things users want to do (look at goto) and add them where
needed. This would mean to basically add all variants above to let
people do things the way they want. On the other hand, adding
strong type hints and scalar (with and without additional info) and
numeric and casts results in endless confusion and violates the
KISS principle PHP has always been based on.”
My personal opinion on this...
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 30 / 52
32. Magic Getters/Setters
class Test {
public $data = null;
public function __construct() {
$this->data = array();
$this->data['Data'] = '2009-05-15';
$this->data['Evento'] = 'phpday';
}
public function getEvento() {
return ucfirst($this->data['Evento']);
}
[...]
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 32 / 52
33. Magic Getters/Setters Cont.d
[…]
public function __get($variable) {
if (method_exists($this, $method = 'get' . $variable)) {
return $this->$method($variable);
} elseif (array_key_exists($variable,$this->data)) {
return $this->data[$variable];
} else {
throw new Exception('No Var Here With That Name!');
}
}
Ease of mainteinance is often facilitated by Magic Methods. Sure
they might slow down the code, but Profiling is the way to go. Better
to avoid premature optimization!
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 33 / 52
34. clone()
● Used to “Make Copies” of objects (since they're passed by
reference)
● __clone() is run on the copied object context
class Person {
private $name = null;
function setName($name) { $this>name = $name; }
function __clone() {
$this>name = null;
}
}
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 34 / 52
36. Anonymous Functions & Closures
● Closures rely upon concept of anonymous
functions (but they're different things!).
● AF allow for the creation of quick throw-away
functions (mainly used for callbacks).
● Don’t confuse with “create_function()”, since
the functions that this construct creates
compile at “run-time” (EVAL), so that Opcode
cachers CANNOT cache them (Bad
practice)
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 36 / 52
37. Basic Anonymous Function Example
<?php
$lambdaFunction=function($x) { return $x*5; };
print $lambdaFunction(10);
?>
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 37 / 52
38. Closure Definition
● “In computer science, a closure is a first-class function
with free variables that are bound in the lexical
environment. Such a function is said to be "closed over" its
free variables. A closure is defined within the scope of its
free variables, and the extent of those variables is at least
as long as the lifetime of the closure itself.”
[Wikipedia]
● In PHP Anonymous Functions/Closures are
implemented as Objects of the type “Closure”
● Any object with an __invoke() method can be used
as closure
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 38 / 52
41. Callback Alternatives to Closures
create_function(), which is not a good choice; and
neither are global variables or objects created for the
purpose; foreach cycles might well be, but watch out
when you do use references, since surprises can arise
if you don't pay attention...
<?php
$testArray = array('Uno','Due','Tre');
foreach ($testArray as &$elemento) { }
foreach ($testArray as $elemento) { }
print_r($testArray);
// Array([0] => Uno, [1] => Due [2] => Due)
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 41 / 52
42. Agenda
● Why worry about Clean Code
● Key Principles
● Coding Standards
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 42 / 52
43. GOTO – Common thoughts
http://xkcd.com/292
● Common fear and loathe towards GOTO, especially after famous
article "Goto Statement Considered Harmful" by E. Dijkstra
(which original title was "A Case Against the Goto Statement", by the way).
● Certainly not an essential construct [see Bohm, Jacopini work]
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 43 / 52
44. PHP GOTO – Damage Limitation
● target label must be within the same file
● you cannot jump out of a function or method, nor
can you jump into one
● you also cannot jump into any sort of loop or switch
structure
● works backwards also (uhmm...)
● interesting S. Golemon blog post about PHP GOTO history:
http://blog.libssh2.org/index.php?/archives/2-GOTO...No,-seriously,-for-real-this-time..html
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 44 / 52
45. GOTO vs BREAK
● Simplistic and yet extreme example
for ($i=0; $i<10; $i++) { for ($i=0; $i<10; $i++) {
for ($x=0; $x<10; $x++) { for ($x=0; $x<10; $x++) {
for ($y=0; $y<10; $y++) { for ($y=0; $y<10; $y++) {
for ($z=0; $z<10; $z++) { for ($z=0; $z<10; $z++) {
// Do Something if ($g > $n) goto second;
if ($g > $n) break substitute();
dosomething(); }
} second:
// Do something else // Do something else
} }
} }
} }
function substitute() {
// Do something
return 4; Note: MISSING ;
}
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 45 / 52
46. Nested If
$filename = 'file.txt';
$somecontent = 'Just Some Example Textn';
if (is_writable($filename)) {
if ($handle = fopen($filename, 'a')) {
if (fwrite($handle, $somecontent) === FALSE) {
echo 'Error Opening File';
echo 'Cleaning Up';
}
fclose($handle);
} else {
echo 'Error Opening File';
echo 'Cleaning Up';
}
} else {
echo 'File is NOT Writable';
echo 'Cleaning Up';
}
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 46 / 52
48. Other Alternatives
$filename = 'file.txt';
$somecontent = 'Dummy Textn';
$error = false;
← Status Variable Approach
if (!is_writable($filename)) {
$error = true;
echo 'File is NOT Writable';
}
if (!$error) {
if(!$handle = fopen($filename, 'a')) {
$error = true;
echo 'Error Opening File';
}
}
if (!$error) {
if (false === fwrite($handle, $somecontent)) {
$error = true;
echo 'Error Opening File';
}
}
if (false !== $error) {
Using Exceptions - Try-Finally
fclose($handle); be sure of applying it consistently
} else { though. Basic functions might
echo 'Cleaning Up'; also need to be wrapped. Might
} be overkill for small scripts
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 48 / 52
49. Another (Last) Example
if ($fileReadActionHappened) {
if (null != $dataAvailable) {
$content = $dataAvailable;
goto process;
}
} else {
$content =file_get_contents('/some/file');
process:
// Rest of Code... ← Here of course, we could decide to put
"Rest of Code" in its own function.
}
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 49 / 52
50. GOTO – Final Thoughts...
● "do you really want to outlaw sharp tools because
amateur builders can hurt themselves?*"
(...BTW when used improperly, even constructs like break, continue
and multiple return points don't cause effects much different than
those of GOTOs) *http://www.procata.com/blog/archives/2004/07/29/goto-in-php/
● I think goto's are fine, and they are often more
readable than large amounts of indentation.
[L. Torvalds] http://kerneltrap.org/node/553/2131
● Bottom Line: it's definitely still possible to write
bad code without goto. It's definitely not a single
construct which can determine the quality of our
code!
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 50 / 52
51. ANY QUESTIONS?
[Feel free to e-mail me]
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
52. THANK YOU FOR
YOUR ATTENTION
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP