Создание приложения на основе базы данных на языке PHP

Урок 4: Оптимизация кода путем добавления классов и объектов

Содержимое на этой странице применимо к IDE NetBeans 7.2, 7.3, 7.4 и 8.0

В этом уроке рассматриваются способы оптимизации кода, позволяющие упростить поддержку этого кода в дальнейшем. Данная процедура затрагивает файлы createNewWisher.php и wishlist.php. Кроме того, создается новый файл под названием db.php.

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

При использовании функции класса WishDB не следует изменять значения каких-либо переменных в классе WishDB. Вместо этого необходимо использовать класс в качестве концептуального проекта для создания объекта WishDB и изменять значения переменных в этом объекте. Объект уничтожается при завершении работы. Поскольку значения непосредственно класса WishDB никогда не изменяются, данный класс можно использовать повторно неограниченное число раз. В некоторых случаях может потребоваться одновременно несколько экземпляров класса, а в других случаях будет предпочтителен "одноэкземплярный" класс, имеющий только один экземпляр в любой момент времени. WishDB в данном руководстве представлен как одноэкземплярный класс.

Следует отметить, что создание объекта класса обозначается термином "создание экземпляра" этого класса и что объект в данном случае называется "экземпляром" класса. Общий термин, обозначающий программирование с использованием классов и объектов, – "объектно-ориентированное программирование" (ООП). В PHP 5 используется сложная модель ООП. См. php.net для получения дополнительной информации.

В данном руководстве вы перемещаете функциональность вызова базы данных из отдельных файлов РНР в класс WishDB. Пользователи MySQL также заменяют процедурные вызовы mysqli объектно-ориентированными. Это соответствует новой объектно-ориентированной структуре приложения.

Текущий документ является частью краткого учебного курса "Создание приложения CRUD в IDE NetBeans для PHP".


Исходный код приложения из предыдущего урока

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

Для пользователей Oracle Database: щелкните здесь для загрузки исходного кода, отражающего состояние проекта по завершении предыдущего урока.

Создание файла db.php

Создайте новую подпапку в папке "Исходные файлы". Дайте папке имя Includes. Создайте новый файл под именем db.php и поместите его в папку Includes. Позже вы сможете добавлять в эту папку файлы, которые будут включаться в другие РНР-файлы.

Чтобы создать файл db.php в новой папке, сделайте следующее:

  1. Щелкните правой кнопкой мыши узел "Source Files" и выберите "New > Folder" в контекстном меню. Откроется диалоговое окно "Новая папка"
  2. В поле "Имя папки" введите Includes. Затем нажмите кнопку "Готово".
  3. Щелкните правой кнопкой мыши узел "Includes" и выберите "New > PHP File" в контекстном меню. Откроется диалоговое окно "Новый файл РНР".
  4. В поле "Имя файла" введите db. Затем нажмите кнопку "Готово".

Создание класса WishDB

Для создания класса WishDB необходимо инициализировать переменные класса и реализовать конструктор класса. Пользователи MySQL, обратите внимание, что класс WishDB расширяет mysqli. Это означает, что WishDB наследует функции и другие характеристики класса PHP mysqli. Вы убедитесь в важности этого при добавлении функций mysqli к классу.

Откройте файл db.php и создайте класс WishDB. В данном классе объявите переменные настройки базы данных для хранения имени и пароля собственника базы данных (пользователя), имени и машины размещения базы данных. Все объявления переменных являются закрытыми: это означает, что начальные значения в этих объявлениях недоступны вне класса WishDB (см. php.net). Вы также объявляете закрытую статическую переменную $instance, которая хранит экземпляр WishDB. Ключевое слово "статический" означает, что функции в классе имеют доступ к переменной даже при отсутствии экземпляра класса.

Для базы данных MySQL:

class WishDB extends mysqli {


    // single instance of self shared among all instances
    private static $instance = null;


    // db connection config vars
    private $user = "phpuser";
    private $pass = "phpuserpw";
    private $dbName = "wishlist";
    private $dbHost = "localhost";
}

Для базы данных Oracle:

class WishDB {

// single instance of self shared among all instances private static $instance = null;

// db connection config vars private $user = "phpuser"; private $pass = "phpuserpw"; private $dbName = "wishlist"; private $dbHost = "localhost/XE"; private $con = null;

}

