PHP 102 Out with the Bad, In with the Good Memphis PHP, February 23, 2012
Who is this guy? <ul>Jeremy Kendall PHP developer since about 2001 Organizer of Memphis PHP Zend Framework  expert  fanboi...
Following Along <ul>You can follow along with the presentation code at github.com. http://git.io/eAUNow </ul>
Why Did You  Start Programming?
I wanted to solve problems, but . . .
. . . I frequently caused  as many problems  as I solved.
Let's Solve a Problem Together <ul><li>We'll create a typical first web application
Make horrible mistakes
Correct those mistakes
Make a cool improvement
Learn something? </li></ul>
What's the Problem? <ul><li>I want to create an application that stores and displays books from my bookshelf
Requirements: </li><ul><li>Display books
Add books
Edit books
Delete books </li></ul><li>Why no delete? I ran out of time and energy. </li></ul>
So, What do we Need? <ul><li>Database (sqlite)
View (index.php)
Form (book-form.php)
Form processor (process-form.php) </li></ul>
index.php – db connection <?php $db   =   realpath ( dirname ( __FILE__ )  .   '/data/db/bookshelf.db' ); $dsn   =   &quot...
index.php – db connection <?php $db   =   realpath ( dirname ( __FILE__ )  .   '/data/db/bookshelf.db' ); $dsn   =   &quot...
index.php – db connection <?php $db   =   realpath ( dirname ( __FILE__ )  .   '/data/db/bookshelf.db'); $dsn   =   &quot;...
index.php – db connection <?php $db   =   realpath ( dirname ( __FILE__ )  .   '/data/db/bookshelf.db'); $dsn   =   &quot;...
index.php
index.php – books table <?php  if  ( count ( $books )  >   0 ): ?>   < table >   < tr >   < th > Title </ th >< th > Autho...
index.php – books table <?php  if  ( count ( $books )  >   0 ): ?>   < table >   < tr >   < th > Title </ th >< th > Autho...
index.php – books table <?php  if  ( count ($books)  >  0): ?>   < table >   < tr >   < th >Title</ th >< th >Author</ th ...
index.php – books table <?php  if  ( count ($books)  >  0): ?>   < table >   < tr >   < th >Title</ th >< th >Author</ th ...
 
