Learn

Pelajari Tentang Flight

Flight adalah kerangka kerja PHP yang cepat, sederhana, dan dapat diperluas. Ini cukup serbaguna dan dapat digunakan untuk membangun berbagai macam aplikasi web. Ini dibangun dengan kesederhanaan dalam pikiran dan ditulis dengan cara yang mudah dipahami dan digunakan.

Konsep Kerangka Kerja yang Penting

Mengapa Kerangka Kerja?

Berikut adalah artikel singkat tentang mengapa Anda harus menggunakan kerangka kerja. Ini adalah ide bagus untuk memahami manfaat menggunakan kerangka kerja sebelum Anda mulai menggunakannya.

Selain itu, tutorial yang sangat baik telah dibuat oleh @lubiana. Meskipun tidak membahas secara mendetail tentang Flight secara spesifik, panduan ini akan membantu Anda memahami beberapa konsep utama yang mengelilingi kerangka kerja dan mengapa mereka bermanfaat untuk digunakan. Anda dapat menemukan tutorial di sini.

Flight Dibandingkan dengan Kerangka Kerja Lain

Jika Anda berpindah dari kerangka kerja lain seperti Laravel, Slim, Fat-Free, atau Symfony ke Flight, halaman ini akan membantu Anda memahami perbedaan antara keduanya.

Topik Inti

Pengautoloadan

Pelajari cara mengautoload kelas Anda sendiri dalam aplikasi Anda.

Routing

Pelajari cara mengelola rute untuk aplikasi web Anda. Ini juga termasuk pengelompokan rute, parameter rute, dan middleware.

Middleware

Pelajari cara menggunakan middleware untuk menyaring permintaan dan respons dalam aplikasi Anda.

Permintaan

Pelajari cara menangani permintaan dan respons dalam aplikasi Anda.

Respons

Pelajari cara mengirim respons kepada pengguna Anda.

Peristiwa

Pelajari cara menggunakan sistem peristiwa untuk menambahkan peristiwa khusus ke aplikasi Anda.

Template HTML

Pelajari cara menggunakan mesin tampilan bawaan untuk merender template HTML Anda.

Keamanan

Pelajari cara mengamankan aplikasi Anda dari ancaman keamanan umum.

Konfigurasi

Pelajari cara mengonfigurasi kerangka kerja untuk aplikasi Anda.

Memperluas Flight

Pelajari cara memperluas kerangka kerja dengan menambahkan metode dan kelas Anda sendiri.

Peristiwa dan Penyaringan

Pelajari cara menggunakan sistem peristiwa untuk menambahkan hook ke metode Anda dan metode kerangka kerja internal.

Kontainer Penyuntikan Ketergantungan

Pelajari cara menggunakan kontainer penyuntikan ketergantungan (DIC) untuk mengelola ketergantungan aplikasi Anda.

API Kerangka Kerja

Pelajari tentang metode inti dari kerangka kerja.

Migrasi ke v3

Kompatibilitas ke belakang sebagian besar telah dipertahankan, tetapi ada beberapa perubahan yang harus Anda ketahui saat bermigrasi dari v2 ke v3.

Pemecahan Masalah

Ada beberapa masalah umum yang mungkin Anda temui saat menggunakan Flight. Halaman ini akan membantu Anda memecahkan masalah tersebut.

Learn/flight_vs_laravel

Flight vs Laravel

Apa itu Laravel?

Laravel adalah framework yang memiliki fitur lengkap yang dilengkapi dengan segala hal menarik dan ekosistem yang berfokus pada pengembang, tetapi dengan biaya dalam hal kinerja dan kompleksitas. Tujuan Laravel adalah agar pengembang memiliki tingkat produktivitas tertinggi dan untuk memudahkan tugas-tugas umum. Laravel adalah pilihan yang baik bagi pengembang yang mencari untuk membangun aplikasi web berfitur lengkap untuk perusahaan. Itu datang dengan beberapa pertukaran, terutama dalam hal kinerja dan kompleksitas. Mempelajari dasar-dasar Laravel bisa jadi mudah, tetapi menguasai framework ini bisa memakan waktu.

Ada juga banyak modul Laravel sehingga pengembang sering merasa bahwa satu-satunya cara untuk menyelesaikan masalah adalah melalui modul-modul ini, padahal sebenarnya Anda bisa saja menggunakan perpustakaan lain atau menulis kode Anda sendiri.

Kelebihan dibandingkan Flight

Kekurangan dibandingkan Flight

Learn/migrating_to_v3

Migrasi ke v3

Kompatibilitas ke belakang sebagian besar telah dipertahankan, tetapi ada beberapa perubahan yang harus Anda perhatikan saat migrasi dari v2 ke v3.

Perilaku Buffering Output (3.5.0)

Buffering output adalah proses di mana output yang dihasilkan oleh skrip PHP disimpan dalam buffer (internal di PHP) sebelum dikirim ke klien. Ini memungkinkan Anda untuk memodifikasi output sebelum dikirim ke klien.

Dalam aplikasi MVC, Controller adalah "pengelola" dan mengatur apa yang dilakukan tampilan. Memiliki output yang dihasilkan di luar controller (atau dalam kasus Flight kadang-kadang fungsi anonim) merusak pola MVC. Perubahan ini bertujuan untuk lebih selaras dengan pola MVC dan untuk membuat framework lebih dapat diprediksi dan lebih mudah digunakan.

Di v2, buffering output ditangani sedemikian rupa sehingga tidak secara konsisten menutup buffer output sendiri dan ini membuat unit testing dan streaming menjadi lebih sulit. Untuk sebagian besar pengguna, perubahan ini mungkin tidak benar-benar memengaruhi Anda. Namun jika Anda mencetak konten di luar callable dan controller (misalnya dalam hook), Anda kemungkinan akan menghadapi masalah. Mencetak konten dalam hook, dan sebelum framework benar-benar mengeksekusi mungkin berfungsi di masa lalu, tetapi tidak akan berfungsi ke depan.

Di mana Anda mungkin mengalami masalah

// index.php
require 'vendor/autoload.php';

// hanya contoh
define('START_TIME', microtime(true));

function hello() {
    echo 'Hello World';
}

Flight::map('hello', 'hello');
Flight::after('hello', function(){
    // ini sebenarnya baik-baik saja
    echo '<p>Frasa Hello World ini dibawakan kepada Anda oleh huruf "H"</p>';
});

Flight::before('start', function(){
    // hal-hal seperti ini akan menyebabkan kesalahan
    echo '<html><head><title>Halaman Saya</title></head><body>';
});

Flight::route('/', function(){
    // ini sebenarnya baik-baik saja
    echo 'Hello World';

    // Ini juga seharusnya baik-baik saja
    Flight::hello();
});

Flight::after('start', function(){
    // ini akan menyebabkan kesalahan
    echo '<div>Halaman Anda dimuat dalam '.(microtime(true) - START_TIME).' detik</div></body></html>';
});

Mengaktifkan Perilaku Rendering v2

Apakah Anda masih bisa menjaga kode lama Anda seperti semula tanpa melakukan penulisan ulang untuk membuatnya berfungsi dengan v3? Ya, Anda bisa! Anda dapat mengaktifkan perilaku rendering v2 dengan mengatur opsi konfigurasi flight.v2.output_buffering menjadi true. Ini akan memungkinkan Anda untuk terus menggunakan perilaku rendering lama, tetapi disarankan untuk memperbaikinya ke depan. Di v4 dari framework, ini akan dihapus.

// index.php
require 'vendor/autoload.php';

Flight::set('flight.v2.output_buffering', true);

Flight::before('start', function(){
    // Sekarang ini akan baik-baik saja
    echo '<html><head><title>Halaman Saya</title></head><body>';
});

// lebih banyak kode 

Perubahan Dispatcher (3.7.0)

Jika Anda langsung memanggil metode statis untuk Dispatcher seperti Dispatcher::invokeMethod(), Dispatcher::execute(), dll. Anda perlu memperbarui kode Anda agar tidak langsung memanggil metode ini. Dispatcher telah diubah menjadi lebih berorientasi objek sehingga Container Penyuntikan Ketergantungan dapat digunakan dengan cara yang lebih mudah. Jika Anda perlu memanggil metode mirip seperti yang dilakukan Dispatcher, Anda dapat menggunakan sesuatu seperti $result = $class->$method(...$params); atau call_user_func_array() sebagai gantinya.

Perubahan halt() stop() redirect() dan error() (3.10.0)

Perilaku default sebelum 3.10.0 adalah untuk menghapus baik header maupun body respons. Ini diubah hanya untuk menghapus body respons. Jika Anda perlu menghapus header juga, Anda bisa menggunakan Flight::response()->clear().

Learn/configuration

Konfigurasi

Anda dapat menyesuaikan perilaku tertentu dari Flight dengan menetapkan nilai konfigurasi melalui metode set.

Flight::set('flight.log_errors', true);

Pengaturan Konfigurasi Tersedia

Berikut adalah daftar semua pengaturan konfigurasi yang tersedia:

Konfigurasi Loader

Selain itu, ada pengaturan konfigurasi lain untuk loader. Ini akan memungkinkan Anda untuk memuat kelas secara otomatis dengan _ dalam nama kelas.

// Aktifkan pemuatan kelas dengan garis bawah
// Defaultnya adalah true
Loader::$v2ClassLoading = false;

Variabel

Flight memungkinkan Anda untuk menyimpan variabel sehingga dapat digunakan di mana saja dalam aplikasi Anda.

// Simpan variabel Anda
Flight::set('id', 123);

// Di tempat lain dalam aplikasi Anda
$id = Flight::get('id');

Untuk melihat apakah sebuah variabel telah disetel, Anda dapat melakukan:

if (Flight::has('id')) {
  // Lakukan sesuatu
}

Anda dapat menghapus variabel dengan melakukan:

// Menghapus variabel id
Flight::clear('id');

// Menghapus semua variabel
Flight::clear();

Flight juga menggunakan variabel untuk tujuan konfigurasi.

Flight::set('flight.log_errors', true);

Penanganan Kesalahan

Kesalahan dan Pengecualian

Semua kesalahan dan pengecualian ditangkap oleh Flight dan diteruskan ke metode error. Perilaku default adalah mengirimkan respons HTTP 500 Internal Server Error dengan beberapa informasi kesalahan.

Anda dapat mengganti perilaku ini sesuai kebutuhan Anda:

Flight::map('error', function (Throwable $error) {
  // Tangani kesalahan
  echo $error->getTraceAsString();
});

Secara default kesalahan tidak dicatat ke server web. Anda dapat mengaktifkan ini dengan mengubah konfigurasi:

Flight::set('flight.log_errors', true);

Tidak Ditemukan

Ketika sebuah URL tidak dapat ditemukan, Flight memanggil metode notFound. Perilaku default adalah mengirimkan respons HTTP 404 Not Found dengan pesan sederhana.

Anda dapat mengganti perilaku ini sesuai kebutuhan Anda:

Flight::map('notFound', function () {
  // Tangani tidak ditemukan
});

Learn/security

Keamanan

Keamanan adalah hal yang penting ketika berhubungan dengan aplikasi web. Anda ingin memastikan bahwa aplikasi Anda aman dan data pengguna Anda terlindungi. Flight menyediakan sejumlah fitur untuk membantu Anda mengamankan aplikasi web Anda.

Header

Header HTTP adalah salah satu cara termudah untuk mengamankan aplikasi web Anda. Anda dapat menggunakan header untuk mencegah clickjacking, XSS, dan serangan lainnya. Ada beberapa cara yang dapat Anda lakukan untuk menambahkan header ini ke aplikasi Anda.

Dua situs web yang bagus untuk memeriksa keamanan header Anda adalah securityheaders.com dan observatory.mozilla.org.

Tambah Secara Manual

Anda dapat menambahkan header ini secara manual dengan menggunakan metode header pada objek Flight\Response.

// Atur header X-Frame-Options untuk mencegah clickjacking
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');

// Atur header Content-Security-Policy untuk mencegah XSS
// Catatan: header ini bisa menjadi sangat kompleks, jadi Anda akan
//  ingin berkonsultasi dengan contoh di internet untuk aplikasi Anda
Flight::response()->header("Content-Security-Policy", "default-src 'self'");

// Atur header X-XSS-Protection untuk mencegah XSS
Flight::response()->header('X-XSS-Protection', '1; mode=block');

// Atur header X-Content-Type-Options untuk mencegah sniffing MIME
Flight::response()->header('X-Content-Type-Options', 'nosniff');

// Atur header Referrer-Policy untuk mengontrol seberapa banyak informasi referer yang dikirim
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');

// Atur header Strict-Transport-Security untuk memaksakan HTTPS
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');

// Atur header Permissions-Policy untuk mengontrol fitur dan API apa yang dapat digunakan
Flight::response()->header('Permissions-Policy', 'geolocation=()');

Header ini dapat ditambahkan di atas file bootstrap.php atau index.php Anda.

Tambah sebagai Filter

Anda juga dapat menambahkannya dalam filter/hook seperti berikut:

// Tambahkan header dalam filter
Flight::before('start', function() {
    Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
    Flight::response()->header("Content-Security-Policy", "default-src 'self'");
    Flight::response()->header('X-XSS-Protection', '1; mode=block');
    Flight::response()->header('X-Content-Type-Options', 'nosniff');
    Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
    Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
    Flight::response()->header('Permissions-Policy', 'geolocation=()');
});

Tambah sebagai Middleware

Anda juga dapat menambahkannya sebagai kelas middleware. Ini adalah cara yang baik untuk menjaga kode Anda tetap bersih dan terorganisir.

// app/middleware/SecurityHeadersMiddleware.php

namespace app\middleware;

class SecurityHeadersMiddleware
{
    public function before(array $params): void
    {
        Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
        Flight::response()->header("Content-Security-Policy", "default-src 'self'");
        Flight::response()->header('X-XSS-Protection', '1; mode=block');
        Flight::response()->header('X-Content-Type-Options', 'nosniff');
        Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
        Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
        Flight::response()->header('Permissions-Policy', 'geolocation=()');
    }
}

// index.php atau di mana pun Anda memiliki rute Anda
// FYI, grup string kosong ini bertindak sebagai middleware global untuk
// semua rute. Tentu saja Anda dapat melakukan hal yang sama dan hanya menambah
// ini hanya di rute tertentu.
Flight::group('', function(Router $router) {
    $router->get('/users', [ 'UserController', 'getUsers' ]);
    // lebih banyak rute
}, [ new SecurityHeadersMiddleware() ]);

Serangan Permintaan Lintas Situs (CSRF)

Serangan Permintaan Lintas Situs (CSRF) adalah jenis serangan di mana situs web jahat dapat membuat browser pengguna mengirim permintaan ke situs web Anda. Ini dapat digunakan untuk melakukan tindakan di situs web Anda tanpa sepengetahuan pengguna. Flight tidak menyediakan mekanisme perlindungan CSRF bawaan, tetapi Anda dapat dengan mudah mengimplementasikan sendiri menggunakan middleware.

Setup

Pertama Anda perlu menghasilkan token CSRF dan menyimpannya di sesi pengguna. Anda kemudian dapat menggunakan token ini dalam formulir Anda dan memeriksanya saat formulir diserahkan.

// Menghasilkan token CSRF dan menyimpannya di sesi pengguna
// (asumsikan Anda telah membuat objek sesi dan mengaitkannya dengan Flight)
// lihat dokumentasi sesi untuk informasi lebih lanjut
Flight::register('session', \Ghostff\Session\Session::class);

// Anda hanya perlu menghasilkan satu token per sesi (agar berfungsi 
// di beberapa tab dan permintaan untuk pengguna yang sama)
if(Flight::session()->get('csrf_token') === null) {
    Flight::session()->set('csrf_token', bin2hex(random_bytes(32)) );
}
<!-- Gunakan token CSRF di formulir Anda -->
<form method="post">
    <input type="hidden" name="csrf_token" value="<?= Flight::session()->get('csrf_token') ?>">
    <!-- field formulir lainnya -->
</form>

Menggunakan Latte

Anda juga dapat mengatur fungsi kustom untuk menampilkan token CSRF dalam template Latte Anda.

// Atur fungsi kustom untuk menampilkan token CSRF
// Catatan: View telah dikonfigurasi dengan Latte sebagai mesin tampilan
Flight::view()->addFunction('csrf', function() {
    $csrfToken = Flight::session()->get('csrf_token');
    return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">');
});

Dan sekarang di template Latte Anda, Anda dapat menggunakan fungsi csrf() untuk menampilkan token CSRF.

<form method="post">
    {csrf()}
    <!-- field formulir lainnya -->
</form>

Singkat dan sederhana, bukan?

Periksa Token CSRF

Anda dapat memeriksa token CSRF menggunakan filter peristiwa:

// Middleware ini memeriksa apakah permintaan adalah permintaan POST dan jika iya, memeriksa apakah token CSRF valid
Flight::before('start', function() {
    if(Flight::request()->method == 'POST') {

        // tangkap token csrf dari nilai formulir
        $token = Flight::request()->data->csrf_token;
        if($token !== Flight::session()->get('csrf_token')) {
            Flight::halt(403, 'Token CSRF tidak valid');
            // atau untuk respons JSON
            Flight::jsonHalt(['error' => 'Token CSRF tidak valid'], 403);
        }
    }
});

Atau Anda dapat menggunakan kelas middleware:

// app/middleware/CsrfMiddleware.php

namespace app\middleware;

class CsrfMiddleware
{
    public function before(array $params): void
    {
        if(Flight::request()->method == 'POST') {
            $token = Flight::request()->data->csrf_token;
            if($token !== Flight::session()->get('csrf_token')) {
                Flight::halt(403, 'Token CSRF tidak valid');
            }
        }
    }
}

// index.php atau di mana pun Anda memiliki rute Anda
Flight::group('', function(Router $router) {
    $router->get('/users', [ 'UserController', 'getUsers' ]);
    // lebih banyak rute
}, [ new CsrfMiddleware() ]);

Serangan Skrip Lintas Situs (XSS)

Serangan Skrip Lintas Situs (XSS) adalah jenis serangan di mana situs web jahat dapat menyuntikkan kode ke situs web Anda. Sebagian besar peluang ini datang dari nilai formulir yang akan diisi oleh pengguna akhir Anda. Anda tidak pernah mempercayai output dari pengguna Anda! Selalu anggap semua dari mereka adalah hacker terbaik di dunia. Mereka dapat menyuntikkan JavaScript atau HTML berbahaya ke halaman Anda. Kode ini dapat digunakan untuk mencuri informasi dari pengguna Anda atau melakukan tindakan di situs web Anda. Dengan menggunakan kelas view dari Flight, Anda dapat dengan mudah melarikan output untuk mencegah serangan XSS.

// Mari kita anggap pengguna cerdas dan mencoba menggunakan ini sebagai nama mereka
$name = '<script>alert("XSS")</script>';

// Ini akan melarikan output
Flight::view()->set('name', $name);
// Ini akan menampilkan: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

// Jika Anda menggunakan sesuatu seperti Latte yang terdaftar sebagai kelas view Anda, ini juga akan otomatis melarikan ini.
Flight::view()->render('template', ['name' => $name]);

SQL Injection

SQL Injection adalah jenis serangan di mana pengguna jahat dapat menyuntikkan kode SQL ke dalam database Anda. Ini dapat digunakan untuk mencuri informasi dari database Anda atau melakukan tindakan di database Anda. Sekali lagi Anda tidak pernah mempercayai input dari pengguna Anda! Selalu anggap mereka berniat buruk. Anda dapat menggunakan pernyataan terprepared dalam objek PDO Anda untuk mencegah SQL injection.

// Menganggap Anda memiliki Flight::db() terdaftar sebagai objek PDO Anda
$statement = Flight::db()->prepare('SELECT * FROM users WHERE username = :username');
$statement->execute([':username' => $username]);
$users = $statement->fetchAll();

// Jika Anda menggunakan kelas PdoWrapper, ini dapat dengan mudah dilakukan dalam satu baris
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE username = :username', [ 'username' => $username ]);

// Anda dapat melakukan hal yang sama dengan objek PDO dengan placeholder ?
$statement = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $username ]);

// Janji Anda tidak akan pernah MELAKUKAN sesuatu seperti ini...
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE username = '{$username}' LIMIT 5");
// karena bagaimana jika $username = "' OR 1=1; -- "; 
// Setelah kueri dibangun, terlihat seperti ini
// SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5
// Ini terlihat aneh, tetapi ini adalah kueri yang valid yang akan berfungsi. 
// Faktanya,
// ini adalah serangan SQL injection yang sangat umum yang akan mengembalikan semua pengguna.

CORS

Cross-Origin Resource Sharing (CORS) adalah mekanisme yang memungkinkan banyak sumber daya (misalnya, font, JavaScript, dll.) di halaman web untuk diminta dari domain lain di luar domain tempat sumber daya tersebut berasal. Flight tidak memiliki fungsi bawaan, tetapi ini dapat dengan mudah ditangani dengan hook untuk dijalankan sebelum metode Flight::start() dipanggil.

// app/utils/CorsUtil.php

namespace app\utils;

class CorsUtil
{
    public function set(array $params): void
    {
        $request = Flight::request();
        $response = Flight::response();
        if ($request->getVar('HTTP_ORIGIN') !== '') {
            $this->allowOrigins();
            $response->header('Access-Control-Allow-Credentials', 'true');
            $response->header('Access-Control-Max-Age', '86400');
        }

        if ($request->method === 'OPTIONS') {
            if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_METHOD') !== '') {
                $response->header(
                    'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD'
                );
            }
            if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') !== '') {
                $response->header(
                    "Access-Control-Allow-Headers",
                    $request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
                );
            }

            $response->status(200);
            $response->send();
            exit;
        }
    }

    private function allowOrigins(): void
    {
        // sesuaikan host yang diizinkan Anda di sini.
        $allowed = [
            'capacitor://localhost',
            'ionic://localhost',
            'http://localhost',
            'http://localhost:4200',
            'http://localhost:8080',
            'http://localhost:8100',
        ];

        $request = Flight::request();

        if (in_array($request->getVar('HTTP_ORIGIN'), $allowed, true) === true) {
            $response = Flight::response();
            $response->header("Access-Control-Allow-Origin", $request->getVar('HTTP_ORIGIN'));
        }
    }
}

// index.php atau di mana pun Anda memiliki rute Anda
$CorsUtil = new CorsUtil();

// Ini perlu dijalankan sebelum start dijalankan.
Flight::before('start', [ $CorsUtil, 'setupCors' ]);

Penanganan Kesalahan

Sembunyikan detail kesalahan sensitif di produksi untuk menghindari kebocoran informasi kepada penyerang.

// Di bootstrap.php atau index.php Anda

// di flightphp/skeleton, ini ada di app/config/config.php
$environment = ENVIRONMENT;
if ($environment === 'production') {
    ini_set('display_errors', 0); // Nonaktifkan tampilan error
    ini_set('log_errors', 1);     // Catat kesalahan sebagai gantinya
    ini_set('error_log', '/path/to/error.log');
}

// Di rute atau pengontrol Anda
// Gunakan Flight::halt() untuk respons kesalahan yang terkendali
Flight::halt(403, 'Akses ditolak');

Sanitasi Input

Jangan pernah mempercayai input pengguna. Sanitasi sebelum memproses untuk mencegah data berbahaya masuk.


// Menganggap permintaan $_POST dengan $_POST['input'] dan $_POST['email']

// Sanitasi input string
$clean_input = filter_var(Flight::request()->data->input, FILTER_SANITIZE_STRING);
// Sanitasi email
$clean_email = filter_var(Flight::request()->data->email, FILTER_SANITIZE_EMAIL);

Hashing Password

Simpan password dengan aman dan verifikasi dengan aman menggunakan fungsi bawaan PHP.

$password = Flight::request()->data->password;
// Hash password saat menyimpan (misalnya, saat pendaftaran)
$hashed_password = password_hash($password, PASSWORD_DEFAULT);

// Verifikasi password (misalnya, saat login)
if (password_verify($password, $stored_hash)) {
    // Password cocok
}

Pembatasan Kecepatan

Lindungi terhadap serangan brute force dengan membatasi laju permintaan menggunakan cache.

// Menganggap Anda telah menginstal dan mendaftarkan flightphp/cache
// Menggunakan flightphp/cache dalam middleware
Flight::before('start', function() {
    $cache = Flight::cache();
    $ip = Flight::request()->ip;
    $key = "rate_limit_{$ip}";
    $attempts = (int) $cache->retrieve($key);

    if ($attempts >= 10) {
        Flight::halt(429, 'Terlalu banyak permintaan');
    }

    $cache->set($key, $attempts + 1, 60); // Reset setelah 60 detik
});

Kesimpulan

Keamanan adalah hal yang penting dan penting untuk memastikan aplikasi web Anda aman. Flight menyediakan sejumlah fitur untuk membantu Anda mengamankan aplikasi web Anda, tetapi penting untuk selalu waspada dan memastikan Anda melakukan segala sesuatu yang Anda bisa untuk menjaga data pengguna Anda tetap aman. Selalu anggap yang terburuk dan jangan pernah mempercayai input dari pengguna Anda. Selalu melarikan output dan gunakan pernyataan terprepared untuk mencegah SQL injection. Selalu gunakan middleware untuk melindungi rute Anda dari serangan CSRF dan CORS. Jika Anda melakukan semua hal ini, Anda akan berada di jalur yang tepat untuk membangun aplikasi web yang aman.

Learn/routing

Routing

Catatan: Ingin memahami lebih lanjut tentang routing? Periksa halaman "mengapa sebuah framework?" untuk penjelasan yang lebih mendalam.

Routing dasar di Flight dilakukan dengan mencocokkan pola URL dengan fungsi callback atau sebuah array dari sebuah kelas dan metode.

Flight::route('/', function(){
    echo 'hello world!';
});

Rute dicocokkan dalam urutan mereka didefinisikan. Rute pertama yang mencocokkan permintaan akan dipanggil.

Callback/Fungsi

Callback dapat berupa objek apa pun yang dapat dipanggil. Jadi Anda dapat menggunakan fungsi biasa:

function hello() {
    echo 'hello world!';
}

Flight::route('/', 'hello');

Kelas

Anda juga dapat menggunakan metode statis dari sebuah kelas:

class Greeting {
    public static function hello() {
        echo 'hello world!';
    }
}

Flight::route('/', [ 'Greeting','hello' ]);

Atau dengan membuat objek terlebih dahulu dan kemudian memanggil metode:


// Greeting.php
class Greeting
{
    public function __construct() {
        $this->name = 'John Doe';
    }

    public function hello() {
        echo "Hello, {$this->name}!";
    }
}

// index.php
$greeting = new Greeting();

Flight::route('/', [ $greeting, 'hello' ]);
// Anda juga dapat melakukan ini tanpa membuat objek terlebih dahulu
// Catatan: Tidak ada argumen yang akan disuntikkan ke konstruktor
Flight::route('/', [ 'Greeting', 'hello' ]);
// Selain itu, Anda dapat menggunakan sintaks lebih pendek ini
Flight::route('/', 'Greeting->hello');
// atau
Flight::route('/', Greeting::class.'->hello');

Dependency Injection melalui DIC (Dependency Injection Container)

Jika Anda ingin menggunakan dependency injection melalui sebuah container (PSR-11, PHP-DI, Dice, dll), satu-satunya jenis rute yang tersedia adalah langsung membuat objek sendiri dan menggunakan container untuk membuat objek Anda atau Anda dapat menggunakan string untuk mendefinisikan kelas dan metode yang akan dipanggil. Anda dapat pergi ke halaman Dependency Injection untuk informasi lebih lanjut.

Berikut adalah contoh cepat:


use flight\database\PdoWrapper;

// Greeting.php
class Greeting
{
    protected PdoWrapper $pdoWrapper;
    public function __construct(PdoWrapper $pdoWrapper) {
        $this->pdoWrapper = $pdoWrapper;
    }

    public function hello(int $id) {
        // lakukan sesuatu dengan $this->pdoWrapper
        $name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
        echo "Hello, world! Nama saya adalah {$name}!";
    }
}

// index.php

// Siapkan container dengan parameter apa pun yang Anda butuhkan
// Lihat halaman Dependency Injection untuk informasi lebih lanjut tentang PSR-11
$dice = new \Dice\Dice();

