我正在尝试通过PHP中的OOP管理我的数据库连接和查询,我并不擅长。我知道我正在重新发明轮子,但这就是我喜欢它的方式:)
我正在使用三个课程,包括SQL parser我自己没有做过。我的实现在创建新连接时返回一个对象。程序员应该通过这个数据库对象创建一个新的查询实例(每个SQL语句一个实例)。我的问题是:如何让我的查询类只能从数据库类中调用?
我正在粘贴我的课程简历和下面的实施。请随时告诉我它有多糟糕。谢谢!
class genc_db_parser
{
/* See at http://www.tehuber.com/article.php?story=20081016164856267
returns an array with indexed values ('select','from','where','update',...) when they are available */
}
class genc_database
{
public $db; /* The database connection */
public $signature; /* Unique signature for the connection */
public static $instances = array(); /* Array of references to connection */
public static function error($e,$sql)
{
/* Errors */
}
private static function singleton($cfg,$inst)
{
$signature = sha1(serialize($cfg));
if ( isset($cfg['host'],$cfg['user'],$cfg['pass'],$cfg['db'],$cfg['engine']) )
{
foreach ( self::$instances as $obj )
{
if ( $obj->signature == $signature )
return $obj->db;
}
try
{ $db = new PDO($cfg['engine'].':host='.$cfg['host'].';dbname='.$cfg['db'], $cfg['user'], $cfg['pass']); }
catch (PDOException $e)
{ self::error($e); }
if ( $db )
{
$t = self::$instances;
array_push($t,$inst);
return $db;
}
}
return false;
}
function __construct($cfg=array())
{
if ( isset($cfg['host'],$cfg['user'],$cfg['pass'],$cfg['db']) )
$cfg['engine'] = isset($cfg['engine']) ? $cfg['engine'] : 'mysql';
else
$cfg = array(
'host' => GEN_DB_HOST,
'user' => GEN_DB_USER,
'pass' => GEN_DB_PASS,
'db' => GEN_DATABASE,
'engine' => GEN_DB_ENGINE
);
if ( isset($cfg['host'],$cfg['user'],$cfg['pass'],$cfg['db'],$cfg['engine']) )
{
if ( $this->db = self::singleton($cfg,$this) )
{
$this->signature = sha1(serialize($cfg));
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if ( $cfg['engine'] == 'mysql' )
{
$this->db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true);
$this->db->exec('SET CHARACTER SET utf8');
}
}
}
}
public function query($sql)
{
return new genc_query($sql,&$this);
}
}
class genc_query
{
private $sql, $conn, $db, $res, $sequences, $num;
function __construct($sql_statement,$db)
{
$sql_statement = trim($sql_statement);
if ( !empty($sql_statement) )
{
$this->sql = $sql_statement;
$this->conn = &$db;
$this->db = &$db->db;
$this->analyze();
}
}
private function analyze()
{
if ( $this->sql !== null )
{
$this->sequences = genc_db_parser::ParseString($this->sql)->getArray();
}
}
private function execute()
{
if ( $this->res === null )
{
$this->res = false;
if ( isset($this->sequences['select']) )
{
try
{ $this->res = $this->db->query($this->sql); }
catch (Exception $e)
{ genc_database::error($e,$this->sql); }
}
else
{
try
{ $this->res = $this->db->exec($this->sql); }
catch (Exception $e)
{ genc_database::error($e,$this->sql); }
}
}
return $this->res;
}
public function count()
{
if ( $this->num === null )
{
$req = false;
$this->num = false;
if ( isset($this->sequences['select']) )
{
$sql = genc_db_parser::ParseString($this->sql)->getCountQuery();
try
{ $req = $this->db->query($sql); }
catch (Exception $e)
{ genc_database::error($e,$sql); }
if ( $req )
$this->num = $req->fetchColumn();
}
}
return $this->num;
}
public function get_result()
{
if ( $this->execute() )
return $this->res;
return false;
}
public function get_row()
{
$this->execute();
if ( $this->res && isset($this->sequences['select']) )
return $this->res->fetch(PDO::FETCH_ASSOC);
return false;
}
/* Other functions working on the result... */
}
实施
/* db is the database object */
$db = new genc_database();
/* concurrent connections can be opened. However giving twice the same argument will return the same corresponding opened connection */
$db2 = new genc_database(array('host'=>'localhost','user'=>'myname','pass'=>'mypass','db'=>'mydb');
/* $db->query($sql) will create a query object ($q) attached to this database */
$q = $db->query(sprintf("
SELECT id,name,modified
FROM users
WHERE id_account = %u",
$id
));
/* $q->count() will return the number of rows returned by the query (through a COUNT), and without taking the limit into account */
echo $q->count();
/* $q->get_row will return the next row of the current recordset indexed by name */
while ( $data = $q->get_row() )
echo $data['id'].': '.$data['name'].'<br />';
/* If we do another action than a select, functions ahead will not return an error but false */
/* On other actions, just to execute the query, use get_result(), which will return the number of affected rows */
$p = $db2->query("UPDATE user2 SET modified = NOW() WHERE id = 1");
echo $p->get_result().'<br />';
答案 0 :(得分:4)
随时告诉我它有多糟糕。
这很糟糕!
...
什么?
你问了!
好吧,严肃地说,它并没有那么糟糕,因为它是愚蠢的。你在另一个类中包装 PDO。如果您想将更多功能添加到PDO,您应该改为扩展它。
我的问题是:如何让我的查询类只能从数据库类中调用?
PDO在日常运营中已经这样做了。当您prepare
查询时,它会返回PDOStatement个对象。您可以将其配置为返回另一个扩展PDOStatement的对象(via PDO::ATTR_STATEMENT_CLASS
)。
如果您想使用解析器预处理查询,则需要覆盖您的exec
,query
和prepare
方法扩展PDO的类。处理完查询后,可以调用父方法并返回扩展语句类。
如果您担心人们在不经过exec
/ query
/ prepare
的情况下调用语句类,请记住,不能执行任何查询除非语句知道如何访问数据库,否则如果没有父PDO对象,它将无法做到这一点。
此外,
$q = $db->query(sprintf("
SELECT id,name,modified
FROM users
WHERE id_account = %u",
$id
));
鉴于这种情况,这是完全荒谬的。你有一个PDO对象,没有理由不在这里使用prepared statements和placeholders。如果你不想一次绑定一个变量(我不怪你),那就是execute
's optional array argument的用途。