SlideShare uma empresa Scribd logo
1 de 81
PHP 102 Out with the Bad, In with the Good Memphis PHP, February 23, 2012
Who is this guy? ,[object Object]
Following Along ,[object Object]
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 ,[object Object]
Make horrible mistakes
Correct those mistakes
Make a cool improvement
Learn something?
What's the Problem? ,[object Object]
Requirements: ,[object Object]
Add books
Edit books
Delete books ,[object Object]
So, What do we Need? ,[object Object]
View (index.php)
Form (book-form.php)
Form processor (process-form.php)
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 />  &quot; ;   die (); } $books   =   $dbh -> query( &quot; SELECT   *   FROM  bookshelf  ORDER BY  title&quot; ) -> fetchAll();
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 />&quot;;   die (); } $books   =   $dbh -> query(&quot; SELECT   *   FROM  bookshelf  ORDER BY  title&quot;) -> fetchAll() ;
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 />  &quot; ;   die (); } $books   =   $dbh -> query(&quot; SELECT   *   FROM  bookshelf  ORDER BY  title&quot;) -> fetchAll();
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 />&quot;;   die (); } $books   =   $dbh -> query( &quot; SELECT   *   FROM  bookshelf  ORDER BY  title&quot; ) -> fetchAll();
index.php
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 ;   ?>
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 ; ?>
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 ; ?>
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 ;   ?>
 
book-form.php
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' ]; }
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']; }
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' ]; }
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 >
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 >
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; );
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;);
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;);
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;);
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;);
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; );
Glaring Problems? ,[object Object]
Input isn't filtered
Output isn't escaped
User-provided data used in SQL
Code Duplication try  {   $dbh   =   new   PDO ( $dsn ,  null ,  null ,  $options ); }  catch  ( PDOException   $e ) {   echo   &quot;Error!: &quot;   .   $e -> getMessage()  .   &quot;<br />  &quot; ;   die (); } ,[object Object]
Maintenance nightmare
The next developer will want to kill you
And your family
And your pets
Consolidate ,[object Object]
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 />  &quot; ;   die (); }
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 />&quot;;   die (); }
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 />&quot;;   die (); }
Replace db info with base.php ,[object Object],require_once   dirname ( __FILE__ )   .   '/library/base.php' ;
Duplication removed, but . . .
 
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 >
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' ]; }
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']; }
FIEO ,[object Object]
Prevent SQL injection ,[object Object],[object Object]
Defend against XSS
http://xkcd.com/327/
book-form.php ,[object Object],$id   =   filter_input ( INPUT_GET ,  'id' ,  FILTER_VALIDATE_INT );
process-book.php ,[object Object],$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 );
index.php ,[object Object],<?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 ; ?>
Prepared Statements ,[object Object]
Help protect against SQL injection
Easier to read and maintain
book-form.php: Before $book   =   $dbh -> query( &quot; SELECT  title, author  FROM  bookshelf  WHERE  id  =   $id &quot; ) -> fetch();
book-form.php: After $statement   =   $dbh -> prepare( ' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id' ); $statement -> bindParam( ':id' ,  $id ); $statement -> execute(); $book   =   $statement -> fetch();
book-form.php: After $statement   =   $dbh -> prepare (' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id'); $statement -> bindParam(':id', $id); $statement -> execute(); $book  =  $statement -> fetch();
book-form.php: After $statement  =  $dbh -> prepare( ' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id' ); $statement -> bindParam(':id', $id); $statement -> execute(); $book  =  $statement -> fetch();
book-form.php: After $statement  =  $dbh -> prepare(' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id'); $statement -> bindParam( ':id' ,  $id ); $statement -> execute(); $book  =  $statement -> fetch();
book-form.php: After $statement  =  $dbh -> prepare(' SELECT  title, author  FROM  bookshelf  WHERE  id  =  :id'); $statement -> bindParam(':id', $id); $statement -> execute(); $book   =   $statement -> fetch();
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 ); }
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(); }
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(); }
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(); }

Mais conteúdo relacionado

Mais procurados

What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?brynary
 
Practical PHP by example Jan Leth-Kjaer
Practical PHP by example   Jan Leth-KjaerPractical PHP by example   Jan Leth-Kjaer
Practical PHP by example Jan Leth-KjaerCOMMON Europe
 
