(php)使用全局变量的更好方法?或者如何避免它们?

时间:2018-04-08 06:42:35

标签: php oop globals

我在某处读到使用全局变量通常是一个坏主意,但我不确定替代方案是什么......

我现在有了这个代码,我需要在每个函数中使用global $config, $db。 = copy&糊

class Layout {

   public static function render($file, $vars) {

     global $config, $mysql_db;

     mysqli_query($mysql_db, "SELECT * FROM users");
}
}

有更好的方法可以做到这一点还是我需要使用全局关键字或定义全局变量?因为我在每个函数中都需要像mysql连接变量这样的东西......

提前致谢!

6 个答案:

答案 0 :(得分:2)

一般来说,全局变量并不好。有一些方法可以避免全局变量。

使用基类

class Base 
{
    protected $db, $config;
}

class Layout extends Base 
{
    public void foo() 
    {
        $this->db->query(...);
    }
}

使用命名空间

namespace MyProject
{
    function DB()
    {
        ....
    }
}

class Layout
{
    public void foo() 
    {
        \MyProject\DB()->query(...);
    }
}

使用一些框架,如Symfony,Laravel等(也可以考虑一些仅限ORM的框架)

着名的框架做得很好。

答案 1 :(得分:0)

一个常见的模式是将任何必需的类传递给类的构造函数,所以这看起来更像......

class Layout {
    private $config;
    private $mysql_db;

    public function __construct ( $config, $mysql_db ) {
        $this->config = $config;
        $this->mysql_db = $mysql_db;
    }

    public function render($file, $vars) {
         $this->mysql_db->query( "SELECT * FROM users");
    }
}

这是基于依赖注入(DI),它在框架中很常见,并且允许更多的灵活性和控制。来自搜索的第一个链接提供 - http://php-di.org/doc/understanding-di.html

另请注意,我已将mysqli_query()的调用更改为面向对象的界面query(),这需要您使用new mysqli("localhost", "my_user", "my_password", "world")之类的内容创建连接,但也会使界面更一致(如果需要,可以更改)

然后使用

创建
$layout = new Layout( $config, $mysql_db);
$layout->render($file, $vars);

这种布局是许多框架的核心,当你想要进行全面测试时也是关键,因为你可以控制传入的信息并在需要的地方模拟类。

答案 2 :(得分:0)

类中的静态变量是避免它们的一种方法。将PHP中的静态变量视为在类上定义的变量而不是类的实例。然后单例模式可以获取变量,或者直接在类上引用变量。或者,编写一个__construct方法以接受传入的变量。另一种方法是PHP中的特性,以帮助减少复制粘贴。不是说特质是好习惯,但它们有助于避免重复自己。最后,几乎总是一种解决问题的方法。在名为render的方法中建立数据库连接已经违反了关注点分离的概念和单一责任原则。依赖注入可能是我解决全球问题的最佳方式。找到支持它的框架或库。有很多好的选择。像Laravel这样的框架或者一些能够为你提供所需功能的作曲家包。

答案 3 :(得分:0)

这是如何使用它的基本示例

创建文件名为Database.php的数据库类

<?php
class Database{

   private $con;

   function __construct() {
        $con = mysqli_connect("localhost","my_user","my_password","my_db");
   }

   function execute($sql){
        // Perform queries, but you should use prepared statemnet of mysqli for
        // sql injection 
        $execute = mysqli_query($con,$sql);
        return $execute;
   }
}
?>

让我们说Render类名为Render.php

<?php
require_once('your_path/Database.php'); // write correct path of file
class Render{
    private $db;

    function __construct() {
        $db = new Database();
    }

    function test(){
        $result = $db->execute('SELECT * FROM users');
    }
}
?>

答案 4 :(得分:0)

很多海报都围绕着布什的答案喋喋不休......对不起,伙计......这是真的。

处理它的常用方法是使用依赖注入。上课

class Layout {

   public static function render($file, $vars) {

         global $config, $mysql_db;

         mysqli_query($mysql_db, "SELECT * FROM users");
    }
}

