corner imagecorner image
IDEPlatformPluginsDocs & SupportCommunityPartners

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

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

Содержимое на этой странице относится к среде IDE NetBeans 6.9-7.0

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

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

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

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

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

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


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

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

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

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

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

Для создания файла db.php в новой папке выполните следующее:

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

// Единственный экземпляр self, совместно используемый всеми экземплярами 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);
//Поскольку пользователь имеет уникальное значение, ожидается только одна строка
    $row = oci_fetch_array($stid, OCI_ASSOC);
if ($row)
return $row["ID"];
else
return null; }
Блок кода выполняет запрос SELECT ID FROM wishers WHERE name = [variable for name of the wisher]. В результате выполнения запроса выдается массив идентификаторов из записей, соответствующих параметрам запроса. Если массив не является пустым, это означает, что он содержит один элемент, поскольку имя поля было определено как "UNIQUE" в ходе создания таблицы. В этом случае функция возвращает первый элемент массива $result (элемент под номером ноль). Если массив пуст, функция возвращает значение "null".

Примечание по безопасности. Для базы данных MySQL строка $name не используется для предотвращения инъекций 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, который является внешним ключом для таблицы пожеланий.

Примечание. Значение идентификатора потребуется только начиная с урока 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 ([variables representing name and password of new wisher]). При выполнении запроса добавляется новая запись в таблицу "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);
    
    //Поскольку пользователь имеет уникальное значение, ожидается только одна строка
    $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);
                        или
     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');
    
    
    
    /** Проверьте, существует ли пользователь с именем, введенным в поле "пользователь" */ 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);
    
    //Все имена пользователей должны быть уникальными. Проверьте, существует ли запрашиваемый пользователь.
    $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