Category: PHP

  • Operator Ternary dan Nilai Boolean

    Di blog ini pernah dibahas mengenai operator ternary untuk menyingkat kode. Contohnya ada sepotong kode seperti berikut:

    if ($nilai > 90)
      echo "Keren";
    
    else
      echo "Lumayan";
    

    Kode di atas dapat ditulis ulang menggunakan operator ternary menjadi seperti berikut:

    echo ($var > 90) ? "Keren" : "Lumayan";
    

    Seorang programer berpengalaman pasti lebih suka menggunakan operator ternary bahkan untuk yang melibatkan lebih dari dua buah pernyataan. Namun tidak direkomendasikan untuk para pemula karena bisa membingungkan.

    Saya sendiri pernah melakukan hal konyol pada tulisan yang lalu, yaitu penggunaan operator ternary dengan nilai boolean yang tidak semestinya.

    $cek = ($var > 5) ? true : false;
    

    Padahal seharusnya cukup ditulis seperti ini:

    $cek = $var > 5;
    

    Variable $cek otomatis bernilai true jika kondisi $var > 5 terpenuhi. Sebaliknya bernilai false jika kondisi tidak terpenuhi. Contoh kasus lain penggunaan operator ternary yang tidak semestinya adalah:

    $cek = ($var == true) ? 1 : 0;
    

    Secara tidak sadar mungkin kita sering melakukan hal semacam ini dalam pengkodean sehari-hari. Pada contoh di atas juga terdapat kondisi yang tidak perlu dan seharusnya dihilangkan sehingga menjadi seperti berikut:

    $cek = $var ? 1 : 0;
    

    Catatan ini juga sebagai koreksi untuk tulisan yang lalu.

  • Akses Database Menggunakan PDO pada PHP

    Sebagian besar programer PHP pasti pernah membuat aplikasi yang harus berinteraksi dengan database. Sampai pada PHP versi 5.1, pilihan yang direkomendasikan adalah menggunakan native drivers seperti Mysql Extensions.

    Sayangnya, pada PHP versi 5.4 penggunaan Mysql Extensions mulai ditinggalkan, dan akan dihapus secara keseluruhan pada PHP versi 5.5. Itu artinya, penggunaan fungsi mysql_connect() atau mysql_query(), seperti pada postingan yang lalu, harus diganti. Pilihannya adalah menggunakan Mysqli Extensions atau yang akan dibahas berikut ini, PDO.

    PDO (PHP Data Object), yang diperkenalkan sejak PHP versi 5.1, menyediakan antarmuka untuk berinteraksi dengan berbagai jenis database secara seragam. Gampangnya, kita bisa mengakses database MySQL, Firebird, atau IBM dengan syntax yang sama. Namun tidak semua driver otomatis tersedia pada sistem Anda. Untuk memeriksanya kita gunakan:

    <?php
    print_r(PDO::getAvailableDrivers()); 
    

    Pada artikel ini menggunakan database MySQL, jadi pastikan tulisan berikut ini yang muncul.

    Array ( [0] => mysql )

    Berikut ini adalah syntax untuk koneksi awal dengan database dan query SELECT MySQL.

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $pdo->query('SELECT nama FROM tabel_pengguna WHERE id = ' . $_GET['id']);
    

    Kode di atas adalah contoh yang buruk karena data dari pengguna langsung dimasukan ke dalam query SQL tanpa difilter, sehingga rentan disusupi kode dari luar (SQL Injection). Jika dahulu kita biasa menggunakan fungsi mysql_real_escape_string(), sekarang dapat menggunakan PDO statement prepare() dan bindParam().

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('SELECT nama FROM tabel_pengguna WHERE id = :id');
    $stmt->bindParam(':id', filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT), PDO::PARAM_INT);
    $stmt->execute();
    

    Pada baris ke-3, masukan dari pengguna diganti dengan sebuah placeholder. Nama untuk placeholder dimulai dengan tanda titik dua (:). Fungsi filter_input() pada baris ke-4 digunakan untuk memastikan masukan dari luar, dalam hal ini $_GET['id'], benar-benar berupa angka integer. Dengan PDO statement dan bindParam() di atas, kode Anda akan aman dari serangan SQL Injection.

    Meskipun kode di atas dapat berjalan dengan baik, namun Anda akan mendapat pesan peringatan (notice):

    Only variables should be passed by reference bla.. bla..

    Itu terjadi karena parameter kedua dari bindParam() harus berupa variabel. Jadi kode di atas diubah menjadi seperti ini:

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('SELECT nama FROM tabel_pengguna WHERE id = :id');
    
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->execute();
    

    Untuk contoh berikutnya, akan kita coba query INSERT dan UPDATE.

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('INSERT INTO tabel_pengguna (id, nama) VALUES (:id, :nama)');
    
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $nama = filter_input(INPUT_GET, 'nama', FILTER_SANITIZE_STRING)
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->bindParam(':nama', $nama, PDO::PARAM_STR);
    $stmt->execute();
    
    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('UPDATE tabel_pengguna SET nama = :nama WHERE id = :id');
    
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $nama = filter_input(INPUT_GET, 'nama', FILTER_SANITIZE_STRING)
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->bindParam(':nama', $nama, PDO::PARAM_STR);
    $stmt->execute();
    

    Cara mencoba kode-kode di atas, langsung ketik saja di address bar. Contoh: localhost/namafile.php?id=10&nama=ronaldo. Jangan lupa isi variabel-variabel $host, $dbname, $user, dan $pass.

  • Menyesuaikan Zona Waktu di MySQL dan PHP

    Ketika membuat sebuah aplikasi website, seringkali waktu yang ditunjukkan oleh server tidak sama dengan waktu client. Ini terjadi karena server terletak di zona waktu yang berbeda dengan client. Misalnya server yang berlokasi di USA, jelas berada di zona waktu yang berbeda dengan client di Indonesia.

    echo date('d-m-Y H:i:s');
    

    Kode PHP di atas akan menampilkan waktu aktual pada server. Jika dicoba di localhost tentu saja waktunya akan sama seperti komputer lokal, namun hasilnya bisa berbeda jika dicoba pada server web Anda. Agar hasilnya sesuai dengan waktu di komputer kita, tambahkan pengaturan zona waktu.

    date_default_timezone_set('Asia/Jakarta');
    echo date('d-m-Y H:i:s');
    

    Untuk zona waktu yang lain bisa menyesuaikan.

    Namun cara di atas memiliki beberapa kelemahan, terutama pada aplikasi yang menggunakan database. Selain kemungkinan terkena bug Y2K38, MySQL dan PHP memiliki pengaturan zona waktu yang terpisah. Misal kita menjalankan fungsi NOW() pada MySQL, waktu yang ditampilkan tidak sesuai dengan zona waktu kita. Selain itu, aplikasi juga membutuhkan tambahan proses setiap kali mengolah data dari field waktu pada MySQL agar sesuai dengan zona waktu kita.

    Agar proses penyesuaian waktu bisa berjalan secara otomatis, kita harus mengetahui berapa selisih waktu client dengan UTC, kemudian beritahukan kepada MySQL. Pertama-tama dengan bantuan class Datetime, kita dapatkan offset (untuk menutupi selisih yang ada) dalam detik, kemudian mudah saja kita konversikan ke dalam menit dan jam.

    $sekarang = new DateTime();
    $menit = $sekarang -> getOffset() / 60;
    

    Kemudian cari tau apakah offset bernilai positif atau negatif, lalu konversikan saja ke bilangan positif untuk memudahkan penghitungan.

    $tanda = ($menit < 0 ? -1 : 1);
    $menit = abs($menit);
    $jam = floor($menit / 60);
    $menit -= $jam * 60;
    

    Gunakan fungsi sprintf untuk memformat string agar bisa diterima MySQL (format yang bisa diterima misalnya: +07:00).

    $offset = sprintf('%+d:%02d', $tanda * $jam, $menit);
    

    Terakhir jalankan query SET time_zone setelah mengkoneksikan aplikasi dengan database. Silakan ganti dengan koneksi database yang Anda gunakan.

    mysql_connect($server, $username, $password);
    mysql_select_db($database);
    
    mysql_query("SET time_zone = '$offset'");
    

    Akhirnya pengaturan zona waktu antara PHP dan MySQL telah sesuai. Berikut ini adalah gabungan kode-kode snippet di atas.

    <?php
    date_default_timezone_set('Asia/Jakarta');
    
    $sekarang = new DateTime();
    $menit = $sekarang -> getOffset() / 60;
    
    $tanda = ($menit < 0 ? -1 : 1);
    $menit = abs($menit);
    $jam = floor($menit / 60);
    $menit -= $jam * 60;
    
    $offset = sprintf('%+d:%02d', $tanda * $jam, $menit);
    
    mysql_connect($server, $username, $password);
    mysql_select_db($database);
    
    mysql_query("SET time_zone = '$offset'");
    
  • 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?

    1. Validasi: menentukan apakah sebuah data memiliki format yang valid.
    2. 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 ‘&amp;‘.
    • "‘ (double quote) dikonversi menjadi ‘&quot;‘.
    • '‘ (single quote) dikonversi menjadi ‘&#039;‘.
    • <' (less than) dikonversi menjadi '&lt;'
    • '>' (greater than) dikonversi menjadi '&gt;'

    Contoh penggunaannya adalah sebagai berikut.

    <?php
    $string = htmlspecialchars("<a href='test'>Test string</a>", ENT_QUOTES);
    echo $string; // &lt;a href=&#039;test&#039;&gt;Test string&lt;/a&gt;
    

    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.

    sql injection

    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);  
    
  • 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.

    login with facebook

    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>