Jika Anda bertanya kepada pengembang PHP peluang apa yang ingin mereka lihat dalam PHP, sebagian besar akan memanggil obat generik.
Dukungan umum di tingkat bahasa akan menjadi solusi terbaik. Tetapi, untuk mewujudkannya itu sulit . Kami berharap bahwa suatu hari dukungan penduduk asli akan menjadi bagian dari bahasa tersebut, tetapi mungkin perlu beberapa tahun untuk menunggu.
Artikel ini akan menunjukkan bagaimana, menggunakan alat yang ada, dalam beberapa kasus dengan modifikasi minimal, kita bisa mendapatkan kekuatan generik dalam PHP sekarang.
Dari seorang penerjemah: saya sengaja menggunakan kertas kalkir dari bahasa Inggris "generics", karena Saya belum pernah mendengar dalam komunikasi bahwa seseorang menyebutnya "pemrograman umum."
Konten:
Apa itu obat generik?
Bagian ini mencakup pengantar singkat tentang obat generik .
Tautan membaca:
- RFC untuk menambahkan generik PHP
- Dukungan umum di Phan
- Generik dan templat mazmur
Contoh paling sederhana
Karena saat ini tidak mungkin untuk mendefinisikan obat generik di tingkat bahasa, kami harus menggunakan peluang besar lainnya - untuk mendefinisikannya di blok dok.
Kami sudah menggunakan opsi ini di banyak proyek. Lihatlah contoh ini:
function createUsers(iterable $names): array { ... }
Dalam kode di atas, kami melakukan apa yang mungkin dilakukan di tingkat bahasa. Kami mendefinisikan parameter $names
sebagai sesuatu yang bisa didaftar. Kami juga menunjukkan bahwa fungsi tersebut akan mengembalikan array. PHP akan melempar TypeError
jika jenis parameter dan nilai kembali tidak cocok.
Docblock meningkatkan pemahaman kode. $names
harus berupa string, dan fungsinya harus mengembalikan array objek User
. PHP sendiri tidak melakukan pengecekan seperti itu. Tetapi IDE seperti PhpStorm memahami notasi ini dan memperingatkan pengembang bahwa kontrak tambahan belum dihormati. Selain itu, alat analisis statis seperti Mazmur, PHPStan dan Phan dapat memvalidasi kebenaran data yang ditransfer ke dan dari fungsi.
Generik untuk menentukan kunci dan nilai tipe yang disebutkan
Di atas adalah contoh paling sederhana dari obat generik. Metode yang lebih kompleks mencakup kemampuan untuk menentukan jenis kuncinya, bersama dengan jenis nilai. Di bawah ini adalah salah satu cara untuk menggambarkan ini:
function getUsers(): array { ... }
Dikatakan di sini bahwa array yang dikembalikan oleh getUsers
memiliki kunci string dan nilai tipe User
.
Analis statis seperti Mazmur, PHPStan dan Phan memahami penjelasan ini dan memperhitungkannya saat memeriksa.
Pertimbangkan kode berikut:
function getUsers(): array { ... } function showAge(int $age): void { ... } foreach(getUsers() as $name => $user) { showAge($name); }
showAge
statis akan memberikan peringatan pada panggilan showAge
dengan kesalahan, seperti ini: Argument 1 of showAge expects int, string provided
.
Sayangnya, pada saat penulisan, PhpStorm tidak tahu caranya.
Obat generik yang lebih canggih
Kami terus mempelajari topik obat generik. Pertimbangkan objek yang merupakan tumpukan :
class Stack { public function push($item): void { ... } public function pop() { ... } }
Tumpukan dapat menerima semua jenis objek. Tetapi bagaimana jika kita ingin membatasi tumpukan hanya pada objek dengan tipe User
?
Psalm dan Phan mendukung anotasi berikut:
class Stack { public function push($item): void; public function pop(); }
Docblock digunakan untuk menyampaikan informasi tipe tambahan, misalnya:
$stack = new Stack(); Means that $userStack must only contain Users.
Mazmur, ketika menganalisis kode berikut:
$userStack->push(new User()); $userStack->push("hello");
Akan mengeluh tentang baris 2 dengan kesalahan Argument 1 of Stack::push expects User, string(hello) provided.
PhpStorm saat ini tidak mendukung anotasi ini.
Faktanya, kita hanya membahas sebagian informasi tentang obat generik, tetapi saat ini sudah cukup.
Bagaimana menerapkan obat generik tanpa dukungan bahasa
Anda harus menyelesaikan langkah-langkah berikut:
- Di tingkat komunitas, tentukan standar umum di blok dok (mis. PSR baru, atau kembali ke PSR-5)
- Tambahkan anotasi dockblock ke kode Anda
- Gunakan IDE yang memahami konvensi ini untuk melakukan analisis statis real-time untuk menemukan inkonsistensi.
- Gunakan alat analisis statis (seperti Mazmur) sebagai salah satu langkah CI untuk menangkap kesalahan.
- Menentukan metode untuk meneruskan informasi tipe ke pustaka pihak ketiga.
Standarisasi
Saat ini, komunitas PHP telah secara tidak resmi mengadopsi format generik ini (mereka didukung oleh sebagian besar alat dan maknanya jelas bagi sebagian besar orang):
function getUsers(): array { ... }
Namun, kami memiliki masalah dengan contoh sederhana seperti ini:
function getUsers(): array { ... }
Mazmur memahaminya, dan tahu jenis apa yang dimiliki kunci dan nilai-nilai array yang dikembalikan.
Pada saat penulisan, PhpStorm tidak mengerti ini. Dengan menggunakan entri ini, saya kehilangan kekuatan analisis statis real-time yang ditawarkan oleh PhpStorm.
Pertimbangkan kode di bawah ini. PhpStorm tidak mengerti bahwa $user
adalah tipe User
dan $name
adalah tipe string:
foreach(getUsers() as $name => $user) { ... }
Jika saya memilih Mazmur sebagai alat analisis statis, saya dapat menulis yang berikut:
function getUsers(): array { ... }
Mazmur mengerti semua ini.
PhpStorm tahu bahwa variabel $user
adalah tipe User
. Tapi, dia masih tidak mengerti bahwa kunci array mengacu pada sebuah string. Phan dan PHPStan tidak mengerti penjelasan mazmur tertentu. Maksimum yang mereka pahami dalam kode ini sama dengan di PhpStorm: tipe $user
Anda bisa berpendapat bahwa PhpStorm seharusnya hanya menerima array<keyType, valueType>
perjanjian array<keyType, valueType>
. Saya tidak setuju dengan Anda, karena Saya percaya bahwa dikte standar ini adalah tugas bahasa dan komunitas, dan alat-alatnya hanya boleh mengikuti mereka.
Saya berasumsi bahwa perjanjian yang dijelaskan di atas akan diterima dengan hangat oleh sebagian besar komunitas PHP. Salah satu yang tertarik pada obat generik. Namun, segala sesuatunya menjadi jauh lebih rumit dalam hal pola. PHPStan maupun PhpStorm saat ini tidak mendukung templat. Berbeda dengan Mazmur dan Phan. Tujuannya serupa, tetapi jika Anda menggali lebih dalam, Anda akan menyadari bahwa implementasinya sedikit berbeda.
Setiap opsi yang disajikan adalah semacam kompromi.
Sederhananya, ada kebutuhan untuk kesepakatan tentang format rekaman umum:
- Mereka meningkatkan kehidupan pengembang. Pengembang dapat menambahkan obat generik ke kode mereka dan mendapat manfaat darinya.
- Pengembang dapat menggunakan alat yang paling mereka sukai dan beralih di antara mereka (alat) seperlunya.
- Pembuat alat dapat membuat alat-alat ini, memahami manfaatnya bagi masyarakat dan tidak takut bahwa sesuatu akan berubah, atau bahwa mereka akan dituduh melakukan "pendekatan yang salah."
Mazmur memiliki semua fungsi yang diperlukan untuk memeriksa obat generik. Phan juga seperti itu.
Saya yakin PhpStorm akan memperkenalkan obat generik segera setelah komunitas membuat perjanjian format tunggal.
Dukungan kode pihak ketiga
Bagian terakhir dari teka-teki umum adalah menambahkan dukungan untuk perpustakaan pihak ketiga.
Mudah-mudahan, segera setelah standar definisi umum muncul, sebagian besar perpustakaan akan mengimplementasikannya. Namun, ini tidak akan terjadi segera. Beberapa perpustakaan digunakan, tetapi tidak memiliki dukungan aktif. Saat menggunakan analisa statis untuk memvalidasi tipe dalam obat generik, penting bahwa semua fungsi yang diterima atau dikembalikan oleh obat generik ini didefinisikan.
Apa yang terjadi jika proyek Anda bergantung pada perpustakaan pihak ketiga yang tidak memiliki dukungan umum?
Untungnya, masalah ini sudah dipecahkan, dan fungsi rintisan adalah solusinya. Rintisan bertopik maz , phan , dan phpstorm .
Rintisan bertopik adalah file biasa yang berisi tanda tangan fungsi dan metode, tetapi jangan menerapkannya. Dengan menambahkan blok dermaga ke bertopik, alat analisis statis mendapatkan informasi tambahan yang mereka butuhkan. Misalnya, jika Anda memiliki kelas stack tanpa tipe petunjuk dan generik seperti ini.
class Stack { public function push($item) { } public function pop() { } }
Anda dapat membuat file rintisan yang memiliki metode yang identik, tetapi dengan penambahan blok dermaga dan tanpa implementasi fungsi.
class Stack { public function push($item); public function pop(); }
Ketika penganalisa statis melihat kelas stack, ia mengambil informasi jenis dari tulisan rintisan, bukan dari kode aktual.
Kemampuan untuk hanya membagikan kode bertopik (misalnya, melalui komposer) akan sangat berguna, karena akan memungkinkan berbagi pekerjaan yang dilakukan.
Langkah selanjutnya
Masyarakat perlu menjauh dari perjanjian dan menetapkan standar.
Mungkin opsi terbaik adalah PSR generik?
Atau mungkin pembuat analisis statis utama, PhpStorm, IDE lain dan siapa pun yang terlibat dalam pengembangan PHP (untuk kontrol) dapat mengembangkan standar yang akan digunakan semua orang.
Segera setelah standar muncul, semua orang akan dapat membantu dengan penambahan obat generik ke perpustakaan dan proyek yang ada, membuat Permintaan Tarik. Dan di mana ini tidak memungkinkan, pengembang dapat menulis dan berbagi bertopik.
Ketika semuanya sudah selesai, kita dapat menggunakan alat seperti PhpStorm untuk memeriksa obat generik secara real time saat kita menulis kode. Kita dapat menggunakan alat analisis statis sebagai bagian dari CI kami sebagai jaminan keamanan.
Generik juga dapat diimplementasikan dalam PHP (well, hampir).
Keterbatasan
Ada sejumlah batasan. PHP adalah bahasa yang dinamis yang memungkinkan Anda melakukan banyak hal "ajaib", seperti ini . Jika Anda menggunakan terlalu banyak keajaiban PHP, mungkin saja penganalisa statis tidak dapat secara akurat mengekstraksi semua jenis dalam sistem. Jika ada jenis yang tidak diketahui, maka alat tidak akan dapat menggunakan obat generik dengan benar dalam semua kasus.
Namun, aplikasi utama analisis ini adalah untuk memvalidasi logika bisnis Anda. Jika Anda menulis kode bersih, jangan gunakan terlalu banyak sihir.
Mengapa Anda tidak menambahkan obat generik saja ke lidah?
Itu akan menjadi pilihan terbaik. PHP memiliki kode sumber terbuka, dan tidak ada yang mengganggu Anda untuk mengkloning sumber dan mengimplementasikan generik!
Bagaimana jika saya tidak memerlukan obat generik?
Abaikan semua hal di atas. Salah satu keunggulan utama PHP adalah fleksibel dalam memilih tingkat kompleksitas implementasi yang tepat tergantung pada apa yang Anda buat. Dengan kode satu kali, Anda tidak perlu memikirkan hal-hal seperti mengetik petunjuk. Tetapi dalam proyek-proyek besar perlu menggunakan peluang seperti itu.
Terima kasih untuk semua orang yang membaca tempat ini. Saya akan senang atas komentar Anda di PM.
UPD : ghost404 dalam komentar mencatat bahwa PHPStan dari versi 0.12.x memahami penjelasan mazmur dan mendukung obat generik