Jquery presentation
Jquery presentationJquery presentation
Jquery presentationguest5d87aa6
 
Web Development Course: PHP lecture 2
Web Development Course: PHP lecture 2Web Development Course: PHP lecture 2
Web Development Course: PHP lecture 2Gheyath M. Othman
 
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018Balázs Tatár
 
Evolution of API With Blogging
Evolution of API With BloggingEvolution of API With Blogging
Evolution of API With BloggingTakatsugu Shigeta
 
Let's write secure Drupal code! - Drupal Camp Poland 2019
Let's write secure Drupal code! - Drupal Camp Poland 2019Let's write secure Drupal code! - Drupal Camp Poland 2019
Let's write secure Drupal code! - Drupal Camp Poland 2019Balázs Tatár
 
Xslate sv perl-2013-7-11
Xslate sv perl-2013-7-11Xslate sv perl-2013-7-11
Xslate sv perl-2013-7-11Goro Fuji
 
2014 database - course 2 - php
2014 database - course 2 - php2014 database - course 2 - php
2014 database - course 2 - phpHung-yu Lin
 
Php Tutorial | Introduction Demo | Basics
 Php Tutorial | Introduction Demo | Basics Php Tutorial | Introduction Demo | Basics
Php Tutorial | Introduction Demo | BasicsShubham Kumar Singh
 
HTML Templates Using Clear Silver
HTML Templates Using Clear SilverHTML Templates Using Clear Silver
HTML Templates Using Clear SilverPaulWay
 
Introducation to php for beginners
Introducation to php for beginners Introducation to php for beginners
Introducation to php for beginners musrath mohammad
 
Let's write secure Drupal code! DUG Belgium - 08/08/2019
Let's write secure Drupal code! DUG Belgium - 08/08/2019Let's write secure Drupal code! DUG Belgium - 08/08/2019
Let's write secure Drupal code! DUG Belgium - 08/08/2019Balázs Tatár
 
Getting started with MongoDB and PHP
Getting started with MongoDB and PHPGetting started with MongoDB and PHP
Getting started with MongoDB and PHPgates10gen
 
IPW HTML course
IPW HTML courseIPW HTML course
IPW HTML courseVlad Posea
 

Mais procurados (18)

Further Php
Further PhpFurther Php
Further Php
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?
 
Practical PHP by example Jan Leth-Kjaer
Practical PHP by example   Jan Leth-KjaerPractical PHP by example   Jan Leth-Kjaer
Practical PHP by example Jan Leth-Kjaer
 
Jquery presentation
Jquery presentationJquery presentation
Jquery presentation
 
Web Development Course: PHP lecture 2
Web Development Course: PHP lecture 2Web Development Course: PHP lecture 2
Web Development Course: PHP lecture 2
 
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018Let's write secure Drupal code! - DrupalCamp Oslo, 2018
Let's write secure Drupal code! - DrupalCamp Oslo, 2018
 
Ruby on discuz
Ruby on discuzRuby on discuz
Ruby on discuz
 
Evolution of API With Blogging
Evolution of API With BloggingEvolution of API With Blogging
Evolution of API With Blogging
 
Let's write secure Drupal code! - Drupal Camp Poland 2019
Let's write secure Drupal code! - Drupal Camp Poland 2019Let's write secure Drupal code! - Drupal Camp Poland 2019
Let's write secure Drupal code! - Drupal Camp Poland 2019
 
Xslate sv perl-2013-7-11
Xslate sv perl-2013-7-11Xslate sv perl-2013-7-11
Xslate sv perl-2013-7-11
 
Changing Template Engine
Changing Template EngineChanging Template Engine
Changing Template Engine
 
2014 database - course 2 - php
2014 database - course 2 - php2014 database - course 2 - php
2014 database - course 2 - php
 
Php Tutorial | Introduction Demo | Basics
 Php Tutorial | Introduction Demo | Basics Php Tutorial | Introduction Demo | Basics
Php Tutorial | Introduction Demo | Basics
 
HTML Templates Using Clear Silver
HTML Templates Using Clear SilverHTML Templates Using Clear Silver
HTML Templates Using Clear Silver
 
