The History of PHPersistence
       Hugo Hamon - @hhamon
       OSIDays November 2011
PHP/FI

Where Everything Begins…
1995

PHP/FI
1995

Files handling
$fp = fopen("/counter.txt","w+");	
	
$counter = fgets($fp,1024);	
$counter++;	
	
fputs($fp, $counter);	
fclose($fp);	
  
1995

Databases support
$db = mysql_connect("localhost", "root", "secret");

$name = "bob";

$result = mysql(
    $db,
    "select * from table where firstname='$name'"
);

$num = mysql_numrows($result);

echo "$num records found!<p>";
$i=0;
while ($i<$num);

    echo mysql_result($result,$i,"lcase(fullname)");
    echo "n";

    echo mysql_result($result,$i,"address");
    echo "n";

    $i++;

endwhile;
PHP 3/4

Code Reusability
User Functions
function db_connect($database, $host, $user, $pwd);

function db_fetch_single($dbh, $query);

function db_fetch_all($dbh, $query);

function db_insert($dbh, $table, $values);

function db_update($dbh, $table, $values, $pk);

function db_delete($dbh, $table, $pk);

function db_list_fields($dbh, $table);

function db_drop_table($dbh, $table);
function db_connect($database, $host, $user, $pwd)
{
    $dbh = mysql_connect($host, $user, $pwd);

    if (!$dbh) {
        die('Server unavailable: '. mysql_error());
    }

    if (!mysql_select_db($database, $dbh)) {
        die("'$database' unavailable: ". mysql_error());
    }

    return $dbh;
}
/**
  * Fetches a single record from a database
  *
  * @param resource $dbh The database handler
  * @param string $query A SQL query
  * @return array A single record as an array
  */
function db_fetch_single($dbh, $query)
{
     return current(db_fetch_all($dbh, $query));
}
function db_fetch_all($dbh, $query)
{
    $result = mysql_query($query, $dbh);
    if (!$result) {
        die('Invalid query: '. mysql_error());
    }

    $records = array();
    while ($record = mysql_fetch_assoc($result)) {
        $records[] = $record;
    }

    return $records;
}
function db_insert($dbh, $table, array $values)
{
    $data = array();
    foreach ($values as $value) {
        $data[] = mysql_real_escape_string($value, $dbh);
    }

    $sql = sprintf("INSERT INTO `$table` (`%s`) VALUES ('%s')",
        implode('`, `', array_keys($values)),
        implode("', '", $data)
    );

    $result = mysql_query($query, $dbh);
    if (!$result) {
        die('Invalid data to insert: '. mysql_error());
    }

    return mysql_insert_id($dbh);
}
include 'lib/database.inc.php';

$dbh = db_connect('osidays');
$query = 'SELECT id, name FROM author';

$authors = db_fetch_all($dbh, $query);
$author = db_fetch_single(
    $dbh,
    'SELECT id, name FROM author WHERE id = 3'
);

$id = db_insert($dbh, 'author', array(
    'name' => 'Jules Verne'
));
PHP 4

OOP & Reusability
class Database
{
    function Database($database, host, $user, $pwd);

    function   disconnect();
    function   connect();
    function   free();
    function   getErrorMessage();
    function   getErrorCode();
    function   getLastInsertId();
    function   insert($table, array $values);
    function   fetchAll($query);
    function   fetchSingle($query);
    function   query($query);
    function   quote($string);
    function   hasError();
    function   _collectError();
    function   _init();
}
require 'lib/Database.php';

$db= &new Database('demo', 'localhost', 'root');

$q = 'SELECT id, name FROM author';

foreach ($db->fetchAll($q) as $author) {
    // ...
}

$db->free();
$db->disconnect();
require 'lib/Database.php';

$db= &new Database('demo', 'localhost', 'root');

$q = 'SELECT id, name FROM author WHERE id = 1';

$author = $db->fetchSingle($q);

$db->free();

$db->disconnect();
require 'lib/Database.php';

$db= &new Database('demo', 'localhost', 'root');

$res = $db->insert('author', array(
    'name' => 'Jules Vernes'
));

if ($result) {
    $id = $db->getLastInsertId();
}

$db->disconnect();
PHP 4

Professional APIs
DBAL
• PEAR::DB   • MDB
• ADOdb      • MDB2
• Metabase • Creole
§  Multiple databases support
§  Uni ed APIs
§  Prepared statements supports
§  Query caching
§  Security against SQL Injections
PEAR MDB2
$dsn = array(
    'phptype'    =>   'mysql',
    'username'   =>   'root',
    'password'   =>   '',
    'hostspec'   =>   'localhost',
    'database'   =>   'demo',
);

