Подтвердить что ты не робот

Использовать глобальные переменные в классе

Я пытаюсь создать класс pagination и использовать переменную вне класса.

Но это дает мне фатальную ошибку "Вызов функции-функции-члена() для не-объекта".

Это индексный файл:

$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");

$pagination = new pagi();
$records = $pagination->get_records("SELECT * FROM `table`");

И это файл pagi.php:

class pagi {

    public function get_records($q) {
        $x = $db->query($q);
        return $db->fetch($x);
    }

}

Можно ли использовать эту переменную извне класса внутри класса, не создавая новую внутри класса?

4b9b3361

Ответ 1

Правильный способ решения этого вопроса - ввести объект базы данных в другой класс (инъекция зависимостей):

$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");

$pagination = new Paginator($db);
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`");

class Paginator
{    
    protected $db;

    // Might be better to use some generic db interface as typehint when available
    public function __construct(DB_MySQL $db)
    {
        $this->db = $db;
    }

    public function get_records($q) {
        $x = $this->db->query($q);
        return $this->db->fetch($x);
    }

}

Другой способ, которым вы могли бы решить это, - ввести экземпляр класса базы данных в метод, который его использует:

$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");

$pagination = new Paginator();
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`", $db);

class Paginator
{
    public function get_records($q, DB_MySQL $db) {
        $x = $db->query($q);
        return $db->fetch($x);
    }

}

Какой бы метод вы ни выбрали, зависит от ситуации. Если только одному методу нужен экземпляр базы данных, вы можете просто ввести его в метод, иначе я бы ввел его в конструктор класса.

Также обратите внимание, что я переименовал ваш класс из pagi в Paginator. Paginator - лучшее имя IMHO для класса, потому что для других людей (re) для вашего кода ясно. Также обратите внимание, что я сделал первую букву в верхнем регистре.

Еще одна вещь, которую я сделал, - это изменить запрос, чтобы выбрать поля, которые вы используете, вместо того, чтобы использовать "подстановочный знак" *. Это по той же причине, что я изменил имя класса: People (re), просматривающий ваш код, точно узнает, какие поля будут извлечены без проверки базы данных и/или результата.

Обновление

Поскольку ответ привел к обсуждению вопроса о том, почему я отправился на путь внедрения зависимостей вместо объявления объекта global, я хотел бы пояснить, почему я бы использовал инъекцию зависимостей над ключевым словом global: когда у вас есть метод вроде:

function get_records($q) {
    global $db;

    $x = $db->query($q);
    return $db->fetch($x);
}

Когда вы используете метод выше, неясно, зависит ли класс или метод от $db. Следовательно, это скрытая зависимость. Другая причина, почему выше сказанное плохо, заключается в том, что вы тесно связали класс $db (таким образом, DB_MySQL) с этим методом/классом. Что делать, если вам нужно использовать 2 базы данных в какой-то момент. Теперь вам нужно пройти весь код, чтобы изменить global $db на global $db2. Вам не нужно менять код, чтобы переключиться на другую базу данных. По этой причине вы не должны делать:

function get_records($q) {
    $db = new DB_MySQL("localhost", "root", "", "test");

    $x = $db->query($q);
    return $db->fetch($x);
}

Опять же, это скрытая зависимость и плотно соединяет класс DB_MySQL с методом/классом. Из-за этого также невозможно правильно unit test класс Paginator. Вместо тестирования только единицы (класс Paginator) вы также тестируете класс DB_MySQL в одно и то же время. А что, если у вас несколько тесно связанных зависимостей? Теперь вы неожиданно тестируете несколько классов с помощью так называемых модульных тестов. Поэтому при использовании инъекции зависимостей вы можете легко переключиться на другой класс базы данных или даже на насмешку для тестирования. Помимо преимуществ тестирования только одного устройства (вам не нужно беспокоиться о неправильных результатах из-за зависимостей), он также гарантирует, что ваши тесты будут быстро завершены.

Некоторые люди могут подумать, что шаблон Singleton - это правильный способ получить доступ к объекту базы данных, но должно быть ясно, что, прочитав все вышеизложенное, singleton - это просто другой способ сделать вещи global. Он может выглядеть по-другому, но он имеет те же самые характеристики и, следовательно, те же проблемы, что и global.

Ответ 2

Хотя я согласен с тем, что модель зависимости хороша, для базы данных я лично использую статическое соединение, доступное для всех экземпляров класса базы данных, и экземпляры экземпляров для запроса, когда мне это нужно. Вот пример:

<?php
//define a database class
class DB {
    //the static connection.
    //This is available to all instances of the class as the same connection.
    private static $_conn;

    //store the result available to all methods
    private $result;
    //store the last query available to all methods
    private $lastQuery;

    //static connection function. connects to the database and stores that connection statically.       
    public static function connect($host, $user, $pass, $db){
        self::$_conn = mysqli_connect($host, $user, $pass, $db);
    }

    //standard function for doing queries. uses the static connnection property.
    public function query($query){
        $this->lastQuery = $query;
        $this->result = mysqli_query(self::$_conn, $query);
        //process result, return expected output.
    }
}

//create connection to the database, this connection will be used in all instances of DB class
DB::connect('local', 'DB_USER', 'DB_PASS');

//create instance to query
$test = new DB;
//do query
$test->query("SELECT * FROM TABLE");

//test function
function foo(){
    //create instance to use in this function
    $bar = new DB;
    //do query
    $bar->query("SELECT * FROM OTHER_TABLE");
    //return results
    return $bar->fetchArray();
}

Таким образом, я могу создать все экземпляры, которые я хочу получить в БД, в любой функции, методе... и т.д. и использовать этот локальный экземпляр класса для выполнения всех моих запросов. Все экземпляры используют одно и то же соединение.

Одно дело отметить, что это только позволяет одно подключение к базе данных для определенного класса, но я использую только один, поэтому это не проблема для меня.

Ответ 3

вы можете добавить db-соединение ($db) к вызову метода get_records:

Вот только соответствующие строки кода:

Первый файл:

$records = $pagination->get_records("SELECT * FROM `table`", $db);

Второй файл:

public function get_records($q, $db) {

Ответ 4

Другие ответы до сих пор, безусловно, предпочтительнее использования глобального, поскольку это разрушит вашу инкапсуляцию (например, вам нужно будет иметь этот объект, определенный до вызова этого метода).

Это намного лучше обеспечить, что в сигнатуре метода или не использовать класс.

Ответ 5

Используйте шаблон singleton и введите DB_MySQL со статическим экземпляром!