Создание экземпляров класса WishDB

При использовании функций класса WishDB в других файлах PHP должна быть вызвана функция, позволяющая создать объект ("создать экземпляр") класса WishDB. WishDB разработан в качестве одноэкземплярного класса; это означает, что в любой определенный момент времени может существовать только один экземпляр класса. Поэтому рекомендуется предотвращать создание экземпляра WishDB, которое осуществляется извне и способствует появлению дублирующихся экземпляров.

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

 //This method must be static, and must return an instance of the object if the object
 //does not already exist.
 public static function getInstance() {
   if (!self::$instance instanceof self) {
     self::$instance = new self;
   }
   return self::$instance;
 }

 // The clone and wakeup methods prevents external instantiation of copies of the Singleton class,
 // thus eliminating the possibility of duplicate objects.
 public function __clone() {
   trigger_error('Clone is not allowed.', E_USER_ERROR);
 }
 public function __wakeup() {
   trigger_error('Deserializing is not allowed.', E_USER_ERROR);
 }

Функция getInstance является общедоступной и статической. Общедоступность означает возможность свободного доступа извне класса. Статическая функция доступна даже в том случае, если для класса не было создано экземпляров. Поскольку функция getInstance вызывается для создания экземпляров класса, она является статической. Обратите внимание, что эта функция имеет доступ к статической переменной$instance и устанавливает ее значение как экземпляр класса.

Двойное двоеточие (::), или "оператор разрешения диапазона" (Scope Resolution Operator), и ключевое слово self используются для получения доступа к статическим функциям. Self в рамках определения класса используется в качестве ссылки на данный класс. Если двойное двоеточие находится вне определения класса, вместо self используется имя класса. См. ресурс php.net для получения информации об операции разрешения диапазона.

Добавление конструктора к классу WishDB

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

Добавьте к WishDB следующий код:

Для базы данных MySQL

// private constructor
private function __construct() {
parent::__construct($this->dbHost, $this->user, $this->pass, $this->dbName);
if (mysqli_connect_error()) {
exit('Connect Error (' . mysqli_connect_errno() . ') '
. mysqli_connect_error());
}
parent::set_charset('utf-8');
}

Для базы данных Oracle

// private constructor
private function __construct() {
    $this->con = oci_connect($this->user, $this->pass, $this->dbHost);
    if (!$this->con) {
        $m = oci_error();
        echo $m['message'], "\n";
        exit;
    }
}

Следует учитывать, что вместо переменных $con, $dbHost, $user или $pass используется псевдопеременная $this. Псевдопеременная $this используется при вызове метода внутри контекста объекта. Она ссылается на значение переменной внутри этого объекта.

Функции класса WishDB

В этом уроке рассматривается реализация следующих функций класса WishDB:

  • get_wisher_id_by_name для извлечения идентификатора пользователя на основе имени
  • get_wishes_by_wisher_id для извлечения списка пожеланий "Wish list", принадлежащего определенному пользователю с соответствующим идентификатором
  • create_wisher для добавления нового пользователя в таблицу "Wishers".

Функция get_wisher_id_by_name

Эта функция возвращает идентификатор пользователя, а в качестве входного параметра для ее выполнения требуется имя пользователя.

После функции WishDB введите или вставьте следующую функцию в класс WishDB:

Для базы данных MySQL

public function get_wisher_id_by_name($name) {
$name = $this->real_escape_string($name);
$wisher = $this->query("SELECT id FROM wishers WHERE name = '"
. $name . "'"); if ($wisher->num_rows > 0){
$row = $wisher->fetch_row();
return $row[0];
} else
return null; }

Для базы данных Oracle

public function get_wisher_id_by_name($name) {
    $query = "SELECT id FROM wishers WHERE name = :user_bv";
    $stid = oci_parse($this->con, $query);
    oci_bind_by_name($stid, ':user_bv', $name);
    oci_execute($stid);
//Because user is a unique value I only expect one row
    $row = oci_fetch_array($stid, OCI_ASSOC);
if ($row)
return $row["ID"];
else
return null; }
Блок кода выполняет запрос SELECT ID FROM wishers WHERE name = [переменная для имени пожелания]. Результат запроса - массив идентификаторов из записей, соответствующих запросу. Если массив не пустой, это по умолчанию означает, что он содержит один элемент, поскольку при создании таблицы имя поля было определено как UNIQUE. В этом случае функция возвращает первый элемент массива $result (элемент под номером ноль). Если массив пуст, функция возвращает значение "null".

