PHP中的全局变量

4

我有一个名为userdb的类,在其中声明了一个返回数据库连接的函数:

return $con = new PDO("mysql:host=$host;dbname=$db", $user, $pass);

我有很多功能,甚至跨越其他类,在这些功能中,我需要访问$con(例如传递查询或获取数据),但我无法访问此变量。

是否有更好的方法来定义和使用数据库类?请记住,我还有其他类需要访问userdb类。


单例模式可能有帮助:http://en.wikipedia.org/wiki/Singleton_pattern - jantimon
7个回答

3
我建议您在这种情况下使用单例模式
在您的userdb类中,声明一个静态属性$scon
private static $scon;

假设你提到的函数名为createConnection(),那么你需要创建以下静态方法:

public static function connect() {
    if (empty(self::$scon)) {
         $instance = new userdb();
         self::$scon = $indtance->createConnection(); 
    }
    return self::$scon; 
}

有了这个,你将能够通过以下方式访问你的userdb连接:

userdb::connect();

由于这是一个单例模式,它只会连接一次,并使用该连接直到脚本结束。

注(关于依赖注入):由于@KevinM1提到了依赖注入,我必须补充说明,这也是一种可能且更优秀的解决方案。它要求您为所有使用数据库连接的类创建一个setConnection()方法(或Abstract祖先),并在这些类的实例化期间,您可以使用工厂将所需的连接添加到对象中。这应该包装在某个类加载器内,该加载器知道您的模型结构。

看,很简单,但对于小而快速的开发,我会坚持使用Singleton ;)


依赖注入是一种更加优秀的解决方案。 - Major Productions
2
@KevinM1:当然,但对于较小的项目,这是非常适合的解决方案。我还会添加一些延迟加载,但这不是这里的问题。 - aorcsik
也许吧。我认为在引导阶段利用默认的按引用传递行为更容易。一般来说,您知道何时需要预先访问数据库。对于较小的项目,不必经过实际 DI 容器的步骤,只需实例化 db 对象并手动将其传递给需要它的其他对象即可。您可以获得相同的单个实例,但没有破坏范围的诱惑。 - Major Productions
我的单例问题,超出了范围/封装问题,它的使用方式就像一个普通的“全局变量”。使用单例的代码不能在其方法签名中提及/引用该使用。这种隐含的要求=更紧密的耦合,这与面向对象的思想背道而驰。 - Major Productions
我完全同意你的观点,并且在我的项目中也使用了 DI,但是提问者是 PHP 的新手,也许他会因为简单而最终使用 global $con。为了防止这种情况发生,并且为了保持面向对象编程的思路,引入 Singleton 在这里是一个不错的选择。 - aorcsik

1

如果它在一个类中,将实例存储在属性中:

class userDB
{

   public $dbCon = false;//because you want to access the instance 
   //from outside the class, I have made the property public

    function connect()
   {

      $con = new PDO("mysql:host=$host;dbname=$db", $user, $pass);

      $this->dbCon = $con;

   }


}

在课堂外访问它:

$useDBInstance->dbCon;


0
return $this->con 

从你的类中以这种方式返回并以这种方式调用它。

$this->classObject->con->prepare();

0

嗯,$con将已经是一个对象,因为它正在实例化一个新的PDO对象。除非你想要为你的PDO对象添加功能,否则包装它是没有意义的。

话虽如此,与其他对象共享你的userdb/PDO对象(取决于你是否坚持使用包装器)的最佳方法是使用依赖注入。这是一个花哨的术语,用于将你的数据库传递给任何需要它的对象。由于在PHP中,默认情况下对象是按引用传递的,如果你首先创建你的数据库对象,所有接收它作为构造函数/方法参数的对象都将使用同一个单一实例。

编辑:依赖注入实现的链接

编辑2:关于小项目中的DI的澄清 -

通常的DI模式通常需要一个特殊的对象,称为DI容器。这是一个特殊的使用对象,它会自动将依赖注入到需要它的对象中。对于小项目来说,这有点过度设计了。简单、低复杂性版本的DI只需:

class SomeClass {
    protected $db;

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

class SomeClass2 {
    public function SomeMethod($db) {
        // do something with the db
    }
}

$db = new PDO(/* connection string */);

$obj = new SomeClass($db, /* other constructor args */);

// or

$obj2 = new SomeClass2(/* constructor args */);
$obj2->someMethod($db, /* method args */);

神奇的是,由于在PHP中默认情况下通过引用传递对象,$obj和$obj2正在使用相同的数据库连接。

整个想法是通过没有静态方法来避免破坏范围或封装,并确保类及其方法明确说明它们需要什么才能工作。

单例模式恰恰相反。它们通过静态方法访问,绕过了范围,由于它们被调用而不是传递,因此它们从不出现在方法签名中,因此不熟悉代码的任何人都不会意识到这个隐藏的要求。 甚至是Erich Gamma,曾经帮助规范Singleton Pattern的人之一,也对此表示后悔

我赞成放弃Singleton。它的使用几乎总是设计上的问题。

在PHP中,没有共享内存的概念,脚本每次请求执行一次,使用单例模式的唯一原因是为了方便访问单个资源。由于对象是按引用传递的,一个实例可以自然地与多个对象共享。从那里开始,重点是良好的设计和委派。

为什么是-1?在PHP中,DI模式在各个方面都优于单例模式。 - Major Productions

0

看看我的视频教程+代码,了解PHP全局变量的替代方法

你做错了。如果你计划在函数中创建连接并将其存储在那里,你需要使用静态变量。否则,每次函数调用都会连接。我的教程解释了常规函数中静态变量的概念。

如果不够清楚,请告诉我,我会尽力回答你的问题。

这里是一些代码来配合说明:

/**
* Arguments are none or [$db, [$user, [$pass[, $host]]]].
*
* @return PDO
*/
function PdoDb(){
    static $con = null; // I'm explicit :)
    // Every time you pass Arguments you reconnect
    if(func_num_args()){
        $args = array_pad(func_get_args(), 4, null);
        list($db, $user, $pass, $host) = $args;
        if(empty($user)) $user = 'root';
        if(empty($host)) $host = 'localhost';
        $con = new PDO("mysql:host={$host};dbname={$db}", $user, $pass);
    }
    if(empty($con)){
        trigger_error('Provide arguments to connect first.', E_USER_ERROR);
        return null;
    }
    return $con;
}

// First run (provide arguments to connect)
PdoDb($db, $user, $pass, $host);
PdoDb($db); // Works if you connect root@localhost with no password

// From here on (it returns PDO)
PdoDb()->DoStuffOfPdo();

一旦连接成功,它将保持连接状态。但是您可以通过提供参数随时重新连接。


-2
使用单例类实现
class connectdb
{
    protected static $_instance = NULL;


    private function __construct()
    {
    }

    public function getinstance()
    {
     if (null === self::$_instance) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }
    public function connect()
    {
     $this->connection =new PDO("mysql:host=$host;dbname=$db", $user, $pass);


    }

}

在PHP中,鉴于其“设置并忘记”的特性,没有理由使用单例模式。依赖注入才是正确的选择。 - Major Productions
在我看来,是的。其他人可能会有不同的看法。 - Major Productions
1
这并不是“错误”的。只是不是做事情的最佳方式。即使如此,有些人可能会有不同的看法(这完全可以接受)。对此进行负面评价是完全没有必要的。+1 - user849137

-4

如果想在类函数或独立函数中使用变量,需要使用global关键字进行声明

$conn=mysql_connect(); 
   function test(){
      global $conn;
   }

现在,在 test 函数的范围内,$conn 将可用,并且当在脚本顶部定义时,它将在任何地方都可用。对于类也需要做同样的事情,创建一个类的对象并在函数内声明为全局变量。

“global $x;”已经过时了,在像这样的情况下完全没有必要使用它。 - user849137
1
“global”在PHP中绝不是正确的选择,特别是在面向对象的上下文中,其中范围和封装至关重要。有更好的替代方案。 - Major Productions

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接