【问题标题】:php database classphp数据库类
【发布时间】:2011-08-01 14:22:52
【问题描述】:

我正在尝试通过 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 />';

【问题讨论】:

    标签: php database oop pdo


    【解决方案1】:

    请随时告诉我它有多糟糕。

    这很糟糕!

    ...

    什么?

    问过

    好吧,说真的,它没有那么糟糕,因为它是愚蠢。您在另一个类中包装 PDO。如果您想向 PDO 添加更多功能,则应改为扩展它。

    我的问题是:我怎样才能让我的查询类只能从数据库类中调用?

    PDO 已经在日常运营中执行此操作。当您prepare 查询时,它会返回一个PDOStatement 对象。您可以将其配置为返回另一个扩展 PDOStatement 的对象 (via PDO::ATTR_STATEMENT_CLASS)。

    如果您想使用解析器对查询进行预处理,则需要覆盖类中扩展 PDO 的 execqueryprepare 方法。处理完查询后,您可以调用父方法并返回您的扩展语句类。

    如果您担心人们调用语句类而不经过exec/query/prepare,请记住,除非语句知道,否则无法执行任何查询如何访问数据库,如果没有父 PDO 对象,它将无法访问。


    还有,

    $q = $db->query(sprintf("
        SELECT id,name,modified
        FROM users
        WHERE id_account = %u",
        $id
    ));
    

    考虑到这种情况,这完全是荒谬的。您在这里有一个 PDO 对象,没有理由在此处使用 prepared statementsplaceholders。如果您不想一次绑定一个变量(我不怪您),这就是 execute's optional array argument 的用途。

    【讨论】:

    • +1,如果可以的话,我会给 +7
    • 是的,我问过,非常感谢!我没想过扩展 PDO,我明白为什么现在更好了。你说得对,关于最后一点我必须说。很久以前的坏习惯......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-30
    • 2011-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-05
    • 1970-01-01
    相关资源
    最近更新 更多