Your SlideShare is downloading. ×
0
PHP 102Out with the Bad,In with the Goodphp[tek] 2013
Who is this guy?Jeremy KendallI love to codeI love to take picturesIm terribly forgetfulI work at OpenSky
Following AlongYou can follow along with the presentationcode at github.com.https://github.com/jeremykendall/bookshelfBefo...
Why Did YouStart Programming?
I wanted to solve problems, but . . .
. . . I frequently causedas many problemsas I solved.
“Always code as if the person who ends upmaintaining your code is a violent psychopathwho knows where you live.”http://c2....
“Alternatively, always code and comment in sucha way that if someone a few notches junior picksup the code, they will take...
Lets Solve a Problem Together• Well review a “typical” PHP application• Find horrible mistakes• Correct those mistakes• Ma...
Typical application?• Ive seen code similar to the samples Ill showin almost every app Ive ever touched.• Ive made the sam...
Whats the Problem?• Crappy application• CRUD bookshelf• What does it do?:– Display books– Add books– Edit books– Delete bo...
Whats There?• Database (MySQL)• View (index.php)• Form (book-form.php)• Form processor (process-form.php)• Delete (delete-...
index.php
book-form.php
index.php – db connection$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);$query = "SELE...
index.php – db connection$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);$query = "SELE...
index.php – db connection$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);$query = "SELE...
index.php – db connection$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);$query = "SELE...
index.php – books table<?php while ($book = mysql_fetch_assoc($result)): ?><tr><td><a href="book-form.php?id=<?php echo $b...
index.php – books table<?php while ($book = mysql_fetch_assoc($result)): ?><tr><td><a href="book-form.php?id=<?php echo $b...
book-form.php// Database connection code$id = empty($_GET[id]) ? null : $_GET[id];if ($id) {// Database connection code$qu...
book-form.php$id = empty($_GET[id]) ? null : $_GET[id];if ($id) {// Database connection code$query = "SELECT title, author...
book-form.php$id = empty($_GET[id]) ? null : $_GET[id];if ($id) {// Database connection code$query = "SELECT title, author...
book-form.php<input type="hidden" name="id" value="<?php echo $id; ?>" /><input type="text" name="title" value="<?php echo...
book-form.php<input type="hidden" name="id" value="<?php echo $id; ?>" /><input type="text" name="title" value="<?php echo...
process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_PO...
process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_PO...
process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_PO...
process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_PO...
process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_PO...
Glaring Problems?• Code duplication• Input isnt filtered• Output isnt escaped• Vendor specific functions• User-provided da...
Deprecated!• mysql extension deprecated as of 5.5.0• Use mysqli or PDO• Read the MySQL “Choosing an API” onphp.net: http:/...
Code Duplication$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);• Violates DRY principl...
ConsolidateLets throw all that duplicated code into aninclude file, say library/base.php.(Well add a few other handy items...
base.php// . . . snip . . .$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);
Replace db info with base.phpRemove the db connection code from eachfile with:require_once dirname(__FILE__) . /library/ba...
STOP!
PDO• PHP Data Objects• Lightweight, consistent interface• Data access abstraction• Not database abstractionhttp://www.php....
base.php// . . . snip . . .$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE => PD...
base.php// . . . snip . . .$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE => PD...
base.php// . . . snip . . .$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE => PD...
base.php// . . . snip . . .$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE => PD...
Now start replacing mysql_*// index.php$books = $dbh->query($sql)->fetchAll();// book-form.php$book = $dbh->query($sql)->f...
Now on to the “handy items” I promised
base.php$env = getenv(APPLICATION_ENVIRONMENT);if (!$env) {$env = "production";}date_default_timezone_set(America/Chicago)...
base.php$env = getenv(APPLICATION_ENVIRONMENT);if (!$env) {$env = "production";}date_default_timezone_set(America/Chicago)...
base.php$env = getenv(APPLICATION_ENVIRONMENT);if (!$env) {$env = "production";}date_default_timezone_set(America/Chicago)...
base.php$env = getenv(APPLICATION_ENVIRONMENT);if (!$env) {$env = "production";}date_default_timezone_set(America/Chicago)...
Duplication removed, but . . .
Besides getting shot . . .“Whenever PHP generates an error messageinternally, its processed and formatted all theway up to...
We echo $title and $author<input type="hidden" name="id" value="<?php echo $id; ?>" /><input type="text" name="title" valu...
Without defining $title and $authorrequire_once dirname(__FILE__) . /library/base.php;$id = empty($_GET[id]) ? null : $_GE...
Super easy to fix$id = empty($_GET[id]) ? null : $_GET[id];$title = null;$author = null;if ($id) {$book = $dbh->query( . ....
FIEO• Filter input– Your users want to destroy your app– Prevent SQL injection• Escape output– Or just destroy your app yo...
http://xkcd.com/327/
book-form.phpUse filter_input() to guarantee $id iseither false or an int.$id = filter_input(INPUT_GET, id, FILTER_VALIDAT...
process-book.phpMore filter_input()$id = filter_input(INPUT_POST, id, FILTER_VALIDATE_INT);$title = filter_input(INPUT_POS...
index.phpUse htmlspecialchars() to escapeoutputhtmlspecialchars($book[title], ENT_COMPAT, UTF-8); ?>htmlspecialchars($book...
Prepared Statements• Uses fewer resources• Runs faster• Protects against SQL injection• Easier to read and maintain
book-form.php: Before$book = $dbh->query("SELECT title, author FROMbookshelf WHERE id = $id")->fetch();
book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:i...
book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:i...
book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:i...
book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:i...
book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:i...
process-book.php: Beforeif (empty($_POST[id])) {$sql = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]},{...
process-book.php: Afterif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE id...
process-book.php: Afterif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE id...
process-book.php: Afterif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE id...
process-book.php: Afterif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE id...
Service LayerLets replace all that baked in DB stuffwith a Service Layer
Why?• Introduce some OOP principles• High ROI– Maintainability– Testability
What? Where?• Class name: BookshelfService• Namespace: BookshelfService• Location: library/Bookshelf/Service• Filename: Bo...
BookshelfService.phpnamespace BookshelfService;class BookshelfService{private $dbh;public function __construct(PDO $dbh) {...
BookshelfService.phpprivate $dbh;public function __construct(PDO $dbh){$this->dbh = $dbh;}
BookshelfService.phppublic function find($id){$sql = SELECT * FROM bookshelf WHERE id = :id;$statement = $this->dbh->prepa...
BookshelfService.phppublic function save(array $options){if ($options[id]) {$statement = $this->dbh->prepare("UPDATE books...
A New Class Means . . .. . . we need to add a few things to base.php.
base.phpset_include_path(implode(PATH_SEPARATOR, array(get_include_path(),dirname(__FILE__))));
base.phpfunction autoload($className){// PSR-0 autoloader}spl_autoload_register(autoload);https://gist.github.com/jwage/22...
base.php// database connection code$bookshelf = new BookshelfServiceBookshelfService($dbh);
And now for some before and aftershots . . .
index.php: Beforerequire_once dirname(__FILE__) . /library/base.php;$books = $dbh->query("SELECT * FROM bookshelf ORDER BY...
index.php: Afterrequire_once dirname(__FILE__) . /library/base.php;$books = $bookshelf->findAll();
book-form.php: Beforeif ($id) {$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->b...
book-form.php: Afterif ($id) {$book = $bookshelf->find($id);$title = $book[title];$author = $book[author];}
process-book.php: Beforeif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE i...
process-book.php: After$book = array(id => $id,title => $title,author => $author);$bookshelf->save($book);
What Have We Done?• Discovered weve been handed a disaster• Iteratively improved on the nightmare• Seen some nice features...
What more could we do?• Global config file• Environment config file• Unit and functional tests• Dont roll your own!• Add C...
Want More?• Presentation source code: https://github.com/jeremykendall/bookshelf• PHP Manual: http://www.php.net/manual/en...
Questions?
Thanks!@JeremyKendalljeremy@jeremykendall.nethttp://joind.in/8188
Php 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the Good
Upcoming SlideShare
Loading in...5
×

Php 102: Out with the Bad, In with the Good

1,752

Published on

In this session, we'll look at a typical PHP application, review a few of the horrible mistakes the fictional developer made, and then refactor the app according to some best practices. Along the way you might even learn a thing or two about PHP you don't already know.

Published in: Technology
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,752
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
20
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide

Transcript of "Php 102: Out with the Bad, In with the Good"

  1. 1. PHP 102Out with the Bad,In with the Goodphp[tek] 2013
  2. 2. Who is this guy?Jeremy KendallI love to codeI love to take picturesIm terribly forgetfulI work at OpenSky
  3. 3. Following AlongYou can follow along with the presentationcode at github.com.https://github.com/jeremykendall/bookshelfBefore = oh-the-horrorAfter = much-better
  4. 4. Why Did YouStart Programming?
  5. 5. I wanted to solve problems, but . . .
  6. 6. . . . I frequently causedas many problemsas I solved.
  7. 7. “Always code as if the person who ends upmaintaining your code is a violent psychopathwho knows where you live.”http://c2.com/cgi/wiki?CodeForTheMaintainer
  8. 8. “Alternatively, always code and comment in sucha way that if someone a few notches junior picksup the code, they will take pleasure in readingand learning from it.”http://c2.com/cgi/wiki?CodeForTheMaintainer
  9. 9. Lets Solve a Problem Together• Well review a “typical” PHP application• Find horrible mistakes• Correct those mistakes• Make a cool improvement• Learn and apply better practices
  10. 10. Typical application?• Ive seen code similar to the samples Ill showin almost every app Ive ever touched.• Ive made the same mistakes in almost everyapp I wrote in my early days.• Im guessing youve seen and made similarmistakes too.
  11. 11. Whats the Problem?• Crappy application• CRUD bookshelf• What does it do?:– Display books– Add books– Edit books– Delete books
  12. 12. Whats There?• Database (MySQL)• View (index.php)• Form (book-form.php)• Form processor (process-form.php)• Delete (delete-book.php)
  13. 13. index.php
  14. 14. book-form.php
  15. 15. index.php – db connection$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);$query = "SELECT * FROM bookshelf ORDER BY title";$result = mysql_query($query);
  16. 16. index.php – db connection$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);$query = "SELECT * FROM bookshelf ORDER BY title";$result = mysql_query($query);
  17. 17. index.php – db connection$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);$query = "SELECT * FROM bookshelf ORDER BY title";$result = mysql_query($query);
  18. 18. index.php – db connection$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);$query = "SELECT * FROM bookshelf ORDER BY title";$result = mysql_query($query);
  19. 19. index.php – books table<?php while ($book = mysql_fetch_assoc($result)): ?><tr><td><a href="book-form.php?id=<?php echo $book[id]; ?>"><?php echo $book[title]; ?></a></td><td><?php echo $book[author]; ?></td></tr><?php endwhile; ?>
  20. 20. index.php – books table<?php while ($book = mysql_fetch_assoc($result)): ?><tr><td><a href="book-form.php?id=<?php echo $book[id]; ?>"><?php echo $book[title]; ?></a></td><td><?php echo $book[author]; ?></td></tr><?php endwhile; ?>
  21. 21. book-form.php// Database connection code$id = empty($_GET[id]) ? null : $_GET[id];if ($id) {// Database connection code$query = "SELECT title, author ". "FROM bookshelf WHERE id = $id";$result = mysql_query($query);$book = mysql_fetch_assoc($result);$title = $book[title];$author = $book[author];}
  22. 22. book-form.php$id = empty($_GET[id]) ? null : $_GET[id];if ($id) {// Database connection code$query = "SELECT title, author ". "FROM bookshelf WHERE id = $id";$result = mysql_query($query);$book = mysql_fetch_assoc($result);$title = $book[title];$author = $book[author];}
  23. 23. book-form.php$id = empty($_GET[id]) ? null : $_GET[id];if ($id) {// Database connection code$query = "SELECT title, author ". "FROM bookshelf WHERE id = $id";$result = mysql_query($query);$book = mysql_fetch_assoc($result);$title = $book[title];$author = $book[author];}
  24. 24. book-form.php<input type="hidden" name="id" value="<?php echo $id; ?>" /><input type="text" name="title" value="<?php echo $title; ?>" /><input type="text" name="author" value="<?php echo $author; ?>" />
  25. 25. book-form.php<input type="hidden" name="id" value="<?php echo $id; ?>" /><input type="text" name="title" value="<?php echo $title; ?>" /><input type="text" name="author" value="<?php echo $author; ?>" />
  26. 26. process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_POST[author]})";$up = "UPDATE bookshelf SET title = {$_POST[title]}, ". "author = {$_POST[author]} WHERE id = {$_POST[id]}";if (empty($_POST[id])) {mysql_query($ins);} else {mysql_query($up);}
  27. 27. process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_POST[author]})";$up = "UPDATE bookshelf SET title = {$_POST[title]}, ". "author = {$_POST[author]} WHERE id = {$_POST[id]}";if (empty($_POST[id])) {mysql_query($ins);} else {mysql_query($up);}
  28. 28. process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_POST[author]})";$up = "UPDATE bookshelf SET title = {$_POST[title]}, ". "author = {$_POST[author]} WHERE id = {$_POST[id]}";if (empty($_POST[id])) {mysql_query($ins);} else {mysql_query($up);}
  29. 29. process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_POST[author]})";$up = "UPDATE bookshelf SET title = {$_POST[title]}, ". "author = {$_POST[author]} WHERE id = {$_POST[id]}";if (empty($_POST[id])) {mysql_query($ins);} else {mysql_query($up);}
  30. 30. process-book.php// Database connection code$ins = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]}, {$_POST[author]})";$up = "UPDATE bookshelf SET title = {$_POST[title]}, ". "author = {$_POST[author]} WHERE id = {$_POST[id]}";if (empty($_POST[id])) {mysql_query($ins);} else {mysql_query($up);}
  31. 31. Glaring Problems?• Code duplication• Input isnt filtered• Output isnt escaped• Vendor specific functions• User-provided data used in SQL• Did you see any others?
  32. 32. Deprecated!• mysql extension deprecated as of 5.5.0• Use mysqli or PDO• Read the MySQL “Choosing an API” onphp.net: http://bit.ly/13zur2e
  33. 33. Code Duplication$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);• Violates DRY principle• Maintenance nightmare• The next developer will want to kill you• And your family• And your pets
  34. 34. ConsolidateLets throw all that duplicated code into aninclude file, say library/base.php.(Well add a few other handy items while were in there)
  35. 35. base.php// . . . snip . . .$db = mysql_connect(localhost, testuser, testpass);mysql_select_db(bookshelf, $db);
  36. 36. Replace db info with base.phpRemove the db connection code from eachfile with:require_once dirname(__FILE__) . /library/base.php;
  37. 37. STOP!
  38. 38. PDO• PHP Data Objects• Lightweight, consistent interface• Data access abstraction• Not database abstractionhttp://www.php.net/manual/en/intro.pdo.php
  39. 39. base.php// . . . snip . . .$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);$dsn =mysql:host=localhost;dbname=bookshelf;charset=utf8;$username = testuser;$password = testpass;try {$dbh = new PDO($dsn, $username, $password, $options);} catch (PDOException $e) {error_log($e->getMessage(), 3, ../logs/errors.log);echo An error occurred while trying to connect to thedatabase.;}
  40. 40. base.php// . . . snip . . .$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);$dsn =mysql:host=localhost;dbname=bookshelf;charset=utf8;$username = testuser;$password = testpass;try {$dbh = new PDO($dsn, $username, $password, $options);} catch (PDOException $e) {error_log($e->getMessage(), 3, ../logs/errors.log);echo An error occurred while trying to connect to thedatabase.;}
  41. 41. base.php// . . . snip . . .$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);$dsn =mysql:host=localhost;dbname=bookshelf;charset=utf8;$username = testuser;$password = testpass;try {$dbh = new PDO($dsn, $username, $password, $options);} catch (PDOException $e) {error_log($e->getMessage(), 3, ../logs/errors.log);echo An error occurred while trying to connect to thedatabase.;}
  42. 42. base.php// . . . snip . . .$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);$dsn =mysql:host=localhost;dbname=bookshelf;charset=utf8;$username = testuser;$password = testpass;try {$dbh = new PDO($dsn, $username, $password, $options);} catch (PDOException $e) {error_log($e->getMessage(), 3, ../logs/errors.log);echo An error occurred while trying to connect to thedatabase.;}
  43. 43. Now start replacing mysql_*// index.php$books = $dbh->query($sql)->fetchAll();// book-form.php$book = $dbh->query($sql)->fetch();// process-book.php and delete-book.php$dbh->exec($sql);
  44. 44. Now on to the “handy items” I promised
  45. 45. base.php$env = getenv(APPLICATION_ENVIRONMENT);if (!$env) {$env = "production";}date_default_timezone_set(America/Chicago);if ($env === "develop") {error_reporting(-1);ini_set(display_errors, 1);ini_set(display_startup_errors, 1);}// . . . snip . . .
  46. 46. base.php$env = getenv(APPLICATION_ENVIRONMENT);if (!$env) {$env = "production";}date_default_timezone_set(America/Chicago);if ($env === "develop") {error_reporting(-1);ini_set(display_errors, 1);ini_set(display_startup_errors, 1);}// . . . snip . . .
  47. 47. base.php$env = getenv(APPLICATION_ENVIRONMENT);if (!$env) {$env = "production";}date_default_timezone_set(America/Chicago);if ($env === "develop") {error_reporting(-1);ini_set(display_errors, 1);ini_set(display_startup_errors, 1);}// . . . snip . . .
  48. 48. base.php$env = getenv(APPLICATION_ENVIRONMENT);if (!$env) {$env = "production";}date_default_timezone_set(America/Chicago);if ($env === "develop") {error_reporting(-1);ini_set(display_errors, 1);ini_set(display_startup_errors, 1);}// . . . snip . . .
  49. 49. Duplication removed, but . . .
  50. 50. Besides getting shot . . .“Whenever PHP generates an error messageinternally, its processed and formatted all theway up to the fully formatted message that canbe outputted straight to the browser. Only justbefore it is displayed the error_reporting settingis checked.”http://derickrethans.nl/five-reasons-why-the-shutop-operator-should-be-avoided.html
  51. 51. We echo $title and $author<input type="hidden" name="id" value="<?php echo $id; ?>" /><input type="text" name="title" value="<?php echo $title; ?>" /><input type="text" name="author" value="<?php echo $author; ?>" />
  52. 52. Without defining $title and $authorrequire_once dirname(__FILE__) . /library/base.php;$id = empty($_GET[id]) ? null : $_GET[id];if ($id) {$book = $dbh->query( . . . )->fetch();$title = $book[title];$author = $book[author];}
  53. 53. Super easy to fix$id = empty($_GET[id]) ? null : $_GET[id];$title = null;$author = null;if ($id) {$book = $dbh->query( . . . )->fetch();$title = $book[title];$author = $book[author];}
  54. 54. FIEO• Filter input– Your users want to destroy your app– Prevent SQL injection• Escape output– Or just destroy your app yourself– Defend against XSS
  55. 55. http://xkcd.com/327/
  56. 56. book-form.phpUse filter_input() to guarantee $id iseither false or an int.$id = filter_input(INPUT_GET, id, FILTER_VALIDATE_INT);
  57. 57. process-book.phpMore filter_input()$id = filter_input(INPUT_POST, id, FILTER_VALIDATE_INT);$title = filter_input(INPUT_POST, title,FILTER_SANITIZE_STRING);$author = filter_input(INPUT_POST, author,FILTER_SANITIZE_STRING);
  58. 58. index.phpUse htmlspecialchars() to escapeoutputhtmlspecialchars($book[title], ENT_COMPAT, UTF-8); ?>htmlspecialchars($book[author], ENT_COMPAT, UTF-8); ?>
  59. 59. Prepared Statements• Uses fewer resources• Runs faster• Protects against SQL injection• Easier to read and maintain
  60. 60. book-form.php: Before$book = $dbh->query("SELECT title, author FROMbookshelf WHERE id = $id")->fetch();
  61. 61. book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:id, $id);$statement->execute();$book = $statement->fetch();
  62. 62. book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:id, $id);$statement->execute();$book = $statement->fetch();
  63. 63. book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:id, $id);$statement->execute();$book = $statement->fetch();
  64. 64. book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:id, $id);$statement->execute();$book = $statement->fetch();
  65. 65. book-form.php: After$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:id, $id);$statement->execute();$book = $statement->fetch();
  66. 66. process-book.php: Beforeif (empty($_POST[id])) {$sql = "INSERT INTO bookshelf (title, author) "."VALUES ({$_POST[title]},{$_POST[author]})";$dbh->exec($sql);} else {$sql = "UPDATE bookshelf SET title ={$_POST[title]}, ". "author = {$_POST[author]} WHERE id ={$_POST[id]}";$dbh->exec($sql);}
  67. 67. process-book.php: Afterif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE id = :id");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->bindParam(:id, $id);$statement->execute();} else {$statement = $dbh->prepare("INSERT INTO bookshelf(title, author) VALUES (:title, :author)");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->execute();}
  68. 68. process-book.php: Afterif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE id = :id");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->bindParam(:id, $id);$statement->execute();} else {$statement = $dbh->prepare("INSERT INTO bookshelf(title, author) VALUES (:title, :author)");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->execute();}
  69. 69. process-book.php: Afterif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE id = :id");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->bindParam(:id, $id);$statement->execute();} else {$statement = $dbh->prepare("INSERT INTO bookshelf(title, author) VALUES (:title, :author)");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->execute();}
  70. 70. process-book.php: Afterif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE id = :id");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->bindParam(:id, $id);$statement->execute();} else {$statement = $dbh->prepare("INSERT INTO bookshelf(title, author) VALUES (:title, :author)");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->execute();}
  71. 71. Service LayerLets replace all that baked in DB stuffwith a Service Layer
  72. 72. Why?• Introduce some OOP principles• High ROI– Maintainability– Testability
  73. 73. What? Where?• Class name: BookshelfService• Namespace: BookshelfService• Location: library/Bookshelf/Service• Filename: BookshelfService.php• PSR-0 compliant(The class name should mirror its location in the filesystem)
  74. 74. BookshelfService.phpnamespace BookshelfService;class BookshelfService{private $dbh;public function __construct(PDO $dbh) {}public function find($id) {}public function findAll() {}public function save(array $options) {}public function delete($id) {}}
  75. 75. BookshelfService.phpprivate $dbh;public function __construct(PDO $dbh){$this->dbh = $dbh;}
  76. 76. BookshelfService.phppublic function find($id){$sql = SELECT * FROM bookshelf WHERE id = :id;$statement = $this->dbh->prepare($sql);$statement->bindParam(:id, $id);$statement->execute();return $statement->fetch();}public function findAll(){$sql = SELECT * FROM bookshelf ORDER BY title;return $this->dbh->query($sql)->fetchAll();}
  77. 77. BookshelfService.phppublic function save(array $options){if ($options[id]) {$statement = $this->dbh->prepare("UPDATE bookshelfSET title = :title, author = :author WHERE id = :id");$statement->execute($options);} else {unset($options[id]);$statement = $this->dbh->prepare("INSERT INTObookshelf (title, author) VALUES (:title, :author)");$statement->execute($options);}}
  78. 78. A New Class Means . . .. . . we need to add a few things to base.php.
  79. 79. base.phpset_include_path(implode(PATH_SEPARATOR, array(get_include_path(),dirname(__FILE__))));
  80. 80. base.phpfunction autoload($className){// PSR-0 autoloader}spl_autoload_register(autoload);https://gist.github.com/jwage/221634
  81. 81. base.php// database connection code$bookshelf = new BookshelfServiceBookshelfService($dbh);
  82. 82. And now for some before and aftershots . . .
  83. 83. index.php: Beforerequire_once dirname(__FILE__) . /library/base.php;$books = $dbh->query("SELECT * FROM bookshelf ORDER BYtitle")->fetchAll();
  84. 84. index.php: Afterrequire_once dirname(__FILE__) . /library/base.php;$books = $bookshelf->findAll();
  85. 85. book-form.php: Beforeif ($id) {$statement = $dbh->prepare(SELECT title, author FROMbookshelf WHERE id = :id);$statement->bindParam(:id, $id);$statement->execute();$book = $statement->fetch();$title = $book[title];$author = $book[author];}
  86. 86. book-form.php: Afterif ($id) {$book = $bookshelf->find($id);$title = $book[title];$author = $book[author];}
  87. 87. process-book.php: Beforeif ($id) {$statement = $dbh->prepare("UPDATE bookshelf SET title= :title, author = :author WHERE id = :id");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->bindParam(:id, $id);$statement->execute();} else {$statement = $dbh->prepare("INSERT INTO bookshelf(title, author) VALUES (:title, :author)");$statement->bindParam(:title, $title);$statement->bindParam(:author, $author);$statement->execute();}
  88. 88. process-book.php: After$book = array(id => $id,title => $title,author => $author);$bookshelf->save($book);
  89. 89. What Have We Done?• Discovered weve been handed a disaster• Iteratively improved on the nightmare• Seen some nice features of PHP– PDO– Filtering and escaping– OOP• Learned something?
  90. 90. What more could we do?• Global config file• Environment config file• Unit and functional tests• Dont roll your own!• Add Composer, add libraries as needed
  91. 91. Want More?• Presentation source code: https://github.com/jeremykendall/bookshelf• PHP Manual: http://www.php.net/manual/en/index.php• PHP The Right Way: http://www.phptherightway.com/• PHP 101 Suggestions:http://csiphp.com/blog/2011/07/19/stop-doing-it-wrong-and-learn-to-code-good-too/• PHP 101: PHP for the Absolute Beginner:http://devzone.zend.com/6/php-101-php-for-the-absolute-beginner/• PHPDeveloper.org http://phpdeveloper.org/• php|architect: http://www.phparch.com/• Composer http://getcomposer.org
  92. 92. Questions?
  93. 93. Thanks!@JeremyKendalljeremy@jeremykendall.nethttp://joind.in/8188
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×