Validasi dan Sanitasi Form dengan PHP
Salah satu fitur PHP yang paling penting adalah kemampuannya untuk mengolah form HTML. Form adalah halaman yang hampir dapat dijumpai pada setiap website, contohnya adalah form untuk login, form pendaftaran, form kontak, dll. Mengolah form HTML dengan PHP sangat mudah, dan karena kemudahannya inilah banyak programer yang membuat form secara sembarangan.
Validasi dan sanitasi data adalah 2 hal yang tidak bisa dilepaskan ketika bekerja dengan form. Data yang tidak valid dan tidak bersih, tidak hanya akan menghasilkan informasi yang salah tetapi juga akan menimbulkan masalah keamanan. Artikel ini akan membahas proses pembuatan form dari yang sederhana sampai yang cukup kompleks dengan validasi dan sanitasi data. Hasil akhir diharapkan mampu meminimalkan kesalahan dan masalah kemananan pada form.
Contoh 1. Pengolahan form paling sederhana dengan PHP
nama file: test.html
<form name="form_test" method="post" action="test.php"> <p><label for="email">Email </label><input name="email" type="text"/></p> <p><label for="website">Website </label><input name="website" type="text" /></p> <p><input type="submit" name="submit" value="kirim"></p> </form>
nama file: test.php
<?php echo 'Email: ' . $_POST['email'] . '<br />'; echo 'Website: ' . $_POST['website'];
Pada contoh di atas, tidak ada kode yang memvalidasi dan menyanitasi masukan dari pengguna. Akibatnya apapun data yang dimasukan oleh pengguna akan langsung diproses oleh PHP. Hal ini tidak boleh terjadi. Salah satu masalah yang mungkin terjadi adalah ketika pengguna tidak memasukan data sesuai format dengan benar (baik email maupun website). Sebenarnya HTML5 dapat sedikit membantu tugas para programer, yaitu dengan mengganti nilai type
menjadi “email
” dan “url
“.
Contoh 2. HTML5 form
nama file: test.html
<form name="form_test" method="post" action="test.php"> <p><label for="email">Email </label><input name="email" type="email"/></p> <p><label for="website">Website </label><input name="website" type="url" /></p> <p><input type="submit" name="submit" value="kirim"></p> </form>
Dengan kode di atas, sekarang akan muncul tooltips ketika pengguna salah memasukan email atau website. Sebagai catatan, belum semua browser mendukung fitur ini, jadi validasi dan sanitasi pada server side (kode PHP) mutlak diperlukan.
Memeriksa validitas dan membersihkan data dari karakter-karakter yang tidak diinginkan adalah pekerjaan yang tidak mudah. Untungnya, mulai versi 5.2, PHP mempunyai function yang disebut filter_var
yang akan membuat pekerjaan programer menjadi jauh lebih mudah. Function filter_var
dapat melakukan kedua hal tersebut, baik validasi maupun sanitasi data. Sebenarnya apakah perbedaan di antara keduanya?
- Validasi: menentukan apakah sebuah data memiliki format yang valid.
- Sanitasi: menghilangkan karakter-karakter yang tidak diinginkan pada sebuah data.
Mengapa harus keduanya? Karena ada kemungkinan pengguna, baik sengaja maupun tidak sengaja memasukan karakter yang bisa berakibat buruk pada aplikasi. Berikut ini adalah contoh penggunaan filter_var
.
<?php $string = "<h1>Hello, World!</h1>"; $string = filter_var($string, FILTER_SANITIZE_STRING); echo 'variabel $string bernilai "' . $string . '"';
Tampak pada contoh kode di atas, filter_var
akan menghilangkan semua tag HTML pada suatu string
. Sekarang ubah file “test.php” di atas untuk memvalidasi dan menyanitasi masukan email dan website.
Contoh 3. PHP filter_var
nama file: test.php
<?php if (!empty($_POST['email'])) { $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL); if (filter_var($email, FILTER_VALIDATE_EMAIL)) { echo "Email $email valid.<br/>"; } else { echo "Email $email tidak valid.<br/>"; } } else { echo "Email harus diisi.<br/>"; } if (!empty($_POST['website'])) { $website = filter_var($_POST['website'], FILTER_SANITIZE_URL); if (filter_var($website, FILTER_VALIDATE_URL)) { echo "Website $website valid."; } else { echo "Website $website tidak valid."; } } else { echo "Website harus diisi"; }
Sebagai catatan, alamat website yang valid itu harus mengandung “http://
“. Kode !empty($_POST['email'])
adalah untuk memeriksa apakah pengguna memasukan data atau tidak.
Selain dengan filter_var
, masih ada cara lain yang bisa digunakan, terutama jika server belum mendukung function ini, yaitu dengan htmlspecialchars
. Function htmlspecialchars
akan mengkonversi 5 karakter khusus menjadi representasinya dalam bentuk kode. Kelima karakter tersebut beserta representasinya adalah sebagai berikut.
- ‘
&
‘ (ampersand) dikonversi menjadi ‘&
‘. - ‘
"
‘ (double quote) dikonversi menjadi ‘"
‘. - ‘
'
‘ (single quote) dikonversi menjadi ‘'
‘. - ‘
<
' (less than) dikonversi menjadi '<
' - '
>
' (greater than) dikonversi menjadi '>
'
Contoh penggunaannya adalah sebagai berikut.
<?php $string = htmlspecialchars("<a href='test'>Test string</a>", ENT_QUOTES); echo $string; // <a href='test'>Test string</a>
Jalankan kode di atas, lalu view source hasilnya. Hasil view source akan sama seperti yang ada pada baris komentar di atas. Pada function htmlspecialchars
di atas, terdapat argumen ENT_QUOTES
yang berarti akan mengkonversi ''
' (single quote) dan '"
' (double quote). Jika argumennya ENT_NOQUOTES
, kedua quote tidak akan dikonversi.
Akhirnya, jika bekerja dengan database, dibutuhkan suatu function yang akan melindungi database dari serangan SQL Injection. Function mysql_real_escape_string
akan membersihkan semua data yang akan dimasukan ke dalam database.
<?php $email = mysql_real_escape_string($email); $website = mysql_real_escape_string($website); $query = 'UPDATE nama_tabel SET website = ' . $website . ' WHERE email = ' . $email; mysql_query($query);
Memahami SQL Join dengan Diagram Venn
Query JOIN
pada bahasa SQL berfungsi untuk menggabungkan data dari 2 atau lebih tabel dalam sebuah database. Syntax SQL mempunyai 4 tipe JOIN
, yaitu: INNER
, OUTER
, LEFT
, dan RIGHT
.
Diagram Venn adalah diagram yang menunjukan semua kemungkinan relasi logis antara sekelompok variabel yang berbeda. Di sekolah dasar, Diagram Venn dikenalkan pada saat mempelajari teori himpunan pada Matematika. Diagram Venn biasanya dilambangkan dengan 2 atau lebih lingkaran yang saling berpotongan (meskipun tidak harus selalu demikian).
Misalkan kita mempunyai 2 buah tabel pada database, TabelA dan TabelB. TabelA di sebelah kiri dan TabelB di sebelah kanan. Kemudian kita akan mengisi masing-masing dengan 4 buah data.
id nama id nama -- ---- -- ---- 1 Andik 1 Andik 2 Binta 2 Bambi 3 Carli 3 Conan 4 Didut 4 Didut
Sekarang kita coba gabungkan kedua tabel di atas menggunakan tipe-tipe JOIN
yang ada.
1. INNER JOIN
SELECT * FROM TabelA INNER JOIN TabelB ON TabelA.nama = TabelB.nama id nama id nama -- ---- -- ---- 1 Andik 1 Andik 4 Didut 4 Didut
INNER JOIN
hanya akan menampilkan data yang cocok satu sama lain (berpasangan) pada kedua tabel. Diagram Venn-nya adalah sebagai berikut.
2. OUTER JOIN
Pada dasarnya OUTER JOIN
dibagi menjadi 3, FULL OUTER JOIN
, LEFT OUTER JOIN
, dan RIGHT OUTER JOIN
.
2.a. FULL OUTER JOIN
SELECT * FROM TabelA FULL OUTER JOIN TabelB ON TabelA.nama = TabelB.nama id nama id nama -- ---- -- ---- 1 Andik 1 Andik 2 Binta null null 3 Carli null null 4 Didut 4 Didut null null 2 Bambi null null 3 Conan
FULL OUTER JOIN
menghasilkan semua data yang ditemukan dan cocok satu sama lain pada kedua tabel, jika ada yang tidak cocok, bagian yang tidak ada tersebut akan berisi null
. Jadi bisa dikatakan FULL OUTER JOIN
ini akan menampilkan semua data yang ada di kedua tabel termasuk yang tidak cocok satu sama lain. Diagram Venn-nya adalah sebagai berikut.
2.b. LEFT OUTER JOIN
SELECT * FROM TabelA LEFT OUTER JOIN TabelB ON TabelA.nama = TabelB.nama id nama id nama -- ---- -- ---- 1 Andik 1 Andik 2 Binta null null 3 Carli null null 4 Didut 4 Didut
LEFT OUTER JOIN
menghasilkan semua data yang ada pada TabelA (tabel sebelah kiri), dengan pasangannya (jika ada) pada TabelB (tabel sebelah kanan). Jika pasangannya tidak ditemukan, tabel sebelah kanan akan berisi null
. Diagram Venn-nya adalah sebagai berikut.
Lalu bagaimana jika kita menginginkan data yang hanya ada pada TabelA (tabel sebelah kiri)? Untuk itu kita membutuhkan bantuan klausa WHERE
.
2.c. LEFT OUTER JOIN
dengan klausa WHERE
SELECT * FROM TabelA LEFT OUTER JOIN TabelB ON TabelA.nama = TabelB.nama WHERE TabelB.id IS null id nama id nama -- ---- -- ---- 2 Binta null null 3 Carli null null
Query LEFT OUTER JOIN
di atas menghasilkan data yang hanya ada pada TabelA (tabel sebelah kiri). Diagram Venn-nya adalah sebagai berikut.
Untuk tipe RIGHT OUTER JOIN
tidak perlu dijelaskan lagi karena hasilnya hanya kebalikan dari LEFT OUTER JOIN
. Terakhir, ada satu bentuk diagram lagi, yaitu jika yang diinginkan hanya data yang tidak memiliki pasangan satu sama lain, alias data tersebut berada di luar bagian yang berpotongan.
2.d. FULL OUTER JOIN
dengan klausa WHERE
SELECT * FROM TabelA FULL OUTER JOIN TabelB ON TabelA.nama = TabelB.nama WHERE TabelA.id IS null OR TabelB.id IS null id nama id nama -- ---- -- ---- 2 Binta null null 3 Carli null null null null 2 Bambi null null 3 Conan
Query FULL OUTER JOIN
di atas menghasilkan data yang unik (tidak ada yang berpasangan) dari TabelA dan TabelB. Diagram Venn-nya adalah sebagai berikut.
Artikel ini merupakan terjemahan bebas dari artikel yang ditulis oleh Jeff Atwood dengan ditambah penjelasan seperlunya.
Jangan Gunakan Autofocus pada Form Login
Autofocus
adalah salah satu atribut baru yang dikenal pada HTML 5. Autofocus
adalah sebuah atribut boolean
(true
/false
). Atribut autofocus
akan secara otomatis mengatur fokus ke kotak input yang telah ditentukan. Jadi bisa dibilang autofocus
menghemat 1 kali mouse klik.
Contoh website yang menggunakan autofocus
pada kotak input adalah mesin pencari Google. Ketika ingin mencari sesuatu melalui Google, kita tidak perlu mengeklik pada kotak input untuk bisa mengetik kata kuncinya. Secara otomatis kursor berkedip di kotak input, pertanda bisa langsung mengetik di situ.
Di bawah ini adalah potongan kode untuk menunjukan penggunaan autofocus
.
<label for="nama">Nama </label><input id="nama" name="nama" type="text" autofocus="autofocus"/>
Pada atribut boolean
seperti autofocus
, pengisian nilai tidak mutlak diperlukan, jadi penulisan seperti di bawah ini juga benar.
<label for="nama">Nama </label><input id="nama" name="nama" type="text" autofocus/>
Semua browser moderen mendukung fitur ini, kecuali Internet Explorer. Untuk mengimplementasikan fitur ini pada browser yang tidak mendukung autofocus
, maka bisa digunakan JavaScript. Namun bukan itu yang menjadi masalah di sini. Perhatikan potongan kode di bawah ini.
<label for="nama">Nama </label><input id="nama" name="nama" type="text" <strong>autofocus="autofocus"</strong>/> <br /> <label for="password">Password </label><input id="password" name="password" type="password" />
Ada sebuah halaman yang berisi sebuah form login. Salah satu kotak input-nya memiliki atribut autofocus
. Jadi kursor akan secara otomatis berkedip di kotak “nama” setelah halaman selesai dimuat seutuhnya. Namun beberapa pengguna tidak sabar atau tidak mengetahui fitur ini, sehingga mereka mengeklik pada kotak input “nama” tanpa menunggu halaman dimuat seutuhnya. Akibatnya, ketika pengguna sampai pada kotak input “password”, lalu halaman selesai dimuat seutuhnya, maka kursor akan berpindah lagi ke kotak input “nama”.
Bayangkan jika seseorang sedang mengetik di kotak password lalu tiba-tiba kursor berpindah ke kotak input “nama” yang notabene bertipe text
. Password-nya akan tercetak di kotak input “nama” tanpa samaran. Hal ini fatal karena password bisa dilihat oleh orang lain yang kebetulan lewat di dekat kita.
Website yang menerapkan fitur ini pada form login contohnya adalah Facebook, sedangkan yang tidak adalah Twitter.
Autentikasi dan Mengambil Data Pengguna dengan Facebook SDK
Tutorial ini sudah banyak didokumentasikan di blog-blog baik dalam dan luar negeri, tetapi sangat membingungkan bagi saya. Beberapa tutorial menggunakan JavaScript SDK, sedangkan yang lain menggunakan PHP SDK. Belum lagi, banyak di antaranya yang sudah kadaluarsa dan diperburuk oleh revisi dari Facebook yang tidak menentu.
Hal lain yang juga membingungkan adalah perbedaan tampilan kotak dialog yang menanyakan izin untuk mengakses data-data. Bandingkan tampilan dialog yang saya ambil dari Facebook Developers Guide berikut dengan yang dihasilkan dari tutorial ini.
Sebenarnya hanya ada 2 hal dasar yang menjadi alasan untuk menggunakan Facebook SDK.
1. Kita mempunyai sistem “register” dan “login” sendiri, dan ingin menambahkan “Login with Facebook” sebagai pilihan bagi pengguna yang malas untuk mendaftar secara normal.
2. Kita membutuhkan data-data pengguna yang sudah dicatat dengan baik oleh Facebook, tentunya dengan persetujuan pemilik akun.
Dokumentasi dan kode sumber yang ada di Facebook Developers Guide sudah lengkap, namun susah diikuti dan tidak semuanya saya butuhkan. Tutorial ini akan menggabungkan Javascript SDK dan PHP SDK, karena cara ini menurut saya paling sederhana untuk memenuhi 2 kebutuhan dasar di atas.
Langkah-langkah untuk menghubungkan website dengan Facebook SDK adalah:
1. Daftarkan website pada halaman Facebook Developers, di situ akan diperoleh “App ID” dan “App Secret”.
2. Buat sebuah table pada database untuk menyimpan data-data pengguna dari Facebook. Field-nya sesuai kebutuhan saja, contoh: nama, password, email, tipe (password yang dimaksud di sini bukan password facebook dan tipe akan berisi “facebook” jika data diambil dari facebook dan “normal” jika tidak).
3. Download Facebook SDK. Ambil semua isi folder “src” (kita hanya membutuhkan ini).
4. Tombol login bisa menggunakan tag HTML biasa
<div class="fb-login-button">
atau menggunakan JavaScript SDK.
<fb:login-button>
Dengan ini kita bisa mengakses data-data pengguna yang diatur secara publik (tidak disembunyikan), misalnya id, nama, jenis kelamin. Data-data yang tidak bisa diakses publik harus ditulis ke dalam atribut scope
.
<fb:login-button scope="email, read_stream"></fb:login-button>
5. Deklarasikan Facebook object
di paling atas halaman. Jangan lupa isi “App ID” dan “App secret”. Kemudian ambil data-data pengguna (sudah termasuk yang ditulis di atribut scope
tadi) dengan Facebook API
.
require_once("facebook.php"); $config = array(); $config['appId'] = ''; $config['secret'] = ''; $facebook = new Facebook($config); $user = $facebook->getUser(); if ($user) { try { $user_profile = $facebook->api('/me'); } catch (FacebookApiException $e) { error_log($e); $user = null; } }
6. Terakhir, data-data yang sudah diperoleh dari Facebook API
bisa diolah sesuai keinginan. Untuk lebih memahami, baca komentar yang ada pada kode sumber.
<?php if($user) { ?> //tampilkan foto dan nama user <img src="https://graph.facebook.com/<?php echo $user; ?>/picture"> <?=$user_profile['name']?> <?php //ambil data-data yang ingin disimpan $fb_id = $user_profile['id']; $username = $user_profile['username']; $email = $user_profile['email']; //periksa apakah user sudah pernah login $insert = true; if (/*query untuk memeriksa apakah data sudah ada di database*/) $insert = false; //jika pertama kali login, masukan data user ke database if($insert) { /*query INSERT untuk memasukan data ke database*/ } //login user /* kode untuk login user dan SESSION */ ?> <?php } else { ?> <fb:login-button scope="email, read_stream"></fb:login-button> <?php } ?>
Gabungkan langkah ke-5 dan ke-6 serta tambahkan JavaScript SDK. Akhirnya kita akan memperoleh data dalam bentuk array setelah login dengan Facebook.
<?php require_once("facebook.php"); $config = array(); $config['appId'] = 'isi_dengan_app_id'; $config['secret'] = 'isi_dengan_app_secret'; $facebook = new Facebook($config); $user = $facebook->getUser(); if ($user) { try { $user_profile = $facebook->api('/me'); } catch (FacebookApiException $e) { error_log($e); $user = null; } } if ($user) { ?> <pre> <?php print_r($user_profile) ?> </pre> <?php } else { ?> <fb:login-button scope="email, read_stream"></fb:login-button> <?php } ?> <div id="fb-root"></div> <script> window.fbAsyncInit = function() { FB.init({ appId: '<?php echo $facebook->getAppID() ?>', cookie: true, xfbml: true, oauth: true }); FB.Event.subscribe('auth.login', function(response) { window.location.reload(); }); FB.Event.subscribe('auth.logout', function(response) { window.location.reload(); }); }; (function() { var e = document.createElement('script'); e.async = true; e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js'; document.getElementById('fb-root').appendChild(e); }()); </script>
JavaScript dan Object Oriented Programming (OOP) Bagian 3
Sedikit kilas balik pada catatan sebelumnya tentang class
, bahwa class
memiliki property
dan method
. Setelah sebuah class
didefinisikan, maka class
tersebut dapat digunakan berulang kali. Caranya adalah dengan menggunakan object constructor
(berikutnya akan ditulis constructor
). Constructor
sebenarnya adalah sebuah function
, bedanya adalah constructor
dipanggil menggunakan kata kunci new
.
var Anjing = function(umur_anjing_saya, warna_anjing_saya) { this.umur = umur_anjing_saya; this.warna = warna_anjing_saya; }; var heli = new Anjing(5, "putih"); var luki = new Anjing(3, "hitam");
Pada contoh di atas, function
“Anjing” adalah sebuah constructor
. Constructor
dapat dipanggil berulang kali untuk menciptakan object
baru dengan kata kunci new
. Jadi constructor
adalah sebuah cetak biru dari sebuah object
, bukan object
itu sendiri.
Lalu bagaimana cara untuk mengubah cetak biru tersebut, katankanlah untuk menambah fungsi/fitur pada class
“Anjing”? Kita bisa menambahkan method
setelah memanggil constructor
.
var Anjing = function(umur_anjing_saya, warna_anjing_saya) { this.umur = umur_anjing_saya; this.warna = warna_anjing_saya; }; var heli = new Anjing(5, "putih"); heli.suara = function() { console.log("guk"); }; heli.suara(); var luki = new Anjing(3, "hitam"); luki.suara(); //error
Pada baris ketujuh dapat dilihat method
“suara” ditambahkan pada object
“heli”. Perhatikan apa yang akan terjadi jika luki juga mencoba untuk bersuara seperti ini luki.suara();
. Meskipun luki termasuk ke dalam class
“Anjing”, tetapi dia tidak tau bagaimana untuk bersuara karena hanya heli yang ditambahi method
“suara”. Jadi ajarkan luki untuk bersuara seperti heli, tapi dengan suara yang agak beda, misalnya “auu”.
var Anjing = function(umur_anjing_saya, warna_anjing_saya) { this.umur = umur_anjing_saya; this.warna = warna_anjing_saya; }; var heli = new Anjing(5, "putih"); heli.suara = function() { console.log("guk"); }; heli.suara(); var luki = new Anjing(3, "hitam"); luki.suara = function() { console.log("auu"); }; luki.suara();
Object
juga bisa digunakan sebagai argumen pada suatu function
, karena pada dasarnya suatu object
adalah variabel yang lebih kompleks.
var Anjing = function(suara) { this.suara = suara; }; function keluarkanSuara(anjing) { console.log(anjing.suara); }; var heli = new Anjing("guk"); keluarkanSuara(heli);
Cara yang lebih elegan untuk menambah fungsi/fitur pada sebuah cetak biru objek adalah dengan menggunakan kata kunci prototype
. Prototype
mewakili sifat pewarisan (inheritance) pada JavaScript. Alih-alih menggunakan heli.suara
untuk menambahkan method
pada object
“heli”, gunakan Anjing.prototype.suara
.
var Anjing = function(umur_anjing_saya, warna_anjing_saya) { this.umur = umur_anjing_saya; this.warna = warna_anjing_saya; }; var heli = new Anjing(5, "putih"); Anjing.prototype.suara = function() { console.log("guk"); }; heli.suara(); var luki = new Anjing(3, "hitam"); luki.suara();
Sekarang “luki” bisa menggunakan method
“suara” seperti pada “heli” tanpa harus diajarkan terlebih dahulu, karena sudah ditambahkan pada class
“Anjing”.
Kegunaan penting dari prototype
adalah agar programer tidak perlu menulis ulang kode yang sudah ada di class
lain. Misal pada contoh berikut kita ingin membuat class
“Kucing”, yang memiliki property
atau method
yang sama dengan class
“Anjing”.
var Kucing = function(umur_kucing_saya, warna_kucing_saya) { this.umur = umur_kucing_saya; this.warna = warna_kucing_saya; this.suara = function() { console.log("mew"); }; };
Pada contoh di atas tampak bahwa class
“Kucing” menggunakan property
“umur” dan “warna” yang juga ada pada class
“Anjing”. Untuk menghindari hal ini maka ciptakan suatu induk, dan gunakan prototype
untuk menurunkan property
dan method
untuk anak-anaknya.
var Hewan = function(suara_hewan) { this.suara = function() { console.log(suara_hewan); }; }; var Anjing = function(umur_anjing, warna_anjing) { this.umur = umur_anjing; this.warna = warna_anjing; }; Anjing.prototype = new Hewan("guk"); var heli = new Anjing(5, "putih"); heli.suara();
Pada contoh di atas, meskipun “heli” sebagai “Anjing” tidak memiliki method
“suara”, tetapi tetap bisa memanggil method
“suara” karena mendapat pewarisan dari class
“Hewan”.
Pewarisan juga bisa dilakukan secara bertingkat (prototype chain
). Misalnya kita mempunyai seekor anjing bernama heli. Heli ini termasuk anjing, tetapi juga termasuk hewan. Jadi, class
“Heli” bisa diturunkan dari class
“Anjing”, dan class
“Anjing” diturunkan dari class
“Hewan”. Mari lanjutkan dan sempurnakan kode di atas sehingga memiliki sifat enkapsulasi dan pewarisan.
var Hewan = function(suara_hewan) { var suara = suara_hewan; this.bersuara = function() { return console.log(suara); }; }; var Anjing = function(umur_hewan, warna_hewan) { var umur = umur_hewan; var warna = warna_hewan; this.berumur = function() { return console.log(umur); }; this.berwarna = function() { return console.log(warna); }; }; var Heli = function(hobi_heli) { var hobi = hobi_heli; this.berhobi = function() { return console.log(hobi); }; }; Anjing.prototype = new Hewan("guk"); Heli.prototype = new Anjing(5, "putih"); var anjing_saya = new Heli("jogging"); anjing_saya.berhobi(); //jogging anjing_saya.berumur(); //5 anjing_saya.bersuara(); //guk