Learn/flight_vs_laravel
Flight vs Laravel
Apa itu Laravel?
Laravel adalah framework lengkap yang memiliki semua fitur lengkap dan ekosistem yang berfokus pada pengembang yang luar biasa, tetapi dengan biaya dalam hal performa dan kompleksitas. Tujuan Laravel adalah agar pengembang memiliki tingkat produktivitas tertinggi dan membuat tugas-tugas umum menjadi mudah. Laravel adalah pilihan yang bagus untuk pengembang yang ingin membangun aplikasi web perusahaan yang lengkap. Itu datang dengan beberapa trade-off, khususnya dalam hal performa dan kompleksitas. Belajar dasar-dasar Laravel bisa mudah, tetapi mencapai kefasihan dalam framework ini bisa memakan waktu.
Ada juga begitu banyak modul Laravel sehingga pengembang sering merasa satu-satunya cara untuk menyelesaikan masalah adalah melalui modul-modul ini, padahal sebenarnya Anda bisa saja menggunakan pustaka lain atau menulis kode sendiri.
Kelebihan dibandingkan Flight
- Laravel memiliki ekosistem besar dari pengembang dan modul yang dapat digunakan untuk menyelesaikan masalah umum.
- Laravel memiliki ORM lengkap yang dapat digunakan untuk berinteraksi dengan database Anda.
- Laravel memiliki jumlah dokumentasi dan tutorial yang gila yang dapat digunakan untuk mempelajari framework. Itu bisa bagus untuk mendalami detail halus atau buruk karena ada begitu banyak yang harus dibaca.
- Laravel memiliki sistem autentikasi bawaan yang dapat digunakan untuk mengamankan aplikasi Anda.
- Laravel memiliki podcast, konferensi, pertemuan, video, dan sumber daya lain yang dapat digunakan untuk mempelajari framework.
- Laravel ditujukan untuk pengembang berpengalaman yang ingin membangun aplikasi web perusahaan yang lengkap.
Kekurangan dibandingkan Flight
- Laravel memiliki lebih banyak hal yang terjadi di balik layar dibandingkan Flight. Ini datang dengan biaya dramatis dalam hal performa. Lihat benchmark TechEmpower untuk informasi lebih lanjut.
- Flight ditujukan untuk pengembang yang ingin membangun aplikasi web ringan, cepat, dan mudah digunakan.
- Flight ditujukan untuk kesederhanaan dan kemudahan penggunaan.
- Salah satu fitur inti Flight adalah bahwa ia berusaha sebaik mungkin untuk mempertahankan kompatibilitas mundur. Laravel menyebabkan banyak frustrasi antara versi mayor.
- Flight ditujukan untuk pengembang yang baru memasuki dunia framework untuk pertama kalinya.
- Flight tidak memiliki dependensi, sedangkan Laravel memiliki jumlah dependensi yang mengerikan
- Flight juga bisa melakukan aplikasi tingkat perusahaan, tetapi tidak memiliki kode boilerplate sebanyak Laravel. Ini juga akan membutuhkan lebih banyak disiplin dari pengembang untuk menjaga semuanya terorganisir dan terstruktur dengan baik.
- Flight memberikan pengembang lebih banyak kendali atas aplikasi, sedangkan Laravel memiliki banyak sihir di balik layar yang bisa menjengkelkan.
Learn/migrating_to_v3
Migrasi ke v3
Kompatibilitas mundur sebagian besar telah dipertahankan, tetapi ada beberapa perubahan yang harus Anda ketahui saat migrasi dari v2 ke v3. Ada beberapa perubahan yang bertentangan terlalu banyak dengan pola desain sehingga beberapa penyesuaian harus dilakukan.
Perilaku Penyanggaan Output
v3.5.0
Penyanggaan output adalah proses di mana output yang dihasilkan oleh skrip PHP disimpan dalam penyangga (internal ke PHP) sebelum dikirim ke klien. Ini memungkinkan Anda untuk memodifikasi output sebelum dikirim ke klien.
Dalam aplikasi MVC, Controller adalah "manajer" dan mengelola apa yang dilakukan oleh view. Memiliki output yang dihasilkan di luar controller (atau dalam kasus Flight terkadang fungsi anonim) merusak pola MVC. Perubahan ini dilakukan untuk lebih selaras dengan pola MVC dan membuat framework lebih dapat diprediksi serta lebih mudah digunakan.
Di v2, penyanggaan output ditangani dengan cara yang tidak secara konsisten menutup penyangga outputnya sendiri, yang membuat pengujian unit dan streaming lebih sulit. Bagi sebagian besar pengguna, perubahan ini mungkin tidak memengaruhi Anda secara aktual. Namun, jika Anda mencetak konten di luar callable dan controller (misalnya dalam hook), kemungkinan Anda akan mengalami masalah. Mencetak konten dalam hook, dan sebelum framework benar-benar dieksekusi mungkin pernah berhasil di masa lalu, tetapi tidak akan berhasil ke depannya.
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 akan baik-baik saja
echo '<p>Kalimat Hello World ini disajikan 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
Bisakah Anda tetap mempertahankan kode lama Anda apa adanya tanpa melakukan penulisan ulang agar kompatibel 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 depannya. 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
v3.7.0
Jika Anda secara langsung memanggil metode statis untuk Dispatcher
seperti Dispatcher::invokeMethod()
, Dispatcher::execute()
, dll.
Anda perlu memperbarui kode Anda agar tidak secara langsung memanggil metode-metode ini. Dispatcher
telah diubah menjadi lebih berorientasi objek sehingga
Container Injeksi Dependensi dapat digunakan dengan lebih mudah. Jika Anda perlu memanggil metode mirip dengan cara Dispatcher, Anda
dapat secara manual menggunakan sesuatu seperti $result = $class->$method(...$params);
atau call_user_func_array()
sebagai gantinya.
Perubahan halt()
stop()
redirect()
dan error()
v3.10.0
Perilaku default sebelum 3.10.0 adalah membersihkan baik header maupun body respons. Ini diubah menjadi hanya membersihkan body respons.
Jika Anda perlu membersihkan header juga, Anda dapat menggunakan Flight::response()->clear()
.
Learn/configuration
Konfigurasi
Gambaran Umum
Flight menyediakan cara sederhana untuk mengonfigurasi berbagai aspek framework agar sesuai dengan kebutuhan aplikasi Anda. Beberapa diatur secara default, tetapi Anda dapat menimpa pengaturan tersebut sesuai kebutuhan. Anda juga dapat mengatur variabel sendiri untuk digunakan di seluruh aplikasi Anda.
Pemahaman
Anda dapat menyesuaikan perilaku tertentu dari Flight dengan mengatur nilai konfigurasi
melalui metode set
.
Flight::set('flight.log_errors', true);
Dalam file app/config/config.php
, Anda dapat melihat semua variabel konfigurasi default yang tersedia untuk Anda.
Penggunaan Dasar
Opsi Konfigurasi Flight
Berikut adalah daftar semua pengaturan konfigurasi yang tersedia:
- flight.base_url
?string
- Timpa URL dasar dari permintaan jika Flight berjalan di subdirektori. (default: null) - flight.case_sensitive
bool
- Pencocokan sensitif huruf besar-kecil untuk URL. (default: false) - flight.handle_errors
bool
- Izinkan Flight untuk menangani semua kesalahan secara internal. (default: true)- Jika Anda ingin Flight menangani kesalahan alih-alih perilaku PHP default, ini perlu diatur ke true.
- Jika Anda memiliki Tracy yang terinstal, Anda ingin mengatur ini ke false agar Tracy dapat menangani kesalahan.
- Jika Anda memiliki plugin APM yang terinstal, Anda ingin mengatur ini ke true agar APM dapat mencatat kesalahan.
- flight.log_errors
bool
- Catat kesalahan ke file log kesalahan server web. (default: false)- Jika Anda memiliki Tracy yang terinstal, Tracy akan mencatat kesalahan berdasarkan konfigurasi Tracy, bukan konfigurasi ini.
- flight.views.path
string
- Direktori yang berisi file template tampilan. (default: ./views) - flight.views.extension
string
- Ekstensi file template tampilan. (default: .php) - flight.content_length
bool
- Atur headerContent-Length
. (default: true)- Jika Anda menggunakan Tracy, ini perlu diatur ke false agar Tracy dapat dirender dengan benar.
- flight.v2.output_buffering
bool
- Gunakan buffering output legacy. Lihat migrating to v3. (default: false)
Konfigurasi Loader
Ada juga pengaturan konfigurasi lain untuk loader. Ini akan memungkinkan Anda
untuk memuat kelas secara otomatis dengan _
dalam nama kelas.
// Aktifkan pemuatan kelas dengan underscore
// Defaulted to true
Loader::$v2ClassLoading = false;
Variabel
Flight memungkinkan Anda menyimpan variabel agar 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 variabel telah diatur, Anda dapat melakukan:
if (Flight::has('id')) {
// Lakukan sesuatu
}
Anda dapat menghapus variabel dengan melakukan:
// Hapus variabel id
Flight::clear('id');
// Hapus semua variabel
Flight::clear();
Catatan: Hanya karena Anda dapat mengatur variabel tidak berarti Anda harus melakukannya. Gunakan fitur ini secara hemat. Alasan mengapa adalah bahwa apa pun yang disimpan di sini menjadi variabel global. Variabel global buruk karena dapat diubah dari mana saja dalam aplikasi Anda, membuat sulit untuk melacak bug. Selain itu, ini dapat mempersulit hal-hal seperti unit testing.
Kesalahan dan Pengecualian
Semua kesalahan dan pengecualian ditangkap oleh Flight dan diteruskan ke metode error
.
jika flight.handle_errors
diatur ke true.
Perilaku default adalah mengirim respons HTTP 500 Internal Server Error
umum dengan beberapa informasi kesalahan.
Anda dapat menimpa perilaku ini untuk kebutuhan Anda sendiri:
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);
404 Tidak Ditemukan
Ketika URL tidak dapat ditemukan, Flight memanggil metode notFound
. Perilaku
default adalah mengirim respons HTTP 404 Not Found
dengan pesan sederhana.
Anda dapat menimpa perilaku ini untuk kebutuhan Anda sendiri:
Flight::map('notFound', function () {
// Tangani tidak ditemukan
});
Lihat Juga
- Extending Flight - Cara memperluas dan menyesuaikan fungsionalitas inti Flight.
- Unit Testing - Cara menulis unit test untuk aplikasi Flight Anda.
- Tracy - Plugin untuk penanganan kesalahan lanjutan dan debugging.
- Tracy Extensions - Ekstensi untuk mengintegrasikan Tracy dengan Flight.
- APM - Plugin untuk pemantauan kinerja aplikasi dan pelacakan kesalahan.
Pemecahan Masalah
- Jika Anda mengalami masalah untuk mengetahui semua nilai konfigurasi Anda, Anda dapat melakukan
var_dump(Flight::get());
Changelog
- v3.5.0 - Ditambahkan konfigurasi untuk
flight.v2.output_buffering
untuk mendukung perilaku buffering output legacy. - v2.0 - Konfigurasi inti ditambahkan.
Learn/ai
AI & Pengalaman Pengembang dengan Flight
Gambaran Umum
Flight memudahkan Anda untuk meningkatkan proyek PHP Anda dengan alat berbasis AI dan alur kerja pengembang modern. Dengan perintah bawaan untuk menghubungkan ke penyedia LLM (Large Language Model) dan menghasilkan instruksi pengkodean AI khusus proyek, Flight membantu Anda dan tim Anda mendapatkan manfaat maksimal dari asisten AI seperti GitHub Copilot, Cursor, dan Windsurf.
Pemahaman
Asisten pengkodean AI paling membantu ketika mereka memahami konteks proyek Anda, konvensi, dan tujuan. Pembantu AI Flight memungkinkan Anda:
- Menghubungkan proyek Anda ke penyedia LLM populer (OpenAI, Grok, Claude, dll.)
- Menghasilkan dan memperbarui instruksi khusus proyek untuk alat AI, sehingga semua orang mendapatkan bantuan yang konsisten dan relevan
- Menjaga tim Anda tetap selaras dan produktif, dengan waktu yang lebih sedikit untuk menjelaskan konteks
Fitur-fitur ini dibangun ke dalam CLI inti Flight dan proyek starter resmi flightphp/skeleton.
Penggunaan Dasar
Menyiapkan Kredensial LLM
Perintah ai:init
memandu Anda melalui proses menghubungkan proyek Anda ke penyedia LLM.
php runway ai:init
Anda akan diminta untuk:
- Memilih penyedia Anda (OpenAI, Grok, Claude, dll.)
- Memasukkan kunci API Anda
- Mengatur URL dasar dan nama model
Ini membuat file .runway-creds.json
di root proyek Anda (dan memastikan itu ada di .gitignore
Anda).
Contoh:
Welcome to AI Init!
Which LLM API do you want to use? [1] openai, [2] grok, [3] claude: 1
Enter the base URL for the LLM API [https://api.openai.com]:
Enter your API key for openai: sk-...
Enter the model name you want to use (e.g. gpt-4, claude-3-opus, etc) [gpt-4o]:
Credentials saved to .runway-creds.json
Menghasilkan Instruksi AI Khusus Proyek
Perintah ai:generate-instructions
membantu Anda membuat atau memperbarui instruksi untuk asisten pengkodean AI, disesuaikan dengan proyek Anda.
php runway ai:generate-instructions
Anda akan menjawab beberapa pertanyaan tentang proyek Anda (deskripsi, database, templating, keamanan, ukuran tim, dll.). Flight menggunakan penyedia LLM Anda untuk menghasilkan instruksi, kemudian menulisnya ke:
.github/copilot-instructions.md
(untuk GitHub Copilot).cursor/rules/project-overview.mdc
(untuk Cursor).windsurfrules
(untuk Windsurf)
Contoh:
Please describe what your project is for? My awesome API
What database are you planning on using? MySQL
What HTML templating engine will you plan on using (if any)? latte
Is security an important element of this project? (y/n) y
...
AI instructions updated successfully.
Sekarang, alat AI Anda akan memberikan saran yang lebih cerdas dan relevan berdasarkan kebutuhan nyata proyek Anda.
Penggunaan Lanjutan
- Anda dapat menyesuaikan lokasi file kredensial atau instruksi Anda menggunakan opsi perintah (lihat
--help
untuk setiap perintah). - Pembantu AI dirancang untuk bekerja dengan penyedia LLM apa pun yang mendukung API yang kompatibel dengan OpenAI.
- Jika Anda ingin memperbarui instruksi Anda seiring berkembangnya proyek, cukup jalankan kembali
ai:generate-instructions
dan jawab prompt lagi.
Lihat Juga
- Flight Skeleton – Starter resmi dengan integrasi AI
- Runway CLI – Lebih lanjut tentang alat CLI yang mendukung perintah ini
Pemecahan Masalah
- Jika Anda melihat "Missing .runway-creds.json", jalankan
php runway ai:init
terlebih dahulu. - Pastikan kunci API Anda valid dan memiliki akses ke model yang dipilih.
- Jika instruksi tidak diperbarui, periksa izin file di direktori proyek Anda.
Changelog
- v3.16.0 – Menambahkan perintah CLI
ai:init
danai:generate-instructions
untuk integrasi AI.
Learn/unit_testing_and_solid_principles
Artikel ini awalnya diterbitkan di Airpair pada tahun 2015. Semua kredit diberikan kepada Airpair dan Brian Fenton yang awalnya menulis artikel ini, meskipun situs web tersebut sudah tidak tersedia lagi dan artikel hanya ada dalam Wayback Machine. Artikel ini telah ditambahkan ke situs untuk tujuan pembelajaran dan pendidikan bagi komunitas PHP secara keseluruhan.
1 Pengaturan dan konfigurasi
1.1 Tetap Terbaru
Mari kita sebutkan ini dari awal - jumlah instalasi PHP yang sedikit menyedihkan di alam liar yang tetap terbaru atau dipertahankan tetap terbaru. Baik itu karena pembatasan hosting bersama, pengaturan default yang tidak ada yang berpikir untuk mengubahnya, atau tidak ada waktu/anggaran untuk pengujian peningkatan, binary PHP yang sederhana cenderung ditinggalkan. Jadi satu praktik terbaik yang jelas yang perlu lebih ditekankan adalah selalu menggunakan versi PHP yang terbaru (5.6.x pada saat artikel ini). Selanjutnya, penting juga untuk menjadwalkan peningkatan reguler baik PHP itu sendiri maupun ekstensi atau pustaka vendor apa pun yang mungkin Anda gunakan. Peningkatan memberi Anda fitur bahasa baru, kecepatan yang ditingkatkan, penggunaan memori yang lebih rendah, dan pembaruan keamanan. Semakin sering Anda meningkatkan, semakin sedikit prosesnya menjadi menyakitkan.
1.2 Atur default yang masuk akal
PHP melakukan pekerjaan yang layak dalam menetapkan default yang baik langsung dari kotak dengan file php.ini.development dan php.ini.production, tetapi kita bisa lebih baik. Untuk satu, mereka tidak menetapkan zona waktu/tanggal untuk kita. Itu masuk akal dari perspektif distribusi, tetapi tanpa satu, PHP akan melemparkan kesalahan E_WARNING setiap kali kita memanggil fungsi terkait tanggal/waktu. Berikut adalah beberapa pengaturan yang direkomendasikan:
- date.timezone - pilih dari daftar zona waktu yang didukung
- session.savepath - jika kita menggunakan file untuk sesi dan bukan penangan penyimpanan lain, atur ini ke sesuatu di luar /tmp. Meninggalkan ini sebagai /tmp bisa berisiko di lingkungan hosting bersama karena /tmp_ biasanya memiliki izin yang luas. Bahkan dengan bit lengket yang diatur, siapa saja yang memiliki akses untuk mencantumkan isi direktori ini bisa mengetahui semua ID sesi aktif Anda.
- session.cookie_secure - hal yang jelas, nyalakan ini jika Anda menyajikan kode PHP Anda melalui HTTPS.
- session.cookie_httponly - atur ini untuk mencegah cookie sesi PHP diakses melalui JavaScript
- Lebih... gunakan alat seperti iniscan untuk menguji konfigurasi Anda terhadap kerentanan umum
1.3 Ekstensi
Ini juga ide bagus untuk menonaktifkan (atau setidaknya tidak mengaktifkan) ekstensi yang tidak akan Anda gunakan, seperti driver basis data. Untuk melihat apa yang diaktifkan, jalankan perintah phpinfo()
atau pergi ke baris perintah dan jalankan ini.
$ php -i
Informasinya sama, tetapi phpinfo() memiliki pemformatan HTML yang ditambahkan. Versi CLI lebih mudah dialirkan ke grep untuk menemukan informasi spesifik meskipun. Contoh.
$ php -i | grep error_log
Satu peringatan dari metode ini meskipun: mungkin ada pengaturan PHP yang berbeda yang berlaku untuk versi yang menghadap web dan versi CLI.
2 Gunakan Composer
Ini mungkin mengejutkan tetapi salah satu praktik terbaik untuk menulis PHP modern adalah menulis lebih sedikit darinya. Meskipun benar bahwa salah satu cara terbaik untuk mahir dalam pemrograman adalah melakukannya, ada banyak masalah yang sudah teratasi di ruang PHP, seperti routing, pustaka validasi input dasar, konversi unit, lapisan abstraksi basis data, dll... Cukup kunjungi Packagist dan jelajahi. Anda mungkin menemukan bahwa bagian signifikan dari masalah yang Anda coba selesaikan sudah ditulis dan diuji.
Meskipun menggoda untuk menulis semua kode sendiri (dan tidak ada yang salah dengan menulis kerangka kerja atau pustaka Anda sendiri sebagai pengalaman belajar) Anda harus melawan perasaan Itu Tidak Diciptakan Di Sini dan menghemat banyak waktu dan sakit kepala. Ikuti doktrin PIE sebagai gantinya - Bangga Dengan Penemuan Lain. Juga, jika Anda memilih untuk menulis sendiri apa pun, jangan rilis kecuali itu melakukan sesuatu yang sangat berbeda atau lebih baik daripada penawaran yang ada.
Composer adalah manajer paket untuk PHP, mirip dengan pip di Python, gem di Ruby, dan npm di Node. Ini memungkinkan Anda mendefinisikan file JSON yang mencantumkan ketergantungan kode Anda, dan itu akan mencoba menyelesaikan persyaratan tersebut dengan mengunduh dan menginstal bundel kode yang diperlukan.
2.1 Menginstal Composer
Kami mengasumsikan ini adalah proyek lokal, jadi mari instal instance Composer hanya untuk proyek saat ini. Navigasi ke direktori proyek Anda dan jalankan ini:
$ curl -sS https://getcomposer.org/installer | php
Ingat bahwa mengalirkan unduhan apa pun langsung ke penerjemah skrip (sh, ruby, php, dll...) adalah risiko keamanan, jadi baca kode instal dan pastikan Anda nyaman dengannya sebelum menjalankan perintah seperti ini.
Untuk kemudahan (jika Anda lebih suka mengetik composer install
daripada php composer.phar install
), Anda bisa menggunakan perintah ini untuk menginstal salinan tunggal composer secara global:
$ mv composer.phar /usr/local/bin/composer
$ chmod +x composer
Anda mungkin perlu menjalankannya dengan sudo
tergantung pada izin file Anda.
2.2 Menggunakan Composer
Composer memiliki dua kategori utama ketergantungan yang bisa dikelolanya: "require" dan "require-dev". Ketergantungan yang tercantum sebagai "require" diinstal di mana-mana, tetapi ketergantungan "require-dev" hanya diinstal saat diminta secara spesifik. Biasanya ini adalah alat untuk saat kode sedang dikembangkan aktif, seperti PHP_CodeSniffer. Baris di bawah menunjukkan contoh cara menginstal Guzzle, sebuah pustaka HTTP populer.
$ php composer.phar require guzzle/guzzle
Untuk menginstal alat hanya untuk tujuan pengembangan, tambahkan flag --dev
:
$ php composer.phar require --dev 'sebastian/phpcpd'
Ini menginstal PHP Copy-Paste Detector, alat kualitas kode lain sebagai ketergantungan hanya untuk pengembangan.
2.3 Install vs update
Saat kita pertama kali menjalankan composer install
itu akan menginstal pustaka dan ketergantungan mereka yang kita butuhkan, berdasarkan file composer.json. Saat selesai, composer membuat file kunci, yang dapat diprediksi disebut composer.lock. File ini berisi daftar ketergantungan yang ditemukan composer untuk kita dan versi tepatnya, dengan hash. Kemudian setiap kali mendatang kita menjalankan composer install
, itu akan melihat di file kunci dan menginstal versi tepat itu.
composer update
adalah binatang yang sedikit berbeda. Ini akan mengabaikan file composer.lock (jika ada) dan mencoba menemukan versi yang paling mutakhir dari setiap ketergantungan yang masih memenuhi batasan di composer.json. Ini kemudian menulis file composer.lock baru saat selesai.
2.4 Autoloading
Baik composer install maupun composer update akan menghasilkan autoloader untuk kita yang memberi tahu PHP di mana menemukan semua file yang diperlukan untuk menggunakan pustaka yang baru saja kita instal. Untuk menggunakannya, cukup tambahkan baris ini (biasanya ke file bootstrap yang dieksekusi pada setiap permintaan):
require 'vendor/autoload.php';
3 Ikuti prinsip desain yang baik
3.1 SOLID
SOLID adalah mnemonik untuk mengingatkan kita akan lima prinsip kunci dalam desain perangkat lunak berorientasi objek yang baik.
3.1.1 S - Prinsip Tanggung Jawab Tunggal
Ini menyatakan bahwa kelas hanya boleh memiliki satu tanggung jawab, atau dengan kata lain, mereka hanya boleh memiliki satu alasan untuk berubah. Ini sesuai dengan filosofi Unix dari banyak alat kecil, melakukan satu hal dengan baik. Kelas yang hanya melakukan satu hal jauh lebih mudah diuji dan di-debug, dan mereka kurang mungkin mengejutkan Anda. Anda tidak ingin panggilan metode ke kelas Validator memperbarui catatan db. Berikut adalah contoh pelanggaran SRP, seperti yang umum Anda lihat di aplikasi berdasarkan pola ActiveRecord.
class Person extends Model
{
public $name;
public $birthDate;
protected $preferences;
public function getPreferences() {}
public function save() {}
}
Jadi ini adalah model entitas yang cukup dasar. Salah satu dari hal-hal ini tidak termasuk di sini meskipun. Tanggung jawab tunggal model entitas haruslah perilaku terkait entitas yang direpresentasikannya, itu tidak boleh bertanggung jawab untuk mempertahankan dirinya sendiri.
class Person extends Model
{
public $name;
public $birthDate;
protected $preferences;
public function getPreferences() {}
}
class DataStore
{
public function save(Model $model) {}
}
Ini lebih baik. Model Person kembali hanya melakukan satu hal, dan perilaku simpan telah dipindahkan ke objek ketekunan sebagai gantinya. Perhatikan juga bahwa saya hanya memberikan petunjuk tipe pada Model, bukan Person. Kita akan kembali ke itu saat kita sampai pada bagian L dan D dari SOLID.
3.1.2 O - Prinsip Terbuka Tertutup
Ada tes luar biasa untuk ini yang cukup merangkum apa prinsip ini: pikirkan fitur untuk diimplementasikan, mungkin yang terbaru yang Anda kerjakan atau sedang kerjakan. Dapatkah Anda mengimplementasikan fitur itu di basis kode yang ada HANYA dengan menambahkan kelas baru dan tidak mengubah kelas yang ada di sistem Anda? Konfigurasi dan kode wiring Anda mendapat sedikit pengabaian, tetapi di sebagian besar sistem ini mengejutkan sulit. Anda harus bergantung banyak pada dispatch polimorfik dan sebagian besar basis kode hanya tidak diatur untuk itu. Jika Anda tertarik pada itu ada pembicaraan Google yang bagus di YouTube tentang polimorfisme dan menulis kode tanpa Ifs yang menggali lebih dalam. Sebagai bonus, pembicaraan diberikan oleh Miško Hevery, yang banyak mungkin tahu sebagai pencipta AngularJs.
3.1.3 L - Prinsip Penggantian Liskov
Prinsip ini dinamai untuk Barbara Liskov, dan dicetak di bawah:
"Objek dalam program harus dapat diganti dengan instance subtipe mereka tanpa mengubah kebenaran program itu."
Itu semua terdengar bagus dan bagus, tetapi lebih jelas diilustrasikan dengan contoh.
abstract class Shape
{
public function getHeight();
public function setHeight($height);
public function getLength();
public function setLength($length);
}
Ini akan mewakili bentuk empat sisi dasar kita. Tidak ada yang mewah di sini.
class Square extends Shape
{
protected $size;
public function getHeight() {
return $this->size;
}
public function setHeight($height) {
$this->size = $height;
}
public function getLength() {
return $this->size;
}
public function setLength($length) {
$this->size = $length;
}
}
Berikut bentuk pertama kita, Kotak. Bentuk yang cukup langsung, kan? Anda bisa mengasumsikan bahwa ada konstruktor di mana kita mengatur dimensi, tetapi Anda melihat dari implementasi ini bahwa panjang dan tinggi selalu akan sama. Kotak hanya seperti itu.
class Rectangle extends Shape
{
protected $height;
protected $length;
public function getHeight() {
return $this->height;
}
public function setHeight($height) {
$this->height = $height;
}
public function getLength() {
return $this->length;
}
public function setLength($length) {
$this->length = $length;
}
}
Jadi di sini kita memiliki bentuk yang berbeda. Masih memiliki tanda tangan metode yang sama, itu masih bentuk empat sisi, tetapi bagaimana jika kita mulai mencoba menggunakannya sebagai pengganti satu sama lain? Sekarang tiba-tiba jika kita mengubah tinggi Bentuk kita, kita tidak lagi bisa mengasumsikan bahwa panjang bentuk kita akan cocok. Kita telah melanggar kontrak yang kita miliki dengan pengguna saat kita memberi mereka bentuk Kotak kita.
Ini adalah contoh teks pelanggaran LSP dan kita memerlukan jenis prinsip ini untuk membuat penggunaan terbaik dari sistem tipe. Bahkan duck typing tidak akan memberi tahu kita jika perilaku dasarnya berbeda, dan karena kita tidak bisa tahu itu tanpa melihatnya rusak, yang terbaik adalah memastikan itu tidak berbeda sejak awal.
3.1.3 I - Prinsip Segregasi Antarmuka
Prinsip ini mengatakan untuk lebih memilih banyak antarmuka kecil dan halus dibandingkan satu yang besar. Antarmuka harus didasarkan pada perilaku daripada "ini salah satu kelas ini". Pikirkan antarmuka yang datang dengan PHP. Traversable, Countable, Serializable, hal-hal seperti itu. Mereka mengiklankan kemampuan yang dimiliki objek, bukan apa yang diwariskannya. Jadi jaga antarmuka Anda tetap kecil. Anda tidak ingin antarmuka memiliki 30 metode di atasnya, 3 adalah tujuan yang lebih baik.
3.1.4 D - Prinsip Pembalikan Ketergantungan
Anda mungkin pernah mendengar tentang ini di tempat lain yang membahas Dependency Injection, tetapi Dependency Inversion dan Dependency Injection bukan cukup hal yang sama. Dependency inversion benar-benar hanya cara mengatakan bahwa Anda harus bergantung pada abstraksi dalam sistem Anda dan bukan pada detailnya. Sekarang apa artinya itu bagi Anda sehari-hari?
Jangan langsung menggunakan mysqli_query() di seluruh kode Anda, gunakan sesuatu seperti DataStore->query() sebagai gantinya.
Inti dari prinsip ini sebenarnya tentang abstraksi. Ini lebih tentang mengatakan "gunakan adaptor basis data" daripada bergantung pada panggilan langsung ke sesuatu seperti mysqli_query. Jika Anda langsung menggunakan mysqli_query di setengah kelas Anda maka Anda mengikat segala sesuatu langsung ke basis data Anda. Tidak ada untuk atau melawan MySQL di sini, tetapi jika Anda menggunakan mysqli_query, jenis detail tingkat rendah itu harus disembunyikan hanya di satu tempat dan kemudian fungsionalitas itu harus diekspos melalui wrapper umum.
Sekarang saya tahu ini adalah contoh yang agak klise jika Anda memikirkannya, karena jumlah kali Anda akan benar-benar mengubah mesin basis data sepenuhnya setelah produk Anda diproduksi sangat, sangat rendah. Saya memilihnya karena saya pikir orang akan akrab dengan ide dari kode mereka sendiri. Juga, bahkan jika Anda memiliki basis data yang Anda ketahui akan tetap, objek wrapper abstrak itu memungkinkan Anda untuk memperbaiki bug, mengubah perilaku, atau mengimplementasikan fitur yang Anda inginkan basis data yang dipilih Anda miliki. Ini juga membuat pengujian unit mungkin di mana panggilan tingkat rendah tidak.
4 Latihan objek
Ini bukan penyelaman penuh ke prinsip-prinsip ini, tetapi dua pertama mudah diingat, memberikan nilai bagus, dan bisa segera diterapkan ke hampir semua basis kode.
4.1 Tidak lebih dari satu level indentasi per metode
Ini adalah cara membantu untuk memikirkan dekomposisi metode menjadi potongan yang lebih kecil, meninggalkan kode yang lebih jelas dan lebih mendokumentasikan diri. Semakin banyak level indentasi yang Anda miliki, semakin banyak metode yang melakukan dan semakin banyak status yang harus Anda ingat dalam pikiran Anda saat bekerja dengannya.
Segera saya tahu orang akan keberatan dengan ini, tetapi ini hanya pedoman/heuristik, bukan aturan keras dan cepat. Saya tidak mengharapkan siapa pun untuk menegakkan aturan PHP_CodeSniffer untuk ini (meskipun orang telah).
Mari kita jalankan melalui sampel cepat apa ini mungkin terlihat seperti:
public function transformToCsv($data)
{
$csvLines = array();
$csvLines[] = implode(',', array_keys($data[0]));
foreach ($data as $row) {
if (!$row) {
continue;
}
$csvLines[] = implode(',', $row);
}
return $csvLines;
}
Meskipun ini bukan kode yang buruk (teknis benar, dapat diuji, dll...) kita bisa melakukan banyak lagi untuk membuat ini jelas. Bagaimana kita mengurangi level nesting di sini?
Kita tahu kita perlu menyederhanakan isi loop foreach (atau menghapusnya sepenuhnya) jadi mari kita mulai di sana.
if (!$row) {
continue;
}
Bagian ini mudah. Yang dilakukan semua ini adalah mengabaikan baris kosong. Kita bisa shortcut proses ini seluruhnya dengan menggunakan fungsi bawaan PHP sebelum kita bahkan sampai ke loop.
$data = array_filter($data);
foreach ($data as $row) {
$csvLines[] = implode(',', $row);
}
Sekarang kita memiliki satu level nesting. Tetapi melihat ini, yang kita lakukan hanyalah menerapkan fungsi ke setiap item dalam array. Kita bahkan tidak perlu loop foreach untuk melakukan itu.
$data = array_filter($data);
$csvLines = array_map(function($row) {
return implode(',', $row);
}, $data);
Sekarang kita tidak memiliki nesting sama sekali, dan kode kemungkinan akan lebih cepat karena kita melakukan semua looping dengan fungsi C asli daripada PHP. Kita harus terlibat dalam sedikit tipuan untuk meneruskan koma ke implode
meskipun, jadi Anda bisa berargumen bahwa berhenti di langkah sebelumnya jauh lebih bisa dipahami.
4.2 Coba tidak gunakan else
Ini benar-benar menangani dua ide utama. Yang pertama adalah pernyataan return ganda dari metode. Jika Anda memiliki cukup informasi untuk membuat keputusan tentang hasil metode, lanjutkan buat keputusan itu dan kembali. Yang kedua adalah ide yang dikenal sebagai Guard Clauses. Ini pada dasarnya adalah pemeriksaan validasi yang dikombinasikan dengan return awal, biasanya di dekat atas metode. Biarkan saya tunjukkan apa yang saya maksud.
public function addThreeInts($first, $second, $third) {
if (is_int($first)) {
if (is_int($second)) {
if (is_int($third)) {
$sum = $first + $second + $third;
} else {
return null;
}
} else {
return null;
}
} else {
return null;
}
return $sum;
}
Jadi ini cukup langsung lagi, itu menambahkan 3 int bersama dan mengembalikan hasilnya, atau null
jika salah satu parameter bukan integer. Mengabaikan fakta bahwa kita bisa menggabungkan semua pemeriksaan itu menjadi satu baris dengan operator AND, saya pikir Anda bisa melihat bagaimana struktur if/else bersarang membuat kode lebih sulit diikuti. Sekarang lihat contoh ini sebagai gantinya.
public function addThreeInts($first, $second, $third) {
if (!is_int($first)) {
return null;
}
if (!is_int($second)) {
return null;
}
if (!is_int($third)) {
return null;
}
return $first + $second + $third;
}
Bagi saya contoh ini jauh lebih mudah diikuti. Di sini kita menggunakan klausa guard untuk memverifikasi asumsi awal kita tentang parameter yang kita lewati dan segera keluar dari metode jika mereka tidak lulus. Kita juga tidak lagi memiliki variabel perantara untuk melacak jumlah sepanjang metode. Dalam kasus ini kita telah memverifikasi bahwa kita sudah di jalur bahagia dan kita bisa langsung melakukan apa yang kita datang kemari untuk lakukan. Sekali lagi kita bisa hanya melakukan semua pemeriksaan itu dalam satu if
tetapi prinsipnya harus jelas.
5 Pengujian unit
Pengujian unit adalah praktik menulis tes kecil yang memverifikasi perilaku dalam kode Anda. Mereka hampir selalu ditulis dalam bahasa yang sama dengan kode (dalam kasus ini PHP) dan dimaksudkan untuk cukup cepat untuk dijalankan kapan saja. Mereka sangat berharga sebagai alat untuk meningkatkan kode Anda. Selain manfaat yang jelas memastikan bahwa kode Anda melakukan apa yang Anda pikirkan, pengujian unit juga bisa memberikan umpan balik desain yang sangat berguna. Jika potongan kode sulit diuji, itu sering menunjukkan masalah desain. Mereka juga memberi Anda jaring pengaman terhadap regresi, dan itu memungkinkan Anda melakukan refaktor lebih sering dan mengembangkan kode Anda ke desain yang lebih bersih.
5.1 Alat
Ada beberapa alat pengujian unit di luar sana di PHP, tetapi jauh dan paling umum adalah PHPUnit. Anda bisa menginstalnya dengan mengunduh file PHAR langsung, atau menginstalnya dengan composer. Karena kita menggunakan composer untuk segala sesuatu yang lain, kita akan menunjukkan metode itu. Juga, karena PHPUnit tidak mungkin akan dikerahkan ke produksi, kita bisa menginstalnya sebagai ketergantungan dev dengan perintah berikut:
composer require --dev phpunit/phpunit
5.2 Tes adalah spesifikasi
Peran paling penting dari tes unit dalam kode Anda adalah memberikan spesifikasi yang dapat dieksekusi dari apa yang seharusnya dilakukan kode. Bahkan jika kode tes salah, atau kode memiliki bug, pengetahuan tentang apa yang seharusnya dilakukan sistem itu tak ternilai harganya.
5.3 Tulis tes Anda terlebih dahulu
Jika Anda punya kesempatan untuk melihat satu set tes yang ditulis sebelum kode dan satu yang ditulis setelah kode selesai, mereka sangat berbeda. Tes "setelah" jauh lebih khawatir dengan detail implementasi kelas dan memastikan mereka memiliki cakupan baris yang baik, sedangkan tes "sebelum" lebih tentang memverifikasi perilaku eksternal yang diinginkan. Itu benar-benar yang kita pedulikan dengan tes unit anyway, adalah memastikan kelas menunjukkan perilaku yang benar. Tes yang berfokus pada implementasi sebenarnya membuat refaktor lebih sulit karena mereka rusak jika internal kelas berubah, dan Anda baru saja kehilangan manfaat penyembunyian informasi OOP.
5.4 Apa yang membuat tes unit yang baik
Tes unit yang baik berbagi banyak karakteristik berikut:
- Cepat - harus berjalan dalam milidetik.
- Tidak ada akses jaringan - harus bisa mematikan nirkabel/mencabut dan semua tes masih lulus.
- Akses sistem file terbatas - ini menambah kecepatan dan fleksibilitas jika menerapkan kode ke lingkungan lain.
- Tidak ada akses basis data - menghindari aktivitas setup dan teardown yang mahal.
- Uji hanya satu hal sekaligus - tes unit harus memiliki hanya satu alasan untuk gagal.
- Bernama dengan baik - lihat 5.2 di atas.
- Sebagian besar objek palsu - satu-satunya "objek nyata" dalam tes unit harus menjadi objek yang kita uji dan objek nilai sederhana. Sisanya harus berupa beberapa bentuk test double
Ada alasan untuk melawan beberapa dari ini tetapi sebagai pedoman umum mereka akan melayani Anda dengan baik.
5.5 Saat pengujian menyakitkan
Pengujian unit memaksa Anda untuk merasakan sakit desain buruk di depan - Michael Feathers
Saat Anda menulis tes unit, Anda memaksa diri Anda untuk benar-benar menggunakan kelas untuk mencapai hal-hal. Jika Anda menulis tes di akhir, atau lebih buruk lagi, hanya melemparkan kode ke atas dinding untuk QA atau siapa pun untuk menulis tes, Anda tidak mendapatkan umpan balik tentang bagaimana kelas benar-benar berperilaku. Jika kita menulis tes, dan kelas itu nyeri nyata untuk digunakan, kita akan mengetahuinya saat kita menulisnya, yang hampir waktu termurah untuk memperbaikinya.
Jika kelas sulit diuji, itu cacat desain. Cacat yang berbeda menampakkan diri dalam cara yang berbeda, meskipun. Jika Anda harus melakukan banyak mocking, kelas Anda mungkin memiliki terlalu banyak ketergantungan, atau metode Anda melakukan terlalu banyak. Semakin banyak setup yang harus Anda lakukan untuk setiap tes, semakin mungkin metode Anda melakukan terlalu banyak. Jika Anda harus menulis skenario tes yang sangat rumit untuk melatih perilaku, metode kelas mungkin melakukan terlalu banyak. Jika Anda harus menggali di dalam banyak metode pribadi dan status untuk menguji hal-hal, mungkin ada kelas lain yang mencoba keluar. Pengujian unit sangat bagus dalam mengekspos "kelas gunung es" di mana 80% dari apa yang dilakukan kelas disembunyikan di kode yang dilindungi atau pribadi. Saya dulu penggemar besar membuat sebanyak mungkin dilindungi, tetapi sekarang saya sadar saya hanya membuat kelas individu saya bertanggung jawab atas terlalu banyak, dan solusi sebenarnya adalah memecah kelas menjadi potongan yang lebih kecil.
Ditulis oleh Brian Fenton - Brian Fenton telah menjadi pengembang PHP selama 8 tahun di Midwest dan Bay Area, saat ini di Thismoment. Dia fokus pada kerajinan kode dan prinsip desain. Blog di www.brianfenton.us, Twitter di @brianfenton. Saat dia tidak sibuk menjadi ayah, dia menikmati makanan, bir, gaming, dan belajar.
Learn/security
Keamanan
Gambaran Umum
Keamanan adalah hal besar ketika berbicara tentang aplikasi web. Anda ingin memastikan bahwa aplikasi Anda aman dan data pengguna Anda aman. Flight menyediakan sejumlah fitur untuk membantu Anda mengamankan aplikasi web Anda.
Pemahaman
Ada sejumlah ancaman keamanan umum yang harus Anda sadari saat membangun aplikasi web. Beberapa ancaman paling umum termasuk:
- Cross Site Request Forgery (CSRF)
- Cross Site Scripting (XSS)
- SQL Injection
- Cross Origin Resource Sharing (CORS)
Templates membantu dengan XSS dengan meng-escape output secara default sehingga Anda tidak perlu mengingat untuk melakukannya. Sessions dapat membantu dengan CSRF dengan menyimpan token CSRF di sesi pengguna seperti yang diuraikan di bawah ini. Menggunakan prepared statements dengan PDO dapat membantu mencegah serangan SQL injection (atau menggunakan metode yang berguna di kelas PdoWrapper). CORS dapat ditangani dengan hook sederhana sebelum Flight::start()
dipanggil.
Semua metode ini bekerja sama untuk membantu menjaga aplikasi web Anda aman. Ini harus selalu menjadi yang terdepan di pikiran Anda untuk belajar dan memahami praktik terbaik keamanan.
Penggunaan Dasar
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 gunakan untuk menambahkan header ini ke aplikasi Anda.
Dua situs web hebat untuk memeriksa keamanan header Anda adalah securityheaders.com dan observatory.mozilla.org. Setelah Anda menyiapkan kode di bawah ini, Anda dapat dengan mudah memverifikasi bahwa header Anda berfungsi dengan dua situs web tersebut.
Tambahkan Secara Manual
Anda dapat menambahkan header ini secara manual dengan menggunakan metode header
pada objek Flight\Response
.
// Set header X-Frame-Options untuk mencegah clickjacking
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
// Set header Content-Security-Policy untuk mencegah XSS
// Catatan: header ini bisa sangat kompleks, jadi Anda akan ingin
// berkonsultasi dengan contoh di internet untuk aplikasi Anda
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
// Set header X-XSS-Protection untuk mencegah XSS
Flight::response()->header('X-XSS-Protection', '1; mode=block');
// Set header X-Content-Type-Options untuk mencegah MIME sniffing
Flight::response()->header('X-Content-Type-Options', 'nosniff');
// Set header Referrer-Policy untuk mengontrol seberapa banyak informasi referrer yang dikirim
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
// Set header Strict-Transport-Security untuk memaksa HTTPS
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// Set header Permissions-Policy untuk mengontrol fitur dan API apa yang dapat digunakan
Flight::response()->header('Permissions-Policy', 'geolocation=()');
Ini dapat ditambahkan di bagian atas file routes.php
atau index.php
Anda.
Tambahkan 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=()');
});
Tambahkan sebagai Middleware
Anda juga dapat menambahkannya sebagai kelas middleware yang memberikan fleksibilitas terbesar untuk rute mana yang akan diterapkan ini. Secara umum, header ini harus diterapkan pada semua respons HTML dan API.
// app/middlewares/SecurityHeadersMiddleware.php
namespace app\middlewares;
use flight\Engine;
class SecurityHeadersMiddleware
{
protected Engine $app;
public function __construct(Engine $app)
{
$this->app = $app;
}
public function before(array $params): void
{
$response = $this->app->response();
$response->header('X-Frame-Options', 'SAMEORIGIN');
$response->header("Content-Security-Policy", "default-src 'self'");
$response->header('X-XSS-Protection', '1; mode=block');
$response->header('X-Content-Type-Options', 'nosniff');
$response->header('Referrer-Policy', 'no-referrer-when-downgrade');
$response->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
$response->header('Permissions-Policy', 'geolocation=()');
}
}
// index.php atau di mana pun Anda memiliki rute
// FYI, grup string kosong ini bertindak sebagai middleware global untuk
// semua rute. Tentu saja Anda bisa melakukan hal yang sama dan hanya menambahkan
// ini ke rute tertentu.
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// lebih banyak rute
}, [ SecurityHeadersMiddleware::class ]);
Cross Site Request Forgery (CSRF)
Cross Site Request Forgery (CSRF) adalah jenis serangan di mana situs web berbahaya 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 dengan menggunakan middleware.
Penyiapan
Pertama, Anda perlu menghasilkan token CSRF dan menyimpannya di sesi pengguna. Kemudian Anda dapat menggunakan token ini di formulir Anda dan memeriksanya ketika formulir dikirim. Kami akan menggunakan plugin flightphp/session untuk mengelola sesi.
// Hasilkan token CSRF dan simpan di sesi pengguna
// (dengan asumsi Anda telah membuat objek sesi dan melampirkannya ke Flight)
// lihat dokumentasi sesi untuk informasi lebih lanjut
Flight::register('session', flight\Session::class);
// Anda hanya perlu menghasilkan satu token per sesi (sehingga berfungsi
// di berbagai tab dan permintaan untuk pengguna yang sama)
if(Flight::session()->get('csrf_token') === null) {
Flight::session()->set('csrf_token', bin2hex(random_bytes(32)) );
}
Menggunakan Template Flight PHP Default
<!-- 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 mengeluarkan token CSRF di template Latte Anda.
Flight::map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// konfigurasi lainnya...
// Set fungsi kustom untuk mengeluarkan token CSRF
$latte->addFunction('csrf', function() {
$csrfToken = Flight::session()->get('csrf_token');
return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">');
});
$latte->render($finalPath, $data, $block);
});
Dan sekarang di template Latte Anda, Anda dapat menggunakan fungsi csrf()
untuk mengeluarkan token CSRF.
<form method="post">
{csrf()}
<!-- field formulir lainnya -->
</form>
Periksa Token CSRF
Anda dapat memeriksa token CSRF menggunakan beberapa metode.
Middleware
// app/middlewares/CsrfMiddleware.php
namespace app\middleware;
use flight\Engine;
class CsrfMiddleware
{
protected Engine $app;
public function __construct(Engine $app)
{
$this->app = $app;
}
public function before(array $params): void
{
if($this->app->request()->method == 'POST') {
$token = $this->app->request()->data->csrf_token;
if($token !== $this->app->session()->get('csrf_token')) {
$this->app->halt(403, 'Invalid CSRF token');
}
}
}
}
// index.php atau di mana pun Anda memiliki rute
use app\middlewares\CsrfMiddleware;
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// lebih banyak rute
}, [ CsrfMiddleware::class ]);
Filter Event
// Middleware ini memeriksa apakah permintaan adalah permintaan POST dan jika ya, 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, 'Invalid CSRF token');
// atau untuk respons JSON
Flight::jsonHalt(['error' => 'Invalid CSRF token'], 403);
}
}
});
Cross Site Scripting (XSS)
Cross Site Scripting (XSS) adalah jenis serangan di mana input formulir berbahaya dapat menyuntikkan kode ke situs web Anda. Sebagian besar peluang ini berasal dari nilai formulir yang akan diisi oleh pengguna akhir Anda. Anda tidak pernah boleh mempercayai output dari pengguna Anda! Selalu asumsikan semua 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 Flight atau engine templating lain seperti Latte, Anda dapat dengan mudah meng-escape output untuk mencegah serangan XSS.
// Mari kita asumsikan pengguna pintar dan mencoba menggunakan ini sebagai nama mereka
$name = '<script>alert("XSS")</script>';
// Ini akan meng-escape output
Flight::view()->set('name', $name);
// Ini akan mengeluarkan: <script>alert("XSS")</script>
// Jika Anda menggunakan sesuatu seperti Latte yang terdaftar sebagai kelas view Anda, itu juga akan auto escape ini.
Flight::view()->render('template', ['name' => $name]);
SQL Injection
SQL Injection adalah jenis serangan di mana pengguna berbahaya dapat menyuntikkan kode SQL ke database Anda. Ini dapat digunakan untuk mencuri informasi
dari database Anda atau melakukan tindakan di database Anda. Sekali lagi Anda tidak pernah boleh mempercayai input dari pengguna Anda! Selalu asumsikan mereka
mengincar darah. Anda dapat menggunakan prepared statements di objek PDO
Anda akan mencegah SQL injection.
// Dengan asumsi Anda memiliki Flight::db() yang 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 ]);
Contoh Tidak Aman
Di bawah ini adalah alasan mengapa kami menggunakan pernyataan prepared SQL untuk melindungi dari contoh tidak bersalah seperti di bawah ini:
// pengguna akhir mengisi formulir web.
// untuk nilai formulir, hacker memasukkan sesuatu seperti ini:
$username = "' OR 1=1; -- ";
$sql = "SELECT * FROM users WHERE username = '$username' LIMIT 5";
$users = Flight::db()->fetchAll($sql);
// Setelah query dibangun, itu terlihat seperti ini
// SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5
// Terlihat aneh, tapi itu query yang valid yang akan berfungsi. Bahkan,
// itu adalah serangan SQL injection yang sangat umum yang akan mengembalikan semua pengguna.
var_dump($users); // ini akan membuang semua pengguna di database, bukan hanya satu nama pengguna tunggal
CORS
Cross-Origin Resource Sharing (CORS) adalah mekanisme yang memungkinkan banyak sumber daya (misalnya, font, JavaScript, dll.) pada halaman web untuk diminta
dari domain lain di luar domain tempat sumber daya berasal. Flight tidak memiliki fungsionalitas 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 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
$CorsUtil = new CorsUtil();
// Ini perlu dijalankan sebelum start berjalan.
Flight::before('start', [ $CorsUtil, 'setupCors' ]);
Penanganan Error
Sembunyikan detail error sensitif di produksi untuk menghindari kebocoran info ke penyerang. Di produksi, log error daripada menampilkannya dengan display_errors
disetel ke 0
.
// Di bootstrap.php atau index.php Anda
// tambahkan ini ke app/config/config.php Anda
$environment = ENVIRONMENT;
if ($environment === 'production') {
ini_set('display_errors', 0); // Nonaktifkan tampilan error
ini_set('log_errors', 1); // Log error sebagai gantinya
ini_set('error_log', '/path/to/error.log');
}
// Di rute atau controller Anda
// Gunakan Flight::halt() untuk respons error yang terkendali
Flight::halt(403, 'Access denied');
Sanitasi Input
Jangan pernah percaya input pengguna. Sanitasi itu menggunakan filter_var sebelum diproses untuk mencegah data berbahaya menyusup masuk.
// Mari kita asumsikan 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);
Penyimpanan Hash Kata Sandi
Simpan kata sandi dengan aman dan verifikasi dengan aman menggunakan fungsi bawaan PHP seperti password_hash dan password_verify. Kata sandi tidak boleh disimpan dalam teks biasa, juga tidak boleh dienkripsi dengan metode yang dapat dibalik. Hashing memastikan bahwa bahkan jika database Anda dikompromikan, kata sandi sebenarnya tetap terlindungi.
$password = Flight::request()->data->password;
// Hash kata sandi saat menyimpan (misalnya, selama pendaftaran)
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Verifikasi kata sandi (misalnya, selama login)
if (password_verify($password, $stored_hash)) {
// Kata sandi cocok
}
Pembatasan Laju
Lindungi dari serangan brute force atau serangan denial-of-service dengan membatasi laju permintaan menggunakan cache.
// Dengan asumsi Anda memiliki flightphp/cache yang diinstal dan terdaftar
// Menggunakan flightphp/cache dalam filter
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, 'Too many requests');
}
$cache->set($key, $attempts + 1, 60); // Reset setelah 60 detik
});
Lihat Juga
- Sessions - Cara mengelola sesi pengguna dengan aman.
- Templates - Menggunakan template untuk auto-escape output dan mencegah XSS.
- PDO Wrapper - Interaksi database yang disederhanakan dengan prepared statements.
- Middleware - Cara menggunakan middleware untuk menyederhanakan proses menambahkan header keamanan.
- Responses - Cara menyesuaikan respons HTTP dengan header aman.
- Requests - Cara menangani dan menyanitasi input pengguna.
- filter_var - Fungsi PHP untuk sanitasi input.
- password_hash - Fungsi PHP untuk hashing kata sandi aman.
- password_verify - Fungsi PHP untuk memverifikasi kata sandi yang di-hash.
Pemecahan Masalah
- Lihat bagian "Lihat Juga" di atas untuk informasi pemecahan masalah terkait isu dengan komponen Framework Flight.
Changelog
- v3.1.0 - Ditambahkan bagian tentang CORS, Penanganan Error, Sanitasi Input, Hashing Kata Sandi, dan Pembatasan Laju.
- v2.0 - Ditambahkan escaping untuk view default untuk mencegah XSS.
Learn/routing
Routing
Gambaran Umum
Routing di Flight PHP memetakan pola URL ke fungsi callback atau metode kelas, memungkinkan penanganan permintaan yang cepat dan sederhana. Ini dirancang untuk overhead minimal, penggunaan yang ramah pemula, dan kemampuan ekstensi tanpa ketergantungan eksternal.
Pemahaman
Routing adalah mekanisme inti yang menghubungkan permintaan HTTP ke logika aplikasi Anda di Flight. Dengan mendefinisikan rute, Anda menentukan bagaimana URL yang berbeda memicu kode spesifik, baik melalui fungsi, metode kelas, atau aksi pengontrol. Sistem routing Flight fleksibel, mendukung pola dasar, parameter bernama, ekspresi reguler, dan fitur lanjutan seperti injeksi dependensi dan routing sumber daya. Pendekatan ini menjaga kode Anda tetap terorganisir dan mudah dipelihara, sambil tetap cepat dan sederhana untuk pemula serta dapat diekstensikan untuk pengguna lanjutan.
Catatan: Ingin memahami lebih lanjut tentang routing? Lihat halaman "why a framework?" untuk penjelasan yang lebih mendalam.
Penggunaan Dasar
Mendefinisikan Rute Sederhana
Routing dasar di Flight dilakukan dengan mencocokkan pola URL dengan fungsi callback atau array dari kelas dan metode.
Flight::route('/', function(){
echo 'hello world!';
});
Rute dicocokkan sesuai urutan yang didefinisikan. Rute pertama yang cocok dengan permintaan akan dipanggil.
Menggunakan Fungsi sebagai Callback
Callback bisa berupa objek apa pun yang dapat dipanggil. Jadi Anda bisa menggunakan fungsi biasa:
function hello() {
echo 'hello world!';
}
Flight::route('/', 'hello');
Menggunakan Kelas dan Metode sebagai Pengontrol
Anda juga bisa menggunakan metode (statis atau tidak) dari kelas:
class GreetingController {
public function hello() {
echo 'hello world!';
}
}
Flight::route('/', [ 'GreetingController','hello' ]);
// or
Flight::route('/', [ GreetingController::class, 'hello' ]); // preferred method
// or
Flight::route('/', [ 'GreetingController::hello' ]);
// or
Flight::route('/', [ 'GreetingController->hello' ]);
Atau dengan membuat objek terlebih dahulu lalu memanggil metodenya:
use flight\Engine;
// GreetingController.php
class GreetingController
{
protected Engine $app
public function __construct(Engine $app) {
$this->app = $app;
$this->name = 'John Doe';
}
public function hello() {
echo "Hello, {$this->name}!";
}
}
// index.php
$app = Flight::app();
$greeting = new GreetingController($app);
Flight::route('/', [ $greeting, 'hello' ]);
Catatan: Secara default, ketika pengontrol dipanggil dalam kerangka kerja, kelas
flight\Engine
selalu diinjeksi kecuali Anda menentukannya melalui container injeksi dependensi
Routing Spesifik Metode
Secara default, pola rute dicocokkan dengan semua metode permintaan. Anda bisa merespons metode spesifik dengan menempatkan pengenal sebelum URL.
Flight::route('GET /', function () {
echo 'I received a GET request.';
});
Flight::route('POST /', function () {
echo 'I received a POST request.';
});
// You cannot use Flight::get() for routes as that is a method
// to get variables, not create a route.
Flight::post('/', function() { /* code */ });
Flight::patch('/', function() { /* code */ });
Flight::put('/', function() { /* code */ });
Flight::delete('/', function() { /* code */ });
Anda juga bisa memetakan beberapa metode ke satu callback dengan menggunakan pemisah |
:
Flight::route('GET|POST /', function () {
echo 'I received either a GET or a POST request.';
});
Penanganan Khusus untuk Permintaan HEAD dan OPTIONS
Flight menyediakan penanganan bawaan untuk permintaan HTTP HEAD
dan OPTIONS
:
Permintaan HEAD
- Permintaan HEAD diperlakukan seperti permintaan
GET
, tetapi Flight secara otomatis menghapus isi respons sebelum mengirimkannya ke klien. - Ini berarti Anda bisa mendefinisikan rute untuk
GET
, dan permintaan HEAD ke URL yang sama akan mengembalikan hanya header (tanpa konten), sesuai standar HTTP.
Flight::route('GET /info', function() {
echo 'This is some info!';
});
// A HEAD request to /info will return the same headers, but no body.
Permintaan OPTIONS
Permintaan OPTIONS
ditangani secara otomatis oleh Flight untuk rute apa pun yang didefinisikan.
- Ketika permintaan OPTIONS diterima, Flight merespons dengan status
204 No Content
dan headerAllow
yang mencantumkan semua metode HTTP yang didukung untuk rute tersebut. - Anda tidak perlu mendefinisikan rute terpisah untuk OPTIONS.
// For a route defined as:
Flight::route('GET|POST /users', function() { /* ... */ });
// An OPTIONS request to /users will respond with:
//
// Status: 204 No Content
// Allow: GET, POST, HEAD, OPTIONS
Menggunakan Objek Router
Selain itu, Anda bisa mengambil objek Router yang memiliki beberapa metode pembantu untuk digunakan:
$router = Flight::router();
// maps all methods just like Flight::route()
$router->map('/', function() {
echo 'hello world!';
});
// GET request
$router->get('/users', function() {
echo 'users';
});
$router->post('/users', function() { /* code */});
$router->put('/users/update/@id', function() { /* code */});
$router->delete('/users/@id', function() { /* code */});
$router->patch('/users/@id', function() { /* code */});
Ekspresi Regulasi (Regex)
Anda bisa menggunakan ekspresi reguler di rute Anda:
Flight::route('/user/[0-9]+', function () {
// This will match /user/1234
});
Meskipun metode ini tersedia, disarankan untuk menggunakan parameter bernama, atau parameter bernama dengan ekspresi reguler, karena lebih mudah dibaca dan dipelihara.
Parameter Bernama
Anda bisa menentukan parameter bernama di rute Anda yang akan diteruskan ke fungsi callback Anda. Ini lebih untuk keterbacaan rute daripada hal lain. Lihat bagian di bawah tentang peringatan penting.
Flight::route('/@name/@id', function (string $name, string $id) {
echo "hello, $name ($id)!";
});
Anda juga bisa menyertakan ekspresi reguler dengan parameter bernama Anda dengan menggunakan pemisah :
:
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
// This will match /bob/123
// But will not match /bob/12345
});
Catatan: Mencocokkan grup regex
()
dengan parameter posisional tidak didukung. Contoh::'\(
Peringatan Penting
Meskipun dalam contoh di atas, tampaknya @name
langsung terkait dengan variabel $name
, sebenarnya bukan. Urutan parameter dalam fungsi callback yang menentukan apa yang diteruskan kepadanya. Jika Anda menukar urutan parameter dalam fungsi callback, variabel juga akan bertukar. Berikut contohnya:
Flight::route('/@name/@id', function (string $id, string $name) {
echo "hello, $name ($id)!";
});
Dan jika Anda mengunjungi URL berikut: /bob/123
, outputnya akan menjadi hello, 123 (bob)!
.
Please be careful ketika Anda menyiapkan rute dan fungsi callback Anda!
Parameter Opsional
Anda bisa menentukan parameter bernama yang opsional untuk pencocokan dengan membungkus segmen dalam tanda kurung.
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// This will match the following URLS:
// /blog/2012/12/10
// /blog/2012/12
// /blog/2012
// /blog
}
);
Parameter opsional apa pun yang tidak cocok akan diteruskan sebagai NULL
.
Routing Wildcard
Pencocokan hanya dilakukan pada segmen URL individu. Jika Anda ingin mencocokkan beberapa segmen, Anda bisa menggunakan wildcard *
.
Flight::route('/blog/*', function () {
// This will match /blog/2000/02/01
});
Untuk merutekan semua permintaan ke satu callback, Anda bisa lakukan:
Flight::route('*', function () {
// Do something
});
Penanganan 404 Tidak Ditemukan
Secara default, jika URL tidak ditemukan, Flight akan mengirim respons HTTP 404 Not Found
yang sangat sederhana dan polos.
Jika Anda ingin respons 404 yang lebih disesuaikan, Anda bisa memetakan metode notFound
sendiri:
Flight::map('notFound', function() {
$url = Flight::request()->url;
// You could also use Flight::render() with a custom template.
$output = <<<HTML
<h1>My Custom 404 Not Found</h1>
<h3>The page you have requested {$url} could not be found.</h3>
HTML;
$this->response()
->clearBody()
->status(404)
->write($output)
->send();
});
Penanganan Metode Tidak Ditemukan
Secara default, jika URL ditemukan tetapi metode tidak diizinkan, Flight akan mengirim respons HTTP 405 Method Not Allowed
yang sangat sederhana dan polos (Contoh: Method Not Allowed. Allowed Methods are: GET, POST). Ini juga akan menyertakan header Allow
dengan metode yang diizinkan untuk URL tersebut.
Jika Anda ingin respons 405 yang lebih disesuaikan, Anda bisa memetakan metode methodNotFound
sendiri:
use flight\net\Route;
Flight::map('methodNotFound', function(Route $route) {
$url = Flight::request()->url;
$methods = implode(', ', $route->methods);
// You could also use Flight::render() with a custom template.
$output = <<<HTML
<h1>My Custom 405 Method Not Allowed</h1>
<h3>The method you have requested for {$url} is not allowed.</h3>
<p>Allowed Methods are: {$methods}</p>
HTML;
$this->response()
->clearBody()
->status(405)
->setHeader('Allow', $methods)
->write($output)
->send();
});
Penggunaan Lanjutan
Injeksi Dependensi di Rute
Jika Anda ingin menggunakan injeksi dependensi melalui container (PSR-11, PHP-DI, Dice, dll), satu-satunya jenis rute di mana itu tersedia adalah dengan membuat objek secara langsung sendiri dan menggunakan container untuk membuat objek Anda atau Anda bisa menggunakan string untuk mendefinisikan kelas dan metode yang akan dipanggil. Anda bisa pergi ke halaman Dependency Injection untuk informasi lebih lanjut.
Berikut 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) {
// do something with $this->pdoWrapper
$name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
echo "Hello, world! My name is {$name}!";
}
}
// index.php
// Setup the container with whatever params you need
// See the Dependency Injection page for more information on PSR-11
$dice = new \Dice\Dice();
// Don't forget to reassign the variable with '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
'shared' => true,
'constructParams' => [
'mysql:host=localhost;dbname=test',
'root',
'password'
]
]);
// Register the container handler
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// Routes like normal
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// or
Flight::route('/hello/@id', 'Greeting->hello');
// or
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();
Mengalihkan Eksekusi ke Rute Berikutnya
Deprecated
Anda bisa mengalihkan eksekusi ke rute pencocokan berikutnya dengan mengembalikan true
dari fungsi callback Anda.
Flight::route('/user/@name', function (string $name) {
// Check some condition
if ($name !== "Bob") {
// Continue to next route
return true;
}
});
Flight::route('/user/*', function () {
// This will get called
});
Sekarang disarankan untuk menggunakan middleware untuk menangani kasus penggunaan kompleks seperti ini.
Alias Rute
Dengan menetapkan alias ke rute, Anda bisa memanggil alias tersebut di aplikasi Anda secara dinamis untuk dihasilkan nanti di kode Anda (contoh: tautan di template HTML, atau menghasilkan URL redirect).
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// or
Flight::route('/users/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');
// later in code somewhere
class UserController {
public function update() {
// code to save user...
$id = $user['id']; // 5 for example
$redirectUrl = Flight::getUrl('user_view', [ 'id' => $id ]); // will return '/users/5'
Flight::redirect($redirectUrl);
}
}
Ini sangat membantu jika URL Anda berubah. Dalam contoh di atas, katakanlah bahwa users dipindahkan ke /admin/users/@id
sebagai gantinya.
Dengan aliasing di tempat untuk rute, Anda tidak lagi perlu mencari semua URL lama di kode Anda dan mengubahnya karena alias sekarang akan mengembalikan /admin/users/5
seperti dalam contoh di atas.
Alias rute masih berfungsi dalam grup juga:
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// or
Flight::route('/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');
});
Memeriksa Informasi Rute
Jika Anda ingin memeriksa informasi rute pencocokan, ada 2 cara Anda bisa lakukan ini:
- Anda bisa menggunakan properti
executedRoute
pada objekFlight::router()
. - Anda bisa meminta objek rute diteruskan ke callback Anda dengan meneruskan
true
sebagai parameter ketiga dalam metode rute. Objek rute akan selalu menjadi parameter terakhir yang diteruskan ke fungsi callback Anda.
executedRoute
Flight::route('/', function() {
$route = Flight::router()->executedRoute;
// Do something with $route
// Array of HTTP methods matched against
$route->methods;
// Array of named parameters
$route->params;
// Matching regular expression
$route->regex;
// Contains the contents of any '*' used in the URL pattern
$route->splat;
// Shows the url path....if you really need it
$route->pattern;
// Shows what middleware is assigned to this
$route->middleware;
// Shows the alias assigned to this route
$route->alias;
});
Catatan: Properti
executedRoute
hanya akan ditetapkan setelah rute dieksekusi. Jika Anda mencoba mengaksesnya sebelum rute dieksekusi, itu akan menjadiNULL
. Anda juga bisa menggunakan executedRoute di middleware juga!
Meneruskan true
ke definisi rute
Flight::route('/', function(\flight\net\Route $route) {
// Array of HTTP methods matched against
$route->methods;
// Array of named parameters
$route->params;
// Matching regular expression
$route->regex;
// Contains the contents of any '*' used in the URL pattern
$route->splat;
// Shows the url path....if you really need it
$route->pattern;
// Shows what middleware is assigned to this
$route->middleware;
// Shows the alias assigned to this route
$route->alias;
}, true);// <-- This true parameter is what makes that happen
Pengelompokan Rute dan Middleware
Mungkin ada saatnya Anda ingin mengelompokkan rute terkait bersama (seperti /api/v1
).
Anda bisa lakukan ini dengan menggunakan metode group
:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Matches /api/v1/users
});
Flight::route('/posts', function () {
// Matches /api/v1/posts
});
});
Anda bahkan bisa menumpuk grup dari grup:
Flight::group('/api', function () {
Flight::group('/v1', function () {
// Flight::get() gets variables, it doesn't set a route! See object context below
Flight::route('GET /users', function () {
// Matches GET /api/v1/users
});
Flight::post('/posts', function () {
// Matches POST /api/v1/posts
});
Flight::put('/posts/1', function () {
// Matches PUT /api/v1/posts
});
});
Flight::group('/v2', function () {
// Flight::get() gets variables, it doesn't set a route! See object context below
Flight::route('GET /users', function () {
// Matches GET /api/v2/users
});
});
});
Pengelompokan dengan Konteks Objek
Anda masih bisa menggunakan pengelompokan rute dengan objek Engine
dengan cara berikut:
$app = Flight::app();
$app->group('/api/v1', function (Router $router) {
// user the $router variable
$router->get('/users', function () {
// Matches GET /api/v1/users
});
$router->post('/posts', function () {
// Matches POST /api/v1/posts
});
});
Catatan: Ini adalah metode yang disukai untuk mendefinisikan rute dan grup dengan objek
$router
.
Pengelompokan dengan Middleware
Anda juga bisa menetapkan middleware ke grup rute:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Matches /api/v1/users
});
}, [ MyAuthMiddleware::class ]); // or [ new MyAuthMiddleware() ] if you want to use an instance
Lihat detail lebih lanjut di halaman group middleware.
Routing Sumber Daya
Anda bisa membuat set rute untuk sumber daya menggunakan metode resource
. Ini akan membuat set 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 itu akan membuat rute berikut:
[
'index' => 'GET /users',
'create' => 'GET /users/create',
'store' => 'POST /users',
'show' => 'GET /users/@id',
'edit' => 'GET /users/@id/edit',
'update' => 'PUT /users/@id',
'destroy' => 'DELETE /users/@id'
]
Dan pengontrol Anda akan menggunakan metode berikut:
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 bisa melihat rute yang baru ditambahkan dengan
runway
dengan menjalankanphp runway routes
.
Menyesuaikan Rute Sumber Daya
Ada beberapa opsi untuk mengonfigurasi rute sumber daya.
Alias Dasar
Anda bisa mengonfigurasi aliasBase
. Secara default alias adalah bagian terakhir dari URL yang ditentukan.
Misalnya /users/
akan menghasilkan aliasBase
dari users
. Ketika rute ini dibuat, aliasnya adalah users.index
, users.create
, dll. Jika Anda ingin mengubah alias, tetapkan aliasBase
ke nilai yang Anda inginkan.
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Only dan Except
Anda juga bisa menentukan rute mana yang ingin Anda buat dengan menggunakan opsi only
dan except
.
// Whitelist only these methods and blacklist the rest
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
// Blacklist only these methods and whitelist the rest
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
Ini pada dasarnya opsi whitelisting dan blacklisting sehingga Anda bisa menentukan rute mana yang ingin Anda buat.
Middleware
Anda juga bisa menentukan middleware yang akan dijalankan pada setiap rute yang dibuat oleh metode resource
.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
Respons Streaming
Anda sekarang bisa melakukan streaming respons ke klien menggunakan stream()
atau streamWithHeaders()
.
Ini berguna untuk mengirim file besar, proses jangka panjang, atau menghasilkan respons besar.
Streaming rute ditangani sedikit berbeda daripada rute biasa.
Catatan: Respons streaming hanya tersedia jika Anda memiliki
flight.v2.output_buffering
yang diatur kefalse
.
Stream dengan Header Manual
Anda bisa melakukan streaming respons ke klien dengan menggunakan metode stream()
pada rute. Jika Anda
melakukan ini, Anda harus menetapkan semua header secara manual sebelum Anda mengoutput apa pun ke klien.
Ini dilakukan dengan fungsi php header()
atau metode Flight::response()->setRealHeader()
.
Flight::route('/@filename', function($filename) {
$response = Flight::response();
// obviously you would sanitize the path and whatnot.
$fileNameSafe = basename($filename);
// If you have additional headers to set here after the route has executed
// you must define them before anything is echoed out.
// They must all be a raw call to the header() function or
// a call to Flight::response()->setRealHeader()
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// or
$response->setRealHeader('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
$filePath = '/some/path/to/files/'.$fileNameSafe;
if (!is_readable($filePath)) {
Flight::halt(404, 'File not found');
}
// manually set the content length if you'd like
header('Content-Length: '.filesize($filePath));
// or
$response->setRealHeader('Content-Length: '.filesize($filePath));
// Stream the file to the client as it's read
readfile($filePath);
// This is the magic line here
})->stream();
Stream dengan Header
Anda juga bisa menggunakan metode streamWithHeaders()
untuk menetapkan header sebelum Anda mulai streaming.
Flight::route('/stream-users', function() {
// you can add any additional headers you want here
// you just must use header() or Flight::response()->setRealHeader()
// however you pull your data, just as an example...
$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 ',';
}
// This is required to send the data to the client
ob_flush();
}
echo '}';
// This is how you'll set the headers before you start streaming.
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// optional status code, defaults to 200
'status' => 200
]);
Lihat Juga
- Middleware - Menggunakan middleware dengan rute untuk autentikasi, logging, dll.
- Dependency Injection - Menyederhanakan pembuatan dan pengelolaan objek di rute.
- Why a Framework? - Memahami manfaat menggunakan kerangka kerja seperti Flight.
- Extending - Cara memperluas Flight dengan fungsionalitas Anda sendiri termasuk metode
notFound
. - php.net: preg_match - Fungsi PHP untuk pencocokan ekspresi reguler.
Pemecahan Masalah
- Parameter rute dicocokkan berdasarkan urutan, bukan nama. Pastikan urutan parameter callback cocok dengan definisi rute.
- Menggunakan
Flight::get()
tidak mendefinisikan rute; gunakanFlight::route('GET /...')
untuk routing atau konteks objek Router di grup (misalnya$router->get(...)
). - Properti executedRoute hanya ditetapkan setelah rute dieksekusi; itu NULL sebelum eksekusi.
- Streaming memerlukan fungsi buffering output Flight legacy dinonaktifkan (
flight.v2.output_buffering = false
). - Untuk injeksi dependensi, hanya definisi rute tertentu yang mendukung instansiasi berbasis container.
404 Tidak Ditemukan atau Perilaku Rute Tak Terduga
Jika Anda melihat kesalahan 404 Tidak Ditemukan (tapi Anda bersumpah dengan hidup Anda bahwa itu benar-benar ada dan bukan kesalahan ketik) ini sebenarnya bisa menjadi masalah dengan Anda mengembalikan nilai di endpoint rute Anda daripada hanya mencetaknya. Alasan untuk ini disengaja tapi bisa menyelinap pada beberapa pengembang.
Flight::route('/hello', function(){
// This might cause a 404 Not Found error
return 'Hello World';
});
// What you probably want
Flight::route('/hello', function(){
echo 'Hello World';
});
Alasan untuk ini adalah karena mekanisme khusus yang dibangun ke dalam router yang menangani output return sebagai sinyal untuk "pergi ke rute berikutnya". Anda bisa melihat perilaku yang didokumentasikan di bagian Routing.
Changelog
- v3: Menambahkan routing sumber daya, alias rute, dan dukungan streaming, grup rute, dan dukungan middleware.
- v1: Sebagian besar fitur dasar tersedia.
Learn/learn
Pelajari Tentang Flight
Flight adalah framework PHP yang cepat, sederhana, dan dapat diperluas. Ini sangat serbaguna dan dapat digunakan untuk membangun berbagai jenis aplikasi web. Ini dibangun dengan prinsip kesederhanaan dan ditulis dengan cara yang mudah dipahami dan digunakan.
Catatan: Anda akan melihat contoh yang menggunakan
Flight::
sebagai variabel statis dan beberapa yang menggunakan objek Engine$app->
. Keduanya dapat digunakan secara bergantian.$app
dan$this->app
di controller/middleware adalah pendekatan yang direkomendasikan oleh tim Flight.
Komponen Inti
Routing
Pelajari cara mengelola rute untuk aplikasi web Anda. Ini juga mencakup pengelompokan rute, parameter rute, dan middleware.
Middleware
Pelajari cara menggunakan middleware untuk memfilter permintaan dan respons di aplikasi Anda.
Autoloading
Pelajari cara memuat otomatis kelas Anda sendiri di aplikasi Anda.
Requests
Pelajari cara menangani permintaan dan respons di aplikasi Anda.
Responses
Pelajari cara mengirim respons ke pengguna Anda.
HTML Templates
Pelajari cara menggunakan engine tampilan bawaan untuk merender template HTML Anda.
Security
Pelajari cara mengamankan aplikasi Anda dari ancaman keamanan umum.
Configuration
Pelajari cara mengonfigurasi framework untuk aplikasi Anda.
Event Manager
Pelajari cara menggunakan sistem event untuk menambahkan event kustom ke aplikasi Anda.
Extending Flight
Pelajari cara memperluas framework dengan menambahkan metode dan kelas Anda sendiri.
Method Hooks and Filtering
Pelajari cara menambahkan hook event ke metode Anda dan metode framework internal.
Dependency Injection Container (DIC)
Pelajari cara menggunakan wadah injeksi dependensi (DIC) untuk mengelola dependensi aplikasi Anda.
Kelas Utilitas
Collections
Koleksi digunakan untuk menyimpan data dan dapat diakses sebagai array atau objek untuk kemudahan penggunaan.
JSON Wrapper
Ini memiliki beberapa fungsi sederhana untuk membuat pengkodean dan dekode JSON Anda konsisten.
PDO Wrapper
PDO terkadang bisa menimbulkan lebih banyak masalah daripada yang diperlukan. Kelas wrapper sederhana ini dapat membuat interaksi dengan database Anda jauh lebih mudah.
Uploaded File Handler
Kelas sederhana untuk membantu mengelola file yang diunggah dan memindahkannya ke lokasi permanen.
Konsep Penting
Why a Framework?
Berikut adalah artikel singkat tentang mengapa Anda harus menggunakan framework. Ini ide bagus untuk memahami manfaat menggunakan framework sebelum Anda mulai menggunakannya.
Selain itu, tutorial yang sangat baik telah dibuat oleh @lubiana. Meskipun tidak membahas secara mendalam tentang Flight secara khusus, panduan ini akan membantu Anda memahami beberapa konsep utama seputar framework dan mengapa mereka bermanfaat untuk digunakan. Anda dapat menemukan tutorial di sini.
Flight Compared to Other Frameworks
Jika Anda bermigrasi dari framework lain seperti Laravel, Slim, Fat-Free, atau Symfony ke Flight, halaman ini akan membantu Anda memahami perbedaan antara keduanya.
Topik Lainnya
Unit Testing
Ikuti panduan ini untuk mempelajari cara melakukan unit testing kode Flight Anda agar menjadi kokoh.
AI & Developer Experience
Pelajari bagaimana Flight bekerja dengan alat AI dan alur kerja pengembang modern untuk membantu Anda mengkode lebih cepat dan lebih cerdas.
Migrating v2 -> v3
Kompatibilitas mundur sebagian besar telah dipertahankan, tetapi ada beberapa perubahan yang harus Anda ketahui saat bermigrasi dari v2 ke v3.
Learn/unit_testing
Pengujian Unit
Gambaran Umum
Pengujian unit di Flight membantu Anda memastikan aplikasi Anda berperilaku sesuai harapan, menangkap bug lebih awal, dan membuat kode dasar Anda lebih mudah dipelihara. Flight dirancang untuk bekerja dengan lancar dengan PHPUnit, framework pengujian PHP paling populer.
Pemahaman
Pengujian unit memeriksa perilaku potongan kecil aplikasi Anda (seperti controller atau service) secara terisolasi. Di Flight, ini berarti menguji bagaimana rute, controller, dan logika Anda merespons input yang berbeda—tanpa bergantung pada status global atau layanan eksternal nyata.
Prinsip kunci:
- Uji perilaku, bukan implementasi: Fokus pada apa yang dilakukan kode Anda, bukan bagaimana cara melakukannya.
- Hindari status global: Gunakan injeksi dependensi daripada
Flight::set()
atauFlight::get()
. - Mock layanan eksternal: Ganti hal-hal seperti database atau mailer dengan double pengujian.
- Jaga pengujian cepat dan terfokus: Pengujian unit tidak boleh menyentuh database atau API nyata.
Penggunaan Dasar
Menyiapkan PHPUnit
- Instal PHPUnit dengan Composer:
composer require --dev phpunit/phpunit
- Buat direktori
tests
di root proyek Anda. - Tambahkan skrip pengujian ke
composer.json
Anda:"scripts": { "test": "phpunit --configuration phpunit.xml" }
- Buat file
phpunit.xml
:<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Flight Tests"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>
Sekarang Anda dapat menjalankan pengujian dengan composer test
.
Menguji Penangan Rute Sederhana
Misalkan Anda memiliki rute yang memvalidasi email:
// index.php
$app->route('POST /register', [ UserController::class, 'register' ]);
// UserController.php
class UserController {
protected $app;
public function __construct(flight\Engine $app) {
$this->app = $app;
}
public function register() {
$email = $this->app->request()->data->email;
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->app->json(['status' => 'error', 'message' => 'Invalid email']);
}
return $this->app->json(['status' => 'success', 'message' => 'Valid email']);
}
}
Pengujian sederhana untuk controller ini:
use PHPUnit\Framework\TestCase;
use flight\Engine;
class UserControllerTest extends TestCase {
public function testValidEmailReturnsSuccess() {
$app = new Engine();
$app->request()->data->email = 'test@example.com';
$controller = new UserController($app);
$controller->register();
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('success', $output['status']);
$this->assertEquals('Valid email', $output['message']);
}
public function testInvalidEmailReturnsError() {
$app = new Engine();
$app->request()->data->email = 'invalid-email';
$controller = new UserController($app);
$controller->register();
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('error', $output['status']);
$this->assertEquals('Invalid email', $output['message']);
}
}
Tips:
- Simulasikan data POST menggunakan
$app->request()->data
. - Hindari menggunakan static
Flight::
di pengujian Anda—gunakan instance$app
.
Menggunakan Injeksi Dependensi untuk Controller yang Dapat Diuji
Injeksi dependensi (seperti database atau mailer) ke dalam controller Anda untuk membuatnya mudah dimock di pengujian:
use flight\database\PdoWrapper;
class UserController {
protected $app;
protected $db;
protected $mailer;
public function __construct($app, $db, $mailer) {
$this->app = $app;
$this->db = $db;
$this->mailer = $mailer;
}
public function register() {
$email = $this->app->request()->data->email;
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->app->json(['status' => 'error', 'message' => 'Invalid email']);
}
$this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
$this->mailer->sendWelcome($email);
return $this->app->json(['status' => 'success', 'message' => 'User registered']);
}
}
Dan pengujian dengan mock:
use PHPUnit\Framework\TestCase;
class UserControllerDICTest extends TestCase {
public function testValidEmailSavesAndSendsEmail() {
$mockDb = $this->createMock(flight\database\PdoWrapper::class);
$mockDb->method('runQuery')->willReturn(true);
$mockMailer = new class {
public $sentEmail = null;
public function sendWelcome($email) { $this->sentEmail = $email; return true; }
};
$app = new flight\Engine();
$app->request()->data->email = 'test@example.com';
$controller = new UserController($app, $mockDb, $mockMailer);
$controller->register();
$response = $app->response()->getBody();
$result = json_decode($response, true);
$this->assertEquals('success', $result['status']);
$this->assertEquals('User registered', $result['message']);
$this->assertEquals('test@example.com', $mockMailer->sentEmail);
}
}
Penggunaan Lanjutan
- Mocking: Gunakan mock bawaan PHPUnit atau kelas anonim untuk mengganti dependensi.
- Menguji controller secara langsung: Instansiasi controller dengan
Engine
baru dan mock dependensi. - Hindari over-mocking: Biarkan logika nyata berjalan jika memungkinkan; hanya mock layanan eksternal.
Lihat Juga
- Panduan Pengujian Unit - Panduan komprehensif tentang praktik terbaik pengujian unit.
- Container Injeksi Dependensi - Cara menggunakan DIC untuk mengelola dependensi dan meningkatkan kemampuan pengujian.
- Memperluas - Cara menambahkan helper sendiri atau mengoverride kelas inti.
- Wrapper PDO - Menyederhanakan interaksi database dan lebih mudah dimock di pengujian.
- Permintaan - Menangani permintaan HTTP di Flight.
- Respons - Mengirim respons ke pengguna.
- Pengujian Unit dan Prinsip SOLID - Pelajari bagaimana prinsip SOLID dapat meningkatkan pengujian unit Anda.
Pemecahan Masalah
- Hindari menggunakan status global (
Flight::set()
,$_SESSION
, dll.) di kode dan pengujian Anda. - Jika pengujian Anda lambat, mungkin Anda sedang menulis pengujian integrasi—mock layanan eksternal untuk menjaga pengujian unit tetap cepat.
- Jika pengaturan pengujian rumit, pertimbangkan untuk merestrukturisasi kode Anda untuk menggunakan injeksi dependensi.
Changelog
- v3.15.0 - Ditambahkan contoh untuk injeksi dependensi dan mocking.
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
- Symfony memiliki ekosistem yang besar dari pengembang dan modul yang dapat digunakan untuk menyelesaikan masalah umum.
- Symfony memiliki ORM yang dilengkapi dengan fitur lengkap (Doctrine) yang dapat digunakan untuk berinteraksi dengan basis data Anda.
- Symfony memiliki banyak dokumentasi dan tutorial yang dapat digunakan untuk mempelajari framework.
- Symfony memiliki podcast, konferensi, pertemuan, video, dan sumber daya lainnya yang dapat digunakan untuk mempelajari framework.
- Symfony ditujukan untuk pengembang berpengalaman yang ingin membangun aplikasi web perusahaan yang kaya fitur.
Kekurangan dibandingkan Flight
- Symfony memiliki lebih banyak yang terjadi di bawah permukaan dibandingkan dengan Flight. Ini datang dengan biaya dramatis dalam hal kinerja. Lihat pengujian TechEmpower untuk informasi lebih lanjut.
- Flight ditujukan untuk pengembang yang ingin membangun aplikasi web yang ringan, cepat, dan mudah digunakan.
- Flight ditujukan untuk kesederhanaan dan kemudahan penggunaan.
- Salah satu fitur inti Flight adalah bahwa ia melakukan yang terbaik untuk mempertahankan kompatibilitas mundur.
- Flight tidak memiliki ketergantungan, sementara Symfony memiliki sejumlah ketergantungan
- Flight ditujukan untuk pengembang yang baru memasuki dunia framework untuk pertama kalinya.
- Flight juga dapat digunakan untuk aplikasi tingkat enterprise, tetapi tidak memiliki sebanyak contoh dan tutorial seperti Symfony. Ini juga akan memerlukan lebih banyak disiplin dari pihak pengembang untuk menjaga agar segala sesuatunya terorganisir dan terstruktur dengan baik.
- Flight memberikan pengembang lebih banyak kontrol atas aplikasi, sementara Symfony dapat menyisipkan sedikit keajaiban di belakang layar.
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/pdo_wrapper
Kelas Pembantu PDO PdoWrapper
Gambaran Umum
Kelas PdoWrapper
di Flight adalah pembantu yang ramah untuk bekerja dengan database menggunakan PDO. Ini menyederhanakan tugas database umum, menambahkan beberapa metode yang berguna untuk mengambil hasil, dan mengembalikan hasil sebagai Collections untuk akses yang mudah. Ini juga mendukung pencatatan query dan pemantauan kinerja aplikasi (APM) untuk kasus penggunaan lanjutan.
Pemahaman
Bekerja dengan database di PHP bisa sedikit verbose, terutama saat menggunakan PDO secara langsung. PdoWrapper
memperluas PDO dan menambahkan metode yang membuat querying, fetching, dan penanganan hasil jauh lebih mudah. Alih-alih mengelola pernyataan yang disiapkan dan mode fetch, Anda mendapatkan metode sederhana untuk tugas umum, dan setiap baris dikembalikan sebagai Collection, sehingga Anda dapat menggunakan notasi array atau objek.
Anda dapat mendaftarkan PdoWrapper
sebagai layanan bersama di Flight, dan kemudian menggunakannya di mana saja di aplikasi Anda melalui Flight::db()
.
Penggunaan Dasar
Mendaftarkan Pembantu PDO
Pertama, daftarkan kelas PdoWrapper
dengan Flight:
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
]
]);
Sekarang Anda dapat menggunakan Flight::db()
di mana saja untuk mendapatkan koneksi database Anda.
Menjalankan Query
runQuery()
function runQuery(string $sql, array $params = []): PDOStatement
Gunakan ini untuk INSERT, UPDATE, atau saat Anda ingin mengambil hasil secara manual:
$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM users WHERE status = ?", ['active']);
while ($row = $statement->fetch()) {
// $row adalah array
}
Anda juga dapat menggunakannya untuk penulisan:
$db->runQuery("INSERT INTO users (name) VALUES (?)", ['Alice']);
$db->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 1]);
fetchField()
function fetchField(string $sql, array $params = []): mixed
Dapatkan satu nilai tunggal dari database:
$count = Flight::db()->fetchField("SELECT COUNT(*) FROM users WHERE status = ?", ['active']);
fetchRow()
function fetchRow(string $sql, array $params = []): Collection
Dapatkan satu baris sebagai Collection (akses array/objek):
$user = Flight::db()->fetchRow("SELECT * FROM users WHERE id = ?", [123]);
echo $user['name'];
// atau
echo $user->name;
fetchAll()
function fetchAll(string $sql, array $params = []): array<Collection>
Dapatkan semua baris sebagai array dari Collections:
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE status = ?", ['active']);
foreach ($users as $user) {
echo $user['name'];
// atau
echo $user->name;
}
Menggunakan Placeholder IN()
Anda dapat menggunakan satu ?
tunggal dalam klausa IN()
dan meneruskan array atau string yang dipisahkan koma:
$ids = [1, 2, 3];
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE id IN (?)", [$ids]);
// atau
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE id IN (?)", ['1,2,3']);
Penggunaan Lanjutan
Pencatatan Query & APM
Jika Anda ingin melacak kinerja query, aktifkan pelacakan APM saat mendaftarkan:
Flight::register('db', \flight\database\PdoWrapper::class, [
'mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [/* options */], true // param terakhir mengaktifkan APM
]);
Setelah menjalankan query, Anda dapat mencatatnya secara manual tetapi APM akan mencatatnya secara otomatis jika diaktifkan:
Flight::db()->logQueries();
Ini akan memicu sebuah event (flight.db.queries
) dengan metrik koneksi dan query, yang dapat Anda dengarkan menggunakan sistem event Flight.
Contoh Lengkap
Flight::route('/users', function () {
// Dapatkan semua pengguna
$users = Flight::db()->fetchAll('SELECT * FROM users');
// Stream semua pengguna
$statement = Flight::db()->runQuery('SELECT * FROM users');
while ($user = $statement->fetch()) {
echo $user['name'];
}
// Dapatkan satu pengguna tunggal
$user = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]);
// Dapatkan satu nilai tunggal
$count = Flight::db()->fetchField('SELECT COUNT(*) FROM users');
// Sintaks IN() khusus
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]);
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', ['1,2,3,4,5']);
// Sisipkan pengguna baru
Flight::db()->runQuery("INSERT INTO users (name, email) VALUES (?, ?)", ['Bob', 'bob@example.com']);
$insert_id = Flight::db()->lastInsertId();
// Perbarui pengguna
Flight::db()->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 123]);
// Hapus 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();
});
Lihat Juga
- Collections - Pelajari cara menggunakan kelas Collection untuk akses data yang mudah.
Pemecahan Masalah
- Jika Anda mendapatkan kesalahan tentang koneksi database, periksa DSN, nama pengguna, kata sandi, dan opsi Anda.
- Semua baris dikembalikan sebagai Collections—jika Anda membutuhkan array biasa, gunakan
$collection->getData()
. - Untuk query
IN (?)
, pastikan untuk meneruskan array atau string yang dipisahkan koma.
Changelog
- v3.2.0 - Rilis awal PdoWrapper dengan metode query dan fetch dasar.
Learn/dependency_injection_container
Wadah Injeksi Ketergantungan
Gambaran Umum
Wadah Injeksi Ketergantungan (DIC) adalah peningkatan kuat yang memungkinkan Anda untuk mengelola ketergantungan aplikasi Anda.
Pemahaman
Injeksi Ketergantungan (DI) adalah konsep kunci dalam framework PHP modern dan digunakan untuk mengelola instansiasi dan konfigurasi objek. Beberapa contoh pustaka DIC adalah: flightphp/container, Dice, Pimple, PHP-DI, dan league/container.
Sebuah DIC adalah cara mewah untuk memungkinkan Anda membuat dan mengelola kelas Anda di lokasi terpusat. Ini berguna ketika Anda perlu meneruskan objek yang sama ke beberapa kelas (seperti controller atau middleware Anda misalnya).
Penggunaan Dasar
Cara lama melakukan hal-hal mungkin terlihat seperti ini:
require 'vendor/autoload.php';
// class to manage users from the 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());
}
}
// in your routes.php file
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$UserController = new UserController($db);
Flight::route('/user/@id', [ $UserController, 'view' ]);
// other UserController routes...
Flight::start();
Anda dapat melihat dari kode di atas bahwa kami membuat objek PDO
baru dan meneruskannya
ke kelas UserController
kami. Ini baik untuk aplikasi kecil, tetapi saat aplikasi
Anda berkembang, Anda akan menemukan bahwa Anda membuat atau meneruskan objek PDO
yang sama
di beberapa tempat. Inilah di mana DIC sangat berguna.
Berikut adalah contoh yang sama menggunakan DIC (menggunakan Dice):
require 'vendor/autoload.php';
// same class as above. Nothing changed
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());
}
}
// create a new container
$container = new \Dice\Dice;
// add a rule to tell the container how to create a PDO object
// don't forget to reassign it to itself like below!
$container = $container->addRule('PDO', [
// shared means that the same object will be returned each time
'shared' => true,
'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);
// This registers the container handler so Flight knows to use it.
Flight::registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
// now we can use the container to create our UserController
Flight::route('/user/@id', [ UserController::class, 'view' ]);
Flight::start();
Saya yakin Anda mungkin berpikir bahwa ada banyak kode tambahan yang ditambahkan ke contoh ini.
Sihirnya muncul ketika Anda memiliki controller lain yang membutuhkan objek PDO
.
// If all your controllers have a constructor that needs a PDO object
// each of the routes below will automatically have it injected!!!
Flight::route('/company/@id', [ CompanyController::class, 'view' ]);
Flight::route('/organization/@id', [ OrganizationController::class, 'view' ]);
Flight::route('/category/@id', [ CategoryController::class, 'view' ]);
Flight::route('/settings', [ SettingsController::class, 'view' ]);
Bonus tambahan dari memanfaatkan DIC adalah bahwa pengujian unit menjadi jauh lebih mudah. Anda bisa membuat objek mock dan meneruskannya ke kelas Anda. Ini adalah manfaat besar ketika Anda menulis tes untuk aplikasi Anda!
Membuat Penangan DIC Terpusat
Anda dapat membuat penangan DIC terpusat di file layanan Anda dengan memperluas aplikasi Anda. Berikut adalah contoh:
// services.php
// create a new container
$container = new \Dice\Dice;
// don't forget to reassign it to itself like below!
$container = $container->addRule('PDO', [
// shared means that the same object will be returned each time
'shared' => true,
'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);
// now we can create a mappable method to create any object.
Flight::map('make', function($class, $params = []) use ($container) {
return $container->create($class, $params);
});
// This registers the container handler so Flight knows to use it for controllers/middleware
Flight::registerContainerHandler(function($class, $params) {
Flight::make($class, $params);
});
// lets say we have the following sample class that takes a PDO object in the constructor
class EmailCron {
protected PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function send() {
// code that sends an email
}
}
// And finally you can create objects using dependency injection
$emailCron = Flight::make(EmailCron::class);
$emailCron->send();
flightphp/container
Flight memiliki plugin yang menyediakan wadah sederhana yang sesuai dengan PSR-11 yang dapat Anda gunakan untuk menangani injeksi ketergantungan Anda. Berikut adalah contoh cepat tentang cara menggunakannya:
// index.php for example
require 'vendor/autoload.php';
use flight\Container;
$container = new Container;
$container->set(PDO::class, fn(): PDO => new PDO('sqlite::memory:'));
Flight::registerContainerHandler([$container, 'get']);
class TestController {
private PDO $pdo;
function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
function index() {
var_dump($this->pdo);
// will output this correctly!
}
}
Flight::route('GET /', [TestController::class, 'index']);
Flight::start();
Penggunaan Lanjutan flightphp/container
Anda juga dapat menyelesaikan ketergantungan secara rekursif. Berikut adalah contoh:
<?php
require 'vendor/autoload.php';
use flight\Container;
class User {}
interface UserRepository {
function find(int $id): ?User;
}
class PdoUserRepository implements UserRepository {
private PDO $pdo;
function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
function find(int $id): ?User {
// Implementation ...
return null;
}
}
$container = new Container;
$container->set(PDO::class, static fn(): PDO => new PDO('sqlite::memory:'));
$container->set(UserRepository::class, PdoUserRepository::class);
$userRepository = $container->get(UserRepository::class);
var_dump($userRepository);
/*
object(PdoUserRepository)#4 (1) {
["pdo":"PdoUserRepository":private]=>
object(PDO)#3 (0) {
}
}
*/
DICE
Anda juga dapat membuat penangan DIC sendiri. Ini berguna jika Anda memiliki wadah kustom yang ingin Anda gunakan yang bukan PSR-11 (Dice). Lihat bagian penggunaan dasar untuk cara melakukannya.
Selain itu, ada beberapa default yang membantu yang akan memudahkan hidup Anda saat menggunakan Flight.
Instance Engine
Jika Anda menggunakan instance Engine
di controller/middleware Anda, berikut
cara Anda mengonfigurasinya:
// Somewhere in your bootstrap file
$engine = Flight::app();
$container = new \Dice\Dice;
$container = $container->addRule('*', [
'substitutions' => [
// This is where you pass in the instance
Engine::class => $engine
]
]);
$engine->registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
// Now you can use the Engine instance in your controllers/middleware
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 wadah, dengan Dice ini mudah karena mereka akan diselesaikan secara otomatis oleh wadah. Berikut adalah contoh:
$container = new \Dice\Dice;
// If you don't need to inject any dependencies into your classes
// you don't need to define anything!
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');
PSR-11
Flight juga dapat menggunakan wadah apa pun yang sesuai dengan PSR-11. Ini berarti bahwa Anda dapat menggunakan wadah apa pun yang mengimplementasikan antarmuka PSR-11. Berikut adalah contoh menggunakan wadah PSR-11 League:
require 'vendor/autoload.php';
// same UserController class as above
$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 bisa sedikit lebih verbose daripada contoh Dice sebelumnya, itu masih mendapatkan pekerjaan selesai dengan manfaat yang sama!
Lihat Juga
- Memperluas Flight - Pelajari bagaimana Anda dapat menambahkan injeksi ketergantungan ke kelas Anda sendiri dengan memperluas framework.
- Konfigurasi - Pelajari bagaimana mengonfigurasi Flight untuk aplikasi Anda.
- Rute - Pelajari bagaimana mendefinisikan rute untuk aplikasi Anda dan bagaimana injeksi ketergantungan bekerja dengan controller.
- Middleware - Pelajari bagaimana membuat middleware untuk aplikasi Anda dan bagaimana injeksi ketergantungan bekerja dengan middleware.
Pemecahan Masalah
- Jika Anda mengalami masalah dengan wadah Anda, pastikan bahwa Anda meneruskan nama kelas yang benar ke wadah.
Changelog
- v3.7.0 - Ditambahkan kemampuan untuk mendaftarkan penangan DIC ke Flight.
Learn/middleware
Middleware
Ikhtisar
Flight mendukung middleware rute dan grup rute. Middleware adalah bagian dari aplikasi Anda di mana kode dieksekusi sebelum (atau setelah) callback rute. Ini adalah cara yang bagus untuk menambahkan pemeriksaan autentikasi API dalam kode Anda, atau untuk memvalidasi bahwa pengguna memiliki izin untuk mengakses rute.
Pemahaman
Middleware dapat sangat menyederhanakan aplikasi Anda. Alih-alih pewarisan kelas abstrak yang kompleks atau override metode, middleware memungkinkan Anda mengontrol rute dengan menetapkan logika aplikasi kustom terhadapnya. Anda dapat membayangkan middleware seperti sebuah sandwich. Anda memiliki roti di luar, dan kemudian lapisan topik seperti selada, tomat, daging dan keju. Kemudian bayangkan seperti setiap permintaan adalah seperti menggigit sandwich di mana Anda makan lapisan luar terlebih dahulu dan bekerja menuju inti.
Berikut adalah visualisasi bagaimana middleware bekerja. Kemudian kami akan menunjukkan kepada Anda contoh praktis bagaimana ini berfungsi.
Permintaan pengguna di URL /api ---->
Middleware->before() dieksekusi ----->
Callable/method yang terpasang ke /api dieksekusi dan respons dihasilkan ------>
Middleware->after() dieksekusi ----->
Pengguna menerima respons dari server
Dan berikut adalah contoh praktis:
Pengguna menavigasi ke URL /dashboard
LoggedInMiddleware->before() dieksekusi
before() memeriksa sesi login yang valid
jika ya lakukan tidak ada dan lanjutkan eksekusi
jika tidak arahkan pengguna ke /login
Callable/method yang terpasang ke /api dieksekusi dan respons dihasilkan
LoggedInMiddleware->after() tidak memiliki apa pun yang didefinisikan sehingga membiarkan eksekusi berlanjut
Pengguna menerima HTML dashboard dari server
Urutan Eksekusi
Fungsi middleware dieksekusi dalam urutan mereka ditambahkan ke rute. Eksekusi mirip dengan bagaimana Slim Framework menangani ini.
Metode before()
dieksekusi dalam urutan ditambahkan, dan metode after()
dieksekusi dalam urutan terbalik.
Contoh: Middleware1->before(), Middleware2->before(), Middleware2->after(), Middleware1->after().
Penggunaan Dasar
Anda dapat menggunakan middleware sebagai metode callable apa pun termasuk fungsi anonim atau kelas (direkomendasikan)
Fungsi Anonim
Berikut adalah contoh sederhana:
Flight::route('/path', function() { echo ' Here I am!'; })->addMiddleware(function() {
echo 'Middleware first!';
});
Flight::start();
// Ini akan menghasilkan "Middleware first! Here I am!"
Catatan: Saat menggunakan fungsi anonim, satu-satunya metode yang diinterpretasikan adalah metode
before()
. Anda tidak bisa mendefinisikan perilakuafter()
dengan kelas anonim.
Menggunakan Kelas
Middleware dapat (dan harus) didaftarkan sebagai kelas. Jika Anda membutuhkan fungsionalitas "after", Anda harus menggunakan kelas.
class MyMiddleware {
public function before($params) {
echo 'Middleware first!';
}
public function after($params) {
echo 'Middleware last!';
}
}
$MyMiddleware = new MyMiddleware();
Flight::route('/path', function() { echo ' Here I am! '; })->addMiddleware($MyMiddleware);
// juga ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]);
Flight::start();
// Ini akan menampilkan "Middleware first! Here I am! Middleware last!"
Anda juga hanya bisa mendefinisikan nama kelas middleware dan itu akan menginstansiasi kelas.
Flight::route('/path', function() { echo ' Here I am! '; })->addMiddleware(MyMiddleware::class);
Catatan: Jika Anda hanya memasukkan nama middleware, itu akan secara otomatis dieksekusi oleh dependency injection container dan middleware akan dieksekusi dengan parameter yang dibutuhkan. Jika Anda tidak memiliki dependency injection container yang terdaftar, itu akan memasukkan instance
flight\Engine
ke dalam__construct(Engine $app)
secara default.
Menggunakan Rute dengan Parameter
Jika Anda membutuhkan parameter dari rute Anda, mereka akan diteruskan dalam satu array ke fungsi middleware Anda. (function($params) { ... }
atau public function before($params) { ... }
). Alasan untuk ini adalah bahwa Anda dapat menyusun parameter Anda menjadi grup dan dalam beberapa grup tersebut, parameter Anda mungkin muncul dalam urutan yang berbeda yang akan merusak fungsi middleware dengan merujuk ke parameter yang salah. Dengan cara ini, Anda dapat mengaksesnya berdasarkan nama bukan posisi.
use flight\Engine;
class RouteSecurityMiddleware {
protected Engine $app;
public function __construct(Engine $app) {
$this->app = $app;
}
public function before(array $params) {
$clientId = $params['clientId'];
// jobId mungkin atau mungkin tidak diteruskan
$jobId = $params['jobId'] ?? 0;
// mungkin jika tidak ada ID pekerjaan, Anda tidak perlu mencari apa pun.
if($jobId === 0) {
return;
}
// lakukan pencarian semacamnya di database Anda
$isValid = !!$this->app->db()->fetchField("SELECT 1 FROM client_jobs WHERE client_id = ? AND job_id = ?", [ $clientId, $jobId ]);
if($isValid !== true) {
$this->app->halt(400, 'You are blocked, muahahaha!');
}
}
}
// routes.php
$router->group('/client/@clientId/job/@jobId', function(Router $router) {
// Grup ini di bawah masih mendapatkan middleware parent
// Tapi parameter diteruskan dalam satu array tunggal
// di middleware.
$router->group('/job/@jobId', function(Router $router) {
$router->get('', [ JobController::class, 'view' ]);
$router->put('', [ JobController::class, 'update' ]);
$router->delete('', [ JobController::class, 'delete' ]);
// lebih banyak rute...
});
}, [ RouteSecurityMiddleware::class ]);
Mengelompokkan Rute dengan 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 banyak rute berdasarkan middleware Auth untuk memeriksa kunci API di header.
// ditambahkan di akhir metode grup
Flight::group('/api', function() {
// Rute "kosong" ini sebenarnya akan cocok dengan /api
Flight::route('', function() { echo 'api'; }, false, 'api');
// Ini akan cocok dengan /api/users
Flight::route('/users', function() { echo 'users'; }, false, 'users');
// Ini akan cocok dengan /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');
}, [ ApiAuthMiddleware::class ]); // atau [ new ApiAuthMiddleware() ], hal yang sama
Kasus Penggunaan Umum
Validasi Kunci API
Jika Anda ingin melindungi rute /api
Anda dengan memverifikasi kunci API yang benar, Anda dapat dengan mudah menanganinya dengan middleware.
use flight\Engine;
class ApiMiddleware {
protected Engine $app;
public function __construct(Engine $app) {
$this->app = $app;
}
public function before(array $params) {
$authorizationHeader = $this->app->request()->getHeader('Authorization');
$apiKey = str_replace('Bearer ', '', $authorizationHeader);
// lakukan pencarian di database Anda untuk kunci api
$apiKeyHash = hash('sha256', $apiKey);
$hasValidApiKey = !!$this->db()->fetchField("SELECT 1 FROM api_keys WHERE hash = ? AND valid_date >= NOW()", [ $apiKeyHash ]);
if($hasValidApiKey !== true) {
$this->app->jsonHalt(['error' => 'Invalid API Key']);
}
}
}
// routes.php
$router->group('/api', function(Router $router) {
$router->get('/users', [ ApiController::class, 'getUsers' ]);
$router->get('/companies', [ ApiController::class, 'getCompanies' ]);
// lebih banyak rute...
}, [ ApiMiddleware::class ]);
Sekarang semua rute API Anda dilindungi oleh middleware validasi kunci API yang Anda siapkan! Jika Anda memasukkan lebih banyak rute ke dalam grup router, mereka akan langsung memiliki perlindungan yang sama!
Validasi Login
Apakah Anda ingin melindungi beberapa rute agar hanya tersedia untuk pengguna yang login? Itu dapat dengan mudah dicapai dengan middleware!
use flight\Engine;
class LoggedInMiddleware {
protected Engine $app;
public function __construct(Engine $app) {
$this->app = $app;
}
public function before(array $params) {
$session = $this->app->session();
if($session->get('logged_in') !== true) {
$this->app->redirect('/login');
exit;
}
}
}
// routes.php
$router->group('/admin', function(Router $router) {
$router->get('/dashboard', [ DashboardController::class, 'index' ]);
$router->get('/clients', [ ClientController::class, 'index' ]);
// lebih banyak rute...
}, [ LoggedInMiddleware::class ]);
Validasi Parameter Rute
Apakah Anda ingin melindungi pengguna Anda dari mengubah nilai di URL untuk mengakses data yang seharusnya tidak mereka akses? Itu dapat diselesaikan dengan middleware!
use flight\Engine;
class RouteSecurityMiddleware {
protected Engine $app;
public function __construct(Engine $app) {
$this->app = $app;
}
public function before(array $params) {
$clientId = $params['clientId'];
$jobId = $params['jobId'];
// lakukan pencarian semacamnya di database Anda
$isValid = !!$this->app->db()->fetchField("SELECT 1 FROM client_jobs WHERE client_id = ? AND job_id = ?", [ $clientId, $jobId ]);
if($isValid !== true) {
$this->app->halt(400, 'You are blocked, muahahaha!');
}
}
}
// routes.php
$router->group('/client/@clientId/job/@jobId', function(Router $router) {
$router->get('', [ JobController::class, 'view' ]);
$router->put('', [ JobController::class, 'update' ]);
$router->delete('', [ JobController::class, 'delete' ]);
// lebih banyak rute...
}, [ RouteSecurityMiddleware::class ]);
Menangani Eksekusi Middleware
Misalkan Anda memiliki middleware auth dan Anda ingin mengarahkan pengguna ke halaman login jika mereka tidak terautentikasi. Anda memiliki beberapa opsi yang tersedia:
- Anda dapat mengembalikan false dari fungsi middleware dan Flight akan secara otomatis mengembalikan kesalahan 403 Forbidden, tapi tidak ada kustomisasi.
- Anda dapat mengarahkan pengguna ke halaman login menggunakan
Flight::redirect()
. - Anda dapat membuat kesalahan kustom dalam middleware dan menghentikan eksekusi rute.
Sederhana dan Langsung
Berikut adalah contoh sederhana return false;
:
class MyMiddleware {
public function before($params) {
$hasUserKey = Flight::session()->exists('user');
if ($hasUserKey === false) {
return false;
}
// karena itu benar, semuanya terus berjalan
}
}
Contoh Pengalihan
Berikut adalah contoh mengarahkan pengguna ke halaman login:
class MyMiddleware {
public function before($params) {
$hasUserKey = Flight::session()->exists('user');
if ($hasUserKey === false) {
Flight::redirect('/login');
exit;
}
}
}
Contoh Kesalahan Kustom
Misalkan Anda perlu melempar kesalahan JSON karena Anda membangun API. Anda dapat melakukannya seperti ini:
class MyMiddleware {
public function before($params) {
$authorization = Flight::request()->getHeader('Authorization');
if(empty($authorization)) {
Flight::jsonHalt(['error' => 'You must be logged in to access this page.'], 403);
// atau
Flight::json(['error' => 'You must be logged in to access this page.'], 403);
exit;
// atau
Flight::halt(403, json_encode(['error' => 'You must be logged in to access this page.']);
}
}
}
Lihat Juga
- Routing - Cara memetakan rute ke controller dan merender tampilan.
- Requests - Memahami cara menangani permintaan masuk.
- Responses - Cara menyesuaikan respons HTTP.
- Dependency Injection - Menyederhanakan pembuatan dan pengelolaan objek di rute.
- Mengapa Framework? - Memahami manfaat menggunakan framework seperti Flight.
- Contoh Strategi Eksekusi Middleware
Pemecahan Masalah
- Jika Anda memiliki pengalihan di middleware Anda, tapi aplikasi Anda tampaknya tidak mengalihkan, pastikan Anda menambahkan pernyataan
exit;
di middleware Anda.
Changelog
- v3.1: Menambahkan dukungan untuk middleware.
Learn/filtering
Penyaringan
Gambaran Umum
Flight memungkinkan Anda menyaring metode yang dipetakan sebelum dan sesudah mereka dipanggil.
Pemahaman
Tidak ada hook yang telah ditentukan sebelumnya yang perlu Anda hafal. Anda dapat menyaring metode kerangka kerja default apa pun serta metode kustom apa pun yang telah Anda petakan.
Fungsi filter terlihat seperti ini:
/**
* @param array $params Parameter yang diteruskan ke metode yang disaring.
* @param string $output (hanya penyanggaan output v2) Output dari metode yang disaring.
* @return bool Kembalikan true/void atau jangan kembalikan untuk melanjutkan rantai, false untuk memutus rantai.
*/
function (array &$params, string &$output): bool {
// Kode filter
}
Dengan menggunakan variabel yang diteruskan, Anda dapat memanipulasi parameter input dan/atau output.
Anda dapat menjalankan filter sebelum metode dengan melakukan:
Flight::before('start', function (array &$params, string &$output): bool {
// Lakukan sesuatu
});
Anda dapat menjalankan filter setelah metode dengan melakukan:
Flight::after('start', function (array &$params, string &$output): bool {
// Lakukan sesuatu
});
Anda dapat menambahkan sebanyak filter yang Anda inginkan ke metode apa pun. Mereka akan dipanggil dalam urutan yang mereka dinyatakan.
Berikut adalah contoh proses penyaringan:
// Petakan metode kustom
Flight::map('hello', function (string $name) {
return "Hello, $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 .= " Have a nice day!";
return true;
});
// Panggil metode kustom
echo Flight::hello('Bob');
Ini seharusnya menampilkan:
Hello Fred! Have a nice day!
Jika Anda telah mendefinisikan beberapa filter, Anda dapat memutus rantai dengan mengembalikan false
dalam fungsi filter mana pun:
Flight::before('start', function (array &$params, string &$output): bool {
echo 'one';
return true;
});
Flight::before('start', function (array &$params, string &$output): bool {
echo 'two';
// Ini akan mengakhiri rantai
return false;
});
// Ini tidak akan dipanggil
Flight::before('start', function (array &$params, string &$output): bool {
echo 'three';
return true;
});
Catatan: Metode inti seperti
map
danregister
tidak dapat disaring karena mereka dipanggil secara langsung dan tidak dipanggil secara dinamis. Lihat Memperluas Flight untuk informasi lebih lanjut.
Lihat Juga
Pemecahan Masalah
- Pastikan Anda mengembalikan
false
dari fungsi filter Anda jika Anda ingin rantai berhenti. Jika Anda tidak mengembalikan apa pun, rantai akan berlanjut.
Log Perubahan
- v2.0 - Rilis Awal.
Learn/requests
Permintaan
Gambaran Umum
Flight merangkum permintaan HTTP ke dalam satu objek, yang dapat diakses dengan melakukan:
$request = Flight::request();
Pemahaman
Permintaan HTTP adalah salah satu aspek inti yang perlu dipahami tentang siklus hidup HTTP. Pengguna melakukan tindakan pada peramban web atau klien HTTP, dan mereka mengirim serangkaian header, body, URL, dll ke proyek Anda. Anda dapat menangkap header ini (bahasa peramban, jenis kompresi yang dapat ditangani, agen pengguna, dll) dan menangkap body serta URL yang dikirim ke aplikasi Flight Anda. Permintaan ini sangat penting agar aplikasi Anda memahami apa yang harus dilakukan selanjutnya.
Penggunaan Dasar
PHP memiliki beberapa super global termasuk $_GET
, $_POST
, $_REQUEST
, $_SERVER
, $_FILES
, dan $_COOKIE
. Flight mengabstraksikan ini menjadi Collections yang berguna. Anda dapat mengakses properti query
, data
, cookies
, dan files
sebagai array atau objek.
Catatan: Sangat TIDAK DISARANKAN menggunakan super global ini dalam proyek Anda dan seharusnya dirujuk melalui objek
request()
.Catatan: Tidak ada abstraksi yang tersedia untuk
$_ENV
.
$_GET
Anda dapat mengakses array $_GET
melalui properti query
:
// GET /search?keyword=something
Flight::route('/search', function(){
$keyword = Flight::request()->query['keyword'];
// atau
$keyword = Flight::request()->query->keyword;
echo "Anda sedang mencari: $keyword";
// query database atau sesuatu yang lain dengan $keyword
});
$_POST
Anda dapat mengakses array $_POST
melalui properti data
:
Flight::route('POST /submit', function(){
$name = Flight::request()->data['name'];
$email = Flight::request()->data['email'];
// atau
$name = Flight::request()->data->name;
$email = Flight::request()->data->email;
echo "Anda mengirimkan: $name, $email";
// simpan ke database atau sesuatu yang lain dengan $name dan $email
});
$_COOKIE
Anda dapat mengakses array $_COOKIE
melalui properti cookies
:
Flight::route('GET /login', function(){
$savedLogin = Flight::request()->cookies['myLoginCookie'];
// atau
$savedLogin = Flight::request()->cookies->myLoginCookie;
// periksa apakah benar-benar tersimpan atau tidak dan jika ya, login otomatis mereka
if($savedLogin) {
Flight::redirect('/dashboard');
return;
}
});
Untuk bantuan dalam mengatur nilai cookie baru, lihat overclokk/cookie
$_SERVER
Ada jalan pintas yang tersedia untuk mengakses array $_SERVER
melalui metode getVar()
:
$host = Flight::request()->getVar('HTTP_HOST');
$_FILES
Anda dapat mengakses file yang diunggah melalui properti files
:
// akses mentah ke properti $_FILES. Lihat di bawah untuk pendekatan yang direkomendasikan
$uploadedFile = Flight::request()->files['myFile'];
// atau
$uploadedFile = Flight::request()->files->myFile;
Lihat Uploaded File Handler untuk info lebih lanjut.
Pemrosesan Unggahan File
v3.12.0
Anda dapat memproses unggahan file menggunakan framework dengan beberapa metode bantu. Ini pada dasarnya berarti menarik 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 melooping 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 sanitasi input pengguna, terutama saat menangani unggahan file. Selalu validasi jenis ekstensi yang akan diizinkan diunggah, tetapi Anda juga harus memvalidasi "magic bytes" file untuk memastikan itu benar-benar jenis file yang diklaim pengguna. Ada artikel dan library yang tersedia untuk membantu dengan ini.
Body Permintaan
Untuk mendapatkan body permintaan HTTP mentah, misalnya saat menangani permintaan POST/PUT, Anda dapat melakukan:
Flight::route('POST /users/xml', function(){
$xmlBody = Flight::request()->getBody();
// lakukan sesuatu dengan XML yang dikirim.
});
Body JSON
Jika Anda menerima permintaan dengan jenis konten application/json
dan data contoh {"id": 123}
itu akan tersedia dari properti data
:
$id = Flight::request()->data->id;
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();
Metode Permintaan
Anda dapat mengakses metode permintaan menggunakan properti method
atau metode getMethod()
:
$method = Flight::request()->method; // sebenarnya diisi oleh getMethod()
$method = Flight::request()->getMethod();
Catatan: Metode getMethod()
pertama-tama menarik metode dari $_SERVER['REQUEST_METHOD']
, kemudian dapat ditimpa
oleh $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']
jika ada atau $_REQUEST['_method']
jika ada.
Properti Objek Permintaan
Objek permintaan menyediakan properti berikut:
- body - Body permintaan HTTP mentah
- url - URL yang diminta
- base - Subdirektori induk dari URL
- method - Metode permintaan (GET, POST, PUT, DELETE)
- referrer - URL referrer
- ip - Alamat IP klien
- ajax - Apakah permintaan adalah permintaan AJAX
- scheme - Protokol server (http, https)
- user_agent - Informasi peramban
- type - Jenis konten
- length - Panjang konten
- query - Parameter string query
- data - Data POST atau data JSON
- cookies - Data cookie
- files - File yang diunggah
- secure - Apakah koneksi aman
- accept - Parameter accept HTTP
- proxy_ip - Alamat IP proxy klien. Memindai array
$_SERVER
untukHTTP_CLIENT_IP
,HTTP_X_FORWARDED_FOR
,HTTP_X_FORWARDED
,HTTP_X_CLUSTER_CLIENT_IP
,HTTP_FORWARDED_FOR
,HTTP_FORWARDED
dalam urutan itu. - host - Nama host permintaan
- servername - SERVER_NAME dari
$_SERVER
Metode Bantu
Ada beberapa metode bantu untuk menyusun bagian-bagian URL, atau menangani header tertentu.
URL Lengkap
Anda dapat mengakses URL permintaan lengkap menggunakan metode getFullUrl()
:
$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar
URL Dasar
Anda dapat mengakses URL dasar menggunakan metode getBaseUrl()
:
// http://example.com/path/to/something/cool?query=yes+thanks
$url = Flight::request()->getBaseUrl();
// https://example.com
// Perhatikan, tidak ada slash akhir.
Parsing Query
Anda dapat meneruskan URL ke metode parseQuery()
untuk mem-parsing string query menjadi array asosiatif:
$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']
Negosiasi Jenis Konten Accept
v3.17.2
Anda dapat menggunakan metode negotiateContentType()
untuk menentukan jenis konten terbaik untuk merespons berdasarkan header Accept
yang dikirim oleh klien.
// Contoh header Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
// Yang di bawah ini mendefinisikan apa yang Anda dukung.
$availableTypes = ['application/json', 'application/xml'];
$typeToServe = Flight::request()->negotiateContentType($availableTypes);
if ($typeToServe === 'application/json') {
// Layani respons JSON
} elseif ($typeToServe === 'application/xml') {
// Layani respons XML
} else {
// Default ke sesuatu yang lain atau lempar kesalahan
}
Catatan: Jika tidak ada jenis yang tersedia ditemukan dalam header
Accept
, metode akan mengembalikannull
. Jika tidak ada headerAccept
yang didefinisikan, metode akan mengembalikan jenis pertama dalam array$availableTypes
.
Lihat Juga
- Routing - Lihat cara memetakan rute ke controller dan merender tampilan.
- Responses - Cara menyesuaikan respons HTTP.
- Why a Framework? - Bagaimana permintaan cocok ke dalam gambaran besar.
- Collections - Bekerja dengan kumpulan data.
- Uploaded File Handler - Menangani unggahan file.
Pemecahan Masalah
request()->ip
danrequest()->proxy_ip
bisa berbeda jika webserver Anda berada di belakang proxy, load balancer, dll.
Changelog
- v3.17.2 - Menambahkan negotiateContentType()
- v3.12.0 - Menambahkan kemampuan menangani unggahan file melalui objek request.
- v1.0 - Rilis awal.
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:
- Pengembangan Cepat: Kerangka menyediakan banyak fungsionalitas langsung dari kotaknya. Ini berarti Anda dapat membangun aplikasi web lebih cepat. Anda tidak perlu menulis sebanyak itu karena kerangka menyediakan banyak fungsionalitas yang Anda butuhkan.
- Konsistensi: Kerangka menyediakan cara yang konsisten untuk melakukan hal-hal. Ini memudahkan Anda untuk memahami cara kerja kode dan memudahkan pengembang lain untuk memahami kode Anda. Jika Anda memiliki skrip demi skrip, Anda mungkin kehilangan konsistensi antara skrip, terutama jika Anda bekerja dengan tim pengembang.
- Keamanan: Kerangka menyediakan fitur keamanan yang membantu melindungi aplikasi web Anda dari ancaman keamanan umum. Ini berarti Anda tidak perlu khawatir sebanyak itu tentang keamanan karena kerangka mengurus banyak hal untuk Anda.
- Komunitas: Kerangka memiliki komunitas besar pengembang yang berkontribusi pada kerangka. Ini berarti Anda dapat mendapatkan bantuan dari pengembang lain ketika Anda memiliki pertanyaan atau masalah. Ini juga berarti bahwa ada banyak sumber daya yang tersedia untuk membantu Anda belajar cara menggunakan kerangka.
- Praktik Terbaik: Kerangka dibangun menggunakan praktik terbaik. Ini berarti Anda dapat belajar dari kerangka dan menggunakan praktik terbaik yang sama dalam kode Anda sendiri. Ini dapat membantu Anda menjadi pemrogram yang lebih baik. Kadang-kadang Anda tidak tahu apa yang tidak Anda ketahui dan itu dapat merugikan Anda pada akhirnya.
- Ekstensibilitas: Kerangka dirancang untuk diperluas. Ini berarti Anda dapat menambahkan fungsionalitas Anda sendiri ke dalam kerangka. Ini memungkinkan Anda untuk membangun aplikasi web yang disesuaikan dengan kebutuhan spesifik Anda.
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:
- Seorang pengguna pergi ke peramban Anda dan mengetik
http://example.com/user/1234
. - Server menerima permintaan dan melihat URL dan meneruskannya ke kode aplikasi Flight Anda.
- Katakanlah di kode Flight Anda Anda memiliki sesuatu seperti
Flight::route('/user/@id', [ 'UserController', 'viewUserProfile' ]);
. Kode aplikasi Flight Anda melihat URL dan melihat bahwa itu cocok dengan jalur yang telah Anda definisikan, dan kemudian menjalankan kode yang telah Anda definisikan untuk jalur tersebut. - Router Flight kemudian akan dijalankan dan memanggil metode
viewUserProfile($id)
dalam kelasUserController
, dengan melewatkan1234
sebagai argumen$id
dalam metode tersebut. - Kode dalam metode
viewUserProfile()
Anda kemudian akan dijalankan dan melakukan apa yang telah Anda katakan untuk dilakukan. Anda mungkin akan mengeluarkan beberapa HTML untuk halaman profil pengguna, atau jika ini adalah API RESTful, Anda mungkin akan mengeluarkan respons JSON dengan informasi pengguna. - Flight membungkus ini dengan rapi, menghasilkan header respons dan mengirimkannya kembali ke peramban pengguna.
- Pengguna merasa senang dan memberi diri mereka pelukan hangat!
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:
- Routing Terpusat: Anda dapat menyimpan semua jalur Anda di satu tempat. Ini memudahkan untuk melihat jalur mana yang Anda miliki dan apa yang mereka lakukan. Ini juga memudahkan untuk mengubahnya jika Anda perlu.
- Parameter Jalur: Anda dapat menggunakan parameter jalur untuk melewatkan data ke metode jalur Anda. Ini adalah cara yang bagus untuk menjaga kode Anda tetap bersih dan teratur.
- Kelompok Jalur: Anda dapat mengelompokkan jalur bersama. Ini bagus untuk menjaga kode Anda teratur dan untuk menerapkan middleware ke sekelompok jalur.
- Alias Jalur: Anda dapat menetapkan alias ke sebuah jalur, sehingga URL dapat dibuat secara dinamis nanti di kode Anda (seperti template misalnya). Contoh: alih-alih menghardcode
/user/1234
di kode Anda, Anda bisa merujuk ke aliasuser_view
dan melewatkanid
sebagai parameter. Ini sangat memudahkan jika Anda memutuskan untuk mengubahnya menjadi/admin/user/1234
nanti. Anda tidak perlu mengubah semua URL yang Anda hardcode, cukup URL yang terhubung ke jalur. - Middleware Jalur: Anda dapat menambahkan middleware ke jalur Anda. Middleware sangat kuat dalam menambahkan perilaku tertentu ke aplikasi Anda, seperti mengotentikasi bahwa pengguna tertentu dapat mengakses jalur atau kelompok jalur.
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
Gambaran Umum
Flight membantu menghasilkan sebagian header respons untuk Anda, tetapi Anda memegang sebagian besar kendali atas apa yang Anda kirim kembali ke pengguna. Sebagian besar waktu Anda akan mengakses objek response()
secara langsung, tetapi Flight memiliki beberapa metode pembantu untuk mengatur beberapa header respons untuk Anda.
Pemahaman
Setelah pengguna mengirimkan permintaan mereka ke aplikasi Anda, Anda perlu menghasilkan respons yang tepat untuk mereka. Mereka telah mengirimkan informasi seperti bahasa yang mereka sukai, apakah mereka dapat menangani jenis kompresi tertentu, agen pengguna mereka, dll., dan setelah memproses semuanya, saatnya mengirimkan respons yang tepat kembali kepada mereka. Ini bisa berupa pengaturan header, mengeluarkan body HTML atau JSON untuk mereka, atau mengarahkan mereka ke halaman.
Penggunaan Dasar
Mengirim Body Respons
Flight menggunakan ob_start()
untuk membuffer output. Ini berarti Anda dapat menggunakan echo
atau print
untuk mengirim respons ke pengguna dan Flight akan menangkapnya dan mengirimkannya kembali ke 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 body juga.
// Ini akan mengirim "Hello, World!" ke browser pengguna
Flight::route('/', function() {
// verbose, tapi kadang-kadang diperlukan saat Anda membutuhkannya
Flight::response()->write("Hello, World!");
// jika Anda ingin mengambil body yang telah Anda atur pada titik ini
// Anda bisa melakukannya seperti ini
$body = Flight::response()->getBody();
});
JSON
Flight menyediakan dukungan untuk mengirim respons JSON dan JSONP. Untuk mengirim respons JSON, Anda meneruskan beberapa data yang akan dikodekan JSON:
Flight::route('/@companyId/users', function(int $companyId) {
// entah bagaimana ambil pengguna Anda dari database misalnya
$users = Flight::db()->fetchAll("SELECT id, first_name, last_name FROM users WHERE company_id = ?", [ $companyId ]);
Flight::json($users);
});
// [{"id":1,"first_name":"Bob","last_name":"Jones"}, /* more users */ ]
Catatan: Secara default, Flight akan mengirim header
Content-Type: application/json
dengan respons. Ini juga akan menggunakan flagJSON_THROW_ON_ERROR
danJSON_UNESCAPED_SLASHES
saat mengkodekan JSON.
JSON dengan Kode Status
Anda juga dapat meneruskan kode status sebagai argumen kedua:
Flight::json(['id' => 123], 201);
JSON dengan Pretty Print
Anda juga dapat meneruskan argumen ke posisi terakhir untuk mengaktifkan pretty printing:
Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);
Mengubah Urutan Argumen JSON
Flight::json()
adalah metode yang sangat lama, tetapi tujuan Flight adalah mempertahankan kompatibilitas mundur
untuk proyek. Sebenarnya sangat sederhana jika Anda ingin mengubah urutan argumen untuk menggunakan sintaks yang lebih sederhana,
Anda hanya perlu memetakan ulang metode JSON seperti metode Flight lainnya:
Flight::map('json', function($data, $code = 200, $options = 0) {
// sekarang Anda tidak perlu `true, 'utf-8'` saat menggunakan metode json()!
Flight::_json($data, $code, true, 'utf-8', $options);
}
// Dan sekarang bisa digunakan seperti ini
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);
JSON dan Menghentikan 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 jenis otorisasi tertentu dan jika
pengguna tidak diotorisasi, Anda dapat mengirim respons JSON segera, membersihkan konten body
yang ada dan menghentikan eksekusi.
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Periksa apakah pengguna diotorisasi
if($authorized === false) {
Flight::jsonHalt(['error' => 'Unauthorized'], 401);
// no exit; needed here.
}
// Lanjutkan dengan sisa route
});
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']));
}
// Lanjutkan dengan sisa route
});
Membersihkan Body Respons
Jika Anda ingin membersihkan body respons, Anda dapat menggunakan metode clearBody
:
Flight::route('/', function() {
if($someCondition) {
Flight::response()->write("Hello, World!");
} else {
Flight::response()->clearBody();
}
});
Kasus penggunaan di atas mungkin tidak umum, namun bisa lebih umum jika ini digunakan dalam middleware.
Menjalankan Callback pada Body Respons
Anda dapat menjalankan callback pada body 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 mengompresi gzip semua respons untuk route apa pun
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
Anda dapat menambahkan beberapa callback dan mereka akan dijalankan dalam urutan yang ditambahkan. Karena ini dapat menerima callable apa pun, ini 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 route tidak akan bekerja jika Anda menggunakan opsi konfigurasi flight.v2.output_buffering
.
Callback Route Spesifik
Jika Anda ingin ini hanya berlaku untuk route spesifik, Anda dapat menambahkan callback di route itu sendiri:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
// Ini akan mengompresi gzip hanya respons untuk route ini
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
});
Opsi Middleware
Anda juga dapat menggunakan middleware untuk menerapkan callback ke semua route 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 {
// minify body entah bagaimana
return $body;
}
}
// index.php
Flight::group('/users', function() {
Flight::route('', function() { /* ... */ });
Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);
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 "Forbidden";
}
});
Jika Anda ingin mendapatkan kode status saat ini, Anda dapat menggunakan metode status
tanpa argumen apa pun:
Flight::response()->status(); // 200
Mengatur Header Respons
Anda dapat mengatur header seperti tipe konten 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!";
});
Redirect
Anda dapat mengarahkan ulang permintaan saat ini dengan menggunakan metode redirect()
dan meneruskan
URL baru:
Flight::route('/login', function() {
$username = Flight::request()->data->username;
$password = Flight::request()->data->password;
$passwordConfirm = Flight::request()->data->password_confirm;
if($password !== $passwordConfirm) {
Flight::redirect('/new/location');
return; // ini diperlukan agar fungsionalitas di bawah tidak dieksekusi
}
// tambahkan pengguna baru...
Flight::db()->runQuery("INSERT INTO users ....");
Flight::redirect('/admin/dashboard');
});
Catatan: Secara default Flight mengirim kode status HTTP 303 ("See Other"). Anda dapat secara opsional mengatur kode kustom:
Flight::redirect('/new/location', 301); // permanen
Menghentikan Eksekusi Route
Anda dapat menghentikan framework dan segera keluar pada titik mana pun dengan memanggil metode halt
:
Flight::halt();
Anda juga dapat menentukan kode status HTTP
dan pesan opsional:
Flight::halt(200, 'Be right back...');
Memanggil halt
akan membuang konten respons apa pun hingga titik itu dan menghentikan semua eksekusi.
Jika Anda ingin menghentikan framework dan mengeluarkan respons saat ini, gunakan metode stop
:
Flight::stop($httpStatusCode = null);
Catatan:
Flight::stop()
memiliki perilaku aneh seperti itu akan mengeluarkan respons tetapi melanjutkan eksekusi skrip Anda yang mungkin bukan yang Anda inginkan. Anda dapat menggunakanexit
ataureturn
setelah memanggilFlight::stop()
untuk mencegah eksekusi lebih lanjut, tetapi umumnya disarankan untuk menggunakanFlight::halt()
.
Ini akan menyimpan kunci dan nilai header ke objek respons. Pada akhir siklus hidup permintaan ini akan membangun header dan mengirim respons.
Penggunaan Lanjutan
Mengirim Header Segera
Mungkin ada saat-saat ketika Anda perlu melakukan sesuatu yang kustom dengan header dan Anda perlu mengirim header
pada baris kode yang sama yang Anda kerjakan. Jika Anda mengatur route yang di-stream,
ini yang Anda butuhkan. Itu dapat dicapai melalui response()->setRealHeader()
.
Flight::route('/', function() {
Flight::response()->setRealHeader('Content-Type: text/plain');
echo 'Streaming response...';
sleep(5);
echo 'Done!';
})->stream();
JSONP
Untuk permintaan JSONP, Anda dapat secara opsional meneruskan nama parameter query yang Anda gunakan untuk mendefinisikan fungsi callback Anda:
Flight::jsonp(['id' => 123], 'q');
Jadi, saat membuat permintaan GET menggunakan ?q=my_func
, Anda seharusnya menerima output:
my_func({"id":123});
Jika Anda tidak meneruskan nama parameter query, itu akan default ke jsonp
.
Catatan: Jika Anda masih menggunakan permintaan JSONP pada 2025 dan seterusnya, lompat ke chat dan beri tahu kami mengapa! Kami suka mendengar cerita pertempuran/horor yang bagus!
Membersihkan Data Respons
Anda dapat membersihkan body respons dan header dengan menggunakan metode clear()
. Ini akan membersihkan
header apa pun yang ditetapkan ke respons, membersihkan body respons, dan mengatur kode status ke 200
.
Flight::response()->clear();
Membersihkan Hanya Body Respons
Jika Anda hanya ingin membersihkan body respons, Anda dapat menggunakan metode clearBody()
:
// Ini masih akan mempertahankan header apa pun yang diatur pada objek response().
Flight::response()->clearBody();
Penyimpanan Cache HTTP
Flight menyediakan dukungan bawaan untuk caching tingkat HTTP. Jika kondisi caching
terpenuhi, Flight akan mengembalikan respons HTTP 304 Not Modified
. Saat berikutnya klien
meminta sumber daya yang sama, mereka akan diminta untuk menggunakan versi cache lokal mereka.
Caching Tingkat Route
Jika Anda ingin menyimpan cache seluruh respons Anda, Anda dapat menggunakan metode cache()
dan meneruskan waktu untuk cache.
// Ini akan menyimpan cache respons selama 5 menit
Flight::route('/news', function () {
Flight::response()->cache(time() + 300);
echo 'This content will be cached.';
});
// Alternatifnya, Anda dapat menggunakan string yang akan Anda teruskan
// ke metode strtotime()
Flight::route('/news', function () {
Flight::response()->cache('+5 minutes');
echo 'This content will be cached.';
});
Last-Modified
Anda dapat menggunakan metode lastModified
dan meneruskan timestamp UNIX untuk mengatur tanggal
dan waktu halaman terakhir dimodifikasi. Klien akan terus menggunakan cache mereka hingga
nilai last modified berubah.
Flight::route('/news', function () {
Flight::lastModified(1234567890);
echo 'This content will be cached.';
});
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 'This content will be cached.';
});
Ingatlah bahwa memanggil lastModified
atau etag
akan sama-sama 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 streaming file ke pengguna akhir. Anda dapat menggunakan metode download
dan meneruskan path.
Flight::route('/download', function () {
Flight::download('/path/to/file.txt');
// Mulai v3.17.1 Anda dapat menentukan nama file kustom untuk unduhan
Flight::download('/path/to/file.txt', 'custom_name.txt');
});
Lihat Juga
- Routing - Cara memetakan route ke controller dan merender view.
- Requests - Memahami cara menangani permintaan masuk.
- Middleware - Menggunakan middleware dengan route untuk autentikasi, logging, dll.
- Mengapa Framework? - Memahami manfaat menggunakan framework seperti Flight.
- Extending - Cara memperluas Flight dengan fungsionalitas Anda sendiri.
Pemecahan Masalah
- Jika Anda mengalami masalah dengan redirect yang tidak bekerja, pastikan Anda menambahkan
return;
ke metode. stop()
danhalt()
bukan hal yang sama.halt()
akan menghentikan eksekusi segera, sementarastop()
akan memungkinkan eksekusi berlanjut.
Changelog
- v3.17.1 - Menambahkan
$fileName
ke metodedownloadFile()
. - v3.12.0 - Menambahkan metode pembantu downloadFile.
- v3.10.0 - Menambahkan
jsonHalt
. - v1.0 - Rilis awal.
Learn/events
Pengelola Acara
sejak v3.15.0
Gambaran Umum
Acara memungkinkan Anda mendaftarkan dan memicu perilaku khusus dalam aplikasi Anda. Dengan penambahan Flight::onEvent()
dan Flight::triggerEvent()
, Anda sekarang dapat menghubungkan ke momen kunci dalam siklus hidup aplikasi Anda atau mendefinisikan acara Anda sendiri (seperti notifikasi dan email) untuk membuat kode Anda lebih modular dan dapat diperluas. Metode-metode ini adalah bagian dari metode yang dapat dipetakan milik Flight, yang berarti Anda dapat menimpa perilakunya sesuai kebutuhan Anda.
Pemahaman
Acara memungkinkan Anda memisahkan berbagai bagian aplikasi Anda sehingga mereka tidak terlalu bergantung satu sama lain. Pemisahan ini—sering disebut decoupling—membuat kode Anda lebih mudah untuk diperbarui, diperluas, atau di-debug. Alih-alih menulis semuanya dalam satu blok besar, Anda dapat membagi logika Anda menjadi potongan-potongan kecil yang independen yang merespons tindakan tertentu (acara).
Bayangkan Anda sedang membangun aplikasi blog:
- Ketika pengguna memposting komentar, Anda mungkin ingin:
- Menyimpan komentar ke database.
- Mengirim email ke pemilik blog.
- Mencatat tindakan untuk keamanan.
Tanpa acara, Anda akan memasukkan semuanya ke dalam satu fungsi. Dengan acara, Anda dapat membaginya: satu bagian menyimpan komentar, bagian lain memicu acara seperti 'comment.posted'
, dan pendengar terpisah menangani email dan pencatatan. Ini membuat kode Anda lebih bersih dan memungkinkan Anda menambahkan atau menghapus fitur (seperti notifikasi) tanpa menyentuh logika inti.
Kasus Penggunaan Umum
Untuk sebagian besar, acara bagus untuk hal-hal yang opsional, tetapi bukan bagian inti mutlak dari sistem Anda. Misalnya, berikut adalah hal-hal yang baik untuk dimiliki tetapi jika mereka gagal karena alasan tertentu, aplikasi Anda masih harus berfungsi:
- Pencatatan: Mencatat tindakan seperti login atau kesalahan tanpa mengacaukan kode utama Anda.
- Notifikasi: Mengirim email atau peringatan ketika sesuatu terjadi.
- Pembaruan Cache: Memperbarui cache atau memberi tahu sistem lain tentang perubahan.
Namun, katakanlah Anda memiliki fitur lupa kata sandi. Itu harus menjadi bagian dari fungsionalitas inti Anda dan bukan acara karena jika email itu tidak terkirim, pengguna Anda tidak dapat mereset kata sandi mereka dan menggunakan aplikasi Anda.
Penggunaan Dasar
Sistem acara Flight dibangun di sekitar dua metode utama: Flight::onEvent()
untuk mendaftarkan pendengar acara dan Flight::triggerEvent()
untuk memicu acara. Berikut adalah cara Anda dapat menggunakannya:
Mendaftarkan Pendengar Acara
Untuk mendengarkan acara, gunakan Flight::onEvent()
. Metode ini memungkinkan Anda mendefinisikan apa yang harus terjadi ketika acara terjadi.
Flight::onEvent(string $event, callable $callback): void
$event
: Nama untuk acara Anda (misalnya,'user.login'
).$callback
: Fungsi yang dijalankan ketika acara dipicu.
Anda "berlangganan" ke acara dengan memberi tahu Flight apa yang harus dilakukan ketika 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 demi satu. Ketika Anda memicu acara, semua pendengar yang terdaftar untuk acara itu akan berjalan hingga selesai sebelum kode Anda melanjutkan. Ini penting untuk dipahami karena berbeda dari sistem acara asinkron di mana pendengar mungkin berjalan secara paralel atau pada waktu yang kemudian.
Contoh Sederhana
Flight::onEvent('user.login', function ($username) {
echo "Selamat datang kembali, $username!";
// Anda dapat mengirim email jika login dari lokasi baru
});
Di sini, ketika acara 'user.login'
dipicu, itu akan menyapa pengguna dengan nama dan juga dapat menyertakan logika untuk mengirim email jika diperlukan.
Catatan: Callback dapat berupa fungsi, fungsi anonim, atau metode dari kelas.
Memicu Acara
Untuk membuat acara terjadi, gunakan Flight::triggerEvent()
. Ini memberi tahu Flight untuk menjalankan semua pendengar yang terdaftar untuk acara itu, sambil meneruskan data apa pun yang Anda berikan.
Flight::triggerEvent(string $event, ...$args): void
$event
: Nama acara yang Anda picu (harus cocok dengan acara yang terdaftar)....$args
: Argumen opsional untuk dikirim ke pendengar (bisa berupa jumlah argumen berapa pun).
Contoh Sederhana
$username = 'alice';
Flight::triggerEvent('user.login', $username);
Ini memicu acara 'user.login'
dan mengirim 'alice'
ke pendengar yang kita definisikan sebelumnya, yang akan menghasilkan: Selamat datang kembali, alice!
.
- Jika tidak ada pendengar yang terdaftar, tidak ada yang terjadi—aplikasi Anda tidak akan rusak.
- Gunakan operator spread (
...
) untuk meneruskan beberapa argumen secara fleksibel.
Menghentikan Acara
Jika pendengar mengembalikan false
, tidak ada pendengar tambahan untuk acara itu yang akan dieksekusi. Ini memungkinkan Anda menghentikan rantai acara berdasarkan kondisi tertentu. Ingat, urutan pendengar penting, karena yang pertama mengembalikan false
akan menghentikan yang lainnya dari berjalan.
Contoh:
Flight::onEvent('user.login', function ($username) {
if (isBanned($username)) {
logoutUser($username);
return false; // Menghentikan pendengar selanjutnya
}
});
Flight::onEvent('user.login', function ($username) {
sendWelcomeEmail($username); // ini tidak pernah dikirim
});
Menimpa Metode Acara
Flight::onEvent()
dan Flight::triggerEvent()
tersedia untuk diperluas, yang berarti Anda dapat mendefinisikan ulang cara kerjanya. Ini bagus untuk pengguna lanjutan yang ingin menyesuaikan sistem acara, seperti menambahkan pencatatan atau mengubah cara acara didistribusikan.
Contoh: Menyesuaikan onEvent
Flight::map('onEvent', function (string $event, callable $callback) {
// Catat setiap pendaftaran acara
error_log("Pendengar acara baru ditambahkan untuk: $event");
// Panggil perilaku default (asumsi sistem acara internal)
Flight::_onEvent($event, $callback);
});
Sekarang, setiap kali Anda mendaftarkan acara, itu akan mencatatnya sebelum melanjutkan.
Mengapa Menimpa?
- Tambahkan debugging atau pemantauan.
- Batasi acara di lingkungan tertentu (misalnya, nonaktifkan saat pengujian).
- Integrasikan dengan pustaka acara yang berbeda.
Di Mana Menempatkan Acara Anda
Jika Anda baru dengan konsep acara di proyek Anda, Anda mungkin bertanya-tanya: di mana saya mendaftarkan semua acara ini di aplikasi saya? Kesederhanaan Flight berarti tidak ada aturan ketat—Anda dapat menempatkannya di mana pun yang masuk akal untuk proyek Anda. Namun, menjaga agar mereka terorganisir membantu Anda mempertahankan kode Anda saat aplikasi Anda berkembang. Berikut adalah beberapa opsi praktis dan praktik terbaik, disesuaikan dengan sifat ringan Flight:
Opsi 1: Di File index.php
Utama Anda
Untuk aplikasi kecil atau prototipe cepat, Anda dapat mendaftarkan acara langsung di file index.php
Anda bersama dengan rute Anda. Ini menjaga semuanya di satu tempat, yang baik ketika kesederhanaan adalah prioritas Anda.
require 'vendor/autoload.php';
// Daftarkan acara
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in at " . date('Y-m-d H:i:s'));
});
// Definisikan rute
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Logged in!";
});
Flight::start();
- Kelebihan: Sederhana, tidak ada file tambahan, bagus untuk proyek kecil.
- Kekurangan: Dapat menjadi berantakan saat aplikasi Anda berkembang dengan lebih banyak acara dan rute.
Opsi 2: File events.php
Terpisah
Untuk aplikasi yang sedikit lebih besar, pertimbangkan untuk memindahkan pendaftaran acara ke file khusus seperti app/config/events.php
. Sertakan file ini di index.php
Anda sebelum rute Anda. Ini meniru cara rute sering diorganisir di app/config/routes.php
dalam proyek Flight.
// app/config/events.php
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in at " . date('Y-m-d H:i:s'));
});
Flight::onEvent('user.registered', function ($email, $name) {
echo "Email sent to $email: Welcome, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Logged in!";
});
Flight::start();
- Kelebihan: Menjaga
index.php
fokus pada routing, mengorganisir acara secara logis, mudah ditemukan dan diedit. - Kekurangan: Menambahkan sedikit struktur, yang mungkin terasa berlebihan untuk aplikasi sangat kecil.
Opsi 3: Dekat dengan Tempat Mereka Dipicu
Pendekatan lain adalah mendaftarkan acara dekat dengan tempat mereka dipicu, seperti di dalam controller atau definisi rute. Ini bekerja dengan baik jika acara spesifik untuk satu bagian dari aplikasi Anda.
Flight::route('/signup', function () {
// Daftarkan acara di sini
Flight::onEvent('user.registered', function ($email) {
echo "Welcome email sent to $email!";
});
$email = 'jane@example.com';
Flight::triggerEvent('user.registered', $email);
echo "Signed up!";
});
- Kelebihan: Menjaga kode terkait bersama, bagus untuk fitur terisolasi.
- Kekurangan: Menyebarkan pendaftaran acara, membuat lebih sulit untuk melihat semua acara sekaligus; berisiko pendaftaran duplikat jika tidak hati-hati.
Praktik Terbaik untuk Flight
- Mulai Sederhana: Untuk aplikasi kecil, letakkan acara di
index.php
. Ini cepat dan selaras dengan minimalisme Flight. - Tumbuh Cerdas: Saat aplikasi Anda berkembang (misalnya, lebih dari 5-10 acara), gunakan file
app/config/events.php
. Ini adalah langkah alami, seperti mengorganisir rute, dan menjaga kode Anda rapi tanpa menambahkan framework kompleks. - Hindari Over-Engineering: Jangan buat kelas “pengelola acara” lengkap atau direktori kecuali aplikasi Anda menjadi sangat besar—Flight berkembang dengan kesederhanaan, jadi jaga agar tetap ringan.
Tips: 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 logged in");
});
Flight::onEvent('user.registered', function ($email) {
echo "Welcome to $email!";
});
// Acara Halaman
Flight::onEvent('page.updated', function ($pageId) {
Flight::cache()->delete("page_$pageId");
});
Struktur ini skalabel dengan baik dan tetap ramah pemula.
Contoh Dunia Nyata
Mari kita jelajahi beberapa skenario dunia nyata untuk menunjukkan bagaimana acara bekerja dan mengapa mereka membantu.
Contoh 1: Mencatat Login Pengguna
// Langkah 1: Daftarkan pendengar
Flight::onEvent('user.login', function ($username) {
$time = date('Y-m-d H:i:s');
error_log("$username logged in at $time");
});
// Langkah 2: Picu di aplikasi Anda
Flight::route('/login', function () {
$username = 'bob'; // Berpura-pura ini berasal dari formulir
Flight::triggerEvent('user.login', $username);
echo "Hi, $username!";
});
Mengapa Berguna: Kode login tidak perlu tahu tentang pencatatan—ia hanya memicu acara. Anda dapat menambahkan lebih banyak pendengar (misalnya, kirim email selamat datang) nanti tanpa mengubah rute.
Contoh 2: Memberi Pemberitahuan Tentang Pengguna Baru
// Pendengar untuk pendaftaran baru
Flight::onEvent('user.registered', function ($email, $name) {
// Simulasikan pengiriman email
echo "Email sent to $email: Welcome, $name!";
});
// Picu ketika seseorang mendaftar
Flight::route('/signup', function () {
$email = 'jane@example.com';
$name = 'Jane';
Flight::triggerEvent('user.registered', $email, $name);
echo "Thanks for signing up!";
});
Mengapa Berguna: Logika pendaftaran fokus pada pembuatan pengguna, sementara acara menangani notifikasi. Anda dapat menambahkan lebih banyak pendengar (misalnya, catat pendaftaran) nanti.
Contoh 3: Membersihkan Cache
// Pendengar untuk membersihkan cache
Flight::onEvent('page.updated', function ($pageId) {
// jika menggunakan plugin flightphp/cache
Flight::cache()->delete("page_$pageId");
echo "Cache cleared for page $pageId.";
});
// Picu ketika halaman diedit
Flight::route('/edit-page/(@id)', function ($pageId) {
// Berpura-pura kita memperbarui halaman
Flight::triggerEvent('page.updated', $pageId);
echo "Page $pageId updated.";
});
Mengapa Berguna: Kode pengeditan tidak peduli dengan caching—ia hanya memberi sinyal pembaruan. Bagian lain dari aplikasi dapat bereaksi sesuai kebutuhan.
Praktik Terbaik
- Berikan Nama Acara dengan Jelas: Gunakan nama spesifik seperti
'user.login'
atau'page.updated'
sehingga jelas apa yang mereka lakukan. - Jaga Pendengar Sederhana: Jangan letakkan tugas lambat atau kompleks di pendengar—jaga aplikasi Anda tetap cepat.
- Uji Acara Anda: Picu mereka secara manual untuk memastikan pendengar bekerja seperti yang diharapkan.
- Gunakan Acara dengan Bijak: Mereka bagus untuk decoupling, tetapi terlalu banyak dapat membuat kode Anda sulit diikuti—gunakan mereka ketika masuk akal.
Sistem acara di Flight PHP, dengan Flight::onEvent()
dan Flight::triggerEvent()
, memberi Anda cara sederhana namun kuat untuk membangun aplikasi fleksibel. Dengan membiarkan berbagai bagian aplikasi Anda berbicara satu sama lain melalui acara, Anda dapat menjaga kode Anda terorganisir, dapat digunakan kembali, dan mudah diperluas. Apakah Anda mencatat tindakan, mengirim notifikasi, atau mengelola pembaruan, acara membantu Anda melakukannya tanpa mengacaukan logika Anda. Plus, dengan kemampuan untuk menimpa metode ini, Anda memiliki kebebasan untuk menyesuaikan sistem sesuai kebutuhan Anda. Mulai kecil dengan satu acara, dan lihat bagaimana itu mengubah struktur aplikasi Anda!
Acara Bawaan
Flight PHP dilengkapi dengan beberapa acara bawaan yang dapat Anda gunakan untuk menghubungkan ke siklus hidup framework. Acara ini dipicu pada titik tertentu dalam siklus permintaan/respons, memungkinkan Anda mengeksekusi logika khusus ketika tindakan tertentu terjadi.
Daftar Acara Bawaan
- flight.request.received:
function(Request $request)
Dipicu ketika permintaan diterima, diurai, dan diproses. - flight.error:
function(Throwable $exception)
Dipicu ketika kesalahan terjadi selama siklus hidup permintaan. - flight.redirect:
function(string $url, int $status_code)
Dipicu ketika pengalihan dimulai. - flight.cache.checked:
function(string $cache_key, bool $hit, float $executionTime)
Dipicu ketika cache diperiksa untuk kunci tertentu dan apakah cache hit atau miss. - flight.middleware.before:
function(Route $route)
Dipicu setelah middleware before dieksekusi. - flight.middleware.after:
function(Route $route)
Dipicu setelah middleware after dieksekusi. - flight.middleware.executed:
function(Route $route, $middleware, string $method, float $executionTime)
Dipicu setelah middleware apa pun dieksekusi - flight.route.matched:
function(Route $route)
Dipicu ketika rute cocok, tetapi belum dijalankan. - flight.route.executed:
function(Route $route, float $executionTime)
Dipicu setelah rute dieksekusi dan diproses.$executionTime
adalah waktu yang dibutuhkan untuk mengeksekusi rute (memanggil controller, dll). - flight.view.rendered:
function(string $template_file_path, float $executionTime)
Dipicu setelah tampilan dirender.$executionTime
adalah waktu yang dibutuhkan untuk merender template. Catatan: Jika Anda menimpa metoderender
, Anda perlu memicu ulang acara ini. - flight.response.sent:
function(Response $response, float $executionTime)
Dipicu setelah respons dikirim ke klien.$executionTime
adalah waktu yang dibutuhkan untuk membangun respons.
Lihat Juga
- Memperluas Flight - Cara memperluas dan menyesuaikan fungsionalitas inti Flight.
- Cache - Contoh menggunakan acara untuk membersihkan cache ketika halaman diperbarui.
Pemecahan Masalah
- Jika Anda tidak melihat pendengar acara Anda dipanggil, pastikan Anda mendaftarkannya sebelum memicu acara. Urutan pendaftaran penting.
Changelog
- v3.15.0 - Menambahkan acara ke Flight.
Learn/templates
Tampilan HTML dan Template
Gambaran Umum
Flight menyediakan fungsionalitas templating HTML dasar secara default. Templating adalah cara yang sangat efektif bagi Anda untuk memisahkan logika aplikasi dari lapisan presentasi Anda.
Pemahaman
Ketika Anda membangun aplikasi, kemungkinan besar Anda akan memiliki HTML yang ingin dikirimkan kembali ke pengguna akhir. PHP dengan sendirinya adalah bahasa templating, tetapi sangat mudah untuk membungkus logika bisnis seperti panggilan database, panggilan API, dll ke dalam file HTML Anda dan membuat pengujian serta pemisahan menjadi proses yang sangat sulit. Dengan mendorong data ke dalam template dan membiarkan template merender dirinya sendiri, menjadi jauh lebih mudah untuk memisahkan dan menguji unit kode Anda. Anda akan berterima kasih kepada kami jika Anda menggunakan template!
Penggunaan Dasar
Flight memungkinkan Anda untuk menukar engine tampilan default hanya dengan mendaftarkan kelas tampilan Anda sendiri. Gulir ke bawah untuk melihat contoh cara menggunakan Smarty, Latte, Blade, dan lainnya!
Latte
direkomendasikan
Berikut adalah cara Anda menggunakan engine template Latte untuk tampilan Anda.
Instalasi
composer require latte/latte
Konfigurasi Dasar
Ide utamanya adalah Anda menimpa metode render
untuk menggunakan Latte alih-alih renderer PHP default.
// overwrite the render method to use latte instead of the default PHP renderer
Flight::map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// Where latte specifically stores its cache
$latte->setTempDirectory(__DIR__ . '/../cache/');
$finalPath = Flight::get('flight.views.path') . $template;
$latte->render($finalPath, $data, $block);
});
Menggunakan Latte di Flight
Sekarang setelah Anda dapat merender dengan Latte, Anda dapat melakukan sesuatu seperti ini:
<!-- app/views/home.latte -->
<html>
<head>
<title>{$title ? $title . ' - '}My App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Hello, {$name}!</h1>
</body>
</html>
// routes.php
Flight::route('/@name', function ($name) {
Flight::render('home.latte', [
'title' => 'Home Page',
'name' => $name
]);
});
Ketika Anda mengunjungi /Bob
di browser Anda, outputnya akan menjadi:
<html>
<head>
<title>Home Page - My App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Hello, Bob!</h1>
</body>
</html>
Bacaan Lebih Lanjut
Contoh yang lebih kompleks tentang penggunaan Latte dengan tata letak ditunjukkan di bagian awesome plugins dari dokumentasi ini.
Anda dapat mempelajari lebih lanjut tentang kemampuan penuh Latte termasuk terjemahan dan kemampuan bahasa dengan membaca dokumentasi resmi.
Engine Tampilan Built-in
deprecated
Catatan: Meskipun ini masih fungsionalitas default dan secara teknis masih berfungsi.
Untuk menampilkan template tampilan, panggil metode render
dengan nama
file template dan data template opsional:
Flight::render('hello.php', ['name' => 'Bob']);
Data template yang Anda berikan secara otomatis disuntikkan ke dalam template dan dapat
dirujuk seperti variabel lokal. File template hanyalah file PHP. Jika
isi file template hello.php
adalah:
Hello, <?= $name ?>!
Outputnya akan menjadi:
Hello, Bob!
Anda juga dapat mengatur variabel tampilan secara manual dengan menggunakan metode set:
Flight::view()->set('name', 'Bob');
Variabel name
sekarang tersedia di seluruh tampilan Anda. Jadi Anda dapat dengan mudah melakukan:
Flight::render('hello');
Perhatikan bahwa ketika menentukan nama template di metode render, Anda dapat
meninggalkan ekstensi .php
.
Secara default Flight akan mencari direktori views
untuk file template. Anda dapat
mengatur jalur alternatif untuk template Anda dengan mengatur konfigurasi berikut:
Flight::set('flight.views.path', '/path/to/views');
Tata Letak
Umum bagi situs web untuk memiliki satu file template tata letak dengan konten
yang saling berganti. Untuk merender konten yang akan digunakan dalam tata letak, Anda dapat memberikan parameter opsional ke metode render
.
Flight::render('header', ['heading' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], 'bodyContent');
Tampilan Anda kemudian akan memiliki variabel yang disimpan bernama 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 akan menjadi:
<html>
<head>
<title>Home Page</title>
</head>
<body>
<h1>Hello</h1>
<div>World</div>
</body>
</html>
Smarty
Berikut adalah cara Anda menggunakan engine template Smarty untuk tampilan Anda:
// Load Smarty library
require './Smarty/libs/Smarty.class.php';
// Register Smarty as the view class
// Also pass a callback function to configure Smarty on load
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
$smarty->setTemplateDir('./templates/');
$smarty->setCompileDir('./templates_c/');
$smarty->setConfigDir('./config/');
$smarty->setCacheDir('./cache/');
});
// Assign template data
Flight::view()->assign('name', 'Bob');
// Display the 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);
});
Blade
Berikut adalah cara Anda menggunakan engine 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
// Load BladeOne library
use eftec\bladeone\BladeOne;
// Register BladeOne as the view class
// Also pass a callback function to configure BladeOne on load
Flight::register('view', BladeOne::class, [], function (BladeOne $blade) {
$views = __DIR__ . '/../views';
$cache = __DIR__ . '/../cache';
$blade->setPath($views);
$blade->setCompiledPath($cache);
});
// Assign template data
Flight::view()->share('name', 'Bob');
// Display the 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 akan menjadi:
Hello, Bob!
Lihat Juga
- Extending - Cara menimpa metode
render
untuk menggunakan engine template yang berbeda. - Routing - Cara memetakan rute ke controller dan merender tampilan.
- Responses - Cara menyesuaikan respons HTTP.
- Why a Framework? - Bagaimana template cocok ke dalam gambaran besar.
Pemecahan Masalah
- Jika Anda memiliki pengalihan di middleware Anda, tetapi aplikasi Anda sepertinya tidak mengalihkan, pastikan Anda menambahkan pernyataan
exit;
di middleware Anda.
Changelog
- v2.0 - Rilis awal.
Learn/collections
Koleksi
Gambaran Umum
Kelas Collection
di Flight adalah utilitas yang berguna untuk mengelola kumpulan data. Ini memungkinkan Anda mengakses dan memanipulasi data menggunakan notasi array maupun objek, membuat kode Anda lebih bersih dan fleksibel.
Pemahaman
Collection
pada dasarnya adalah pembungkus sekitar array, tetapi dengan beberapa kemampuan tambahan. Anda dapat menggunakannya seperti array, mengulanginya, menghitung item-nya, dan bahkan mengakses item seolah-olah itu adalah properti objek. Ini sangat berguna ketika Anda ingin meneruskan data terstruktur di aplikasi Anda, atau ketika Anda ingin membuat kode Anda sedikit lebih mudah dibaca.
Koleksi mengimplementasikan beberapa antarmuka PHP:
ArrayAccess
(sehingga Anda dapat menggunakan sintaks array)Iterator
(sehingga Anda dapat mengulang denganforeach
)Countable
(sehingga Anda dapat menggunakancount()
)JsonSerializable
(sehingga Anda dapat dengan mudah mengonversi ke JSON)
Penggunaan Dasar
Membuat Koleksi
Anda dapat membuat koleksi dengan hanya meneruskan array ke konstruktornya:
use flight\util\Collection;
$data = [
'name' => 'Flight',
'version' => 3,
'features' => ['routing', 'views', 'extending']
];
$collection = new Collection($data);
Mengakses Item
Anda dapat mengakses item menggunakan notasi array atau objek:
// Notasi array
echo $collection['name']; // Output: Flight
// Notasi objek
echo $collection->version; // Output: 3
Jika Anda mencoba mengakses kunci yang tidak ada, Anda akan mendapatkan null
alih-alih kesalahan.
Mengatur Item
Anda juga dapat mengatur item menggunakan notasi yang sama:
// Notasi array
$collection['author'] = 'Mike Cao';
// Notasi objek
$collection->license = 'MIT';
Memeriksa dan Menghapus Item
Periksa apakah item ada:
if (isset($collection['name'])) {
// Lakukan sesuatu
}
if (isset($collection->version)) {
// Lakukan sesuatu
}
Hapus item:
unset($collection['author']);
unset($collection->license);
Mengulang Koleksi
Koleksi dapat diulang, sehingga Anda dapat menggunakannya dalam loop foreach
:
foreach ($collection as $key => $value) {
echo "$key: $value\n";
}
Menghitung Item
Anda dapat menghitung jumlah item dalam koleksi:
echo count($collection); // Output: 4
Mendapatkan Semua Kunci atau Data
Dapatkan semua kunci:
$keys = $collection->keys(); // ['name', 'version', 'features', 'license']
Dapatkan semua data sebagai array:
$data = $collection->getData();
Membersihkan Koleksi
Hapus semua item:
$collection->clear();
Serialisasi JSON
Koleksi dapat dengan mudah dikonversi ke JSON:
echo json_encode($collection);
// Output: {"name":"Flight","version":3,"features":["routing","views","extending"],"license":"MIT"}
Penggunaan Lanjutan
Anda dapat mengganti array data internal sepenuhnya jika diperlukan:
$collection->setData(['foo' => 'bar']);
Koleksi sangat berguna ketika Anda ingin meneruskan data terstruktur antar komponen, atau ketika Anda ingin menyediakan antarmuka yang lebih berorientasi objek untuk data array.
Lihat Juga
- Requests - Pelajari cara menangani permintaan HTTP dan bagaimana koleksi dapat digunakan untuk mengelola data permintaan.
- PDO Wrapper - Pelajari cara menggunakan pembungkus PDO di Flight dan bagaimana koleksi dapat digunakan untuk mengelola hasil basis data.
Pemecahan Masalah
- Jika Anda mencoba mengakses kunci yang tidak ada, Anda akan mendapatkan
null
alih-alih kesalahan. - Ingatlah bahwa koleksi tidak bersifat rekursif: array bersarang tidak secara otomatis dikonversi menjadi koleksi.
- Jika Anda perlu mereset koleksi, gunakan
$collection->clear()
atau$collection->setData([])
.
Changelog
- v3.0 - Peningkatan petunjuk tipe dan dukungan PHP 8+.
- v1.0 - Rilis awal kelas Collection.
Learn/flight_vs_fat_free
Flight vs Fat-Free
Apa itu Fat-Free?
Fat-Free (dikenal dengan sayang sebagai F3) adalah micro-framework PHP yang kuat namun mudah digunakan yang dirancang untuk membantu Anda membangun aplikasi web dinamis dan kokoh - dengan cepat!
Flight dibandingkan dengan Fat-Free dalam banyak hal dan mungkin sepupu terdekat dalam hal fitur dan kesederhanaan. Fat-Free memiliki banyak fitur yang tidak dimiliki Flight, tetapi ia juga memiliki banyak fitur yang dimiliki Flight. Fat-Free mulai menunjukkan usianya dan tidak sepopuler dulu.
Pembaruan menjadi kurang sering dan komunitas tidak seaktif dulu. Kode sederhana, tetapi terkadang kurangnya disiplin sintaks dapat membuatnya sulit dibaca dan dipahami. Ia bekerja untuk PHP 8.3, tetapi kode itu sendiri masih terlihat seperti hidup di PHP 5.3.
Kelebihan dibandingkan Flight
- Fat-Free memiliki beberapa bintang lebih banyak di GitHub daripada Flight.
- Fat-Free memiliki dokumentasi yang cukup baik, tetapi kurang jelas di beberapa area.
- Fat-Free memiliki beberapa sumber daya langka seperti tutorial YouTube dan artikel online yang dapat digunakan untuk mempelajari framework.
- Fat-Free memiliki beberapa plugin bermanfaat bawaan yang kadang-kadang membantu.
- Fat-Free memiliki ORM bawaan yang disebut Mapper yang dapat digunakan untuk berinteraksi dengan database Anda. Flight memiliki active-record.
- Fat-Free memiliki Sessions, Caching, dan lokalisasi bawaan. Flight mengharuskan Anda menggunakan pustaka pihak ketiga, tetapi tercakup dalam dokumentasi.
- Fat-Free memiliki kelompok kecil plugin buatan komunitas yang dapat digunakan untuk memperluas framework. Flight memiliki beberapa yang tercakup dalam halaman dokumentasi dan contoh.
- Fat-Free seperti Flight tidak memiliki dependensi.
- Fat-Free seperti Flight diarahkan untuk memberikan kontrol kepada pengembang atas aplikasi mereka dan pengalaman pengembang yang sederhana.
- Fat-Free mempertahankan kompatibilitas mundur seperti Flight (sebagian karena pembaruan semakin jarang).
- Fat-Free seperti Flight ditujukan untuk pengembang yang baru memasuki dunia framework untuk pertama kalinya.
- Fat-Free memiliki mesin template bawaan yang lebih kuat daripada mesin template Flight. Flight merekomendasikan Latte untuk mencapai ini.
- Fat-Free memiliki perintah CLI unik jenis "route" di mana Anda dapat membangun aplikasi CLI di dalam Fat-Free itu sendiri dan memperlakukannya seperti permintaan
GET
. Flight mencapai ini dengan runway.
Kekurangan dibandingkan Flight
- Fat-Free memiliki beberapa tes implementasi dan bahkan memiliki kelas test sendiri yang sangat dasar. Namun, ia tidak diuji unit 100% seperti Flight.
- Anda harus menggunakan mesin pencari seperti Google untuk mencari situs dokumentasi.
- Flight memiliki mode gelap di situs dokumentasi mereka. (mic drop)
- Fat-Free memiliki beberapa modul yang sangat tidak terawat.
- Flight memiliki PdoWrapper sederhana yang sedikit lebih sederhana daripada kelas
DB\SQL
bawaan Fat-Free. - Flight memiliki plugin permissions yang dapat digunakan untuk mengamankan aplikasi Anda. Fat-Free mengharuskan Anda menggunakan pustaka pihak ketiga.
- Flight memiliki ORM yang disebut active-record yang terasa lebih seperti ORM daripada Mapper Fat-Free.
Manfaat tambahan dari
active-record
adalah Anda dapat mendefinisikan hubungan antar rekaman untuk join otomatis di mana Mapper Fat-Free mengharuskan Anda membuat view SQL. - Sungguh menakjubkan, Fat-Free tidak memiliki namespace root. Flight memiliki namespace sepanjang untuk menghindari tabrakan dengan kode Anda sendiri.
kelas
Cache
adalah pelanggar terbesar di sini. - Fat-Free tidak memiliki middleware. Sebaliknya ada hook
beforeroute
danafterroute
yang dapat digunakan untuk memfilter permintaan dan respons di controller. - Fat-Free tidak dapat mengelompokkan rute.
- Fat-Free memiliki penangan kontainer injeksi dependensi, tetapi dokumentasi sangat minim tentang cara menggunakannya.
- Debugging bisa menjadi sedikit rumit karena pada dasarnya semuanya disimpan di yang disebut
HIVE
Learn/extending
Memperluas
Gambaran Umum
Flight dirancang sebagai kerangka kerja yang dapat diperluas. Kerangka kerja ini dilengkapi dengan seperangkat metode dan komponen default, tetapi memungkinkan Anda untuk memetakan metode Anda sendiri, mendaftarkan kelas Anda sendiri, atau bahkan menimpa kelas dan metode yang ada.
Pemahaman
Ada 2 cara yang dapat Anda gunakan untuk memperluas fungsionalitas Flight:
- Pemetaan Metode - Ini digunakan untuk membuat metode kustom sederhana yang dapat Anda panggil dari mana saja di aplikasi Anda. Ini biasanya digunakan untuk fungsi utilitas yang ingin Anda panggil dari mana saja di kode Anda.
- Pendaftaran Kelas - Ini digunakan untuk mendaftarkan kelas Anda sendiri dengan Flight. Ini biasanya digunakan untuk kelas yang memiliki dependensi atau memerlukan konfigurasi.
Anda juga dapat menimpa metode kerangka kerja yang ada untuk mengubah perilaku defaultnya agar lebih sesuai dengan kebutuhan proyek Anda.
Jika Anda mencari DIC (Dependency Injection Container), kunjungi halaman Dependency Injection Container.
Penggunaan Dasar
Menimpa Metode Kerangka Kerja
Flight memungkinkan Anda menimpa fungsionalitas defaultnya agar sesuai dengan kebutuhan Anda sendiri, tanpa harus memodifikasi kode apa pun. Anda dapat melihat semua metode yang dapat ditimpa di bawah.
Misalnya, ketika Flight tidak dapat mencocokkan URL dengan rute, ia memanggil metode notFound
yang mengirim respons HTTP 404
generik. Anda dapat menimpa perilaku ini
dengan menggunakan metode map
:
Flight::map('notFound', function() {
// Tampilkan halaman 404 kustom
include 'errors/404.html';
});
Flight juga memungkinkan Anda untuk mengganti komponen inti kerangka kerja. Misalnya, Anda dapat mengganti kelas Router default dengan kelas kustom Anda sendiri:
// buat kelas Router kustom Anda
class MyRouter extends \flight\net\Router {
// timpa metode di sini
// misalnya pintasan untuk permintaan GET untuk menghapus
// fitur rute pass
public function get($pattern, $callback, $alias = '') {
return parent::get($pattern, $callback, false, $alias);
}
}
// Daftarkan kelas kustom Anda
Flight::register('router', MyRouter::class);
// Saat Flight memuat instance Router, ia akan memuat kelas Anda
$myRouter = Flight::router();
$myRouter->get('/hello', function() {
echo "Hello World!";
}, 'hello_alias');
Namun, metode kerangka kerja seperti map
dan register
tidak dapat ditimpa. Anda akan
mendapat kesalahan jika mencoba melakukannya (lihat lagi di bawah untuk daftar metode).
Metode Kerangka Kerja yang Dapat Dipetakan
Berikut adalah kumpulan lengkap metode untuk kerangka kerja. Ini terdiri dari metode inti, yang merupakan metode statis biasa, dan metode yang dapat diperluas, yang merupakan metode yang dipetakan yang dapat difilter atau ditimpa.
Metode Inti
Metode ini adalah inti dari kerangka kerja dan tidak dapat ditimpa.
Flight::map(string $name, callable $callback, bool $pass_route = false) // Membuat metode kerangka kerja kustom.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Mendaftarkan kelas ke metode kerangka kerja.
Flight::unregister(string $name) // Membatalkan pendaftaran kelas ke metode kerangka kerja.
Flight::before(string $name, callable $callback) // Menambahkan filter sebelum metode kerangka kerja.
Flight::after(string $name, callable $callback) // Menambahkan filter setelah metode kerangka kerja.
Flight::path(string $path) // Menambahkan jalur untuk autoloading kelas.
Flight::get(string $key) // Mendapatkan variabel yang ditetapkan oleh Flight::set().
Flight::set(string $key, mixed $value) // Mengatur variabel dalam mesin Flight.
Flight::has(string $key) // Memeriksa apakah variabel telah ditetapkan.
Flight::clear(array|string $key = []) // Membersihkan variabel.
Flight::init() // Menginisialisasi kerangka kerja ke pengaturan defaultnya.
Flight::app() // Mendapatkan instance objek aplikasi
Flight::request() // Mendapatkan instance objek permintaan
Flight::response() // Mendapatkan instance objek respons
Flight::router() // Mendapatkan instance objek router
Flight::view() // Mendapatkan instance objek tampilan
Metode yang Dapat Diperluas
Flight::start() // Memulai kerangka kerja.
Flight::stop() // Menghentikan kerangka kerja dan mengirim respons.
Flight::halt(int $code = 200, string $message = '') // Menghentikan kerangka kerja 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) // Mengarahkan ke URL lain.
Flight::download(string $filePath) // Mengunduh file.
Flight::render(string $file, array $data, ?string $key = null) // Merender file template.
Flight::error(Throwable $error) // Mengirim respons HTTP 500.
Flight::notFound() // Mengirim respons HTTP 404.
Flight::etag(string $id, string $type = 'string') // Melakukan caching HTTP ETag.
Flight::lastModified(int $time) // Melakukan caching HTTP last modified.
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Mengirim respons JSON.
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Mengirim respons JSONP.
Flight::jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Mengirim respons JSON dan menghentikan kerangka kerja.
Flight::onEvent(string $event, callable $callback) // Mendaftarkan pendengar acara.
Flight::triggerEvent(string $event, ...$args) // Memicu acara.
Metode kustom apa pun yang ditambahkan dengan map
dan register
juga dapat difilter. Untuk contoh tentang cara memfilter metode ini, lihat panduan Filtering Methods.
Kelas Kerangka Kerja yang Dapat Diperluas
Ada beberapa kelas yang dapat Anda timpa fungsionalitasnya dengan memperluasnya dan mendaftarkan kelas Anda sendiri. Kelas-kelas ini adalah:
Flight::app() // Kelas Aplikasi - perluas kelas flight\Engine
Flight::request() // Kelas Permintaan - perluas kelas flight\net\Request
Flight::response() // Kelas Respons - perluas kelas flight\net\Response
Flight::router() // Kelas Router - perluas kelas flight\net\Router
Flight::view() // Kelas Tampilan - perluas kelas flight\template\View
Flight::eventDispatcher() // Kelas Event Dispatcher - perluas kelas flight\core\Dispatcher
Pemetaan Metode Kustom
Untuk memetakan metode kustom sederhana Anda sendiri, gunakan fungsi map
:
// Petakan metode Anda
Flight::map('hello', function (string $name) {
echo "hello $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 autocomplete di IDE dan lebih mudah dibaca. Setara dengan kode di atas adalah:
function hello(string $name) {
echo "hello $name!";
}
hello('Bob');
Ini digunakan lebih banyak ketika Anda perlu meneruskan variabel ke metode Anda untuk mendapatkan nilai
yang diharapkan. Menggunakan metode register()
seperti di bawah ini lebih untuk meneruskan konfigurasi
dan kemudian memanggil kelas yang telah dikonfigurasi sebelumnya.
Pendaftaran Kelas Kustom
Untuk mendaftarkan kelas Anda sendiri dan mengonfigurasinya, gunakan fungsi register
. Keuntungan yang dimiliki ini dibandingkan map() adalah Anda dapat menggunakan kembali kelas yang sama ketika Anda memanggil fungsi ini (akan membantu dengan Flight::db()
untuk berbagi instance yang sama).
// Daftarkan kelas Anda
Flight::register('user', User::class);
// Dapatkan instance kelas Anda
$user = Flight::user();
Metode register juga memungkinkan Anda untuk meneruskan parameter ke konstruktor kelas Anda. Jadi ketika Anda memuat kelas kustom Anda, ia akan datang sudah diinisialisasi. Anda dapat mendefinisikan parameter konstruktor dengan meneruskan 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']);
// Dapatkan instance kelas Anda
// Ini akan membuat objek dengan parameter yang didefinisikan
//
// new PDO('mysql:host=localhost;dbname=test','user','pass');
//
$db = Flight::db();
// dan jika Anda membutuhkannya nanti di kode Anda, Anda hanya memanggil metode yang sama lagi
class SomeController {
public function __construct() {
$this->db = Flight::db();
}
}
Jika Anda meneruskan parameter callback tambahan, ia akan dieksekusi segera setelah konstruksi kelas. Ini memungkinkan Anda untuk melakukan prosedur penyiapan apa pun untuk objek baru Anda. Fungsi callback mengambil satu parameter, instance objek baru.
// Callback akan diteruskan objek yang dibuat
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 teruskan false
sebagai parameter:
// Instance kelas yang dibagikan
$shared = Flight::db();
// Instance kelas yang baru
$new = Flight::db(false);
Catatan: Ingatlah bahwa metode yang dipetakan memiliki prioritas atas kelas yang terdaftar. Jika Anda menyatakan keduanya menggunakan nama yang sama, hanya metode yang dipetakan yang akan dipanggil.
Contoh
Berikut adalah beberapa contoh tentang bagaimana Anda dapat memperluas Flight dengan fungsionalitas yang tidak ada di inti.
Logging
Flight tidak memiliki sistem logging bawaan, namun, sangat mudah untuk menggunakan perpustakaan logging dengan Flight. Berikut adalah contoh menggunakan perpustakaan Monolog:
// services.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 rute Anda
Flight::log()->warning('This is a warning message');
Ini akan mencatat pesan ke file log yang Anda tentukan. Bagaimana jika Anda ingin mencatat sesuatu ketika terjadi
kesalahan? Anda dapat menggunakan metode error
:
// Di controller atau rute Anda
Flight::map('error', function(Throwable $ex) {
Flight::log()->error($ex->getMessage());
// Tampilkan halaman kesalahan kustom Anda
include 'errors/500.html';
});
Anda juga dapat membuat sistem APM (Application Performance Monitoring) dasar
menggunakan metode before
dan after
:
// Di file services.php 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('Request '.Flight::request()->url.' took ' . round($end - $start, 4) . ' seconds');
// Anda juga dapat menambahkan header permintaan atau respons Anda
// untuk mencatatnya juga (berhati-hatilah karena ini akan menjadi banyak data
// jika Anda memiliki banyak permintaan)
Flight::log()->info('Request Headers: ' . json_encode(Flight::request()->headers));
Flight::log()->info('Response Headers: ' . json_encode(Flight::response()->headers));
});
Caching
Flight tidak memiliki sistem caching bawaan, namun, sangat mudah untuk menggunakan perpustakaan caching dengan Flight. Berikut adalah contoh menggunakan PHP File Cache library:
// services.php
// Daftarkan cache dengan Flight
Flight::register('cache', \flight\Cache::class, [ __DIR__ . '/../cache/' ], function(\flight\Cache $cache) {
$cache->setDevMode(ENVIRONMENT === 'development');
});
Sekarang setelah terdaftar, Anda dapat menggunakannya di aplikasi Anda:
// Di controller atau rute Anda
$data = Flight::cache()->get('my_cache_key');
if (empty($data)) {
// Lakukan pemrosesan untuk mendapatkan data
$data = [ 'some' => 'data' ];
Flight::cache()->set('my_cache_key', $data, 3600); // cache untuk 1 jam
}
Instantiation Objek DIC yang Mudah
Jika Anda menggunakan DIC (Dependency Injection Container) di aplikasi Anda, Anda dapat menggunakan Flight untuk membantu Anda menginisialisasi objek Anda. Berikut adalah contoh menggunakan perpustakaan Dice:
// services.php
// buat container baru
$container = new \Dice\Dice;
// jangan lupa untuk menugaskan ulang ke dirinya sendiri seperti di bawah!
$container = $container->addRule('PDO', [
// shared berarti bahwa objek yang sama akan dikembalikan setiap kali
'shared' => true,
'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);
// sekarang kita dapat membuat metode yang dapat dipetakan untuk membuat objek apa pun.
Flight::map('make', function($class, $params = []) use ($container) {
return $container->create($class, $params);
});
// Ini mendaftarkan penangan container sehingga Flight tahu untuk menggunakannya untuk controller/middleware
Flight::registerContainerHandler(function($class, $params) {
Flight::make($class, $params);
});
// katakanlah kita memiliki kelas sampel berikut yang mengambil objek PDO di konstruktor
class EmailCron {
protected PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function send() {
// kode yang mengirim email
}
}
// Dan akhirnya Anda dapat membuat objek menggunakan dependency injection
$emailCron = Flight::make(EmailCron::class);
$emailCron->send();
Keren, bukan?
Lihat Juga
- Dependency Injection Container - Cara menggunakan DIC dengan Flight.
- File Cache - Contoh menggunakan perpustakaan caching dengan Flight.
Pemecahan Masalah
- Ingatlah bahwa metode yang dipetakan memiliki prioritas atas kelas yang terdaftar. Jika Anda menyatakan keduanya menggunakan nama yang sama, hanya metode yang dipetakan yang akan dipanggil.
Changelog
- v2.0 - Rilis Awal.
Learn/json
JSON Wrapper
Gambaran Umum
Kelas Json
di Flight menyediakan cara sederhana dan konsisten untuk mengkodekan dan mendekodekan data JSON dalam aplikasi Anda. Ini membungkus fungsi JSON asli PHP dengan penanganan kesalahan yang lebih baik dan beberapa pengaturan default yang membantu, membuatnya lebih mudah dan aman untuk bekerja dengan JSON.
Pemahaman
Bekerja dengan JSON sangat umum di aplikasi PHP modern, terutama saat membangun API atau menangani permintaan AJAX. Kelas Json
memusatkan semua pengkodean dan dekodean JSON Anda, sehingga Anda tidak perlu khawatir tentang kasus tepi yang aneh atau kesalahan kriptik dari fungsi bawaan PHP.
Fitur utama:
- Penanganan kesalahan yang konsisten (melemparkan pengecualian saat gagal)
- Opsi default untuk pengkodean/dekodean (seperti garis miring yang tidak di-escape)
- Metode utilitas untuk pencetakan cantik dan validasi
Penggunaan Dasar
Mengkodekan Data ke JSON
Untuk mengonversi data PHP ke string JSON, gunakan Json::encode()
:
use flight\util\Json;
$data = [
'framework' => 'Flight',
'version' => 3,
'features' => ['routing', 'views', 'extending']
];
$json = Json::encode($data);
echo $json;
// Output: {"framework":"Flight","version":3,"features":["routing","views","extending"]}
Jika pengkodean gagal, Anda akan mendapatkan pengecualian dengan pesan kesalahan yang membantu.
Pencetakan Cantik
Ingin JSON Anda mudah dibaca oleh manusia? Gunakan prettyPrint()
:
echo Json::prettyPrint($data);
/*
{
"framework": "Flight",
"version": 3,
"features": [
"routing",
"views",
"extending"
]
}
*/
Mendekodekan String JSON
Untuk mengonversi string JSON kembali ke data PHP, gunakan Json::decode()
:
$json = '{"framework":"Flight","version":3}';
$data = Json::decode($json);
echo $data->framework; // Output: Flight
Jika Anda ingin array asosiatif daripada objek, berikan true
sebagai argumen kedua:
$data = Json::decode($json, true);
echo $data['framework']; // Output: Flight
Jika dekodean gagal, Anda akan mendapatkan pengecualian dengan pesan kesalahan yang jelas.
Memvalidasi JSON
Periksa apakah string adalah JSON yang valid:
if (Json::isValid($json)) {
// Itu valid!
} else {
// Bukan JSON yang valid
}
Mendapatkan Kesalahan Terakhir
Jika Anda ingin memeriksa pesan kesalahan JSON terakhir (dari fungsi PHP asli):
$error = Json::getLastError();
if ($error !== '') {
echo "Last JSON error: $error";
}
Penggunaan Lanjutan
Anda dapat menyesuaikan opsi pengkodean dan dekodean jika Anda membutuhkan kontrol lebih (lihat opsi json_encode PHP):
// Encode dengan opsi HEX_TAG
$json = Json::encode($data, JSON_HEX_TAG);
// Decode dengan kedalaman khusus
$data = Json::decode($json, false, 1024);
Lihat Juga
- Collections - Untuk bekerja dengan data terstruktur yang dapat dengan mudah dikonversi ke JSON.
- Configuration - Cara mengonfigurasi aplikasi Flight Anda.
- Extending - Cara menambahkan utilitas sendiri atau menimpa kelas inti.
Pemecahan Masalah
- Jika pengkodean atau dekodean gagal, pengecualian dilemparkan—bungkus panggilan Anda dalam try/catch jika Anda ingin menangani kesalahan dengan anggun.
- Jika Anda mendapatkan hasil yang tidak diharapkan, periksa data Anda untuk referensi melingkar atau karakter non-UTF8.
- Gunakan
Json::isValid()
untuk memeriksa apakah string adalah JSON yang valid sebelum mendekode.
Changelog
- v3.16.0 - Ditambahkan kelas utilitas pembungkus JSON.
Learn/flight_vs_slim
Flight vs Slim
Apa itu Slim?
Slim adalah kerangka kerja mikro PHP yang membantu Anda menulis aplikasi web dan API sederhana namun kuat dengan cepat.
Banyak inspirasi untuk beberapa fitur v3 dari Flight sebenarnya berasal dari Slim. Pengelompokan rute, dan eksekusi middleware dalam urutan tertentu adalah dua fitur yang terinspirasi dari Slim. Slim v3 dirilis dengan fokus pada kesederhanaan, tetapi ada ulasan campuran mengenai v4.
Kelebihan dibandingkan Flight
- Slim memiliki komunitas pengembang yang lebih besar, yang pada gilirannya membuat modul berguna untuk membantu Anda tidak perlu menciptakan roda ulang.
- Slim mengikuti banyak antarmuka dan standar yang umum di komunitas PHP, yang meningkatkan interoperabilitas.
- Slim memiliki dokumentasi dan tutorial yang layak untuk digunakan belajar kerangka kerja (meskipun tidak sebaik Laravel atau Symfony).
- Slim memiliki berbagai sumber daya seperti tutorial YouTube dan artikel online yang dapat digunakan untuk belajar kerangka kerja.
- Slim memungkinkan Anda menggunakan komponen apa pun yang Anda inginkan untuk menangani fitur routing inti karena sesuai dengan PSR-7.
Kekurangan dibandingkan Flight
- Menariknya, Slim tidak secepat yang Anda bayangkan untuk sebuah kerangka kerja mikro. Lihat benchmark TechEmpower untuk informasi lebih lanjut.
- Flight ditujukan untuk pengembang yang ingin membangun aplikasi web ringan, cepat, dan mudah digunakan.
- Flight tidak memiliki ketergantungan, sedangkan Slim memiliki beberapa ketergantungan yang harus Anda instal.
- Flight ditujukan untuk kesederhanaan dan kemudahan penggunaan.
- Salah satu fitur inti Flight adalah bahwa ia berusaha sebaik mungkin untuk mempertahankan kompatibilitas mundur. Perubahan dari Slim v3 ke v4 adalah perubahan yang merusak.
- Flight ditujukan untuk pengembang yang baru memasuki dunia kerangka kerja untuk pertama kalinya.
- Flight juga dapat menangani aplikasi tingkat enterprise, tetapi tidak memiliki sebanyak contoh dan tutorial seperti Slim. Ini juga akan membutuhkan lebih banyak disiplin dari pengembang untuk menjaga semuanya terorganisir dan terstruktur dengan baik.
- Flight memberikan pengembang lebih banyak kendali atas aplikasi, sedangkan Slim dapat menyelinapkan beberapa sihir di balik layar.
- Flight memiliki PdoWrapper sederhana yang dapat digunakan untuk berinteraksi dengan database Anda. Slim mengharuskan Anda menggunakan pustaka pihak ketiga.
- Flight memiliki plugin permissions yang dapat digunakan untuk mengamankan aplikasi Anda. Slim mengharuskan Anda menggunakan pustaka pihak ketiga.
- Flight memiliki ORM bernama active-record yang dapat digunakan untuk berinteraksi dengan database Anda. Slim mengharuskan Anda menggunakan pustaka pihak ketiga.
- Flight memiliki aplikasi CLI bernama runway yang dapat digunakan untuk menjalankan aplikasi Anda dari baris perintah. Slim tidak memiliki.
Learn/autoloading
Autoloading
Gambaran Umum
Autoloading adalah konsep di PHP di mana Anda menentukan direktori atau direktori untuk memuat kelas dari. Ini jauh lebih bermanfaat daripada menggunakan require
atau include
untuk memuat kelas. Ini juga merupakan persyaratan untuk menggunakan paket Composer.
Pemahaman
Secara default, kelas Flight
apa pun dimuat secara otomatis untuk Anda berkat composer. Namun, jika Anda ingin memuat kelas Anda sendiri secara otomatis, Anda dapat menggunakan metode Flight::path()
untuk menentukan direktori untuk memuat kelas dari.
Menggunakan autoloader dapat membantu menyederhanakan kode Anda secara signifikan. Alih-alih memiliki file yang dimulai dengan berbagai pernyataan include
atau require
di bagian atas untuk menangkap semua kelas yang digunakan dalam file tersebut, Anda dapat secara dinamis memanggil kelas Anda dan mereka akan disertakan secara otomatis.
Penggunaan Dasar
Misalkan kita memiliki pohon direktori seperti berikut:
# Contoh path
/home/user/project/my-flight-project/
├── app
│ ├── cache
│ ├── config
│ ├── controllers - berisi controller untuk proyek ini
│ ├── translations
│ ├── UTILS - berisi kelas untuk aplikasi ini saja (semua huruf kapital dengan sengaja untuk 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 seperti ini:
/**
* public/index.php
*/
// Tambahkan path ke autoloader
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
/**
* app/controllers/MyController.php
*/
// tidak diperlukan namespacing
// Semua kelas yang dimuat secara otomatis disarankan menggunakan Pascal Case (setiap kata dikapitalisasi, tanpa spasi)
class MyController {
public function index() {
// lakukan sesuatu
}
}
Namespaces
Jika Anda memiliki namespace, sebenarnya sangat mudah untuk mengimplementasikannya. Anda harus menggunakan metode Flight::path()
untuk menentukan direktori root (bukan document root atau folder public/
) dari aplikasi Anda.
/**
* public/index.php
*/
// Tambahkan path ke autoloader
Flight::path(__DIR__.'/../');
Sekarang ini adalah tampilan controller 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 case yang sama dengan struktur direktori
// namespace dan direktori tidak boleh memiliki underscore (kecuali Loader::setV2ClassLoading(false) diatur)
namespace app\controllers;
// Semua kelas yang dimuat secara otomatis disarankan menggunakan Pascal Case (setiap kata dikapitalisasi, 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 kelas di direktori utils Anda secara otomatis, Anda akan melakukan hal yang sama secara dasar:
/**
* app/UTILS/ArrayHelperUtil.php
*/
// namespace harus cocok dengan struktur direktori dan case (perhatikan direktori UTILS semua huruf kapital
// seperti di pohon file di atas)
namespace app\UTILS;
class ArrayHelperUtil {
public function changeArrayCase(array $array) {
// lakukan sesuatu
}
}
Underscores di 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 menggunakan underscore di nama kelas Anda.
Ini tidak disarankan, tetapi tersedia untuk mereka yang membutuhkannya.
use flight\core\Loader;
/**
* 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 diperlukan namespacing
class My_Controller {
public function index() {
// lakukan sesuatu
}
}
Lihat Juga
- Routing - Cara memetakan rute ke controller dan merender views.
- Mengapa Framework? - Memahami manfaat menggunakan framework seperti Flight.
Pemecahan Masalah
- Jika Anda tidak dapat mengetahui mengapa kelas namespaced Anda tidak ditemukan, ingatlah untuk menggunakan
Flight::path()
ke direktori root di proyek Anda, bukan direktoriapp/
atausrc/
atau setara.
Kelas Tidak Ditemukan (autoloading tidak berfungsi)
Ada beberapa alasan mengapa ini bisa terjadi. Di bawah ini beberapa contoh tetapi pastikan Anda juga memeriksa bagian autoloading.
Nama File Salah
Yang paling umum adalah nama kelas tidak cocok dengan nama file.
Jika Anda memiliki kelas bernama MyClass
maka file harus bernama MyClass.php
. Jika Anda memiliki kelas bernama MyClass
dan file bernama myclass.php
maka autoloader tidak akan dapat menemukannya.
Namespace Salah
Jika Anda menggunakan namespace, maka namespace harus cocok dengan struktur direktori.
// ...code...
// jika MyController Anda berada di direktori app/controllers dan bernamespace
// ini tidak akan berfungsi.
Flight::route('/hello', 'MyController->hello');
// Anda perlu memilih salah satu 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
Di aplikasi skeleton, ini didefinisikan di dalam file config.php
, tetapi agar kelas Anda ditemukan, Anda perlu memastikan bahwa metode path()
didefinisikan (mungkin ke root direktori Anda) sebelum Anda mencoba menggunakannya.
// Tambahkan path ke autoloader
Flight::path(__DIR__.'/../');
Changelog
- v3.7.2 - Anda dapat menggunakan Pascal_Snake_Case untuk nama kelas Anda dengan menjalankan
Loader::setV2ClassLoading(false);
- v2.0 - Fungsi autoload ditambahkan.
Learn/uploaded_file
Penanganan File yang Diunggah
Gambaran Umum
Kelas UploadedFile
di Flight memudahkan dan aman untuk menangani unggahan file dalam aplikasi Anda. Ini membungkus detail proses unggahan file PHP, memberikan cara yang sederhana dan berorientasi objek untuk mengakses informasi file dan memindahkan file yang diunggah.
Pemahaman
Ketika pengguna mengunggah file melalui formulir, PHP menyimpan informasi tentang file tersebut di superglobal $_FILES
. Di Flight, Anda jarang berinteraksi langsung dengan $_FILES
. Sebaliknya, objek Request
milik Flight (dapat diakses melalui Flight::request()
) menyediakan metode getUploadedFiles()
yang mengembalikan array objek UploadedFile
, membuat penanganan file jauh lebih nyaman dan kuat.
Kelas UploadedFile
menyediakan metode untuk:
- Mendapatkan nama file asli, tipe MIME, ukuran, dan lokasi sementara
- Memeriksa kesalahan unggahan
- Memindahkan file yang diunggah ke lokasi permanen
Kelas ini membantu Anda menghindari kesalahan umum dengan unggahan file, seperti penanganan kesalahan atau memindahkan file dengan aman.
Penggunaan Dasar
Mengakses File yang Diunggah dari Permintaan
Cara yang direkomendasikan untuk mengakses file yang diunggah adalah melalui objek permintaan:
Flight::route('POST /upload', function() {
// Untuk field formulir bernama <input type="file" name="myFile">
$uploadedFiles = Flight::request()->getUploadedFiles();
$file = $uploadedFiles['myFile'];
// Sekarang Anda dapat menggunakan metode UploadedFile
if ($file->getError() === UPLOAD_ERR_OK) {
$file->moveTo('/path/to/uploads/' . $file->getClientFilename());
echo "File berhasil diunggah!";
} else {
echo "Unggahan gagal: " . $file->getError();
}
});
Menangani Beberapa Unggahan File
Jika formulir Anda menggunakan name="myFiles[]"
untuk beberapa unggahan, Anda akan mendapatkan array objek UploadedFile
:
Flight::route('POST /upload', function() {
// Untuk field formulir bernama <input type="file" name="myFiles[]">
$uploadedFiles = Flight::request()->getUploadedFiles();
foreach ($uploadedFiles['myFiles'] as $file) {
if ($file->getError() === UPLOAD_ERR_OK) {
$file->moveTo('/path/to/uploads/' . $file->getClientFilename());
echo "Diunggah: " . $file->getClientFilename() . "<br>";
} else {
echo "Gagal mengunggah: " . $file->getClientFilename() . "<br>";
}
}
});
Membuat Instance UploadedFile Secara Manual
Biasanya, Anda tidak akan membuat UploadedFile
secara manual, tetapi Anda bisa jika diperlukan:
use flight\net\UploadedFile;
$file = new UploadedFile(
$_FILES['myfile']['name'],
$_FILES['myfile']['type'],
$_FILES['myfile']['size'],
$_FILES['myfile']['tmp_name'],
$_FILES['myfile']['error']
);
Mengakses Informasi File
Anda dapat dengan mudah mendapatkan detail tentang file yang diunggah:
echo $file->getClientFilename(); // Nama file asli dari komputer pengguna
echo $file->getClientMediaType(); // Tipe MIME (misalnya, image/png)
echo $file->getSize(); // Ukuran file dalam byte
echo $file->getTempName(); // Jalur file sementara di server
echo $file->getError(); // Kode kesalahan unggahan (0 berarti tidak ada kesalahan)
Memindahkan File yang Diunggah
Setelah memvalidasi file, pindahkan ke lokasi permanen:
try {
$file->moveTo('/path/to/uploads/' . $file->getClientFilename());
echo "File berhasil diunggah!";
} catch (Exception $e) {
echo "Unggahan gagal: " . $e->getMessage();
}
Metode moveTo()
akan melempar pengecualian jika ada yang salah (seperti kesalahan unggahan atau masalah izin).
Menangani Kesalahan Unggahan
Jika ada masalah selama unggahan, Anda dapat mendapatkan pesan kesalahan yang dapat dibaca oleh manusia:
if ($file->getError() !== UPLOAD_ERR_OK) {
// Anda dapat menggunakan kode kesalahan atau menangkap pengecualian dari moveTo()
echo "Ada kesalahan saat mengunggah file.";
}
Lihat Juga
- Requests - Pelajari cara mengakses file yang diunggah dari permintaan HTTP dan lihat lebih banyak contoh unggahan file.
- Configuration - Cara mengonfigurasi batas unggahan dan direktori di PHP.
- Extending - Cara menyesuaikan atau memperluas kelas inti Flight.
Pemecahan Masalah
- Selalu periksa
$file->getError()
sebelum memindahkan file. - Pastikan direktori unggahan Anda dapat ditulis oleh server web.
- Jika
moveTo()
gagal, periksa pesan pengecualian untuk detail. - Pengaturan
upload_max_filesize
danpost_max_size
PHP dapat membatasi unggahan file. - Untuk beberapa unggahan file, selalu loop melalui array objek
UploadedFile
.
Changelog
- v3.12.0 - Menambahkan kelas
UploadedFile
ke objek permintaan untuk penanganan file yang lebih mudah.
Guides/unit_testing
Pengujian Unit di Flight PHP dengan PHPUnit
Panduan ini memperkenalkan pengujian unit di Flight PHP menggunakan PHPUnit, ditujukan untuk pemula yang ingin memahami mengapa pengujian unit penting dan bagaimana menerapkannya secara praktis. Kami akan fokus pada pengujian perilaku—memastikan aplikasi Anda melakukan apa yang diharapkan, seperti mengirim email atau menyimpan catatan—bukan perhitungan sepele. Kami akan mulai dengan route handler sederhana dan maju ke controller yang lebih kompleks, menggabungkan dependency injection (DI) dan mocking layanan pihak ketiga.
Mengapa Pengujian Unit?
Pengujian unit memastikan kode Anda berperilaku seperti yang diharapkan, menangkap bug sebelum mencapai produksi. Ini sangat berharga di Flight, di mana routing ringan dan fleksibilitas dapat menyebabkan interaksi kompleks. Bagi pengembang solo atau tim, pengujian unit berfungsi sebagai jaring pengaman, mendokumentasikan perilaku yang diharapkan dan mencegah regresi saat Anda mengunjungi ulang kode nanti. Mereka juga meningkatkan desain: kode yang sulit diuji sering menandakan kelas yang terlalu kompleks atau terikat erat.
Tidak seperti contoh sederhana (misalnya, menguji x * y = z
), kami akan fokus pada perilaku dunia nyata, seperti memvalidasi input, menyimpan data, atau memicu tindakan seperti email. Tujuan kami adalah membuat pengujian mudah diakses dan bermakna.
Prinsip Panduan Umum
- Uji Perilaku, Bukan Implementasi: Fokus pada hasil (misalnya, "email dikirim" atau "catatan disimpan") daripada detail internal. Ini membuat pengujian tahan terhadap refactoring.
- Berhenti menggunakan
Flight::
: Metode statis Flight sangat nyaman, tapi membuat pengujian sulit. Anda harus terbiasa menggunakan variabel$app
dari$app = Flight::app();
.$app
memiliki semua metode yang sama denganFlight::
. Anda masih bisa menggunakan$app->route()
atau$this->app->json()
di controller Anda dll. Anda juga harus menggunakan router Flight yang sebenarnya dengan$router = $app->router()
dan kemudian Anda bisa menggunakan$router->get()
,$router->post()
,$router->group()
dll. Lihat Routing. - Jaga Pengujian Cepat: Pengujian cepat mendorong eksekusi yang sering. Hindari operasi lambat seperti panggilan database di pengujian unit. Jika Anda memiliki pengujian lambat, itu tanda Anda sedang menulis pengujian integrasi, bukan pengujian unit. Pengujian integrasi adalah ketika Anda benar-benar melibatkan database nyata, panggilan HTTP nyata, pengiriman email nyata dll. Mereka memiliki tempatnya, tapi mereka lambat dan bisa tidak stabil, artinya kadang gagal karena alasan yang tidak diketahui.
- Gunakan Nama Deskriptif: Nama pengujian harus dengan jelas menggambarkan perilaku yang diuji. Ini meningkatkan keterbacaan dan pemeliharaan.
- Hindari Globals Seperti Wabah: Minimalkan penggunaan
$app->set()
dan$app->get()
, karena mereka bertindak seperti state global, memerlukan mock di setiap pengujian. Lebih suka DI atau container DI (lihat Dependency Injection Container). Bahkan menggunakan metode$app->map()
secara teknis adalah "global" dan harus dihindari demi DI. Gunakan pustaka sesi seperti flightphp/session sehingga Anda bisa mock objek sesi di pengujian Anda. Jangan panggil$_SESSION
secara langsung di kode Anda karena itu menyuntikkan variabel global ke kode Anda, membuatnya sulit diuji. - Gunakan Dependency Injection: Suntik dependensi (misalnya,
PDO
, mailer) ke controller untuk mengisolasi logika dan menyederhanakan mocking. Jika Anda memiliki kelas dengan terlalu banyak dependensi, pertimbangkan untuk merestrukturisasinya menjadi kelas yang lebih kecil yang masing-masing memiliki tanggung jawab tunggal mengikuti prinsip SOLID. - Mock Layanan Pihak Ketiga: Mock database, klien HTTP (cURL), atau layanan email untuk menghindari panggilan eksternal. Uji satu atau dua lapis dalam, tapi biarkan logika inti berjalan. Misalnya, jika aplikasi Anda mengirim pesan teks, Anda TIDAK ingin benar-benar mengirim pesan teks setiap kali menjalankan pengujian karena biaya itu akan bertambah (dan akan lebih lambat). Sebaliknya, mock layanan pesan teks dan hanya verifikasi bahwa kode Anda memanggil layanan pesan teks dengan parameter yang benar.
- Bidik Cakupan Tinggi, Bukan Kesempurnaan: Cakupan baris 100% bagus, tapi itu tidak benar-benar berarti bahwa semuanya di kode Anda diuji seperti yang seharusnya (silakan teliti branch/path coverage di PHPUnit). Prioritaskan perilaku kritis (misalnya, pendaftaran pengguna, respons API dan menangkap respons gagal).
- Gunakan Controller untuk Route: Di definisi route Anda, gunakan controller bukan closures.
flight\Engine $app
disuntikkan ke setiap controller melalui constructor secara default. Di pengujian, gunakan$app = new Flight\Engine()
untuk menginisialisasi Flight dalam pengujian, suntikkan ke controller Anda, dan panggil metode secara langsung (misalnya,$controller->register()
). Lihat Extending Flight dan Routing. - Pilih gaya mocking dan patuhi itu: PHPUnit mendukung beberapa gaya mocking (misalnya, prophecy, mock built-in), atau Anda bisa menggunakan kelas anonim yang memiliki manfaat sendiri seperti code completion, rusak jika Anda mengubah definisi metode, dll. Hanya konsisten di seluruh pengujian Anda. Lihat PHPUnit Mock Objects.
- Gunakan visibilitas
protected
untuk metode/properti yang ingin Anda uji di subclass: Ini memungkinkan Anda untuk menimpa mereka di subclass pengujian tanpa membuatnya public, ini sangat berguna untuk mock kelas anonim.
Menyiapkan PHPUnit
Pertama, siapkan PHPUnit di proyek Flight PHP Anda menggunakan Composer untuk pengujian yang mudah. Lihat panduan PHPUnit Getting Started untuk detail lebih lanjut.
-
Di direktori proyek Anda, jalankan:
composer require --dev phpunit/phpunit
Ini menginstal PHPUnit terbaru sebagai dependensi pengembangan.
-
Buat direktori
tests
di root proyek Anda untuk file pengujian. -
Tambahkan skrip pengujian ke
composer.json
untuk kenyamanan:// other composer.json content "scripts": { "test": "phpunit --configuration phpunit.xml" }
-
Buat file
phpunit.xml
di root:<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Flight Tests"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>
Sekarang ketika pengujian Anda dibangun, Anda bisa menjalankan composer test
untuk mengeksekusi pengujian.
Menguji Route Handler Sederhana
Mari mulai dengan route dasar yang memvalidasi input email pengguna. Kami akan menguji perilakunya: mengembalikan pesan sukses untuk email valid dan kesalahan untuk yang tidak valid. Untuk validasi email, kami menggunakan filter_var
.
// index.php
$app->route('POST /register', [ UserController::class, 'register' ]);
// UserController.php
class UserController {
protected $app;
public function __construct(flight\Engine $app) {
$this->app = $app;
}
public function register() {
$email = $this->app->request()->data->email;
$responseArray = [];
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$responseArray = ['status' => 'error', 'message' => 'Invalid email'];
} else {
$responseArray = ['status' => 'success', 'message' => 'Valid email'];
}
$this->app->json($responseArray);
}
}
Untuk menguji ini, buat file pengujian. Lihat Unit Testing and SOLID Principles untuk lebih lanjut tentang struktur pengujian:
// tests/UserControllerTest.php
use PHPUnit\Framework\TestCase;
use Flight;
use flight\Engine;
class UserControllerTest extends TestCase {
public function testValidEmailReturnsSuccess() {
$app = new Engine();
$request = $app->request();
$request->data->email = 'test@example.com'; // Simulate POST data
$UserController = new UserController($app);
$UserController->register($request->data->email);
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('success', $output['status']);
$this->assertEquals('Valid email', $output['message']);
}
public function testInvalidEmailReturnsError() {
$app = new Engine();
$request = $app->request();
$request->data->email = 'invalid-email'; // Simulate POST data
$UserController = new UserController($app);
$UserController->register($request->data->email);
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('error', $output['status']);
$this->assertEquals('Invalid email', $output['message']);
}
}
Poin Kunci:
- Kami mensimulasikan data POST menggunakan kelas request. Jangan gunakan globals seperti
$_POST
,$_GET
, dll karena membuat pengujian lebih rumit (Anda harus selalu mereset nilai-nilai itu atau pengujian lain mungkin meledak). - Semua controller secara default akan memiliki instance
flight\Engine
yang disuntikkan ke dalamnya bahkan tanpa container DIC yang disiapkan. Ini membuat lebih mudah untuk menguji controller secara langsung. - Tidak ada penggunaan
Flight::
sama sekali, membuat kode lebih mudah diuji. - Pengujian memverifikasi perilaku: status dan pesan yang benar untuk email valid/tidak valid.
Jalankan composer test
untuk memverifikasi route berperilaku seperti yang diharapkan. Untuk lebih lanjut tentang requests dan responses di Flight, lihat dokumen terkait.
Menggunakan Dependency Injection untuk Controller yang Dapat Diuji
Untuk skenario yang lebih kompleks, gunakan dependency injection (DI) untuk membuat controller dapat diuji. Hindari globals Flight (misalnya, Flight::set()
, Flight::map()
, Flight::register()
) karena mereka bertindak seperti state global, memerlukan mock untuk setiap pengujian. Sebaliknya, gunakan container DI Flight, DICE, PHP-DI atau DI manual.
Mari gunakan flight\database\PdoWrapper
daripada PDO mentah. Wrapper ini jauh lebih mudah untuk dimock dan diuji unit!
Berikut adalah controller yang menyimpan pengguna ke database dan mengirim email selamat datang:
use flight\database\PdoWrapper;
class UserController {
protected $app;
protected $db;
protected $mailer;
public function __construct(Engine $app, PdoWrapper $db, MailerInterface $mailer) {
$this->app = $app;
$this->db = $db;
$this->mailer = $mailer;
}
public function register() {
$email = $this->app->request()->data->email;
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
// adding the return here helps unit testing to stop execution
return $this->app->jsonHalt(['status' => 'error', 'message' => 'Invalid email']);
}
$this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
$this->mailer->sendWelcome($email);
return $this->app->json(['status' => 'success', 'message' => 'User registered']);
}
}
Poin Kunci:
- Controller bergantung pada instance
PdoWrapper
danMailerInterface
(layanan email pihak ketiga pura-pura). - Dependensi disuntikkan melalui constructor, menghindari globals.
Menguji Controller dengan Mocks
Sekarang, mari uji perilaku UserController
: memvalidasi email, menyimpan ke database, dan mengirim email. Kami akan mock database dan mailer untuk mengisolasi controller.
// tests/UserControllerDICTest.php
use PHPUnit\Framework\TestCase;
class UserControllerDICTest extends TestCase {
public function testValidEmailSavesAndSendsEmail() {
// Sometimes mixing mocking styles is necessary
// Here we use PHPUnit's built-in mock for PDOStatement
$statementMock = $this->createMock(PDOStatement::class);
$statementMock->method('execute')->willReturn(true);
// Using an anonymous class to mock PdoWrapper
$mockDb = new class($statementMock) extends PdoWrapper {
protected $statementMock;
public function __construct($statementMock) {
$this->statementMock = $statementMock;
}
// When we mock it this way, we are not really making a database call.
// We can further setup this to alter the PDOStatement mock to simulate failures, etc.
public function runQuery(string $sql, array $params = []): PDOStatement {
return $this->statementMock;
}
};
$mockMailer = new class implements MailerInterface {
public $sentEmail = null;
public function sendWelcome($email): bool {
$this->sentEmail = $email;
return true;
}
};
$app = new Engine();
$app->request()->data->email = 'test@example.com';
$controller = new UserControllerDIC($app, $mockDb, $mockMailer);
$controller->register();
$response = $app->response()->getBody();
$result = json_decode($response, true);
$this->assertEquals('success', $result['status']);
$this->assertEquals('User registered', $result['message']);
$this->assertEquals('test@example.com', $mockMailer->sentEmail);
}
public function testInvalidEmailSkipsSaveAndEmail() {
$mockDb = new class() extends PdoWrapper {
// An empty constructor bypasses the parent constructor
public function __construct() {}
public function runQuery(string $sql, array $params = []): PDOStatement {
throw new Exception('Should not be called');
}
};
$mockMailer = new class implements MailerInterface {
public $sentEmail = null;
public function sendWelcome($email): bool {
throw new Exception('Should not be called');
}
};
$app = new Engine();
$app->request()->data->email = 'invalid-email';
// Need to map jsonHalt to avoid exiting
$app->map('jsonHalt', function($data) use ($app) {
$app->json($data, 400);
});
$controller = new UserControllerDIC($app, $mockDb, $mockMailer);
$controller->register();
$response = $app->response()->getBody();
$result = json_decode($response, true);
$this->assertEquals('error', $result['status']);
$this->assertEquals('Invalid email', $result['message']);
}
}
Poin Kunci:
- Kami mock
PdoWrapper
danMailerInterface
untuk menghindari panggilan database atau email nyata. - Pengujian memverifikasi perilaku: email valid memicu sisipan database dan pengiriman email; email tidak valid melewatkan keduanya.
- Mock dependensi pihak ketiga (misalnya,
PdoWrapper
,MailerInterface
), membiarkan logika controller berjalan.
Mocking Terlalu Banyak
Hati-hati jangan mock terlalu banyak kode Anda. Biarkan saya beri contoh di bawah tentang mengapa ini mungkin hal buruk menggunakan UserController
kami. Kami akan ubah pemeriksaan itu menjadi metode yang disebut isEmailValid
(menggunakan filter_var
) dan penambahan baru lainnya menjadi metode terpisah yang disebut registerUser
.
use flight\database\PdoWrapper;
use flight\Engine;
// UserControllerDICV2.php
class UserControllerDICV2 {
protected $app;
protected $db;
protected $mailer;
public function __construct(Engine $app, PdoWrapper $db, MailerInterface $mailer) {
$this->app = $app;
$this->db = $db;
$this->mailer = $mailer;
}
public function register() {
$email = $this->app->request()->data->email;
if (!$this->isEmailValid($email)) {
// adding the return here helps unit testing to stop execution
return $this->app->jsonHalt(['status' => 'error', 'message' => 'Invalid email']);
}
$this->registerUser($email);
$this->app->json(['status' => 'success', 'message' => 'User registered']);
}
protected function isEmailValid($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
protected function registerUser($email) {
$this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
$this->mailer->sendWelcome($email);
}
}
Dan sekarang pengujian unit yang terlalu dimock yang sebenarnya tidak menguji apa-apa:
use PHPUnit\Framework\TestCase;
class UserControllerTest extends TestCase {
public function testValidEmailSavesAndSendsEmail() {
$app = new Engine();
$app->request()->data->email = 'test@example.com';
// we are skipping the extra dependency injection here cause it's "easy"
$controller = new class($app) extends UserControllerDICV2 {
protected $app;
// Bypass the deps in the construct
public function __construct($app) {
$this->app = $app;
}
// We'll just force this to be valid.
protected function isEmailValid($email) {
return true; // Always return true, bypassing real validation
}
// Bypass the actual DB and mailer calls
protected function registerUser($email) {
return false;
}
};
$controller->register();
$response = $app->response()->getBody();
$result = json_decode($response, true);
$this->assertEquals('success', $result['status']);
$this->assertEquals('User registered', $result['message']);
}
}
Hore kami memiliki pengujian unit dan mereka lulus! Tapi tunggu, bagaimana jika saya benar-benar mengubah cara kerja internal isEmailValid
atau registerUser
? Pengujian saya masih akan lulus karena saya telah mock semua fungsionalitas. Biarkan saya tunjukkan apa yang saya maksud.
// UserControllerDICV2.php
class UserControllerDICV2 {
// ... other methods ...
protected function isEmailValid($email) {
// Changed logic
$validEmail = filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
// Now it should only have a specific domain
$validDomain = strpos($email, '@example.com') !== false;
return $validEmail && $validDomain;
}
}
Jika saya jalankan pengujian unit di atas, mereka masih lulus! Tapi karena saya tidak menguji perilaku (benar-benar membiarkan sebagian kode berjalan), saya berpotensi mengkode bug yang menunggu untuk terjadi di produksi. Pengujian harus dimodifikasi untuk memperhitungkan perilaku baru, dan juga kebalikan ketika perilaku bukan seperti yang kami harapkan.
Contoh Lengkap
Anda bisa menemukan contoh lengkap proyek Flight PHP dengan pengujian unit di GitHub: n0nag0n/flight-unit-tests-guide. Untuk pemahaman lebih dalam, lihat Unit Testing and SOLID Principles.
Kesalahan Umum
- Over-Mocking: Jangan mock setiap dependensi; biarkan beberapa logika (misalnya, validasi controller) berjalan untuk menguji perilaku nyata. Lihat Unit Testing and SOLID Principles.
- Global State: Menggunakan variabel PHP global (misalnya,
$_SESSION
,$_COOKIE
) secara berat membuat pengujian rapuh. Hal yang sama berlaku denganFlight::
. Refactor untuk melewatkan dependensi secara eksplisit. - Setup Kompleks: Jika setup pengujian merepotkan, kelas Anda mungkin memiliki terlalu banyak dependensi atau tanggung jawab yang melanggar prinsip SOLID.
Skala dengan Pengujian Unit
Pengujian unit bersinar di proyek yang lebih besar atau saat mengunjungi ulang kode setelah berbulan-bulan. Mereka mendokumentasikan perilaku dan menangkap regresi, menghemat Anda dari belajar ulang aplikasi Anda. Bagi pengembang solo, uji jalur kritis (misalnya, pendaftaran pengguna, pemrosesan pembayaran). Bagi tim, pengujian memastikan perilaku konsisten di seluruh kontribusi. Lihat Why Frameworks? untuk lebih lanjut tentang manfaat menggunakan framework dan pengujian.
Sumbangkan tips pengujian Anda sendiri ke repositori dokumentasi Flight PHP!
Ditulis oleh n0nag0n 2025
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
- PHP 7.4+: Terinstal di sistem Anda.
- Composer: Untuk manajemen ketergantungan.
- Editor Teks: Editor apa pun seperti VS Code atau PHPStorm.
- Pengetahuan dasar tentang PHP dan pengembangan web.
Langkah 1: Siapkan Proyek Anda
Mulailah dengan membuat direktori proyek baru dan menginstal Flight melalui Composer.
-
Buat Direktori:
mkdir flight-blog cd flight-blog
-
Instal Flight:
composer require flightphp/core
-
Buat Direktori Publik: Flight menggunakan titik masuk tunggal (
index.php
). Buat folderpublic/
untuk itu:mkdir public
-
index.php
Dasar: Buatpublic/index.php
dengan rute "hello world" yang sederhana:<?php require '../vendor/autoload.php'; Flight::route('/', function () { echo 'Halo, Flight!'; }); Flight::start();
-
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
app/config/
: File konfigurasi (misalnya, acara, rute).app/views/
: Template untuk merender halaman.data/
: File JSON untuk menyimpan pos blog.public/
: Root web denganindex.php
.
Langkah 3: Instal dan Konfigurasi Latte
Latte adalah mesin templating ringan yang terintegrasi dengan baik dengan Flight.
-
Instal Latte:
composer require latte/latte
-
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();
-
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>© {date('Y')} Blog Flight</p> </footer> </body> </html>
-
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. -
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.
-
Buat
routes.php
: Diapp/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']); });
-
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.
-
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); });
-
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.
-
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.
-
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}
-
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('/'); });
-
Uji Coba:
- Kunjungi
http://localhost:8000/create
. - Kirim pos baru (misalnya, “Pos Kedua” dengan beberapa konten).
- Periksa halaman utama untuk melihatnya terdaftar.
- Kunjungi
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
- Tambahkan Gaya: Gunakan CSS di template Anda untuk tampilan yang lebih baik.
- Database: Ganti
posts.json
dengan database seperti SQLite menggunakanPdoWrapper
. - Validasi: Tambahkan cek untuk slug duplikat atau input kosong.
- Middleware: Implementasikan autentikasi untuk pembuatan pos.
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
Kerangka Kerja PHP Flight
Flight adalah kerangka kerja yang cepat, sederhana, dan dapat diperluas untuk PHP—dibuat untuk pengembang yang ingin menyelesaikan pekerjaan dengan cepat, tanpa keributan. Baik Anda membangun aplikasi web klasik, API yang sangat cepat, atau bereksperimen dengan alat-alat terkini yang didukung AI, jejak rendah dan desain langsung Flight membuatnya cocok sempurna. Flight dimaksudkan untuk ringan, tetapi juga dapat menangani kebutuhan arsitektur perusahaan.
Mengapa Memilih Flight?
- Pemula Ramah: Flight adalah titik awal yang bagus untuk pengembang PHP baru. Struktur yang jelas dan sintaks sederhana membantu Anda belajar pengembangan web tanpa tersesat dalam boilerplate.
- Disukai oleh Profesional: Pengembang berpengalaman mencintai Flight karena fleksibilitas dan kontrolnya. Anda dapat mengembangkan dari prototipe kecil hingga aplikasi lengkap tanpa berganti kerangka kerja.
- Ramah AI: Beban minimal dan arsitektur bersih Flight membuatnya ideal untuk mengintegrasikan alat dan API AI. Baik Anda membangun chatbot pintar, dasbor yang didukung AI, atau hanya ingin bereksperimen, Flight tidak menghalangi sehingga Anda dapat fokus pada hal yang penting. Aplikasi skeleton app dilengkapi dengan file instruksi yang sudah dibuat sebelumnya untuk asisten pengkodean AI utama langsung dari kotak! Pelajari lebih lanjut tentang menggunakan AI dengan Flight
Ikhtisar Video
Mulai Cepat
Untuk instalasi dasar yang cepat, instal dengan Composer:
composer require flightphp/core
Atau Anda dapat mengunduh zip dari repo di sini. Kemudian Anda akan memiliki file index.php
dasar 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 'hello world!';
});
Flight::route('/json', function() {
Flight::json([
'hello' => 'world'
]);
});
Flight::start();
Itu saja! Anda memiliki aplikasi Flight dasar. Anda sekarang dapat menjalankan file ini dengan php -S localhost:8000
dan kunjungi http://localhost:8000
di browser Anda untuk melihat output.
Aplikasi Skeleton/Boilerplate
Ada contoh aplikasi untuk membantu Anda memulai proyek dengan Flight. Ini memiliki tata letak terstruktur, konfigurasi dasar yang sudah disetel, dan menangani skrip composer langsung dari awal! Periksa flightphp/skeleton untuk proyek yang siap pakai, atau kunjungi halaman examples untuk inspirasi. Ingin melihat bagaimana AI cocok? Jelajahi contoh yang didukung AI.
Menginstal Aplikasi Skeleton
Cukup mudah!
# Buat proyek baru
composer create-project flightphp/skeleton my-project/
# Masuk ke direktori proyek baru Anda
cd my-project/
# Buka server pengembangan lokal untuk memulai segera!
composer start
Ini akan membuat struktur proyek, menyiapkan file yang Anda butuhkan, dan Anda siap berangkat!
Kinerja Tinggi
Flight adalah salah satu kerangka kerja PHP tercepat di luar sana. Inti ringannya berarti overhead lebih sedikit dan kecepatan lebih—sempurna untuk aplikasi tradisional dan proyek modern yang didukung AI. Anda dapat melihat semua benchmark di TechEmpower
Lihat benchmark di bawah dengan beberapa kerangka kerja PHP populer lainnya.
Framework | Plaintext Reqs/sec | JSON Reqs/sec |
---|---|---|
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 |
Flight dan AI
Penasaran bagaimana menanganinya AI? Temukan bagaimana Flight membuat bekerja dengan LLM pengkodean favorit Anda menjadi mudah!
Komunitas
Kami ada di Matrix Chat
Dan Discord
Berkontribusi
Ada dua cara Anda dapat berkontribusi ke Flight:
- Berkontribusi ke kerangka kerja inti dengan mengunjungi core repository.
- Bantu membuat dokumen lebih baik! Situs web dokumentasi ini dihosting di Github. Jika Anda menemukan kesalahan atau ingin meningkatkan sesuatu, silakan submit pull request. Kami menyukai pembaruan dan ide baru—terutama seputar AI dan teknologi baru!
Persyaratan
Flight memerlukan PHP 7.4 atau lebih baru.
Catatan: PHP 7.4 didukung karena pada saat penulisan (2024) PHP 7.4 adalah versi default untuk beberapa distribusi Linux LTS. Memaksa perpindahan ke PHP >8 akan menimbulkan masalah bagi pengguna tersebut. Kerangka kerja 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 penangkapan file PHP mandiri yang ringan, sederhana dan standalone, difork dari Wruczek/PHP-File-Cache
Keunggulan
- Ringan, mandiri dan sederhana
- Semua kode dalam satu file - tidak ada driver yang tidak berguna.
- Aman - setiap file cache yang dihasilkan memiliki header PHP dengan die, membuat akses langsung tidak mungkin bahkan jika seseorang mengetahui jalur dan server Anda tidak dikonfigurasi dengan benar
- Didokumentasikan dengan baik dan diuji
- Menangani konkurensi dengan benar melalui flock
- Mendukung PHP 7.4+
- Gratis di bawah lisensi MIT
Situs dokumentasi ini menggunakan pustaka ini untuk menangkap setiap halaman!
Klik di sini untuk melihat kode.
Instalasi
Instal melalui composer:
composer require flightphp/cache
Penggunaan
Penggunaan cukup sederhana. Ini menyimpan file cache di direktori cache.
use flight\Cache;
$app = Flight::app();
// Anda melewatkan 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 disetel dalam file bootstrap Anda atau di tempat lain dalam aplikasi Anda
$cache->setDevMode(ENVIRONMENT === 'development');
});
Mendapatkan Nilai Cache
Anda menggunakan metode get()
untuk mendapatkan nilai cache. Jika Anda ingin metode kemudahan yang akan menyegarkan cache jika sudah kedaluwarsa, Anda bisa menggunakan refreshIfExpired()
.
// Dapatkan instance cache
$cache = Flight::cache();
$data = $cache->refreshIfExpired('simple-cache-test', function () {
return date("H:i:s"); // return data to be cached
}, 10); // 10 detik
// atau
$data = $cache->get('simple-cache-test');
if(empty($data)) {
$data = date("H:i:s");
$cache->set('simple-cache-test', $data, 10); // 10 detik
}
Menyimpan Nilai Cache
Anda menggunakan metode set()
untuk menyimpan nilai di cache.
Flight::cache()->set('simple-cache-test', 'my cached data', 10); // 10 detik
Menghapus Nilai Cache
Anda menggunakan metode delete()
untuk menghapus nilai di cache.
Flight::cache()->delete('simple-cache-test');
Memeriksa Apakah Nilai Cache Ada
Anda menggunakan metode exists()
untuk memeriksa apakah nilai ada di cache.
if(Flight::cache()->exists('simple-cache-test')) {
// lakukan sesuatu
}
Membersihkan Cache
Anda menggunakan metode flush()
untuk membersihkan seluruh cache.
Flight::cache()->flush();
Mengambil metadata dengan cache
Jika Anda ingin mengambil timestamp dan metadata lainnya tentang entri cache, pastikan Anda melewatkan true
sebagai parameter yang benar.
$data = $cache->refreshIfExpired("simple-cache-meta-test", function () {
echo "Refreshing data!" . PHP_EOL;
return date("H:i:s"); // return data to be cached
}, 10, true); // true = return with metadata
// atau
$data = $cache->get("simple-cache-meta-test", true); // true = return with metadata
/*
Contoh item cache yang diambil dengan metadata:
{
"time":1511667506, <-- save unix timestamp
"expire":10, <-- expire time in seconds
"data":"04:38:26", <-- unserialized data
"permanent":false
}
Menggunakan metadata, kita bisa, misalnya, menghitung kapan item disimpan atau kapan kedaluwarsa
Kita juga bisa mengakses data itu sendiri dengan kunci "data"
*/
$expiresin = ($data["time"] + $data["expire"]) - time(); // get unix timestamp when data expires and subtract current timestamp from it
$cacheddate = $data["data"]; // we access the data itself with the "data" key
echo "Latest cache save: $cacheddate, expires in $expiresin seconds";
Dokumentasi
Kunjungi https://github.com/flightphp/cache untuk melihat kode. Pastikan Anda melihat folder examples untuk cara tambahan menggunakan cache.
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:
command
: Perintah untuk menjalankan pekerja Andadirectory
: Direktori kerja untuk pekerjaautostart
: Mulai secara otomatis saat supervisord dimulaiautorestart
: Mulai ulang secara otomatis jika proses keluarstartretries
: Jumlah kali untuk mencoba memulai jika gagalstderr_logfile
/stdout_logfile
: Lokasi file loguser
: Pengguna sistem untuk menjalankan prosesnumprocs
: Jumlah instance pekerja yang akan dijalankanprocess_name
: Format penamaan untuk beberapa proses pekerja
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/n0nag0n_wordpress
Integrasi WordPress: n0nag0n/wordpress-integration-for-flight-framework
Ingin menggunakan Flight PHP di dalam situs WordPress Anda? Plugin ini membuatnya sangat mudah! Dengan n0nag0n/wordpress-integration-for-flight-framework
, Anda dapat menjalankan aplikasi Flight penuh tepat di samping instalasi WordPress Anda—sempurna untuk membangun API khusus, microservices, atau bahkan aplikasi lengkap tanpa meninggalkan kenyamanan WordPress.
Apa yang Dilakukannya?
- Mengintegrasikan Flight PHP dengan WordPress secara mulus
- Arahkan permintaan ke Flight atau WordPress berdasarkan pola URL
- Atur kode Anda dengan controller, model, dan views (MVC)
- Mudah atur struktur folder Flight yang direkomendasikan
- Gunakan koneksi database WordPress atau milik Anda sendiri
- Sesuaikan interaksi antara Flight dan WordPress
- Antarmuka admin sederhana untuk konfigurasi
Instalasi
- Unggah folder
flight-integration
ke direktori/wp-content/plugins/
Anda. - Aktifkan plugin di admin WordPress (menu Plugins).
- Buka Pengaturan > Flight Framework untuk mengonfigurasi plugin.
- Atur jalur vendor ke instalasi Flight Anda (atau gunakan Composer untuk menginstal Flight).
- Konfigurasi jalur folder aplikasi Anda dan buat struktur folder (plugin dapat membantu dengan ini!).
- Mulailah membangun aplikasi Flight Anda!
Contoh Penggunaan
Contoh Rute Dasar
Di file app/config/routes.php
Anda:
Flight::route('GET /api/hello', function() {
Flight::json(['message' => 'Hello World!']);
});
Contoh Controller
Buat controller di app/controllers/ApiController.php
:
namespace app\controllers;
use Flight;
class ApiController {
public function getUsers() {
// Anda dapat menggunakan fungsi WordPress di dalam Flight!
$users = get_users();
$result = [];
foreach($users as $user) {
$result[] = [
'id' => $user->ID,
'name' => $user->display_name,
'email' => $user->user_email
];
}
Flight::json($result);
}
}
Kemudian, di routes.php
Anda:
Flight::route('GET /api/users', [app\controllers\ApiController::class, 'getUsers']);
FAQ
T: Apakah saya perlu mengetahui Flight untuk menggunakan plugin ini?
J: Ya, ini untuk pengembang yang ingin menggunakan Flight dalam WordPress. Pengetahuan dasar tentang routing dan penanganan permintaan Flight direkomendasikan.
T: Apakah ini akan memperlambat situs WordPress saya?
J: Tidak! Plugin hanya memproses permintaan yang sesuai dengan rute Flight Anda. Semua permintaan lainnya tetap ke WordPress seperti biasa.
T: Bisakah saya menggunakan fungsi WordPress di aplikasi Flight saya?
J: Tentu saja! Anda memiliki akses penuh ke semua fungsi WordPress, hooks, dan globals dari dalam rute dan controller Flight Anda.
T: Bagaimana cara membuat rute khusus?
J: Tentukan rute Anda di file config/routes.php
di folder aplikasi Anda. Lihat file sampel yang dibuat oleh generator struktur folder untuk contoh.
Changelog
1.0.0
Rilis awal.
Untuk informasi lebih lanjut, periksa GitHub repo.
Awesome-plugins/ghost_session
Ghostff/Session
Manajer Sesi PHP (non-blocking, flash, segment, enkripsi sesi). Menggunakan PHP open_ssl untuk enkripsi/dekripsi data sesi 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 diharuskan untuk mengirimkan apa pun untuk menggunakan pengaturan default dengan sesi Anda. Anda dapat membaca tentang pengaturan lebih lanjut 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 melakukan commit sesi pada setiap pemuatan 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 melakukan commit secara sengaja.
$session->commit();
});
// Periksa ini bisa ada di 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 reguler
})->addMiddleware(function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
});
Contoh Lebih Kompleks
Berikut adalah contoh lebih kompleks tentang bagaimana Anda mungkin menggunakan ini.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
// atur jalur khusus ke file konfigurasi sesi Anda sebagai argumen pertama
// atau berikan array khusus
$app->register('session', Session::class, [
[
// jika Anda ingin menyimpan data sesi di database (bagus jika Anda ingin sesuatu seperti, "keluarkan saya dari semua perangkat" fungsionalitas)
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 diperlukan dan/atau sulit untuk commit() sesi Anda.
// selain itu Anda bisa melakukan Flight::after('start', function() { Flight::session()->commit(); });
Session::CONFIG_MYSQL_DS => [
'driver' => 'mysql', # Pengandar basis data untuk PDO dns misalnya (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 biaya overhead dari membangun koneksi baru setiap kali skrip perlu berbicara ke basis data, menghasilkan aplikasi web yang lebih cepat. CARI BACKSIDE SENDIRI
]
]
]);
Bantuan! Data Sesi Saya Tidak Bertahan!
Apakah Anda mengatur data sesi dan itu tidak bertahan antara permintaan? Anda mungkin lupa untuk melakukan commit data sesi Anda. Anda bisa melakukan ini 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 melakukan commit secara 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 melakukan commit 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 bisa melakukan Flight::after('start', function() { Flight::session()->commit(); });
untuk melakukan commit data sesi Anda setelah setiap permintaan.
Dokumentasi
Kunjungi Github Readme untuk dokumentasi lengkap. Opsi konfigurasi didokumentasikan dengan baik di file default_config.php itu sendiri. Kode ini sederhana untuk dipahami jika Anda ingin menelusuri paket ini sendiri.
Awesome-plugins/async
Async
Async adalah paket kecil untuk framework Flight yang memungkinkan Anda menjalankan aplikasi Flight di dalam server dan runtime asinkron seperti Swoole, AdapterMan, ReactPHP, Amp, RoadRunner, Workerman, dll. Secara default, ia menyertakan adapter untuk Swoole dan AdapterMan.
Tujuan: mengembangkan dan mendebug dengan PHP-FPM (atau server bawaan) dan beralih ke Swoole (atau driver asinkron lainnya) untuk produksi dengan perubahan minimal.
Persyaratan
- PHP 7.4 atau lebih tinggi
- Framework Flight 3.16.1 atau lebih tinggi
- Ekstensi Swoole
Instalasi
Instal melalui composer:
composer require flightphp/async
Jika Anda berencana menjalankan dengan Swoole, instal ekstensi tersebut:
# menggunakan pecl
pecl install swoole
# atau openswoole
pecl install openswoole
# atau dengan pengelola paket (contoh Debian/Ubuntu)
sudo apt-get install php-swoole
Contoh Cepat Swoole
Berikut adalah pengaturan minimal yang menunjukkan cara mendukung baik PHP-FPM (atau server bawaan) maupun Swoole menggunakan kode dasar yang sama.
File yang Anda butuhkan dalam proyek Anda:
- index.php
- swoole_server.php
- SwooleServerDriver.php
index.php
File ini adalah saklar sederhana yang memaksa aplikasi berjalan dalam mode PHP untuk pengembangan.
// index.php
<?php
define('NOT_SWOOLE', true);
include 'swoole_server.php';
swoole_server.php
File ini memulai aplikasi Flight Anda dan akan memulai driver Swoole ketika NOT_SWOOLE tidak didefinisikan.
// swoole_server.php
<?php
require_once __DIR__ . '/vendor/autoload.php';
$app = Flight::app();
$app->route('/', function() use ($app) {
$app->json(['hello' => 'world']);
});
if (!defined('NOT_SWOOLE')) {
// Require kelas SwooleServerDriver ketika berjalan dalam mode Swoole.
require_once __DIR__ . '/SwooleServerDriver.php';
Swoole\Runtime::enableCoroutine();
$Swoole_Server = new SwooleServerDriver('127.0.0.1', 9501, $app);
$Swoole_Server->start();
} else {
$app->start();
}
SwooleServerDriver.php
Driver ringkas yang menunjukkan cara menjembatani permintaan Swoole ke Flight menggunakan AsyncBridge dan adapter Swoole.
// SwooleServerDriver.php
<?php
use flight\adapter\SwooleAsyncRequest;
use flight\adapter\SwooleAsyncResponse;
use flight\AsyncBridge;
use flight\Engine;
use Swoole\HTTP\Server as SwooleServer;
use Swoole\HTTP\Request as SwooleRequest;
use Swoole\HTTP\Response as SwooleResponse;
class SwooleServerDriver {
protected $Swoole;
protected $app;
public function __construct(string $host, int $port, Engine $app) {
$this->Swoole = new SwooleServer($host, $port);
$this->app = $app;
$this->setDefault();
$this->bindWorkerEvents();
$this->bindHttpEvent();
}
protected function setDefault() {
$this->Swoole->set([
'daemonize' => false,
'dispatch_mode' => 1,
'max_request' => 8000,
'open_tcp_nodelay' => true,
'reload_async' => true,
'max_wait_time' => 60,
'enable_reuse_port' => true,
'enable_coroutine' => true,
'http_compression' => false,
'enable_static_handler' => true,
'document_root' => __DIR__,
'static_handler_locations' => ['/css', '/js', '/images', '/.well-known'],
'buffer_output_size' => 4 * 1024 * 1024,
'worker_num' => 4,
]);
$app = $this->app;
$app->map('stop', function (?int $code = null) use ($app) {
if ($code !== null) {
$app->response()->status($code);
}
});
}
protected function bindHttpEvent() {
$app = $this->app;
$AsyncBridge = new AsyncBridge($app);
$this->Swoole->on('Start', function(SwooleServer $server) {
echo "Swoole http server is started at http://127.0.0.1:9501\n";
});
$this->Swoole->on('Request', function (SwooleRequest $request, SwooleResponse $response) use ($AsyncBridge) {
$SwooleAsyncRequest = new SwooleAsyncRequest($request);
$SwooleAsyncResponse = new SwooleAsyncResponse($response);
$AsyncBridge->processRequest($SwooleAsyncRequest, $SwooleAsyncResponse);
$response->end();
gc_collect_cycles();
});
}
protected function bindWorkerEvents() {
$createPools = function() {
// create worker-specific connection pools here
};
$closePools = function() {
// close pools / cleanup here
};
$this->Swoole->on('WorkerStart', $createPools);
$this->Swoole->on('WorkerStop', $closePools);
$this->Swoole->on('WorkerError', $closePools);
}
public function start() {
$this->Swoole->start();
}
}
Menjalankan Server
- Pengembangan (server bawaan PHP / PHP-FPM):
- php -S localhost:8000 (atau tambahkan -t public/ jika index Anda berada di public/)
- Produksi (Swoole):
- php swoole_server.php
Tips: Untuk penggunaan produksi, gunakan proxy terbalik (Nginx) di depan Swoole untuk menangani TLS, file statis, dan penyeimbangan beban.
Catatan Konfigurasi
Driver Swoole mengekspos beberapa opsi konfigurasi:
- worker_num: jumlah proses pekerja
- max_request: permintaan per pekerja sebelum restart
- enable_coroutine: gunakan coroutine untuk konkurensi
- buffer_output_size: ukuran buffer output
Sesuaikan ini dengan sumber daya host dan pola lalu lintas Anda.
Penanganan Kesalahan
AsyncBridge menerjemahkan kesalahan Flight menjadi respons HTTP yang tepat. Anda juga dapat menambahkan penanganan kesalahan pada tingkat rute:
$app->route('/*', function() use ($app) {
try {
// logika rute
} catch (Exception $e) {
$app->response()->status(500);
$app->json(['error' => $e->getMessage()]);
}
});
AdapterMan dan Runtime Lainnya
AdapterMan didukung sebagai adapter runtime alternatif. Paket ini dirancang untuk dapat diadaptasi — menambahkan atau menggunakan adapter lain umumnya mengikuti pola yang sama: mengonversi permintaan/respons server menjadi permintaan/respons Flight melalui AsyncBridge dan adapter khusus runtime.
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:
- Membuat Skrip SQL
- Mengelola menggunakan Baris Perintah atau API.
Skrip SQL
Skrip dibagi menjadi tiga set skrip:
- Skrip BASIS berisi SEMUA perintah SQL untuk membuat basis data yang baru;
- Skrip UP berisi semua perintah migrasi SQL untuk "naik" versi basis data;
- Skrip DOWN berisi semua perintah migrasi SQL untuk "turun" atau mengembalikan versi basis data;
Direktori skrip adalah:
<root dir>
|
+-- base.sql
|
+-- /migrations
|
+-- /up
|
+-- 00001.sql
+-- 00002.sql
+-- /down
|
+-- 00000.sql
+-- 00001.sql
- "base.sql" adalah skrip dasar
- Folder "up" berisi skrip untuk migrasi naik versi. Sebagai contoh: 00002.sql adalah skrip untuk mengubah basis data dari versi '1' ke '2'.
- Folder "down" berisi skrip untuk migrasi turun versi. Sebagai contoh: 00001.sql adalah skrip untuk mengubah basis data dari versi '2' ke '1'. Folder "down" adalah opsional.
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:
- Pengembang 1 membuat cabang dan versi terbaru misalnya 42.
- Pengembang 2 membuat cabang pada saat yang sama dan memiliki nomor versi basis data yang sama.
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
- Membuat koneksi objek ConnectionManagement. Untuk informasi lebih lanjut, lihat komponen "byjg/anydataset".
- Membuat objek Migrasi dengan koneksi ini dan folder tempat skrip SQL berada.
- Gunakan perintah yang sesuai untuk "reset", "up", atau "down" skrip migrasi.
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 - Penanganan Sesi Berbasis File Ringan
Ini adalah plugin penanganan sesi berbasis file yang ringan untuk Flight PHP Framework. Ini menyediakan solusi sederhana namun kuat untuk mengelola sesi, dengan fitur seperti pembacaan sesi non-blocking, enkripsi opsional, fungsi 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 yang memiliki banyak fitur serupa tetapi dengan backend basis data.
Kunjungi repositori Github untuk kode sumber lengkap dan detail.
Instalasi
Instal plugin melalui Composer:
composer require flightphp/session
Penggunaan Dasar
Berikut adalah contoh sederhana 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'); // Keluaran: johndoe
echo $session->get('preferences', 'default_theme'); // Keluaran: default_theme
if ($session->get('user_id')) {
Flight::json(['message' => 'Pengguna telah masuk!', '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
- Non-Blocking: Menggunakan
read_and_close
secara default untuk memulai sesi, mencegah masalah penguncian sesi. - Auto-Commit: Diaktifkan secara default, sehingga perubahan disimpan secara otomatis saat shutdown kecuali dinonaktifkan.
- File Storage: Sesi disimpan di direktori temp sistem di bawah
/flight_sessions
secara default.
Konfigurasi
Anda dapat menyesuaikan penanganan sesi dengan meneruskan array opsi saat mendaftarkan:
// Ya, ini array ganda :)
$app->register('session', Session::class, [ [
'save_path' => '/custom/path/to/sessions', // Direktori untuk file sesi
'prefix' => 'myapp_', // Awalan untuk file sesi
'encryption_key' => 'a-secure-32-byte-key-here', // Aktifkan enkripsi (32 byte direkomendasikan 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
'serialization' => 'json', // Metode serialisasi: 'json' (default) atau 'php' (legacy)
] ]);
Opsi Konfigurasi
Option | Description | Default Value |
---|---|---|
save_path |
Direktori tempat file sesi disimpan | sys_get_temp_dir() . '/flight_sessions' |
prefix |
Awalan untuk file sesi yang disimpan | sess_ |
encryption_key |
Kunci untuk enkripsi AES-256-CBC (opsional) | null (tanpa enkripsi) |
auto_commit |
Auto-simpan data sesi saat shutdown | true |
start_session |
Mulai sesi secara otomatis | true |
test_mode |
Jalankan dalam mode uji tanpa memengaruhi sesi PHP | false |
test_session_id |
ID sesi khusus untuk mode uji (opsional) | Dibuat acak jika tidak disetel |
serialization |
Metode serialisasi: 'json' (default, aman) atau 'php' (legacy, mengizinkan objek) | 'json' |
Mode Serialisasi
Secara default, pustaka ini menggunakan serialisasi JSON untuk data sesi, yang aman dan mencegah kerentanan injeksi objek PHP. Jika Anda perlu menyimpan objek PHP dalam sesi (tidak direkomendasikan untuk sebagian besar aplikasi), Anda dapat memilih serialisasi PHP legacy:
'serialization' => 'json'
(default):- Hanya array dan primitif yang diizinkan dalam data sesi.
- Lebih aman: kebal terhadap injeksi objek PHP.
- File diawali dengan
J
(JSON biasa) atauF
(JSON terenkripsi).
'serialization' => 'php'
:- Mengizinkan penyimpanan objek PHP (gunakan dengan hati-hati).
- File diawali dengan
P
(serialisasi PHP biasa) atauE
(serialisasi PHP terenkripsi).
Catatan: Jika Anda menggunakan serialisasi JSON, upaya untuk menyimpan objek akan melemparkan pengecualian.
Penggunaan Lanjutan
Commit Manual
Jika Anda menonaktifkan auto-commit, Anda harus secara manual melakukan commit perubahan:
$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 pengambilan
});
Regenerasi Sesi
Regenerasikan ID sesi untuk keamanan (misalnya, setelah masuk):
Flight::route('/post-login', function() {
$session = Flight::session();
$session->regenerate(); // ID baru, simpan 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 hanya contoh sederhana cara menggunakannya dalam middleware. Untuk contoh yang lebih mendalam, lihat dokumentasi middleware.
Metode
Kelas Session
menyediakan metode-metode berikut:
set(string $key, $value)
: Menyimpan nilai dalam sesi.get(string $key, $default = null)
: Mengambil nilai, dengan default opsional jika kunci tidak ada.delete(string $key)
: Menghapus kunci tertentu dari sesi.clear()
: Menghapus semua data sesi, tetapi mempertahankan nama file sesi yang sama.commit()
: Menyimpan data sesi saat ini ke sistem file.id()
: Mengembalikan ID sesi saat ini.regenerate(bool $deleteOldFile = false)
: Meregenerasikan ID sesi termasuk membuat file sesi baru, mempertahankan semua data lama dan file lama tetap ada. Jika$deleteOldFile
adalahtrue
, file sesi lama dihapus.destroy(string $id)
: Menghancurkan sesi berdasarkan ID dan menghapus file sesi dari sistem. Ini bagian dariSessionHandlerInterface
dan$id
diperlukan. Penggunaan khas adalah$session->destroy($session->id())
.getAll()
: Mengembalikan semua data dari sesi saat ini.
Semua metode kecuali get()
dan id()
mengembalikan instance Session
untuk chaining.
Mengapa Menggunakan Plugin Ini?
- Ringan: Tidak ada ketergantungan eksternal—hanya file.
- Non-Blocking: Menghindari penguncian sesi dengan
read_and_close
secara default. - Aman: Mendukung enkripsi AES-256-CBC untuk data sensitif.
- Fleksibel: Opsi auto-commit, mode uji, dan kontrol manual.
- Flight-Native: Dibuat khusus untuk kerangka Flight.
Detail Teknis
- Format Penyimpanan: File sesi diawali dengan
sess_
dan disimpan disave_path
yang dikonfigurasi. Awalan konten file:J
: JSON biasa (default, tanpa enkripsi)F
: JSON terenkripsi (default dengan enkripsi)P
: Serialisasi PHP biasa (legacy, tanpa enkripsi)E
: Serialisasi PHP terenkripsi (legacy dengan enkripsi)
- Enkripsi: Menggunakan AES-256-CBC dengan IV acak per tulis sesi saat
encryption_key
disediakan. Enkripsi berfungsi untuk kedua mode serialisasi JSON dan PHP. - Serialisasi: JSON adalah metode default dan paling aman. Serialisasi PHP tersedia untuk penggunaan legacy/tingkat lanjut, tetapi kurang aman.
- Garbage Collection: Mengimplementasikan
SessionHandlerInterface::gc()
untuk membersihkan sesi yang kedaluwarsa.
Berkontribusi
Kontribusi diterima! Fork repositori, buat perubahan Anda, dan kirimkan pull request. Laporkan bug atau sarankan fitur melalui pelacak isu Github.
Lisensi
Plugin ini dilisensikan di bawah Lisensi MIT. Lihat repositori Github untuk detail.
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.
- Jika Anda menggunakan proyek tulang, Anda dapat menjalankan
php runway [command]
dari akar proyek Anda. - 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 menjadi sedikit lebih kaya.
- Flight - Analisis semua variabel Flight.
- Database - Analisis semua kueri yang telah dijalankan di halaman (jika Anda menginisialisasi koneksi database dengan benar)
- Request - Analisis semua variabel
$_SERVER
dan periksa semua payload global ($_GET
,$_POST
,$_FILES
) - Session - Analisis semua variabel
$_SESSION
jika sesi aktif.
Ini adalah Panel
Dan setiap panel menampilkan informasi yang sangat membantu tentang aplikasi Anda!
Klik di sini untuk melihat kode.
Installation
Jalankan composer require flightphp/tracy-extensions --dev
dan Anda siap melanjutkan!
Configuration
Ada sangat sedikit konfigurasi yang perlu Anda lakukan untuk memulai ini. Anda perlu menginisialisasi debugger Tracy sebelum menggunakan ini https://tracy.nette.org/en/guide:
<?php
use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;
// bootstrap code
require __DIR__ . '/vendor/autoload.php';
Debugger::enable();
// You may need to specify your environment with Debugger::enable(Debugger::DEVELOPMENT)
// if you use database connections in your app, there is a
// required PDO wrapper to use ONLY IN DEVELOPMENT (not production please!)
// It has the same parameters as a regular PDO connection
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// or if you attach this to the Flight framework
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// now whenever you make a query it will capture the time, query, and parameters
// This connects the dots
if(Debugger::$showBar === true) {
// This needs to be false or Tracy can't actually render :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app());
}
// more code
Flight::start();
Additional Configuration
Session Data
Jika Anda memiliki handler sesi kustom (seperti ghostff/session), Anda dapat mengirimkan array data sesi apa pun ke Tracy dan itu akan secara otomatis menampilkannya untuk Anda. Anda mengirimkannya dengan kunci session_data
di parameter kedua dari konstruktor TracyExtensionLoader
.
use Ghostff\Session\Session;
// or use flight\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
if(Debugger::$showBar === true) {
// This needs to be false or Tracy can't actually render :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}
// routes and other things...
Flight::start();
Latte
PHP 8.1+ diperlukan untuk bagian ini.
Jika Anda memiliki Latte yang terinstal di proyek Anda, Tracy memiliki integrasi native dengan Latte untuk menganalisis template Anda. Anda cukup mendaftarkan ekstensi dengan instance Latte Anda.
require 'vendor/autoload.php';
$app = Flight::app();
$app->map('render', function($template, $data, $block = null) {
$latte = new Latte\Engine;
// other configurations...
// only add the extension if Tracy Debug Bar is enabled
if(Debugger::$showBar === true) {
// this is where you add the Latte Panel to Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
}
$latte->render($template, $data, $block);
});
Awesome-plugins/apm
Dokumentasi FlightPHP APM
Selamat datang di FlightPHP APM—pelatih performa pribadi untuk aplikasi Anda! Panduan ini adalah peta jalan Anda untuk menyiapkan, menggunakan, dan menguasai Application Performance Monitoring (APM) dengan FlightPHP. Baik Anda sedang memburu permintaan lambat atau hanya ingin bersemangat dengan grafik latensi, kami siap membantu. Mari buat aplikasi Anda lebih cepat, pengguna Anda lebih bahagia, dan sesi debugging Anda menjadi mudah!
Lihat demo dari dashboard untuk situs Flight Docs.
Mengapa APM Penting
Bayangkan ini: aplikasi Anda seperti restoran sibuk. Tanpa cara untuk melacak berapa lama pesanan memakan waktu atau di mana dapur tersendat, Anda hanya menebak-nebak mengapa pelanggan pergi dengan kesal. APM adalah sous-chef Anda—ia mengawasi setiap langkah, dari permintaan masuk hingga kueri database, dan menandai apa pun yang memperlambat Anda. Halaman lambat membuat pengguna pergi (studi mengatakan 53% bounce jika situs memakan waktu lebih dari 3 detik untuk dimuat!), dan APM membantu Anda menangkap masalah-masalah itu sebelum mereka menyakitkan. Ini adalah ketenangan pikiran yang proaktif—lebih sedikit momen “mengapa ini rusak?” dan lebih banyak kemenangan “lihat betapa lancarnya ini berjalan!”.
Instalasi
Mulai dengan Composer:
composer require flightphp/apm
Anda memerlukan:
- PHP 7.4+: Menjaga kami kompatibel dengan distribusi Linux LTS sambil mendukung PHP modern.
- FlightPHP Core v3.15+: Framework ringan yang kami tingkatkan.
Database yang Didukung
FlightPHP APM saat ini mendukung database berikut untuk menyimpan metrik:
- SQLite3: Sederhana, berbasis file, dan bagus untuk pengembangan lokal atau aplikasi kecil. Opsi default di sebagian besar pengaturan.
- MySQL/MariaDB: Ideal untuk proyek besar atau lingkungan produksi di mana Anda membutuhkan penyimpanan yang kuat dan skalabel.
Anda dapat memilih jenis database Anda selama langkah konfigurasi (lihat di bawah). Pastikan lingkungan PHP Anda memiliki ekstensi yang diperlukan terinstal (misalnya, pdo_sqlite
atau pdo_mysql
).
Memulai
Berikut langkah demi langkah untuk kehebatan APM:
1. Daftarkan APM
Masukkan ini ke dalam file index.php
atau services.php
Anda untuk mulai melacak:
use flight\apm\logger\LoggerFactory;
use flight\Apm;
$ApmLogger = LoggerFactory::create(__DIR__ . '/../../.runway-config.json');
$Apm = new Apm($ApmLogger);
$Apm->bindEventsToFlightInstance($app);
// Jika Anda menambahkan koneksi database
// Harus berupa PdoWrapper atau PdoQueryCapture dari Tracy Extensions
$pdo = new PdoWrapper('mysql:host=localhost;dbname=example', 'user', 'pass', null, true); // <-- True diperlukan untuk mengaktifkan pelacakan di APM.
$Apm->addPdoConnection($pdo);
Apa yang terjadi di sini?
LoggerFactory::create()
mengambil konfigurasi Anda (lebih lanjut nanti) dan menyiapkan logger—SQLite secara default.Apm
adalah bintangnya—ia mendengarkan peristiwa Flight (permintaan, rute, kesalahan, dll.) dan mengumpulkan metrik.bindEventsToFlightInstance($app)
menghubungkannya semua ke aplikasi Flight Anda.
Tips Pro: Sampling Jika aplikasi Anda sibuk, mencatat setiap permintaan mungkin membebani sistem. Gunakan tingkat sampel (0.0 hingga 1.0):
$Apm = new Apm($ApmLogger, 0.1); // Mencatat 10% permintaan
Ini menjaga performa tetap cepat sambil tetap memberikan data yang solid.
2. Konfigurasikan Itu
Jalankan ini untuk membuat .runway-config.json
Anda:
php vendor/bin/runway apm:init
Apa yang dilakukan ini?
- Meluncurkan wizard yang menanyakan dari mana metrik mentah berasal (sumber) dan ke mana data yang diproses pergi (tujuan).
- Default adalah SQLite—misalnya,
sqlite:/tmp/apm_metrics.sqlite
untuk sumber, yang lain untuk tujuan. - Anda akan mendapatkan konfigurasi seperti:
{ "apm": { "source_type": "sqlite", "source_db_dsn": "sqlite:/tmp/apm_metrics.sqlite", "storage_type": "sqlite", "dest_db_dsn": "sqlite:/tmp/apm_metrics_processed.sqlite" } }
Proses ini juga akan menanyakan apakah Anda ingin menjalankan migrasi untuk pengaturan ini. Jika Anda menyiapkannya untuk pertama kali, jawabannya ya.
Mengapa dua lokasi? Metrik mentah menumpuk dengan cepat (bayangkan log yang tidak difilter). Worker memprosesnya menjadi tujuan terstruktur untuk dashboard. Menjaga semuanya rapi!
3. Proses Metrik dengan Worker
Worker mengubah metrik mentah menjadi data siap dashboard. Jalankan sekali:
php vendor/bin/runway apm:worker
Apa yang dilakukannya?
- Membaca dari sumber Anda (misalnya,
apm_metrics.sqlite
). - Memproses hingga 100 metrik (ukuran batch default) ke tujuan Anda.
- Berhenti ketika selesai atau jika tidak ada metrik yang tersisa.
Jaga Agar Tetap Berjalan Untuk aplikasi langsung, Anda ingin pemrosesan berkelanjutan. Berikut opsi Anda:
-
Mode Daemon:
php vendor/bin/runway apm:worker --daemon
Berjalan selamanya, memproses metrik saat datang. Bagus untuk dev atau pengaturan kecil.
-
Crontab: Tambahkan ini ke crontab Anda (
crontab -e
):* * * * * php /path/to/project/vendor/bin/runway apm:worker
Berjalan setiap menit—sempurna untuk produksi.
-
Tmux/Screen: Mulai sesi yang dapat dilepas:
tmux new -s apm-worker php vendor/bin/runway apm:worker --daemon # Ctrl+B, kemudian D untuk melepaskan; `tmux attach -t apm-worker` untuk terhubung kembali
Menjaganya tetap berjalan bahkan jika Anda logout.
-
Penyesuaian Kustom:
php vendor/bin/runway apm:worker --batch_size 50 --max_messages 1000 --timeout 300
--batch_size 50
: Proses 50 metrik sekaligus.--max_messages 1000
: Berhenti setelah 1000 metrik.--timeout 300
: Keluar setelah 5 menit.
Mengapa repot? Tanpa worker, dashboard Anda kosong. Ini adalah jembatan antara log mentah dan wawasan yang dapat ditindaklanjuti.
4. Luncurkan Dashboard
Lihat vital aplikasi Anda:
php vendor/bin/runway apm:dashboard
Apa ini?
- Menghidupkan server PHP di
http://localhost:8001/apm/dashboard
. - Menampilkan log permintaan, rute lambat, tingkat kesalahan, dan lainnya.
Kustomisasi Itu:
php vendor/bin/runway apm:dashboard --host 0.0.0.0 --port 8080 --php-path=/usr/local/bin/php
--host 0.0.0.0
: Dapat diakses dari IP mana pun (berguna untuk penampilan jarak jauh).--port 8080
: Gunakan port berbeda jika 8001 sudah digunakan.--php-path
: Arahkan ke PHP jika tidak ada di PATH Anda.
Buka URL di browser Anda dan jelajahi!
Mode Produksi
Untuk produksi, Anda mungkin harus mencoba beberapa teknik untuk menjalankan dashboard karena mungkin ada firewall dan langkah keamanan lainnya. Berikut beberapa opsi:
- Gunakan Reverse Proxy: Siapkan Nginx atau Apache untuk meneruskan permintaan ke dashboard.
- SSH Tunnel: Jika Anda bisa SSH ke server, gunakan
ssh -L 8080:localhost:8001 youruser@yourserver
untuk menyalurkan dashboard ke mesin lokal Anda. - VPN: Jika server Anda di belakang VPN, hubungkan ke sana dan akses dashboard secara langsung.
- Konfigurasikan Firewall: Buka port 8001 untuk IP Anda atau jaringan server. (atau port apa pun yang Anda atur).
- Konfigurasikan Apache/Nginx: Jika Anda memiliki server web di depan aplikasi Anda, Anda dapat mengonfigurasinya ke domain atau subdomain. Jika Anda melakukan ini, Anda akan mengatur root dokumen ke
/path/to/your/project/vendor/flightphp/apm/dashboard
Ingin dashboard berbeda?
Anda bisa membangun dashboard sendiri jika mau! Lihat direktori vendor/flightphp/apm/src/apm/presenter untuk ide tentang cara menyajikan data untuk dashboard Anda sendiri!
Fitur Dashboard
Dashboard adalah markas APM Anda—berikut yang akan Anda lihat:
- Log Permintaan: Setiap permintaan dengan cap waktu, URL, kode respons, dan waktu total. Klik “Detail” untuk middleware, kueri, dan kesalahan.
- Permintaan Terlambat: 5 permintaan teratas yang memakan waktu (misalnya, “/api/heavy” di 2.5s).
- Rute Terlambat: 5 rute teratas berdasarkan waktu rata-rata—bagus untuk melihat pola.
- Tingkat Kesalahan: Persentase permintaan yang gagal (misalnya, 2.3% 500s).
- Persentil Latensi: 95th (p95) dan 99th (p99) waktu respons—tahu skenario kasus terburuk Anda.
- Grafik Kode Respons: Visualisasikan 200s, 404s, 500s seiring waktu.
- Kueri/Middleware Panjang: 5 panggilan database lambat teratas dan lapisan middleware.
- Cache Hit/Miss: Seberapa sering cache Anda menyelamatkan hari.
Tambahan:
- Filter berdasarkan “Jam Terakhir,” “Hari Terakhir,” atau “Minggu Terakhir.”
- Toggle mode gelap untuk sesi malam larut.
Contoh:
Permintaan ke /users
mungkin menunjukkan:
- Waktu Total: 150ms
- Middleware:
AuthMiddleware->handle
(50ms) - Kueri:
SELECT * FROM users
(80ms) - Cache: Hit pada
user_list
(5ms)
Menambahkan Peristiwa Kustom
Lacak apa saja—seperti panggilan API atau proses pembayaran:
use flight\apm\CustomEvent;
$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('api_call', [
'endpoint' => 'https://api.example.com/users',
'response_time' => 0.25,
'status' => 200
]));
Di mana munculnya? Di detail permintaan dashboard di bawah “Peristiwa Kustom”—dapat diperluas dengan format JSON yang bagus.
Kasus Penggunaan:
$start = microtime(true);
$apiResponse = file_get_contents('https://api.example.com/data');
$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('external_api', [
'url' => 'https://api.example.com/data',
'time' => microtime(true) - $start,
'success' => $apiResponse !== false
]));
Sekarang Anda akan melihat jika API itu menyeret aplikasi Anda!
Pemantauan Database
Lacak kueri PDO seperti ini:
use flight\database\PdoWrapper;
$pdo = new PdoWrapper('sqlite:/path/to/db.sqlite', null, null, null, true); // <-- True diperlukan untuk mengaktifkan pelacakan di APM.
$Apm->addPdoConnection($pdo);
Apa yang Anda Dapat:
- Teks kueri (misalnya,
SELECT * FROM users WHERE id = ?
) - Waktu eksekusi (misalnya, 0.015s)
- Jumlah baris (misalnya, 42)
Peringatan:
- Opsional: Lewati ini jika Anda tidak membutuhkan pelacakan DB.
- Hanya PdoWrapper: PDO inti belum terhubung—pantau terus!
- Peringatan Performa: Mencatat setiap kueri di situs berat DB bisa memperlambat. Gunakan sampling (
$Apm = new Apm($ApmLogger, 0.1)
) untuk meringankan beban.
Contoh Output:
- Kueri:
SELECT name FROM products WHERE price > 100
- Waktu: 0.023s
- Baris: 15
Opsi Worker
Sesuaikan worker sesuai keinginan Anda:
--timeout 300
: Berhenti setelah 5 menit—bagus untuk pengujian.--max_messages 500
: Dibatasi pada 500 metrik—menjaganya terbatas.--batch_size 200
: Memproses 200 sekaligus—menyeimbangkan kecepatan dan memori.--daemon
: Berjalan tanpa henti—ideal untuk pemantauan langsung.
Contoh:
php vendor/bin/runway apm:worker --daemon --batch_size 100 --timeout 3600
Berjalan selama satu jam, memproses 100 metrik sekaligus.
ID Permintaan di Aplikasi
Setiap permintaan memiliki ID permintaan unik untuk pelacakan. Anda dapat menggunakan ID ini di aplikasi Anda untuk mengkorelasikan log dan metrik. Misalnya, Anda dapat menambahkan ID permintaan ke halaman kesalahan:
Flight::map('error', function($message) {
// Dapatkan ID permintaan dari header respons X-Flight-Request-Id
$requestId = Flight::response()->getHeader('X-Flight-Request-Id');
// Selain itu, Anda bisa mengambilnya dari variabel Flight
// Metode ini tidak akan bekerja dengan baik di swoole atau platform async lainnya.
// $requestId = Flight::get('apm.request_id');
echo "Error: $message (Request ID: $requestId)";
});
Upgrade
Jika Anda sedang meng-upgrade ke versi APM yang lebih baru, ada kemungkinan ada migrasi database yang perlu dijalankan. Anda bisa melakukannya dengan menjalankan perintah berikut:
php vendor/bin/runway apm:migrate
Ini akan menjalankan migrasi apa pun yang diperlukan untuk memperbarui skema database ke versi terbaru.
Catatan: Jika database APM Anda besar ukurannya, migrasi ini mungkin memakan waktu. Anda mungkin ingin menjalankan perintah ini selama jam non-puncak.
Membersihkan Data Lama
Untuk menjaga database Anda rapi, Anda bisa membersihkan data lama. Ini sangat berguna jika Anda menjalankan aplikasi sibuk dan ingin menjaga ukuran database tetap terkendali. Anda bisa melakukannya dengan menjalankan perintah berikut:
php vendor/bin/runway apm:purge
Ini akan menghapus semua data lebih tua dari 30 hari dari database. Anda bisa menyesuaikan jumlah hari dengan memberikan nilai berbeda ke opsi --days
:
php vendor/bin/runway apm:purge --days 7
Ini akan menghapus semua data lebih tua dari 7 hari dari database.
Pemecahan Masalah
Tersangkut? Coba ini:
-
Tidak Ada Data Dashboard?
- Apakah worker berjalan? Periksa
ps aux | grep apm:worker
. - Path konfigurasi cocok? Verifikasi DSN di
.runway-config.json
menunjuk ke file nyata. - Jalankan
php vendor/bin/runway apm:worker
secara manual untuk memproses metrik tertunda.
- Apakah worker berjalan? Periksa
-
Kesalahan Worker?
- Lihat file SQLite Anda (misalnya,
sqlite3 /tmp/apm_metrics.sqlite "SELECT * FROM apm_metrics_log LIMIT 5"
). - Periksa log PHP untuk jejak tumpukan.
- Lihat file SQLite Anda (misalnya,
-
Dashboard Tidak Mau Mulai?
- Port 8001 sedang digunakan? Gunakan
--port 8080
. - PHP tidak ditemukan? Gunakan
--php-path /usr/bin/php
. - Firewall memblokir? Buka port atau gunakan
--host localhost
.
- Port 8001 sedang digunakan? Gunakan
-
Terlalu Lambat?
- Turunkan tingkat sampel:
$Apm = new Apm($ApmLogger, 0.05)
(5%). - Kurangi ukuran batch:
--batch_size 20
.
- Turunkan tingkat sampel:
-
Tidak Melacak Pengecualian/Kesalahan?
- Jika Anda memiliki Tracy diaktifkan untuk proyek Anda, itu akan menggantikan penanganan kesalahan Flight. Anda perlu menonaktifkan Tracy dan kemudian pastikan bahwa
Flight::set('flight.handle_errors', true);
diatur.
- Jika Anda memiliki Tracy diaktifkan untuk proyek Anda, itu akan menggantikan penanganan kesalahan Flight. Anda perlu menonaktifkan Tracy dan kemudian pastikan bahwa
-
Tidak Melacak Kueri Database?
- Pastikan Anda menggunakan
PdoWrapper
untuk koneksi database Anda. - Pastikan Anda membuat argumen terakhir di konstruktor
true
.
- Pastikan Anda menggunakan
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.
bdump($var)
- Ini akan mencetak variabel ke Tracy Bar di panel terpisah.dumpe($var)
- Ini akan mencetak variabel dan kemudian mati segera.
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 lengkap yang sangat mudah digunakan dan terasa lebih dekat dengan sintaks PHP daripada 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 tentangnya di Dokumentasi Latte.
require 'vendor/autoload.php';
$app = Flight::app();
$app->map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// Tempat di mana latte secara khusus menyimpan cache-nya
$latte->setTempDirectory(__DIR__ . '/../cache/');
$finalPath = Flight::get('flight.views.path') . $template;
$latte->render($finalPath, $data, $block);
});
Contoh Layout Sederhana
Berikut 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 . ' - '}My App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav>
<!-- elemen nav Anda di sini -->
</nav>
</header>
<div id="content">
<!-- Ini adalah keajaiban di sini -->
{block content}{/block}
</div>
<div id="footer">
© Copyright
</div>
</body>
</html>
Dan sekarang kita punya 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 Beranda</h1>
<p>Selamat datang di aplikasi saya!</p>
{/block}
Kemudian ketika Anda pergi untuk merender ini di dalam fungsi atau controller Anda, Anda akan melakukan sesuatu seperti ini:
// rute sederhana
Flight::route('/', function () {
Flight::render('home.latte', [
'title' => 'Halaman Beranda'
]);
});
// atau jika Anda menggunakan controller
Flight::route('/', [HomeController::class, 'index']);
// HomeController.php
class HomeController
{
public function index()
{
Flight::render('home.latte', [
'title' => 'Halaman Beranda'
]);
}
}
Lihat Dokumentasi Latte untuk informasi lebih lanjut tentang cara menggunakan Latte secara maksimal!
Debugging dengan Tracy
PHP 8.1+ diperlukan untuk bagian ini.
Anda juga dapat menggunakan Tracy untuk membantu debugging file template Latte Anda langsung dari kotak! Jika Anda sudah menginstal Tracy, Anda perlu menambahkan ekstensi Latte ke Tracy.
// services.php
use Tracy\Debugger;
$app->map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// Tempat di mana latte secara khusus menyimpan cache-nya
$latte->setTempDirectory(__DIR__ . '/../cache/');
$finalPath = Flight::get('flight.views.path') . $template;
// Ini hanya akan menambahkan ekstensi jika Bilah Debug Tracy diaktifkan
if (Debugger::$showBar === true) {
// ini adalah tempat Anda menambahkan Panel Latte ke Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
}
$latte->render($finalPath, $data, $block);
});
Awesome-plugins/awesome_plugins
Plugin Hebat
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 yang 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.
- FlightPHP OpenAPI Generator - Posting blog yang ditulis oleh Daniel Schreiber tentang cara menggunakan Spesifikasi OpenAPI dengan FlightPHP untuk membangun API Anda menggunakan pendekatan API pertama.
- SwaggerUI - Swagger UI adalah alat hebat untuk membantu Anda menghasilkan dokumentasi API untuk proyek Flight Anda. Ini sangat mudah digunakan dan dapat disesuaikan sesuai kebutuhan Anda. Ini adalah pustaka PHP untuk membantu Anda menghasilkan dokumentasi Swagger.
Pemantauan Kinerja Aplikasi (APM)
Pemantauan Kinerja Aplikasi (APM) sangat penting untuk aplikasi apa pun. Ini membantu Anda memahami bagaimana aplikasi Anda berkinerja dan di mana titik penyumbatannya. Ada sejumlah alat APM yang dapat digunakan dengan Flight.
- resmi flightphp/apm - Flight APM adalah pustaka APM sederhana yang dapat digunakan untuk memantau aplikasi Flight Anda. Ini dapat digunakan untuk memantau kinerja aplikasi Anda dan membantu Anda mengidentifikasi titik penyumbatan.
Async
Flight sudah merupakan framework yang cepat, tetapi menambahkan mesin turbo padanya membuat semuanya lebih menyenangkan (dan menantang)!
- flightphp/async - Pustaka Async Flight resmi. Pustaka ini adalah cara sederhana untuk menambahkan pemrosesan asinkron ke aplikasi Anda. Ini menggunakan Swoole/Openswoole di balik layar untuk menyediakan cara sederhana dan efektif untuk menjalankan tugas secara asinkron.
Otorisasi/Izin
Otorisasi dan Izin sangat penting untuk aplikasi apa pun yang memerlukan kontrol untuk siapa yang dapat mengakses apa.
- resmi flightphp/permissions - Pustaka Permissions Flight resmi. Pustaka ini adalah cara sederhana untuk menambahkan izin tingkat pengguna dan aplikasi ke aplikasi Anda.
Penyimpanan Cache
Penyimpanan cache adalah cara hebat untuk mempercepat aplikasi Anda. Ada sejumlah pustaka caching yang dapat digunakan dengan Flight.
- resmi flightphp/cache - Kelas caching in-file PHP yang ringan, sederhana, dan standalone
CLI
Aplikasi CLI adalah cara hebat untuk berinteraksi dengan aplikasi Anda. Anda dapat menggunakannya untuk menghasilkan controller, menampilkan semua rute, dan banyak lagi.
- resmi flightphp/runway - Runway adalah aplikasi CLI yang membantu Anda mengelola aplikasi Flight Anda.
Cookies
Cookies adalah cara hebat untuk menyimpan bit data kecil di sisi klien. Mereka dapat digunakan untuk menyimpan preferensi pengguna, pengaturan aplikasi, dan banyak lagi.
- overclokk/cookie - PHP Cookie adalah pustaka PHP yang menyediakan cara sederhana dan efektif untuk mengelola cookies.
Debugging
Debugging sangat penting ketika Anda mengembangkan di lingkungan lokal Anda. Ada beberapa plugin yang dapat meningkatkan pengalaman debugging Anda.
- tracy/tracy - Ini adalah penanganan kesalahan lengkap yang dapat digunakan dengan Flight. Ini memiliki sejumlah panel yang dapat membantu Anda mendebug aplikasi Anda. Ini juga sangat mudah untuk diperluas dan menambahkan panel Anda sendiri.
- resmi flightphp/tracy-extensions - Digunakan dengan penanganan kesalahan Tracy, plugin ini menambahkan beberapa panel ekstra untuk membantu debugging khusus untuk proyek Flight.
Database
Database adalah inti dari sebagian besar aplikasi. Ini adalah cara Anda menyimpan dan mengambil data. Beberapa pustaka database hanyalah wrapper untuk menulis query dan beberapa adalah ORM lengkap.
- resmi flightphp/core PdoWrapper - Wrapper PDO Flight resmi yang merupakan bagian dari inti. Ini adalah wrapper sederhana untuk membantu menyederhanakan proses penulisan query dan mengeksekusinya. Ini bukan ORM.
- resmi flightphp/active-record - ORM/Mapper ActiveRecord Flight resmi. Pustaka kecil yang hebat untuk dengan mudah mengambil dan menyimpan data di database Anda.
- byjg/php-migration - Plugin untuk melacak semua perubahan database untuk proyek Anda.
Enkripsi
Enkripsi sangat penting untuk aplikasi apa pun yang menyimpan data sensitif. Mengenkripsi dan mendekripsi data tidak terlalu sulit, tetapi menyimpan kunci enkripsi dengan benar bisa menjadi sulit. Hal yang paling penting adalah jangan pernah menyimpan kunci enkripsi Anda di direktori publik atau mengommitnya ke repositori kode Anda.
- defuse/php-encryption - Ini adalah pustaka yang dapat digunakan untuk mengenkripsi dan mendekripsi data. Memulai dan menjalankannya cukup sederhana untuk mulai mengenkripsi dan mendekripsi data.
Antrian Pekerjaan
Antrian pekerjaan sangat membantu untuk memproses tugas secara asinkron. Ini bisa mengirim email, memproses gambar, atau apa pun yang tidak perlu dilakukan secara real-time.
- n0nag0n/simple-job-queue - Simple Job Queue adalah pustaka yang dapat digunakan untuk memproses pekerjaan secara asinkron. Ini dapat digunakan dengan beanstalkd, MySQL/MariaDB, SQLite, dan PostgreSQL.
Sesi
Sesi tidak terlalu berguna untuk API tetapi untuk membangun aplikasi web, sesi bisa sangat penting untuk mempertahankan status dan informasi login.
- resmi flightphp/session - Pustaka Sesi Flight resmi. Ini adalah pustaka sesi sederhana yang dapat digunakan untuk menyimpan dan mengambil data sesi. Ini menggunakan penanganan sesi bawaan PHP.
- Ghostff/Session - Manajer Sesi PHP (non-blocking, flash, segment, enkripsi sesi). Menggunakan PHP open_ssl untuk enkripsi/dekripsi data sesi opsional.
Templating
Templating adalah inti dari aplikasi web apa pun dengan UI. Ada sejumlah mesin templating yang dapat digunakan dengan Flight.
- deprecated flightphp/core View - Ini adalah mesin templating dasar yang merupakan bagian dari inti. Tidak disarankan untuk digunakan jika Anda memiliki lebih dari beberapa halaman di proyek Anda.
- latte/latte - Latte adalah mesin templating lengkap yang sangat mudah digunakan dan terasa lebih dekat dengan sintaks PHP daripada Twig atau Smarty. Ini juga sangat mudah untuk diperluas dan menambahkan filter dan fungsi Anda sendiri.
Integrasi WordPress
Ingin menggunakan Flight di proyek WordPress Anda? Ada plugin yang berguna untuk itu!
- n0nag0n/wordpress-integration-for-flight-framework - Plugin WordPress ini memungkinkan Anda menjalankan Flight tepat di samping WordPress. Ini sempurna untuk menambahkan API khusus, microservices, atau bahkan aplikasi lengkap ke situs WordPress Anda menggunakan framework Flight. Sangat berguna jika Anda ingin yang terbaik dari kedua dunia!
Kontribusi
Punya plugin yang ingin Anda bagikan? Kirimkan pull request untuk menambahkannya ke daftar!
Media
Media
Kami telah berusaha melacak apa yang kami bisa tentang berbagai jenis media di internet seputar Flight. Lihat di bawah untuk sumber daya berbeda yang dapat Anda gunakan untuk mempelajari lebih lanjut tentang Flight.
Articles and Write-ups
- Unit Testing and SOLID Principles by Brian Fenton (2015?)
- PHP Web Framework Flight by ojambo (2025)
- Define, Generate, and Implement: An API-First Approach with OpenAPI Generator and FlightPHP by Daniel Schreiber (2025)
- Best PHP Micro Frameworks for 2024 by n0nag0n (2024)
- Creating a RESTful API with Flight Framework by n0nag0n (2024)
- Building a Simple Blog with Flight Part 2 by n0nag0n (2024)
- Building a Simple Blog with Flight Part 1 by n0nag0n (2024)
- 🚀 Build a Simple CRUD API in PHP with the Flight Framework by soheil-khaledabadi (2024)
- Building a PHP Web Application with the Flight Micro-framework by Arthur C. Codex (2023)
- Best PHP Frameworks for Web Development in 2024 by Ravikiran A S (2023)
- Top 12 PHP Frameworks: A Comprehensive Guide for 2023 by marketing kbk (2023)
- 5 PHP Frameworks You've (Probably) Never Heard of by n0nag0n (2022)
- 12 top PHP frameworks for web developers to consider in 2023 by Anna Monus (2022)
- The Best PHP Microframeworks on a Cloud Server by Shahzeb Ahmed (2021)
- PHP framework: Top 15 powerful ones for your web development by AHT Tech (2020)
- Easy PHP Routing with FlightPHP by Lucas Conceição (2019)
- Trying Out New PHP Framework (Flight) by Leon (2017)
- Setting up FlightPHP to work with Backbonejs by Timothy Tocci (2015)
Videos and Tutorials
- Build a Flight PHP App with MVC & MariaDB in 10 Minutes! (Beginner Friendly) by ojamboshop (2025)
- Create a REST API for IoT Devices Using PHP & FlightPHP - ESP32 API by IoT Craft Hub (2024)
- PHP Flight Framework Simple Introductory Video by n0nag0n (2024)
- Set header HTTP code in Flightphp (3 Solutions!!) by Roel Van de Paar (2024)
- PHP Flight Framework Tutorial. Super easy API Project! by n0nag0n (2022)
- Aplicación web CRUD con php y mysql y bootstrap usando flight by Devlopteca - Oscar Uh (2021)
- DevOps & SysAdmins: Lighttpd rewrite rule for Flight PHP microframework by Roel Van de Paar (2021)
- Tutorial REST API Flight PHP #PART2 INSERT TABLE Info #Code (Tagalog) by Info Singkat Official (2020)
- Tutorial REST API Flight PHP #PART1 Info #Code (Tagalog) by Info Singkat Official (2020)
- How To Create JSON REST API IN PHP - Part 2 by Codewife (2018)
- How To Create JSON REST API IN PHP - Part 1 by Codewife (2018)
- Teste Micro Frameworks PHP - Flight PHP, Lumen, Slim 3 e Laravel by Codemarket (2016)
- Tutorial 1 Flight PHP - Instalación by absagg (2014)
- Tutorial 2 Flight PHP - Route parte 1 by absagg (2014)
Missing Anything?
Apakah kami melewatkan sesuatu yang Anda tulis atau rekam? Beri tahu kami dengan issue atau pull request!
Examples
Butuh mulai cepat?
Anda memiliki dua opsi untuk memulai proyek Flight baru:
- Full Skeleton Boilerplate: Contoh yang lebih lengkap dengan controller dan view.
- Single File Skeleton Boilerplate: Satu file tunggal yang mencakup semua yang Anda butuhkan untuk menjalankan aplikasi Anda dalam satu file sederhana.
Contoh yang disumbangkan oleh komunitas:
- flightravel: FlightPHP dengan direktori Laravel, dengan alat PHP + GH Actions
- fleact - Kit starter FlightPHP dengan integrasi ReactJS.
- flastro - Kit starter FlightPHP dengan integrasi Astro.
- velt - Velt adalah template starter Svelte yang cepat dan mudah dengan backend FlightPHP.
Butuh 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!
- Ivox Car Rental - Ivox Car Rental adalah aplikasi web penyewaan mobil satu halaman yang ramah seluler, dibangun dengan PHP (FlightPHP), JavaScript, dan MySQL. Ini mendukung pendaftaran pengguna, penelusuran, dan pemesanan mobil, sementara admin dapat mengelola mobil, pengguna, dan pemesanan. Aplikasi ini memiliki REST API, autentikasi JWT, dan desain responsif untuk pengalaman penyewaan modern.
- Decay - Flight v3 dengan HTMX dan SleekDB semuanya tentang zombie! (Demo)
- Flight Example Blog - Flight v3 dengan Middleware, Controllers, Active Record, dan Latte.
- Flight CRUD RESTful API - Proyek API CRUD sederhana menggunakan framework Flight, yang menyediakan struktur dasar bagi pengguna baru untuk dengan cepat menyiapkan aplikasi PHP dengan operasi CRUD dan konektivitas database. Proyek ini mendemonstrasikan cara menggunakan Flight untuk pengembangan RESTful API, menjadikannya alat pembelajaran ideal bagi pemula dan kit starter yang berguna bagi pengembang yang lebih berpengalaman.
- Flight School Management System - Flight v3
- Paste Bin with Comments - Flight v3
- Basic Skeleton App
- Example Wiki
- The IT-Innovator PHP Framework Application
- LittleEducationalCMS (Spanish)
- Italian Yellow Pages API
- Generic Content Management System (with....very little documentation)
- A tiny php framework based on Flight and medoo.
- Example MVC Application
- Production ready Flight Boilerplate - Framework autentikasi siap produksi yang menghemat minggu pengembangan Anda. Fitur keamanan tingkat enterprise: 2FA/TOTP, integrasi LDAP, Azure SSO, pembatasan tingkat cerdas, fingerprinting sesi, perlindungan brute-force, dasbor analitik keamanan, logging audit komprehensif, dan kontrol akses berbasis peran granular.
Ingin Berbagi Contoh Anda Sendiri?
Jika Anda memiliki proyek yang ingin dibagikan, silakan kirimkan pull request untuk menambahkannya ke daftar ini!
Install/install
Petunjuk Instalasi
Ada beberapa prasyarat dasar sebelum Anda dapat menginstal Flight. Yaitu, Anda perlu:
- Instal PHP di sistem Anda
- Instal Composer untuk pengalaman pengembang terbaik.
Instalasi Dasar
Jika Anda menggunakan Composer, Anda dapat menjalankan perintah berikut:
composer require flightphp/core
Ini hanya akan meletakkan file inti Flight di sistem Anda. Anda perlu mendefinisikan struktur proyek, layout, dependencies, configs, autoloading, dll. Metode ini memastikan bahwa tidak ada dependensi lain selain Flight yang diinstal.
Anda juga dapat mengunduh file secara langsung dan mengekstraknya ke direktori web Anda.
Instalasi yang Direkomendasikan
Sangat disarankan untuk memulai dengan aplikasi flightphp/skeleton untuk proyek baru apa pun. Instalasi sangat mudah.
composer create-project flightphp/skeleton my-project/
Ini akan menyiapkan struktur proyek Anda, mengonfigurasi autoloading dengan namespace, menyiapkan konfigurasi, dan menyediakan alat lain seperti Tracy, Tracy Extensions, dan Runway
Konfigurasi Server Web Anda
Server Pengembangan PHP Bawaan
Ini adalah cara termudah untuk memulai dan menjalankan. Anda dapat menggunakan server bawaan untuk menjalankan aplikasi Anda dan bahkan menggunakan SQLite untuk database (selama sqlite3 diinstal di sistem Anda) dan tidak memerlukan banyak hal apa pun! Cukup jalankan perintah berikut setelah PHP diinstal:
php -S localhost:8000
# atau dengan aplikasi skeleton
composer start
Kemudian buka browser Anda dan pergi ke http://localhost:8000
.
Jika Anda ingin menjadikan document root proyek Anda direktori yang berbeda (Contoh: proyek Anda adalah ~/myproject
, tetapi document root Anda adalah ~/myproject/public/
), Anda dapat menjalankan perintah berikut setelah berada di direktori ~/myproject
:
php -S localhost:8000 -t public/
# dengan aplikasi skeleton, ini sudah dikonfigurasi
composer start
Kemudian buka browser Anda dan pergi ke http://localhost:8000
.
Apache
Pastikan Apache sudah diinstal di sistem Anda. Jika tidak, cari di Google 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 setelahRewriteEngine On
.Catatan: Jika Anda ingin melindungi semua file server, seperti file db atau env. Letakkan ini di file
.htaccess
Anda:
RewriteEngine On
RewriteRule ^(.*)$ index.php
Nginx
Pastikan Nginx sudah diinstal di sistem Anda. Jika tidak, cari di Google 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
Jika Anda melakukan instalasi dasar, Anda akan membutuhkan beberapa kode untuk memulai.
<?php
// Jika Anda menggunakan Composer, require the autoloader.
require 'vendor/autoload.php';
// jika Anda tidak menggunakan Composer, load the framework directly
// require 'flight/Flight.php';
// Kemudian definisikan rute dan tetapkan fungsi untuk menangani permintaan.
Flight::route('/', function () {
echo 'hello world!';
});
// Akhirnya, mulai framework.
Flight::start();
Dengan aplikasi skeleton, ini sudah dikonfigurasi dan ditangani di file app/config/routes.php
Anda. Layanan dikonfigurasi di app/config/services.php
Menginstal PHP
Jika Anda sudah memiliki php
yang diinstal di sistem Anda, lanjutkan dan lewati petunjuk ini dan pindah ke bagian unduhan
macOS
Menginstal PHP menggunakan Homebrew
-
Instal Homebrew (jika belum diinstal):
- Buka Terminal dan jalankan:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- Buka Terminal dan jalankan:
-
Instal PHP:
- Instal versi terbaru:
brew install php
- Untuk menginstal versi spesifik, misalnya, PHP 8.1:
brew tap shivammathur/php brew install shivammathur/php/php@8.1
- Instal versi terbaru:
-
Beralih antar versi PHP:
- Unlink versi saat ini dan link versi yang diinginkan:
brew unlink php brew link --overwrite --force php@8.1
- Verifikasi versi yang diinstal:
php -v
- Unlink versi saat ini dan link versi yang diinginkan:
Windows 10/11
Menginstal PHP secara manual
-
Unduh PHP:
- Kunjungi PHP for Windows dan unduh versi terbaru atau versi spesifik (misalnya, 7.4, 8.0) sebagai file zip non-thread-safe.
-
Ekstrak PHP:
- Ekstrak file zip yang diunduh ke
C:\php
.
- Ekstrak file zip yang diunduh ke
-
Tambahkan PHP ke PATH sistem:
- Pergi ke System Properties > Environment Variables.
- Di bawah System variables, temukan Path dan klik Edit.
- Tambahkan path
C:\php
(atau di mana pun Anda mengekstrak PHP). - Klik OK untuk menutup semua jendela.
-
Konfigurasi PHP:
- Salin
php.ini-development
kephp.ini
. - Edit
php.ini
untuk mengonfigurasi PHP sesuai kebutuhan (misalnya, mengaturextension_dir
, mengaktifkan ekstensi).
- Salin
-
Verifikasi instalasi PHP:
- Buka Command Prompt dan jalankan:
php -v
- Buka Command Prompt dan jalankan:
Menginstal Beberapa Versi PHP
-
Ulangi langkah di atas untuk setiap versi, letakkan masing-masing di direktori terpisah (misalnya,
C:\php7
,C:\php8
). -
Beralih antar versi dengan menyesuaikan variabel PATH sistem untuk menunjuk ke direktori versi yang diinginkan.
Ubuntu (20.04, 22.04, dll.)
Menginstal PHP menggunakan apt
-
Perbarui daftar paket:
- Buka Terminal dan jalankan:
sudo apt update
- Buka Terminal dan jalankan:
-
Instal PHP:
- Instal versi PHP terbaru:
sudo apt install php
- Untuk menginstal versi spesifik, misalnya, PHP 8.1:
sudo apt install php8.1
- Instal versi PHP terbaru:
-
Instal modul tambahan (opsional):
- Misalnya, untuk menginstal dukungan MySQL:
sudo apt install php8.1-mysql
- Misalnya, untuk menginstal dukungan MySQL:
-
Beralih antar versi PHP:
- Gunakan
update-alternatives
:sudo update-alternatives --set php /usr/bin/php8.1
- Gunakan
-
Verifikasi versi yang diinstal:
- Jalankan:
php -v
- Jalankan:
Rocky Linux
Menginstal PHP menggunakan yum/dnf
-
Aktifkan repositori EPEL:
- Buka Terminal dan jalankan:
sudo dnf install epel-release
- Buka Terminal dan jalankan:
-
Instal repositori Remi's:
- Jalankan:
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm sudo dnf module reset php
- Jalankan:
-
Instal PHP:
- Untuk menginstal versi default:
sudo dnf install php
- Untuk menginstal versi spesifik, misalnya, PHP 7.4:
sudo dnf module install php:remi-7.4
- Untuk menginstal versi default:
-
Beralih antar versi PHP:
- Gunakan perintah modul
dnf
:sudo dnf module reset php sudo dnf module enable php:remi-8.0 sudo dnf install php
- Gunakan perintah modul
-
Verifikasi versi yang diinstal:
- Jalankan:
php -v
- Jalankan:
Catatan Umum
- Untuk lingkungan pengembangan, penting untuk mengonfigurasi pengaturan PHP sesuai dengan persyaratan proyek Anda.
- Saat beralih versi PHP, pastikan semua ekstensi PHP yang relevan diinstal untuk versi spesifik yang ingin Anda gunakan.
- Restart server web Anda (Apache, Nginx, dll.) setelah beralih versi PHP atau memperbarui konfigurasi untuk menerapkan perubahan.
Guides
Panduan
Flight PHP dirancang untuk sederhana namun powerful, 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 Sebuah Blog
Pelajari cara membuat aplikasi blog fungsional dengan Flight PHP. Panduan ini membawa Anda melalui:
- Menyiapkan struktur proyek
- Bekerja dengan templat menggunakan Latte
- Mengimplementasikan rute untuk postingan
- Menyimpan dan mengambil data
- Menangani pengiriman formulir
- Penanganan kesalahan dasar
Tutorial ini sempurna untuk pemula yang ingin melihat bagaimana semua bagian saling terhubung dalam aplikasi nyata.
Pengujian Unit dan Prinsip SOLID
Panduan ini mencakup dasar-dasar pengujian unit dalam aplikasi Flight PHP. Ini mencakup:
- Menyiapkan PHPUnit
- Menulis kode yang dapat diuji menggunakan prinsip SOLID
- Mocking dependensi
- Jebakan umum yang harus dihindari
- Menskala tes Anda seiring pertumbuhan aplikasi Tutorial ini ideal untuk pengembang yang ingin meningkatkan kualitas kode dan maintainability.
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 pembuatan 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 pembuatan blog dasar menggunakan framework Flight PHP. Sebenarnya ada 2 bagian: satu untuk mencakup dasar-dasar dan yang lain untuk mencakup topik lebih lanjut serta perbaikan untuk blog yang siap produksi.
- Membangun Blog Sederhana dengan Flight - Bagian 1 - Memulai dengan blog sederhana.
- Membangun Blog Sederhana dengan Flight - Bagian 2 - Memperbaiki blog untuk produksi.
Membangun API Pokémon di PHP: Panduan untuk Pemula
Panduan menyenangkan ini membawa Anda melalui pembuatan API Pokémon sederhana menggunakan Flight PHP. Ini mencakup dasar-dasar pengaturan API, mendefinisikan rute, dan mengembalikan respons JSON.
Berkontribusi
Punya ide untuk panduan? Menemukan kesalahan? Kami menyambut kontribusi! Panduan kami dikelola di repositori dokumentasi FlightPHP.
Jika Anda telah membangun sesuatu yang menarik dengan Flight dan ingin membagikannya sebagai panduan, silakan kirimkan pull request. Berbagi pengetahuan membantu komunitas Flight berkembang.
Mencari Dokumentasi API?
Jika Anda mencari informasi spesifik tentang fitur dan metode inti Flight, periksa bagian Learn dari dokumentasi kami.