blob: 051554be355941ee92533a3fc23691657f7443ab [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から任意の関数>>を使用できます。このような方法によって、問合せまたは関数への変更を1箇所で行うことができ、アプリケーション・コード全体を解析する必要はありません。
WishDBから関数を使用する場合、WishDBの変数の値は変更しません。かわりに、WishDBのオブジェクト作成用の設計図としてWishDBクラスを使用して、そのオブジェクトの変数の値を変更します。そのオブジェクトの操作が終了すると、そのオブジェクトは破棄されます。WishDBクラス自体の値は変わらないので、クラスを何度も再利用できます。場合によっては、クラスのインスタンスが複数同時に存在したり、同時に存在するインスタンスが1つのみ、つまり「単一」クラスにする場合もあります。このチュートリアルのWishDBは単一クラスです。
クラスのオブジェクトの作成を示す用語はクラスの「インスタンス化」で、オブジェクトを別の言葉で言うと、クラスの「インスタンス」です。クラスとオブジェクトを使用するプログラミングを示す一般的な用語は、「オブジェクト指向プログラミング」、またはOOPです。PHP 5では、高度なOOPモデルを使用します。詳細は、link:http://us3.php.net/zend-engine-2.php[+php.net+]を参照してください。
このチュートリアルでは、データベース・コール機能を個々のPHPファイルからWishDBクラスに移動します。また、MySQLのユーザーは、手続き型の `mysqli` コールをオブジェクト指向のコールに置き換えます。これは、アプリケーションの新しいオブジェクト指向設計に沿っています。
現在のドキュメントは、PHP向けのNetBeans IDEでのCRUDアプリケーションの作成というPHPチュートリアルの一部です。
[[previousLessonSourceCode]]
== 前のレッスンからのアプリケーション・ソース・コード
MySQLユーザー: 前のレッスンが完了した後のプロジェクトの状態を反映したソース・コードをダウンロードするには、link:https://netbeans.org/files/documents/4/1929/lesson3.zip[+ここ+]をクリックします。
Oracleデータベース・ユーザー: 前のレッスンが完了した後のプロジェクトの状態を反映したソース・コードをダウンロードするには、link:https://netbeans.org/projects/www/downloads/download/php%252Foracle-lesson3.zip[+ここ+]をクリックします。
[[createDbPhpFile]]
== db.phpファイルの作成
「ソース・ファイル」フォルダに新しいサブフォルダを作成します。フォルダに「Includes」という名前を付けます。db.phpという名前の新しいファイルを作成し、このファイルを「Includes」に配置します。後で、他のPHPファイルでインクルードされる別のファイルをこのフォルダに追加できます。
*db.phpを新しいフォルダに作成するには:*
1. 「ソース・ファイル」ノードでマウスの右ボタンをクリックし、コンテキスト・メニューから「新規」>「フォルダ」を選択します。「新規フォルダ」ダイアログが開きます。
2. 「フォルダ名」フィールドに「Includes」と入力します。次に、「終了」をクリックします。
3. Includes」ノードでマウスの右ボタンをクリックし、コンテキスト・メニューから「新規」>「PHPファイル」を選択します。「新規PHPファイル」ダイアログが開きます。
4. 「ファイル名」フィールドに「db」と入力します。次に、「終了」をクリックします。
[[wishDBClass]]
== WishDBクラスの作成
WishDBクラスを作成するには、クラスの変数を初期化し、クラスのコンストラクタを実装する必要があります。MySQLユーザーは、WishDBクラスが `mysqli` _拡張_することに注意してください。これは、WishDBPHP mysqliクラスの関数およびその他の特性を_継承_することを意味します。 `mysqli` 関数をクラスに追加するときに、この重要性が理解できます。
db.phpファイルを開き、WishDBクラスを作成します。クラス内では、データベースの所有者(ユーザー)の名前とパスワード、データベースの名前、およびデータベース・ホストを格納するためのデータベース構成変数を宣言します。これらの変数の宣言はすべて「private」で、これは、WishDBクラスの外部から宣言の初期値にアクセスできないことを意味します(link:http://us3.php.net/manual/en/language.oop5.visibility.php[+php.net+]を参照)。また、WishDBのインスタンスを格納する、private_ static_ `$instance` 変数も宣言します。「static」キーワードは、クラスのインスタンスが存在しない場合でも、クラス内の関数は変数にアクセスできることを意味します。
*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ファイルの場合、これらのPHPファイルは、WishDBクラスのオブジェクトを作成(インスタンス化)する関数をコールする必要があります。WishDBlink:http://www.phpclasses.org/browse/package/1151.html[+単一クラス+]として設計されており、これは、同時に存在するクラスのインスタンスが1つのみであることを意味します。そのため、重複するインスタンスが作成される可能性のある、外部からの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` 関数は、「public」および「static」です。「public」とは、クラスの外部から自由にアクセスできることを意味します。「static」とは、クラスがインスタンス化されていない場合でも、その関数が使用可能であることを意味します。 `getInstance` 関数はクラスをインスタンス化するためにコールされるため、staticである必要があります。この関数は静的な `$instance` 変数にアクセスし、その値をクラスのインスタンスとして設定します。
ダブルコロン(::)はスコープ解決演算子と呼ばれ、静的関数にアクセスするために `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クラスの次の関数を実装します。
* ウィッシャの名前に基づいてウィッシャのIDを取得する<<getIDByName,get_wisher_id_by_name>>
* 特定のIDを持つウィッシャのウィッシュのリストを取得する<<getWishesByID,get_wishes_by_wisher_id>>
* wishers表に新しいウィッシャのレコードを追加する<<createWisher,create_wisher>>
[[getIDByName]]
=== 関数get_wisher_id_by_name
この関数は、入力パラメータとしてウィッシャの名前が必要で、ウィッシャのIDを返します。
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=[ウィッシャの名前の変数]` を実行します。問合せの結果は、問合せに一致するレコードのIDの配列です。配列が空でない場合は自動的に要素を1つ含むことを意味し、これは、表の作成時にフィールド名がUNIQUEとして指定されたためです。この場合、関数は `$result` 配列の最初の要素(番号が0の要素)を返します。配列が空の場合、関数はnullを返します。
*セキュリティ上の注意:* MySQLデータベースの場合、 `$name` 文字列は、SQLインジェクション攻撃を避けるためにエスケープされます。link:http://en.wikipedia.org/wiki/SQL_injection[+SQLインジェクションに関するWikipedia+]およびlink:http://us3.php.net/mysql_real_escape_string[+mysql_real_escape_stringのドキュメント+]を参照してください。このチュートリアルのコンテキストでは、有害なSQLインジェクションのリスクはありませんが、そのような攻撃のリスクになるようなMySQL問合せの文字列はエスケープすることをお薦めします。Oracleデータベースでは、バインド変数を使用してこの問題を回避します。
[[getWishesByID]]
=== 関数get_wishes_by_wisher_id
この関数は、入力パラメータとしてウィッシャの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データベースでは、データベースのパフォーマンスとセキュリティ上の理由でバインド変数を使用します。)この選択は、 `wishes` 表の外部キーであるwisherIDによって実行されます。
*注意:* `id` 値はレッスン7までは不要です。
[[createWisher]]
=== 関数create_wisher
この関数は、wishers表に新しいレコードを作成します。この関数は、入力パラメータとして新しいウィッシャの名前とパスワードを必要とし、何もデータを返しません。
次のコード・ブロックを入力します。
*MySQLデータベースの場合:*
[source,java]
----
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データベースの場合:*
[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 ([新しいウィッシャの名前とパスワードを表す変数])` を実行します。問合せによって、 `$name` `$password` の値がそれぞれ入力された「name」フィールドと「password」フィールドを持つ「wishers」表に、新しいレコードが追加されます。
[[refactoring]]
== アプリケーション・コードのリファクタリング
データベースを操作するための別のクラスができたので、重複したブロックを、このクラスからの関連する関数へのコールに置き換えることができます。これは、今後のミススペルと不整合を回避するのに役立ちます。機能に影響しないコードの最適化は、リファクタリングと呼ばれます。
[[refactoringWishlistFile]]
=== wishlist.phpファイルのリファクタリング
wishlist.phpファイルは短く、具体的に改良できるため、このファイルから開始します。
1. <?php?>ブロックの先頭に次の行を入力して、 `db.php` ファイルを使用できるようにします。
[source,php]
----
require_once("Includes/db.php");
----
[start=2]
. データベースに接続してウィッシャのIDを取得するコードを、 `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" );
}
----
新しいコードは、最初にWishDB `getInstance` 関数をコールします。 `getInstance` 関数はWishDBのインスタンスを返し、コードはそのインスタンス内の `get_wisher_id_by_name` 関数をコールします。リクエストされたウィッシャがデータベース内で見つからない場合、コードはプロセスを強制終了し、エラー・メッセージを表示します。
データベースへの接続を開くコードは、ここでは必要ありません。WishDBクラスのコンストラクタによって接続が開かれます。名前またはパスワード(あるいはその両方)を変更した場合は、WishDBクラスの関連する変数のみ更新する必要があります。
[start=3]
. IDによって識別されるウィッシャのウィッシュを取得するコードを、 `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` 関数をコールするか、またはデータベース接続を持つインスタンスを破棄した場合でも、接続を使用するすべてのリソースを解放して、接続が正しく切断されるようにする必要があります。
===  
=== createNewWisher.phpファイルのリファクタリング
リファクタリングは、HTML入力フォーム、または関連するエラー・メッセージを表示するためのコードには影響がありません。
1. <?php?>ブロックの先頭に次のコードを入力して、 `db.php` ファイルを使用できるようにします。
[source,java]
----
require_once("Includes/db.php");
----
[start=2]
. データベース接続の資格情報( `$dbHost,` など)を削除します。これらは現在 `db.php` にあります。
[start=3]
. データベースに接続してウィッシャのIDを取得するコードを、 `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;
}
----
== 現在のレッスン完了後のアプリケーション・ソース・コード
MySQLユーザー: このレッスンが完了した後のプロジェクトの状態を反映したソース・コードをダウンロードするには、link:https://netbeans.org/projects/www/downloads/download/php%252Flesson4.zip[+ここ+]をクリックします。
Oracleデータベース・ユーザー: このレッスンが完了した後のプロジェクトの状態を反映したソース・コードをダウンロードするには、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[+Classes and Objects+]
PHPコードのリファクタリングについては、次を参照してください。
* link:http://www.slideshare.net/spriebsch/seven-steps-to-better-php-code-presentation/[+Seven Steps To Better PHP Code+]
* link:http://www.dokeos.com/wiki/index.php/Refactoring[+PHP Refactoring+]
link:/about/contact_form.html?to=3&subject=Feedback:%20PHP%20Wish%20List%20CRUD%204:%20Optimizing%20Code[+このチュートリアルに関するご意見をお寄せください+]
link:../../../community/lists/top.html[+users@php.netbeans.orgメーリング・リストに登録する+]ことによって、NetBeans IDE PHP開発機能に関するご意見やご提案を送信したり、サポートを受けたり、最新の開発情報を入手したりできます。
link:../../trails/php.html[+PHPの学習に戻る+]