SlideShare a Scribd company logo
PHP 102
Out with the Bad,
In with the Good
php[tek] 2013
Who is this guy?
Jeremy Kendall
I love to code
I love to take pictures
I'm terribly forgetful
I work at OpenSky
Following Along
You can follow along with the presentation
code at github.com.
https://github.com/jeremykendall/bookshelf
Before = oh-the-horror
After = much-better
Why Did You
Start Programming?
I wanted to solve problems, but . . .
. . . I frequently caused
as many problems
as I solved.
“Always code as if the person who ends up
maintaining your code is a violent psychopath
who knows where you live.”
http://c2.com/cgi/wiki?CodeForTheMaintainer
“Alternatively, always code and comment in such
a way that if someone a few notches junior picks
up the code, they will take pleasure in reading
and learning from it.”
http://c2.com/cgi/wiki?CodeForTheMaintainer
Let's Solve a Problem Together
• We'll review a “typical” PHP application
• Find horrible mistakes
• Correct those mistakes
• Make a cool improvement
• Learn and apply better practices
Typical application?
• I've seen code similar to the samples I'll show
in almost every app I've ever touched.
• I've made the same mistakes in almost every
app I wrote in my early days.
• I'm guessing you've seen and made similar
mistakes too.
What's the Problem?
• Crappy application
• CRUD bookshelf
• What does it do?:
– Display books
– Add books
– Edit books
– Delete books
What's There?
• Database (MySQL)
• View (index.php)
• Form (book-form.php)
• Form processor (process-form.php)
• Delete (delete-book.php)
index.php
book-form.php
index.php – db connection
$db = mysql_connect('localhost', 'testuser', 'testpass');
mysql_select_db('bookshelf', $db);
$query = "SELECT * FROM bookshelf ORDER BY title";
$result = mysql_query($query);
index.php – db connection
$db = mysql_connect('localhost', 'testuser', 'testpass');
mysql_select_db('bookshelf', $db);
$query = "SELECT * FROM bookshelf ORDER BY title";
$result = mysql_query($query);
index.php – db connection
$db = mysql_connect('localhost', 'testuser', 'testpass');
mysql_select_db('bookshelf', $db);
$query = "SELECT * FROM bookshelf ORDER BY title";
$result = mysql_query($query);
index.php – db connection
$db = mysql_connect('localhost', 'testuser', 'testpass');
mysql_select_db('bookshelf', $db);
$query = "SELECT * FROM bookshelf ORDER BY title";
$result = mysql_query($query);
index.php – books table
<?php while ($book = mysql_fetch_assoc($result)): ?>
<tr>
<td>
<a href="book-form.php?id=<?php echo $book['id']; ?>">
<?php echo $book['title']; ?>
</a>
</td>
<td>
<?php echo $book['author']; ?>
</td>
</tr>
<?php endwhile; ?>
index.php – books table
<?php while ($book = mysql_fetch_assoc($result)): ?>
<tr>
<td>
<a href="book-form.php?id=<?php echo $book['id']; ?>">
<?php echo $book['title']; ?>
</a>
</td>
<td>
<?php echo $book['author']; ?>
</td>
</tr>
<?php endwhile; ?>
book-form.php
// Database connection code
$id = empty($_GET['id']) ? null : $_GET['id'];
if ($id) {
// Database connection code
$query = "SELECT title, author "
. "FROM bookshelf WHERE id = $id";
$result = mysql_query($query);
$book = mysql_fetch_assoc($result);
$title = $book['title'];
$author = $book['author'];
}
book-form.php
$id = empty($_GET['id']) ? null : $_GET['id'];
if ($id) {
// Database connection code
$query = "SELECT title, author "
. "FROM bookshelf WHERE id = $id";
$result = mysql_query($query);
$book = mysql_fetch_assoc($result);
$title = $book['title'];
$author = $book['author'];
}
book-form.php
$id = empty($_GET['id']) ? null : $_GET['id'];
if ($id) {
// Database connection code
$query = "SELECT title, author "
. "FROM bookshelf WHERE id = $id";
$result = mysql_query($query);
$book = mysql_fetch_assoc($result);
$title = $book['title'];
$author = $book['author'];
}
book-form.php
<input type="hidden" name="id" value="<?php echo $id; ?>" />
<input type="text" name="title" value="<?php echo $title; ?>" />
<input type="text" name="author" value="<?php echo $author; ?>" />
book-form.php
<input type="hidden" name="id" value="<?php echo $id; ?>" />
<input type="text" name="title" value="<?php echo $title; ?>" />
<input type="text" name="author" value="<?php echo $author; ?>" />
process-book.php
// Database connection code
$ins = "INSERT INTO bookshelf (title, author) "
."VALUES ('{$_POST['title']}', '{$_POST['author']}')";
$up = "UPDATE bookshelf SET title = '{$_POST['title']}', "
. "author = '{$_POST['author']}' WHERE id = {$_POST['id']}";
if (empty($_POST['id'])) {
mysql_query($ins);
} else {
mysql_query($up);
}
process-book.php
// Database connection code
$ins = "INSERT INTO bookshelf (title, author) "
."VALUES ('{$_POST['title']}', '{$_POST['author']}')";
$up = "UPDATE bookshelf SET title = '{$_POST['title']}', "
. "author = '{$_POST['author']}' WHERE id = {$_POST['id']}";
if (empty($_POST['id'])) {
mysql_query($ins);
} else {
mysql_query($up);
}
process-book.php
// Database connection code
$ins = "INSERT INTO bookshelf (title, author) "
."VALUES ('{$_POST['title']}', '{$_POST['author']}')";
$up = "UPDATE bookshelf SET title = '{$_POST['title']}', "
. "author = '{$_POST['author']}' WHERE id = {$_POST['id']}";
if (empty($_POST['id'])) {
mysql_query($ins);
} else {
mysql_query($up);
}
process-book.php
// Database connection code
$ins = "INSERT INTO bookshelf (title, author) "
."VALUES ('{$_POST['title']}', '{$_POST['author']}')";
$up = "UPDATE bookshelf SET title = '{$_POST['title']}', "
. "author = '{$_POST['author']}' WHERE id = {$_POST['id']}";
if (empty($_POST['id'])) {
mysql_query($ins);
} else {
mysql_query($up);
}
process-book.php
// Database connection code
$ins = "INSERT INTO bookshelf (title, author) "
."VALUES ('{$_POST['title']}', '{$_POST['author']}')";
$up = "UPDATE bookshelf SET title = '{$_POST['title']}', "
. "author = '{$_POST['author']}' WHERE id = {$_POST['id']}";
if (empty($_POST['id'])) {
mysql_query($ins);
} else {
mysql_query($up);
}
Php 102: Out with the Bad, In with the Good
Glaring Problems?
• Code duplication
• Input isn't filtered
• Output isn't escaped
• Vendor specific functions
• User-provided data used in SQL
• Did you see any others?
Deprecated!
• mysql extension deprecated as of 5.5.0
• Use mysqli or PDO
• Read the MySQL “Choosing an API” on
php.net: http://bit.ly/13zur2e
Code Duplication
$db = mysql_connect('localhost', 'testuser', 'testpass');
mysql_select_db('bookshelf', $db);
• Violates DRY principle
• Maintenance nightmare
• The next developer will want to kill you
• And your family
• And your pets
Consolidate
Let's throw all that duplicated code into an
include file, say library/base.php.
(We'll add a few other handy items while we're in there)
base.php
// . . . snip . . .
$db = mysql_connect('localhost', 'testuser', 'testpass');
mysql_select_db('bookshelf', $db);
Replace db info with base.php
Remove the db connection code from each
file with:
require_once dirname(__FILE__) . '/library/base.php';
STOP!
PDO
• PHP Data Objects
• Lightweight, consistent interface
• Data access abstraction
• Not database abstraction
http://www.php.net/manual/en/intro.pdo.php
base.php
// . . . snip . . .
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$dsn =
'mysql:host=localhost;dbname=bookshelf;charset=utf8';
$username = 'testuser';
$password = 'testpass';
try {
$dbh = new PDO($dsn, $username, $password, $options);
} catch (PDOException $e) {
error_log($e->getMessage(), 3, '../logs/errors.log');
echo 'An error occurred while trying to connect to the
database.';
}
base.php
// . . . snip . . .
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$dsn =
'mysql:host=localhost;dbname=bookshelf;charset=utf8';
$username = 'testuser';
$password = 'testpass';
try {
$dbh = new PDO($dsn, $username, $password, $options);
} catch (PDOException $e) {
error_log($e->getMessage(), 3, '../logs/errors.log');
echo 'An error occurred while trying to connect to the
database.';
}
base.php
// . . . snip . . .
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$dsn =
'mysql:host=localhost;dbname=bookshelf;charset=utf8';
$username = 'testuser';
$password = 'testpass';
try {
$dbh = new PDO($dsn, $username, $password, $options);
} catch (PDOException $e) {
error_log($e->getMessage(), 3, '../logs/errors.log');
echo 'An error occurred while trying to connect to the
database.';
}
base.php
// . . . snip . . .
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$dsn =
'mysql:host=localhost;dbname=bookshelf;charset=utf8';
$username = 'testuser';
$password = 'testpass';
try {
$dbh = new PDO($dsn, $username, $password, $options);
} catch (PDOException $e) {
error_log($e->getMessage(), 3, '../logs/errors.log');
echo 'An error occurred while trying to connect to the
database.';
}
Now start replacing mysql_*
// index.php
$books = $dbh->query($sql)->fetchAll();
// book-form.php
$book = $dbh->query($sql)->fetch();
// process-book.php and delete-book.php
$dbh->exec($sql);
Now on to the “handy items” I promised
base.php
$env = getenv('APPLICATION_ENVIRONMENT');
if (!$env) {
$env = "production";
}
date_default_timezone_set('America/Chicago');
if ($env === "develop") {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
}
// . . . snip . . .
base.php
$env = getenv('APPLICATION_ENVIRONMENT');
if (!$env) {
$env = "production";
}
date_default_timezone_set('America/Chicago');
if ($env === "develop") {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
}
// . . . snip . . .
base.php
$env = getenv('APPLICATION_ENVIRONMENT');
if (!$env) {
$env = "production";
}
date_default_timezone_set('America/Chicago');
if ($env === "develop") {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
}
// . . . snip . . .
base.php
$env = getenv('APPLICATION_ENVIRONMENT');
if (!$env) {
$env = "production";
}
date_default_timezone_set('America/Chicago');
if ($env === "develop") {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
}
// . . . snip . . .
Duplication removed, but . . .
Php 102: Out with the Bad, In with the Good
Besides getting shot . . .
“Whenever PHP generates an error message
internally, it's processed and formatted all the
way up to the fully formatted message that can
be outputted straight to the browser. Only just
before it is displayed the error_reporting setting
is checked.”
http://derickrethans.nl/five-reasons-why-the-shutop-operator-should-be-avoided.html
We echo $title and $author
<input type="hidden" name="id" value="<?php echo $id; ?>" />
<input type="text" name="title" value="<?php echo $title; ?>" />
<input type="text" name="author" value="<?php echo $author; ?>" />
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
$id = empty($_GET['id']) ? null : $_GET['id'];
$title = null;
$author = null;
if ($id) {
$book = $dbh->query( . . . )->fetch();
$title = $book['title'];
$author = $book['author'];
}
FIEO
• Filter input
– Your users want to destroy your app
– Prevent SQL injection
• Escape output
– Or just destroy your app yourself
– Defend against XSS
http://xkcd.com/327/
book-form.php
Use filter_input() to guarantee $id is
either false or an int.
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
process-book.php
More filter_input()
$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
$title = filter_input(INPUT_POST, 'title',
FILTER_SANITIZE_STRING);
$author = filter_input(INPUT_POST, 'author',
FILTER_SANITIZE_STRING);
index.php
Use htmlspecialchars() to escape
output
htmlspecialchars($book['title'], ENT_COMPAT, 'UTF-8'); ?>
htmlspecialchars($book['author'], ENT_COMPAT, 'UTF-8'); ?>
Prepared Statements
• Uses fewer resources
• Runs faster
• Protects against SQL injection
• Easier to read and maintain
book-form.php: Before
$book = $dbh->query("SELECT title, author FROM
bookshelf WHERE id = $id")->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 = "INSERT INTO bookshelf (title, author) "
."VALUES ('{$_POST['title']}',
'{$_POST['author']}')";
$dbh->exec($sql);
} else {
$sql = "UPDATE bookshelf SET title =
'{$_POST['title']}', "
. "author = '{$_POST['author']}' WHERE id =
{$_POST['id']}";
$dbh->exec($sql);
}
process-book.php: After
if ($id) {
$statement = $dbh->prepare("UPDATE bookshelf SET title
= :title, author = :author WHERE id = :id");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->bindParam(':id', $id);
$statement->execute();
} else {
$statement = $dbh->prepare("INSERT INTO bookshelf
(title, author) VALUES (:title, :author)");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->execute();
}
process-book.php: After
if ($id) {
$statement = $dbh->prepare("UPDATE bookshelf SET title
= :title, author = :author WHERE id = :id");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->bindParam(':id', $id);
$statement->execute();
} else {
$statement = $dbh->prepare("INSERT INTO bookshelf
(title, author) VALUES (:title, :author)");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->execute();
}
process-book.php: After
if ($id) {
$statement = $dbh->prepare("UPDATE bookshelf SET title
= :title, author = :author WHERE id = :id");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->bindParam(':id', $id);
$statement->execute();
} else {
$statement = $dbh->prepare("INSERT INTO bookshelf
(title, author) VALUES (:title, :author)");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->execute();
}
process-book.php: After
if ($id) {
$statement = $dbh->prepare("UPDATE bookshelf SET title
= :title, author = :author WHERE id = :id");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->bindParam(':id', $id);
$statement->execute();
} else {
$statement = $dbh->prepare("INSERT INTO bookshelf
(title, author) VALUES (:title, :author)");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->execute();
}
Service Layer
Let's replace all that baked in DB stuff
with a Service Layer
Why?
• Introduce some OOP principles
• High ROI
– Maintainability
– Testability
What? Where?
• Class name: BookshelfService
• Namespace: BookshelfService
• Location: library/Bookshelf/Service
• Filename: BookshelfService.php
• PSR-0 compliant
(The class name should mirror its location in the file
system)
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) {}
public function delete($id) {}
}
BookshelfService.php
private $dbh;
public function __construct(PDO $dbh)
{
$this->dbh = $dbh;
}
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();
}
BookshelfService.php
public function save(array $options)
{
if ($options['id']) {
$statement = $this->dbh->prepare("UPDATE bookshelf
SET title = :title, author = :author WHERE id = :id");
$statement->execute($options);
} else {
unset($options['id']);
$statement = $this->dbh->prepare("INSERT INTO
bookshelf (title, author) VALUES (:title, :author)");
$statement->execute($options);
}
}
A New Class Means . . .
. . . we need to add a few things to base.php.
base.php
set_include_path(
implode(PATH_SEPARATOR, array(
get_include_path(),
dirname(__FILE__)
)
)
);
base.php
function autoload($className)
{
// PSR-0 autoloader
}
spl_autoload_register('autoload');
https://gist.github.com/jwage/221634
base.php
// database connection code
$bookshelf = new BookshelfServiceBookshelfService($dbh);
And now for some before and after
shots . . .
index.php: Before
require_once dirname(__FILE__) . '/library/base.php';
$books = $dbh->query("SELECT * FROM bookshelf ORDER BY
title")->fetchAll();
index.php: After
require_once dirname(__FILE__) . '/library/base.php';
$books = $bookshelf->findAll();
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'];
}
book-form.php: After
if ($id) {
$book = $bookshelf->find($id);
$title = $book['title'];
$author = $book['author'];
}
process-book.php: Before
if ($id) {
$statement = $dbh->prepare("UPDATE bookshelf SET title
= :title, author = :author WHERE id = :id");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->bindParam(':id', $id);
$statement->execute();
} else {
$statement = $dbh->prepare("INSERT INTO bookshelf
(title, author) VALUES (:title, :author)");
$statement->bindParam(':title', $title);
$statement->bindParam(':author', $author);
$statement->execute();
}
process-book.php: After
$book = array(
'id' => $id,
'title' => $title,
'author' => $author
);
$bookshelf->save($book);
What Have We Done?
• Discovered we've been handed a disaster
• Iteratively improved on the nightmare
• Seen some nice features of PHP
– PDO
– Filtering and escaping
– OOP
• Learned something?
What more could we do?
• Global config file
• Environment config file
• Unit and functional tests
• Don't roll your own!
• Add Composer, add libraries as needed
Want More?
• Presentation source code: https://github.com/jeremykendall/bookshelf
• PHP Manual: http://www.php.net/manual/en/index.php
• PHP The Right Way: http://www.phptherightway.com/
• PHP 101 Suggestions:
http://csiphp.com/blog/2011/07/19/stop-doing-it-wrong-and-learn-to-code-good-too/
• PHP 101: PHP for the Absolute Beginner:
http://devzone.zend.com/6/php-101-php-for-the-absolute-beginner/
• PHPDeveloper.org http://phpdeveloper.org/
• php|architect: http://www.phparch.com/
• Composer http://getcomposer.org
Questions?
Thanks!
@JeremyKendall
jeremy@jeremykendall.net
http://joind.in/8188

More Related Content

PDF
Leveraging the Power of Graph Databases in PHP
PDF
Leveraging the Power of Graph Databases in PHP
PDF
Assetic (OSCON)
PDF
New SPL Features in PHP 5.3
PDF
The jQuery Divide
PDF
NoSQL & MongoDB
PPTX
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
Assetic (OSCON)
New SPL Features in PHP 5.3
The jQuery Divide
NoSQL & MongoDB

What's hot (18)

KEY
Potential Friend Finder
KEY
(Ab)Using the MetaCPAN API for Fun and Profit
PDF
NoSQL を Ruby で実践するための n 個の方法
PDF
Tips of CakePHP and MongoDB - Cakefest2011 ichikaway
PDF
Hd insight programming
PPT
Fantom and Tales
PDF
RESTFUL SERVICES MADE EASY: THE EVE REST API FRAMEWORK - Nicola Iarocci - Co...
PPT
PHP Tutorial (funtion)
PDF
Current state-of-php
PDF
How to use MongoDB with CakePHP
PDF
jQuery%20on%20Rails%20Presentation
PDF
Fazendo mágica com ElasticSearch
PPTX
PHP Basics and Demo HackU
PDF
My First Ruby
PDF
How else can you write the code in PHP?
PDF
[WLDN] Supercharging word press development in 2018
PDF
Fun with Python
TXT
Wsomdp
Potential Friend Finder
(Ab)Using the MetaCPAN API for Fun and Profit
NoSQL を Ruby で実践するための n 個の方法
Tips of CakePHP and MongoDB - Cakefest2011 ichikaway
Hd insight programming
Fantom and Tales
RESTFUL SERVICES MADE EASY: THE EVE REST API FRAMEWORK - Nicola Iarocci - Co...
PHP Tutorial (funtion)
Current state-of-php
How to use MongoDB with CakePHP
jQuery%20on%20Rails%20Presentation
Fazendo mágica com ElasticSearch
PHP Basics and Demo HackU
My First Ruby
How else can you write the code in PHP?
[WLDN] Supercharging word press development in 2018
Fun with Python
Wsomdp
Ad

Viewers also liked (9)

PDF
PHP Data Objects
ODP
PHP 102: Out with the Bad, In with the Good
PDF
Django a whirlwind tour
ODP
Tdd in php a brief example
PPTX
PHP and MySQL
PPT
PHP - PDO Objects
KEY
Php 101: PDO
PPTX
PDF
TDC SP 2015 - PHP7: melhor e mais rápido
PHP Data Objects
PHP 102: Out with the Bad, In with the Good
Django a whirlwind tour
Tdd in php a brief example
PHP and MySQL
PHP - PDO Objects
Php 101: PDO
TDC SP 2015 - PHP7: melhor e mais rápido
Ad

Similar to Php 102: Out with the Bad, In with the Good (20)

PDF
The History of PHPersistence
PDF
DBIx::Class introduction - 2010
PPT
Propel sfugmd
TXT
My shell
DOC
PDF
Adventures in Optimization
PDF
PHP and Rich Internet Applications
PDF
DBIx::Class beginners
PPTX
Basics of Working with PHP and MySQL.pptx
PDF
Mongo Presentation by Metatagg Solutions
PDF
Why Hacking WordPress Search Isn't Some Big Scary Thing
PPTX
php part 2
PDF
Zend Certification Preparation Tutorial
PDF
Redis for the Everyday Developer
PDF
PHP Tips & Tricks
PPTX
Open Source Search: An Analysis
KEY
Desarrollando aplicaciones web en minutos
PDF
Not Really PHP by the book
PDF
Drupal - dbtng 25th Anniversary Edition
PPT
PHP POWERPOINT SLIDES
The History of PHPersistence
DBIx::Class introduction - 2010
Propel sfugmd
My shell
Adventures in Optimization
PHP and Rich Internet Applications
DBIx::Class beginners
Basics of Working with PHP and MySQL.pptx
Mongo Presentation by Metatagg Solutions
Why Hacking WordPress Search Isn't Some Big Scary Thing
php part 2
Zend Certification Preparation Tutorial
Redis for the Everyday Developer
PHP Tips & Tricks
Open Source Search: An Analysis
Desarrollando aplicaciones web en minutos
Not Really PHP by the book
Drupal - dbtng 25th Anniversary Edition
PHP POWERPOINT SLIDES

More from Jeremy Kendall (11)

PDF
5 Ways to Awesome-ize Your (PHP) Code
PDF
Game Changing Dependency Management
PDF
Keeping it small - Getting to know the Slim PHP micro framework
PDF
Keeping it Small: Getting to know the Slim Micro Framework
KEY
Keeping it small: Getting to know the Slim micro framework
ODP
Intro to #memtech PHP 2011-12-05
ODP
TDD in PHP - Memphis PHP 2011-08-25
ODP
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
PDF
Zero to ZF in 10 Minutes
ODP
A Brief Introduction to Zend_Form
ODP
Zero to Zend Framework in 10 minutes
5 Ways to Awesome-ize Your (PHP) Code
Game Changing Dependency Management
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 framework
Intro to #memtech PHP 2011-12-05
TDD in PHP - Memphis PHP 2011-08-25
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Zero to ZF in 10 Minutes
A Brief Introduction to Zend_Form
Zero to Zend Framework in 10 minutes

Recently uploaded (20)

PDF
August Patch Tuesday
PPTX
Machine Learning_overview_presentation.pptx
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Machine learning based COVID-19 study performance prediction
PPTX
TechTalks-8-2019-Service-Management-ITIL-Refresh-ITIL-4-Framework-Supports-Ou...
PDF
Getting Started with Data Integration: FME Form 101
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPT
Teaching material agriculture food technology
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Video forgery: An extensive analysis of inter-and intra-frame manipulation al...
PDF
Univ-Connecticut-ChatGPT-Presentaion.pdf
PDF
A comparative analysis of optical character recognition models for extracting...
PPTX
1. Introduction to Computer Programming.pptx
PDF
Assigned Numbers - 2025 - Bluetooth® Document
August Patch Tuesday
Machine Learning_overview_presentation.pptx
MIND Revenue Release Quarter 2 2025 Press Release
Unlocking AI with Model Context Protocol (MCP)
Machine learning based COVID-19 study performance prediction
TechTalks-8-2019-Service-Management-ITIL-Refresh-ITIL-4-Framework-Supports-Ou...
Getting Started with Data Integration: FME Form 101
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Teaching material agriculture food technology
Advanced methodologies resolving dimensionality complications for autism neur...
Mobile App Security Testing_ A Comprehensive Guide.pdf
Per capita expenditure prediction using model stacking based on satellite ima...
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Network Security Unit 5.pdf for BCA BBA.
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Video forgery: An extensive analysis of inter-and intra-frame manipulation al...
Univ-Connecticut-ChatGPT-Presentaion.pdf
A comparative analysis of optical character recognition models for extracting...
1. Introduction to Computer Programming.pptx
Assigned Numbers - 2025 - Bluetooth® Document

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

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