Your SlideShare is downloading. ×
0
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
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
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

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

1,101

Published on

We'll look at a typical first 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 …

We'll look at a typical first 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.

Presented at Memphis PHP on Feb 23, 2012.

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,101
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
15
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. PHP 102 Out with the Bad, In with the Good Memphis PHP, February 23, 2012
  • 2. Who is this guy?
      Jeremy Kendall PHP developer since about 2001 Organizer of Memphis PHP Zend Framework expert fanboi Speaker Photographer Sometime blogger Sometime open source contributor
  • 3. Following Along
      You can follow along with the presentation code at github.com. http://git.io/eAUNow
  • 4. Why Did You Start Programming?
  • 5. I wanted to solve problems, but . . .
  • 6. . . . I frequently caused as many problems as I solved.
  • 7. Let's Solve a Problem Together
    • We'll create a typical first web application
    • 8. Make horrible mistakes
    • 9. Correct those mistakes
    • 10. Make a cool improvement
    • 11. Learn something?
  • 12. What's the Problem?
    • I want to create an application that stores and displays books from my bookshelf
    • 13. Requirements:
    • Why no delete? I ran out of time and energy.
  • 17. So, What do we Need?
    • Database (sqlite)
    • 18. View (index.php)
    • 19. Form (book-form.php)
    • 20. Form processor (process-form.php)
  • 21. index.php – db connection <?php $db = realpath ( dirname ( __FILE__ ) . '/data/db/bookshelf.db' ); $dsn = &quot;sqlite: $db &quot; ; $options = array ( PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION , PDO :: ATTR_DEFAULT_FETCH_MODE => PDO :: FETCH_ASSOC ); try { $dbh = new PDO ( $dsn , null , null , $options ); } catch ( PDOException $e ) { echo &quot;Error!: &quot; . $e -> getMessage() . &quot;<br /> n &quot; ; die (); } $books = $dbh -> query( &quot; SELECT * FROM bookshelf ORDER BY title&quot; ) -> fetchAll();
  • 22. index.php – db connection <?php $db = realpath ( dirname ( __FILE__ ) . '/data/db/bookshelf.db' ); $dsn = &quot;sqlite: $db &quot; ; $options = array ( PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION , PDO :: ATTR_DEFAULT_FETCH_MODE => PDO :: FETCH_ASSOC ); try { $dbh = new PDO ($dsn, null , null , $options); } catch ( PDOException $e) { echo &quot;Error!: &quot; . $e -> getMessage() . &quot;<br />n&quot;; die (); } $books = $dbh -> query(&quot; SELECT * FROM bookshelf ORDER BY title&quot;) -> fetchAll() ;
  • 23. index.php – db connection <?php $db = realpath ( dirname ( __FILE__ ) . '/data/db/bookshelf.db'); $dsn = &quot;sqlite:$db&quot;; $options = array ( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION , PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ); try { $dbh = new PDO ( $dsn , null , null , $options ); } catch ( PDOException $e ) { echo &quot;Error!: &quot; . $e -> getMessage() . &quot;<br /> n &quot; ; die (); } $books = $dbh -> query(&quot; SELECT * FROM bookshelf ORDER BY title&quot;) -> fetchAll();
  • 24. index.php – db connection <?php $db = realpath ( dirname ( __FILE__ ) . '/data/db/bookshelf.db'); $dsn = &quot;sqlite:$db&quot;; $options = array ( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION , PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ); try { $dbh = new PDO ($dsn, null , null , $options); } catch ( PDOException $e) { echo &quot;Error!: &quot; . $e -> getMessage() . &quot;<br />n&quot;; die (); } $books = $dbh -> query( &quot; SELECT * FROM bookshelf ORDER BY title&quot; ) -> fetchAll();
  • 25. index.php
  • 26. index.php – books table <?php if ( count ( $books ) > 0 ): ?> < table > < tr > < th > Title </ th >< th > Author </ th > </ tr > <?php foreach ( $books as $book ): ?> < tr > < td > < a href= &quot;book-form.php?id=<?php echo $book ['id']; ?>&quot; > <?php echo $book [ 'title' ]; ?> </ a > </ td > < td > <?php echo $book [ 'author' ]; ?> </ td > </ tr > <?php endforeach ; ?> </ table > <?php else : ?> < p > We have no books! </ p > <?php endif ; ?>
  • 27. index.php – books table <?php if ( count ( $books ) > 0 ): ?> < table > < tr > < th > Title </ th >< th > Author </ th > </ tr > <?php foreach ( $books as $book ): ?> < tr > < td > < a href= &quot;book-form.php?id=<?php echo $book ['id']; ?>&quot; > <?php echo $book [ 'title' ]; ?> </ a > </ td > < td > <?php echo $book [ 'author' ]; ?> </ td > </ tr > <?php endforeach ; ?> </ table > <?php else : ?> < p >We have no books!</ p > <?php endif ; ?>
  • 28. index.php – books table <?php if ( count ($books) > 0): ?> < table > < tr > < th >Title</ th >< th >Author</ th > </ tr > <?php foreach ( $books as $book ): ?> < tr > < td > < a href= &quot;book-form.php?id=<?php echo $book ['id']; ?>&quot; > <?php echo $book [ 'title' ]; ?> </ a > </ td > < td > <?php echo $book [ 'author' ]; ?> </ td > </ tr > <?php endforeach ; ?> </ table > <?php else : ?> < p >We have no books!</ p > <?php endif ; ?>
  • 29. index.php – books table <?php if ( count ($books) > 0): ?> < table > < tr > < th >Title</ th >< th >Author</ th > </ tr > <?php foreach ($books as $book): ?> < tr > < td > < a href=&quot;book-form.php?id=<?php echo $book['id']; ?>&quot;> <?php echo $book['title']; ?> </ a > </ td > < td > <?php echo $book['author']; ?> </ td > </ tr > <?php endforeach ; ?> </ table > <?php else : ?> < p > We have no books! </ p > <?php endif ; ?>
  • 30.  
  • 31. book-form.php
  • 32. book-form.php <?php $id = empty ( $_GET [ 'id' ]) ? null : $_GET [ 'id' ]; if ( $id ) { // Database connection code $book = $dbh -> query( &quot; SELECT title, author FROM bookshelf WHERE id = $id &quot; ) -> fetch(); $title = $book [ 'title' ]; $author = $book [ 'author' ]; }
  • 33. book-form.php <?php $id = empty ( $_GET [ 'id' ]) ? null : $_GET [ 'id' ]; if ($id) { // Database connection code $book = $dbh -> query(&quot; SELECT title, author FROM bookshelf WHERE id = $id&quot;) -> fetch(); $title = $book['title']; $author = $book['author']; }
  • 34. book-form.php <?php $id = empty ($_GET['id']) ? null : $_GET['id']; if ( $id ) { // Database connection code $book = $dbh -> query( &quot; SELECT title, author FROM bookshelf WHERE id = $id &quot; ) -> fetch(); $title = $book [ 'title' ]; $author = $book [ 'author' ]; }
  • 35. book-form.php < form method= &quot;post&quot; action= &quot;process-book.php&quot; > < input type= &quot;hidden&quot; id= &quot;id&quot; name= &quot;id&quot; value= &quot;<?php echo $id ; ?>&quot; /> < dl > < dt > < label for= &quot;title&quot; > Title </ label > </ dt > < dd > < input type= &quot;text&quot; id= &quot;title&quot; name= &quot;title&quot; value= &quot;<?php echo $title ; ?>&quot; /> </ dd > < dt > < label for= &quot;author&quot; > Author </ label > </ dt > < dd > < input type= &quot;text&quot; id= &quot;author&quot; name= &quot;author&quot; value= &quot;<?php echo $author ; ?>&quot; /> </ dd > < dt > &nbsp; </ dt > < dd > < input type= &quot;submit&quot; value= &quot;Submit&quot; /> </ dd > </ dl > </ form >
  • 36. book-form.php < form method= &quot;post&quot; action= &quot;process-book.php&quot; > < input type=&quot;hidden&quot; id=&quot;id&quot; name=&quot;id&quot; value= &quot;<?php echo $id ; ?>&quot; /> < dl > < dt > < label for=&quot;title&quot;>Title</ label > </ dt > < dd > < input type=&quot;text&quot; id=&quot;title&quot; name=&quot;title&quot; value= &quot;<?php echo $title ; ?>&quot; /> </ dd > < dt > < label for=&quot;author&quot;>Author</ label > </ dt > < dd > < input type=&quot;text&quot; id=&quot;author&quot; name=&quot;author&quot; value= &quot;<?php echo $author ; ?>&quot; /> </ dd > < dt > &nbsp; </ dt > < dd > < input type=&quot;submit&quot; value=&quot;Submit&quot; /> </ dd > </ dl > </ form >
  • 37. process-book.php <?php if ( strtolower ( $_SERVER [ 'REQUEST_METHOD' ]) == 'get' ) { header ( &quot;Location: /&quot; ); } // Database connection code if ( empty ( $_POST [ 'id' ])) { $sql = &quot; INSERT INTO bookshelf (title, author) &quot; . &quot;VALUES ('{ $_POST ['title']}', '{ $_POST ['author']}')&quot; ; $dbh -> exec( $sql ); } else { $sql = &quot; UPDATE bookshelf SET title = '{ $_POST ['title']}', &quot; . &quot;author = '{ $_POST ['author']}' WHERE id = { $_POST ['id']}&quot; ; $dbh -> exec( $sql ); } header ( &quot;Location: /&quot; );
  • 38. process-book.php <?php if ( strtolower ( $_SERVER [ 'REQUEST_METHOD' ]) == 'get' ) { header ( &quot;Location: /&quot; ); } // Database connection code if ( empty ($_POST['id'])) { $sql = &quot; INSERT INTO bookshelf (title, author) &quot; . &quot;VALUES ('{$_POST['title']}', '{$_POST['author']}')&quot;; $dbh -> exec($sql); } else { $sql = &quot; UPDATE bookshelf SET title = '{$_POST['title']}', &quot; . &quot;author = '{$_POST['author']}' WHERE id = {$_POST['id']}&quot;; $dbh -> exec($sql); } header (&quot;Location: /&quot;);
  • 39. process-book.php <?php if ( strtolower ($_SERVER['REQUEST_METHOD']) == 'get') { header (&quot;Location: /&quot;); } // Database connection code if ( empty ($_POST['id'])) { $sql = &quot; INSERT INTO bookshelf (title, author) &quot; . &quot;VALUES ('{$_POST['title']}', '{$_POST['author']}')&quot;; $dbh -> exec($sql); } else { $sql = &quot; UPDATE bookshelf SET title = '{$_POST['title']}', &quot; . &quot;author = '{$_POST['author']}' WHERE id = {$_POST['id']}&quot;; $dbh -> exec($sql); } header (&quot;Location: /&quot;);
  • 40. process-book.php <?php if ( strtolower ($_SERVER['REQUEST_METHOD']) == 'get') { header (&quot;Location: /&quot;); } // Database connection code if ( empty ( $_POST [ 'id' ])) { $sql = &quot; INSERT INTO bookshelf (title, author) &quot; . &quot;VALUES ('{ $_POST ['title']}', '{ $_POST ['author']}')&quot; ; $dbh -> exec( $sql ); } else { $sql = &quot; UPDATE bookshelf SET title = '{$_POST['title']}', &quot; . &quot;author = '{$_POST['author']}' WHERE id = {$_POST['id']}&quot;; $dbh -> exec($sql); } header (&quot;Location: /&quot;);
  • 41. process-book.php <?php if ( strtolower ($_SERVER['REQUEST_METHOD']) == 'get') { header (&quot;Location: /&quot;); } // Database connection code if ( empty ($_POST['id'])) { $sql = &quot; INSERT INTO bookshelf (title, author) &quot; . &quot;VALUES ('{$_POST['title']}', '{$_POST['author']}')&quot;; $dbh -> exec($sql); } else { $sql = &quot; UPDATE bookshelf SET title = '{ $_POST ['title']}', &quot; . &quot;author = '{ $_POST ['author']}' WHERE id = { $_POST ['id']}&quot; ; $dbh -> exec( $sql ); } header (&quot;Location: /&quot;);
  • 42. process-book.php <?php if ( strtolower ($_SERVER['REQUEST_METHOD']) == 'get') { header (&quot;Location: /&quot;); } // Database connection code if ( empty ($_POST['id'])) { $sql = &quot; INSERT INTO bookshelf (title, author) &quot; . &quot;VALUES ('{$_POST['title']}', '{$_POST['author']}')&quot;; $dbh -> exec($sql); } else { $sql = &quot; UPDATE bookshelf SET title = '{$_POST['title']}', &quot; . &quot;author = '{$_POST['author']}' WHERE id = {$_POST['id']}&quot;; $dbh -> exec($sql); } header ( &quot;Location: /&quot; );
  • 43. Glaring Problems?
    • Code duplication
    • 44. Input isn't filtered
    • 45. Output isn't escaped
    • 46. User-provided data used in SQL
  • 47. Code Duplication try { $dbh = new PDO ( $dsn , null , null , $options ); } catch ( PDOException $e ) { echo &quot;Error!: &quot; . $e -> getMessage() . &quot;<br /> n &quot; ; die (); }
    • Violates DRY principle
    • 48. Maintenance nightmare
    • 49. The next developer will want to kill you
    • 50. And your family
    • 51. And your pets
  • 52. Consolidate
      Let's throw all that duplicated code into an include file, say library/base.php . (We'll add a few other handy items while we're in there)
  • 53. base.php <?php date_default_timezone_set ('America/Chicago'); error_reporting ( - 1); ini_set ('display_errors', 1); ini_set ('display_startup_errors', 1); $db = realpath ( dirname ( __FILE__ ) . '/../data/db/bookshelf.db' ); $dsn = &quot;sqlite: $db &quot; ; $options = array ( PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION , PDO :: ATTR_DEFAULT_FETCH_MODE => PDO :: FETCH_ASSOC ); try { $dbh = new PDO ( $dsn , null , null , $options ); } catch ( PDOException $e ) { throw $e ; echo &quot;Error!: &quot; . $e -> getMessage() . &quot;<br /> n &quot; ; die (); }
  • 54. base.php <?php date_default_timezone_set ( 'America/Chicago' ); error_reporting ( - 1); ini_set ('display_errors', 1); ini_set ('display_startup_errors', 1); $db = realpath ( dirname ( __FILE__ ) . '/../data/db/bookshelf.db'); $dsn = &quot;sqlite:$db&quot;; $options = array ( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION , PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ); try { $dbh = new PDO ($dsn, null , null , $options); } catch ( PDOException $e) { throw $e; echo &quot;Error!: &quot; . $e -> getMessage() . &quot;<br />n&quot;; die (); }
  • 55. base.php <?php date_default_timezone_set ('America/Chicago'); error_reporting ( - 1 ); ini_set ( 'display_errors' , 1 ); ini_set ( 'display_startup_errors' , 1 ); $db = realpath ( dirname ( __FILE__ ) . '/../data/db/bookshelf.db'); $dsn = &quot;sqlite:$db&quot;; $options = array ( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION , PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ); try { $dbh = new PDO ($dsn, null , null , $options); } catch ( PDOException $e) { throw $e; echo &quot;Error!: &quot; . $e -> getMessage() . &quot;<br />n&quot;; die (); }
  • 56. Replace db info with base.php
      Remove the db connection code from each file with:
    require_once dirname ( __FILE__ ) . '/library/base.php' ;
  • 57. Duplication removed, but . . .
  • 58.  
  • 59. We echo $title and $author < form method=&quot;post&quot; action=&quot;process-book.php&quot;> < input type=&quot;hidden&quot; id=&quot;id&quot; name=&quot;id&quot; value=&quot;<?php echo $id; ?>&quot; /> < dl > < dt > < label for=&quot;title&quot;>Title</ label > </ dt > < dd > < input type=&quot;text&quot; id=&quot;title&quot; name=&quot;title&quot; value= &quot;<?php echo $title ; ?>&quot; /> </ dd > < dt > < label for=&quot;author&quot;>Author</ label > </ dt > < dd > < input type=&quot;text&quot; id=&quot;author&quot; name=&quot;author&quot; value= &quot;<?php echo $author ; ?>&quot; /> </ dd > < dt > &nbsp; </ dt > < dd > < input type=&quot;submit&quot; value=&quot;Submit&quot; /> </ dd > </ dl > </ form >
  • 60. Without defining $title and $author require_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' ]; }
  • 61. Super easy to fix require_once dirname ( __FILE__ ) . '/library/base.php'; $id = empty ($_GET['id']) ? null : $_GET['id']; $title = null ; $author = null ; if ($id) { $book = $dbh -> query( . . . ) -> fetch(); $title = $book['title']; $author = $book['author']; }
  • 62. FIEO
    • Filter input
      • Your users want to destroy your app
      • 63. Prevent SQL injection
    • Escape output
      • Or just destroy your app yourself
      • 64. Defend against XSS
  • 65. http://xkcd.com/327/
  • 66. book-form.php
      Use filter_input() to guarantee $id is either false or an int.
    $id = filter_input ( INPUT_GET , 'id' , FILTER_VALIDATE_INT );
  • 67. process-book.php
      More 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 );
  • 68. index.php
      Use htmlspecialchars() to escape output
    <?php foreach ($books as $book): ?> < tr > < td > < a href=&quot;book-form.php?id=<?php echo $book['id']; ?>&quot;> <?php echo htmlspecialchars ( $book [ 'title' ], ENT_COMPAT , 'UTF-8' ); ?> </ a > </ td > < td > <?php echo htmlspecialchars ( $book [ 'author' ], ENT_COMPAT , 'UTF-8' ); ?> </ td > </ tr > <?php endforeach ; ?>
  • 69. Prepared Statements
    • More efficient (in some cases)
    • 70. Help protect against SQL injection
    • 71. Easier to read and maintain
  • 72. book-form.php: Before $book = $dbh -> query( &quot; SELECT title, author FROM bookshelf WHERE id = $id &quot; ) -> fetch();
  • 73. book-form.php: After $statement = $dbh -> prepare( ' SELECT title, author FROM bookshelf WHERE id = :id' ); $statement -> bindParam( ':id' , $id ); $statement -> execute(); $book = $statement -> fetch();
  • 74. book-form.php: After $statement = $dbh -> prepare (' SELECT title, author FROM bookshelf WHERE id = :id'); $statement -> bindParam(':id', $id); $statement -> execute(); $book = $statement -> fetch();
  • 75. book-form.php: After $statement = $dbh -> prepare( ' SELECT title, author FROM bookshelf WHERE id = :id' ); $statement -> bindParam(':id', $id); $statement -> execute(); $book = $statement -> fetch();
  • 76. book-form.php: After $statement = $dbh -> prepare(' SELECT title, author FROM bookshelf WHERE id = :id'); $statement -> bindParam( ':id' , $id ); $statement -> execute(); $book = $statement -> fetch();
  • 77. book-form.php: After $statement = $dbh -> prepare(' SELECT title, author FROM bookshelf WHERE id = :id'); $statement -> bindParam(':id', $id); $statement -> execute(); $book = $statement -> fetch();
  • 78. process-book.php: Before if ( empty ( $_POST [ 'id' ])) { $sql = &quot; INSERT INTO bookshelf (title, author) &quot; . &quot;VALUES ('{ $_POST ['title']}', '{ $_POST ['author']}')&quot; ; $dbh -> exec( $sql ); } else { $sql = &quot; UPDATE bookshelf SET title = '{ $_POST ['title']}', &quot; . &quot;author = '{ $_POST ['author']}' WHERE id = { $_POST ['id']}&quot; ; $dbh -> exec( $sql ); }
  • 79. process-book.php: After if ( $id ) { $statement = $dbh -> prepare( &quot; UPDATE bookshelf SET title = :title, author = :author WHERE id = :id&quot; ); $statement -> bindParam( ':title' , $title ); $statement -> bindParam( ':author' , $author ); $statement -> bindParam( ':id' , $id ); $statement -> execute(); } else { $statement = $dbh -> prepare( &quot; INSERT INTO bookshelf (title, author) VALUES (:title, :author)&quot; ); $statement -> bindParam( ':title' , $title ); $statement -> bindParam( ':author' , $author ); $statement -> execute(); }
  • 80. process-book.php: After if ( $id ) { $statement = $dbh -> prepare( &quot; UPDATE bookshelf SET title = :title, author = :author WHERE id = :id&quot; ); $statement -> bindParam(':title', $title); $statement -> bindParam(':author', $author); $statement -> bindParam(':id', $id); $statement -> execute(); } else { $statement = $dbh -> prepare( &quot; INSERT INTO bookshelf (title, author) VALUES (:title, :author)&quot; ); $statement -> bindParam(':title', $title); $statement -> bindParam(':author', $author); $statement -> execute(); }
  • 81. process-book.php: After if ($id) { $statement = $dbh -> prepare(&quot; UPDATE bookshelf SET title = :title, author = :author WHERE id = :id&quot;); $statement -> bindParam( ':title' , $title ); $statement -> bindParam( ':author' , $author ); $statement -> bindParam( ':id' , $id ); $statement -> execute(); } else { $statement = $dbh -> prepare(&quot; INSERT INTO bookshelf (title, author) VALUES (:title, :author)&quot;); $statement -> bindParam( ':title' , $title ); $statement -> bindParam( ':author' , $author ); $statement -> execute(); }
  • 82. process-book.php: After if ($id) { $statement = $dbh -> prepare(&quot; UPDATE bookshelf SET title = :title, author = :author WHERE id = :id&quot;); $statement -> bindParam(':title', $title); $statement -> bindParam(':author', $author); $statement -> bindParam(':id', $id); $statement -> execute(); } else { $statement = $dbh -> prepare(&quot; INSERT INTO bookshelf (title, author) VALUES (:title, :author)&quot;); $statement -> bindParam(':title', $title); $statement -> bindParam(':author', $author); $statement -> execute(); }
  • 83. Let's add a Service Layer
    • Yes, Virginia, PHP has a full object model
    • 84. Object model rewritten for PHP 5
    • 85. Unused in this app (besides PDO)
  • 86. Why?
    • Introduce some OOP principles
    • 87. Take advantage of namespaces
    • 88. High ROI
      • Maintainability
      • 89. Testability
  • 90. What? Where?
    • Class name: BookshelfService
    • 91. Namespace: BookshelfService
    • 92. Location: library/Bookshelf/Service
    • 93. Filename: BookshelfService.php
    • 94. PSR-0 compliant
    • 95. (The class name should mirror its location in the file system)
  • 96. BookshelfService.php namespace BookshelfService; class BookshelfService { private $_dbh ; public function __construct ( PDO $dbh ) { } public function find ( $id ) { } public function findAll () { } public function save ( array $options ) { } }
  • 97. BookshelfService.php private $_dbh ; public function __construct ( PDO $dbh ) { $this -> _dbh = $dbh ; }
  • 98. BookshelfService.php public 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(); }
  • 99. BookshelfService.php public function save ( array $options ) { if ( $options [ 'id' ]) { $statement = $this -> _dbh -> prepare( &quot; UPDATE bookshelf SET title = :title, author = :author WHERE id = :id&quot; ); $statement -> execute( $options ); } else { unset ( $options [ 'id' ]); $statement = $this -> _dbh -> prepare( &quot; INSERT INTO bookshelf (title, author) VALUES (:title, :author)&quot; ); $statement -> execute( $options ); } }
  • 100. A New Class Means . . .
      . . . we need to add a few things to base.php.
  • 101. base.php set_include_path ( implode ( PATH_SEPARATOR , array ( get_include_path (), dirname ( __FILE__ ) ) ) );
  • 102. base.php function autoload ( $className ) { $className = ltrim ( $className , ' ' ); $fileName = '' ; $namespace = '' ; if ( $lastNsPos = strripos ( $className , ' ' )) { $namespace = substr ( $className , 0 , $lastNsPos ); $className = substr ( $className , $lastNsPos + 1 ); $fileName = str_replace ( ' ' , DIRECTORY_SEPARATOR , $namespace ) . DIRECTORY_SEPARATOR ; } $fileName .= str_replace ( '_' , DIRECTORY_SEPARATOR , $className ) . '.php' ; require $fileName ; } spl_autoload_register ( 'autoload' );
  • 103. base.php // database connection code $bookshelf = new BookshelfService BookshelfService ( $dbh );
  • 104. And now for some before and after shots . . .
  • 105. index.php: Before <?php require_once dirname ( __FILE__ ) . '/library/base.php' ; $books = $dbh -> query( &quot; SELECT * FROM bookshelf ORDER BY title&quot; ) -> fetchAll(); ?>
  • 106. index.php: After <?php require_once dirname ( __FILE__ ) . '/library/base.php' ; $books = $bookshelf -> findAll(); ?>
  • 107. book-form.php: Before if ( $id ) { $statement = $dbh -> prepare( ' SELECT title, author FROM bookshelf WHERE id = :id' ); $statement -> bindParam( ':id' , $id ); $statement -> execute(); $book = $statement -> fetch(); $title = $book['title']; $author = $book['author']; }
  • 108. book-form.php: After if ( $id ) { $book = $bookshelf -> find( $id ); $title = $book['title']; $author = $book['author']; }
  • 109. process-book.php: Before if ( $id ) { $statement = $dbh -> prepare( &quot; UPDATE bookshelf SET title = :title, author = :author WHERE id = :id&quot; ); $statement -> bindParam( ':title' , $title ); $statement -> bindParam( ':author' , $author ); $statement -> bindParam( ':id' , $id ); $statement -> execute(); } else { $statement = $dbh -> prepare( &quot; INSERT INTO bookshelf (title, author) VALUES (:title, :author)&quot; ); $statement -> bindParam( ':title' , $title ); $statement -> bindParam( ':author' , $author ); $statement -> execute(); }
  • 110. process-book.php: After $book = array ( 'id' => $id , 'title' => $title , 'author' => $author ); $bookshelf -> save( $book );
  • 111. What Have We Done?
    • Put together a nice first application
    • 112. Made and corrected some typical mistakes
    • 113. Seen some nice features of PHP
    • Learned something?
  • 117. Want More?
    • Presentation source code: https://github.com/jeremykendall/bookshelf
    • 118. PHP Manual: http://www.php.net/manual/en/index.php
    • 119. CSI: PHP http://csiphp.com
    • 120. PHP 101 Suggestions: http://csiphp.com/blog/2011/07/19/stop-doing-it-wrong-and-learn-to-code-good-too/
    • 121. PHP 101: PHP for the Absolute Beginner: http://devzone.zend.com/6/php-101-php-for-the-absolute-beginner/
    • 122. PHPDeveloper.org http://phpdeveloper.org/
    • 123. php|architect: http://www.phparch.com/
  • 124. Thanks!
      Jeremy Kendall http://about.me/jeremykendall [email_address]

×