blob: b8dab0a49132f42bd39037c5ee52a740892476dd [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= Создание приложения на основе базы данных на языке PHP
:jbake-type: tutorial
:jbake-tags: tutorials
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: Создание приложения на основе базы данных на языке PHP - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, Создание приложения на основе базы данных на языке PHP
= Урок 4: Оптимизация кода путем добавления классов и объектов
:jbake-type: tutorial
:jbake-tags: tutorials
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: Урок 4: Оптимизация кода путем добавления классов и объектов - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, Урок 4: Оптимизация кода путем добавления классов и объектов
В этом уроке рассматриваются способы оптимизации кода, позволяющие упростить поддержку этого кода в дальнейшем. Данная процедура затрагивает файлы ``createNewWisher.php`` и ``wishlist.php`` . Кроме того, создается новый файл под названием ``db.php`` .
Код приложения содержит несколько похожих блоков с запросами к базе данных. Для упрощения чтения и поддержки кода в будущем можно извлечь эти блоки, реализовать их в качестве функций отдельного класса ``WishDB`` и поместить текст ``WishDB`` в файл ``db.php`` . Впоследствии можно включить файл ``db.php`` в любой файл PHP и использовать любую <<includedFunctions,функцию класса WishDB>> без дублирования кода. Такой подход гарантирует, что любые изменения в запросах или функциях будут выполнены в одном местоположении, и анализировать весь код приложения не потребуется.
При использовании функции класса WishDB не следует изменять значения каких-либо переменных в классе WishDB. Вместо этого необходимо использовать класс в качестве концептуального проекта для создания объекта WishDB и изменять значения переменных в этом объекте. Объект уничтожается при завершении работы. Поскольку значения непосредственно класса WishDB никогда не изменяются, данный класс можно использовать повторно неограниченное число раз. В некоторых случаях может потребоваться одновременно несколько экземпляров класса, а в других случаях будет предпочтителен "одноэкземплярный" класс, имеющий только один экземпляр в любой момент времени. WishDB в данном руководстве представлен как одноэкземплярный класс.
Следует отметить, что создание объекта класса обозначается термином "создание экземпляра" этого класса и что объект в данном случае называется "экземпляром" класса. Общий термин, обозначающий программирование с использованием классов и объектов, "объектно-ориентированное программирование" (ООП). В PHP 5 используется сложная модель ООП. См. link:http://us3.php.net/zend-engine-2.php[+php.net+] для получения дополнительной информации.
В данном руководстве вы перемещаете функциональность вызова базы данных из отдельных файлов РНР в класс WishDB. Пользователи MySQL также заменяют процедурные вызовы ``mysqli`` объектно-ориентированными. Это соответствует новой объектно-ориентированной структуре приложения.
Текущий документ является частью краткого учебного курса "Создание приложения CRUD в IDE NetBeans для PHP".
[[previousLessonSourceCode]]
== Исходный код приложения из предыдущего урока
Для пользователей MySQL: перейдите по link:https://netbeans.org/files/documents/4/1929/lesson3.zip[+этой ссылке+] для загрузки исходного кода, описывающего состояние проекта на момент завершения предыдущего урока.
Для пользователей Oracle Database: щелкните link:https://netbeans.org/projects/www/downloads/download/php%252Foracle-lesson3.zip[+здесь+] для загрузки исходного кода, отражающего состояние проекта по завершении предыдущего урока.
[[createDbPhpFile]]
== Создание файла db.php
Создайте новую подпапку в папке "Исходные файлы". Дайте папке имя Includes. Создайте новый файл под именем db.php и поместите его в папку Includes. Позже вы сможете добавлять в эту папку файлы, которые будут включаться в другие РНР-файлы.
*Чтобы создать файл db.php в новой папке, сделайте следующее:*
1. Щелкните правой кнопкой мыши узел "Source Files" и выберите "New > Folder" в контекстном меню. Откроется диалоговое окно "Новая папка"
2. В поле "Имя папки" введите Includes. Затем нажмите кнопку "Готово".
3. Щелкните правой кнопкой мыши узел "Includes" и выберите "New > PHP File" в контекстном меню. Откроется диалоговое окно "Новый файл РНР".
4. В поле "Имя файла" введите db. Затем нажмите кнопку "Готово".
[[wishDBClass]]
== Создание класса WishDB
Для создания класса WishDB необходимо инициализировать переменные класса и реализовать конструктор класса. Пользователи MySQL, обратите внимание, что класс WishDB _расширяет_ ``mysqli`` . Это означает, что WishDB _наследует_ функции и другие характеристики класса PHP mysqli. Вы убедитесь в важности этого при добавлении функций ``mysqli `` к классу.
Откройте файл db.php и создайте класс WishDB. В данном классе объявите переменные настройки базы данных для хранения имени и пароля собственника базы данных (пользователя), имени и машины размещения базы данных. Все объявления переменных являются закрытыми: это означает, что начальные значения в этих объявлениях недоступны вне класса WishDB (см. link:http://us3.php.net/manual/en/language.oop5.visibility.php[+php.net+]). Вы также объявляете закрытую _ статическую переменную_ ``$instance`` , которая хранит экземпляр WishDB. Ключевое слово "статический" означает, что функции в классе имеют доступ к переменной даже при отсутствии экземпляра класса.
*Для базы данных MySQL:*
[[source,php]
----
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:*
[source,php]
----
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;
}
----
[[instantiate-wishdb]]
== Создание экземпляров класса WishDB
При использовании функций класса WishDB в других файлах PHP должна быть вызвана функция, позволяющая создать объект ("создать экземпляр") класса WishDB. WishDB разработан в качестве link:http://www.phpclasses.org/browse/package/1151.html[+одноэкземплярного класса+]; это означает, что в любой определенный момент времени может существовать только один экземпляр класса. Поэтому рекомендуется предотвращать создание экземпляра WishDB, которое осуществляется извне и способствует появлению дублирующихся экземпляров.
Внутри класса WishDB введите или вставьте следующий код:
[source,php]
----
// 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`` используется имя класса. См. ресурс link:http://us3.php.net/manual/en/language.oop5.paamayim-nekudotayim.php[+php.net для получения информации об операции разрешения диапазона+].
[[wishdb-constructor]]
== Добавление конструктора к классу WishDB
Класс может содержать в себе специальный метод, известный как "конструктор", который выполняется автоматически каждый раз при создании экземпляра этого класса. В данном руководстве рассматривается добавление к классу WishDB конструктора, который подключается к базе данных каждый раз при создании экземпляра WishDB.
Добавьте к WishDB следующий код:
*Для базы данных MySQL*
[source,php]
----
// 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*
[source,php]
----
// 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`` используется при вызове метода внутри контекста объекта. Она ссылается на значение переменной внутри этого объекта.
[[includedFunctions]]
== Функции класса WishDB
В этом уроке рассматривается реализация следующих функций класса WishDB:
* <<getIDByName,get_wisher_id_by_name>> для извлечения идентификатора пользователя на основе имени
* <<getWishesByID,get_wishes_by_wisher_id>> для извлечения списка пожеланий "Wish list", принадлежащего определенному пользователю с соответствующим идентификатором
* <<createWisher,create_wisher>> для добавления нового пользователя в таблицу "Wishers".
=== Функция get_wisher_id_by_name
Эта функция возвращает идентификатор пользователя, а в качестве входного параметра для ее выполнения требуется имя пользователя.
После функции WishDB введите или вставьте следующую функцию в класс WishDB:
*Для базы данных MySQL*
[[source,php]
----
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*
[source,php]
----
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-инъекций. См. link:http://en.wikipedia.org/wiki/SQL_injection[+статью энциклопедии Wikipedia о введении SQL +] и link:http://us3.php.net/mysql_real_escape_string[+документацию mysql_real_escape_string+]. Несмотря на то, что в контексте этого руководства риск возникновения опасных атак внедрения SQL маловероятен, рекомендуется исключить из участия в запросах MySQL такие строки, которые могли бы быть подвержены подобной атаке. База данных Oracle избегает данной проблемы, используя связанные переменные.
[[getWishesByID]]
=== Функция get_wishes_by_wisher_id
Эта функция возвращает зарегистрированные пожелания пользователя, и для ее выполнения в качестве входного параметра требуется идентификатор пользователя.
Введите следующий блок кода:
*Для базы данных MySQL*
[[source,php]
----
public function get_wishes_by_wisher_id($wisherID) {
return $this->query("SELECT id, description, due_date FROM wishes WHERE wisher_id=" . $wisherID);
}
----
*Для базы данных Oracle*
[source,php]
----
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.
[[createWisher]]
=== Функция create_wisher
Функция создает новую запись в таблице "Wishers". Эта функция не возвращает каких-либо данных, и в качестве входных параметров для ее выполнения требуется имя и пароль нового пользователя.
Введите следующий блок кода:
*Для базы данных MySQL*
[source,php]
----
public function create_wisher ($name, $password) {
$name = $this->real_escape_string($name);
$password = $this->real_escape_string($password);
return $this->query("INSERT INTO wishers (name, password) VALUES ('" . $name . "', '" . $password . "')");
}
----
*Для базы данных Oracle*
[source,php]
----
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);
return $stid;
}
----
Блок кода выполняет запрос ``"INSERT wishers (Name, Password) VALUES ([переменные представляющие имя и пароль нового пожелания])`` . При выполнении запроса добавляется новая запись в таблицу "Wishers" с полями "name" и "password", заполненными значениями ``$name`` и ``$password`` соответственно.
[[refactoring]]
== Реорганизация кода приложения
Теперь при наличии отдельного класса для работы с базой данных дублированные блоки можно заменить вызовами соответствующих функций из этого класса. Это помогает в дальнейшем избежать ошибок и противоречий в написании кода. Усовершенствование кода, не оказывающее влияния на функциональные возможности, называется "реорганизацией".
[[refactoringWishlistFile]]
=== Реорганизация файла wishlist.php
Начнем с файла wishlist.php, поскольку он небольшой и дает возможность представить оптимизацию более иллюстративно.
1. В верхней части блока <? php? > введите следующую строку, делающую возможным использование файла ``db.php`` :
[source,php]
----
require_once("Includes/db.php");
----
[start=2]
. Замените код, который подключается к базе данных и получает идентификатор пожелания, вызовом функции ``get_wisher_id_by_name`` .
Для *базы данных MySQL* вы заменяете следующий код:
[source,php]
----
// to remove
$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);
// to replace
$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 * вы заменяете следующий код:
[source,php]
----
// to remove
$con = oci_connect("phpuser", "phpuserpw", "localhost/XE");
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'];
// to replace
$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.
[start=3]
. Замените код, который получает пожелания для автора пожеланий, идентифицированного с помощью кода, кодом, который вызывает функцию ``get_wishes_by_wisher_id`` .
Для *базы данных MySQL * вы заменяете следующий код:
[source,php]
----
// to remove
$result = mysqli_query($con, "SELECT description, due_date FROM wishes WHERE wisher_id=" . $wisherID);
// to replace
$result = WishDB::getInstance()->get_wishes_by_wisher_id($wisherID);
----
Для *базы данных Oracle * вы заменяете следующий код:
[source,php]
----
// to remove
$query = "SELECT description, due_date FROM wishes WHERE wisher_id = :id_bv";
$stid = oci_parse($con, $query);
oci_bind_by_name($stid, ":id_bv", $wisherID);
oci_execute($stid);
// to replace
$stid = WishDB::getInstance()->get_wishes_by_wisher_id($wisherID);
----
[start=4]
. Удалите строку, которая закрывает подключение к базе данных.
[source,php]
----
// For MYSQL database
mysqli_close($con);
// For Oracle database
oci_close($con);
----
Код не нужен, потому что подключение к базе данных автоматически закрывается при уничтожении объекта WishDB. Однако рекомендуем сохранять код, освобождающий ресурс. Вам необходимо освободить все ресурсы, которые используют подключение, чтобы убедиться в том, что оно закрыто, даже при вызове функции ``close`` или уничтожении экземпляра с подключением к базе данных.
[[refactoringCreateNewWisher]]
=== Реорганизация файла createNewWisher.php
Реорганизация не оказывает воздействия на форму ввода HTML или код для вывода на экран соответствующих сообщений об ошибках.
1. В верхней части блока <? php? > введите следующий код, делающий возможным использование файла ``db.php`` :
[source,php]
----
require_once("Includes/db.php");
----
[start=2]
. Удалите подтверждения подключения к базе данных ( ``$dbHost`` и пр.). Теперь они находятся в ``db.php`` .
[start=3]
. Замените код, который подключается к базе данных и получает идентификатор пожелания, вызовом функции ``get_wisher_id_by_name`` .
Для *базы данных MySQL * вы заменяете следующий код:
[source,php]
----
// to remove
$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;
}
// to replace
$wisherID = WishDB::getInstance()->get_wisher_id_by_name($_POST["user"]);
if ($wisherID) {
$userNameIsUnique = false;
}
----
Для *базы данных Oracle * вы заменяете следующий код:
[source,php]
----
// to remove
$con = oci_connect("phpuser", "phpuserpw", "localhost/XE", "AL32UTF8");
if (!$con) {
$m = oci_error();
exit('Connect Error ' . $m['message']);
}
$query = "SELECT id FROM wishers WHERE name = :user_bv";
$stid = oci_parse($con, $query);
$user = $_POST['user'];
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) {
$userNameIsUnique = false;
}
// to replace
$wisherID = WishDB::getInstance()->get_wisher_id_by_name($_POST["user"]);
if ($wisherID) {
$userNameIsUnique = false;
}
----
Объект ``WishDB`` существует до тех пор, пока обрабатывается текущая страница. Если обработка завершена или прервана, этот объект уничтожается. Код для открытия подключения к базе данных не является необходимым, поскольку подключение выполняется посредством функции WishDB. Код для закрытия подключения также не является необходимым, поскольку подключение будет закрыто сразу же после уничтожения объекта ``WishDB`` .
[start=4]
. Замените код, который вставляет новых авторов пожеланий в базу данных, кодом, который вызывает функцию ``create_wisher`` .
Для *базы данных MySQL * вы заменяете следующий код:
[source,php]
----
// to remove
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;
}
// to replace
if (!$userIsEmpty && $userNameIsUnique && !$passwordIsEmpty && !$password2IsEmpty && $passwordIsValid) {
WishDB::getInstance()->create_wisher($_POST["user"], $_POST["password"]);
header('Location: editWishList.php' );
exit;
}
----
Для *базы данных Oracle * вы заменяете следующий код:
[source,php]
----
// to remove
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_free_statement($stid);
oci_close($con);
header('Location: editWishList.php');
exit;
}
// to replace
if (!$userIsEmpty && $userNameIsUnique && !$passwordIsEmpty && !$password2IsEmpty && $passwordIsValid) {
WishDB::getInstance()->create_wisher($_POST["user"], $_POST["password"]);
header('Location: editWishList.php' );
exit;
}
----
[[lessonResultSourceCode]]
== Исходный код приложения на момент завершения текущего урока
Для пользователей MySQL: щелкните link:https://netbeans.org/projects/www/downloads/download/php%252Flesson4.zip[+сюда+] для загрузки исходного кода, отражающего состояние проекта по завершении данного урока.
Для пользователей Oracle Database: щелкните link:https://netbeans.org/projects/www/downloads/download/php%252Foracle-lesson4.zip[+здесь+] для загрузки исходного кода, отражающего состояние проекта по завершении данного урока.
== Что дальше?
link:wish-list-lesson3.html[+<<Предыдущий урок+]
link:wish-list-lesson5.html[+Следующий урок >>+]
link:wish-list-tutorial-main-page.html[+Назад на главную страницу руководства+]
== Полезные ссылки
Дополнительная информация об использовании классов в PHP:
* link:http://us3.php.net/manual/en/language.oop5.php[+Классы и объекты+]
Дополнительная информация о реорганизации кода PHP:
* link:http://www.slideshare.net/spriebsch/seven-steps-to-better-php-code-presentation/[+Семь действий по усовершенствованию кода PHP+]
* link:http://www.dokeos.com/wiki/index.php/Refactoring[+Реорганизация PHP+]
link:/about/contact_form.html?to=3&subject=Feedback:%20PHP%20Wish%20List%20CRUD%204:%20Optimizing%20Code[+Отправить отзыв по этому учебному курсу+]
Для отправки комментариев и предложений, получения поддержки и новостей о последних разработках, связанных с PHP IDE NetBeans link:../../../community/lists/top.html[+присоединяйтесь к списку рассылки users@php.netbeans.org+].
link:../../trails/php.html[+Возврат к учебной карте PHP+]