Примечание к безопасности. Для базы данных MySQL строка $name используется с с escape-символом для предотвращения атак SQL-инъекций. См. статью энциклопедии Wikipedia о введении SQL и документацию mysql_real_escape_string. Несмотря на то, что в контексте этого руководства риск возникновения опасных атак внедрения SQL маловероятен, рекомендуется исключить из участия в запросах MySQL такие строки, которые могли бы быть подвержены подобной атаке. База данных Oracle избегает данной проблемы, используя связанные переменные.

Функция get_wishes_by_wisher_id

Эта функция возвращает зарегистрированные пожелания пользователя, и для ее выполнения в качестве входного параметра требуется идентификатор пользователя.

Введите следующий блок кода:

Для базы данных MySQL

public function get_wishes_by_wisher_id($wisherID) {
return $this->query("SELECT id, description, due_date FROM wishes WHERE wisher_id=" . $wisherID);
}

Для базы данных Oracle

public function get_wishes_by_wisher_id($wisherID) {
    $query = "SELECT id, description, due_date FROM wishes WHERE wisher_id = :id_bv";
    $stid = oci_parse($this->con, $query);
    oci_bind_by_name($stid, ":id_bv", $wisherID);
    oci_execute($stid);
    return $stid;
}

Блок кода выполняет запрос "SELECT id, description, due_date FROM wishes WHERE wisherID=" . $wisherID и возвращает набор результатов, который является массивом записей, соответствующих запросу. (База данных Oracle использует связанную переменную для повышения производительности базы данных и уровня безопасности). Выделение выполняется с помощью wisherID, который является внешним ключом для таблицы wishes.

Примечание. Значение идентификатора не требуется до занятия 7.

Функция create_wisher

Функция создает новую запись в таблице "Wishers". Эта функция не возвращает каких-либо данных, и в качестве входных параметров для ее выполнения требуется имя и пароль нового пользователя.

Введите следующий блок кода:

Для базы данных MySQL

public function create_wisher ($name, $password){
    $name = $this->real_escape_string($name);
$password = $this->real_escape_string($password);
$this->query("INSERT INTO wishers (name, password) VALUES ('" . $name . "', '" . $password . "')"); }

Для базы данных Oracle

public function create_wisher($name, $password) {
    $query = "INSERT INTO wishers (name, password) VALUES (:user_bv, :pwd_bv)";
    $stid = oci_parse($this->con, $query);
    oci_bind_by_name($stid, ':user_bv', $name);
    oci_bind_by_name($stid, ':pwd_bv', $password);
    oci_execute($stid);
}
Блок кода выполняет запрос "INSERT wishers (Name, Password) VALUES ([переменные представляющие имя и пароль нового пожелания]). При выполнении запроса добавляется новая запись в таблицу "Wishers" с полями "name" и "password", заполненными значениями $name и $password соответственно.

Реорганизация кода приложения

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

Реорганизация файла wishlist.php