你依赖于$config, $mysql_db;你的班级取决于他们。通常,您会将这些注入到构造函数中。

 protected $Mysqli;
 protected $config;

 public function __construct(array $config, Mysqli $Mysqli){
     $this->config = $config;
     $this->Mysqli = $mysqli;
 }

你遇到的问题是方法本身是静态的,因此绕过了调用构造函数的保证。您可以直接调用该方法。如果可能的话,我会将其更改为不是静态的。

但是如果你坚持让它保持静态,那么有一些常见的方法来解决这个问题,这取决于课堂其他部分的样子。对于这些我将忽略$config(在大多数情况下)它没有被使用,并且不清楚它是如何使用的或它用于什么(我假设它是用于数据库)。我还将使用Object接口Mysqli而不是程序接口。

最明显的方法是将它们粘贴在方法调用中

public static function render($file, $vars, Mysqli $Mysqli) {
    $Mysqli->query("SELECT * FROM users");
}

您可以检查方法何时被调用并连接

protected static $Mysqli;

public static function connect(){
   //this has the obvious problem of getting the connection info.
   $config = require 'config.php';
   if(!is_array($config)) throw new Exception('Bad config..'); //etc.

   self::$Mysqli = new mysqli(
       $config['dbhost'],
       $config['dbuser'],
       $config['dbpass'],
       $config['dbname']
   );
}


public static function render($file, $vars) {
    if(!$Mysqli) self::connect();
    self::$Mysqli->query("SELECT * FROM users");
}

//config.php would look like this
<?php

     return [
        'dbname' => 'database',
        'dbuser' => 'username',
        'dbpass' => 'password',
        'dbhost' => 'host'
    ];

这有效,但它可能不太理想,因为现在你的类负责外部文件config.php,如果它不存在会导致问题。

如果此类是所有静态方法,则可以使用单例模式。这样可以在静态属性中保存对类实例(它是自己的)的引用。然后,当您致电getInstance时,它始终返回相同的实例,即称为single

class Layout {

    //saves a reference to self
    private static $instance;

    protected $Mysqli;

    final private function __construct(Mysqli $Mysqli){
       if(!$Mysqli) throw new Exception('Instance of Mysqli required');
       $this->Mysqli = $Mysqli;
    }

    final private function __clone(){}

    final private function __wakeup(){}

    public static function getInstance(Mysqli $Mysqli = null)
    {
        if (!self::$instance)
            self::$instance = new self($Mysqli);
        return self::$instance;
    }

    public function render($file, $vars) {
        self::$Mysqli->query("SELECT * FROM users");
    }

}

$Layout = Layout::getInstance();
$Layout->render($file, $vars);

这个问题是第一次调用Mysqli类(或配置或其他什么)是必需的,但在后续调用中它不是。您可能提前或可能不知道它的第一次通话。但是,您也可以在此示例中从文件加载配置。

您还会注意到一些方法Final private这是为了防止超载并从课堂外调用它们。

我也有我在GitHub和Composer上放置的Singleton和Multiton(单个容器,多个Singletons)的特性,你可以在这里找到

https://github.com/ArtisticPhoenix/Pattern/

每个人都有它的优点和缺点。

P.S。我只是在这个页面上输入了这些,所以我没有测试它们中的任何一个,但理论是合理的......

答案 5 :(得分:0)

全局变量的问题

  • 您将难以命名新的全局变量,意外地覆盖以前定义的全局变量中的有用数据
  • 您的代码可能会保留您不再需要的不合理变量,并带来所有后果

规则

总是有例外

然而,最好避免教条极端主义。有时你会因某种原因需要全局变量,但是你需要确保你有充分的理由使用全局变量。

替代

在任何地方使用global变量的替代方法是使用privateprotected class成员,通过构造函数初始化主值,例如:

class Layout {

   private $config;
   private $mysql_db;

    public function render($file, $vars) {

         mysqli_query($mysql_db, "SELECT * FROM users");
    }

    public function __construct($config, $mysql_db) {
        $this->config = $config;
        $this->mysql_db = $mysql_db;
    }

}

然后,您可以通过新关键字实例化Layout并调用其实例的render

进一步说明

此外,您可以使用名称空间,因此您可能有几个具有相同名称的类,我也想在函数内部提及局部变量。