Introducation to php for beginners
Introducation to php for beginners Introducation to php for beginners
Introducation to php for beginners
 
Let's write secure Drupal code! DUG Belgium - 08/08/2019
Let's write secure Drupal code! DUG Belgium - 08/08/2019Let's write secure Drupal code! DUG Belgium - 08/08/2019
Let's write secure Drupal code! DUG Belgium - 08/08/2019
 
Getting started with MongoDB and PHP
Getting started with MongoDB and PHPGetting started with MongoDB and PHP
Getting started with MongoDB and PHP
 
IPW HTML course
IPW HTML courseIPW HTML course
IPW HTML course
 

Destaque

Tdd in php a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief exampleJeremy Kendall
 
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPJeremy Kendall
 
Php 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the GoodPhp 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the GoodJeremy Kendall
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data ObjectsWez Furlong
 
TDC SP 2015 - PHP7: melhor e mais rápido
TDC SP 2015 - PHP7: melhor e mais rápidoTDC SP 2015 - PHP7: melhor e mais rápido
TDC SP 2015 - PHP7: melhor e mais rápidoBruno Ricardo Siqueira
 

Destaque (7)

Tdd in php a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief example
 
Django a whirlwind tour
Django   a whirlwind tourDjango   a whirlwind tour
Django a whirlwind tour
 
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
 
Php 101: PDO
Php 101: PDOPhp 101: PDO
Php 101: PDO
 
Php 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the GoodPhp 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the Good
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data Objects
 
TDC SP 2015 - PHP7: melhor e mais rápido
TDC SP 2015 - PHP7: melhor e mais rápidoTDC SP 2015 - PHP7: melhor e mais rápido
TDC SP 2015 - PHP7: melhor e mais rápido
 

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

03 Php Array String Functions
03 Php Array String Functions03 Php Array String Functions
03 Php Array String FunctionsGeshan Manandhar
 
PHP Basics for Designers
PHP Basics for DesignersPHP Basics for Designers
PHP Basics for DesignersMatthew Turland
 
Php Calling Operators
Php Calling OperatorsPhp Calling Operators
Php Calling Operatorsmussawir20
 
Introduction To Lamp
Introduction To LampIntroduction To Lamp
Introduction To LampAmzad Hossain
 
Php Crash Course
Php Crash CoursePhp Crash Course
Php Crash Coursemussawir20
 
Php Training
Php TrainingPhp Training
Php Trainingadfa
 
Haml & Sass presentation
Haml & Sass presentationHaml & Sass presentation
Haml & Sass presentationbryanbibat
 
The Basics Of Page Creation
The Basics Of Page CreationThe Basics Of Page Creation
The Basics Of Page CreationWildan Maulana
 
Terms of endearment - the ElasticSearch Query DSL explained
Terms of endearment - the ElasticSearch Query DSL explainedTerms of endearment - the ElasticSearch Query DSL explained
Terms of endearment - the ElasticSearch Query DSL explainedclintongormley
 
Optimizing Drupal for Mobile Devices
Optimizing Drupal for Mobile DevicesOptimizing Drupal for Mobile Devices
Optimizing Drupal for Mobile DevicesSugree Phatanapherom
 
High-level Web Testing
High-level Web TestingHigh-level Web Testing
High-level Web Testingpetersergeant
 
Forum Presentation
Forum PresentationForum Presentation
Forum PresentationAngus Pratt
 

Semelhante a PHP 102: Out with the Bad, In with the Good (20)

03 Php Array String Functions
03 Php Array String Functions03 Php Array String Functions
03 Php Array String Functions
 
PHP Basics for Designers
PHP Basics for DesignersPHP Basics for Designers
PHP Basics for Designers
 
Php Calling Operators
Php Calling OperatorsPhp Calling Operators
Php Calling Operators
 
Introduction To Lamp
Introduction To LampIntroduction To Lamp
Introduction To Lamp
 
Web Scraping with PHP
Web Scraping with PHPWeb Scraping with PHP
Web Scraping with PHP
 
Php Crash Course
Php Crash CoursePhp Crash Course
Php Crash Course
 
Os Nixon
Os NixonOs Nixon
Os Nixon
 
Php
PhpPhp
Php
 
Php Sq Lite
Php Sq LitePhp Sq Lite
Php Sq Lite
 
Php Training
Php TrainingPhp Training
Php Training
 
Haml & Sass presentation
Haml & Sass presentationHaml & Sass presentation
Haml & Sass presentation
 
The Basics Of Page Creation
The Basics Of Page CreationThe Basics Of Page Creation
The Basics Of Page Creation
 
JQuery 101
JQuery 101JQuery 101
JQuery 101
 
JQuery Basics
JQuery BasicsJQuery Basics
JQuery Basics
 
Terms of endearment - the ElasticSearch Query DSL explained
Terms of endearment - the ElasticSearch Query DSL explainedTerms of endearment - the ElasticSearch Query DSL explained
Terms of endearment - the ElasticSearch Query DSL explained
 
Optimizing Drupal for Mobile Devices
Optimizing Drupal for Mobile DevicesOptimizing Drupal for Mobile Devices
Optimizing Drupal for Mobile Devices
 
Php My Sql
Php My SqlPhp My Sql
Php My Sql
 
High-level Web Testing
High-level Web TestingHigh-level Web Testing
High-level Web Testing
 
Forum Presentation
Forum PresentationForum Presentation
Forum Presentation
 
Ae internals
Ae internalsAe internals
Ae internals
 

Mais de Jeremy Kendall

Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPJeremy Kendall
 
5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) Code5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) CodeJeremy Kendall
 
Game Changing Dependency Management
Game Changing Dependency ManagementGame Changing Dependency Management
Game Changing Dependency ManagementJeremy Kendall
 
Keeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro frameworkKeeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro frameworkJeremy Kendall
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkJeremy Kendall
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkJeremy Kendall
 
TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25Jeremy Kendall
 
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_FormZend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_FormJeremy Kendall
 
Zero to ZF in 10 Minutes
Zero to ZF in 10 MinutesZero to ZF in 10 Minutes
Zero to ZF in 10 MinutesJeremy Kendall
 
A Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormA Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormJeremy Kendall
 
Zero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutesZero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutesJeremy Kendall
 

Mais de Jeremy Kendall (11)

Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
 
5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) Code5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) Code
 
Game Changing Dependency Management
Game Changing Dependency ManagementGame Changing Dependency Management
Game Changing Dependency Management
 
Keeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro frameworkKeeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro framework
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro framework
 
TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25
 
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_FormZend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
 
Zero to ZF in 10 Minutes
Zero to ZF in 10 MinutesZero to ZF in 10 Minutes
Zero to ZF in 10 Minutes
 
A Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormA Brief Introduction to Zend_Form
A Brief Introduction to Zend_Form
 
Zero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutesZero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutes
 

Último

Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demoHarshalMandlekar2
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 

Último (20)

Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demo
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 

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

  • 1. PHP 102 Out with the Bad, In with the Good Memphis PHP, February 23, 2012
  • 2.
  • 3.
  • 4. Why Did You Start Programming?
  • 5. I wanted to solve problems, but . . .
  • 6. . . . I frequently caused as many problems as I solved.
  • 7.
  • 10. Make a cool improvement
  • 12.
  • 13.
  • 16.
  • 17.
  • 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 /> &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 />&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 /> &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 />&quot;; die (); } $books = $dbh -> query( &quot; SELECT * FROM bookshelf ORDER BY title&quot; ) -> fetchAll();
  • 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.  
  • 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.
  • 47.
  • 49. The next developer will want to kill you
  • 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 /> &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 />&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 />&quot;; die (); }
  • 56.
  • 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.
  • 63.
  • 66.
  • 67.
  • 68.
  • 69.
  • 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.
  • 85. Unused in this app (besides PDO)
  • 86.
  • 87. Take advantage of namespaces
  • 88.
  • 90.
  • 95. (The class name should mirror its location in the file system)
  • 96. BookshelfService.php namespace Bookshelfervice; 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.
  • 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 BookshelferviceBookshelfService ( $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.
  • 112. Made and corrected some typical mistakes
  • 113.
  • 116.
  • 117.
  • 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/
  • 124.