1. Objek DBQuery
Sekarang, objek DBQuery kami hanya mengemulasi prosedur tersimpan - setelah dijalankan, ia mengembalikan sumber daya hasil yang harus disimpan; dan jika Anda ingin menggunakan fungsi pada kumpulan hasil (seperti num_rows() atau Fetch_row() ) ), Anda harus meneruskan objek MySqlDB. Jadi, apa efeknya jika objek DBQuery mengimplementasikan fungsi yang diterapkan oleh objek MySqlDB (yang dirancang untuk beroperasi pada hasil query yang dieksekusi)? Mari lanjutkan menggunakan kode dari contoh sebelumnya; dan asumsikan sumber daya hasil kita sekarang dikelola oleh objek DBQuery. Kode sumber kelas DBQuery ditunjukkan pada Listing 1.
Listing 1. Menggunakan kelas DBQuery.
memerlukan 'mysql_db.php';
require_once 'query.php';
$db = MySqlDb baru;
$db->connect('host', 'nama pengguna', 'lulus');
$db->query('gunakan sistem_manajemen_konten');
$query = DBQuery baru($db);
$query->prepare('PILIH fname,sname DARI pengguna WHERE nama pengguna=:1S DAN pword=:2S DAN expired_time<:3I');
mencoba {
if($query->execute("visualad", "apron", time()))->num_rows() == 1) {
echo('Kredensial Benar');
} kalau tidak {
echo('Kredensial Salah / Sesi Kedaluwarsa');
}
} tangkapan (QueryException $e) {
echo('Kesalahan saat menjalankan query: ' .$e);
}
Yang paling menarik perhatian kami pada kode yang dimodifikasi di atas adalah pernyataan catch dan pernyataan eksekusi.
· Pernyataan eksekusi tidak lagi mengembalikan sumber daya hasil, namun sekarang mengembalikan objek DBQuery itu sendiri.
· Objek DBQuery sekarang mengimplementasikan fungsi num_rows()—yang sudah kita kenal dari antarmuka DB.
· Jika eksekusi query gagal, maka akan muncul pengecualian bertipe QueryException. Ketika dikonversi ke string, ia mengembalikan rincian kesalahan yang terjadi.
Untuk melakukan ini, Anda perlu menggunakan proxy. Faktanya, Anda sudah menggunakan proxy di objek DBQuery kami, tetapi sekarang Anda akan menggunakannya secara lebih mendalam untuk mengikatnya secara erat ke objek MySqlDB. Objek DBQuery telah diinisialisasi dengan objek yang mengimplementasikan antarmuka DB, dan objek tersebut sudah berisi fungsi anggota mengeksekusi—yang memanggil metode query() objek DB untuk mengeksekusi kueri. Objek DBQuery sendiri tidak benar-benar menanyakan database, ia menyerahkan tugas ini ke objek DB. Ini adalah proxy, yaitu proses dimana suatu objek dapat mengimplementasikan perilaku tertentu dengan mengirimkan pesan ke objek lain yang mengimplementasikan perilaku yang sama atau serupa.
Untuk melakukan ini, Anda perlu memodifikasi objek DBQuery untuk menyertakan semua fungsi yang beroperasi pada sumber daya hasil dari objek DB. Anda perlu menggunakan hasil yang disimpan saat menjalankan kueri untuk memanggil fungsi yang sesuai dari objek DB dan mengembalikan hasilnya. Fungsi-fungsi berikut akan ditambahkan:
Listing 2: Memperluas kelas DBQuery menggunakan proxy.
kelasDBQuery
{
.....
fungsi publik ambil_array()
{
if (!is_resource($ini->hasil)) {
throw new Exception('Query tidak dieksekusi.');
}
kembalikan $ini->db->fetch_array($ini->hasil);
}
fungsi publik ambil_baris()
{
if (!is_resource($ini->hasil)) {
throw new Exception('Query tidak dieksekusi.');
}
kembalikan $ini->db->fetch_row($ini->hasil);
}
fungsi publik ambil_assoc()
{
if (!is_resource($ini->hasil)) {
throw new Exception('Query tidak dieksekusi.');
}
kembalikan $ini->db->fetch_assoc($ini->hasil);
}
fungsi publik ambil_objek()
{
if (!is_resource($ini->hasil)) {
throw new Exception('Query tidak dieksekusi.');
}
kembalikan $ini->db->fetch_object($ini->hasil);
}
fungsi publik num_rows()
{
if (!is_resource($ini->hasil)) {
throw new Exception('Query tidak dieksekusi.');
}
kembalikan $ini->db->num_rows($ini->hasil);
}
}
Implementasi setiap fungsi cukup sederhana. Pertama-tama ia memeriksa untuk memastikan kueri telah dieksekusi, kemudian mendelegasikan tugas tersebut ke objek DB, mengembalikan hasilnya seolah-olah itu adalah objek kueri itu sendiri (disebut fungsi basis data dasar).
2. Petunjuk Ketik
Agar proksi dapat berfungsi, kita perlu memastikan bahwa variabel $db dari objek DBQuery adalah turunan dari objek yang mengimplementasikan antarmuka DB. Petunjuk tipe adalah fitur baru di PHP 5 yang memungkinkan Anda untuk memaksakan parameter fungsi ke dalam objek dengan tipe tertentu. Sebelum PHP 5, satu-satunya cara untuk memastikan bahwa parameter fungsi adalah tipe objek tertentu adalah dengan menggunakan fungsi pemeriksaan tipe yang disediakan di PHP (yaitu, is_a()). Sekarang, Anda cukup mentransmisikan tipe objek—dengan mengawali parameter fungsi dengan nama tipe. Anda telah melihat petunjuk tipe dari objek DBQuery kami, yang memastikan bahwa objek yang mengimplementasikan antarmuka DB diteruskan ke konstruktor objek.
fungsi publik __konstruksi(DB $db)
{
$ini->db = $db;
}
Saat menggunakan petunjuk tipe, Anda tidak hanya dapat menentukan tipe objek, tetapi juga kelas abstrak dan antarmuka.
3. Melempar pengecualian
Anda mungkin telah memperhatikan dari kode di atas bahwa apa yang Anda tangkap adalah pengecualian yang disebut QueryException (kita akan mengimplementasikan objek ini nanti). Pengecualian mirip dengan kesalahan, namun lebih umum. Cara terbaik untuk mendeskripsikan pengecualian adalah dengan menggunakan keadaan darurat. Meskipun keadaan darurat mungkin tidak berakibat fatal, namun tetap harus ditangani. Ketika pengecualian dilemparkan di PHP, cakupan eksekusi saat ini dengan cepat dihentikan, baik itu fungsi, blok try..catch, atau skrip itu sendiri. Pengecualian kemudian melintasi tumpukan panggilan—mengakhiri setiap cakupan eksekusi—sampai ia terjebak dalam blok try..catch atau mencapai bagian atas tumpukan panggilan—yang kemudian menghasilkan kesalahan fatal.
Penanganan pengecualian adalah fitur baru lainnya di PHP 5. Ketika digunakan bersama dengan OOP, ini dapat mencapai kontrol yang baik atas penanganan dan pelaporan kesalahan. Blok try..catch adalah mekanisme penting untuk menangani pengecualian. Setelah tertangkap, eksekusi skrip akan dilanjutkan dari baris kode berikutnya tempat pengecualian ditangkap dan ditangani.
Jika kueri gagal, Anda perlu mengubah fungsi eksekusi untuk memberikan pengecualian. Anda akan menampilkan objek pengecualian khusus yang disebut QueryException - objek DBQuery yang menyebabkan kesalahan diteruskan ke objek tersebut.
Listing 3. Melemparkan pengecualian.
/**
*Jalankan kueri saat ini
*
* Jalankan kueri saat ini—ganti titik mana pun dengan argumen yang disediakan
* .
*
* @parameters: campuran $queryParams,... parameter kueri
* @return: Sumber Daya A—referensi yang menjelaskan sumber daya tempat kueri dijalankan.
*/
eksekusi fungsi publik($queryParams = '')
{
//Misalnya: SELECT * FROM table WHERE name=:1S AND type=:2I AND level=:3N
$args = fungsi_get_args();
if ($ini->prosedur_tersimpan) {
/*Panggil fungsi kompilasi untuk mendapatkan kueri*/
$query = call_user_func_array(array($ini, 'kompilasi'), $args);
} kalau tidak {
/*Prosedur tersimpan belum diinisialisasi, oleh karena itu, dijalankan sebagai kueri standar*/
$query = $queryParams;
}
$hasil = $ini->db->query($query);
jika (! $hasil) {
melempar QueryException baru($ini);
}
$ini->hasil = $hasil;
/* Perhatikan bagaimana sekarang kita mengembalikan objek itu sendiri, yang memungkinkan kita memanggil fungsi anggota dari hasil pengembalian fungsi ini */
kembalikan $ini;
}
4. Gunakan pewarisan untuk menampilkan pengecualian khusus
Di PHP, Anda dapat menampilkan objek apa pun sebagai pengecualian, namun, pertama-tama, pengecualian tersebut harus diwarisi dari kelas pengecualian bawaan PHP. Dengan membuat pengecualian khusus Anda sendiri, Anda dapat mencatat informasi lain tentang kesalahan tersebut, membuat entri dalam file log, atau melakukan apa pun yang Anda suka. Pengecualian khusus Anda akan melakukan hal berikut:
· Catat pesan kesalahan dari objek DB yang dihasilkan oleh kueri.
· Berikan detail yang tepat dari baris kode tempat terjadinya kesalahan kueri—dengan memeriksa tumpukan panggilan.
· Menampilkan pesan kesalahan dan teks kueri—ketika dikonversi ke string.
Untuk mendapatkan pesan kesalahan dan teks query, beberapa perubahan perlu dilakukan pada objek DBQuery.
1. Atribut baru yang dilindungi—compiledQuery—perlu ditambahkan ke kelas.
2. Fungsi kompilasi() memperbarui properti compilerQuery dengan teks kueri.
3. Sebuah fungsi harus ditambahkan untuk mengambil teks kueri yang dikompilasi.
4. Sebuah fungsi juga harus ditambahkan - fungsi tersebut mendapatkan objek DB saat ini yang terkait dengan objek DBQuery.
Listing 4. Berikan pengecualian.
kelasDBQuery
{
/**
*Simpan versi kueri yang dikompilasi setelah memanggil kompilasi() atau eksekusi()*
* @var string $compiledQuery
*/
dilindungi $compiledQuery;
/**
* Mengembalikan kueri yang dikompilasi tanpa menjalankannya.
* @parameter: campuran $params,...parameter kueri* @return: string—kueri yang dikompilasi*/
kompilasi fungsi publik($params='')
{
jika (! $ini->prosedur_tersimpan) {
throw new Exception("Prosedur tersimpan belum diinisialisasi.");
}
/*mengganti parameter*/
$params = func_get_args(); //Dapatkan parameter fungsi $query = preg_replace("/(?compile_callback($params, 1, "2")', $this->query);
return ($this->compiledQuery = $this->add_strings($query)); //Masukkan kembali string ke dalam query}
fungsi publik getDB()
{
kembalikan $ini->db;
}
fungsi publik getCompiledQuery()
{
kembalikan $ini->Query yang dikompilasi;
}
}
Sekarang, Anda bisa mengimplementasikan kelas QueryException. Perhatikan bagaimana Anda menelusuri tumpukan panggilan untuk menemukan lokasi di skrip yang sebenarnya menyebabkan kesalahan. Hal ini persis terjadi ketika objek DBQuery yang memunculkan pengecualian adalah subkelas yang mewarisi objek DBQuery.
Daftar 5: Kelas QueryException.
/**
*Pengecualian permintaan
*
*Saat mencoba mengeksekusi kueri, jika terjadi kesalahan, kesalahan akan dilempar oleh objek {@link DBQuery}
*/
kelas QueryException memperluas Pengecualian
{
/**
*Teks pertanyaan*
* @var string $QueryText;
*/
dilindungi $QueryText;
/**
*Nomor/kode kesalahan dari database*
* @var string $Kode Kesalahan
*/
dilindungi $ErrorNumber;
/**
*Pesan kesalahan dari database*
* @var string $Pesan Kesalahan
*/
dilindungi $ErrorMessage;
/**
*Konstruktor kelas*
* @Parameter: DBQuery $db, yang merupakan objek kueri yang memunculkan pengecualian */
fungsi publik __konstruksi(DBQuery $query)
{
/*Dapatkan tumpukan panggilan*/
$backtrace = $ini->GetTrace();
/*Mengatur baris dan file ke lokasi di mana kesalahan sebenarnya terjadi*/
jika (hitung($pelacakan balik) > 0) {
$x = 1;
/*Jika kelas kueri diwarisi, maka kita perlu mengabaikan panggilan yang dibuat oleh subkelas*/
while((!isset($backtrace[$x]['garis'])) ||
(isset($backtrace[$x]['class']) && is_subclass_of($backtrace[$x]['class'], 'DBQuery')) ||
(strpos(strtolower(@$backtrace[$x]['fungsi']), 'call_user_func')) !== false ) {
/*Eksekusi loop, selama tidak ada nomor baris atau fungsi yang dipanggil merupakan subkelas dari kelas DBQuery*/
++$x;
/*Jika kita mencapai bagian terbawah tumpukan, maka kita menggunakan pemanggil pertama*/
if (($x) >= hitungan($pelacakan balik)) {
$x = hitungan($jejak balik);
merusak;
}
}
/*Jika loop di atas dijalankan setidaknya sekali, maka kita dapat menguranginya sebanyak 1 untuk menemukan baris kode sebenarnya yang menyebabkan kesalahan*/
jika ($x != 1) {
$x -= 1;
}
/*Terakhir, kita dapat mengatur nomor file dan baris, yang harus mencerminkan pernyataan SQL yang menyebabkan kesalahan*/
$ini->baris = $lacakan balik[$x]['baris'];
$ini->file = $backtrace[$x]['file'];
}
$ini->QueryText = $query->getCompiledQuery();
$ini->ErrorNumber = $query->getDB()->errno();
$ini->Pesan Kesalahan = $query->getDB()->kesalahan();
/*Panggil konstruktor pengecualian superkelas*/
parent::__construct('Kesalahan Kueri', 0);
}
/**
*dapatkan teks permintaan*
* @mengembalikan teks kueri string */
fungsi publik GetQueryText()
{
kembalikan $ini->QueryText;
}
/**
*mendapat nomor kesalahan*
* @mengembalikan nomor kesalahan string */
fungsi publik GetErrorNumber()
{
kembalikan $ini->ErrorNumber;
}
/**
*mendapat pesan kesalahan*
* @pesan kesalahan string kembali */
fungsi publik GetErrorMessage()
{
kembalikan $ini->Pesan Kesalahan;
}
/**
* Dipanggil ketika objek diubah menjadi string.
* @mengembalikan string */
fungsi publik __toString()
{
$output = "Kesalahan Kueri pada {$this->file} pada baris {$this->line}nn";
$output .= "Permintaan: {$ini->QueryText}n";
$output .= "Kesalahan: {$this->ErrorMessage} ({$this->ErrorNumber})nn";
kembalikan $output;
}
}
Sekarang kode yang Anda lihat di awal bagian ini berfungsi.
5. Kesimpulan
Dalam artikel ini, Anda melihat bagaimana agen memetakan antarmuka DB yang terkait dengan kueri ke operasi pada hasil kueri tertentu. Objek DBQuery mengekspos fungsi yang sama, seperti ambil_assoc(), sebagai objek DB. Namun, ini semua berfungsi untuk satu permintaan. Anda juga mempelajari cara menggunakan pengecualian khusus untuk memberikan informasi mendetail tentang kapan dan di mana kesalahan terjadi, dan bagaimana pengecualian tersebut dapat mengontrol penanganan kesalahan dengan lebih baik.