【问题标题】:Global variable in phpphp中的全局变量
【发布时间】:2012-10-10 00:46:32
【问题描述】:

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

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

我有各种功能,甚至跨其他类,我必须访问 $con(例如传递查询或获取数据),但我无法访问此变量。

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

【问题讨论】:

标签: php database global


【解决方案1】:

我建议您为此使用 Singleton Pattern

在你的 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() 方法(或抽象祖先),并且在这些类的实例化期间,您可以使用 Factory 将所需的连接添加到物体。这应该包含在一些 类加载器 中,这与您的模型结构不同。

看,小菜一碟,但对于小而快的开发,我会坚持使用 Singleton ;)

【讨论】:

  • 依赖注入是一个非常优秀的解决方案。
  • @KevinM1:Ofc,但对于较小的项目,这是一个非常适合的解决方案。我也会添加一些延迟加载,但这不是这里的问题。
  • 也许吧。我认为在引导阶段利用默认的按引用传递行为更容易。一般来说,您知道何时需要预先访问数据库。对于较小的项目,无需通过实际 DI 容器的箍,只需实例化 db 对象并手动将其传递给需要它的其他对象。您获得了相同的单个实例,但没有破坏范围的诱惑。
  • 我对单例的问题,除了范围/封装问题之外,它在使用方面就像一个普通的“全局”。使用单例的代码不能在其方法签名中提及/引用该使用。隐含的要求 = 更紧密的耦合,这是 OO 的对立面。
  • 我完全同意你的观点,我在我的项目中也使用了 DI,但是提问者是 PHP 新手,也许他最终会使用 global $con 以防止这种情况发生,并且为了留在OOP之地,这里介绍Singleton make是个不错的选择。
【解决方案2】:

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

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;

【讨论】:

    【解决方案3】:
    return $this->con 
    

    从你的班级以这种方式返回并以这种方式调用它..

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

    【讨论】:

      【解决方案4】:

      查看我的视频教程 + 代码,获取alternative to global PHP variables

      你做错了。如果你打算在一个函数中创建你的连接并将它存储在那里,你需要使用一个静态变量。 否则,您将在每个函数调用上保持连接。我的教程解释了常规函数中静态变量的这个概念。

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

      下面是一些代码:

      /**
      * 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();
      

      一旦连接,它就会保持这种状态。但是您可以通过提供参数随意重新连接。

      【讨论】:

        【解决方案5】:

        好吧,$con 已经是一个对象,因为它正在实例化一个新的 PDO 对象。除非您尝试向 PDO 对象添加功能,否则包装它是没有意义的。

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

        编辑:Link to Dependency Injection implementation

        EDIT2:澄清小型项目中的 DI -

        正常的 DI 模式通常需要一个称为 DI Container 的特殊对象。这是一个特殊用途的对象,它会自动将依赖注入到需要它的对象中。对于小型项目,这是矫枉过正的。 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 使用相同的数据库连接。

        整个想法是不要通过使用静态方法来破坏作用域或封装,并确保类和它们的方法预先知道它们需要什么才能工作。

        单身人士正好相反。它们是通过绕过作用域的静态方法访问的,并且由于它们被调用而不是传递,它们永远不会出现在方法签名中,因此任何不熟悉代码的人都不会意识到隐藏的要求。 Even Erich Gamma, one of the people who helped codify the Singleton Pattern has regrets about it

        我赞成放弃 Singleton。它的使用几乎总是一种设计气味。

        在没有共享内存概念的 PHP 中,脚本每次请求执行一次,想要使用单例的唯一原因是可以轻松访问单个资源。由于对象是通过引用传递的,一个实例自然可以与多个对象共享。从那里开始,它就是关于良好的设计和授权。

        【讨论】:

        • 为什么是-1? DI 全面优于 PHP 中的单例模式。
        【解决方案6】:

        使用单例类实现

        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 中使用单例,因为它具有即发即弃的特性。依赖注入是要走的路。
        • 国际海事组织,是的。其他人会不同意。
        • 这不是“错误的”。这不是做到这一点的最佳方式。即便如此,有些人会说不同(这很好)。对此的否决票是完全没有必要的。 +1
        【解决方案7】:

        要在类函数或独立函数中使用变量,您需要放置一个全局关键字

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

        现在$conn 将在测试功能范围内可用,并且在脚本顶部定义时将在任何地方可用。对于类,你也需要做同样的事情,创建一个类的对象并在函数中将其声明为全局

        【讨论】:

        • global $x; 已成为过去。在这种情况下完全没有必要使用它。
        • 'global' 在 PHP 中永远不是要走的路。尤其是在范围和封装至关重要的 OO 环境中。还有更好的选择。
        猜你喜欢
        • 1970-01-01
        • 2011-04-06
        • 1970-01-01
        • 1970-01-01
        • 2013-09-19
        • 2019-11-30
        • 1970-01-01
        • 1970-01-01
        • 2013-05-26
        相关资源
        最近更新 更多