// Jangan lupa untuk menetapkan kembali variabel dengan '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
    'shared' => true,
    'constructParams' => [ 
        'mysql:host=localhost;dbname=test', 
        'root',
        'password'
    ]
]);

// Daftarkan pengendali container
Flight::registerContainerHandler(function($class, $params) use ($dice) {
    return $dice->create($class, $params);
});

// Rute seperti biasa
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// atau
Flight::route('/hello/@id', 'Greeting->hello');
// atau
Flight::route('/hello/@id', 'Greeting::hello');

Flight::start();

Metode Routing

Secara default, pola rute dicocokkan dengan semua metode permintaan. Anda dapat merespons metode tertentu dengan menempatkan pengenal sebelum URL.

Flight::route('GET /', function () {
  echo 'Saya menerima permintaan GET.';
});

Flight::route('POST /', function () {
  echo 'Saya menerima permintaan POST.';
});

// Anda tidak dapat menggunakan Flight::get() untuk rute karena itu adalah metode 
//    untuk mendapatkan variabel, tidak membuat rute.
// Flight::post('/', function() { /* kode */ });
// Flight::patch('/', function() { /* kode */ });
// Flight::put('/', function() { /* kode */ });
// Flight::delete('/', function() { /* kode */ });

Anda juga dapat memetakan beberapa metode ke satu callback dengan menggunakan pemisah |:

Flight::route('GET|POST /', function () {
  echo 'Saya menerima baik permintaan GET atau POST.';
});

Selain itu, Anda dapat mengambil objek Router yang memiliki beberapa metode pembantu untuk Anda gunakan:


$router = Flight::router();

// memetakan semua metode
$router->map('/', function() {
    echo 'hello world!';
});

// permintaan GET
$router->get('/users', function() {
    echo 'users';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();

Ekspresi Reguler

Anda dapat menggunakan ekspresi reguler dalam rute Anda:

Flight::route('/user/[0-9]+', function () {
  // Ini akan mencocokkan /user/1234
});

Meskipun metode ini tersedia, disarankan untuk menggunakan parameter bernama, atau parameter bernama dengan ekspresi reguler, karena lebih mudah dibaca dan lebih mudah untuk dipelihara.

Parameter Bernama

Anda dapat menentukan parameter bernama dalam rute Anda yang akan diteruskan ke fungsi callback Anda. Ini lebih untuk keterbacaan rute daripada yang lain . Silakan lihat bagian di bawah tentang caveat penting.

Flight::route('/@name/@id', function (string $name, string $id) {
  echo "hello, $name ($id)!";
});

Anda juga dapat menyertakan ekspresi reguler dengan parameter bernama Anda menggunakan pemisah ::

Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
  // Ini akan mencocokkan /bob/123
  // Tetapi tidak akan mencocokkan /bob/12345
});

Catatan: Mencocokkan grup regex () dengan parameter posisi tidak didukung. :'(

Caveat Penting

Meskipun dalam contoh di atas, tampaknya @name terikat langsung pada variabel $name, itu tidak. Urutan parameter dalam fungsi callback yang menentukan apa yang diteruskan ke dalamnya. Jadi jika Anda membalik urutan parameter dalam fungsi callback, variabel juga akan dibalik. Berikut adalah contohnya:

Flight::route('/@name/@id', function (string $id, string $name) {
  echo "hello, $name ($id)!";
});

Dan jika Anda mengunjungi URL berikut: /bob/123, hasilnya akan menjadi hello, 123 (bob)!. Silakan berhati-hati saat Anda menetapkan rute dan fungsi callback Anda.

Parameter Opsional

Anda dapat menentukan parameter bernama yang bersifat opsional untuk pencocokan dengan membungkus segmen dalam tanda kurung.

Flight::route(
  '/blog(/@year(/@month(/@day)))',
  function(?string $year, ?string $month, ?string $day) {
    // Ini akan mencocokkan URL berikut:
    // /blog/2012/12/10
    // /blog/2012/12
    // /blog/2012
    // /blog
  }
);

Parameter opsional yang tidak dicocokkan akan diteruskan sebagai NULL.

Wildcards

Pencocokan hanya dilakukan pada segmen URL individu. Jika Anda ingin mencocokkan beberapa segmen Anda dapat menggunakan wildcard *.

Flight::route('/blog/*', function () {
  // Ini akan mencocokkan /blog/2000/02/01
});

Untuk merutekan semua permintaan ke satu callback, Anda dapat melakukan:

Flight::route('*', function () {
  // Lakukan sesuatu
});

Menyampaikan

Anda dapat meneruskan eksekusi ke rute berikutnya yang cocok dengan mengembalikan true dari fungsi callback Anda.

Flight::route('/user/@name', function (string $name) {
  // Periksa beberapa kondisi
  if ($name !== "Bob") {
    // Lanjutkan ke rute berikutnya
    return true;
  }
});

Flight::route('/user/*', function () {
  // Ini akan dipanggil
});

Aliasing Rute

Anda dapat menetapkan alias ke sebuah rute, sehingga URL dapat dibuat secara dinamis dilanjutkan dalam kode Anda (seperti template misalnya).

Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');

// nanti dalam kode di suatu tempat
Flight::getUrl('user_view', [ 'id' => 5 ]); // akan mengembalikan '/users/5'

Ini sangat membantu jika URL Anda kebetulan berubah. Dalam contoh di atas, katakanlah bahwa pengguna dipindahkan ke /admin/users/@id alih-alih. Dengan aliasing di tempat, Anda tidak perlu mengubah di mana pun Anda merujuk alias karena alias sekarang akan mengembalikan /admin/users/5 seperti dalam contoh di atas.

Aliasing rute masih berfungsi dalam grup juga:

Flight::group('/users', function() {
    Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
});

// nanti dalam kode di suatu tempat
Flight::getUrl('user_view', [ 'id' => 5 ]); // akan mengembalikan '/users/5'

Informasi Rute

Jika Anda ingin memeriksa informasi rute yang cocok, Anda dapat meminta objek rute untuk diteruskan ke fungsi callback Anda dengan meneruskan true sebagai parameter ketiga di metode rute. Objek rute akan selalu menjadi parameter terakhir yang diteruskan ke fungsi callback Anda.

Flight::route('/', function(\flight\net\Route $route) {
  // Array metode HTTP yang dicocokkan
  $route->methods;

  // Array parameter bernama
  $route->params;

  // Ekspresi reguler yang cocok
  $route->regex;

  // Berisi konten dari setiap '*' yang digunakan dalam pola URL
  $route->splat;

  // Menunjukkan jalur url....jika Anda benar-benar membutuhkannya
  $route->pattern;

  // Menunjukkan middleware apa yang ditugaskan untuk ini
  $route->middleware;

  // Menunjukkan alias yang ditugaskan untuk rute ini
  $route->alias;
}, true);

Pengelompokan Rute

Mungkin ada waktu ketika Anda ingin mengelompokkan rute-rute terkait bersama (seperti /api/v1). Anda dapat melakukan ini dengan menggunakan metode group:

Flight::group('/api/v1', function () {
  Flight::route('/users', function () {
    // Mencocokkan /api/v1/users
  });

  Flight::route('/posts', function () {
    // Mencocokkan /api/v1/posts
  });
});

Anda bahkan dapat menelurkan grup dari grup:

Flight::group('/api', function () {
  Flight::group('/v1', function () {
    // Flight::get() mendapatkan variabel, itu tidak menetapkan rute! Lihat konteks objek di bawah
    Flight::route('GET /users', function () {
      // Mencocokkan GET /api/v1/users
    });

    Flight::post('/posts', function () {
      // Mencocokkan POST /api/v1/posts
    });

    Flight::put('/posts/1', function () {
      // Mencocokkan PUT /api/v1/posts
    });
  });
  Flight::group('/v2', function () {

    // Flight::get() mendapatkan variabel, itu tidak menetapkan rute! Lihat konteks objek di bawah
    Flight::route('GET /users', function () {
      // Mencocokkan GET /api/v2/users
    });
  });
});

Pengelompokan dengan Konteks Objek

Anda masih dapat menggunakan pengelompokan rute dengan objek Engine dengan cara berikut:

$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {

  // gunakan variabel $router
  $router->get('/users', function () {
    // Mencocokkan GET /api/v1/users
  });

  $router->post('/posts', function () {
    // Mencocokkan POST /api/v1/posts
  });
});

Resource Routing

Anda dapat membuat serangkaian rute untuk sebuah sumber daya menggunakan metode resource. Ini akan membuat serangkaian rute untuk sumber daya yang mengikuti konvensi RESTful.

Untuk membuat sumber daya, lakukan hal berikut:

Flight::resource('/users', UsersController::class);

Dan yang akan terjadi di latar belakang adalah ini akan membuat rute berikut:

[
      'index' => 'GET ',
      'create' => 'GET /create',
      'store' => 'POST ',
      'show' => 'GET /@id',
      'edit' => 'GET /@id/edit',
      'update' => 'PUT /@id',
      'destroy' => 'DELETE /@id'
]

Dan controller Anda akan terlihat seperti ini:

class UsersController
{
    public function index(): void
    {
    }

    public function show(string $id): void
    {
    }

    public function create(): void
    {
    }

    public function store(): void
    {
    }

    public function edit(string $id): void
    {
    }

    public function update(string $id): void
    {
    }

    public function destroy(string $id): void
    {
    }
}

Catatan: Anda dapat melihat rute yang baru ditambahkan dengan runway dengan menjalankan php runway routes.

Menyesuaikan Rute Sumber Daya

Ada beberapa opsi untuk mengonfigurasi rute sumber daya.

Alias Basis

Anda dapat mengonfigurasi aliasBase. Secara default, alias adalah bagian terakhir dari URL yang ditentukan. Misalnya /users/ akan menghasilkan aliasBase menjadi users. Ketika rute ini dibuat, alias adalah users.index, users.create, dst. Jika Anda ingin mengubah alias, atur aliasBase ke nilai yang Anda inginkan.

Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);

Hanya dan Kecuali

Anda juga dapat menentukan rute mana yang ingin Anda buat dengan menggunakan opsi only dan except.

Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);

Ini pada dasarnya adalah opsi daftar putih dan daftar hitam sehingga Anda dapat menentukan rute mana yang ingin Anda buat.

Middleware

Anda juga dapat menentukan middleware yang akan dijalankan di setiap rute yang dibuat oleh metode resource.

Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);

Streaming

Anda sekarang dapat melakukan streaming respons ke klien menggunakan metode streamWithHeaders(). Ini berguna untuk mengirim file besar, proses yang memakan waktu lama, atau menghasilkan respons besar. Streaming rute ditangani sedikit berbeda dari rute biasa.

Catatan: Streaming respons hanya tersedia jika Anda memiliki flight.v2.output_buffering disetel ke false.

Stream dengan Header Manual

Anda dapat melakukan streaming respons ke klien dengan menggunakan metode stream() pada sebuah rute. Jika Anda melakukan ini, Anda harus menetapkan semua metode secara manual sebelum Anda mengeluarkan apapun kepada klien. Ini dilakukan dengan fungsi header() php atau metode Flight::response()->setRealHeader().

Flight::route('/@filename', function($filename) {

    // jelas Anda akan menyaring jalur dan lain-lain.
    $fileNameSafe = basename($filename);

    // Jika Anda memiliki header tambahan untuk diatur di sini setelah rute dieksekusi
    // Anda harus mendefinisikannya sebelum ada yang di-echo keluar.
    // Mereka semua harus merupakan panggilan mentah ke fungsi header() 
    // atau panggilan ke Flight::response()->setRealHeader()
    header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
    // atau
    Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$fileNameSafe.'"');

    $fileData = file_get_contents('/some/path/to/files/'.$fileNameSafe);

    // Penanganan kesalahan dan lain-lain
    if(empty($fileData)) {
        Flight::halt(404, 'File tidak ditemukan');
    }

    // set panjang konten secara manual jika Anda suka
    header('Content-Length: '.filesize($filename));

    // Streaming data ke klien
    echo $fileData;

// Ini adalah baris ajaib di sini
})->stream();

Stream dengan Header

Anda juga dapat menggunakan metode streamWithHeaders() untuk mengatur header sebelum Anda mulai streaming.

Flight::route('/stream-users', function() {

    // Anda dapat menambahkan header tambahan apa pun yang Anda inginkan di sini
    // Anda hanya harus menggunakan header() atau Flight::response()->setRealHeader()

    // namun cara Anda menarik data Anda, hanya sebagai contoh...
    $users_stmt = Flight::db()->query("SELECT id, first_name, last_name FROM users");

    echo '{';
    $user_count = count($users);
    while($user = $users_stmt->fetch(PDO::FETCH_ASSOC)) {
        echo json_encode($user);
        if(--$user_count > 0) {
            echo ',';
        }

        // Ini diperlukan untuk mengirim data ke klien
        ob_flush();
    }
    echo '}';

// Ini adalah bagaimana Anda mengatur header sebelum Anda mulai streaming.
})->streamWithHeaders([
    'Content-Type' => 'application/json',
    'Content-Disposition' => 'attachment; filename="users.json"',
    // kode status opsional, default ke 200
    'status' => 200
]);

Learn/flight_vs_symfony

Flight vs Symfony

Apa itu Symfony?

Symfony adalah sekumpulan komponen PHP yang dapat digunakan kembali dan framework PHP untuk proyek web.

Landasan standar di mana aplikasi PHP terbaik dibangun. Pilih salah satu dari 50 komponen mandiri yang tersedia untuk aplikasi Anda sendiri.

Percepat pembuatan dan pemeliharaan aplikasi web PHP Anda. Akhiri tugas pengkodean yang repetitif dan nikmati kekuatan mengendalikan kode Anda.

Kelebihan dibandingkan Flight

Kekurangan dibandingkan Flight

Learn/flight_vs_another_framework

Membandingkan Flight dengan Framework Lain

Jika Anda bermigrasi dari framework lain seperti Laravel, Slim, Fat-Free, atau Symfony ke Flight, halaman ini akan membantu Anda memahami perbedaan antara keduanya.

Laravel

Laravel adalah framework yang penuh fitur yang memiliki segala fasilitas dan ekosistem yang menakjubkan yang berfokus pada pengembang, tetapi dengan biaya dalam kinerja dan kompleksitas.

Lihat perbandingan antara Laravel dan Flight.

Slim

Slim adalah micro-framework yang mirip dengan Flight. Ini dirancang agar ringan dan mudah digunakan, tetapi bisa sedikit lebih kompleks daripada Flight.

Lihat perbandingan antara Slim dan Flight.

Fat-Free

Fat-Free adalah framework full-stack dalam paket yang jauh lebih kecil. Meskipun memiliki semua alat dalam kotak perkakas, ia memiliki arsitektur data yang dapat membuat beberapa proyek menjadi lebih kompleks daripada yang seharusnya.

Lihat perbandingan antara Fat-Free dan Flight.

Symfony

Symfony adalah framework modular tingkat perusahaan yang dirancang untuk fleksibel dan skalabel. Untuk proyek yang lebih kecil atau pengembang yang lebih baru, Symfony bisa sedikit membingungkan.

Lihat perbandingan antara Symfony dan Flight.

Learn/dependency_injection_container

Kontainer Penyuntikan Ketergantungan

Pengenalan

Kontainer Penyuntikan Ketergantungan (DIC) adalah alat yang kuat yang memungkinkan Anda untuk mengelola ketergantungan aplikasi Anda. Ini adalah konsep kunci dalam kerangka PHP modern dan digunakan untuk mengelola instansiasi dan konfigurasi objek. Beberapa contoh pustaka DIC adalah: Dice, Pimple, PHP-DI, dan league/container.

DIC adalah cara mewah untuk mengatakan bahwa ia memungkinkan Anda untuk membuat dan mengelola kelas Anda di lokasi terpusat. Ini berguna ketika Anda perlu mengoper objek yang sama ke beberapa kelas (seperti pengontrol Anda). Contoh sederhana mungkin membantu ini menjadi lebih jelas.

Contoh Dasar

Cara lama dalam melakukan sesuatu mungkin terlihat seperti ini:


require 'vendor/autoload.php';

// kelas untuk mengelola pengguna dari database
class UserController {

    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function view(int $id) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
        $stmt->execute(['id' => $id]);

        print_r($stmt->fetch());
    }
}

$User = new UserController(new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'));
Flight::route('/user/@id', [ $UserController, 'view' ]);

Flight::start();

Anda dapat melihat dari kode di atas bahwa kami membuat objek PDO baru dan mengoperasikan ke kelas UserController kami. Ini baik untuk aplikasi kecil, tetapi seiring pertumbuhan aplikasi Anda, Anda akan menemukan bahwa Anda membuat objek PDO yang sama di beberapa tempat. Inilah saatnya DIC berguna.

Berikut adalah contoh yang sama menggunakan DIC (menggunakan Dice):


require 'vendor/autoload.php';

// kelas yang sama seperti di atas. Tidak ada yang berubah
class UserController {

    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function view(int $id) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
        $stmt->execute(['id' => $id]);

        print_r($stmt->fetch());
    }
}

