52
[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP 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 Twitter: http://twitter.com/maraspin Website: http://www.maraspin.net PHPDAY Corropoli, TE - ITALY May, 15 th 2010

Striving towards better PHP code

Embed Size (px)

DESCRIPTION

Slides of Steve's talk at the Italian PHPDay 2010, in Corropoli (TE), May 15th 2012

Citation preview

Page 1: Striving towards better PHP code

[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

Page 2: Striving towards better PHP code

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

Page 3: Striving towards better PHP code

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

Page 4: Striving towards better PHP code

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

Page 5: Striving towards better PHP code

5 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

Agenda

● Key Principles

● Coding Standards

● Comments

● Namespaces

● Type Hinting

● The Magic Stuff

● Closures

● GOTO

Page 6: Striving towards better PHP code

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

Page 7: Striving towards better PHP code

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"

Page 8: Striving towards better PHP code

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]

Page 9: Striving towards better PHP code

9 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

Agenda

● Key Principles

● Coding Standards

● Comments

● Namespaces

● Type Hinting

● The Magic Stuff

● Closures

● GOTO

Page 10: Striving towards better PHP code

10 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

No Standards, uh?

www.flickr.com/photos/european_community/529820772/sizes/l

Page 11: Striving towards better PHP code

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!

Page 12: Striving towards better PHP code

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...

Page 13: Striving towards better PHP code

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

Page 14: Striving towards better PHP code

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

}

Page 15: Striving towards better PHP code

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

Page 16: Striving towards better PHP code

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

Page 17: Striving towards better PHP code

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?

Page 18: Striving towards better PHP code

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]

Page 19: Striving towards better PHP code

19 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

Agenda

● Key Principles

● Coding Standards

● Comments

● Namespaces

● Type Hinting

● The Magic Stuff

● Closures

● GOTO

Page 20: Striving towards better PHP code

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

Page 21: Striving towards better PHP code

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;

[...]}

Page 22: Striving towards better PHP code

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); }

Page 23: Striving towards better PHP code

23 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

PHPDocumentor Method Example

Page 24: Striving towards better PHP code

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

Page 25: Striving towards better PHP code

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).

Page 26: Striving towards better PHP code

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;?>

Page 27: Striving towards better PHP code

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

Page 28: Striving towards better PHP code

28 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

Agenda

● Key Principles

● Coding Standards

● Comments

● Namespaces

● Type Hinting

● The Magic Stuff

● Closures

● GOTO

Page 29: Striving towards better PHP code

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

Page 30: Striving towards better PHP code

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...

Page 31: Striving towards better PHP code

31 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

Agenda

● Key Principles

● Coding Standards

● Comments

● Namespaces

● Type Hinting

● The Magic Stuff

● Closures

● GOTO

Page 32: Striving towards better PHP code

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']);

}

[...]

Page 33: Striving towards better PHP code

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!

Page 34: Striving towards better PHP code

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;

}}

Page 35: Striving towards better PHP code

35 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

Agenda

● Key Principles

● Coding Standards

● Comments

● Namespaces

● Type Hinting

● The Magic Stuff

● Closures

● GOTO

Page 36: Striving towards better PHP code

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)

Page 37: Striving towards better PHP code

37 / 52[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

Basic Anonymous Function Example

<?php

$lambdaFunction=function($x)  { return $x*5; };

print $lambdaFunction(10);

?> 

Page 38: Striving towards better PHP code

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

Page 39: Striving towards better PHP code

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

Page 40: Striving towards better PHP code

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

Page 41: Striving towards better PHP code

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)

 

Page 42: Striving towards better PHP code

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

Page 43: Striving towards better PHP code

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]

Page 44: Striving towards better PHP code

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

Page 45: Striving towards better PHP code

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 ;

Page 46: Striving towards better PHP code

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';}

Page 47: Striving towards better PHP code

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";

Page 48: Striving towards better PHP code

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

Page 49: Striving towards better PHP code

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.

Page 50: Striving towards better PHP code

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/

Page 51: Striving towards better PHP code

[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

ANY QUESTIONS?[Feel free to e-mail me]

Page 52: Striving towards better PHP code

[S. Maraspin] STRIVING TOWARD BETTER CODE WITH PHP

THANK YOU FORYOUR ATTENTION