I'm one of those coder/designers that has been at it since day one, but is still getting the hang of several important coding nuances (e.g. OOP for PHP, and concepts such as this).
In this case, I'm migrating my toolkit from MySQL to PDO. I had considered MySQLi, but a critical mass of stackoverflow leans towards PDO, so ... I defer to experience. I've spent a few weeks in search of the best PDO wrapper class, studying dozens of PDO db wrapper classes on github, sourceforge, google code, etc.
Most of what I found was either kind of absurd, or added excessive complexity, or was over my head. So I opted to cannibalize, recombine and redesign things to work with my coding flow. As I'm new to PHP OOP, and new to PDO, it's likely I'm overlooking major things here.
I'd be most grateful if anyone could help improve this, or shed light on anything I'm not seeing! Thanks :)
Rough docs and examples of use can be found here: http://www.designosis.com/PDO_class/
class db extends PDO {
private $error = '';
private $crypt_salt = 'somesillystringforsalt'; // min 22 chars alphanumeric
public $querycount = 0;
public function __construct($dsn, $user='', $passwd='') {
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => true
);
try {
parent::__construct($dsn, $user, $passwd, $options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
}
}
public function run($query, $bind=false, $handler=false) {
$this->querycount++;
try {
if ($bind !== false) {
$bind = (array) $bind;
$dbh = $this->prepare( trim($query) );
$dbh->execute( $bind );
} else {
$dbh = $this->query( trim($query) ); // because query is 3x faster than prepare+execute
}
if (preg_match('/^(select|describe|pragma)/i', $query)) {
// if $query begins with select|describe|pragma, either return handler or fetch
return ($handler) ? $dbh : $dbh->fetchAll();
} else if (preg_match('/^(delete|insert|update)/i', $query)) {
// if $query begins with delete|insert|update, return count
return $dbh->rowCount();
} else {
return true;
}
} catch (PDOException $e) {
$this->error = $e->getMessage();
return false;
}
}
private function prepBind($pairs, $glue) {
$parts = array();
foreach ($pairs as $k=>$v) { $parts[] = "`$k` = ?"; }
return implode($glue, $parts);
}
public function update($table, $data, $where, $limit=false) {
if (is_array($data) && is_array($where)) {
$dataStr = $this->prepBind( $data, ', ' );
$whereStr = $this->prepBind( $where, ' AND ' );
$bind = array_merge( array_values($data), array_values($where) );
$sql = "UPDATE `$table` SET $dataStr WHERE $whereStr";
if ($limit && is_int($limit)) { $sql .= ' LIMIT '. $limit; }
return $this->run($sql, $bind);
}
return false;
}
public function insert($table, $data) {
if (is_array($data)) {
$dataStr = $this->prepBind( $data, ', ' );
$bind = array_values( $data );
$sql = "INSERT `$table` SET $dataStr";
return $this->run($sql, $bind);
}
return false;
}
public function delete($table, $where, $limit=false) {
if (is_array($where)) {
$whereStr = $this->prepBind( $where, ' AND ' );
$bind = array_values( $where );
$sql = "DELETE FROM `$table` WHERE $whereStr";
if ($limit && is_int($limit)) { $sql .= ' LIMIT '. $limit; }
return $this->run($sql, $bind);
}
return false;
}
// crypt (one-way encryption)
public function crypt($str, $moresalt='') {
$salt = $moresalt . $this->crypt_salt;
if (CRYPT_BLOWFISH == 1) {
$presalt = (version_compare(PHP_VERSION,'5.3.7','<')) ? '2a' : '2y'; // PHP 5.3.7 fixed stuff
$blowfish_salt = '$'. $presalt .'$07$'. substr($salt, 0, CRYPT_SALT_LENGTH) .'$';
return crypt($str, $blowfish_salt);
}
return sha1($str . $salt);
}
}