$db = & MDB2::connect($dsn);
$db->setFetchMode(MDB2_FETCHMODE_ASSOC);

$rs = $db->queryAll('SELECT id, name FROM author');

$db->disconnect();
ADOdb
include('adodb.inc.php');

$db = NewADOConnection('mysql');
$db->Connect('localhost', 'root', 'password', 'demo');

$result = $db->Execute('SELECT id, name FROM author');

if ($result) {
    while (!$result->EOF) {
        echo 'ID: ', $result->fields[0] ,"n";
        echo 'Name:', $result->fields[1] ,"n";
        $result->MoveNext();
    }
}
2004

PHP 5
2005

PHP Data Object
•  PECL extension as of PHP 5.0.0
•  Core extension since PHP 5.1.0 (11/2005)
•  12 official drivers as of today
•  Extensible Object Oriented API
•  Prepared statements & transaction support
•  Stored procedures support
« PDO provides a data-access abstraction
layer, which means that, regardless of
which database you're using, you use the
same functions to issue queries and fetch
data. »
« PDO does not provide a
database abstraction as it
doesn't rewrite SQL or emulate
missing features »
Transactions
         /
Prepared Statements
try {
  $pdo->beginTransaction();

 $query = 'INSERT INTO author (name) VALUES (?)';

 $stmt = $pdo->prepare($query);
 $stmt->bindValue(1, 'Jules Verne');
 $stmt->execute();

  $id = $pdo->lastInsertId();
  $pdo->commit();
} catch (PDOException $e) {
  $pdo->rollback();
}
Stored Procedures
DELIMITER |
CREATE PROCEDURE coming_events (IN start DATE, OUT events INT)
BEGIN
  SELECT COUNT(*) INTO events FROM events WHERE start_at >=
start;
END
|


$query = "CALL coming_events('2011-03-01', @events);";

$pdo->query($query);

$stmt = $pdo->query("SELECT @events;");

echo $stmt->fetchColumn() ,' events to come.';
PHP 5

Zend_Db
Table Data Gateway
class AuthorGateway
    extends Zend_Db_Table_Abstract
{

    protected $_name = 'authors';
    protected $_primary = 'author_id';

}
$data = array(
    'first_name' => 'Jules',
    'last_name' => 'Vernes',
);

$table = new AuthorGateway();
$table->insert($data);
Row Data Gateway
$table = new AuthorGateway();

// New empty row
$row = $table->createRow();

// Insert a new row
$row->firstName = 'Jules';
$row->lastName = 'Verne';
$row->save();
Active Query
$isbn = '1234567890';

$rows = $db->select()
    ->from(array('b' => 'books'))
    ->where('b.isbn = ?', $isbn)
    ->order(array('b.title ASC'))
    ->query()
    ->fetchAll()
;
PHP 5

Object Relational Mapping
•  Use objects instead of raw SQL queries

•  Database abstraction layer (not always)

•  Relationships support

•  Behaviors support (i18n, timestampable…)

•  Querying API

•  Error logging
2005

Propel ORM
$author = new Author();
$author->setFirstName("Leo");
$author->setLastName("Tolstoy");

$book = new Book();
$book->setTitle("War & Peace");
$book->setIsbn("0140444173");
$book->setAuthor($author);
$book->save();
$query = BookQuery::create()
  ->joinWith('Book.Author')
  ->joinWith('Book.Publisher')
  ->where('Author.firstName = ?', 'Leo')
  ->where('Author.lastName = ?', 'Tolstoï')
  ->orderByTitle()
  ->filterByPublishYear(2009)
  ->find()
;
2009

Doctrine 1.x
$books = Doctrine_Query::create()
    ->select('b.*, a.*, p.*')
    ->from('Book b')
    ->leftJoin('b.Author a')
    ->leftJoin('b.Publisher p')
    ->where('a.firstName = ?', 'Karl')
    ->andWhere('a.lastName = ?', 'Marx')
    ->orderBy('b.title ASC')
    ->execute()
;
2009	
  
   	
  
PHP	
  5.3	
  
ORM Frameworks
Doctrine2
•  Database Abstraction Layer

•  Object Relational Mapping

•  Schema management

•  Migrations support