book-form.php
book-form.php <?php $id   =   empty ( $_GET [ 'id' ]) ?  null  :  $_GET [ 'id' ]; if  ( $id ) {   // Database connection c...
book-form.php <?php $id   =   empty ( $_GET [ 'id' ]) ?  null  :  $_GET [ 'id' ]; if  ($id) {   // Database connection cod...
book-form.php <?php $id  =   empty ($_GET['id']) ?  null  : $_GET['id']; if  ( $id ) {   // Database connection code   $bo...
book-form.php < form   method= &quot;post&quot;   action= &quot;process-book.php&quot; >   < input   type= &quot;hidden&qu...
book-form.php < form   method= &quot;post&quot;   action= &quot;process-book.php&quot; >   < input  type=&quot;hidden&quot...
process-book.php <?php if  ( strtolower ( $_SERVER [ 'REQUEST_METHOD' ])  ==   'get' ) {   header ( &quot;Location: /&quot...
process-book.php <?php if  ( strtolower ( $_SERVER [ 'REQUEST_METHOD' ])  ==   'get' ) {   header ( &quot;Location: /&quot...
process-book.php <?php if  ( strtolower ($_SERVER['REQUEST_METHOD'])  ==  'get') {   header (&quot;Location: /&quot;); } /...
process-book.php <?php if  ( strtolower ($_SERVER['REQUEST_METHOD'])  ==  'get') {   header (&quot;Location: /&quot;); } /...
process-book.php <?php if  ( strtolower ($_SERVER['REQUEST_METHOD'])  ==  'get') {   header (&quot;Location: /&quot;); } /...
process-book.php <?php if  ( strtolower ($_SERVER['REQUEST_METHOD'])  ==  'get') {   header (&quot;Location: /&quot;); } /...
Glaring Problems? <ul><li>Code duplication
Input isn't filtered
Output isn't escaped
User-provided data used in SQL </li></ul>
Code Duplication try  {   $dbh   =   new   PDO ( $dsn ,  null ,  null ,  $options ); }  catch  ( PDOException   $e ) {   e...
Maintenance nightmare
The next developer will want to kill you
And your family
And your pets </li></ul>
Consolidate <ul>Let's throw all that duplicated code into an include file, say  library/base.php . (We'll add a few other ...
base.php <?php date_default_timezone_set ('America/Chicago'); error_reporting ( - 1); ini_set ('display_errors', 1); ini_s...
base.php <?php date_default_timezone_set ( 'America/Chicago' ); error_reporting ( - 1); ini_set ('display_errors', 1); ini...
base.php <?php date_default_timezone_set ('America/Chicago'); error_reporting ( - 1 ); ini_set ( 'display_errors' ,  1 ); ...
Replace db info with base.php <ul>Remove the db connection code from each file with: </ul>require_once   dirname ( __FILE_...
Duplication removed, but . . .
 
We echo $title and $author < form  method=&quot;post&quot; action=&quot;process-book.php&quot;>   < input  type=&quot;hidd...
Without defining $title and $author require_once   dirname ( __FILE__ )   .   '/library/base.php' ; $id   =   empty ( $_GE...
Super easy to fix require_once   dirname ( __FILE__ )  .  '/library/base.php'; $id  =   empty ($_GET['id']) ?  null  : $_G...
FIEO <ul><li>Filter input </li><ul><li>Your users want to destroy your app
Prevent SQL injection </li></ul><li>Escape output </li><ul><li>Or just destroy your app yourself
Defend against XSS </li></ul></ul>
http://xkcd.com/327/
book-form.php <ul>Use  filter_input()  to guarantee  $id  is either false or an int. </ul>$id   =   filter_input ( INPUT_G...
process-book.php <ul>More  filter_input() </ul>$id   =   filter_input ( INPUT_POST ,  'id' ,  FILTER_VALIDATE_INT ); $titl...
index.php <ul>Use  htmlspecialchars()  to escape output </ul><?php  foreach  ($books  as  $book): ?> < tr >   < td >   < a...
Prepared Statements <ul><li>More efficient (in some cases)
Help protect against SQL injection
Easier to read and maintain </li></ul>
book-form.php: Before $book   =   $dbh -> query( &quot; SELECT  title, author  FROM  bookshelf  WHERE  id  =   $id &quot; ...
book-form.php: After $statement   =   $dbh -> prepare( ' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id' ); $st...
book-form.php: After $statement   =   $dbh -> prepare (' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id'); $sta...
book-form.php: After $statement  =  $dbh -> prepare( ' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id' ); $stat...
book-form.php: After $statement  =  $dbh -> prepare(' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id'); $statem...
book-form.php: After $statement  =  $dbh -> prepare(' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id'); $statem...
process-book.php: Before if  ( empty ( $_POST [ 'id' ])) {   $sql   =   &quot; INSERT INTO  bookshelf (title, author) &quo...
process-book.php: After if  ( $id ) {   $statement   =   $dbh -> prepare( &quot; UPDATE  bookshelf  SET  title  =  :title,...
process-book.php: After if  ( $id ) {   $statement   =   $dbh -> prepare( &quot; UPDATE  bookshelf  SET  title  =  :title,...
process-book.php: After if  ($id) {   $statement  =  $dbh -> prepare(&quot; UPDATE  bookshelf  SET  title  =  :title, auth...
Upcoming SlideShare
Loading in...5
×

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

3,572

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 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
3,572
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
15
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

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

  1. 1. PHP 102 Out with the Bad, In with the Good Memphis PHP, February 23, 2012
  2. 2. Who is this guy? <ul>Jeremy Kendall PHP developer since about 2001 Organizer of Memphis PHP Zend Framework expert fanboi Speaker Photographer Sometime blogger Sometime open source contributor </ul>
  3. 3. Following Along <ul>You can follow along with the presentation code at github.com. http://git.io/eAUNow </ul>
  4. 4. Why Did You Start Programming?
  5. 5. I wanted to solve problems, but . . .
  6. 6. . . . I frequently caused as many problems as I solved.
  7. 7. Let's Solve a Problem Together <ul><li>We'll create a typical first web application
  8. 8. Make horrible mistakes
  9. 9. Correct those mistakes
  10. 10. Make a cool improvement
  11. 11. Learn something? </li></ul>
  12. 12. What's the Problem? <ul><li>I want to create an application that stores and displays books from my bookshelf
  13. 13. Requirements: </li><ul><li>Display books
  14. 14. Add books
  15. 15. Edit books
  16. 16. Delete books </li></ul><li>Why no delete? I ran out of time and energy. </li></ul>
  17. 17. So, What do we Need? <ul><li>Database (sqlite)
  18. 18. View (index.php)
  19. 19. Form (book-form.php)
  20. 20. Form processor (process-form.php) </li></ul>
  21. 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. 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. 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. 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. 25. index.php
  26. 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. 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. 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. 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
  31. 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' ]; }
  32. 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']; }
  33. 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' ]; }
  34. 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 >
  35. 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 >
  36. 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; );
  37. 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;);
  38. 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;);
  39. 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;);
  40. 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;);
  41. 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; );
  42. 43. Glaring Problems? <ul><li>Code duplication
  43. 44. Input isn't filtered
  44. 45. Output isn't escaped
  45. 46. User-provided data used in SQL </li></ul>
  46. 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 (); } <ul><li>Violates DRY principle
  47. 48. Maintenance nightmare
  48. 49. The next developer will want to kill you
  49. 50. And your family
  50. 51. And your pets </li></ul>
  51. 52. Consolidate <ul>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) </ul>
  52. 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 (); }
  53. 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 (); }
  54. 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 (); }
  55. 56. Replace db info with base.php <ul>Remove the db connection code from each file with: </ul>require_once dirname ( __FILE__ ) . '/library/base.php' ;
  56. 57. Duplication removed, but . . .
  57. 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 >
  58. 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' ]; }
  59. 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']; }
  60. 62. FIEO <ul><li>Filter input </li><ul><li>Your users want to destroy your app
  61. 63. Prevent SQL injection </li></ul><li>Escape output </li><ul><li>Or just destroy your app yourself
  62. 64. Defend against XSS </li></ul></ul>
  63. 65. http://xkcd.com/327/
  64. 66. book-form.php <ul>Use filter_input() to guarantee $id is either false or an int. </ul>$id = filter_input ( INPUT_GET , 'id' , FILTER_VALIDATE_INT );
  65. 67. process-book.php <ul>More filter_input() </ul>$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 );
  66. 68. index.php <ul>Use htmlspecialchars() to escape output </ul><?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 ; ?>
  67. 69. Prepared Statements <ul><li>More efficient (in some cases)
  68. 70. Help protect against SQL injection
  69. 71. Easier to read and maintain </li></ul>
  70. 72. book-form.php: Before $book = $dbh -> query( &quot; SELECT title, author FROM bookshelf WHERE id = $id &quot; ) -> fetch();
  71. 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();
  72. 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();
  73. 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();
  74. 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();
  75. 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();
  76. 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 ); }
  77. 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(); }
  78. 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(); }
  79. 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(); }
  80. 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(); }
  81. 83. Let's add a Service Layer <ul><li>Yes, Virginia, PHP has a full object model
  82. 84. Object model rewritten for PHP 5
  83. 85. Unused in this app (besides PDO) </li></ul>
  84. 86. Why? <ul><li>Introduce some OOP principles
  85. 87. Take advantage of namespaces
  86. 88. High ROI </li><ul><li>Maintainability
  87. 89. Testability </li></ul></ul>
  88. 90. What? Where? <ul><li>Class name: BookshelfService
  89. 91. Namespace: BookshelfService
  90. 92. Location: library/Bookshelf/Service
  91. 93. Filename: BookshelfService.php
  92. 94. PSR-0 compliant
  93. 95. (The class name should mirror its location in the file system) </li></ul>
  94. 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 ) { } }
  95. 97. BookshelfService.php private $_dbh ; public function __construct ( PDO $dbh ) { $this -> _dbh = $dbh ; }
  96. 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(); }
  97. 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 ); } }
  98. 100. A New Class Means . . . <ul>. . . we need to add a few things to base.php. </ul>
  99. 101. base.php set_include_path ( implode ( PATH_SEPARATOR , array ( get_include_path (), dirname ( __FILE__ ) ) ) );
  100. 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' );
  101. 103. base.php // database connection code $bookshelf = new BookshelfService BookshelfService ( $dbh );
  102. 104. And now for some before and after shots . . .
  103. 105. index.php: Before <?php require_once dirname ( __FILE__ ) . '/library/base.php' ; $books = $dbh -> query( &quot; SELECT * FROM bookshelf ORDER BY title&quot; ) -> fetchAll(); ?>
  104. 106. index.php: After <?php require_once dirname ( __FILE__ ) . '/library/base.php' ; $books = $bookshelf -> findAll(); ?>
  105. 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']; }
  106. 108. book-form.php: After if ( $id ) { $book = $bookshelf -> find( $id ); $title = $book['title']; $author = $book['author']; }
  107. 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(); }
  108. 110. process-book.php: After $book = array ( 'id' => $id , 'title' => $title , 'author' => $author ); $bookshelf -> save( $book );
  109. 111. What Have We Done? <ul><li>Put together a nice first application
  110. 112. Made and corrected some typical mistakes
  111. 113. Seen some nice features of PHP </li><ul><li>PDO
  112. 114. Filtering and escaping
  113. 115. Object model
  114. 116. Namespaces </li></ul><li>Learned something? </li></ul>
  115. 117. Want More? <ul><li>Presentation source code: https://github.com/jeremykendall/bookshelf
  116. 118. PHP Manual: http://www.php.net/manual/en/index.php
  117. 119. CSI: PHP http://csiphp.com
  118. 120. PHP 101 Suggestions: http://csiphp.com/blog/2011/07/19/stop-doing-it-wrong-and-learn-to-code-good-too/
  119. 121. PHP 101: PHP for the Absolute Beginner: http://devzone.zend.com/6/php-101-php-for-the-absolute-beginner/
  120. 122. PHPDeveloper.org http://phpdeveloper.org/
  121. 123. php|architect: http://www.phparch.com/ </li></ul>
  122. 124. Thanks! <ul>Jeremy Kendall http://about.me/jeremykendall [email_address] </ul>
  1. A particular slide catching your eye?

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

×