2

我知道标题并没有提供太多关于我所要求的线索,所以这里是简化的情况:

class MyPDO extends PDO
{
    private $stmt;

    function __construct($dsn...)
    {
        parent:__construct($dsn...);
    }

    function myQuery($sql)
    {
        $this->stmt = $this->query($query);
    }

    function myFetchAll()
    {
        return $this->stmt->fetchAll($mode);
    }

    function myFetchRow()
    {
        return $this->stmt->fetch();
    }

}

在整个应用程序中,我有一个 MyPDO 的基本实例并将其传递给不同的对象、映射器。

$adapter = new MyPDO($dsn...);
$adapter->myQuery('SELECT * FROM table');
$rows = $adapter->myFetchAll();

$another_object = new ObjectThatNeedsPDO($adapter);
$another_object->adapter->myQuery('SELECT * from another_table');
$rows = $another_object->adapter->myFetchAll();

这种方法安全吗,尤其是从 MyPDO::stmt 的角度来看?应用程序流程是否会搞砸,所以我最终可以从另一个 $stmt 获取数据而不是预期?

4

3 回答 3

2

就个人而言,我不会采取你的方法。原因是我不希望一个类创建的语句对象暴露给另一个不相关的类。此外,每个实现类可能有不同类型的参数绑定,它需要执行,它需要访问数据的方式(即获取所有行,获取每一行,获取作为对象与数组等),方式以特定于类的方式处理错误,等等。

对我来说,通过在基本 PDO 类的某个子类中包含此逻辑,您将一无所获。我的意思是真的很难做到:

$stmt = $this->pdo->query(...)
$data = $stmt->fetchAll();

比:

$this->myPDO->query(...);
$data = $this->myPDO->myFetchAll();

除了将这个附加类不必要地耦合到所有将使用它的类之外,您还能获得什么?确实,语句交互总是非常特定于类,只有基本 PDO 实例提供的唯一通用功能(DB 连接)。

因此,当然可以随意在类之间传递一个公共 PDO 实例,这绝对是一个很好的做法(即依赖注入)。

仔细考虑一下,当您对提议的 myPDO 类进行更改时,您是否希望潜在地更改每个实现类,或者您是否想在每次某些实现类需要一些自定义方式与语句对象交互时更改您的 myPDO 类。

根据下面的讨论,您可能需要考虑扩展 PDOStatement 以提供最大的灵活性。

这可能看起来像这样:

class myPDOFactory {
    public static function getInstance($dsn, $pdo_statement_class = 'myPDOStatement', $pdo_constructor_args = NULL);
        $pdo = new PDO($dsn);
        if (empty($pdo_statement_class)) {
            $pdo_statement_class = 'PDOStatement';
        }
        if (empty($pdo_constructor_args) || !is_array($pdo_constructor_args)) {
            $pdo_constructor_args = array();
        }
        $config_array = array($pdo_statement_class, $pdo_constructor_args);
        $pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, $config_array);
        return $pdo;
    }
}

class myPDOStatement extends PDO Statement {
    public function __construct(<any custom parameters you may need to have passed - items in $pdo_constructor_args from myPDOFactory class>) {
        parent::__construct();
        // any special stuff you want to do with any passed parameters here
    }

    public function fetchAll() {
        // override any functionality you desire here
    }

    public function fetchAllObjects() {
        return $this->fetchAll(PDO::FETCH_OBJ);
    }
}

class someClassThatNeedsPDO {
    protected $pdo = NULL;
    public function __construct($pdo) {
        if($pdo instanceof PDO) {
            $this->pdo = $pdo;
        } else {
            throw new Exception('Ooops!');
        }
    }

    public function doSomethingWithPDO() {
        $stmt = $this->PDO->prepare('SELECT * FROM sometable');
        $stmt = execute();
        return $stmt->fetchAllObjects();
    }
}

使用示例:

$pdo = myPDOFactory::getInstance($dsn, 'myPDOStatement', $constructor_args);
$consuming_class = new someClassThatNeedsPDO($pdo);
$object_array = $consuming_class->doSomethingWithPDO();  
于 2013-01-07T20:37:42.617 回答
1

您的方法看起来像是不必要的障碍,但这应该可以正常工作。我会在您的fetch方法中添加一个检查,以确保在调用本机 fetch/fetchall 之前存在有效的 PDOstatement。

另外,我建议不要为每个 MyPDO 对象创建新连接。传入 PDO 连接。

于 2013-01-07T20:37:15.437 回答
0

在处理 PDO(之前使用 mysqli 包装器)时,我总是使用单例模式。这意味着您只有一个实例,而不必将其传递给其他类。你这样做的方式是好的和安全的,但使用单例更好。

制作单例真的很容易:

class myClass{
    private static $instance;

    public static function singleton()
    {
        if (!self::$instance) {
            return self::$instance = new myClass();
        } else {
            return self::$instance;
        }       
    }

    public static function myQuery($query)
    {
        #Do stuff
    }
}

创建实例时,只需调用:

myClass::singleton()

然后,如果您想使用这些方法,只需使用以下命令:

myClass::myQuery($query)

如果您使用命名空间,则需要使用完全限定名称,例如 my\namespace\myClass

希望这是有道理的!

于 2013-01-07T20:45:51.933 回答