[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
STRIVING TOWARD BETTER CODE WITH PHPhttp://joind.in/talk/view/1493
Stefano "Steve" MaraspinE-Mail: steve [AT] maraspin [DOT] netLinkedIn: http://it.linkedin.com/in/maraspinTwitter: http://twitter.com/maraspinWebsite: http://www.maraspin.net
PHPDAYCorropoli, TE - ITALY
May, 15th 2010
2 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
3 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
4 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
5 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Agenda
● Key Principles
● Coding Standards
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
6 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
7 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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"
8 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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]
9 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Agenda
● Key Principles
● Coding Standards
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
10 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
No Standards, uh?
www.flickr.com/photos/european_community/529820772/sizes/l
11 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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!
12 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
A first attempt to improve code
<?php
/*** John Doe Yearly Feelings* @author John Doe* This snippet tells users how John Doe* feels through the year*/$month = $_GET['month'];const SUNDAY = 1;
if ('may' == $month || 'jun' == $month || 'jul' == $month || 'aug' == $month) { echo 'Hot!';} elseif ('feb' == $month || 'dec' == $month || 'jan' == $month) { // When skying I'm excited // otherwise I feel cold! if (SUNDAY == $dayOfWeek) {
echo 'Gone Skying for the whole'. 'week-end. Will be back on'. 'monday!'; } else {
echo 'Cold!'; }}
● Sure enough, this is still far from perfect code – in fact the poor design/logic still remains the same ...let's see what's been already improved though:
● We do not rely on register_globals ● We do not use php short_tags● Our naming convention is uniform● We have no magic numbers● Code is reasonably indented● Line length is limited● Only one from print & echo is used● Header comment is easier to maintain● There are better (although still imperfect) comments● Closing tag is omitted
There road towards good code is not a nicely paved one...
13 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
14 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Braces
● Better to avoid situations such as:if ($condition) call_function();
● Use braces, and do it consistently
GNU Style
if ($condition) {
// statement }
BSD Style
if ($condition){
// statement}
K&R Style
if ($condition){// statement
}
15 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
16 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
17 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Alignment Styles
STYLE A
$person->firstname = 'John';$person->lastname = 'Doe';$person->birthdate = '1980-01-01';
$car->color = colors::RED; $car->owner = $person;
STYLE B
$person->firstname = 'John';$person->lastname = 'Doe';$person->birthdate = '1980-01-01';
$car->color = colors::RED; $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?
18 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Coding Standard Guidelines
● PEAR Coding Standardshttp://pear.php.net/manual/en/standards.php
● Zend Framework Coding Standardshttp://framework.zend.com/manual/en/coding-standard.html
● eZComponents Implementation guidelineshttp://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]
19 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Agenda
● Key Principles
● Coding Standards
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
20 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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 C++ Style // ● Shell/Perl style #● PHPDocumentor
21 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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;
[...]}
22 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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); }
23 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
PHPDocumentor Method Example
24 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Agenda
● Why worry about Clean Code
● Key Principles
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
25 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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).
26 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Namespace Example
<?phpnamespace Phpday;const ANSWER = 'PHP';class C { /* ... */ }function bestLanguage() { return ANSWER; }?>
<?phpuse Phpday\C;use Phpday as Fun;echo Phpday\ANSWER;new Phpday\C();Phpday\bestLanguage();echo Fun\ANSWER;?>
27 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Another Namespace Example
<?phpnamepace Phpday\steve;class Presentation { /* ... */ }?>
<?phpclass odpPresentation extends Phpday\steve\Presentation { /* ... */ }?>
<?phpnamepace Phpday\steve;echo strlen();echo Phpday\otherspeaker\strlen();?>
The compile translates this to Steve\phpday\Presentation
We can use the full name from within another file
We can solve ambiguities
28 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Agenda
● Key Principles
● Coding Standards
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
29 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
30 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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...
31 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Agenda
● Key Principles
● Coding Standards
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
32 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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']);
}
[...]
33 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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!
34 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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;
}}
35 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Agenda
● Key Principles
● Coding Standards
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
36 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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)
37 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Basic Anonymous Function Example
<?php
$lambdaFunction=function($x) { return $x*5; };
print $lambdaFunction(10);
?>
38 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
39 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
OK, again, here's an example...
<?php
$x = 0;
$closure = function() use ($x) { echo $x . "\n"; };
$closure(); // 0
$x = 10;
$closure(); // 0
40 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Passing by Reference
function fancy_count($arr) { $count = 0; $callback = function($dat) use (&$count) { $count++;}; array_walk($arr, $callback); return $count; }
echo fancy_count(array(0,1,2,3,4)); // 5
41 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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)
42 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Agenda
● Why worry about Clean Code
● Key Principles
● Coding Standards
● Comments
● Namespaces
● Type Hinting
● The Magic Stuff
● Closures
● GOTO
43 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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]
44 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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
45 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
GOTO vs BREAK
● Simplistic and yet extreme example
for ($i=0; $i<10; $i++) { for ($x=0; $x<10; $x++) { for ($y=0; $y<10; $y++) { for ($z=0; $z<10; $z++) { // Do Something if ($g > $n) break dosomething(); } // Do something else }
}}
for ($i=0; $i<10; $i++) { for ($x=0; $x<10; $x++) { for ($y=0; $y<10; $y++) { for ($z=0; $z<10; $z++) { if ($g > $n) goto second; substitute(); } second: // Do something else }
}}
function substitute() { // Do something return 4;}
Note: MISSING ;
46 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Nested If
$filename = 'file.txt';$somecontent = 'Just Some Example Text\n';
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';}
47 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
GOTO
$filename = 'file.txt';$somecontent = 'Dummy Text\n';
if (!is_writable($filename)) { echo 'File is NOT Writable'; goto abort;}
if(!$handle = fopen($filename, 'a')) { echo 'Error Opening File'; goto cleanup;}
if (false === fwrite($handle, $somecontent)) { echo 'Error Opening File'; goto cleanup;}
fclose($handle);
cleanup:echo "Cleaning Up";
48 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
Other Alternatives
$filename = 'file.txt';$somecontent = 'Dummy Text\n';$error = false;
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) { fclose($handle);} else { echo 'Cleaning Up';}
Using Exceptions - Try-Finally be sure of applying it consistently though. Basic functions might also need to be wrapped. Might be overkill for small scripts
← Status Variable Approach
49 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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.
50 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
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)
● I think goto's are fine, and they are often more readable than large amounts of indentation. [L. Torvalds]
● 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!
http://kerneltrap.org/node/553/2131
*http://www.procata.com/blog/archives/2004/07/29/goto-in-php/
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
ANY QUESTIONS?[Feel free to e-mail me]
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP
THANK YOU FORYOUR ATTENTION
Top Related