•  XML & NoSQL databases support
/** @Entity() */
class Author
{
  /**
   * @Id()
   * @GeneratedValue()
   * @Column(type="integer")
   */
  private $id;

    /** @Column(type="string", length="30") */
    private $name;

    /** @ReferenceMany(targetDocument="BlogPost") */
    private $posts = array();
}
$post = new BlogPost();
$post->setTitle('My First Blog Post');
$post->setBody('Some content...');

$author = new Author();
$author->setName('Hugo Hamon');
$author->addPost($post);

$em->persist($user);
$em->persist($post);
$dm->flush();
Toward NoSQL…
«	
  Not	
  Only	
  SQL	
  »	
  
Key   abcdef	
     Hugo	
  Hamon	
     Value
FirstName:	
  Hugo	
     Column 1

Key   abcdef	
     LastName:	
  Hamon	
      Column 2

                     Role:	
  Speaker	
      Column 3
Document
Key

  abcdef	
     Name:	
  Hugo	
  Hamon	
  


                                            0:	
  PHP	
  


               Skills	
                     1:	
  HTML	
  


                                            2:	
  CSS	
  


               Role:	
  Speaker	
  
2009

MongoDB Driver
•  Cross-platform support

•  Schemaless language

•  BSON type support

•  Binary le storage support (GridFS)

•  Master – slave replication support

•  Sharding (horizontal scaling)
$mongo = new Mongo();

$col = $mongo->selectDb('osidays')->books;

$books = array(
    array('title' => 'Da Vinci Code'),
    array('title' => 'The Lost Symbol'),
    array('title' => 'Digital Fortress'),
);

foreach ($books as $book) {
    $collection->insert($book);
}
What’s next…
… for today and tomorrow?
2011

PHP 5.3 ODM Frameworks
•  ORM Layers for Mongodb
•  Dealing with objects instead of arrays
•  Relationships support
•  Query abstraction
•  GridFS support
•  Query logging & caching
$post = new BlogPost();
$post->setTitle('My First Blog Post');
$post->setBody('Some content...');

$author = new Author();
$author->setName('Hugo Hamon');
$author->setEmail('hugo@example.com');

$dm->persist($user);
$user->addPost($post);

$dm->flush();
QuesAons?	
  




 92-98, boulevard Victor Hugo
 92 115 Clichy Cedex
 trainings@sensio.com (+33 (0)1 40 99 82 11)

 sensiolabs.com - symfony.com – trainings.sensiolabs.com

The History of PHPersistence

  • 2.
    The History ofPHPersistence Hugo Hamon - @hhamon OSIDays November 2011
  • 3.
  • 4.
  • 5.
  • 6.
    $fp = fopen("/counter.txt","w+"); $counter= fgets($fp,1024); $counter++; fputs($fp, $counter); fclose($fp);  
  • 7.
  • 8.
    $db = mysql_connect("localhost","root", "secret"); $name = "bob"; $result = mysql( $db, "select * from table where firstname='$name'" ); $num = mysql_numrows($result); echo "$num records found!<p>";
  • 9.
    $i=0; while ($i<$num); echo mysql_result($result,$i,"lcase(fullname)"); echo "n"; echo mysql_result($result,$i,"address"); echo "n"; $i++; endwhile;
  • 10.
  • 11.
  • 12.
    function db_connect($database, $host,$user, $pwd); function db_fetch_single($dbh, $query); function db_fetch_all($dbh, $query); function db_insert($dbh, $table, $values); function db_update($dbh, $table, $values, $pk); function db_delete($dbh, $table, $pk); function db_list_fields($dbh, $table); function db_drop_table($dbh, $table);
  • 13.
    function db_connect($database, $host,$user, $pwd) { $dbh = mysql_connect($host, $user, $pwd); if (!$dbh) { die('Server unavailable: '. mysql_error()); } if (!mysql_select_db($database, $dbh)) { die("'$database' unavailable: ". mysql_error()); } return $dbh; }
  • 14.
    /** *Fetches a single record from a database * * @param resource $dbh The database handler * @param string $query A SQL query * @return array A single record as an array */ function db_fetch_single($dbh, $query) { return current(db_fetch_all($dbh, $query)); }
  • 15.
    function db_fetch_all($dbh, $query) { $result = mysql_query($query, $dbh); if (!$result) { die('Invalid query: '. mysql_error()); } $records = array(); while ($record = mysql_fetch_assoc($result)) { $records[] = $record; } return $records; }
  • 16.
    function db_insert($dbh, $table,array $values) { $data = array(); foreach ($values as $value) { $data[] = mysql_real_escape_string($value, $dbh); } $sql = sprintf("INSERT INTO `$table` (`%s`) VALUES ('%s')", implode('`, `', array_keys($values)), implode("', '", $data) ); $result = mysql_query($query, $dbh); if (!$result) { die('Invalid data to insert: '. mysql_error()); } return mysql_insert_id($dbh); }
  • 17.
    include 'lib/database.inc.php'; $dbh =db_connect('osidays'); $query = 'SELECT id, name FROM author'; $authors = db_fetch_all($dbh, $query); $author = db_fetch_single( $dbh, 'SELECT id, name FROM author WHERE id = 3' ); $id = db_insert($dbh, 'author', array( 'name' => 'Jules Verne' ));
  • 18.
    PHP 4 OOP &Reusability
  • 19.
    class Database { function Database($database, host, $user, $pwd); function disconnect(); function connect(); function free(); function getErrorMessage(); function getErrorCode(); function getLastInsertId(); function insert($table, array $values); function fetchAll($query); function fetchSingle($query); function query($query); function quote($string); function hasError(); function _collectError(); function _init(); }
  • 20.
    require 'lib/Database.php'; $db= &newDatabase('demo', 'localhost', 'root'); $q = 'SELECT id, name FROM author'; foreach ($db->fetchAll($q) as $author) { // ... } $db->free(); $db->disconnect();
  • 21.
    require 'lib/Database.php'; $db= &newDatabase('demo', 'localhost', 'root'); $q = 'SELECT id, name FROM author WHERE id = 1'; $author = $db->fetchSingle($q); $db->free(); $db->disconnect();
  • 22.
    require 'lib/Database.php'; $db= &newDatabase('demo', 'localhost', 'root'); $res = $db->insert('author', array( 'name' => 'Jules Vernes' )); if ($result) { $id = $db->getLastInsertId(); } $db->disconnect();
  • 23.
  • 24.
  • 25.
    • PEAR::DB • MDB • ADOdb • MDB2 • Metabase • Creole
  • 26.
    §  Multiple databasessupport §  Uni ed APIs §  Prepared statements supports §  Query caching §  Security against SQL Injections
  • 27.
  • 28.
    $dsn = array( 'phptype' => 'mysql', 'username' => 'root', 'password' => '', 'hostspec' => 'localhost', 'database' => 'demo', ); $db = & MDB2::connect($dsn); $db->setFetchMode(MDB2_FETCHMODE_ASSOC); $rs = $db->queryAll('SELECT id, name FROM author'); $db->disconnect();
  • 29.
  • 30.
    include('adodb.inc.php'); $db = NewADOConnection('mysql'); $db->Connect('localhost','root', 'password', 'demo'); $result = $db->Execute('SELECT id, name FROM author'); if ($result) { while (!$result->EOF) { echo 'ID: ', $result->fields[0] ,"n"; echo 'Name:', $result->fields[1] ,"n"; $result->MoveNext(); } }
  • 31.
  • 32.
  • 33.
    •  PECL extensionas of PHP 5.0.0 •  Core extension since PHP 5.1.0 (11/2005) •  12 official drivers as of today •  Extensible Object Oriented API •  Prepared statements & transaction support •  Stored procedures support
  • 35.
    « PDO provides adata-access abstraction layer, which means that, regardless of which database you're using, you use the same functions to issue queries and fetch data. »
  • 36.
    « PDO does notprovide a database abstraction as it doesn't rewrite SQL or emulate missing features »
  • 37.
    Transactions / Prepared Statements
  • 38.
    try { $pdo->beginTransaction(); $query = 'INSERT INTO author (name) VALUES (?)'; $stmt = $pdo->prepare($query); $stmt->bindValue(1, 'Jules Verne'); $stmt->execute(); $id = $pdo->lastInsertId(); $pdo->commit(); } catch (PDOException $e) { $pdo->rollback(); }
  • 39.
  • 40.
    DELIMITER | CREATE PROCEDUREcoming_events (IN start DATE, OUT events INT) BEGIN SELECT COUNT(*) INTO events FROM events WHERE start_at >= start; END | $query = "CALL coming_events('2011-03-01', @events);"; $pdo->query($query); $stmt = $pdo->query("SELECT @events;"); echo $stmt->fetchColumn() ,' events to come.';
  • 41.
  • 42.
  • 43.
    class AuthorGateway extends Zend_Db_Table_Abstract { protected $_name = 'authors'; protected $_primary = 'author_id'; }
  • 44.
    $data = array( 'first_name' => 'Jules', 'last_name' => 'Vernes', ); $table = new AuthorGateway(); $table->insert($data);
  • 45.
  • 46.
    $table = newAuthorGateway(); // New empty row $row = $table->createRow(); // Insert a new row $row->firstName = 'Jules'; $row->lastName = 'Verne'; $row->save();
  • 47.
  • 48.
    $isbn = '1234567890'; $rows= $db->select() ->from(array('b' => 'books')) ->where('b.isbn = ?', $isbn) ->order(array('b.title ASC')) ->query() ->fetchAll() ;
  • 49.
  • 50.
    •  Use objectsinstead of raw SQL queries •  Database abstraction layer (not always) •  Relationships support •  Behaviors support (i18n, timestampable…) •  Querying API •  Error logging
  • 51.
  • 52.
    $author = newAuthor(); $author->setFirstName("Leo"); $author->setLastName("Tolstoy"); $book = new Book(); $book->setTitle("War & Peace"); $book->setIsbn("0140444173"); $book->setAuthor($author); $book->save();
  • 53.
    $query = BookQuery::create() ->joinWith('Book.Author') ->joinWith('Book.Publisher') ->where('Author.firstName = ?', 'Leo') ->where('Author.lastName = ?', 'Tolstoï') ->orderByTitle() ->filterByPublishYear(2009) ->find() ;
  • 54.
  • 55.
    $books = Doctrine_Query::create() ->select('b.*, a.*, p.*') ->from('Book b') ->leftJoin('b.Author a') ->leftJoin('b.Publisher p') ->where('a.firstName = ?', 'Karl') ->andWhere('a.lastName = ?', 'Marx') ->orderBy('b.title ASC') ->execute() ;
  • 56.
    2009     PHP  5.3  
  • 57.
  • 58.
  • 59.
    •  Database AbstractionLayer •  Object Relational Mapping •  Schema management •  Migrations support •  XML & NoSQL databases support
  • 60.
    /** @Entity() */ classAuthor { /** * @Id() * @GeneratedValue() * @Column(type="integer") */ private $id; /** @Column(type="string", length="30") */ private $name; /** @ReferenceMany(targetDocument="BlogPost") */ private $posts = array(); }
  • 61.
    $post = newBlogPost(); $post->setTitle('My First Blog Post'); $post->setBody('Some content...'); $author = new Author(); $author->setName('Hugo Hamon'); $author->addPost($post); $em->persist($user); $em->persist($post); $dm->flush();
  • 62.
  • 63.
    «  Not  Only  SQL  »  
  • 64.
    Key abcdef   Hugo  Hamon   Value
  • 65.
    FirstName:  Hugo   Column 1 Key abcdef   LastName:  Hamon   Column 2 Role:  Speaker   Column 3
  • 66.
    Document Key abcdef   Name:  Hugo  Hamon   0:  PHP   Skills   1:  HTML   2:  CSS   Role:  Speaker  
  • 67.
  • 68.
    •  Cross-platform support • Schemaless language •  BSON type support •  Binary le storage support (GridFS) •  Master – slave replication support •  Sharding (horizontal scaling)
  • 69.
    $mongo = newMongo(); $col = $mongo->selectDb('osidays')->books; $books = array( array('title' => 'Da Vinci Code'), array('title' => 'The Lost Symbol'), array('title' => 'Digital Fortress'), ); foreach ($books as $book) { $collection->insert($book); }
  • 70.
    What’s next… … fortoday and tomorrow?
  • 71.
    2011 PHP 5.3 ODMFrameworks
  • 73.
    •  ORM Layersfor Mongodb •  Dealing with objects instead of arrays •  Relationships support •  Query abstraction •  GridFS support •  Query logging & caching
  • 74.
    $post = newBlogPost(); $post->setTitle('My First Blog Post'); $post->setBody('Some content...'); $author = new Author(); $author->setName('Hugo Hamon'); $author->setEmail('[email protected]'); $dm->persist($user); $user->addPost($post); $dm->flush();
  • 75.
    QuesAons?   92-98,boulevard Victor Hugo 92 115 Clichy Cedex [email protected] (+33 (0)1 40 99 82 11) sensiolabs.com - symfony.com – trainings.sensiolabs.com