在PHP中,将数据库连接/对象导入函数的最佳方法是什么?

在PHP中,将数据库连接/对象导入函数的最佳方法是什么?,php,database,Php,Database,以下是几个选项: $connection = {my db connection/object}; function PassedIn($connection) { ... } function PassedByReference(&$connection) { ... } function UsingGlobal() { global $connection; ... } 因此,传入、通过引用传递或使用全局。我在考虑只在一个只有一个数据库连接的项目中使用的函数。

以下是几个选项:

$connection = {my db connection/object};

function PassedIn($connection) { ... }

function PassedByReference(&$connection) { ... }

function UsingGlobal() {
    global $connection;
    ...
}
因此,传入、通过引用传递或使用全局。我在考虑只在一个只有一个数据库连接的项目中使用的函数。如果有多个连接,则必须传入或通过引用传递

我认为当您在PHP5中使用对象时,不需要通过引用传递,因此传入或使用全局是两种可能性

我问这个问题的原因是因为我厌倦了总是在函数参数中加入$connection。

以上都没有


所有的
mysql
函数都可以选择使用数据库连接参数。如果不使用该参数,则假定mysql_connect()的最后一个连接。

我的建议是在大部分代码中避免使用全局连接-这很危险,很难跟踪,并且会咬到你

我这样做的方法是使用一个名为getDB()的函数,该函数可以通过构造函数注入在类级别,也可以是公共类中的静态函数

因此,代码变为

class SomeClass {
    protected $dbc;

    public function __construct($db) {
        $this->dbc = $db;
    }

    public function getDB() {
        return $this->dbc;
    }

    function read_something() {
        $db = getDB();
        $db->query();
    }
}
或者使用公共共享类

function read_something() {
    $db = System::getDB();
    $db->query();
}
无论您做了多少优雅的系统设计,总有一些项目在范围内必须是全局的(比如DB、Session、Config),我更喜欢将它们作为静态方法保留在我的系统类中

让每个类都需要通过构造函数进行连接是实现这一点的最佳方式,我所说的最佳方式是最可靠和隔离的


但是,请注意,使用公共共享类执行此操作可能会影响使用它完全隔离对象的能力,以及对这些对象执行单元测试的能力。

尝试以面向对象的方式设计代码。使用数据库的方法应分组在一个类中,类实例应包含数据库连接作为类变量。这样,数据库连接就可以用于需要它的函数,但它不是全局的

class MyClass {
  protected $_db;

  public function __construct($db)
  {
    $this->_db = $db;
  }

  public function doSomething()
  {
    $this->_db->query(...);
  }
}

这样,静态$connectionObject在getConnection调用之间被保留。

我使用Singleton ResourceManager类通过整个应用程序处理DB连接和配置设置之类的内容:

class ResourceManager {
    private static $DB;
    private static $Config;

    public static function get($resource, $options = false) {
        if (property_exists('ResourceManager', $resource)) {
            if (empty(self::$$resource)) {
                self::_init_resource($resource, $options);
            }
            if (!empty(self::$$resource)) {
                return self::$$resource;
            }
        }
        return null;
    }

    private static function _init_resource($resource, $options = null) {
        if ($resource == 'DB') {
            $dsn = 'mysql:host=localhost';
            $username = 'my_username';
            $password = 'p4ssw0rd';
            try {
                self::$DB = new PDO($dsn, $username, $password);
            } catch (PDOException $e) {
                echo 'Connection failed: ' . $e->getMessage();
            }
        } elseif (class_exists($resource) && property_exists('ResourceManager', $resource)) {
            self::$$resource = new $resource($options);
        }
    }
}
然后在函数/对象/任何地方:

function doDBThingy() {
    $db = ResourceManager::get('DB');
    if ($db) {
        $stmt = $db->prepare('SELECT * FROM `table`');
        etc...
    }
}

我使用它来存储消息、错误消息和警告,以及全局变量。关于何时实际使用这种类型的类,有一个有趣的问题。

我看到很多人都建议使用某种静态变量

从本质上讲,全局变量和静态变量之间的差别很小。除了语法,它们有完全相同的特征。因此,通过用静态变量替换全局变量,您将一无所获。在大多数示例中,存在一定程度的解耦,静态变量不是直接引用的,而是通过静态方法(例如单例或静态注册表)引用的。虽然稍好一些,但这仍然存在全球性的问题。如果您需要在应用程序中使用多个数据库连接,那么您就完蛋了。如果您想知道代码的哪些部分有副作用,您需要手动检查实现。这并不是决定应用程序成败的因素,但它会使应用程序更难维护

我建议您在以下两项中选择一项:

  • 将实例作为参数传递给需要它的函数。这是迄今为止最简单的,它具有范围狭窄的所有优点,但它可能变得相当笨拙。它也是引入依赖关系的来源,因为代码的某些部分可能最终成为中间人。如果发生这种情况,请继续

  • 将实例放在对象的范围内,对象具有所需的方法。例如,如果方法
    Foo->doStuff()
    需要数据库连接,则将其传递到
    Foo
    的构造函数中,并将其设置为
    Foo
    上的受保护实例变量。您仍然可能会遇到传递方法的一些问题,但与方法相比,使用笨拙的构造函数通常不是什么问题。如果您的应用程序变得足够大,您可以使用依赖项注入容器来实现自动化


如果您有一个自定义db连接对象,或者您正在使用PDO:Harsh,会发生什么情况?海报上没有说他是。在进一步澄清之前,这不值得否决票。很好,我喜欢这个想法。现在我必须看看如何实现它。我不久前就开始使用它,它为我打开了很多可能性。对于每个函数来说,它比混乱的全局变量和db参数要好得多。它还保持全局名称空间中没有变量。这与全局变量一样是全局的。静态不是避免全局状态的解决方案,构造函数注入是。尽管有负面的声誉,我还是决定把它保留在这里——作为一个不这样做的例子。匆忙进行重构这里的主要问题是,必须将$db对象传递到频繁实例化的类中是非常烦人的。如果有静态函数也需要$db对象呢?您是否也必须将$db传递到这些中?它确实有效,但似乎没有必要。不,它不是没有必要的,构造函数注入是开发可测试,解耦代码。有关此主题的更广泛讨论,请参阅。您对void global的建议很好,但您的解决方案都是全局状态的一种形式。谢谢-回顾此答案,我意识到我不清楚我是在建议构造函数注入还是单例,并且我没有正确解释每种方法。答案现已编辑。
function doDBThingy() {
    $db = ResourceManager::get('DB');
    if ($db) {
        $stmt = $db->prepare('SELECT * FROM `table`');
        etc...
    }
}