Начнем с файла wishlist.php, поскольку он небольшой и дает возможность представить оптимизацию более иллюстративно.
  1. В верхней части блока <? php? > введите следующую строку, делающую возможным использование файла db.php:
    require_once("Includes/db.php");
  2. Замените код, который подключается к базе данных и получает идентификатор пожелания, вызовом функции get_wisher_id_by_name.

    Для базы данных MySQL вы заменяете следующий код:

    $con = mysqli_connect("localhost", "phpuser", "phpuserpw");
    if (!$con) {
        exit('Connect Error (' . mysqli_connect_errno() . ') '
                . mysqli_connect_error());
    }
    //set the default client character set 
    mysqli_set_charset($con, 'utf-8');
    
    mysqli_select_db($con, "wishlist");
    $user = mysqli_real_escape_string($con, $_GET['user']);
    $wisher = mysqli_query($con, "SELECT id FROM wishers WHERE name='" . $user . "'");
    if (mysqli_num_rows($wisher) < 1) {
        exit("The person " . $_GET['user'] . " is not found. Please check the spelling and try again");
    }
    $row = mysqli_fetch_row($wisher);
    $wisherID = $row[0]; mysqli_free_result($wisher);


    $wisherID = WishDB::getInstance()->get_wisher_id_by_name($_GET["user"]); if (!$wisherID) { exit("The person " .$_GET["user"]. " is not found. Please check the spelling and try again" ); }

    Для базы данных Oracle вы заменяете следующий код:

    $con = oci_connect("phpuser", "phpuserpw", "localhost/XE", "AL32UTF8");
    if (!$con) {
       $m = oci_error();
       echo $m['message'], "\n";
       exit;
    }
    $query = "SELECT id FROM wishers WHERE name = :user_bv";
    $stid = oci_parse($con, $query);
    $user = $_GET["user"];
    
    oci_bind_by_name($stid, ':user_bv', $user);
    oci_execute($stid);
    
    //Because user is a unique value I only expect one row
    $row = oci_fetch_array($stid, OCI_ASSOC); if (!$row) { echo("The person " . $user . " is not found. Please check the spelling and try again" );
    exit;
    } $wisherID = $row["ID"];

    $wisherID = WishDB::getInstance()->get_wisher_id_by_name($_GET["user"]); if (!$wisherID) { exit("The person " .$_GET["user"]. " is not found. Please check the spelling and try again" ); }

    Новый код сначала вызывает функцию getInstance в WishDB. Функция getInstance возвращает экземпляр WishDB, а код вызывает функцию get_wisher_id_by_name в пределах данного экземпляра. Если требуемое пожелание в базе данных не найдено, код завершает процесс и отображает сообщение об ошибке.

    Для открытия подключения к базе данных наличие кода не является необходимым. Открытие подключения выполняется конструктором класса WishDB. Если имя и/или пароль изменяются, необходимо обновить только соответствующие переменные класса WishDB.

  3. Замените код, который получает пожелания для автора пожеланий, идентифицированного с помощью кода, кодом, который вызывает функцию get_wishes_by_wisher_id.

    Для базы данных MySQL вы заменяете следующий код:

    $result = mysqli_query($con, "SELECT description, due_date FROM wishes WHERE wisher_id=". $wisherID);
                    
    $result = WishDB::getInstance()->get_wishes_by_wisher_id($wisherID);

    Для базы данных Oracle вы заменяете следующий код:

    $query = "select * from wishes where wisher_id = :id_bv";
    $stid = oci_parse($con, $query);
    oci_bind_by_name($stid, ":id_bv", $wisherID);
    oci_execute($stid);
    $stid = WishDB::getInstance()->get_wishes_by_wisher_id($wisherID);
  4. Удалите строку, которая закрывает подключение к базе данных.
     mysqli_close($con);
                        or
     oci_close($con);                
    Код не нужен, потому что подключение к базе данных автоматически закрывается при уничтожении объекта WishDB. Однако рекомендуем сохранять код, освобождающий ресурс. Вам необходимо освободить все ресурсы, которые используют подключение, чтобы убедиться в том, что оно закрыто, даже при вызове функции close или уничтожении экземпляра с подключением к базе данных.

 

Реорганизация файла createNewWisher.php

