Labels

Cari di blog ini

Codeigniter Development PHP

Class Exception pada CodeIgniter dan Dilema Errors Template

Silakan Ngomen
Beberapa bulan terakhir ini saya dan beberapa teman kru Nyankod sedang mengembangkan produk baru Nyankod yaitu Devository, situs tanya jawab seputar pemrograman. Kami mengembangkannya di atas framework CodeIgniter, lebih khususnya di atas CMS yang kami kembangkan sendiri bernama Jooglo. Di dalamnya kami buat beberapa pondasi supaya pengembangan bersifat modular dan reuse. Kedepannya, setelah devository beres, kami akan mengembangkan Jooglo ini sebagai CMS baru berbasis CodeIgniter.

Hari ini saya sedang menggarap modul tanya jawab, ketika tiba-tiba muncul error pada class exception. Seperti kita tau, class exception ini kan mengatur tampilan error, mulai dari error general seperti 404 sampe error php. Jadi ada error pada class pengatur error hahaha.. lucu ga? Engga? Ok. Kita lanjut.

Saya sudah mengintegrasikan CodeIgniter versi 3.0-dev ke dalam inti Jooglo setelah sebelumnya menggunakan versi 2.1.4. Banyak peningkatan kemanan dan fitur, salahsatunya adalah error templating. Kalo di versi 2 itu template error disimpan di dalam folder application/errors/, sekarang sudah disimpan di dalam folder application/views/errors/. Ada 5 buah file template, diantaranya error_404.php, error_db.php, error_general.php dan error_php.php.  Keuntungannya adalah, kita dapat memanfaatkan $this->load->view() pada template error ini. Kalo dulu kan terpisah dari lingkungan kerja view, sehingga templatingnya berdiri sendiri mulai dari tag <html> sampe ditutup. Kemajuan ini sangat membantu pekerjaan kita sebagai programmer CodeIgniter.

Nah, sekarang, di Jooglo, kami memanfaatkan librari Template yang sudah kami modifikasi sehingga librari ini mendukung modularisasi tema. Maksudnya, kalo secara default kita menyimpan semua file view di dalam folder application/views/, nah dengan modifikasi kami, file view bisa disimpan di dalam folder tema. Dengan demikian kita tinggal memilih tema mana yang akan digunakan dan sistem pun akan menggunakan file view yang ada pada folder tema yang dipilih.

Nah permasalahannya adalah, keinginan saya, dimana file template error, yang sekarang sudah ada di dalam folder views/ itu, juga bisa di-override dari views/ di dalam folder tema. Gimana caranya? Yaa, karena template errors itu sudah berfungsi di dalam application/views/, mestinya ketika saya sudah bisa membuat sistem override view ke dalam tema, berarti template errors itu berfungsi juga dong. Yah, harapannya kan begitu.

Ternyata, template error itu berbeda cara pemanggilannya dengan template view yang lain. Kalo file view yang biasa kita tinggal panggil saja $this->load->view() dari dalam controller atau file view lain. Kalo template errors itu yang pake ternyata adalah file class di dalam folder system/core/ yang bernama Exception.php. File inilah yang mengatur bagaimana sebuah error ditampilkan. Method di dalam class inilah yang menggunakan template errors.

Override Class Exception

Kondisi tersebut membuat saya mesti meng-override class Exception. Caranya adalah dengan membuat class MY_Exception yang extends dari class CI_Exception, dan menyimpannya di dalam folder application/core/ dengan nama file MY_Exception.php. Ini adalah salahsatu method yang mesti di-override.

Kondisi mula sebelum override di dalam class CI_Exception

function show_php_error($severity, $message, $filepath, $line)
{
    $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;
    $filepath = str_replace('\\', '/', $filepath);

    // For safety reasons we do not show the full file path
    if (FALSE !== strpos($filepath, '/'))
    {
        $x = explode('/', $filepath);
        $filepath = $x[count($x)-2].'/'.end($x);
    }

    if (ob_get_level() > $this->ob_level + 1)
    {
        ob_end_flush();
    }

    ob_start();
    include(VIEWPATH.'errors/error_php.php');
    $buffer = ob_get_contents();
    ob_end_clean();
    echo $buffer;
}

Kemudian saya modif jadi begini supaya template errors dapat dibaca dari dalam views yang ada di dalam tema yang sedang aktif.

function show_php_error($severity, $message, $filepath, $line)
{
    $CI =& get_instance();

    $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;
    $filepath = str_replace('\\', '/', $filepath);

    // For safety reasons we do not show the full file path
    if (FALSE !== strpos($filepath, '/'))
    {
        $x = explode('/', $filepath);
        $filepath = $x[count($x)-2].'/'.end($x);
    }

    if (ob_get_level() > $this->ob_level + 1)
    {
        ob_end_flush();
    }

    $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR.'error_php.php';

    if(file_exists($CI->template->theme_path().'/views/errors/error_php.php'))
    $templates_path = $CI->template->theme_path().'/views/errors/error_php.php';
   
    ob_start();
    include($templates_path);
    $buffer = ob_get_contents();
    ob_end_clean();
    echo $buffer;
}

Saya menggunakan $CI =& get_instance(); yang berarti sistem memuat objek controller sehingga dari objek itu saya bisa panggil library atau objek lain yang sudah diload oleh controller. Tapi ternyata fungsi theme_path() yang saya panggil dari library template tidak dikenali. Aneh kan, biasanya dengan cara yang sama kita bisa panggil library apapun di dalam library atau helper atau file core lain. Tapi di class Exception ini tidak berfungsi sebagaimana mestinya.

Setelah googling, ketemulah inti masalahnya. Ternyata, class Exception ini diload oleh sistem sebelum sistem meload class Controller, jadi wajarlah bila objek Controller tidak dikenali oleh Exception. Sampe kapanpun $CI =& get_instance(); ga akan pernah berjalan di dalam Exception.

Solusi Errors Template

Yang terbayang sama saya ada dua solusi. Pertama adalah dengan membuat fungsi error di dalam controller sehingga setiap error yang muncul itu pada dasarnya memanggil fungsi error di dalam controller atau dengan kata lain, template errors ini tidak digunakan oleh class Exception, melainkan oleh fungsi error yang kita buat di controller tadi. Konsekuensinya adalah, kita harus mengalihkan pemanggilan setiap error ke controller. Seperti kita tau, fungsi show_error(), show_404() dan fungsi error lain itu terdapat di dalam file system/core/Common.php dan semua fungsi di dalamnya digunakan hampir di semua tempat di dalam file core lain. Kalo kamu buka file system/core/Codeigniter.php, disitu kita lihat file Common.php dimuat paling awal, karena dia digunakan oleh hampir semua class lainnya. Dengan kata lain, fungsi di dalam Common.php ini tidak bisa memanggil fungsi yang ada pada controller karena controllernya kan belum diload saat Common.php ini diload. Paling banter adalah kita hanya bisa melakukan ini pada fungsi show_404(), dengan cara mengeset variabel $route['404_override'] = ''; pada file config routes.php dengan URI yang menampilkan error 404.

Tapi da saya mah pengennya semua template errors bisa dioverride, ga hanya yang error_404 aja. Akhirnya saya pake cara kedua, dengan mengambil data nama tema yang digunakan dari database. Lengkapnya seperti ini:

function show_php_error($severity, $message, $filepath, $line)
{
    // ambil data dari database
    require_once BASEPATH.'database/DB'.EXT;
    $theme = DB()
            ->select('value')
            ->where('name', 'template')
            ->get('jooglo_options')
            ->row();

    $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;
    $filepath = str_replace('\\', '/', $filepath);

    // For safety reasons we do not show the full file path
    if (FALSE !== strpos($filepath, '/'))
    {
        $x = explode('/', $filepath);
        $filepath = $x[count($x)-2].'/'.end($x);
    }

    if (ob_get_level() > $this->ob_level + 1)
    {
        ob_end_flush();
    }

    $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR.'error_php.php';

    if(file_exists(APPPATH.'themes/'.$theme->value.'/views/errors/error_php.php'))
    $templates_path = APPPATH.'themes/'.$theme->value.'/views/errors/error_php.php';
   
    ob_start();
    include($templates_path);
    $buffer = ob_get_contents();
    ob_end_clean();
    echo $buffer;
}

Disini saya mengambil terlebih dahulu nama tema yang sedang aktif dari dalam database, dan menggunakannya untuk merujuk folder tema mana yang akan digunakan. Saya melakukannya pada fungsi yang lain yang ada di dalam class MY_Exception. Dengan begini semua template errors bisa dioverride dari dalam tema. Yes! Alhamdulillah..

0 tanggapan:

Posting Komentar

terima kasih sebelumnya untuk tanggapannya ^_^