// buat kontainer baru
$container = new \Dice\Dice;
// jangan lupa untuk menetapkannya kembali ke dirinya sendiri seperti di bawah ini!
$container = $container->addRule('PDO', [
    // dibagikan berarti objek yang sama akan dikembalikan setiap kali
    'shared' => true,
    'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);

// Ini mendaftarkan penangan kontainer sehingga Flight tahu untuk menggunakannya.
Flight::registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

// sekarang kita bisa menggunakan kontainer untuk membuat UserController kita
Flight::route('/user/@id', [ 'UserController', 'view' ]);
// atau alternatifnya, Anda dapat mendefinisikan rute seperti ini
Flight::route('/user/@id', 'UserController->view');
// atau
Flight::route('/user/@id', 'UserController::view');

Flight::start();

Saya yakin Anda mungkin berpikir bahwa banyak kode tambahan ditambahkan ke contoh ini. Keajaiban datang ketika Anda memiliki pengontrol lain yang membutuhkan objek PDO.


// Jika semua pengontrol Anda memiliki konstruktor yang membutuhkan objek PDO
// masing-masing rute di bawah ini akan secara otomatis menginjeksikannya!!!
Flight::route('/company/@id', 'CompanyController->view');
Flight::route('/organization/@id', 'OrganizationController->view');
Flight::route('/category/@id', 'CategoryController->view');
Flight::route('/settings', 'SettingsController->view');

Bonus tambahan dari memanfaatkan DIC adalah bahwa pengujian unit menjadi jauh lebih mudah. Anda dapat membuat objek tiruan dan mengoperasikannya ke kelas Anda. Ini adalah manfaat besar ketika Anda menulis tes untuk aplikasi Anda!

PSR-11

Flight juga dapat menggunakan kontainer yang sesuai dengan PSR-11. Ini berarti Anda dapat menggunakan kontainer apa pun yang mengimplementasikan antarmuka PSR-11. Berikut adalah contoh menggunakan kontainer PSR-11 dari League:


require 'vendor/autoload.php';

// kelas UserController yang sama seperti di atas

$container = new \League\Container\Container();
$container->add(UserController::class)->addArgument(PdoWrapper::class);
$container->add(PdoWrapper::class)
    ->addArgument('mysql:host=localhost;dbname=test')
    ->addArgument('user')
    ->addArgument('pass');
Flight::registerContainerHandler($container);

Flight::route('/user', [ 'UserController', 'view' ]);

Flight::start();

Ini mungkin sedikit lebih panjang daripada contoh Dice sebelumnya, tetapi tetap menyelesaikan pekerjaan dengan keuntungan yang sama!

Penangan DIC Kustom

Anda juga dapat membuat penangan DIC Anda sendiri. Ini berguna jika Anda memiliki kontainer kustom yang ingin Anda gunakan yang bukan PSR-11 (Dice). Lihat contoh dasar untuk cara melakukannya.

Selain itu, ada beberapa default yang berguna yang akan membuat hidup Anda lebih mudah saat menggunakan Flight.

Instance Engine

Jika Anda menggunakan instance Engine di pengontrol/middleware Anda, inilah cara Anda mengkonfigurasinya:


// Di suatu tempat di file bootstrap Anda
$engine = Flight::app();

$container = new \Dice\Dice;
$container = $container->addRule('*', [
    'substitutions' => [
        // Ini adalah tempat Anda mengoper instance
        Engine::class => $engine
    ]
]);

$engine->registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

// Sekarang Anda dapat menggunakan instance Engine di pengontrol/middleware Anda

class MyController {
    public function __construct(Engine $app) {
        $this->app = $app;
    }

    public function index() {
        $this->app->render('index');
    }
}

Menambahkan Kelas Lain

Jika Anda memiliki kelas lain yang ingin Anda tambahkan ke kontainer, dengan Dice sangat mudah karena mereka akan secara otomatis diselesaikan oleh kontainer. Berikut adalah contohnya:


$container = new \Dice\Dice;
// Jika Anda tidak perlu menyuntikkan apa pun ke dalam kelas Anda
// Anda tidak perlu mendefinisikan apa pun!
Flight::registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

class MyCustomClass {
    public function parseThing() {
        return 'thing';
    }
}

class UserController {

    protected MyCustomClass $MyCustomClass;

    public function __construct(MyCustomClass $MyCustomClass) {
        $this->MyCustomClass = $MyCustomClass;
    }

    public function index() {
        echo $this->MyCustomClass->parseThing();
    }
}

Flight::route('/user', 'UserController->index');

Learn/middleware

Middleware Rute

Flight mendukung middleware rute dan group rute. Middleware adalah fungsi yang dijalankan sebelum (atau setelah) callback rute. Ini adalah cara yang bagus untuk menambahkan pemeriksaan otentikasi API dalam kode Anda, atau untuk memvalidasi bahwa pengguna memiliki izin untuk mengakses rute tersebut.

Middleware Dasar

Berikut adalah contoh dasar:

// Jika Anda hanya menyediakan fungsi anonim, itu akan dieksekusi sebelum callback rute. 
// tidak ada fungsi middleware "setelah" kecuali untuk kelas (lihat di bawah)
Flight::route('/path', function() { echo ' Di sini saya!'; })->addMiddleware(function() {
    echo 'Middleware pertama!';
});

Flight::start();

// Ini akan menghasilkan "Middleware pertama! Di sini saya!"

Ada beberapa catatan yang sangat penting tentang middleware yang harus Anda ketahui sebelum menggunakannya:

Kelas Middleware

Middleware dapat terdaftar sebagai kelas juga. Jika Anda perlu fungsi "setelah", Anda harus menggunakan kelas.

class MyMiddleware {
    public function before($params) {
        echo 'Middleware pertama!';
    }

    public function after($params) {
        echo 'Middleware terakhir!';
    }
}

$MyMiddleware = new MyMiddleware();
Flight::route('/path', function() { echo ' Di sini saya! '; })->addMiddleware($MyMiddleware); // juga ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]);

Flight::start();

// Ini akan menampilkan "Middleware pertama! Di sini saya! Middleware terakhir!"

Menangani Kesalahan Middleware

Misalkan Anda memiliki middleware otentikasi dan Anda ingin mengalihkan pengguna ke halaman login jika mereka tidak terautentikasi. Anda memiliki beberapa opsi di tangan Anda:

  1. Anda dapat mengembalikan false dari fungsi middleware dan Flight akan secara otomatis mengembalikan kesalahan 403 Forbidden, tetapi tidak ada kustomisasi.
  2. Anda dapat mengalihkan pengguna ke halaman login menggunakan Flight::redirect().
  3. Anda dapat membuat kesalahan kustom dalam middleware dan menghentikan eksekusi rute.

Contoh Dasar

Ini adalah contoh sederhana return false;:

class MyMiddleware {
    public function before($params) {
        if (isset($_SESSION['user']) === false) {
            return false;
        }

        // karena ini benar, semuanya akan terus berjalan
    }
}

Contoh Redirect

Ini adalah contoh mengalihkan pengguna ke halaman login:

class MyMiddleware {
    public function before($params) {
        if (isset($_SESSION['user']) === false) {
            Flight::redirect('/login');
            exit;
        }
    }
}

Contoh Kesalahan Kustom

Misalkan Anda perlu membuang kesalahan JSON karena Anda sedang membangun API. Anda dapat melakukannya seperti ini:

class MyMiddleware {
    public function before($params) {
        $authorization = Flight::request()->headers['Authorization'];
        if(empty($authorization)) {
            Flight::jsonHalt(['error' => 'Anda harus masuk untuk mengakses halaman ini.'], 403);
            // atau
            Flight::json(['error' => 'Anda harus masuk untuk mengakses halaman ini.'], 403);
            exit;
            // atau
            Flight::halt(403, json_encode(['error' => 'Anda harus masuk untuk mengakses halaman ini.']));
        }
    }
}

Pengelompokan Middleware

Anda dapat menambahkan grup rute, dan kemudian setiap rute dalam grup itu akan memiliki middleware yang sama juga. Ini berguna jika Anda perlu mengelompokkan sejumlah rute dengan misalnya middleware Auth untuk memeriksa kunci API di header.


// ditambahkan di akhir metode grup
Flight::group('/api', function() {

    // Rute yang "kosong" ini sebenarnya akan mencocokkan /api
    Flight::route('', function() { echo 'api'; }, false, 'api');
    // Ini akan mencocokkan /api/users
    Flight::route('/users', function() { echo 'users'; }, false, 'users');
    // Ini akan mencocokkan /api/users/1234
    Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);

Jika Anda ingin menerapkan middleware global ke semua rute Anda, Anda dapat menambahkan grup "kosong":


// ditambahkan di akhir metode grup
Flight::group('', function() {

    // Ini masih /users
    Flight::route('/users', function() { echo 'users'; }, false, 'users');
    // Dan ini masih /users/1234
    Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);

Learn/filtering

Penyaringan

Flight memungkinkan Anda untuk menyaring metode sebelum dan setelah mereka dipanggil. Tidak ada hook yang telah ditentukan sebelumnya yang perlu Anda ingat. Anda dapat menyaring salah satu dari metode kerangka kerja default serta metode kustom apa pun yang telah Anda peta.

Fungsi filter terlihat seperti ini:

function (array &$params, string &$output): bool {
  // Kode penyaring
}

Dengan menggunakan variabel yang diteruskan, Anda dapat memanipulasi parameter input dan/atau output.

Anda dapat menjalankan filter sebelum sebuah metode dengan melakukan:

Flight::before('start', function (array &$params, string &$output): bool {
  // Lakukan sesuatu
});

Anda dapat menjalankan filter setelah sebuah metode dengan melakukan:

Flight::after('start', function (array &$params, string &$output): bool {
  // Lakukan sesuatu
});

Anda dapat menambahkan sebanyak mungkin filter yang Anda inginkan ke metode mana pun. Mereka akan dipanggil dalam urutan di mana mereka dideklarasikan.

Berikut adalah contoh proses penyaringan:

// Peta metode kustom
Flight::map('hello', function (string $name) {
  return "Halo, $name!";
});

// Tambahkan filter sebelum
Flight::before('hello', function (array &$params, string &$output): bool {
  // Manipulasi parameter
  $params[0] = 'Fred';
  return true;
});

// Tambahkan filter setelah
Flight::after('hello', function (array &$params, string &$output): bool {
  // Manipulasi output
  $output .= " Semoga harimu menyenangkan!";
  return true;
});

// Panggil metode kustom
echo Flight::hello('Bob');

Ini seharusnya menampilkan:

Halo Fred! Semoga harimu menyenangkan!

Jika Anda telah mendefinisikan beberapa filter, Anda dapat memutus rantai dengan mengembalikan false di salah satu fungsi filter Anda:

Flight::before('start', function (array &$params, string &$output): bool {
  echo 'satu';
  return true;
});

Flight::before('start', function (array &$params, string &$output): bool {
  echo 'dua';

  // Ini akan mengakhiri rantai
  return false;
});

// Ini tidak akan dipanggil
Flight::before('start', function (array &$params, string &$output): bool {
  echo 'tiga';
  return true;
});

Catatan, metode inti seperti map dan register tidak dapat disaring karena mereka dipanggil secara langsung dan tidak dipanggil secara dinamis.

Learn/requests

Permintaan

Flight mengenkapsulasi permintaan HTTP ke dalam satu objek, yang dapat diakses dengan melakukan:

$request = Flight::request();

Kasus Penggunaan Umum

Ketika Anda sedang bekerja dengan permintaan di aplikasi web, biasanya Anda ingin mengambil header, atau parameter $_GET atau $_POST, atau mungkin bahkan body permintaan mentah. Flight menyediakan antarmuka sederhana untuk melakukan semua hal ini.

Berikut adalah contoh mengambil parameter string kueri:

Flight::route('/search', function(){
    $keyword = Flight::request()->query['keyword'];
    echo "Anda sedang mencari: $keyword";
    // kueri database atau sesuatu lainnya dengan $keyword
});

Berikut adalah contoh dari mungkin sebuah formulir dengan metode POST:

Flight::route('POST /submit', function(){
    $name = Flight::request()->data['name'];
    $email = Flight::request()->data['email'];
    echo "Anda mengirim: $name, $email";
    // simpan ke dalam database atau sesuatu lainnya dengan $name dan $email
});

Properti Objek Permintaan

Objek permintaan menyediakan properti berikut:

Anda dapat mengakses properti query, data, cookies, dan files sebagai array atau objek.

Jadi, untuk mendapatkan parameter string kueri, Anda dapat melakukan:

$id = Flight::request()->query['id'];

Atau Anda dapat melakukan:

$id = Flight::request()->query->id;

Body Permintaan RAW

Untuk mendapatkan body permintaan HTTP mentah, misalnya saat menangani permintaan PUT, Anda dapat melakukan:

$body = Flight::request()->getBody();

Input JSON

Jika Anda mengirimkan permintaan dengan tipe application/json dan data {"id": 123} itu akan tersedia dari properti data:

$id = Flight::request()->data->id;

$_GET

Anda dapat mengakses array $_GET melalui properti query:

$id = Flight::request()->query['id'];

$_POST

Anda dapat mengakses array $_POST melalui properti data:

$id = Flight::request()->data['id'];

$_COOKIE

Anda dapat mengakses array $_COOKIE melalui properti cookies:

$myCookieValue = Flight::request()->cookies['myCookieName'];

$_SERVER

Ada pintasan yang tersedia untuk mengakses array $_SERVER melalui metode getVar():


$host = Flight::request()->getVar['HTTP_HOST'];

Mengakses File yang Diunggah melalui $_FILES

Anda dapat mengakses file yang diunggah melalui properti files:

$uploadedFile = Flight::request()->files['myFile'];

Memproses Unggahan File (v3.12.0)

Anda dapat memproses unggahan file menggunakan framework dengan beberapa metode pembantu. Secara dasar, ini menyangkut mengambil data file dari permintaan, dan memindahkannya ke lokasi baru.

Flight::route('POST /upload', function(){
    // Jika Anda memiliki field input seperti <input type="file" name="myFile">
    $uploadedFileData = Flight::request()->getUploadedFiles();
    $uploadedFile = $uploadedFileData['myFile'];
    $uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
});

Jika Anda memiliki beberapa file yang diunggah, Anda dapat melakukan pengulangan melalui mereka:

Flight::route('POST /upload', function(){
    // Jika Anda memiliki field input seperti <input type="file" name="myFiles[]">
    $uploadedFiles = Flight::request()->getUploadedFiles()['myFiles'];
    foreach ($uploadedFiles as $uploadedFile) {
        $uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
    }
});

Catatan Keamanan: Selalu validasi dan bersihkan input pengguna, terutama saat menangani unggahan file. Selalu validasi tipe ekstensi yang akan Anda izinkan untuk diunggah, tetapi Anda juga harus memvalidasi "magic bytes" dari file untuk memastikan file tersebut sebenarnya adalah tipe file yang diklaim pengguna. Terdapat artikel dan perpustakaan yang tersedia untuk membantu dengan ini.

Header Permintaan

Anda dapat mengakses header permintaan menggunakan metode getHeader() atau getHeaders():


// Mungkin Anda membutuhkan header Authorization
$host = Flight::request()->getHeader('Authorization');
// atau
$host = Flight::request()->header('Authorization');

// Jika Anda perlu mengambil semua header
$headers = Flight::request()->getHeaders();
// atau
$headers = Flight::request()->headers();

Body Permintaan

Anda dapat mengakses body permintaan mentah menggunakan metode getBody():

$body = Flight::request()->getBody();

Metode Permintaan

Anda dapat mengakses metode permintaan menggunakan properti method atau metode getMethod():

$method = Flight::request()->method; // sebenarnya memanggil getMethod()
$method = Flight::request()->getMethod();

Catatan: Metode getMethod() pertama-tama menarik metode dari $_SERVER['REQUEST_METHOD'], kemudian bisa ditimpa oleh $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] jika ada atau $_REQUEST['_method'] jika ada.

URL Permintaan

Terdapat beberapa metode pembantu untuk menyusun bagian dari URL demi kenyamanan Anda.

URL Lengkap

Anda dapat mengakses URL permintaan penuh menggunakan metode getFullUrl():

$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar

URL Dasar

Anda dapat mengakses URL dasar menggunakan metode getBaseUrl():

$url = Flight::request()->getBaseUrl();
// Perhatikan, tidak ada garis miring di akhir.
// https://example.com

Penguraian Kueri

Anda dapat mengoper URL ke metode parseQuery() untuk menguraikan string kueri menjadi array asosiatif:

$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']

Learn/api

Metode API Framework

Flight dirancang untuk mudah digunakan dan dipahami. Berikut adalah set lengkap metode untuk framework. Ini terdiri dari metode inti, yang merupakan metode statis biasa, dan metode yang dapat diperluas, yang merupakan metode yang dipetakan yang dapat disaring atau ditimpa.

Metode Inti

Metode-metode ini adalah inti dari framework dan tidak dapat ditimpa.

Flight::map(string $name, callable $callback, bool $pass_route = false) // Membuat metode framework khusus.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Mendaftarkan kelas ke metode framework.
Flight::unregister(string $name) // Menghapus pendaftaran kelas pada metode framework.
Flight::before(string $name, callable $callback) // Menambahkan filter sebelum metode framework.
Flight::after(string $name, callable $callback) // Menambahkan filter setelah metode framework.
Flight::path(string $path) // Menambahkan jalur untuk memuat kelas secara otomatis.
Flight::get(string $key) // Mengambil variabel yang disetel oleh Flight::set().
Flight::set(string $key, mixed $value) // Menyetel variabel dalam mesin Flight.
Flight::has(string $key) // Memeriksa apakah sebuah variabel disetel.
Flight::clear(array|string $key = []) // Menghapus sebuah variabel.
Flight::init() // Menginisialisasi framework ke pengaturan defaultnya.
Flight::app() // Mengambil instance objek aplikasi.
Flight::request() // Mengambil instance objek permintaan.
Flight::response() // Mengambil instance objek respons.
Flight::router() // Mengambil instance objek router.
Flight::view() // Mengambil instance objek tampilan.

Metode yang Dapat Diperluas

Flight::start() // Memulai framework.
Flight::stop() // Menghentikan framework dan mengirimkan respons.
Flight::halt(int $code = 200, string $message = '') // Menghentikan framework dengan kode status dan pesan opsional.
Flight::route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Memetakan pola URL ke callback.
Flight::post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Memetakan pola URL permintaan POST ke callback.
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Memetakan pola URL permintaan PUT ke callback.
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Memetakan pola URL permintaan PATCH ke callback.
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Memetakan pola URL permintaan DELETE ke callback.
Flight::group(string $pattern, callable $callback) // Membuat pengelompokan untuk URL, pola harus berupa string.
Flight::getUrl(string $name, array $params = []) // Menghasilkan URL berdasarkan alias rute.
Flight::redirect(string $url, int $code) // Mengalihkan ke URL lain.
Flight::download(string $filePath) // Mengunduh sebuah file.
Flight::render(string $file, array $data, ?string $key = null) // Merender file template.
Flight::error(Throwable $error) // Mengirimkan respons HTTP 500.
Flight::notFound() // Mengirimkan respons HTTP 404.
Flight::etag(string $id, string $type = 'string') // Melakukan caching HTTP ETag.
Flight::lastModified(int $time) // Melakukan caching HTTP terakhir dimodifikasi.
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Mengirimkan respons JSON.
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Mengirimkan respons JSONP.
Flight::jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Mengirimkan respons JSON dan menghentikan framework.
Flight::onEvent(string $event, callable $callback) // Mendaftarkan pendengar acara.
Flight::triggerEvent(string $event, ...$args) // Memicu sebuah acara.

Metode kustom apa pun yang ditambahkan dengan map dan register juga dapat disaring. Untuk contoh tentang bagaimana memetakan metode ini, lihat panduan Memperluas Flight.

Learn/why_frameworks

Mengapa Kerangka?

Beberapa pemrogram sangat menentang penggunaan kerangka. Mereka berpendapat bahwa kerangka itu berlebihan, lambat, dan sulit dipelajari. Mereka mengatakan bahwa kerangka tidak diperlukan dan bahwa Anda dapat menulis kode yang lebih baik tanpa mereka. Tentu ada beberapa poin yang valid mengenai kekurangan menggunakan kerangka. Namun, ada juga banyak keuntungan dalam menggunakan kerangka.

Alasan untuk Menggunakan Kerangka

Berikut adalah beberapa alasan mengapa Anda mungkin ingin mempertimbangkan untuk menggunakan kerangka:

Flight adalah micro-framework. Ini berarti bahwa ia kecil dan ringan. Ia tidak menyediakan sebanyak fungsionalitas seperti kerangka besar seperti Laravel atau Symfony. Namun, ia menyediakan banyak fungsionalitas yang Anda butuhkan untuk membangun aplikasi web. Ini juga mudah dipelajari dan digunakan. Ini membuatnya menjadi pilihan yang baik untuk membangun aplikasi web dengan cepat dan mudah. Jika Anda baru mengenal kerangka, Flight adalah kerangka pemula yang hebat untuk mulai digunakan. Ini akan membantu Anda belajar tentang keuntungan menggunakan kerangka tanpa membebani Anda dengan terlalu banyak kompleksitas. Setelah Anda memiliki beberapa pengalaman dengan Flight, akan lebih mudah untuk beralih ke kerangka yang lebih kompleks seperti Laravel atau Symfony, namun Flight masih dapat membuat aplikasi yang berhasil dan tangguh.

Apa itu Routing?

Routing adalah inti dari kerangka Flight, tetapi apa itu sebenarnya? Routing adalah proses mengambil URL dan mencocokkannya dengan fungsi tertentu di kode Anda. Inilah cara Anda dapat membuat situs web Anda melakukan hal-hal yang berbeda berdasarkan URL yang diminta. Misalnya, Anda mungkin ingin menampilkan profil pengguna ketika mereka mengunjungi /user/1234, tetapi menampilkan daftar semua pengguna ketika mereka mengunjungi /users. Semua ini dilakukan melalui routing.

Ini mungkin bekerja seperti ini:

Dan Mengapa Ini Penting?

Memiliki router terpusat yang baik sebenarnya dapat membuat hidup Anda jauh lebih mudah! Ini mungkin sulit dilihat pada awalnya. Berikut adalah beberapa alasan mengapa:

Saya yakin Anda sudah familiar dengan cara skrip demi skrip untuk membuat situs web. Anda mungkin memiliki file bernama index.php yang memiliki banyak pernyataan if untuk memeriksa URL dan kemudian menjalankan fungsi tertentu berdasarkan URL tersebut. Ini adalah bentuk routing, tetapi tidak sangat teratur dan dapat menjadi tidak terkendali dengan cepat. Sistem routing Flight adalah cara yang jauh lebih teratur dan kuat untuk menangani routing.

Ini?


// /user/view_profile.php?id=1234
if ($_GET['id']) {
    $id = $_GET['id'];
    viewUserProfile($id);
}

// /user/edit_profile.php?id=1234
if ($_GET['id']) {
    $id = $_GET['id'];
    editUserProfile($id);
}

// dll...

Atau ini?


// index.php
Flight::route('/user/@id', [ 'UserController', 'viewUserProfile' ]);
Flight::route('/user/@id/edit', [ 'UserController', 'editUserProfile' ]);

// Mungkin di dalam app/controllers/UserController.php Anda
class UserController {
    public function viewUserProfile($id) {
        // lakukan sesuatu
    }

    public function editUserProfile($id) {
        // lakukan sesuatu
    }
}

Semoga Anda mulai melihat manfaat menggunakan sistem routing terpusat. Ini jauh lebih mudah untuk dikelola dan dipahami dalam jangka panjang!

Permintaan dan Respons

Flight menyediakan cara yang sederhana dan mudah untuk menangani permintaan dan respons. Ini adalah inti dari apa yang dilakukan kerangka web. Ini menerima permintaan dari peramban pengguna, memprosesnya, dan kemudian mengirim kembali respons. Ini adalah cara Anda dapat membangun aplikasi web yang melakukan hal-hal seperti menampilkan profil pengguna, memungkinkan pengguna masuk, atau memungkinkan pengguna membuat posting blog baru.

Permintaan

Permintaan adalah apa yang dikirim peramban pengguna ke server Anda ketika mereka mengunjungi situs web Anda. Permintaan ini mengandung informasi tentang apa yang ingin dilakukan pengguna. Misalnya, mungkin berisi informasi tentang URL apa yang ingin dikunjungi pengguna, data apa yang ingin dikirim pengguna ke server Anda, atau jenis data apa yang ingin diterima pengguna dari server Anda. Penting untuk diketahui bahwa permintaan bersifat read-only. Anda tidak dapat mengubah permintaan, tetapi Anda dapat membacanya.

Flight menyediakan cara yang sederhana untuk mengakses informasi tentang permintaan tersebut. Anda dapat mengakses informasi tentang permintaan menggunakan metode Flight::request() . Metode ini mengembalikan objek Request yang berisi informasi tentang permintaan. Anda dapat menggunakan objek ini untuk mengakses informasi tentang permintaan, seperti URL, metode, atau data yang dikirim pengguna ke server Anda.

Respons

Respons adalah apa yang dikirim server Anda kembali ke peramban pengguna ketika mereka mengunjungi situs web Anda. Respons ini berisi informasi tentang apa yang ingin dilakukan server Anda. Misalnya, mungkin berisi informasi tentang jenis data apa yang ingin dikirim server Anda kepada pengguna, jenis data apa yang ingin diterima server Anda dari pengguna, atau jenis data apa yang ingin disimpan server Anda di komputer pengguna.

Flight menyediakan cara yang sederhana untuk mengirim respons ke peramban pengguna. Anda dapat mengirim respons menggunakan metode Flight::response(). Metode ini mengambil objek Response sebagai argumen dan mengirimkan respons ke peramban pengguna. Anda dapat menggunakan objek ini untuk mengirim respons kepada peramban pengguna, seperti HTML, JSON, atau file. Flight membantu Anda secara otomatis menghasilkan beberapa bagian dari respons untuk mempermudah, tetapi pada akhirnya Anda memiliki kendali atas apa yang Anda kirim kembali kepada pengguna.

Learn/responses

Respons

Flight membantu menghasilkan sebagian header respons untuk Anda, tetapi Anda memiliki sebagian besar kontrol atas apa yang Anda kirim kembali kepada pengguna. Terkadang Anda dapat mengakses objek Response secara langsung, tetapi sebagian besar waktu Anda akan menggunakan instance Flight untuk mengirim respons.

Mengirim Respons Dasar

Flight menggunakan ob_start() untuk menampung output. Ini berarti Anda dapat menggunakan echo atau print untuk mengirim respons kepada pengguna dan Flight akan menangkapnya dan mengirimkannya kembali kepada pengguna dengan header yang sesuai.


// Ini akan mengirim "Hello, World!" ke browser pengguna
Flight::route('/', function() {
    echo "Hello, World!";
});

// HTTP/1.1 200 OK
// Content-Type: text/html
//
// Hello, World!

Sebagai alternatif, Anda dapat memanggil metode write() untuk menambahkan ke tubuh juga.


// Ini akan mengirim "Hello, World!" ke browser pengguna
Flight::route('/', function() {
    // verbose, tetapi berhasil menyelesaikan pekerjaan kadang-kadang saat Anda membutuhkannya
    Flight::response()->write("Hello, World!");

    // jika Anda ingin mengambil tubuh yang telah Anda atur pada titik ini
    // Anda dapat melakukannya seperti ini
    $body = Flight::response()->getBody();
});

Kode Status

Anda dapat mengatur kode status respons dengan menggunakan metode status:

Flight::route('/@id', function($id) {
    if($id == 123) {
        Flight::response()->status(200);
        echo "Hello, World!";
    } else {
        Flight::response()->status(403);
        echo "Dilarang";
    }
});

Jika Anda ingin mendapatkan kode status saat ini, Anda dapat menggunakan metode status tanpa argumen:

Flight::response()->status(); // 200

Mengatur Tubuh Respons

Anda dapat mengatur tubuh respons dengan menggunakan metode write, namun, jika Anda echo atau print sesuatu, itu akan ditangkap dan dikirim sebagai tubuh respons melalui penampungan output.

Flight::route('/', function() {
    Flight::response()->write("Hello, World!");
});

// sama dengan

Flight::route('/', function() {
    echo "Hello, World!";
});

Menghapus Tubuh Respons

Jika Anda ingin menghapus tubuh respons, Anda dapat menggunakan metode clearBody:

Flight::route('/', function() {
    if($someCondition) {
        Flight::response()->write("Hello, World!");
    } else {
        Flight::response()->clearBody();
    }
});

Menjalankan Callback pada Tubuh Respons

Anda dapat menjalankan callback pada tubuh respons dengan menggunakan metode addResponseBodyCallback:

Flight::route('/users', function() {
    $db = Flight::db();
    $users = $db->fetchAll("SELECT * FROM users");
    Flight::render('users_table', ['users' => $users]);
});

// Ini akan gzip semua respons untuk rute mana pun
Flight::response()->addResponseBodyCallback(function($body) {
    return gzencode($body, 9);
});

Anda dapat menambahkan beberapa callback dan mereka akan dijalankan dalam urutan mereka ditambahkan. Karena ini dapat menerima callable, itu dapat menerima array kelas [ $class, 'method' ], closure $strReplace = function($body) { str_replace('hi', 'there', $body); };, atau nama fungsi 'minify' jika Anda memiliki fungsi untuk meminify kode html Anda misalnya.

Catatan: Callback rute tidak akan berfungsi jika Anda menggunakan opsi konfigurasi flight.v2.output_buffering.

Callback Rute Khusus

Jika Anda ingin ini hanya berlaku untuk rute tertentu, Anda dapat menambahkan callback di dalam rute itu sendiri:

Flight::route('/users', function() {
    $db = Flight::db();
    $users = $db->fetchAll("SELECT * FROM users");
    Flight::render('users_table', ['users' => $users]);

    // Ini akan gzip hanya respons untuk rute ini
    Flight::response()->addResponseBodyCallback(function($body) {
        return gzencode($body, 9);
    });
});

Opsi Middleware

Anda juga dapat menggunakan middleware untuk menerapkan callback ke semua rute melalui middleware:

// MinifyMiddleware.php
class MinifyMiddleware {
    public function before() {
        // Terapkan callback di sini pada objek response().
        Flight::response()->addResponseBodyCallback(function($body) {
            return $this->minify($body);
        });
    }

    protected function minify(string $body): string {
        // meminify tubuh entah bagaimana
        return $body;
    }
}

// index.php
Flight::group('/users', function() {
    Flight::route('', function() { /* ... */ });
    Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);

Mengatur Header Respons

Anda dapat mengatur header seperti jenis konten dari respons dengan menggunakan metode header:


// Ini akan mengirim "Hello, World!" ke browser pengguna dalam teks biasa
Flight::route('/', function() {
    Flight::response()->header('Content-Type', 'text/plain');
    // atau
    Flight::response()->setHeader('Content-Type', 'text/plain');
    echo "Hello, World!";
});

JSON

Flight menyediakan dukungan untuk mengirim respons JSON dan JSONP. Untuk mengirim respons JSON Anda mengirimkan beberapa data untuk di-JSON-kan:

Flight::json(['id' => 123]);

Catatan: Secara default, Flight akan mengirimkan header Content-Type: application/json bersamaan dengan respons. Ini juga akan menggunakan konstanta JSON_THROW_ON_ERROR dan JSON_UNESCAPED_SLASHES saat mengkodekan JSON.

JSON dengan Kode Status

Anda juga dapat memasukkan kode status sebagai argumen kedua:

Flight::json(['id' => 123], 201);

JSON dengan Pretty Print

Anda juga dapat memasukkan argumen di posisi terakhir untuk mengaktifkan pencetakan yang indah:

Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);

Jika Anda mengubah opsi yang diteruskan ke Flight::json() dan ingin sintaks yang lebih sederhana, Anda dapat mengganti metode JSON:

Flight::map('json', function($data, $code = 200, $options = 0) {
    Flight::_json($data, $code, true, 'utf-8', $options);
}

// Dan sekarang bisa digunakan seperti ini
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);

JSON dan Hentikan Eksekusi (v3.10.0)

Jika Anda ingin mengirim respons JSON dan menghentikan eksekusi, Anda dapat menggunakan metode jsonHalt. Ini berguna untuk kasus di mana Anda memeriksa mungkin beberapa jenis otorisasi dan jika pengguna tidak diotorisasi, Anda bisa segera mengirim respons JSON, membersihkan konten tubuh yang ada dan menghentikan eksekusi.

Flight::route('/users', function() {
    $authorized = someAuthorizationCheck();
    // Periksa apakah pengguna diotorisasi
    if($authorized === false) {
        Flight::jsonHalt(['error' => 'Unauthorized'], 401);
    }

    // Melanjutkan dengan sisa rute
});

Sebelum v3.10.0, Anda harus melakukan sesuatu seperti ini:

Flight::route('/users', function() {
    $authorized = someAuthorizationCheck();
    // Periksa apakah pengguna diotorisasi
    if($authorized === false) {
        Flight::halt(401, json_encode(['error' => 'Unauthorized']));
    }

    // Melanjutkan dengan sisa rute
});

JSONP

Untuk permintaan JSONP, Anda dapat secara opsional memasukkan nama parameter kueri yang Anda gunakan untuk mendefinisikan fungsi callback Anda:

Flight::jsonp(['id' => 123], 'q');

Jadi, ketika melakukan permintaan GET menggunakan ?q=my_func, Anda harus menerima output:

my_func({"id":123});

Jika Anda tidak memasukkan nama parameter kueri, itu akan default ke jsonp.

Mengalihkan ke URL lain

Anda dapat mengalihkan permintaan saat ini dengan menggunakan metode redirect() dan memasukkan URL baru:

Flight::redirect('/new/location');

Secara default, Flight mengirimkan status kode HTTP 303 ("Lihat Lain"). Anda juga dapat mengatur kode kustom:

Flight::redirect('/new/location', 401);

Menghentikan

Anda dapat menghentikan framework kapan saja dengan memanggil metode halt:

Flight::halt();

Anda juga dapat menentukan status kode dan pesan HTTP opsional:

Flight::halt(200, 'Be right back...');

Memanggil halt akan membuang konten respons apapun hingga saat itu. Jika Anda ingin menghentikan framework dan menampilkan respons saat ini, gunakan metode stop:

Flight::stop();

Menghapus Data Respons

Anda dapat menghapus tubuh dan header respons dengan menggunakan metode clear(). Ini akan menghapus header apapun yang ditetapkan pada respons, menghapus tubuh respons, dan mengatur kode status menjadi 200.

Flight::response()->clear();

Menghapus Hanya Tubuh Respons

Jika Anda hanya ingin menghapus tubuh respons, Anda dapat menggunakan metode clearBody():

// Ini masih akan mempertahankan header apapun yang diatur pada objek response().
Flight::response()->clearBody();

Caching HTTP

Flight menyediakan dukungan bawaan untuk caching level HTTP. Jika kondisi caching terpenuhi, Flight akan mengembalikan respons HTTP 304 Not Modified. Waktu berikutnya klien meminta sumber daya yang sama, mereka akan diminta untuk menggunakan versi lokal yang telah mereka cache.

Caching Level Rute

Jika Anda ingin menyimpan cache seluruh respons Anda, Anda dapat menggunakan metode cache() dan memasukkan waktu untuk disimpan dalam cache.


// Ini akan menyimpan cache respons selama 5 menit
Flight::route('/news', function () {
  Flight::response()->cache(time() + 300);
  echo 'Konten ini akan disimpan dalam cache.';
});

// Atau, Anda dapat menggunakan string yang akan Anda kirim
// ke metode strtotime()
Flight::route('/news', function () {
  Flight::response()->cache('+5 minutes');
  echo 'Konten ini akan disimpan dalam cache.';
});

Last-Modified

Anda dapat menggunakan metode lastModified dan memasukkan timestamp UNIX untuk mengatur tanggal dan waktu halaman terakhir dimodifikasi. Klien akan terus menggunakan cache mereka sampai nilai terakhir dimodifikasi diubah.

Flight::route('/news', function () {
  Flight::lastModified(1234567890);
  echo 'Konten ini akan disimpan dalam cache.';
});

ETag

Caching ETag mirip dengan Last-Modified, kecuali Anda dapat menentukan id apa pun yang Anda inginkan untuk sumber daya:

Flight::route('/news', function () {
  Flight::etag('my-unique-id');
  echo 'Konten ini akan disimpan dalam cache.';
});

Perlu diingat bahwa memanggil lastModified atau etag akan mengatur dan memeriksa nilai cache. Jika nilai cache sama antara permintaan, Flight akan segera mengirim respons HTTP 304 dan menghentikan pemrosesan.

Mengunduh File (v3.12.0)

Ada metode pembantu untuk mengunduh file. Anda dapat menggunakan metode download dan memasukkan path.

Flight::route('/download', function () {
  Flight::download('/path/to/file.txt');
});

Learn/events

Sistem Acara di Flight PHP (v3.15.0+)

Flight PHP memperkenalkan sistem acara yang ringan dan intuitif yang memungkinkan Anda mendaftar dan memicu acara kustom di aplikasi Anda. Dengan penambahan Flight::onEvent() dan Flight::triggerEvent(), Anda sekarang dapat terhubung ke momen-momen penting dalam siklus hidup aplikasi Anda atau mendefinisikan acara Anda sendiri untuk membuat kode Anda lebih modular dan dapat diperluas. Metode ini adalah bagian dari metode yang dapat dipetakan Flight, yang berarti Anda dapat mengganti perilaku mereka sesuai dengan kebutuhan Anda.

Panduan ini mencakup semuanya yang perlu Anda ketahui untuk memulai dengan acara, termasuk mengapa mereka berharga, bagaimana cara menggunakannya, dan contoh praktis untuk membantu pemula memahami kekuatannya.

Mengapa Menggunakan Acara?

Acara memungkinkan Anda untuk memisahkan bagian-bagian berbeda dari aplikasi Anda sehingga mereka tidak terlalu bergantung satu sama lain. Pemisahan ini—sering disebut decoupling—memudahkan kode Anda untuk diperbarui, diperluas, atau di-debug. Alih-alih menulis semuanya dalam satu blok besar, Anda dapat memecah logika Anda menjadi potongan-potongan kecil yang independen yang merespons tindakan spesifik (acara).

Bayangkan Anda sedang membangun aplikasi blog:

Tanpa acara, Anda akan memasukkan semua ini ke dalam satu fungsi. Dengan acara, Anda dapat memecahnya: satu bagian menyimpan komentar, bagian lain memicu acara seperti 'comment.posted', dan pendengar terpisah menangani email dan pencatatan. Ini menjaga kode Anda lebih bersih dan memungkinkan Anda menambah atau menghapus fitur (seperti notifikasi) tanpa menyentuh logika inti.

Penggunaan Umum

Mendaftar Pendengar Acara

Untuk mendengarkan sebuah acara, gunakan Flight::onEvent(). Metode ini memungkinkan Anda menentukan apa yang harus terjadi ketika sebuah acara terjadi.

Sintaks

Flight::onEvent(string $event, callable $callback): void

Cara Kerjanya

Anda "berlangganan" ke sebuah acara dengan memberi tahu Flight apa yang harus dilakukan ketika acara itu terjadi. Callback dapat menerima argumen yang diteruskan dari pemicu acara.

Sistem acara Flight bersifat sinkron, yang berarti setiap pendengar acara dieksekusi secara berurutan, satu setelah yang lain. Ketika Anda memicu sebuah acara, semua pendengar terdaftar untuk acara itu akan dijalankan hingga selesai sebelum kode Anda dilanjutkan. Ini penting untuk dipahami karena berbeda dari sistem acara asinkron di mana pendengar mungkin dijalankan secara paralel atau pada waktu yang lebih lambat.

Contoh Sederhana

Flight::onEvent('user.login', function ($username) {
    echo "Selamat datang kembali, $username!";
});

Di sini, ketika acara 'user.login' dipicu, ia akan menyapa pengguna dengan nama.

Poin Utama

Memicu Acara

Untuk membuat sebuah acara terjadi, gunakan Flight::triggerEvent(). Ini memberi tahu Flight untuk menjalankan semua pendengar yang terdaftar untuk acara tersebut, meneruskan data yang Anda berikan.

Sintaks

Flight::triggerEvent(string $event, ...$args): void

Contoh Sederhana

$username = 'alice';
Flight::triggerEvent('user.login', $username);

Ini memicu acara 'user.login' dan mengirimkan 'alice' kepada pendengar yang telah kita definisikan sebelumnya, yang akan menghasilkan: Selamat datang kembali, alice!.

Poin Utama

Mendaftar Pendengar Acara

...

Menghentikan Pendengar Selanjutnya: Jika sebuah pendengar mengembalikan false, pendengar tambahan untuk acara itu tidak akan dieksekusi. Ini memungkinkan Anda untuk menghentikan rantai acara berdasarkan kondisi tertentu. Ingat, urutan pendengar itu penting, karena pendengar pertama yang mengembalikan false akan menghentikan yang lainnya dari berjalan.

Contoh:

Flight::onEvent('user.login', function ($username) {
    if (isBanned($username)) {
        logoutUser($username);
        return false; // Menghentikan pendengar berikutnya
    }
});
Flight::onEvent('user.login', function ($username) {
    sendWelcomeEmail($username); // ini tidak pernah dikirim
});

Mengganti Metode Acara

Flight::onEvent() dan Flight::triggerEvent() tersedia untuk diperluas, yang berarti Anda dapat mendefinisikan ulang bagaimana mereka bekerja. Ini sangat baik untuk pengguna tingkat lanjut yang ingin menyesuaikan sistem acara, seperti menambahkan pencatatan atau mengubah cara acara dikirimkan.

Contoh: Menyesuaikan onEvent

Flight::map('onEvent', function (string $event, callable $callback) {
    // Mencatat setiap pendaftaran acara
    error_log("Pendengar acara baru ditambahkan untuk: $event");
    // Panggil perilaku default (mengasumsikan ada sistem acara internal)
    Flight::_onEvent($event, $callback);
});

Sekarang, setiap kali Anda mendaftarkan sebuah acara, ia akan mencatatnya sebelum melanjutkan.

Mengapa Mengganti?

Di Mana Menempatkan Acara Anda

Sebagai pemula, Anda mungkin bertanya: di mana saya mendaftarkan semua acara ini di aplikasi saya? Kesederhanaan Flight berarti tidak ada aturan ketat—Anda dapat menempatkannya di mana pun terasa tepat untuk proyek Anda. Namun, menjaga mereka terorganisir membantu Anda mempertahankan kode Anda seiring pertumbuhan aplikasi Anda. Berikut adalah beberapa opsi praktis dan praktik terbaik, disesuaikan dengan sifat ringan Flight:

Opsi 1: Di index.php Utama Anda

Untuk aplikasi kecil atau prototipe cepat, Anda dapat mendaftarkan acara langsung di file index.php Anda bersamaan dengan rute Anda. Ini menjaga semuanya di satu tempat, yang baik ketika kesederhanaan adalah prioritas Anda.

require 'vendor/autoload.php';

// Mendaftarkan acara
Flight::onEvent('user.login', function ($username) {
    error_log("$username login pada " . date('Y-m-d H:i:s'));
});

// Mendefinisikan rute
Flight::route('/login', function () {
    $username = 'bob';
    Flight::triggerEvent('user.login', $username);
    echo "Telah login!";
});

Flight::start();

Opsi 2: File events.php Terpisah

Untuk aplikasi yang sedikit lebih besar, pertimbangkan untuk memindahkan pendaftaran acara ke dalam file khusus seperti app/config/events.php. Sertakan file ini di index.php Anda sebelum rute Anda. Ini meniru cara rute sering diorganisir dalam app/config/routes.php di proyek Flight.

// app/config/events.php
Flight::onEvent('user.login', function ($username) {
    error_log("$username login pada " . date('Y-m-d H:i:s'));
});

Flight::onEvent('user.registered', function ($email, $name) {
    echo "Email dikirim ke $email: Selamat datang, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';

Flight::route('/login', function () {
    $username = 'bob';
    Flight::triggerEvent('user.login', $username);
    echo "Telah login!";
});

Flight::start();

Opsi 3: Dekat Tempat Acara Dipicu

Pendekatan lain adalah mendaftarkan acara dekat tempat mereka dipicu, seperti di dalam kontroler atau definisi rute. Ini bekerja dengan baik jika sebuah acara khusus untuk satu bagian aplikasi Anda.

Flight::route('/signup', function () {
    // Mendaftarkan acara di sini
    Flight::onEvent('user.registered', function ($email) {
        echo "Email selamat datang dikirim ke $email!";
    });

    $email = 'jane@example.com';
    Flight::triggerEvent('user.registered', $email);
    echo "Telah mendaftar!";
});

Praktik Terbaik untuk Flight

Tip: Kelompokkan Berdasarkan Tujuan

Di events.php, kelompokkan acara terkait (misalnya, semua acara terkait pengguna bersama) dengan komentar untuk kejelasan:

// app/config/events.php
// Acara Pengguna
Flight::onEvent('user.login', function ($username) {
    error_log("$username login");
});
Flight::onEvent('user.registered', function ($email) {
    echo "Selamat datang di $email!";
});

// Acara Halaman
Flight::onEvent('page.updated', function ($pageId) {
    unset($_SESSION['pages'][$pageId]);
});

Struktur ini berkembang dengan baik dan tetap ramah pemula.

Contoh untuk Pemula

Mari kita lihat beberapa skenario dunia nyata untuk menunjukkan bagaimana acara bekerja dan mengapa mereka berguna.

Contoh 1: Mencatat Login Pengguna

// Langkah 1: Mendaftarkan pendengar
Flight::onEvent('user.login', function ($username) {
    $time = date('Y-m-d H:i:s');
    error_log("$username login pada $time");
});

// Langkah 2: Memicu di aplikasi Anda
Flight::route('/login', function () {
    $username = 'bob'; // Asumsikan ini berasal dari formulir
    Flight::triggerEvent('user.login', $username);
    echo "Hai, $username!";
});

Mengapa Ini Berguna: Kode login tidak perlu tahu tentang pencatatan—hanya memicu acara. Anda bisa menambahkan lebih banyak pendengar (misalnya, mengirim email selamat datang) tanpa mengubah rute.

Contoh 2: Memberi Tahu tentang Pengguna Baru

// Pendengar untuk pendaftaran baru
Flight::onEvent('user.registered', function ($email, $name) {
    // Mensimulasikan mengirim email
    echo "Email dikirim ke $email: Selamat datang, $name!";
});

// Memicu ketika seseorang mendaftar
Flight::route('/signup', function () {
    $email = 'jane@example.com';
    $name = 'Jane';
    Flight::triggerEvent('user.registered', $email, $name);
    echo "Terima kasih telah mendaftar!";
});

Mengapa Ini Berguna: Logika pendaftaran fokus pada pembuatan pengguna, sementara acara menangani notifikasi. Anda dapat menambahkan lebih banyak pendengar (misalnya, mencatat pendaftaran) nanti.

Contoh 3: Menghapus Cache

// Pendengar untuk menghapus cache
Flight::onEvent('page.updated', function ($pageId) {
    unset($_SESSION['pages'][$pageId]); // Menghapus cache sesi jika berlaku
    echo "Cache dihapus untuk halaman $pageId.";
});

// Memicu saat halaman diedit
Flight::route('/edit-page/(@id)', function ($pageId) {
    // Asumsikan kami memperbarui halaman
    Flight::triggerEvent('page.updated', $pageId);
    echo "Halaman $pageId diperbarui.";
});

Mengapa Ini Berguna: Kode pengeditan tidak peduli tentang caching—hanya memberi sinyal pembaruan. Bagian lain dari aplikasi dapat bereaksi sesuai kebutuhan.

Praktik Terbaik

Sistem acara di Flight PHP, dengan Flight::onEvent() dan Flight::triggerEvent(), memberi Anda cara yang sederhana namun kuat untuk membangun aplikasi yang fleksibel. Dengan membiarkan bagian-bagian berbeda dari aplikasi Anda berkomunikasi melalui acara, Anda dapat menjaga kode Anda terorganisir, dapat digunakan kembali, dan mudah untuk diperluas. Apakah Anda sedang mencatat tindakan, mengirim notifikasi, atau mengelola pembaruan, acara membantu Anda melakukannya tanpa merumitkan logika Anda. Ditambah dengan kemampuan untuk menimpa metode-metode ini, Anda memiliki kebebasan untuk menyesuaikan sistem dengan kebutuhan Anda. Mulailah kecil dengan satu acara, dan saksikan bagaimana hal itu mengubah struktur aplikasi Anda!

Acara Bawaan

Flight PHP dilengkapi dengan beberapa acara bawaan yang dapat Anda gunakan untuk terhubung ke siklus hidup framework. Acara ini dipicu pada titik tertentu dalam siklus permintaan/respons, memungkinkan Anda untuk mengeksekusi logika kustom saat tindakan tertentu terjadi.

Daftar Acara Bawaan

Learn/templates

Tampilan HTML dan Template

Flight menyediakan beberapa fungsionalitas templating dasar secara default.

Flight memungkinkan Anda untuk mengganti mesin tampilan default hanya dengan mendaftarkan kelas tampilan Anda sendiri. Gulir ke bawah untuk melihat contoh cara menggunakan Smarty, Latte, Blade, dan lainnya!

Mesin Tampilan Bawaan

Untuk menampilkan sebuah template tampilan, panggil metode render dengan nama file template dan data template opsional:

Flight::render('hello.php', ['name' => 'Bob']);

Data template yang Anda masukkan secara otomatis disuntikkan ke dalam template dan dapat dirujuk seperti variabel lokal. File template hanyalah file PHP. Jika konten dari file template hello.php adalah:

Hello, <?= $name ?>!

Outputnya adalah:

Hello, Bob!

Anda juga dapat secara manual mengatur variabel tampilan dengan menggunakan metode set:

Flight::view()->set('name', 'Bob');

Variabel name kini tersedia di semua tampilan Anda. Jadi Anda dapat dengan mudah melakukan:

Flight::render('hello');

Perhatikan bahwa saat menentukan nama template dalam metode render, Anda dapat menghilangkan ekstensi .php.

Secara default, Flight akan mencari direktori views untuk file template. Anda dapat menetapkan jalur alternatif untuk template Anda dengan mengatur konfigurasi berikut:

Flight::set('flight.views.path', '/path/to/views');

Tata Letak

Adalah hal yang umum untuk situs web memiliki satu file template tata letak dengan konten yang saling bertukar. Untuk merender konten yang digunakan dalam tata letak, Anda dapat memasukkan parameter opsional ke dalam metode render.

Flight::render('header', ['heading' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], 'bodyContent');

Tampilan Anda kemudian akan memiliki variabel yang disimpan yang disebut headerContent dan bodyContent. Anda kemudian dapat merender tata letak Anda dengan melakukan:

Flight::render('layout', ['title' => 'Home Page']);

Jika file template terlihat seperti ini:

header.php:

<h1><?= $heading ?></h1>

body.php:

<div><?= $body ?></div>

layout.php:

<html>
  <head>
    <title><?= $title ?></title>
  </head>
  <body>
    <?= $headerContent ?>
    <?= $bodyContent ?>
  </body>
</html>

Outputnya adalah:

<html>
  <head>
    <title>Home Page</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div>World</div>
  </body>
</html>

Smarty

Berikut cara menggunakan mesin template Smarty untuk tampilan Anda:

// Muat pustaka Smarty
require './Smarty/libs/Smarty.class.php';

// Daftarkan Smarty sebagai kelas tampilan
// Juga lewati fungsi callback untuk mengonfigurasi Smarty saat dimuat
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
  $smarty->setTemplateDir('./templates/');
  $smarty->setCompileDir('./templates_c/');
  $smarty->setConfigDir('./config/');
  $smarty->setCacheDir('./cache/');
});

// Tetapkan data template
Flight::view()->assign('name', 'Bob');

// Tampilkan template
Flight::view()->display('hello.tpl');

Untuk kelengkapan, Anda juga harus menimpa metode render default Flight:

Flight::map('render', function(string $template, array $data): void {
  Flight::view()->assign($data);
  Flight::view()->display($template);
});

Latte

Berikut cara menggunakan mesin template Latte untuk tampilan Anda:

// Daftarkan Latte sebagai kelas tampilan
// Juga lewati fungsi callback untuk mengonfigurasi Latte saat dimuat
Flight::register('view', Latte\Engine::class, [], function (Latte\Engine $latte) {
  // Di sinilah Latte akan menyimpan cache template Anda untuk mempercepat segalanya
    // Satu hal menarik tentang Latte adalah bahwa ia secara otomatis menyegarkan
    // cache saat Anda melakukan perubahan pada template Anda!
    $latte->setTempDirectory(__DIR__ . '/../cache/');

    // Beri tahu Latte di mana direktori akar untuk tampilan Anda akan berada.
    $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../views/'));
});

// Dan akhiri sehingga Anda dapat menggunakan Flight::render() dengan benar
Flight::map('render', function(string $template, array $data): void {
  // Ini seperti $latte_engine->render($template, $data);
  echo Flight::view()->render($template, $data);
});

Blade

Berikut cara menggunakan mesin template Blade untuk tampilan Anda:

Pertama, Anda perlu menginstal pustaka BladeOne melalui Composer:

composer require eftec/bladeone

Kemudian, Anda dapat mengonfigurasi BladeOne sebagai kelas tampilan di Flight:

<?php
// Muat pustaka BladeOne
use eftec\bladeone\BladeOne;

// Daftarkan BladeOne sebagai kelas tampilan
// Juga lewati fungsi callback untuk mengonfigurasi BladeOne saat dimuat
Flight::register('view', BladeOne::class, [], function (BladeOne $blade) {
  $views = __DIR__ . '/../views';
  $cache = __DIR__ . '/../cache';

  $blade->setPath($views);
  $blade->setCompiledPath($cache);
});

// Tetapkan data template
Flight::view()->share('name', 'Bob');

// Tampilkan template
echo Flight::view()->run('hello', []);

Untuk kelengkapan, Anda juga harus menimpa metode render default Flight:

<?php
Flight::map('render', function(string $template, array $data): void {
  echo Flight::view()->run($template, $data);
});

Dalam contoh ini, file template hello.blade.php mungkin terlihat seperti ini:

<?php
Hello, {{ $name }}!

Outputnya adalah:

Hello, Bob!

Dengan mengikuti langkah-langkah ini, Anda dapat mengintegrasikan mesin template Blade dengan Flight dan menggunakannya untuk merender tampilan Anda.

Learn/flight_vs_fat_free

Flight vs Fat-Free

Apa itu Fat-Free?

Fat-Free (dikenal dengan penuh kasih sebagai F3) adalah sebuah mikro-framework PHP yang kuat namun mudah digunakan, dirancang untuk membantu Anda membangun aplikasi web yang dinamis dan tangguh - dengan cepat!

Flight dibandingkan dengan Fat-Free dalam banyak hal dan mungkin merupakan kerabat terdekat dalam hal fitur dan kesederhanaan. Fat-Free memiliki banyak fitur yang tidak dimiliki oleh Flight, tetapi juga memiliki banyak fitur yang dimiliki oleh Flight. Fat-Free mulai menunjukkan usianya dan tidak sepopuler dulu.

Pembaruan menjadi semakin jarang dan komunitas tidak seaktif dulu. Kode ini cukup sederhana, tetapi terkadang kurangnya disiplin sintaks dapat membuatnya sulit untuk dibaca dan dipahami. Ini berfungsi untuk PHP 8.3, tetapi kode itu sendiri masih terlihat seolah-olah berada di PHP 5.3.

Kelebihan dibandingkan Flight

Kekurangan dibandingkan Flight

Learn/extending

Memperluas

Flight dirancang untuk menjadi kerangka kerja yang dapat diperluas. Kerangka kerja ini dilengkapi dengan serangkaian metode dan komponen default, tetapi memungkinkan Anda untuk memetakan metode Anda sendiri, mendaftarkan kelas Anda sendiri, atau bahkan mengganti kelas dan metode yang sudah ada.

Jika Anda mencari DIC (Dependency Injection Container), silakan kunjungi halaman Dependency Injection Container.

Memetakan Metode

Untuk memetakan metode kustom sederhana Anda sendiri, Anda menggunakan fungsi map:

// Pemetakan metode Anda
Flight::map('hello', function (string $name) {
  echo "halo $name!";
});

// Panggil metode kustom Anda
Flight::hello('Bob');

Meskipun mungkin untuk membuat metode kustom sederhana, disarankan untuk hanya membuat fungsi standar di PHP. Ini memiliki autocompletion di IDE dan lebih mudah dibaca. Padanan dari kode di atas adalah:

function hello(string $name) {
  echo "halo $name!";
}

hello('Bob');

Ini lebih sering digunakan ketika Anda perlu mengoper variabel ke metode Anda untuk mendapatkan nilai yang diharapkan. Menggunakan metode register() seperti di bawah ini lebih untuk mengoper konfigurasi dan kemudian memanggil kelas yang sudah dikonfigurasi sebelumnya.

Mendaftarkan Kelas

Untuk mendaftarkan kelas Anda sendiri dan mengkonfigurasinya, Anda menggunakan fungsi register:

// Daftarkan kelas Anda
Flight::register('user', User::class);

// Ambil instance dari kelas Anda
$user = Flight::user();

Metode register juga memungkinkan Anda untuk mengoper parameter ke konstruktor kelas Anda. Jadi, ketika Anda memuat kelas kustom Anda, itu akan datang dengan inisialisasi awal. Anda dapat menentukan parameter konstruktor dengan mengoper array tambahan. Berikut adalah contoh memuat koneksi database:

// Daftarkan kelas dengan parameter konstruktor
Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'user', 'pass']);

// Ambil instance dari kelas Anda
// Ini akan membuat objek dengan parameter yang ditentukan
//
// new PDO('mysql:host=localhost;dbname=test','user','pass');
//
$db = Flight::db();

// dan jika Anda membutuhkannya nanti dalam kode Anda, Anda cukup memanggil metode yang sama lagi
class SomeController {
  public function __construct() {
    $this->db = Flight::db();
  }
}

Jika Anda mengoper parameter callback tambahan, itu akan dieksekusi segera setelah konstruktor kelas. Ini memungkinkan Anda untuk melakukan prosedur penyiapan untuk objek baru Anda. Fungsi callback mengambil satu parameter, sebuah instance dari objek baru.

// Callback akan menerima objek yang telah dibangun
Flight::register(
  'db',
  PDO::class,
  ['mysql:host=localhost;dbname=test', 'user', 'pass'],
  function (PDO $db) {
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
);

Secara default, setiap kali Anda memuat kelas Anda, Anda akan mendapatkan instance yang dibagikan. Untuk mendapatkan instance baru dari kelas, cukup oper false sebagai parameter:

// Instance bersama dari kelas
$shared = Flight::db();

// Instance baru dari kelas
$new = Flight::db(false);

Ingat bahwa metode yang dipetakan memiliki prioritas atas kelas yang terdaftar. Jika Anda mendeklarasikan keduanya menggunakan nama yang sama, hanya metode yang dipetakan yang akan dipanggil.

Logging

Flight tidak memiliki sistem logging bawaan, namun, sangat mudah untuk menggunakan pustaka logging dengan Flight. Berikut adalah contoh menggunakan pustaka Monolog:

// index.php atau bootstrap.php

// Daftarkan logger dengan Flight
Flight::register('log', Monolog\Logger::class, [ 'name' ], function(Monolog\Logger $log) {
    $log->pushHandler(new Monolog\Handler\StreamHandler('path/to/your.log', Monolog\Logger::WARNING));
});

Sekarang setelah terdaftar, Anda dapat menggunakannya di aplikasi Anda:

// Di controller atau route Anda
Flight::log()->warning('Ini adalah pesan peringatan');

Ini akan mencatat pesan ke file log yang Anda tentukan. Bagaimana jika Anda ingin mencatat sesuatu saat terjadi kesalahan? Anda dapat menggunakan metode error:

// Di controller atau route Anda

Flight::map('error', function(Throwable $ex) {
    Flight::log()->error($ex->getMessage());
    // Tampilkan halaman kesalahan kustom Anda
    include 'errors/500.html';
});

Anda juga bisa membuat sistem APM (Application Performance Monitoring) dasar menggunakan metode before dan after:

// Di file bootstrap Anda

Flight::before('start', function() {
    Flight::set('start_time', microtime(true));
});

Flight::after('start', function() {
    $end = microtime(true);
    $start = Flight::get('start_time');
    Flight::log()->info('Permintaan '.Flight::request()->url.' memakan waktu ' . round($end - $start, 4) . ' detik');

    // Anda juga bisa menambahkan header permintaan atau respons Anda
    // untuk mencatatnya juga (hati-hati karena ini akan jadi
    // banyak data jika Anda memiliki banyak permintaan)
    Flight::log()->info('Header Permintaan: ' . json_encode(Flight::request()->headers));
    Flight::log()->info('Header Respons: ' . json_encode(Flight::response()->headers));
});

Mengganti Metode Framework

Flight memungkinkan Anda untuk mengganti fungsionalitas bawaannya agar sesuai dengan kebutuhan Anda, tanpa harus memodifikasi kode apa pun. Anda dapat melihat semua metode yang dapat Anda ganti di sini.

Sebagai contoh, ketika Flight tidak dapat mencocokkan URL dengan rute, ia memanggil metode notFound yang mengirimkan tanggapan HTTP 404 umum. Anda dapat mengganti perilaku ini dengan menggunakan metode map:

Flight::map('notFound', function() {
  // Tampilkan halaman kustom 404
  include 'errors/404.html';
});

Flight juga memungkinkan Anda untuk mengganti komponen inti dari kerangka kerja. Sebagai contoh, Anda dapat mengganti kelas Router default dengan kelas kustom Anda sendiri:

// Daftarkan kelas kustom Anda
Flight::register('router', MyRouter::class);

// Ketika Flight memuat instance Router, itu akan memuat kelas Anda
$myrouter = Flight::router();

Namun, metode kerangka kerja seperti map dan register tidak dapat diganti. Anda akan mendapatkan kesalahan jika Anda mencoba melakukannya.

Learn/flight_vs_slim

Flight vs Slim

Apa itu Slim?

Slim adalah kerangka mikro PHP yang membantu Anda dengan cepat menulis aplikasi web dan API yang sederhana namun kuat.

Banyak inspirasi untuk beberapa fitur v3 dari Flight sebenarnya datang dari Slim. Pengelompokan rute, dan menjalankan middleware dalam urutan tertentu adalah dua fitur yang terinspirasi oleh Slim. Slim v3 diluncurkan dengan mengutamakan kesederhanaan, tetapi terdapat ulasan campuran mengenai v4.

Kelebihan dibandingkan Flight

Kekurangan dibandingkan Flight

Learn/autoloading

Autoloading

Autoloading adalah konsep dalam PHP di mana Anda menentukan satu direktori atau beberapa direktori untuk memuat kelas. Ini jauh lebih menguntungkan daripada menggunakan require atau include untuk memuat kelas. Ini juga merupakan persyaratan untuk menggunakan paket Composer.

Secara default, setiap kelas Flight dimuat otomatis untuk Anda berkat composer. Namun, jika Anda ingin memuat otomatis kelas Anda sendiri, Anda dapat menggunakan metode Flight::path() untuk menentukan direktori yang akan memuat kelas.

Contoh Dasar

Mari kita anggap kita memiliki pohon direktori seperti berikut:

# Contoh path
/home/user/project/my-flight-project/
├── app
│   ├── cache
│   ├── config
│   ├── controllers - berisi kontroler untuk proyek ini
│   ├── translations
│   ├── UTILS - berisi kelas untuk aplikasi ini saja (ini huruf kapital semua dengan tujuan sebagai contoh nanti)
│   └── views
└── public
    └── css
    └── js
    └── index.php

Anda mungkin telah memperhatikan bahwa ini adalah struktur file yang sama dengan situs dokumentasi ini.

Anda dapat menentukan setiap direktori untuk dimuat dari seperti ini:


/**
 * public/index.php
 */

// Tambahkan path ke autoloader
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');

/**
 * app/controllers/MyController.php
 */

// tidak ada namespace yang diperlukan

// Semua kelas yang dimuat otomatis disarankan untuk menggunakan Pascal Case (setiap kata dengan huruf kapital, tanpa spasi)
// Mulai dari 3.7.2, Anda dapat menggunakan Pascal_Snake_Case untuk nama kelas Anda dengan menjalankan Loader::setV2ClassLoading(false);
class MyController {

    public function index() {
        // lakukan sesuatu
    }
}

Namespace

Jika Anda memiliki namespace, sebenarnya menjadi sangat mudah untuk menerapkan ini. Anda harus menggunakan metode Flight::path() untuk menentukan direktori root (bukan document root atau folder public/) aplikasi Anda.


/**
 * public/index.php
 */

// Tambahkan path ke autoloader
Flight::path(__DIR__.'/../');

Sekarang inilah yang mungkin terlihat seperti kontroler Anda. Lihat contoh di bawah ini, tetapi perhatikan komentar untuk informasi penting.

/**
 * app/controllers/MyController.php
 */

// namespace diperlukan
// namespace sama dengan struktur direktori
// namespace harus mengikuti huruf besar yang sama dengan struktur direktori
// namespace dan direktori tidak dapat memiliki garis bawah (kecuali Loader::setV2ClassLoading(false) diset)
namespace app\controllers;

// Semua kelas yang dimuat otomatis disarankan untuk menggunakan Pascal Case (setiap kata dengan huruf kapital, tanpa spasi)
// Mulai dari 3.7.2, Anda dapat menggunakan Pascal_Snake_Case untuk nama kelas Anda dengan menjalankan Loader::setV2ClassLoading(false);
class MyController {

    public function index() {
        // lakukan sesuatu
    }
}

Dan jika Anda ingin memuat otomatis kelas di direktori utilitas Anda, Anda akan melakukan hal yang hampir sama:


/**
 * app/UTILS/ArrayHelperUtil.php
 */

// namespace harus cocok dengan struktur direktori dan huruf besar (perhatikan direktori UTILS semua huruf kapital
//     seperti pada pohon file di atas)
namespace app\UTILS;

class ArrayHelperUtil {

    public function changeArrayCase(array $array) {
        // lakukan sesuatu
    }
}

Garis Bawah dalam Nama Kelas

Mulai dari 3.7.2, Anda dapat menggunakan Pascal_Snake_Case untuk nama kelas Anda dengan menjalankan Loader::setV2ClassLoading(false);. Ini akan memungkinkan Anda untuk menggunakan garis bawah dalam nama kelas Anda. Ini tidak disarankan, tetapi tersedia untuk mereka yang membutuhkannya.


/**
 * public/index.php
 */

// Tambahkan path ke autoloader
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
Loader::setV2ClassLoading(false);

/**
 * app/controllers/My_Controller.php
 */

// tidak ada namespace yang diperlukan

class My_Controller {

    public function index() {
        // lakukan sesuatu
    }
}

Learn/troubleshooting

Pemecahan Masalah

Halaman ini akan membantu Anda memecahkan masalah umum yang mungkin Anda temui saat menggunakan Flight.

Masalah Umum

404 Tidak Ditemukan atau Perilaku Rute Tak Terduga

Jika Anda melihat kesalahan 404 Tidak Ditemukan (tetapi Anda bersumpah bahwa itu benar-benar ada dan bukan kesalahan ketik) ini sebenarnya bisa menjadi masalah dengan Anda yang mengembalikan nilai di titik akhir rute Anda alih-alih hanya mencetaknya. Alasan untuk ini adalah disengaja tetapi bisa mengganggu beberapa pengembang.


Flight::route('/hello', function(){
    // Ini mungkin menyebabkan kesalahan 404 Tidak Ditemukan
    return 'Hello World';
});

// Apa yang mungkin Anda inginkan
Flight::route('/hello', function(){
    echo 'Hello World';
});

Alasan untuk ini adalah karena mekanisme khusus yang dibangun ke dalam router yang menangani keluaran pengembalian sebagai sinyal untuk "melanjutkan ke rute berikutnya". Anda dapat melihat perilaku ini didokumentasikan di bagian Routing.

Kelas Tidak Ditemukan (autoloading tidak bekerja)

Ada beberapa alasan mengapa ini tidak terjadi. Di bawah ini adalah beberapa contoh tetapi pastikan Anda juga memeriksa bagian autoloading.

Nama File yang Salah

Yang paling umum adalah bahwa nama kelas tidak cocok dengan nama file.

Jika Anda memiliki kelas bernama MyClass maka file tersebut harus dinamai MyClass.php. Jika Anda memiliki kelas bernama MyClass dan file tersebut dinamai myclass.php maka autoloader tidak akan dapat menemukannya.

Namespace yang Salah

Jika Anda menggunakan namespace, maka namespace harus cocok dengan struktur direktori.

// kode

// jika MyController Anda berada di direktori app/controllers dan memiliki namespace
// ini tidak akan berfungsi.
Flight::route('/hello', 'MyController->hello');

// Anda perlu memilih salah satu dari opsi ini
Flight::route('/hello', 'app\controllers\MyController->hello');
// atau jika Anda memiliki pernyataan use di atas

use app\controllers\MyController;

Flight::route('/hello', [ MyController::class, 'hello' ]);
// juga bisa ditulis
Flight::route('/hello', MyController::class.'->hello');
// juga...
Flight::route('/hello', [ 'app\controllers\MyController', 'hello' ]);

path() tidak didefinisikan

Dalam aplikasi kerangka kerja, ini didefinisikan di dalam file config.php, tetapi agar kelas Anda dapat ditemukan, Anda perlu memastikan bahwa metode path() didefinisikan (kemungkinan ke root direktori Anda) sebelum Anda mencoba menggunakannya.


// Tambahkan path ke autoloader
Flight::path(__DIR__.'/../');

Guides/blog

Membangun Blog Sederhana dengan Flight PHP

Panduan ini memandu Anda melalui pembuatan blog dasar menggunakan framework Flight PHP. Anda akan mengatur proyek, mendefinisikan rute, mengelola pos dengan JSON, dan merendernya dengan mesin templating Latte—semuanya menunjukkan kesederhanaan dan fleksibilitas Flight. Pada akhir panduan, Anda akan memiliki blog fungsional dengan halaman utama, halaman pos individu, dan formulir pembuatan.

Prasyarat

Langkah 1: Siapkan Proyek Anda

Mulailah dengan membuat direktori proyek baru dan menginstal Flight melalui Composer.

  1. Buat Direktori:

    mkdir flight-blog
    cd flight-blog
  2. Instal Flight:

    composer require flightphp/core
  3. Buat Direktori Publik: Flight menggunakan titik masuk tunggal (index.php). Buat folder public/ untuk itu:

    mkdir public
  4. index.php Dasar: Buat public/index.php dengan rute "hello world" yang sederhana:

    <?php
    require '../vendor/autoload.php';
    
    Flight::route('/', function () {
       echo 'Halo, Flight!';
    });
    
    Flight::start();
  5. Jalankan Server Bawaan: Uji pengaturan Anda dengan server pengembangan PHP:

    php -S localhost:8000 -t public/

    Kunjungi http://localhost:8000 untuk melihat "Halo, Flight!".

Langkah 2: Atur Struktur Proyek Anda

Untuk pengaturan yang bersih, struktur proyek Anda seperti ini:

flight-blog/
├── app/
│   ├── config/
│   └── views/
├── data/
├── public/
│   └── index.php
├── vendor/
└── composer.json

Langkah 3: Instal dan Konfigurasi Latte

Latte adalah mesin templating ringan yang terintegrasi dengan baik dengan Flight.

  1. Instal Latte:

    composer require latte/latte
  2. Konfigurasikan Latte di Flight: Perbarui public/index.php untuk mendaftarkan Latte sebagai mesin tampilan:

    <?php
    require '../vendor/autoload.php';
    
    use Latte\Engine;
    
    Flight::register('view', Engine::class, [], function ($latte) {
       $latte->setTempDirectory(__DIR__ . '/../cache/');
       $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/'));
    });
    
    Flight::route('/', function () {
       Flight::view()->render('home.latte', ['title' => 'Blog Saya']);
    });
    
    Flight::start();
  3. Buat Template Layout: Di app/views/layout.latte:

    <!DOCTYPE html>
    <html>
    <head>
    <title>{$title}</title>
    </head>
    <body>
    <header>
        <h1>Blog Saya</h1>
        <nav>
            <a href="/">Beranda</a> | 
            <a href="/create">Buat Pos</a>
        </nav>
    </header>
    <main>
        {block content}{/block}
    </main>
    <footer>
        <p>&copy; {date('Y')} Blog Flight</p>
    </footer>
    </body>
    </html>
  4. Buat Template Beranda: Di app/views/home.latte:

    {extends 'layout.latte'}
    
    {block content}
        <h2>{$title}</h2>
        <ul>
        {foreach $posts as $post}
            <li><a href="/post/{$post['slug']}">{$post['title']}</a></li>
        {/foreach}
        </ul>
    {/block}

    Mulai ulang server jika Anda keluar dan kunjungi http://localhost:8000 untuk melihat halaman yang dirender.

  5. Buat File Data:

    Gunakan file JSON untuk mensimulasikan database untuk kesederhanaan.

    Di data/posts.json:

    [
       {
           "slug": "first-post",
           "title": "Pos Pertama Saya",
           "content": "Ini adalah pos blog pertama saya dengan Flight PHP!"
       }
    ]

Langkah 4: Definisikan Rute

Pisahkan rute Anda ke dalam file konfigurasi untuk organisasi yang lebih baik.

  1. Buat routes.php: Di app/config/routes.php:

    <?php
    Flight::route('/', function () {
       Flight::view()->render('home.latte', ['title' => 'Blog Saya']);
    });
    
    Flight::route('/post/@slug', function ($slug) {
       Flight::view()->render('post.latte', ['title' => 'Pos: ' . $slug, 'slug' => $slug]);
    });
    
    Flight::route('GET /create', function () {
       Flight::view()->render('create.latte', ['title' => 'Buat Pos']);
    });
  2. Perbarui index.php: Termasuk file rute:

    <?php
    require '../vendor/autoload.php';
    
    use Latte\Engine;
    
    Flight::register('view', Engine::class, [], function ($latte) {
       $latte->setTempDirectory(__DIR__ . '/../cache/');
       $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/'));
    });
    
    require '../app/config/routes.php';
    
    Flight::start();

Langkah 5: Simpan dan Ambil Pos Blog

Tambahkan metode untuk memuat dan menyimpan pos.

  1. Tambahkan Metode Pos: Di index.php, tambahkan metode untuk memuat pos:

    Flight::map('posts', function () {
       $file = __DIR__ . '/../data/posts.json';
       return json_decode(file_get_contents($file), true);
    });
  2. Perbarui Rute: Modifikasi app/config/routes.php untuk menggunakan pos:

    <?php
    Flight::route('/', function () {
       $posts = Flight::posts();
       Flight::view()->render('home.latte', [
           'title' => 'Blog Saya',
           'posts' => $posts
       ]);
    });
    
    Flight::route('/post/@slug', function ($slug) {
       $posts = Flight::posts();
       $post = array_filter($posts, fn($p) => $p['slug'] === $slug);
       $post = reset($post) ?: null;
       if (!$post) {
           Flight::notFound();
           return;
       }
       Flight::view()->render('post.latte', [
           'title' => $post['title'],
           'post' => $post
       ]);
    });
    
    Flight::route('GET /create', function () {
       Flight::view()->render('create.latte', ['title' => 'Buat Pos']);
    });

Langkah 6: Buat Template

Perbarui template Anda untuk menampilkan pos.

  1. Halaman Pos (app/views/post.latte):

    {extends 'layout.latte'}
    
    {block content}
        <h2>{$post['title']}</h2>
        <div class="post-content">
            <p>{$post['content']}</p>
        </div>
    {/block}

Langkah 7: Tambahkan Pembuatan Pos

Tangani pengiriman formulir untuk menambahkan pos baru.

  1. Formulir Buat (app/views/create.latte):

    {extends 'layout.latte'}
    
    {block content}
        <h2>{$title}</h2>
        <form method="POST" action="/create">
            <div class="form-group">
                <label for="title">Judul:</label>
                <input type="text" name="title" id="title" required>
            </div>
            <div class="form-group">
                <label for="content">Konten:</label>
                <textarea name="content" id="content" required></textarea>
            </div>
            <button type="submit">Simpan Pos</button>
        </form>
    {/block}
  2. Tambahkan Rute POST: Di app/config/routes.php:

    Flight::route('POST /create', function () {
       $request = Flight::request();
       $title = $request->data['title'];
       $content = $request->data['content'];
       $slug = strtolower(str_replace(' ', '-', $title));
    
       $posts = Flight::posts();
       $posts[] = ['slug' => $slug, 'title' => $title, 'content' => $content];
       file_put_contents(__DIR__ . '/../../data/posts.json', json_encode($posts, JSON_PRETTY_PRINT));
    
       Flight::redirect('/');
    });
  3. Uji Coba:

    • Kunjungi http://localhost:8000/create.
    • Kirim pos baru (misalnya, “Pos Kedua” dengan beberapa konten).
    • Periksa halaman utama untuk melihatnya terdaftar.

Langkah 8: Perbaiki dengan Penanganan Kesalahan

Timpakan metode notFound untuk pengalaman 404 yang lebih baik.

Di index.php:

Flight::map('notFound', function () {
    Flight::view()->render('404.latte', ['title' => 'Halaman Tidak Ditemukan']);
});

Buat app/views/404.latte:

{extends 'layout.latte'}

{block content}
    <h2>404 - {$title}</h2>
    <p>Maaf, halaman tersebut tidak ada!</p>
{/block}

Langkah Selanjutnya

Kesimpulan

Anda telah membangun blog sederhana dengan Flight PHP! Panduan ini menunjukkan fitur inti seperti routing, templating dengan Latte, dan menangani pengiriman formulir—semuanya tetap ringan. Jelajahi dokumentasi Flight untuk fitur-fitur lebih lanjut untuk membawa blog Anda lebih jauh!

License

Lisensi MIT (MIT)

Hak Cipta © 2024 @mikecao, @n0nag0n

Izin diberikan di sini, tanpa biaya, kepada setiap orang yang mendapatkan salinan perangkat lunak ini dan dokumentasi terkait (disebut "Perangkat Lunak"), untuk berurusan dengan Perangkat Lunak tanpa pembatasan, termasuk tanpa batasan hak untuk menggunakan, menyalin, memodifikasi, menggabungkan, menerbitkan, mendistribusikan, sublisensikan, dan/atau menjual salinan Perangkat Lunak, dan untuk mengizinkan orang-orang yang menerima Perangkat Lunak untuk melakukannya, dengan syarat berikut:

Pemberitahuan hak cipta di atas dan pemberitahuan izin ini harus disertakan dalam semua salinan atau bagian substansial dari Perangkat Lunak.

PERANGKAT LUNAK DIBERIKAN "SEBAGAIMANA ADANYA", TANPA JAMINAN DALAM BENTUK APAPUN, TERSURAT ATAU TERSIRAT, TERMASUK NAMUN TIDAK TERBATAS PADA JAMINAN KELAYAKAN PERDAGANGAN, KECOCOKAN UNTUK TUJUAN TERTENTU DAN TIDAK MELANGGAR. DALAM HAL APA PUN PENULIS ATAU PEMEGANG HAK CIPTA TIDAK BERTANGGUNG JAWAB ATAS CLAIM, KERUSAKAN ATAU TANGGUNG JAWAB LAINNYA, BAIK DALAM TINDAKAN KONTRAK, TORT ATAU SEBALIKNYA, YANG TIMBUL DARI, DARI ATAU SEHUBUNGAN DENGAN PERANGKAT LUNAK ATAU PENGGUNAAN ATAU TRANSAKSI LAINNYA DALAM PERANGKAT LUNAK.

About

Apa itu Flight?

Flight adalah framework PHP yang cepat, sederhana, dan dapat diperluas. Ini cukup serbaguna dan dapat digunakan untuk membangun berbagai jenis aplikasi web. Dibangun dengan pemikiran kesederhanaan dan ditulis dengan cara yang mudah dipahami dan digunakan.

Flight adalah framework yang hebat untuk pemula yang baru mengenal PHP dan ingin belajar bagaimana membangun aplikasi web. Ini juga merupakan framework yang hebat untuk pengembang berpengalaman yang ingin lebih mengontrol aplikasi web mereka. Ini dirancang untuk dengan mudah membangun RESTful API, aplikasi web sederhana, atau aplikasi web yang kompleks.

Mulai Cepat

Pertama, instal dengan Composer

composer require flightphp/core

atau Anda bisa mengunduh zip dari repositori di sini. Kemudian Anda akan memiliki file dasar index.php seperti berikut:

<?php

// jika diinstal dengan composer
require 'vendor/autoload.php';
// atau jika diinstal secara manual dengan file zip
// require 'flight/Flight.php';

Flight::route('/', function() {
  echo 'halo dunia!';
});

Flight::route('/json', function() {
  Flight::json(['halo' => 'dunia']);
});

Flight::start();

Itu saja! Anda memiliki aplikasi dasar Flight. Anda sekarang dapat menjalankan file ini dengan php -S localhost:8000 dan kunjungi http://localhost:8000 di browser Anda untuk melihat output.

Apakah ini cepat?

Ya! Flight cepat. Ini adalah salah satu framework PHP tercepat yang tersedia. Anda dapat melihat semua tolok ukur di TechEmpower

Lihat tolok ukur di bawah ini dengan beberapa framework PHP populer lainnya.

Framework Permintaan Teks Biasa/detik Permintaan JSON/detik
Flight 190.421 182.491
Yii 145.749 131.434
Fat-Free 139.238 133.952
Slim 89.588 87.348
Phalcon 95.911 87.675
Symfony 65.053 63.237
Lumen 40.572 39.700
Laravel 26.657 26.901
CodeIgniter 20.628 19.901

Aplikasi Skeleton/Boilerplate

Ada aplikasi contoh yang dapat membantu Anda memulai dengan Framework Flight. Kunjungi flightphp/skeleton untuk instruksi tentang cara memulai! Anda juga dapat mengunjungi halaman contoh untuk inspirasi tentang beberapa hal yang dapat Anda lakukan dengan Flight.

Komunitas

Kami ada di Matrix Chat

Matrix

Dan Discord

Kontribusi

Ada dua cara Anda dapat berkontribusi pada Flight:

  1. Anda dapat berkontribusi pada framework inti dengan mengunjungi repositori inti.
  2. Anda dapat berkontribusi pada dokumentasi. Situs web dokumentasi ini dihosting di Github. Jika Anda menemukan kesalahan atau ingin memperbaiki sesuatu menjadi lebih baik, silakan perbaiki dan kirim permintaan tarik! Kami berusaha untuk tetap mengikuti hal-hal, tetapi pembaruan dan terjemahan bahasa sangat diterima.

Persyaratan

Flight memerlukan PHP 7.4 atau lebih tinggi.

Catatan: PHP 7.4 didukung karena pada saat penulisan ini (2024) PHP 7.4 adalah versi default untuk beberapa distribusi Linux LTS. Memaksa pindah ke PHP >8 akan menyebabkan banyak masalah bagi pengguna tersebut. Framework ini juga mendukung PHP >8.

Lisensi

Flight dirilis di bawah lisensi MIT.

Awesome-plugins/php_cookie

Cookies

overclokk/cookie adalah perpustakaan sederhana untuk mengelola cookie dalam aplikasi Anda.

Instalasi

Instalasi sangat sederhana dengan composer.

composer require overclokk/cookie

Penggunaan

Penggunaan semudah mendaftarkan metode baru pada kelas Flight.


use Overclokk\Cookie\Cookie;

/*
 * Set di file bootstrap atau public/index.php Anda
 */

Flight::register('cookie', Cookie::class);

/**
 * ExampleController.php
 */

class ExampleController {
    public function login() {
        // Set sebuah cookie

        // Anda ingin ini menjadi false agar Anda mendapatkan instance baru
        // gunakan komentar di bawah jika Anda ingin autocomplete
        /** @var \Overclokk\Cookie\Cookie $cookie */
        $cookie = Flight::cookie(false);
        $cookie->set(
            'stay_logged_in', // nama cookie
            '1', // nilai yang ingin Anda atur
            86400, // jumlah detik cookie harus bertahan
            '/', // jalur yang akan tersedia untuk cookie
            'example.com', // domain yang akan tersedia untuk cookie
            true, // cookie hanya akan ditransmisikan melalui koneksi HTTPS yang aman
            true // cookie hanya akan tersedia melalui protokol HTTP
        );

        // opsional, jika Anda ingin mempertahankan nilai default
        // dan memiliki cara cepat untuk mengatur cookie untuk waktu yang lama
        $cookie->forever('stay_logged_in', '1');
    }

    public function home() {
        // Periksa apakah Anda memiliki cookie
        if (Flight::cookie()->has('stay_logged_in')) {
            // tempatkan mereka di area dasbor misalnya.
            Flight::redirect('/dashboard');
        }
    }
}

Awesome-plugins/php_encryption

Enkripsi PHP

defuse/php-encryption adalah perpustakaan yang dapat digunakan untuk mengenkripsi dan mendekripsi data. Memulai dan menjalankan cukup sederhana untuk mulai mengenkripsi dan mendekripsi data. Mereka memiliki tutorial yang sangat membantu menjelaskan dasar-dasar cara menggunakan perpustakaan serta implikasi keamanan penting terkait enkripsi.

Instalasi

Instalasi sangat sederhana dengan composer.

composer require defuse/php-encryption

Pengaturan

Kemudian Anda perlu menghasilkan kunci enkripsi.

vendor/bin/generate-defuse-key

Ini akan menghasilkan kunci yang perlu Anda simpan dengan aman. Anda bisa menyimpan kunci di file app/config/config.php Anda di array di bagian bawah file. Meskipun itu bukan tempat yang sempurna, paling tidak itu adalah sesuatu.

Penggunaan

Sekarang Anda memiliki perpustakaan dan kunci enkripsi, Anda dapat mulai mengenkripsi dan mendekripsi data.


use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

/*
 * Tetapkan di file bootstrap atau public/index.php Anda
 */

// Metode enkripsi
Flight::map('encrypt', function($raw_data) {
    $encryption_key = /* $config['encryption_key'] atau file_get_contents tempat Anda meletakkan kunci */;
    return Crypto::encrypt($raw_data, Key::loadFromAsciiSafeString($encryption_key));
});

// Metode dekripsi
Flight::map('decrypt', function($encrypted_data) {
    $encryption_key = /* $config['encryption_key'] atau file_get_contents tempat Anda meletakkan kunci */;
    try {
        $raw_data = Crypto::decrypt($encrypted_data, Key::loadFromAsciiSafeString($encryption_key));
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
        // Sebuah serangan! Entah kunci yang salah dimuat, atau ciphertext telah
        // berubah sejak dibuat -- baik rusak di database atau
        // sengaja dimodifikasi oleh Eve yang mencoba melakukan serangan.

        // ... tangani kasus ini dengan cara yang sesuai untuk aplikasi Anda ...
    }
    return $raw_data;
});

Flight::route('/encrypt', function() {
    $encrypted_data = Flight::encrypt('Ini adalah rahasia');
    echo $encrypted_data;
});

Flight::route('/decrypt', function() {
    $encrypted_data = '...'; // Ambil data terenkripsi dari suatu tempat
    $decrypted_data = Flight::decrypt($encrypted_data);
    echo $decrypted_data;
});

Awesome-plugins/php_file_cache

flightphp/cache

Kelas caching PHP di-file yang ringan, sederhana, dan mandiri

Keuntungan

Situs dokumen ini menggunakan perpustakaan ini untuk menyimpan cache setiap halaman!

Klik di sini untuk melihat kodenya.

Instalasi

Instal melalui composer:

composer require flightphp/cache

Penggunaan

Penggunaannya cukup sederhana. Ini menyimpan file cache di direktori cache.

use flight\Cache;

$app = Flight::app();

// Anda memasukkan direktori tempat cache akan disimpan ke dalam konstruktor
$app->register('cache', Cache::class, [ __DIR__ . '/../cache/' ], function(Cache $cache) {

    // Ini memastikan bahwa cache hanya digunakan saat dalam mode produksi
    // ENVIRONMENT adalah konstanta yang diatur dalam file bootstrap Anda atau di tempat lain dalam aplikasi Anda
    $cache->setDevMode(ENVIRONMENT === 'development');
});

Kemudian Anda dapat menggunakannya di kode Anda seperti ini:


// Dapatkan instance cache
$cache = Flight::cache();
$data = $cache->refreshIfExpired('simple-cache-test', function () {
    return date("H:i:s"); // kembalikan data untuk disimpan di cache
}, 10); // 10 detik

// atau
$data = $cache->retrieve('simple-cache-test');
if(empty($data)) {
    $data = date("H:i:s");
    $cache->store('simple-cache-test', $data, 10); // 10 detik
}

Dokumentasi

Kunjungi https://github.com/flightphp/cache untuk dokumentasi lengkap dan pastikan Anda melihat folder contoh.

Awesome-plugins/permissions

FlightPHP/Permissions

Ini adalah modul izin yang dapat digunakan dalam proyek Anda jika Anda memiliki beberapa peran di aplikasi Anda dan setiap peran memiliki sedikit fungsi yang berbeda. Modul ini memungkinkan Anda untuk mendefinisikan izin untuk setiap peran dan kemudian memeriksa apakah pengguna saat ini memiliki izin untuk mengakses halaman tertentu atau melakukan tindakan tertentu.

Klik di sini untuk repositori di GitHub.

Instalasi

Jalankan composer require flightphp/permissions dan Anda sudah siap!

Penggunaan

Pertama, Anda perlu mengatur izin Anda, lalu Anda memberi tahu aplikasi Anda apa arti izin tersebut. Pada akhirnya, Anda akan memeriksa izin Anda dengan $Permissions->has(), ->can(), atau is(). has() dan can() memiliki fungsionalitas yang sama, tetapi dinamai berbeda untuk membuat kode Anda lebih mudah dibaca.

Contoh Dasar

Mari kita anggap Anda memiliki fitur dalam aplikasi Anda yang memeriksa apakah seorang pengguna sudah masuk. Anda dapat membuat objek izin seperti ini:

// index.php
require 'vendor/autoload.php';

// beberapa kode

// lalu Anda mungkin memiliki sesuatu yang memberi tahu Anda siapa peran saat ini dari orang tersebut
// kemungkinan Anda memiliki sesuatu di mana Anda mengambil peran saat ini
// dari variabel sesi yang mendefinisikan ini
// setelah seseorang masuk, jika tidak, mereka akan memiliki peran 'tamu' atau 'publik'.
$current_role = 'admin';

// mengatur izin
$permission = new \flight\Permission($current_role);
$permission->defineRule('loggedIn', function($current_role) {
    return $current_role !== 'guest';
});

// Anda mungkin ingin menyimpan objek ini di Flight di suatu tempat
Flight::set('permission', $permission);

Kemudian di dalam controller di suatu tempat, Anda mungkin memiliki sesuatu seperti ini.

<?php

// beberapa controller
class SomeController {
    public function someAction() {
        $permission = Flight::get('permission');
        if ($permission->has('loggedIn')) {
            // lakukan sesuatu
        } else {
            // lakukan sesuatu yang lain
        }
    }
}

Anda juga dapat menggunakan ini untuk melacak apakah mereka memiliki izin untuk melakukan sesuatu dalam aplikasi Anda. Sebagai contoh, jika Anda memiliki cara bagi pengguna untuk berinteraksi dengan posting di perangkat lunak Anda, Anda dapat memeriksa apakah mereka memiliki izin untuk melakukan tindakan tertentu.

$current_role = 'admin';

// mengatur izin
$permission = new \flight\Permission($current_role);
$permission->defineRule('post', function($current_role) {
    if($current_role === 'admin') {
        $permissions = ['create', 'read', 'update', 'delete'];
    } else if($current_role === 'editor') {
        $permissions = ['create', 'read', 'update'];
    } else if($current_role === 'author') {
        $permissions = ['create', 'read'];
    } else if($current_role === 'contributor') {
        $permissions = ['create'];
    } else {
        $permissions = [];
    }
    return $permissions;
});
Flight::set('permission', $permission);

Kemudian di dalam controller di suatu tempat...

class PostController {
    public function create() {
        $permission = Flight::get('permission');
        if ($permission->can('post.create')) {
            // lakukan sesuatu
        } else {
            // lakukan sesuatu yang lain
        }
    }
}

Menginjeksi ketergantungan

Anda dapat menginjeksi ketergantungan ke dalam closure yang mendefinisikan izin. Ini berguna jika Anda memiliki semacam toggle, id, atau titik data lain yang ingin Anda periksa. Hal ini juga berlaku untuk panggilan jenis Class->Method, kecuali Anda mendefinisikan argumen dalam metode tersebut.

Closure

$Permission->defineRule('order', function(string $current_role, MyDependency $MyDependency = null) {
    // ... kode
});

// di file controller Anda
public function createOrder() {
    $MyDependency = Flight::myDependency();
    $permission = Flight::get('permission');
    if ($permission->can('order.create', $MyDependency)) {
        // lakukan sesuatu
    } else {
        // lakukan sesuatu yang lain
    }
}

Kelas

namespace MyApp;

class Permissions {

    public function order(string $current_role, MyDependency $MyDependency = null) {
        // ... kode
    }
}

Pintasan untuk mengatur izin dengan kelas

Anda juga dapat menggunakan kelas untuk mendefinisikan izin Anda. Ini berguna jika Anda memiliki banyak izin dan Anda ingin menjaga kode Anda tetap bersih. Anda bisa melakukan sesuatu seperti ini:

<?php

// kode bootstrap
$Permissions = new \flight\Permission($current_role);
$Permissions->defineRule('order', 'MyApp\Permissions->order');

// myapp/Permissions.php
namespace MyApp;

class Permissions {

    public function order(string $current_role, int $user_id) {
        // Mengasumsikan Anda telah mengatur ini sebelumnya
        /** @var \flight\database\PdoWrapper $db */
        $db = Flight::db();
        $allowed_permissions = [ 'read' ]; // semua orang dapat melihat sebuah pesanan
        if($current_role === 'manager') {
            $allowed_permissions[] = 'create'; // manajer dapat membuat pesanan
        }
        $some_special_toggle_from_db = $db->fetchField('SELECT some_special_toggle FROM settings WHERE id = ?', [ $user_id ]);
        if($some_special_toggle_from_db) {
            $allowed_permissions[] = 'update'; // jika pengguna memiliki toggle khusus, mereka dapat memperbarui pesanan
        }
        if($current_role === 'admin') {
            $allowed_permissions[] = 'delete'; // admin dapat menghapus pesanan
        }
        return $allowed_permissions;
    }
}

Bagian yang keren adalah bahwa ada juga pintasan yang dapat Anda gunakan (yang juga dapat dicache!!!) di mana Anda cukup memberi tahu kelas izin untuk memetakan semua metode dalam sebuah kelas ke dalam izin. Jadi jika Anda memiliki metode bernama order() dan metode bernama company(), ini akan secara otomatis dipetakan sehingga Anda cukup menjalankan $Permissions->has('order.read') atau $Permissions->has('company.read') dan itu akan berhasil. Mendefinisikan ini sangat sulit, jadi ikutlah bersama saya di sini. Anda hanya perlu melakukan ini:

Buat kelas izin yang ingin Anda kelompokkan bersama.

class MyPermissions {
    public function order(string $current_role, int $order_id = 0): array {
        // kode untuk menentukan izin
        return $permissions_array;
    }

    public function company(string $current_role, int $company_id): array {
        // kode untuk menentukan izin
        return $permissions_array;
    }
}

Kemudian buat izin tersebut dapat ditemukan menggunakan pustaka ini.

$Permissions = new \flight\Permission($current_role);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class);
Flight::set('permissions', $Permissions);

Akhirnya, panggil izin di basis kode Anda untuk memeriksa apakah pengguna diizinkan untuk melakukan izin tertentu.

class SomeController {
    public function createOrder() {
        if(Flight::get('permissions')->can('order.create') === false) {
            die('Anda tidak bisa membuat sebuah pesanan. Maaf!');
        }
    }
}

Cache

Untuk mengaktifkan caching, lihat pustaka sederhana wruczak/phpfilecache. Contoh untuk mengaktifkannya ada di bawah ini.


// $app ini bisa menjadi bagian dari kode Anda, atau
// Anda bisa langsung meneruskan null dan itu akan
// mengambil dari Flight::app() di dalam konstruktor
$app = Flight::app();

// Untuk saat ini, ini menerima sebagai cache file. Lainnya dapat dengan mudah
// ditambahkan di masa depan. 
$Cache = new Wruczek\PhpFileCache\PhpFileCache;

$Permissions = new \flight\Permission($current_role, $app, $Cache);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class, 3600); // 3600 adalah berapa detik untuk menyimpan cache ini. Biarkan ini kosong untuk tidak menggunakan caching