Реорганизация не оказывает воздействия на форму ввода HTML или код для вывода на экран соответствующих сообщений об ошибках.

  1. В верхней части блока <? php? > введите следующий код, делающий возможным использование файла db.php:
    require_once("Includes/db.php");
  2. Удалите подтверждения подключения к базе данных ($dbHost и пр.). Теперь они находятся вdb.php.
  3. Замените код, который подключается к базе данных и получает идентификатор пожелания, вызовом функции get_wisher_id_by_name.

    Для базы данных MySQL вы заменяете следующий код:

    
    $con = mysqli_connect("localhost", "phpuser", "phpuserpw");
    if (!$con) {
        exit('Connect Error (' . mysqli_connect_errno() . ') '
                . mysqli_connect_error());
    }
    //set the default client character set 
    mysqli_set_charset($con, 'utf-8');
    
    
    
    /** Check whether a user whose name matches the "user" field already exists */ mysqli_select_db($con, "wishlist"); $user = mysqli_real_escape_string($con, $_POST['user']); $wisher = mysqli_query($con, "SELECT id FROM wishers WHERE name='".$user."'"); $wisherIDnum=mysqli_num_rows($wisher); if ($wisherIDnum) { $userNameIsUnique = false; }

    $wisherID = WishDB::getInstance()->get_wisher_id_by_name($_POST["user"]);
    if ($wisherID) {
    $userNameIsUnique = false;
    }

    Для базы данных Oracle вы заменяете следующий код:

    
    $con = oci_connect("phpuser", "phpuserpw", "localhost");
    if (!$con) {
        $m = oci_error();
        echo $m['message'], "\n";
        exit;
    }
    $query = "select ID from wishers where name = :user_bv";
    $stid = oci_parse($con, $query);
    $user = $_POST['user'];
    $wisherID = null;
    oci_bind_by_name($stid, ':user_bv', $user);
    oci_execute($stid);
    
    //Each user name should be unique. Check if the submitted user already exists.
    $row = oci_fetch_array($stid, OCI_ASSOC);
    if ($row) {
    $wisherID = $row["ID"];
    }
    if ($wisherID != null) {
    $userNameIsUnique = false;
    }
    $wisherID = WishDB::getInstance()->get_wisher_id_by_name($_POST["user"]);
    if ($wisherID) {
    $userNameIsUnique = false;
    }
    Объект WishDB существует до тех пор, пока обрабатывается текущая страница. Если обработка завершена или прервана, этот объект уничтожается. Код для открытия подключения к базе данных не является необходимым, поскольку подключение выполняется посредством функции WishDB. Код для закрытия подключения также не является необходимым, поскольку подключение будет закрыто сразу же после уничтожения объекта WishDB.
  4. Замените код, который вставляет новых авторов пожеланий в базу данных, кодом, который вызывает функцию create_wisher.

    Для базы данных MySQL вы заменяете следующий код:

    if (!$userIsEmpty && $userNameIsUnique && !$passwordIsEmpty && !$password2IsEmpty && $passwordIsValid) {
        $password = mysqli_real_escape_string($con, $_POST["password"]);
    mysqli_select_db($con, "wishlist");
    mysqli_query($con, "INSERT wishers (name, password) VALUES ('" . $user . "', '" . $password . "')");
    mysqli_free_result($wisher);
    mysqli_close($con);
    header('Location: editWishList.php');
    exit;
    }
    if (!$userIsEmpty && $userNameIsUnique && !$passwordIsEmpty && !$password2IsEmpty && $passwordIsValid) {
    WishDB::getInstance()->create_wisher($_POST["user"], $_POST["password"]);
    header('Location: editWishList.php' );
    exit;
    }

    Для базы данных Oracle вы заменяете следующий код:

    
    if (!$userIsEmpty && $userNameIsUnique && !$passwordIsEmpty && !$password2IsEmpty && $passwordIsValid) {
        $query = "INSERT INTO wishers (name, password) VALUES (:user_bv, :pwd_bv)";
        $stid = oci_parse($con, $query);
        $pwd = $_POST['password'];
        oci_bind_by_name($stid, ':user_bv', $user);
        oci_bind_by_name($stid, ':pwd_bv', $pwd);
        oci_execute($stid);
        oci_close($con);
        header('Location: editWishList.php');
        exit;
    }
    
    
    if (!$userIsEmpty && $userNameIsUnique && !$passwordIsEmpty && !$password2IsEmpty && $passwordIsValid) {
    WishDB::getInstance()->create_wisher($_POST["user"], $_POST["password"]);
    header('Location: editWishList.php' );
    exit;
    }

Исходный код приложения на момент завершения текущего урока

Для пользователей MySQL: щелкните сюда для загрузки исходного кода, отражающего состояние проекта по завершении данного урока.

Для пользователей Oracle Database: щелкните здесь для загрузки исходного кода, отражающего состояние проекта по завершении данного урока.

Что дальше?

<<Предыдущий урок

Следующий урок >>

Назад на главную страницу руководства

Полезные ссылки

Дополнительная информация об использовании классов в PHP:

Дополнительная информация о реорганизации кода PHP:



Для отправки комментариев и предложений, получения поддержки и новостей о последних разработках, связанных с PHP IDE NetBeans присоединяйтесь к списку рассылки .

Возврат к учебной карте PHP

get support for the NetBeans

Support


By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2013, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo