-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added PdoWriter, Added writer exception to stop PDO exceptions leakin…
…g, updated readme, added PdoWriter tests
- Loading branch information
Stefan Warman
committed
May 6, 2014
1 parent
8e7b760
commit f043eef
Showing
4 changed files
with
228 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
namespace Ddeboer\DataImport\Exception; | ||
|
||
class WriterException extends \Exception implements ExceptionInterface | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
<?php | ||
|
||
namespace Ddeboer\DataImport\Writer; | ||
|
||
use \Ddeboer\DataImport\Exception\WriterException; | ||
|
||
/** | ||
* Class PdoWriter | ||
* | ||
* Write data into a specific database table using a PDO instance. | ||
* | ||
* IMPORTANT: If your PDO instance does not have ERRMODE_EXCEPTION any write failure will be silent or logged to | ||
* stderr only. It is strongly recomended you enable Exceptions with: | ||
* | ||
* $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); | ||
* | ||
*/ | ||
class PdoWriter implements WriterInterface | ||
{ | ||
/** | ||
* @var \PDO | ||
*/ | ||
protected $pdo; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
protected $tableName; | ||
|
||
/** | ||
* @var \PDOStatement | ||
*/ | ||
protected $statement; | ||
|
||
/** | ||
* Note if your table name is a reserved word for your target DB you should quote it in the appropriate way e.g. | ||
* for MySQL enclose the name in `backticks`. | ||
* | ||
* @param \PDO $pdo | ||
* @param string $tableName | ||
*/ | ||
public function __construct(\PDO $pdo, $tableName) | ||
{ | ||
$this->pdo = $pdo; | ||
$this->tableName = $tableName; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function prepare() | ||
{ | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function writeItem(array $item) | ||
{ | ||
try { | ||
//prepare the statment as soon as we know how many values there are | ||
if (!$this->statement) { | ||
|
||
$this->statement = $this->pdo->prepare( | ||
'INSERT INTO '.$this->tableName.' VALUES ('.substr(str_repeat('?,', count($item)), 0, -1).')' | ||
); | ||
|
||
//for PDO objects that do not have exceptions enabled | ||
if (!$this->statement) { | ||
throw new WriterException('Failed to prepare write statement for item: '.implode(',', $item)); | ||
} | ||
} | ||
|
||
//do the insert | ||
if (!$this->statement->execute(array_values($item))) { | ||
throw new WriterException('Failed to write item: '.implode(',', $item)); | ||
} | ||
|
||
} catch (\Exception $e) { | ||
//convert exception so the abstracton doesn't leak | ||
throw new WriterException('Write failed ('.$e->getMessage().').', null, $e); | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function finish() | ||
{ | ||
} | ||
} |
106 changes: 106 additions & 0 deletions
106
tests/Ddeboer/DataImport/Tests/Writer/PdoWriterTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
<?php | ||
|
||
namespace Ddeboer\DataImport\Tests\Writer; | ||
|
||
use Ddeboer\DataImport\Writer\PdoWriter; | ||
|
||
class PdoWriterTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
/** | ||
* @var \PDO | ||
*/ | ||
private $pdo; | ||
|
||
public function setUp() | ||
{ | ||
$this->pdo = new \PDO('sqlite::memory:'); | ||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); //important | ||
$this->pdo->exec('DROP TABLE IF EXISTS `example`'); | ||
$this->pdo->exec('CREATE TABLE `example` (a TEXT, b TEXT)'); | ||
} | ||
|
||
public function testValidWriteItem() | ||
{ | ||
$writer = new PdoWriter($this->pdo, 'example'); | ||
$writer->prepare(); | ||
$writer->writeItem(array('foo', 'bar')); | ||
$writer->finish(); | ||
|
||
$stmnt = $this->pdo->query('SELECT * FROM `example`'); | ||
$this->assertEquals( | ||
array(array('a'=>'foo', 'b'=>'bar')), | ||
$stmnt->fetchAll(\PDO::FETCH_ASSOC), | ||
'database does not contain expected row' | ||
); | ||
} | ||
|
||
public function testValidWriteMultiple() | ||
{ | ||
$writer = new PdoWriter($this->pdo, 'example'); | ||
$writer->prepare(); | ||
$writer->writeItem(array('foo', 'bar')); | ||
$writer->writeItem(array('cat', 'dog')); | ||
$writer->writeItem(array('ac', 'dc')); | ||
$writer->finish(); | ||
|
||
$stmnt = $this->pdo->query('SELECT * FROM `example`'); | ||
$this->assertEquals( | ||
array(array('a'=>'foo', 'b'=>'bar'), array('a'=>'cat', 'b'=>'dog'), array('a'=>'ac', 'b'=>'dc')), | ||
$stmnt->fetchAll(\PDO::FETCH_ASSOC), | ||
'database does not contain all expected rows' | ||
); | ||
} | ||
|
||
/** | ||
* @expectedException \Ddeboer\DataImport\Exception\WriterException | ||
*/ | ||
public function testWriteTooManyValues() | ||
{ | ||
$writer = new PdoWriter($this->pdo, 'example'); | ||
$writer->prepare(); | ||
$writer->writeItem(array('foo', 'bar', 'baz')); //expects two | ||
$writer->finish(); | ||
} | ||
|
||
/** | ||
* @expectedException \Ddeboer\DataImport\Exception\WriterException | ||
*/ | ||
public function testWriteToNonexistentTable() | ||
{ | ||
$writer = new PdoWriter($this->pdo, 'foobar'); | ||
$writer->prepare(); | ||
$writer->writeItem(array('foo', 'bar')); | ||
$writer->finish(); | ||
} | ||
|
||
/** | ||
* Tests PDO instance with silent errors. | ||
* | ||
* @expectedException \Ddeboer\DataImport\Exception\WriterException | ||
*/ | ||
public function testStatementCreateFailureWithNoException() | ||
{ | ||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); | ||
|
||
$writer = new PdoWriter($this->pdo, 'foob`ar'); | ||
$writer->prepare(); | ||
$writer->writeItem(array('foo', 'bar')); | ||
$writer->finish(); | ||
} | ||
|
||
/** | ||
* Tests PDO instance with silent errors. First inert prepares the statement, second creates an exception. | ||
* | ||
* @expectedException \Ddeboer\DataImport\Exception\WriterException | ||
*/ | ||
public function testWriteFailureWithNoException() | ||
{ | ||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); | ||
|
||
$writer = new PdoWriter($this->pdo, 'example'); | ||
$writer->prepare(); | ||
$writer->writeItem(array('foo', 'bar')); | ||
$writer->writeItem(array('foo', 'bar', 'baz')); | ||
$writer->finish(); | ||
} | ||
} |