Dan Anda sudah siap!

Awesome-plugins/simple_job_queue

Antrean Pekerjaan Sederhana

Antrean Pekerjaan Sederhana adalah sebuah pustaka yang dapat digunakan untuk memproses pekerjaan secara asinkron. Ini dapat digunakan dengan beanstalkd, MySQL/MariaDB, SQLite, dan PostgreSQL.

Instal

composer require n0nag0n/simple-job-queue

Penggunaan

Agar ini dapat berfungsi, Anda memerlukan cara untuk menambahkan pekerjaan ke antrean dan cara untuk memproses pekerjaan (pekerja). Berikut adalah contoh tentang cara menambahkan pekerjaan ke antrean dan cara memproses pekerjaan.

Menambahkan ke Flight

Menambahkan ini ke Flight sangat sederhana dan dilakukan dengan menggunakan metode register(). Berikut adalah contoh cara menambahkan ini ke Flight.

<?php
require 'vendor/autoload.php';

// Ubah ['mysql'] menjadi ['beanstalkd'] jika Anda ingin menggunakan beanstalkd
Flight::register('queue', n0nag0n\Job_Queue::class, ['mysql'], function($Job_Queue) {
    // jika Anda sudah memiliki koneksi PDO di Flight::db();
    $Job_Queue->addQueueConnection(Flight::db());

    // atau jika Anda menggunakan beanstalkd/Pheanstalk
    $pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
    $Job_Queue->addQueueConnection($pheanstalk);
});

Menambahkan pekerjaan baru

Saat Anda menambahkan pekerjaan, Anda perlu menentukan sebuah pipeline (antrean). Ini sebanding dengan sebuah saluran di RabbitMQ atau sebuah tabung di beanstalkd.

<?php
Flight::queue()->selectPipeline('send_important_emails');
Flight::queue()->addJob(json_encode([ 'something' => 'that', 'ends' => 'up', 'a' => 'string' ]));

Menjalankan seorang pekerja

Berikut adalah contoh file tentang cara menjalankan seorang pekerja.

<?php

require 'vendor/autoload.php';

$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Koneksi PDO
$PDO = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'user', 'pass');
$Job_Queue->addQueueConnection($PDO);

// atau jika Anda menggunakan beanstalkd/Pheanstalk
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);

$Job_Queue->watchPipeline('send_important_emails');
while(true) {
    $job = $Job_Queue->getNextJobAndReserve();

    // sesuaikan dengan apa pun yang membuat Anda tidur lebih nyenyak di malam hari (hanya untuk antrean basis data, beanstalkd tidak memerlukan pernyataan if ini)
    if(empty($job)) {
        usleep(500000);
        continue;
    }

    echo "Memproses {$job['id']}\n";
    $payload = json_decode($job['payload'], true);

    try {
        $result = doSomethingThatDoesSomething($payload);

        if($result === true) {
            $Job_Queue->deleteJob($job);
        } else {
            // ini mengeluarkannya dari antrean siap dan menempatkannya dalam antrean lain yang dapat diambil dan "dikejutkan" nanti.
            $Job_Queue->buryJob($job);
        }
    } catch(Exception $e) {
        $Job_Queue->buryJob($job);
    }
}

Menangani Proses Panjang dengan Supervisord

Supervisord adalah sistem kontrol proses yang memastikan bahwa proses pekerja Anda tetap berjalan terus-menerus. Berikut adalah panduan yang lebih lengkap tentang cara mengaturnya dengan pekerja Antrean Pekerjaan Sederhana Anda:

Menginstal Supervisord

# Di Ubuntu/Debian
sudo apt-get install supervisor

# Di CentOS/RHEL
sudo yum install supervisor

# Di macOS dengan Homebrew
brew install supervisor

Membuat Skrip Pekerja

Pertama, simpan kode pekerja Anda ke dalam file PHP yang didedikasikan:

<?php

require 'vendor/autoload.php';

$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Koneksi PDO
$PDO = new PDO('mysql:dbname=your_database;host=127.0.0.1', 'username', 'password');
$Job_Queue->addQueueConnection($PDO);

// Tentukan pipeline untuk diawasi
$Job_Queue->watchPipeline('send_important_emails');

// Catat awal pekerja
echo date('Y-m-d H:i:s') . " - Pekerja dimulai\n";

while(true) {
    $job = $Job_Queue->getNextJobAndReserve();

    if(empty($job)) {
        usleep(500000); // Tidur selama 0.5 detik
        continue;
    }

    echo date('Y-m-d H:i:s') . " - Memproses pekerjaan {$job['id']}\n";
    $payload = json_decode($job['payload'], true);

    try {
        $result = doSomethingThatDoesSomething($payload);

        if($result === true) {
            $Job_Queue->deleteJob($job);
            echo date('Y-m-d H:i:s') . " - Pekerjaan {$job['id']} berhasil diselesaikan\n";
        } else {
            $Job_Queue->buryJob($job);
            echo date('Y-m-d H:i:s') . " - Pekerjaan {$job['id']} gagal, dibuang\n";
        }
    } catch(Exception $e) {
        $Job_Queue->buryJob($job);
        echo date('Y-m-d H:i:s') . " - Pengecualian saat memproses pekerjaan {$job['id']}: {$e->getMessage()}\n";
    }
}

Mengkonfigurasi Supervisord

Buat file konfigurasi untuk pekerja Anda:

[program:email_worker]
command=php /path/to/worker.php
directory=/path/to/project
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/simple_job_queue_err.log
stdout_logfile=/var/log/simple_job_queue.log
user=www-data
numprocs=2
process_name=%(program_name)s_%(process_num)02d

Opsi Konfigurasi Utama:

Mengelola Pekerja dengan Supervisorctl

Setelah membuat atau mengubah konfigurasi:

# Muat ulang konfigurasi supervisor
sudo supervisorctl reread
sudo supervisorctl update

# Kontrol proses pekerja tertentu
sudo supervisorctl start email_worker:*
sudo supervisorctl stop email_worker:*
sudo supervisorctl restart email_worker:*
sudo supervisorctl status email_worker:*

Menjalankan Beberapa Pipeline

Untuk beberapa pipeline, buat file pekerja dan konfigurasi terpisah:

[program:email_worker]
command=php /path/to/email_worker.php
# ... konfigurasi lainnya ...

[program:notification_worker]
command=php /path/to/notification_worker.php
# ... konfigurasi lainnya ...

Memantau dan Log

Periksa log untuk memantau aktivitas pekerja:

# Lihat log
sudo tail -f /var/log/simple_job_queue.log

# Periksa status
sudo supervisorctl status

Pengaturan ini memastikan pekerja pekerjaan Anda terus berjalan meskipun setelah kerusakan, reboot server, atau masalah lainnya, menjadikan sistem antrean Anda andal untuk lingkungan produksi.

Awesome-plugins/ghost_session

Ghostff/Session

Pengelola Sesi PHP (non-blocking, flash, segment, enkripsi sesi). Menggunakan PHP open_ssl untuk enkripsi/dekripsi data sesi secara opsional. Mendukung File, MySQL, Redis, dan Memcached.

Klik di sini untuk melihat kode.

Instalasi

Instal dengan composer.

composer require ghostff/session

Konfigurasi Dasar

Anda tidak perlu mengirimkan apa pun untuk menggunakan pengaturan default dengan sesi Anda. Anda bisa membaca lebih lanjut tentang pengaturan lainnya di Github Readme.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

// satu hal yang perlu diingat adalah bahwa Anda harus mengkomit sesi Anda di setiap muatan halaman
// atau Anda perlu menjalankan auto_commit dalam konfigurasi Anda. 

Contoh Sederhana

Berikut adalah contoh sederhana tentang bagaimana Anda mungkin menggunakan ini.

Flight::route('POST /login', function() {
    $session = Flight::session();

    // lakukan logika login Anda di sini
    // validasi kata sandi, dll.

    // jika login berhasil
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // setiap kali Anda menulis ke sesi, Anda harus mengkomitnya dengan sengaja.
    $session->commit();
});

// Pemeriksaan ini bisa ada dalam logika halaman terbatas, atau dibungkus dengan middleware.
Flight::route('/some-restricted-page', function() {
    $session = Flight::session();

    if(!$session->get('is_logged_in')) {
        Flight::redirect('/login');
    }

    // lakukan logika halaman terbatas Anda di sini
});

// versi middleware
Flight::route('/some-restricted-page', function() {
    // logika halaman biasa
})->addMiddleware(function() {
    $session = Flight::session();

    if(!$session->get('is_logged_in')) {
        Flight::redirect('/login');
    }
});

Contoh yang Lebih Kompleks

Berikut adalah contoh yang lebih kompleks tentang bagaimana Anda mungkin menggunakan ini.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

// setel jalur kustom ke file konfigurasi sesi Anda dan berikan string acak untuk id sesi
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
        // atau Anda dapat secara manual menimpa opsi konfigurasi
        $session->updateConfiguration([
            // jika Anda ingin menyimpan data sesi Anda dalam basis data (baik jika Anda menginginkan sesuatu seperti, "keluar dari semua perangkat")
            Session::CONFIG_DRIVER        => Ghostff\Session\Drivers\MySql::class,
            Session::CONFIG_ENCRYPT_DATA  => true,
            Session::CONFIG_SALT_KEY      => hash('sha256', 'my-super-S3CR3T-salt'), // silakan ubah ini menjadi sesuatu yang lain
            Session::CONFIG_AUTO_COMMIT   => true, // hanya lakukan ini jika memerlukannya dan/atau sulit untuk mengkomit sesi Anda.
                                                   // selain itu Anda bisa melakukan Flight::after('start', function() { Flight::session()->commit(); });
            Session::CONFIG_MYSQL_DS         => [
                'driver'    => 'mysql',             # Driver basis data untuk dns PDO misal(mysql:host=...;dbname=...)
                'host'      => '127.0.0.1',         # Host basis data
                'db_name'   => 'my_app_database',   # Nama basis data
                'db_table'  => 'sessions',          # Tabel basis data
                'db_user'   => 'root',              # Nama pengguna basis data
                'db_pass'   => '',                  # Kata sandi basis data
                'persistent_conn'=> false,          # Hindari overhead dari pembuatan koneksi baru setiap kali skrip perlu berbicara dengan basis data, yang menghasilkan aplikasi web yang lebih cepat. TEMUKAN BELAKANG SENDIRI
            ]
        ]);
    }
);

Bantu! Data Sesi Saya Tidak Bertahan!

Apakah Anda mengatur data sesi Anda dan itu tidak bertahan antar permintaan? Anda mungkin telah lupa untuk mengkomit data sesi Anda. Anda dapat melakukannya dengan memanggil $session->commit() setelah Anda mengatur data sesi Anda.

Flight::route('POST /login', function() {
    $session = Flight::session();

    // lakukan logika login Anda di sini
    // validasi kata sandi, dll.

    // jika login berhasil
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // setiap kali Anda menulis ke sesi, Anda harus mengkomitnya dengan sengaja.
    $session->commit();
});

Cara lain untuk mengatasi ini adalah ketika Anda mengatur layanan sesi Anda, Anda harus mengatur auto_commit ke true dalam konfigurasi Anda. Ini akan secara otomatis mengkomit data sesi Anda setelah setiap permintaan.


$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
        $session->updateConfiguration([
            Session::CONFIG_AUTO_COMMIT   => true,
        ]);
    }
);

Selain itu, Anda dapat melakukan Flight::after('start', function() { Flight::session()->commit(); }); untuk mengkomit data sesi Anda setelah setiap permintaan.

Dokumentasi

Kunjungi Github Readme untuk dokumentasi lengkap. Opsi konfigurasi dijelaskan secara rinci dalam default_config.php file itu sendiri. Kodenya mudah dipahami jika Anda ingin menelusuri paket ini sendiri.

Awesome-plugins/pdo_wrapper

Kelas Pembantu PDO PdoWrapper

Flight hadir dengan kelas pembantu untuk PDO. Kelas ini memungkinkan Anda untuk dengan mudah melakukan kueri ke basis data Anda dengan semua persiapan/eksekusi/fetchAll() yang membingungkan. Ini sangat menyederhanakan cara Anda dapat melakukan kueri ke basis data Anda. Setiap hasil baris dikembalikan sebagai kelas Koleksi Flight yang memungkinkan Anda mengakses data Anda melalui sintaks array atau sintaks objek.

Mendaftar Kelas Pembantu PDO

// Daftarkan kelas pembantu PDO
Flight::register('db', \flight\database\PdoWrapper::class, ['mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [
        PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'utf8mb4\'',
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_STRINGIFY_FETCHES => false,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]
]);

Penggunaan

Objek ini memperluas PDO sehingga semua metode PDO normal tersedia. Metode berikut ditambahkan untuk mempermudah kueri ke basis data:

runQuery(string $sql, array $params = []): PDOStatement

Gunakan ini untuk INSERTS, UPDATES, atau jika Anda berencana menggunakan SELECT dalam loop while

$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM table WHERE something = ?", [ $something ]);
while($row = $statement->fetch()) {
    // ...
}

// Atau menulis ke basis data
$db->runQuery("INSERT INTO table (name) VALUES (?)", [ $name ]);
$db->runQuery("UPDATE table SET name = ? WHERE id = ?", [ $name, $id ]);

fetchField(string $sql, array $params = []): mixed

Mengambil field pertama dari kueri

$db = Flight::db();
$count = $db->fetchField("SELECT COUNT(*) FROM table WHERE something = ?", [ $something ]);

fetchRow(string $sql, array $params = []): array

Mengambil satu baris dari kueri

$db = Flight::db();
$row = $db->fetchRow("SELECT id, name FROM table WHERE id = ?", [ $id ]);
echo $row['name'];
// atau
echo $row->name;

fetchAll(string $sql, array $params = []): array

Mengambil semua baris dari kueri

$db = Flight::db();
$rows = $db->fetchAll("SELECT id, name FROM table WHERE something = ?", [ $something ]);
foreach($rows as $row) {
    echo $row['name'];
    // atau
    echo $row->name;
}

Catatan dengan sintaks IN()

Ini juga memiliki pembungkus yang berguna untuk pernyataan IN(). Anda cukup memasukkan tanda tanya tunggal sebagai placeholder untuk IN() dan kemudian array nilai. Berikut adalah contoh seperti apa itu:

$db = Flight::db();
$name = 'Bob';
$company_ids = [1,2,3,4,5];
$rows = $db->fetchAll("SELECT id, name FROM table WHERE name = ? AND company_id IN (?)", [ $name, $company_ids ]);

Contoh Lengkap

// Contoh rute dan cara Anda akan menggunakan pembungkus ini
Flight::route('/users', function () {
    // Dapatkan semua pengguna
    $users = Flight::db()->fetchAll('SELECT * FROM users');

    // Streaming semua pengguna
    $statement = Flight::db()->runQuery('SELECT * FROM users');
    while ($user = $statement->fetch()) {
        echo $user['name'];
        // atau echo $user->name;
    }

    // Dapatkan satu pengguna
    $user = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]);

    // Dapatkan satu nilai
    $count = Flight::db()->fetchField('SELECT COUNT(*) FROM users');

    // Sintaks IN() khusus untuk membantu (pastikan IN ditulis dengan huruf kapital)
    $users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]);
    // Anda juga dapat melakukan ini
    $users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [ '1,2,3,4,5']);

    // Memasukkan pengguna baru
    Flight::db()->runQuery("INSERT INTO users (name, email) VALUES (?, ?)", ['Bob', 'bob@example.com']);
    $insert_id = Flight::db()->lastInsertId();

    // Memperbarui pengguna
    Flight::db()->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 123]);

    // Menghapus pengguna
    Flight::db()->runQuery("DELETE FROM users WHERE id = ?", [123]);

    // Dapatkan jumlah baris yang terpengaruh
    $statement = Flight::db()->runQuery("UPDATE users SET name = ? WHERE name = ?", ['Bob', 'Sally']);
    $affected_rows = $statement->rowCount();

});

Awesome-plugins/migrations

Migrations

Migrasi untuk proyek Anda menjaga semua perubahan basis data yang terlibat dalam proyek Anda.
byjg/php-migration adalah pustaka inti yang sangat membantu untuk memulai.

Menginstal

Pustaka PHP

Jika Anda ingin menggunakan hanya Pustaka PHP di proyek Anda:

composer require "byjg/migration"

Antarmuka Baris Perintah

Antarmuka baris perintah berdiri sendiri dan tidak memerlukan Anda menginstalnya bersama proyek Anda.

Anda dapat menginstalnya secara global dan membuat tautan simbolis

composer require "byjg/migration-cli"

Silakan kunjungi byjg/migration-cli untuk mendapatkan lebih banyak informasi tentang Migration CLI.

Basis data yang didukung

Basis Data Driver String Koneksi
Sqlite pdo_sqlite sqlite:///path/to/file
MySql/MariaDb pdo_mysql mysql://username:password@hostname:port/database
Postgres pdo_pgsql pgsql://username:password@hostname:port/database
Sql Server pdo_dblib, pdo_sysbase Linux dblib://username:password@hostname:port/database
Sql Server pdo_sqlsrv Windows sqlsrv://username:password@hostname:port/database

Bagaimana Ini Bekerja?

Migrasi Basis Data menggunakan SQL MURNI untuk mengelola versi basis data.
Untuk dapat berfungsi, Anda perlu:

Skrip SQL

Skrip dibagi menjadi tiga set skrip:

Direktori skrip adalah:

 <root dir>
     |
     +-- base.sql
     |
     +-- /migrations
              |
              +-- /up
                   |
                   +-- 00001.sql
                   +-- 00002.sql
              +-- /down
                   |
                   +-- 00000.sql
                   +-- 00001.sql

Lingkungan Pengembangan Multi

Jika Anda bekerja dengan beberapa pengembang dan beberapa cabang, sulit untuk menentukan nomor berikutnya.

Dalam kasus itu, Anda mempunyai akhiran "-dev" setelah nomor versi.

Lihat skenarionya:

Dalam kedua kasus, para pengembang akan membuat file bernama 43-dev.sql. Kedua pengembang akan bermigrasi NAIK dan TURUN tanpa masalah dan versi lokal Anda akan menjadi 43.

Namun pengembang 1 menggabungkan perubahan Anda dan membuat versi akhir 43.sql (git mv 43-dev.sql 43.sql). Jika pengembang 2 memperbarui cabang lokal Anda, dia akan memiliki file 43.sql (dari dev 1) dan file Anda 43-dev.sql.
Jika dia mencoba untuk bermigrasi NAIK atau TURUN, skrip migrasi akan turun dan memberi tahu bahwa terdapat DUA versi 43. Dalam kasus ini, pengembang 2 harus memperbarui file-nya menjadi 44-dev.sql dan melanjutkan bekerja hingga menggabungkan perubahan Anda dan menghasilkan versi akhir.

Menggunakan API PHP dan Mengintegrasikannya ke dalam Proyek Anda

Penggunaan dasar adalah

Lihat contohnya:

<?php
// Membuat URI Koneksi
// Lihat lebih lanjut: https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');

// Daftarkan Database atau Basis Data yang dapat menangani URI tersebut:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);

// Membuat instance Migrasi
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');

// Tambahkan fungsi progres callback untuk menerima info dari eksekusi
$migration->addCallbackProgress(function ($action, $currentVersion, $fileInfo) {
    echo "$action, $currentVersion, ${fileInfo['description']}\n";
});

// Mengembalikan basis data menggunakan skrip "base.sql"
// dan menjalankan SEMUA skrip yang ada untuk menaikkan versi basis data ke versi terbaru
$migration->reset();

// Jalankan SEMUA skrip yang ada untuk naik atau turun versi basis data
// dari versi sekarang hingga nomor $version;
// Jika nomor versi tidak ditentukan, migrasi hingga versi basis data terakhir
$migration->update($version = null);

Objek Migrasi mengontrol versi basis data.

Membuat kontrol versi di proyek Anda

<?php
// Daftarkan Database atau Basis Data yang dapat menangani URI tersebut:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);

// Membuat instance Migrasi
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');

// Perintah ini akan membuat tabel versi di basis data Anda
$migration->createVersion();

Mendapatkan versi saat ini

<?php
$migration->getCurrentVersion();

Menambahkan Callback untuk mengontrol progres

<?php
$migration->addCallbackProgress(function ($command, $version, $fileInfo) {
    echo "Melakukan Perintah: $command di versi $version - ${fileInfo['description']}, ${fileInfo['exists']}, ${fileInfo['file']}, ${fileInfo['checksum']}\n";
});

Mendapatkan instance Driver Db

<?php
$migration->getDbDriver();

Untuk menggunakannya, silakan kunjungi: https://github.com/byjg/anydataset-db

Menghindari Migrasi Parsial (tidak tersedia untuk MySQL)

Migrasi parsial adalah ketika skrip migrasi terhenti di tengah proses karena kesalahan atau penghentian manual.

Tabel migrasi akan memiliki status partial up atau partial down dan perlu diperbaiki secara manual sebelum dapat bermigrasi lagi.

Untuk menghindari situasi ini, Anda dapat menentukan migrasi akan dijalankan dalam konteks transaksional.
Jika skrip migrasi gagal, transaksi akan dibatalkan dan tabel migrasi akan ditandai sebagai complete dan versi akan menjadi versi sebelumnya yang segera sebelum skrip yang menyebabkan kesalahan.

Untuk mengaktifkan fitur ini, Anda perlu memanggil metode withTransactionEnabled dengan melewatkan true sebagai parameter:

<?php
$migration->withTransactionEnabled(true);

CATATAN: Fitur ini tidak tersedia untuk MySQL karena tidak mendukung perintah DDL di dalam transaksi.
Jika Anda menggunakan metode ini dengan MySQL, Migrasi akan mengabaikannya tanpa pemberitahuan.
Info lebih lanjut: https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html

Tips dalam menulis migrasi SQL untuk Postgres

Saat membuat trigger dan fungsi SQL

-- Lakukan
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
    BEGIN
        -- Periksa bahwa empname dan salary diberikan
        IF NEW.empname IS NULL THEN
            RAISE EXCEPTION 'empname tidak boleh null'; -- tidak masalah apakah komentar ini kosong atau tidak
        END IF; --
        IF NEW.salary IS NULL THEN
            RAISE EXCEPTION '% tidak dapat memiliki salary null', NEW.empname; --
        END IF; --

        -- Siapa yang bekerja untuk kita ketika mereka harus membayar untuk itu?
        IF NEW.salary < 0 THEN
            RAISE EXCEPTION '% tidak dapat memiliki salary negatif', NEW.empname; --
        END IF; --

        -- Ingat siapa yang mengubah gaji ketika
        NEW.last_date := current_timestamp; --
        NEW.last_user := current_user; --
        RETURN NEW; --
    END; --
$emp_stamp$ LANGUAGE plpgsql;

-- JANGAN
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
    BEGIN
        -- Periksa bahwa empname dan salary diberikan
        IF NEW.empname IS NULL THEN
            RAISE EXCEPTION 'empname tidak boleh null';
        END IF;
        IF NEW.salary IS NULL THEN
            RAISE EXCEPTION '% tidak dapat memiliki salary null', NEW.empname;
        END IF;

        -- Siapa yang bekerja untuk kita ketika mereka harus membayar untuk itu?
        IF NEW.salary < 0 THEN
            RAISE EXCEPTION '% tidak dapat memiliki salary negatif', NEW.empname;
        END IF;

        -- Ingat siapa yang mengubah gaji ketika
        NEW.last_date := current_timestamp;
        NEW.last_user := current_user;
        RETURN NEW;
    END;
$emp_stamp$ LANGUAGE plpgsql;

Karena lapisan abstraksi basis data PDO tidak dapat menjalankan kelompok pernyataan SQL,
ketika byjg/migration membaca file migrasi, itu harus memisahkan seluruh isi file SQL pada titik koma, dan menjalankan pernyataan satu per satu. Namun, ada satu jenis pernyataan yang dapat memiliki beberapa titik koma di antara tubuhnya: fungsi.

Agar dapat mem-parsing fungsi dengan benar, byjg/migration 2.1.0 mulai memisahkan file migrasi pada urutan semicolon + EOL bukannya hanya titik koma. Dengan cara ini, jika Anda menambahkan komentar kosong setelah setiap titik koma dalam definisi fungsi, byjg/migration akan dapat mem-parsingnya.

Sayangnya, jika Anda lupa menambahkan salah satu komentar ini, pustaka akan memisahkan pernyataan CREATE FUNCTION menjadi beberapa bagian dan migrasi akan gagal.

Hindari karakter titik dua (:)

-- Lakukan
CREATE TABLE bookings (
  booking_id UUID PRIMARY KEY,
  booked_at  TIMESTAMPTZ NOT NULL CHECK (CAST(booked_at AS DATE) <= check_in),
  check_in   DATE NOT NULL
);

-- JANGAN
CREATE TABLE bookings (
  booking_id UUID PRIMARY KEY,
  booked_at  TIMESTAMPTZ NOT NULL CHECK (booked_at::DATE <= check_in),
  check_in   DATE NOT NULL
);

Karena PDO menggunakan karakter titik dua untuk menjelaskan parameter bernama dalam pernyataan yang sudah disiapkan, penggunaannya akan menyebabkan kesalahan dalam konteks lain.

Misalnya, pernyataan PostgreSQL dapat menggunakan :: untuk mengonversi nilai antar tipe. Di sisi lain, PDO akan membaca ini sebagai parameter bernama yang tidak valid dalam konteks yang tidak valid dan gagal ketika mencoba menjalankannya.

Satu-satunya cara untuk memperbaiki ketidakkonsistenan ini adalah dengan menghindari titik dua sama sekali (dalam hal ini, PostgreSQL juga memiliki sintaks alternatif: CAST(value AS type)).

Gunakan editor SQL

Akhirnya, menulis migrasi SQL manual bisa melelahkan, tetapi jauh lebih mudah jika Anda menggunakan editor yang mampu memahami sintaks SQL, menyediakan autocompletion, mengintrospeksi skema basis data Anda saat ini dan/atau memformat kode Anda secara otomatis.

Menangani berbagai migrasi di dalam satu skema

Jika Anda perlu membuat skrip migrasi yang berbeda dan versi di dalam skema yang sama, itu mungkin
tetapi terlalu berisiko dan saya tidak merekomendasikannya sama sekali.

Untuk melakukan ini, Anda perlu membuat "tabel migrasi" yang berbeda dengan mengoper parameter pada konstruktor.

<?php
$migration = new \ByJG\DbMigration\Migration("db:/uri", "/path", true, "NEW_MIGRATION_TABLE_NAME");

Untuk alasan keamanan, fitur ini tidak tersedia di baris perintah, tetapi Anda dapat menggunakan variabel lingkungan
MIGRATION_VERSION untuk menyimpan namanya.

Kami sangat merekomendasikan untuk tidak menggunakan fitur ini. Rekomendasi adalah satu migrasi untuk satu skema.

Menjalankan Uji Unit

Uji unit dasar dapat dijalankan dengan:

vendor/bin/phpunit

Menjalankan uji basis data

Menjalankan uji integrasi memerlukan Anda untuk memiliki basis data yang aktif dan berjalan. Kami menyediakan docker-compose.yml dasar dan Anda
dapat menggunakannya untuk memulai basis data untuk pengujian.

Menjalankan basis data

docker-compose up -d postgres mysql mssql

Menjalankan uji

vendor/bin/phpunit
vendor/bin/phpunit tests/SqliteDatabase*
vendor/bin/phpunit tests/MysqlDatabase*
vendor/bin/phpunit tests/PostgresDatabase*
vendor/bin/phpunit tests/SqlServerDblibDatabase*
vendor/bin/phpunit tests/SqlServerSqlsrvDatabase*

Opsional Anda dapat mengatur host dan kata sandi yang digunakan oleh uji unit

export MYSQL_TEST_HOST=localhost     # default ke localhost
export MYSQL_PASSWORD=newpassword    # gunakan '.' jika ingin memiliki kata sandi null
export PSQL_TEST_HOST=localhost      # default ke localhost
export PSQL_PASSWORD=newpassword     # gunakan '.' jika ingin memiliki kata sandi null
export MSSQL_TEST_HOST=localhost     # default ke localhost
export MSSQL_PASSWORD=Pa55word
export SQLITE_TEST_HOST=/tmp/test.db      # default ke /tmp/test.db

Awesome-plugins/session

FlightPHP Sesi - Pengelola Sesi Berbasis File yang Ringan

Ini adalah plugin pengelola sesi berbasis file yang ringan untuk Flight PHP Framework. Ini memberikan solusi sederhana namun kuat untuk mengelola sesi, dengan fitur seperti pembacaan sesi non-blocking, enkripsi opsional, fungsionalitas auto-commit, dan mode uji untuk pengembangan. Data sesi disimpan dalam file, menjadikannya ideal untuk aplikasi yang tidak memerlukan basis data.

Jika Anda ingin menggunakan basis data, periksa plugin ghostff/session dengan banyak fitur yang sama tetapi dengan backend basis data.

Kunjungi repositori Github untuk kode sumber lengkap dan detailnya.

Instalasi

Instal plugin melalui Composer:

composer require flightphp/session

Penggunaan Dasar

Berikut adalah contoh sederhana tentang cara menggunakan plugin flightphp/session dalam aplikasi Flight Anda:

require 'vendor/autoload.php';

use flight\Session;

$app = Flight::app();

// Daftarkan layanan sesi
$app->register('session', Session::class);

// Contoh rute dengan penggunaan sesi
Flight::route('/login', function() {
    $session = Flight::session();
    $session->set('user_id', 123);
    $session->set('username', 'johndoe');
    $session->set('is_admin', false);

    echo $session->get('username'); // Menghasilkan: johndoe
    echo $session->get('preferences', 'default_theme'); // Menghasilkan: default_theme

    if ($session->get('user_id')) {
        Flight::json(['message' => 'Pengguna sudah login!', 'user_id' => $session->get('user_id')]);
    }
});

Flight::route('/logout', function() {
    $session = Flight::session();
    $session->clear(); // Hapus semua data sesi
    Flight::json(['message' => 'Berhasil keluar']);
});

Flight::start();

Poin Kunci

Konfigurasi

Anda dapat menyesuaikan pengelola sesi dengan melewatkan array opsi saat mendaftar:

$app->register('session', Session::class, [
    'save_path' => '/custom/path/to/sessions',         // Direktori untuk file sesi
    'encryption_key' => 'a-secure-32-byte-key-here',   // Aktifkan enkripsi (32 byte disarankan untuk AES-256-CBC)
    'auto_commit' => false,                            // Nonaktifkan auto-commit untuk kontrol manual
    'start_session' => true,                           // Mulai sesi secara otomatis (default: true)
    'test_mode' => false                               // Aktifkan mode uji untuk pengembangan
]);

Opsi Konfigurasi

Opsi Deskripsi Nilai Default
save_path Direktori tempat file sesi disimpan sys_get_temp_dir() . '/flight_sessions'
encryption_key Kunci untuk enkripsi AES-256-CBC (opsional) null (tidak ada enkripsi)
auto_commit Simpan data sesi secara otomatis saat dimatikan true
start_session Mulai sesi secara otomatis true
test_mode Jalankan dalam mode uji tanpa mempengaruhi sesi PHP false
test_session_id ID sesi kustom untuk mode uji (opsional) Dihasilkan secara acak jika tidak disetel

Penggunaan Lanjut

Manual Commit

Jika Anda menonaktifkan auto-commit, Anda harus melakukan commit perubahan secara manual:

$app->register('session', Session::class, ['auto_commit' => false]);

Flight::route('/update', function() {
    $session = Flight::session();
    $session->set('key', 'value');
    $session->commit(); // Simpan perubahan secara eksplisit
});

Keamanan Sesi dengan Enkripsi

Aktifkan enkripsi untuk data sensitif:

$app->register('session', Session::class, [
    'encryption_key' => 'your-32-byte-secret-key-here'
]);

Flight::route('/secure', function() {
    $session = Flight::session();
    $session->set('credit_card', '4111-1111-1111-1111'); // Dienkripsi secara otomatis
    echo $session->get('credit_card'); // Didekripsi saat diambil
});

Regenerasi Sesi

Regenerasi ID sesi untuk keamanan (misalnya, setelah login):

Flight::route('/post-login', function() {
    $session = Flight::session();
    $session->regenerate(); // ID baru, pertahankan data
    // ATAU
    $session->regenerate(true); // ID baru, hapus data lama
});

Contoh Middleware

Lindungi rute dengan otentikasi berbasis sesi:

Flight::route('/admin', function() {
    Flight::json(['message' => 'Selamat datang di panel admin']);
})->addMiddleware(function() {
    $session = Flight::session();
    if (!$session->get('is_admin')) {
        Flight::halt(403, 'Akses ditolak');
    }
});

Ini hanyalah contoh sederhana tentang cara menggunakan ini dalam middleware. Untuk contoh yang lebih mendalam, lihat dokumentasi middleware.

Metode

Kelas Session menyediakan metode ini:

Semua metode kecuali get() dan id() mengembalikan instance Session untuk chaining.

Mengapa Menggunakan Plugin Ini?

Detail Teknis

Berkontribusi

Kontribusi sangat diterima! Fork repositori, lakukan perubahan Anda, dan kirim pull request. Laporkan bug atau sarankan fitur melalui pelacak masalah Github.

Lisensi

Plugin ini dilisensikan di bawah Lisensi MIT. Lihat repositori Github untuk detailnya.

Awesome-plugins/runway

Jalur

Jalur adalah aplikasi CLI yang membantu Anda mengelola aplikasi Flight Anda. Ini dapat menghasilkan pengontrol, menampilkan semua rute, dan banyak lagi. Ini didasarkan pada pustaka adhocore/php-cli yang sangat baik.

Klik di sini untuk melihat kodenya.

Instalasi

Instal dengan composer.

composer require flightphp/runway

Konfigurasi Dasar

Kali pertama Anda menjalankan Jalur, itu akan memandu Anda melalui proses pengaturan dan membuat file konfigurasi .runway.json di akar proyek Anda. File ini akan berisi beberapa konfigurasi yang diperlukan agar Jalur berfungsi dengan baik.

Penggunaan

Jalur memiliki sejumlah perintah yang dapat Anda gunakan untuk mengelola aplikasi Flight Anda. Ada dua cara mudah untuk menggunakan Jalur.

  1. Jika Anda menggunakan proyek tulang, Anda dapat menjalankan php runway [command] dari akar proyek Anda.
  2. Jika Anda menggunakan Jalur sebagai paket yang diinstal melalui composer, Anda dapat menjalankan vendor/bin/runway [command] dari akar proyek Anda.

Untuk setiap perintah, Anda dapat melewatkan bendera --help untuk mendapatkan informasi lebih lanjut tentang cara menggunakan perintah tersebut.

php runway routes --help

Berikut adalah beberapa contoh:

Menghasilkan Pengontrol

Berdasarkan konfigurasi dalam file .runway.json Anda, lokasi default akan menghasilkan pengontrol untuk Anda di direktori app/controllers/.

php runway make:controller MyController

Menghasilkan Model Rekaman Aktif

Berdasarkan konfigurasi dalam file .runway.json Anda, lokasi default akan menghasilkan pengontrol untuk Anda di direktori app/records/.

php runway make:record users

Jika misalnya Anda memiliki tabel users dengan skema berikut: id, name, email, created_at, updated_at, sebuah file yang mirip dengan yang berikut akan dibuat di file app/records/UserRecord.php:

<?php

declare(strict_types=1);

namespace app\records;

/**
 * Kelas ActiveRecord untuk tabel users.
 * @link https://docs.flightphp.com/awesome-plugins/active-record
 * 
 * @property int $id
 * @property string $name
 * @property string $email
 * @property string $created_at
 * @property string $updated_at
 * // Anda juga bisa menambahkan hubungan di sini setelah Anda mendefinisikannya di array $relations
 * @property CompanyRecord $company Contoh hubungan
 */
class UserRecord extends \flight\ActiveRecord
{
    /**
     * @var array $relations Tetapkan hubungan untuk model
     *   https://docs.flightphp.com/awesome-plugins/active-record#relationships
     */
    protected array $relations = [];

    /**
     * Konstruktor
     * @param mixed $databaseConnection Koneksi ke database
     */
    public function __construct($databaseConnection)
    {
        parent::__construct($databaseConnection, 'users');
    }
}

Tampilkan Semua Rute

Ini akan menampilkan semua rute yang saat ini terdaftar dengan Flight.

php runway routes

Jika Anda ingin hanya melihat rute tertentu, Anda dapat melewatkan bendera untuk menyaring rute.

# Tampilkan hanya rute GET
php runway routes --get

# Tampilkan hanya rute POST
php runway routes --post

# dll.

Menyesuaikan Jalur

Jika Anda membuat paket untuk Flight, atau ingin menambahkan perintah kustom Anda sendiri ke dalam proyek Anda, Anda dapat melakukannya dengan membuat direktori src/commands/, flight/commands/, app/commands/, atau commands/ untuk proyek/paket Anda. Jika Anda memerlukan penyesuaian lebih lanjut, lihat bagian di bawah tentang Konfigurasi.

Untuk membuat perintah, Anda cukup memperluas kelas AbstractBaseCommand, dan menerapkan setidaknya metode __construct dan metode execute.

<?php

declare(strict_types=1);

namespace flight\commands;

class ExampleCommand extends AbstractBaseCommand
{
    /**
     * Konstruktor
     *
     * @param array<string,mixed> $config Konfigurasi JSON dari .runway-config.json
     */
    public function __construct(array $config)
    {
        parent::__construct('make:example', 'Buat contoh untuk dokumentasi', $config);
        $this->argument('<funny-gif>', 'Nama gif lucu');
    }

    /**
     * Menjalankan fungsi
     *
     * @return void
     */
    public function execute(string $controller)
    {
        $io = $this->app()->io();

        $io->info('Membuat contoh...');

        // Lakukan sesuatu di sini

        $io->ok('Contoh dibuat!');
    }
}

Lihat adhocore/php-cli Documentation untuk informasi lebih lanjut tentang cara membangun perintah kustom Anda sendiri ke dalam aplikasi Flight Anda!

Konfigurasi

Jika Anda perlu menyesuaikan konfigurasi untuk Jalur, Anda dapat membuat file .runway-config.json di akar proyek Anda. Di bawah ini adalah beberapa konfigurasi tambahan yang dapat Anda tetapkan:

{

    // Ini adalah tempat direktori aplikasi Anda berada
    "app_root": "app/",

    // Ini adalah direktori tempat file indeks akar Anda berada
    "index_root": "public/",

    // Ini adalah jalur ke akar proyek lainnya
    "root_paths": [
        "/home/user/different-project",
        "/var/www/another-project"
    ],

    // Jalur dasar kemungkinan besar tidak perlu dikonfigurasi, tapi ada di sini jika Anda menginginkannya
    "base_paths": {
        "/includes/libs/vendor", // jika Anda memiliki jalur yang sangat unik untuk direktori vendor Anda atau sesuatu
    },

    // Jalur akhir adalah lokasi dalam proyek untuk mencari file perintah
    "final_paths": {
        "src/diff-path/commands",
        "app/module/admin/commands",
    },

    // Jika Anda ingin hanya menambahkan jalur lengkap, silakan saja (absolut atau relatif terhadap akar proyek)
    "paths": [
        "/home/user/different-project/src/diff-path/commands",
        "/var/www/another-project/app/module/admin/commands",
        "app/my-unique-commands"
    ]
}

Awesome-plugins/tracy_extensions

Tracy Flight Panel Extensions

Ini adalah serangkaian ekstensi untuk membuat kerja dengan Flight sedikit lebih kaya.

Ini adalah Panel

Flight Bar

Dan setiap panel menampilkan informasi yang sangat berguna tentang aplikasi Anda!

Flight Data Flight Database Flight Request

Klik di sini untuk melihat kode.

Instalasi

Jalankan composer require flightphp/tracy-extensions --dev dan Anda sudah siap!

Konfigurasi

Ada sangat sedikit konfigurasi yang perlu Anda lakukan untuk memulai ini. Anda perlu memulai debugger Tracy sebelum menggunakan ini https://tracy.nette.org/en/guide:

<?php

use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;

// kode bootstrap
require __DIR__ . '/vendor/autoload.php';

Debugger::enable();
// Anda mungkin perlu menentukan lingkungan Anda dengan Debugger::enable(Debugger::DEVELOPMENT)

// jika Anda menggunakan koneksi database dalam aplikasi Anda, ada 
// pembungkus PDO yang diperlukan untuk digunakan HANYA DI PENGEMBANGAN (bukan produksi, tolong!)
// Ini memiliki parameter yang sama dengan koneksi PDO biasa
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// atau jika Anda melampirkan ini ke framework Flight
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// sekarang setiap kali Anda melakukan kueri, akan menangkap waktu, kueri, dan parameter

// Ini menghubungkan titik-titik
if(Debugger::$showBar === true) {
    // Ini perlu false atau Tracy tidak bisa benar-benar merender :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

// lebih banyak kode

Flight::start();

Konfigurasi Tambahan

Data Sesi

Jika Anda memiliki pengendali sesi kustom (seperti ghostff/session), Anda dapat meneruskan array data sesi apa pun ke Tracy dan secara otomatis akan mengeluarkannya untuk Anda. Anda mengirimkannya dengan kunci session_data di parameter kedua konstruktor TracyExtensionLoader.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

if(Debugger::$showBar === true) {
    // Ini perlu false atau Tracy tidak bisa benar-benar merender :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}

// rute dan hal-hal lain...

Flight::start();

Latte

Jika Anda telah menginstal Latte di proyek Anda, Anda dapat menggunakan panel Latte untuk menganalisis template Anda. Anda dapat meneruskan instance Latte ke konstruktor TracyExtensionLoader dengan kunci latte di parameter kedua.


use Latte\Engine;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('latte', Engine::class, [], function($latte) {
    $latte->setTempDirectory(__DIR__ . '/temp');

    // di sinilah Anda menambahkan Panel Latte ke Tracy
    $latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
});

if(Debugger::$showBar === true) {
    // Ini perlu false atau Tracy tidak bisa benar-benar merender :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

Awesome-plugins/tracy

Tracy

Tracy adalah penangan kesalahan yang luar biasa yang dapat digunakan dengan Flight. Ia memiliki sejumlah panel yang dapat membantu Anda dalam mendebug aplikasi Anda. Ini juga sangat mudah untuk diperluas dan menambahkan panel Anda sendiri. Tim Flight telah membuat beberapa panel khusus untuk proyek Flight dengan plugin flightphp/tracy-extensions.

Instalasi

Instal dengan composer. Dan Anda akan ingin menginstal ini tanpa versi dev karena Tracy dilengkapi dengan komponen penanganan kesalahan produksi.

composer require tracy/tracy

Konfigurasi Dasar

Ada beberapa opsi konfigurasi dasar untuk memulai. Anda dapat membaca lebih lanjut tentang mereka di Dokumentasi Tracy.


require 'vendor/autoload.php';

use Tracy\Debugger;

// Mengaktifkan Tracy
Debugger::enable();
// Debugger::enable(Debugger::DEVELOPMENT) // kadang-kadang Anda harus eksplisit (juga Debugger::PRODUCTION)
// Debugger::enable('23.75.345.200'); // Anda juga dapat menyediakan array alamat IP

// Di sinilah kesalahan dan pengecualian akan dicatat. Pastikan direktori ini ada dan dapat ditulisi.
Debugger::$logDirectory = __DIR__ . '/../log/';
Debugger::$strictMode = true; // tampilkan semua kesalahan
// Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // semua kesalahan kecuali pemberitahuan kadaluarsa
if (Debugger::$showBar) {
    $app->set('flight.content_length', false); // jika bilah Debugger terlihat, maka panjang konten tidak dapat diatur oleh Flight

    // Ini khusus untuk Ekstensi Tracy untuk Flight jika Anda telah menyertakannya
    // jika tidak, silakan komentari ini.
    new TracyExtensionLoader($app);
}

Tips Berguna

Saat Anda mendebug kode Anda, ada beberapa fungsi yang sangat berguna untuk mengeluarkan data untuk Anda.

Awesome-plugins/active_record

Flight Active Record

Sebuah active record adalah pemetaan entitas basis data ke objek PHP. Sederhananya, jika Anda memiliki tabel pengguna di basis data Anda, Anda dapat "menerjemahkan" sebuah baris di tabel tersebut ke dalam kelas User dan objek $user dalam kode Anda. Lihat contoh dasar.

Klik di sini untuk repositori di GitHub.

Contoh Dasar

Mari kita asumsikan Anda memiliki tabel berikut:

CREATE TABLE users (
    id INTEGER PRIMARY KEY, 
    name TEXT, 
    password TEXT 
);

Sekarang Anda dapat mengatur kelas baru untuk mewakili tabel ini:

/**
 * Sebuah kelas ActiveRecord biasanya tunggal
 * 
 * Sangat disarankan untuk menambahkan properti tabel sebagai komentar di sini
 * 
 * @property int    $id
 * @property string $name
 * @property string $password
 */ 
class User extends flight\ActiveRecord {
    public function __construct($database_connection)
    {
        // Anda dapat mengatur ini dengan cara ini
        parent::__construct($database_connection, 'users');
        // atau dengan cara ini
        parent::__construct($database_connection, null, [ 'table' => 'users']);
    }
}

Sekarang saksikan sihir terjadi!

// untuk sqlite
$database_connection = new PDO('sqlite:test.db'); // ini hanya untuk contoh, Anda mungkin akan menggunakan koneksi basis data yang nyata

// untuk mysql
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');

// atau mysqli
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// atau mysqli dengan pembuatan yang tidak berdasarkan objek
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');

$user = new User($database_connection);
$user->name = 'Bobby Tables';
$user->password = password_hash('some cool password');
$user->insert();
// atau $user->save();

echo $user->id; // 1

$user->name = 'Joseph Mamma';
$user->password = password_hash('some cool password again!!!');
$user->insert();
// tidak dapat menggunakan $user->save() di sini, atau itu akan mengira ini adalah pembaruan!

echo $user->id; // 2

Dan itu sangat mudah untuk menambahkan pengguna baru! Sekarang setelah ada baris pengguna di basis data, bagaimana cara Anda mengeluarkannya?

$user->find(1); // cari id = 1 dalam basis data dan kembalikan.
echo $user->name; // 'Bobby Tables'

Dan bagaimana jika Anda ingin menemukan semua pengguna?

$users = $user->findAll();

Bagaimana dengan kondisi tertentu?

$users = $user->like('name', '%mamma%')->findAll();

Lihat betapa menyenangkannya ini? Mari kita instal dan mulai!

Instalasi

Cukup instal dengan Composer

composer require flightphp/active-record 

Penggunaan

Ini dapat digunakan sebagai pustaka mandiri atau dengan Flight PHP Framework. Sepenuhnya terserah Anda.

Mandiri

Pastikan Anda mengoper koneksi PDO ke konstruktor.

$pdo_connection = new PDO('sqlite:test.db'); // ini hanya untuk contoh, Anda mungkin akan menggunakan koneksi basis data yang nyata

$User = new User($pdo_connection);

Tidak ingin selalu mengatur koneksi basis data Anda di konstruktor? Lihat Manajemen Koneksi Basis Data untuk ide lainnya!

Daftarkan sebagai metode dalam Flight

Jika Anda menggunakan Flight PHP Framework, Anda dapat mendaftarkan kelas ActiveRecord sebagai layanan, tetapi sejujurnya Anda tidak harus melakukannya.

Flight::register('user', 'User', [ $pdo_connection ]);

// kemudian Anda dapat menggunakannya seperti ini di pengontrol, fungsi, dll.

Flight::user()->find(1);

Metode runway

runway adalah alat CLI untuk Flight yang memiliki perintah khusus untuk pustaka ini.

# Penggunaan
php runway make:record database_table_name [class_name]

# Contoh
php runway make:record users

Ini akan membuat kelas baru di direktori app/records/ sebagai UserRecord.php dengan konten berikut:

<?php

declare(strict_types=1);

namespace app\records;

/**
 * Kelas ActiveRecord untuk tabel pengguna.
 * @link https://docs.flightphp.com/awesome-plugins/active-record
 *
 * @property int $id
 * @property string $username
 * @property string $email
 * @property string $password_hash
 * @property string $created_dt
 */
class UserRecord extends \flight\ActiveRecord
{
    /**
     * @var array $relations Menetapkan hubungan untuk model
     *   https://docs.flightphp.com/awesome-plugins/active-record#relationships
     */
    protected array $relations = [
        // 'relation_name' => [ self::HAS_MANY, 'RelatedClass', 'foreign_key' ],
    ];

    /**
     * Konstruktor
     * @param mixed $databaseConnection Koneksi ke basis data
     */
    public function __construct($databaseConnection)
    {
        parent::__construct($databaseConnection, 'users');
    }
}

Fungsi CRUD

find($id = null) : boolean|ActiveRecord

Mencari satu catatan dan menetapkannya pada objek saat ini. Jika Anda mengoper $id dari jenis tertentu, itu akan melakukan pencarian pada kunci utama dengan nilai itu. Jika tidak ada yang dipassing, ini hanya akan menemukan catatan pertama di tabel.

Selain itu, Anda dapat mengoper metode pembantu lainnya untuk menanyakan tabel Anda.

// mencari catatan dengan beberapa kondisi terlebih dahulu
$user->notNull('password')->orderBy('id DESC')->find();

// mencari catatan berdasarkan id tertentu
$id = 123;
$user->find($id);

findAll(): array<int,ActiveRecord>

Menemukan semua catatan di tabel yang Anda tentukan.

$user->findAll();

isHydrated(): boolean (v0.4.0)

Mengembalikan true jika catatan saat ini telah terhidrat (diambil dari database).

$user->find(1);
// jika catatan ditemukan dengan data...
$user->isHydrated(); // true

insert(): boolean|ActiveRecord

Menyisipkan catatan saat ini ke dalam basis data.

$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();
Kunci Utama berbasis Teks

Jika Anda memiliki kunci utama berbasis teks (seperti UUID), Anda dapat mengatur nilai kunci utama sebelum menyisipkan dalam dua cara.

$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'some-uuid';
$user->name = 'demo';
$user->password = md5('demo');
$user->insert(); // atau $user->save();

atau Anda dapat membiarkan kunci utama dihasilkan secara otomatis untuk Anda melalui peristiwa.

class User extends flight\ActiveRecord {
    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users', [ 'primaryKey' => 'uuid' ]);
        // Anda juga dapat mengatur primaryKey ini alih-alih array di atas.
        $this->primaryKey = 'uuid';
    }

    protected function beforeInsert(self $self) {
        $self->uuid = uniqid(); // atau sesuaikan bagaimana Anda perlu menghasilkan id unik Anda
    }
}

Jika Anda tidak mengatur kunci utama sebelum menyisipkan, itu akan diatur ke rowid dan basis data akan menghasilkan untuk Anda, tetapi tidak akan dipertahankan karena bidang itu mungkin tidak ada dalam tabel Anda. Inilah sebabnya mengapa disarankan untuk menggunakan peristiwa untuk menangani ini secara otomatis untuk Anda.

update(): boolean|ActiveRecord

Memperbarui catatan saat ini ke dalam basis data.

$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@example.com';
$user->update();

save(): boolean|ActiveRecord

Menyisipkan atau memperbarui catatan saat ini ke dalam basis data. Jika catatan memiliki id, itu akan memperbarui, jika tidak, itu akan menyisipkan.

$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->save();

Catatan: Jika Anda memiliki hubungan yang ditentukan dalam kelas, itu akan menyimpan hubungan tersebut secara rekursif juga jika telah ditentukan, diinstansiasi, dan memiliki data yang perlu diperbarui. (v0.4.0 dan lebih baru)

delete(): boolean

Menghapus catatan saat ini dari basis data.

$user->gt('id', 0)->orderBy('id desc')->find();
$user->delete();

Anda juga dapat menghapus beberapa catatan dengan mengeksekusi pencarian terlebih dahulu.

$user->like('name', 'Bob%')->delete();

dirty(array $dirty = []): ActiveRecord

Data "dirty" merujuk pada data yang telah diubah dalam sebuah catatan.

$user->greaterThan('id', 0)->orderBy('id desc')->find();

// tidak ada yang "dirty" pada titik ini.

$user->email = 'test@example.com'; // sekarang email dianggap "dirty" karena telah diubah.
$user->update();
// sekarang tidak ada data yang dirty karena telah diperbarui dan dipertahankan dalam basis data

$user->password = password_hash('newpassword'); // sekarang ini kotor
$user->dirty(); // melewatkan apa pun akan membersihkan semua entri yang kotor.
$user->update(); // tidak ada yang akan diperbarui karena tidak ada yang ditangkap sebagai kotor.

$user->dirty([ 'name' => 'sesuatu', 'password' => password_hash('password yang berbeda') ]);
$user->update(); // baik nama dan kata sandi diperbarui.

copyFrom(array $data): ActiveRecord (v0.4.0)

Ini adalah alias untuk metode dirty(). Ini sedikit lebih jelas tentang apa yang Anda lakukan.

$user->copyFrom([ 'name' => 'sesuatu', 'password' => password_hash('password yang berbeda') ]);
$user->update(); // baik nama dan kata sandi diperbarui.

isDirty(): boolean (v0.4.0)

Mengembalikan true jika catatan saat ini telah diubah.

$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@email.com';
$user->isDirty(); // true

reset(bool $include_query_data = true): ActiveRecord

Mereset catatan saat ini ke keadaan awalnya. Ini sangat baik digunakan dalam perilaku tipe loop. Jika Anda mengoper true, itu juga akan mereset data kueri yang digunakan untuk menemukan objek saat ini (perilaku default).

$users = $user->greaterThan('id', 0)->orderBy('id desc')->find();
$user_company = new UserCompany($pdo_connection);

foreach($users as $user) {
    $user_company->reset(); // mulai dengan slate yang bersih
    $user_company->user_id = $user->id;
    $user_company->company_id = $some_company_id;
    $user_company->insert();
}

getBuiltSql(): string (v0.4.1)

Setelah Anda menjalankan metode find(), findAll(), insert(), update(), atau save(), Anda dapat memperoleh SQL yang dibangun dan menggunakannya untuk tujuan debugging.

Metode Kuery SQL

select(string $field1 [, string $field2 ... ])

Anda dapat memilih hanya beberapa kolom di tabel jika Anda mau (ini lebih efisien pada tabel yang sangat lebar dengan banyak kolom)

$user->select('id', 'name')->find();

from(string $table)

Anda dapat memilih tabel lain juga! Untuk apa tidak?!

$user->select('id', 'name')->from('user')->find();

join(string $table_name, string $join_condition)

Anda bahkan dapat bergabung dengan tabel lain di basis data.

$user->join('contacts', 'contacts.user_id = users.id')->find();

where(string $where_conditions)

Anda dapat menetapkan beberapa argumen where kustom (Anda tidak dapat mengatur parameter dalam pernyataan where ini)

$user->where('id=1 AND name="demo"')->find();

Catatan Keamanan - Anda mungkin terdorong untuk melakukan sesuatu seperti $user->where("id = '{$id}' AND name = '{$name}'")->find();. Tolong JANGAN LAKUKAN INI!!! Ini rentan terhadap apa yang dikenal sebagai serangan SQL Injection. Ada banyak artikel di internet, silakan Google "sql injection attacks php" dan Anda akan menemukan banyak artikel tentang subjek ini. Cara yang tepat untuk menangani ini dengan perpustakaan ini adalah alih-alih metode where() ini, Anda akan melakukan sesuatu yang lebih seperti $user->eq('id', $id)->eq('name', $name)->find(); Jika Anda harus melakukan ini, pustaka PDO memiliki $pdo->quote($var) untuk menghindarinya untuk Anda. Hanya setelah Anda menggunakan quote() Anda dapat menggunakannya dalam pernyataan where().

group(string $group_by_statement)/groupBy(string $group_by_statement)

Kelompokkan hasil Anda berdasarkan kondisi tertentu.

$user->select('COUNT(*) as count')->groupBy('name')->findAll();

order(string $order_by_statement)/orderBy(string $order_by_statement)

Urutkan kueri yang dikembalikan dengan cara tertentu.

$user->orderBy('name DESC')->find();

limit(string $limit)/limit(int $offset, int $limit)

Batasi jumlah rekaman yang dikembalikan. Jika bilangan kedua diberikan, itu akan di-offset, batasi saja seperti di SQL.

$user->orderby('name DESC')->limit(0, 10)->findAll();

Kondisi WHERE

equal(string $field, mixed $value) / eq(string $field, mixed $value)

Di mana field = $value

$user->eq('id', 1)->find();

notEqual(string $field, mixed $value) / ne(string $field, mixed $value)

Di mana field <> $value

$user->ne('id', 1)->find();

isNull(string $field)

Di mana field IS NULL

$user->isNull('id')->find();

isNotNull(string $field) / notNull(string $field)

Di mana field IS NOT NULL

$user->isNotNull('id')->find();

greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)

Di mana field > $value

$user->gt('id', 1)->find();

lessThan(string $field, mixed $value) / lt(string $field, mixed $value)

Di mana field < $value

$user->lt('id', 1)->find();

greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)

Di mana field >= $value

$user->ge('id', 1)->find();

lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)

Di mana field <= $value

$user->le('id', 1)->find();

like(string $field, mixed $value) / notLike(string $field, mixed $value)

Di mana field LIKE $value atau field NOT LIKE $value

$user->like('name', 'de')->find();

in(string $field, array $values) / notIn(string $field, array $values)

Di mana field IN($value) atau field NOT IN($value)

$user->in('id', [1, 2])->find();

between(string $field, array $values)

Di mana field BETWEEN $value AND $value1

$user->between('id', [1, 2])->find();

Kondisi OR

Dimungkinkan untuk membungkus kondisi Anda dalam pernyataan OR. Ini dilakukan dengan metode startWrap() dan endWrap() atau dengan mengisi parameter ke-3 dari kondisi setelah bidang dan nilai.

// Metode 1
$user->eq('id', 1)->startWrap()->eq('name', 'demo')->or()->eq('name', 'test')->endWrap('OR')->find();
// Ini akan dievaluasi menjadi `id = 1 AND (name = 'demo' OR name = 'test')`

// Metode 2
$user->eq('id', 1)->eq('name', 'demo', 'OR')->find();
// Ini akan dievaluasi menjadi `id = 1 OR name = 'demo'`

Hubungan

Anda dapat mengatur beberapa jenis hubungan menggunakan pustaka ini. Anda dapat mengatur hubungan satu->banyak dan satu->satu antara tabel. Ini membutuhkan pengaturan ekstra dalam kelas sebelumnya.

Mengatur array $relations tidaklah sulit, tetapi menebak sintaks yang benar bisa membingungkan.

protected array $relations = [
    // Anda dapat memberi nama kuncinya dengan cara apa pun yang Anda suka. Nama ActiveRecord mungkin bagus. Mis: user, contact, client
    'user' => [
        // wajib
        // self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
        self::HAS_ONE, // ini adalah jenis hubungan

        // wajib
        'Some_Class', // ini adalah kelas ActiveRecord "lain" yang akan direferensikan

        // wajib
        // tergantung pada jenis hubungan
        // self::HAS_ONE = kunci asing yang mereferensikan gabungan
        // self::HAS_MANY = kunci asing yang mereferensikan gabungan
        // self::BELONGS_TO = kunci lokal yang mereferensikan gabungan
        'local_or_foreign_key',
        // hanya FYI, ini juga hanya bergabung dengan kunci utama model "lain"

        // opsional
        [ 'eq' => [ 'client_id', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // kondisi tambahan yang Anda inginkan ketika menggabungkan hubungan
        // $record->eq('client_id', 5)->select('COUNT(*) as count')->limit(5))

        // opsional
        'nama_referensi_kembali' // ini jika Anda ingin merujuk kembali hubungan ini kembali ke dirinya sendiri Mis: $user->contact->user;
    ];
]
class User extends ActiveRecord{
    protected array $relations = [
        'contacts' => [ self::HAS_MANY, Contact::class, 'user_id' ],
        'contact' => [ self::HAS_ONE, Contact::class, 'user_id' ],
    ];

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }
}

class Contact extends ActiveRecord{
    protected array $relations = [
        'user' => [ self::BELONGS_TO, User::class, 'user_id' ],
        'user_with_backref' => [ self::BELONGS_TO, User::class, 'user_id', [], 'contact' ],
    ];
    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'contacts');
    }
}

Sekarang kita telah mengatur referensi sehingga kita dapat menggunakannya dengan sangat mudah!

$user = new User($pdo_connection);

// cari pengguna terbaru.
$user->notNull('id')->orderBy('id desc')->find();

// ambil kontak dengan menggunakan hubungan:
foreach($user->contacts as $contact) {
    echo $contact->id;
}

// atau kita bisa pergi ke arah yang lain.
$contact = new Contact();

// cari satu kontak
$contact->find();

// dapatkan pengguna dengan menggunakan hubungan:
echo $contact->user->name; // ini adalah nama pengguna

Keren kan?

Mengatur Data Kustom

Terkadang Anda mungkin perlu melampirkan sesuatu yang unik pada ActiveRecord Anda seperti perhitungan khusus yang mungkin lebih mudah untuk dilampirkan pada objek yang kemudian akan diteruskan ke template.

setCustomData(string $field, mixed $value)

Anda melampirkan data kustom dengan metode setCustomData().

$user->setCustomData('page_view_count', $page_view_count);

Dan kemudian Anda cukup merujuknya seperti properti objek biasa.

echo $user->page_view_count;

Peristiwa

Satu fitur luar biasa lainnya tentang pustaka ini adalah tentang peristiwa. Peristiwa dipicu pada saat tertentu berdasarkan metode tertentu yang Anda panggil. Mereka sangat membantu dalam menyiapkan data untuk Anda secara otomatis.

onConstruct(ActiveRecord $ActiveRecord, array &config)

Ini sangat membantu jika Anda perlu mengatur koneksi default atau sesuatu seperti itu.

// index.php atau bootstrap.php
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);

//
//
//

// User.php
class User extends flight\ActiveRecord {

    protected function onConstruct(self $self, array &$config) { // jangan lupa referensi &
        // Anda bisa melakukan ini untuk secara otomatis mengatur koneksi
        $config['connection'] = Flight::db();
        // atau ini
        $self->transformAndPersistConnection(Flight::db());

        // Anda juga dapat mengatur nama tabel dengan cara ini.
        $config['table'] = 'users';
    } 
}

beforeFind(ActiveRecord $ActiveRecord)

Ini mungkin hanya berguna jika Anda perlu manipulasi kueri setiap kali.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeFind(self $self) {
        // selalu jalankan id >= 0 jika itu adalah yang Anda inginkan
        $self->gte('id', 0); 
    } 
}

afterFind(ActiveRecord $ActiveRecord)

Yang ini mungkin lebih berguna jika Anda selalu perlu menjalankan beberapa logika setiap kali catatan ini diambil. Apakah Anda perlu mendekripsi sesuatu? Apakah Anda perlu menjalankan kueri hitung kustom setiap kali (tidak efisien tetapi tidak apa-apa)?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterFind(self $self) {
        // mendekripsi sesuatu
        $self->secret = yourDecryptFunction($self->secret, $some_key);

        // mungkin menyimpan sesuatu yang kustom seperti kueri???
        $self->setCustomData('view_count', $self->select('COUNT(*) count')->from('user_views')->eq('user_id', $self->id)['count']); 
    } 
}

beforeFindAll(ActiveRecord $ActiveRecord)

Ini mungkin hanya berguna jika Anda perlu manipulasi kueri setiap kali.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeFindAll(self $self) {
        // selalu jalankan id >= 0 jika itu adalah yang Anda inginkan
        $self->gte('id', 0); 
    } 
}

afterFindAll(array<int,ActiveRecord> $results)

Mirip dengan afterFind() tetapi Anda bisa melakukannya ke semua catatan!

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterFindAll(array $results) {

        foreach($results as $self) {
            // lakukan sesuatu yang keren seperti afterFind()
        }
    } 
}

beforeInsert(ActiveRecord $ActiveRecord)

Sangat berguna jika Anda perlu menetapkan beberapa nilai default setiap kali.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeInsert(self $self) {
        // menetapkan beberapa default yang baik
        if(!$self->created_date) {
            $self->created_date = gmdate('Y-m-d');
        }

        if(!$self->password) {
            $self->password = password_hash((string) microtime(true));
        }
    } 
}

afterInsert(ActiveRecord $ActiveRecord)

Mungkin Anda memiliki skenario untuk mengubah data setelah disisipkan?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterInsert(self $self) {
        // Anda melakukan Anda
        Flight::cache()->set('most_recent_insert_id', $self->id);
        // atau apa pun....
    } 
}

beforeUpdate(ActiveRecord $ActiveRecord)

Sangat berguna jika Anda perlu menetapkan beberapa nilai default setiap kali ada pembaruan.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeInsert(self $self) {
        // menetapkan beberapa default yang baik
        if(!$self->updated_date) {
            $self->updated_date = gmdate('Y-m-d');
        }
    } 
}

afterUpdate(ActiveRecord $ActiveRecord)

Mungkin Anda memiliki skenario untuk mengubah data setelah diperbarui?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterInsert(self $self) {
        // Anda melakukan Anda
        Flight::cache()->set('most_recently_updated_user_id', $self->id);
        // atau apa pun....
    } 
}

beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)

Ini berguna jika Anda ingin peristiwa terjadi baik saat sisip atau pembaruan terjadi. Saya akan menghemat penjelasan panjangnya, tetapi saya yakin Anda bisa menebak apa itu.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeSave(self $self) {
        $self->last_updated = gmdate('Y-m-d H:i:s');
    } 
}

beforeDelete(ActiveRecord $ActiveRecord)/afterDelete(ActiveRecord $ActiveRecord)

Tidak yakin apa yang ingin Anda lakukan di sini, tetapi tidak ada penilaian di sini! Ayo lakukan!

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeDelete(self $self) {
        echo 'Dia adalah seorang prajurit yang berani... :cry-face:';
    } 
}

Manajemen Koneksi Basis Data

Ketika Anda menggunakan pustaka ini, Anda dapat mengatur koneksi basis data dengan beberapa cara berbeda. Anda dapat mengatur koneksi di konstruktor, Anda dapat mengatur melalui variabel konfigurasi $config['connection'] atau Anda dapat mengatur melalui setDatabaseConnection() (v0.4.1).

$pdo_connection = new PDO('sqlite:test.db'); // untuk contoh
$user = new User($pdo_connection);
// atau
$user = new User(null, [ 'connection' => $pdo_connection ]);
// atau
$user = new User();
$user->setDatabaseConnection($pdo_connection);

Jika Anda ingin menghindari selalu mengatur $database_connection setiap kali Anda memanggil record aktif, ada cara untuk mengatasinya!

// index.php atau bootstrap.php
// Set ini sebagai kelas terdaftar di Flight
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);

// User.php
class User extends flight\ActiveRecord {

    public function __construct(array $config = [])
    {
        $database_connection = $config['connection'] ?? Flight::db();
        parent::__construct($database_connection, 'users', $config);
    }
}

// Dan sekarang, tidak ada argumen yang diperlukan!
$user = new User();

Catatan: Jika Anda berencana untuk melakukan pengujian unit, melakukan ini dapat menambah beberapa tantangan untuk pengujian unit, tetapi secara keseluruhan karena Anda dapat menyuntikkan koneksi Anda dengan setDatabaseConnection() atau $config['connection'], ini tidak terlalu buruk.

Jika Anda perlu menyegarkan koneksi basis data, misalnya jika Anda menjalankan skrip CLI yang berjalan lama dan perlu menyegarkan koneksi setiap saat, Anda dapat mengatur ulang koneksi dengan $your_record->setDatabaseConnection($pdo_connection).

Kontribusi

Silakan lakukan. :D

Pengaturan

Saat Anda berkontribusi, pastikan Anda menjalankan composer test-coverage untuk mempertahankan 100% cakupan pengujian (ini bukan cakupan pengujian unit yang sebenarnya, lebih seperti pengujian integrasi).

Juga pastikan Anda menjalankan composer beautify dan composer phpcs untuk memperbaiki kesalahan linting.

Lisensi

MIT

Awesome-plugins/latte

Latte

Latte adalah mesin templating dengan fitur lengkap yang sangat mudah digunakan dan terasa lebih dekat dengan sintaks PHP dibandingkan Twig atau Smarty. Ini juga sangat mudah untuk diperluas dan menambahkan filter serta fungsi Anda sendiri.

Instalasi

Instal dengan composer.

composer require latte/latte

Konfigurasi Dasar

Ada beberapa opsi konfigurasi dasar untuk memulai. Anda dapat membaca lebih lanjut tentang mereka di Dokumentasi Latte.


use Latte\Engine as LatteEngine;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('latte', LatteEngine::class, [], function(LatteEngine $latte) use ($app) {

    // Di sinilah Latte akan menyimpan cache untuk template Anda untuk mempercepat proses
    // Satu hal menarik tentang Latte adalah bahwa ia secara otomatis menyegarkan 
    // cache saat Anda membuat perubahan pada template Anda!
    $latte->setTempDirectory(__DIR__ . '/../cache/');

    // Beri tahu Latte di mana direktori root untuk tampilan Anda akan berada.
    // $app->get('flight.views.path') diatur di file config.php
    //   Anda juga bisa melakukan sesuatu seperti `__DIR__ . '/../views/'`
    $latte->setLoader(new \Latte\Loaders\FileLoader($app->get('flight.views.path')));
});

Contoh Layout Sederhana

Ini adalah contoh sederhana dari file layout. Ini adalah file yang akan digunakan untuk membungkus semua tampilan Anda yang lain.

<!-- app/views/layout.latte -->
<!doctype html>
<html lang="en">
    <head>
        <title>{$title ? $title . ' - '}Aplikasi Saya</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <nav>
                <!-- elemen navigasi Anda di sini -->
            </nav>
        </header>
        <div id="content">
            <!-- Ini adalah keajaiban yang terjadi di sini -->
            {block content}{/block}
        </div>
        <div id="footer">
            &copy; Hak Cipta
        </div>
    </body>
</html>

Dan sekarang kita memiliki file Anda yang akan dirender di dalam blok konten tersebut:

<!-- app/views/home.latte -->
<!-- Ini memberi tahu Latte bahwa file ini "di dalam" file layout.latte -->
{extends layout.latte}

<!-- Ini adalah konten yang akan dirender di dalam layout di dalam blok konten -->
{block content}
    <h1>Halaman Utama</h1>
    <p>Selamat datang di aplikasi saya!</p>
{/block}

Kemudian saat Anda pergi untuk merender ini di dalam fungsi atau kontroler Anda, Anda akan melakukan sesuatu seperti ini:

// rute sederhana
Flight::route('/', function () {
    Flight::latte()->render('home.latte', [
        'title' => 'Halaman Utama'
    ]);
});

// atau jika Anda menggunakan kontroler
Flight::route('/', [HomeController::class, 'index']);

// HomeController.php
class HomeController
{
    public function index()
    {
        Flight::latte()->render('home.latte', [
            'title' => 'Halaman Utama'
        ]);
    }
}

Lihat Dokumentasi Latte untuk informasi lebih lanjut tentang cara menggunakan Latte untuk potensi penuh!

Awesome-plugins/awesome_plugins

Plugin Luar Biasa

Flight sangat dapat diperluas. Ada sejumlah plugin yang dapat digunakan untuk menambahkan fungsionalitas ke aplikasi Flight Anda. Beberapa didukung secara resmi oleh Tim Flight dan lainnya adalah pustaka mikro/lite untuk membantu Anda memulai.

Dokumentasi API

Dokumentasi API sangat penting untuk API apa pun. Ini membantu pengembang memahami cara berinteraksi dengan API Anda dan apa yang diharapkan sebagai balasan. Ada beberapa alat yang tersedia untuk membantu Anda menghasilkan dokumentasi API untuk Proyek Flight Anda.

Autentikasi/Otorisasi

Autentikasi dan Otorisasi sangat penting untuk setiap aplikasi yang memerlukan kontrol untuk siapa yang dapat mengakses apa.

Cache

Cache adalah cara yang bagus untuk mempercepat aplikasi Anda. Ada sejumlah pustaka caching yang dapat digunakan dengan Flight.

CLI

Aplikasi CLI adalah cara yang bagus untuk berinteraksi dengan aplikasi Anda. Anda dapat menggunakannya untuk menghasilkan pengontrol, menampilkan semua rute, dan lainnya.

Cookies

Cookies adalah cara yang bagus untuk menyimpan sedikit data di sisi klien. Ini dapat digunakan untuk menyimpan preferensi pengguna, pengaturan aplikasi, dan lainnya.

Debugging

Debugging sangat penting ketika Anda sedang mengembangkan di lingkungan lokal Anda. Ada beberapa plugin yang dapat meningkatkan pengalaman debugging Anda.

Basis Data

Basis data adalah inti dari sebagian besar aplikasi. Ini adalah cara Anda menyimpan dan mengambil data. Beberapa pustaka basis data hanyalah pembungkus untuk menulis kueri dan beberapa adalah ORM lengkap.

Enkripsi

Enkripsi sangat penting untuk aplikasi apa pun yang menyimpan data sensitif. Mengenskripsi dan mendekripsi data tidak terlalu sulit, tetapi menyimpan kunci enkripsi dengan benar dapat menjadi sulit. Hal yang paling penting adalah tidak pernah menyimpan kunci enkripsi Anda di direktori publik atau mengkomitnya ke repositori kode Anda.

Antrian Pekerjaan

Antrian pekerjaan sangat membantu untuk memproses tugas secara asinkron. Ini dapat berupa mengirim email, memproses gambar, atau apa pun yang tidak perlu dilakukan secara waktu nyata.

Sesi

Sesi tidak terlalu berguna untuk API, tetapi untuk membangun aplikasi web, sesi dapat sangat penting untuk memelihara status dan informasi login.

Templating

Templating adalah inti dari setiap aplikasi web dengan UI. Ada sejumlah mesin templating yang dapat digunakan dengan Flight.

Kontribusi

Apakah Anda memiliki plugin yang ingin Anda bagikan? Kirim permohonan tarik untuk menambahkannya ke daftar!

Media

Media

Kami telah berusaha untuk melacak apa yang kami bisa tentang berbagai jenis media di internet seputar Flight. Lihat di bawah untuk berbagai sumber yang dapat Anda gunakan untuk mempelajari lebih lanjut tentang Flight.

Artikel dan Tulisannya

Video dan Tutorial

Examples

Butuh permulaan yang cepat?

Anda memiliki dua opsi untuk memulai proyek Flight baru:

Contoh yang disumbangkan oleh komunitas:

Butuh Beberapa Inspirasi?

Meskipun ini tidak secara resmi disponsori oleh Tim Flight, ini bisa memberi Anda ide tentang cara menyusun proyek Anda sendiri yang dibangun dengan Flight!

Ingin Membagikan Contoh Anda Sendiri?

Jika Anda memiliki proyek yang ingin Anda bagikan, silakan kirim permintaan tarik untuk menambahkannya ke daftar ini!

Install/install

Instalasi

Unduh berkas

Pastikan Anda memiliki PHP terinstal di sistem Anda. Jika tidak, klik di sini untuk petunjuk tentang cara menginstalnya di sistem Anda.

Jika Anda menggunakan Composer, Anda dapat menjalankan perintah berikut:

composer require flightphp/core

ATAU Anda dapat mengunduh berkas langsung dan mengekstraknya ke direktori web Anda.

Konfigurasikan Server Web Anda

Server Pengembangan PHP Bawaan

Ini adalah cara termudah untuk memulai dan menjalankan aplikasi. Anda dapat menggunakan server bawaan untuk menjalankan aplikasi Anda dan bahkan menggunakan SQLite untuk basis data (selama sqlite3 terinstal di sistem Anda) dan tidak memerlukan banyak hal! Jalankan perintah berikut setelah PHP terinstal:

php -S localhost:8000

Kemudian buka browser Anda dan pergi ke http://localhost:8000.

Jika Anda ingin membuat akar dokumen proyek Anda ke direktori yang berbeda (Mis: proyek Anda adalah ~/myproject, tetapi akar dokumen Anda adalah ~/myproject/public/), Anda dapat menjalankan perintah berikut setelah Anda berada di direktori ~/myproject:

php -S localhost:8000 -t public/

Kemudian buka browser Anda dan pergi ke http://localhost:8000.

Apache

Pastikan Apache sudah terinstal di sistem Anda. Jika tidak, cari tahu cara menginstal Apache di sistem Anda.

Untuk Apache, edit file .htaccess Anda dengan yang berikut:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

Catatan: Jika Anda perlu menggunakan flight di subdirektori, tambahkan baris RewriteBase /subdir/ tepat setelah RewriteEngine On.

Catatan: Jika Anda ingin melindungi semua berkas server, seperti berkas db atau env. Tempatkan ini di file .htaccess Anda:

RewriteEngine On
RewriteRule ^(.*)$ index.php

Nginx

Pastikan Nginx sudah terinstal di sistem Anda. Jika tidak, cari tahu cara menginstal Nginx di sistem Anda.

Untuk Nginx, tambahkan yang berikut ke deklarasi server Anda:

server {
  location / {
    try_files $uri $uri/ /index.php;
  }
}

Buat file index.php Anda

<?php

// Jika Anda menggunakan Composer, persyaratan autoloader.
require 'vendor/autoload.php';
// jika Anda tidak menggunakan Composer, muat kerangka kerja secara langsung
// require 'flight/Flight.php';

// Kemudian tetapkan rute dan tetapkan fungsi untuk menangani permintaan.
Flight::route('/', function () {
  echo 'hello world!';
});

// Akhirnya, mulai kerangka kerja.
Flight::start();

Instalasi PHP

Jika Anda sudah memiliki php terinstal di sistem Anda, silakan lewati petunjuk ini dan lanjut ke bagian unduh

macOS

Menginstal PHP menggunakan Homebrew

  1. Instal Homebrew (jika belum terinstal):

    • Buka Terminal dan jalankan:
      /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  2. Instal PHP:

    • Instal versi terbaru:
      brew install php
    • Untuk menginstal versi tertentu, misalnya, PHP 8.1:
      brew tap shivammathur/php
      brew install shivammathur/php/php@8.1
  3. Beralih antara versi PHP:

    • Lepaskan versi saat ini dan tautkan versi yang diinginkan:
      brew unlink php
      brew link --overwrite --force php@8.1
    • Verifikasi versi yang diinstal:
      php -v

Windows 10/11

Menginstal PHP secara manual

  1. Unduh PHP:

    • Kunjungi PHP untuk Windows dan unduh versi terbaru atau versi tertentu (misalnya, 7.4, 8.0) sebagai berkas zip yang tidak aman untuk thread.
  2. Ekstrak PHP:

    • Ekstrak berkas zip yang diunduh ke C:\php.
  3. Tambahkan PHP ke PATH sistem:

    • Masuk ke Properti Sistem > Variabel Lingkungan.
    • Di bawah variabel sistem, cari Path dan klik Edit.
    • Tambahkan jalur C:\php (atau di mana pun Anda mengekstrak PHP).
    • Klik OK untuk menutup semua jendela.
  4. Konfigurasi PHP:

    • Salin php.ini-development ke php.ini.
    • Edit php.ini untuk mengonfigurasi PHP sesuai kebutuhan (misalnya, mengatur extension_dir, mengaktifkan ekstensi).
  5. Verifikasi instalasi PHP:

    • Buka Command Prompt dan jalankan:
      php -v

Menginstal Beberapa Versi PHP

  1. Ulangi langkah di atas untuk setiap versi, menempatkan masing-masing di direktori terpisah (misalnya, C:\php7, C:\php8).

  2. Beralih antara versi dengan menyesuaikan variabel PATH sistem untuk menunjuk ke direktori versi yang diinginkan.

Ubuntu (20.04, 22.04, dll.)

Menginstal PHP menggunakan apt

  1. Perbarui daftar paket:

    • Buka Terminal dan jalankan:
      sudo apt update
  2. Instal PHP:

    • Instal versi PHP terbaru:
      sudo apt install php
    • Untuk menginstal versi tertentu, misalnya, PHP 8.1:
      sudo apt install php8.1
  3. Instal modul tambahan (opsional):

    • Misalnya, untuk menginstal dukungan MySQL:
      sudo apt install php8.1-mysql
  4. Beralih antara versi PHP:

    • Gunakan update-alternatives:
      sudo update-alternatives --set php /usr/bin/php8.1
  5. Verifikasi versi yang diinstal:

    • Jalankan:
      php -v

Rocky Linux

Menginstal PHP menggunakan yum/dnf

  1. Aktifkan repositori EPEL:

    • Buka Terminal dan jalankan:
      sudo dnf install epel-release
  2. Instal repositori Remi:

    • Jalankan:
      sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
      sudo dnf module reset php
  3. Instal PHP:

    • Untuk menginstal versi default:
      sudo dnf install php
    • Untuk menginstal versi tertentu, misalnya, PHP 7.4:
      sudo dnf module install php:remi-7.4
  4. Beralih antara versi PHP:

    • Gunakan perintah modul dnf:
      sudo dnf module reset php
      sudo dnf module enable php:remi-8.0
      sudo dnf install php
  5. Verifikasi versi yang diinstal:

    • Jalankan:
      php -v

Catatan Umum

Guides

Panduan

Flight PHP dirancang untuk menjadi sederhana namun kuat, dan panduan kami akan membantu Anda membangun aplikasi dunia nyata langkah demi langkah. Tutorial praktis ini membawa Anda melalui proyek lengkap untuk menunjukkan bagaimana Flight dapat digunakan secara efektif.

Panduan Resmi

Membangun Blog

Pelajari cara membuat aplikasi blog fungsional dengan Flight PHP. Panduan ini membawa Anda melalui:

Tutorial ini sempurna untuk pemula yang ingin melihat bagaimana semua bagian cocok bersama dalam aplikasi nyata.

Panduan Tidak Resmi

Meskipun panduan ini tidak secara resmi dikelola oleh tim Flight, mereka adalah sumber daya berharga yang dibuat oleh komunitas. Mereka mencakup berbagai topik dan kasus penggunaan, memberikan wawasan tambahan tentang penggunaan Flight PHP.

Membuat API RESTful dengan Flight Framework

Panduan ini membawa Anda melalui cara membuat API RESTful menggunakan framework Flight PHP. Ini mencakup dasar-dasar pengaturan API, mendefinisikan rute, dan mengembalikan respons JSON.

Membangun Blog Sederhana

Panduan ini membawa Anda melalui cara membuat blog dasar menggunakan framework Flight PHP. Sebenarnya, ini memiliki 2 bagian: satu untuk mencakup dasar-dasar dan yang lainnya untuk mencakup topik yang lebih maju dan perbaikan untuk blog yang siap digunakan di produksi.

Membangun API Pokémon di PHP: Panduan untuk Pemula

Panduan menyenangkan ini membawa Anda melalui cara membuat API Pokémon sederhana menggunakan Flight PHP. Ini mencakup dasar-dasar pengaturan API, mendefinisikan rute, dan mengembalikan respons JSON.

Kontribusi

Ada ide untuk panduan? Menemukan kesalahan? Kami menyambut kontribusi! Panduan kami dikelola dalam repositori dokumentasi FlightPHP.

Jika Anda telah membuat sesuatu yang menarik dengan Flight dan ingin membagikannya sebagai panduan, silakan ajukan permintaan tarik. Membagikan pengetahuan Anda membantu komunitas Flight berkembang.

Mencari Dokumentasi API?

Jika Anda mencari informasi spesifik tentang fitur dan metode inti dari Flight, periksa bagian Belajar dari dokumentasi kami.