PHP数据库类

发布于 2024-10-31 08:41:48 字数 6954 浏览 1 评论 0原文

我正在尝试通过 PHP 中的 OOP 管理我的数据库连接和查询,但我并不擅长。我知道我正在重新发明轮子,但这就是我喜欢的方式:)

我使用三个类,包括 SQL解析器我自己还没做过。创建新连接时,我的实现返回一个对象。程序员应该通过这个数据库对象创建一个新的查询实例(每个 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 />';

I am trying to manage my database connections and queries through OOP in PHP, and I'm not great at it. I know I'm reinventing the wheel, but that's the way I like it :)

I am using three classes, including a SQL parser I haven't done myself. My implementation returns an object when a new connection is created. Programmer should create a new query instance (one instance per SQL statement) through this database object. My question is: how can I get my query class to be only invocable from the database class?

I'm pasting a resume of my classes and the implementation below. Feel free to let me know how bad it is. Thanks!

    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... */
    }

Implementation

    /* 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 />';

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

独留℉清风醉 2024-11-07 08:41:48

请随时告诉我情况有多糟糕。

不好了!

...

什么?

问了

好吧,说实话,这并没有那么糟糕,因为它愚蠢。您正在将 PDO 包装在另一个类中。如果您想向 PDO 添加更多功能,您应该扩展它。

我的问题是:如何使我的查询类只能从数据库类调用?

PDO 已经在日常运营中做到了这一点。当您准备查询时,它返回一个 PDOStatement 对象。您可以将其配置为返回另一个对象(通过 PDO::ATTR_STATMENT_CLASS< /code>),它扩展了 PDOStatement。

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

如果您担心人们在不经过 exec/query/prepare 的情况下调用语句类,请记住,不能执行任何查询执行除非该语句知道如何访问数据库,并且如果没有父 PDO 对象,它将无法执行此操作。


而且,

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

考虑到当时的情况,这是完全荒谬的。您这里有一个 PDO 对象,没有理由不使用 准备好的语句占位符位于此处。如果您不想一次绑定一个变量(我不怪您),那就是 execute 的可选数组参数 用于。

Feel free to let me know how bad it is.

It's bad!

...

What?

You asked!

Okay, in all seriousness, it's not so much bad as it is silly. You're wrapping PDO in another class. If you want to add more functionality to PDO, you should be extending it instead.

My question is: how can I get my query class to be only invocable from the database class?

PDO already does this during day to day operations. When you prepare a query, it returns a PDOStatement object. You can configure it to return another object (via PDO::ATTR_STATEMENT_CLASS) that extends PDOStatement instead.

If you want to pre-process the query using your parser, you'll need to override the exec, query and prepare methods in your class that extends PDO. Once you've processed the query, you can call the parent method and return your extended statement class.

If you're worried about people invoking the statement class without going through exec/query/prepare, just keep in mind that no queries can be executed unless the statement knows how to access the database, and it won't be able to do that without the parent PDO object.


Also,

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

This is downright absurd given the circumstances. You have a PDO object here, there's no reason not to use prepared statements and placeholders here. If you don't want to bind one variable at a time (and I don't blame you), that's what execute's optional array argument is for.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文