Panduan Utama untuk Transpiling Kode PHP
Diterbitkan: 2021-09-22Dalam keadaan ideal, kita harus menggunakan PHP 8.0 (versi terbaru pada saat penulisan ini) untuk semua situs kita dan memperbaruinya segera setelah versi baru dirilis. Namun, pengembang sering kali harus bekerja dengan versi PHP sebelumnya, seperti saat membuat plugin publik untuk WordPress atau bekerja dengan kode lama yang menghambat peningkatan lingkungan server web.
Dalam situasi ini, kita bisa putus asa untuk menggunakan kode PHP terbaru. Tapi ada alternatif yang lebih baik: kita masih bisa menulis kode sumber kita dengan PHP 8.0 dan mentranspilenya ke versi PHP sebelumnya — bahkan ke PHP 7.1.
Dalam panduan ini, kami akan mengajari Anda semua yang perlu Anda ketahui tentang mentranspilasikan kode PHP.
Apa itu Transpiling?
Transpiling mengubah kode sumber dari bahasa pemrograman menjadi kode sumber yang setara dengan bahasa pemrograman yang sama atau berbeda.
Transpiling bukanlah konsep baru dalam pengembangan web: pengembang sisi klien kemungkinan besar akan akrab dengan Babel, transpiler untuk kode JavaScript.
Babel mengonversi kode JavaScript dari versi ECMAScript 2015+ modern menjadi versi lawas yang kompatibel dengan browser lama. Misalnya, diberikan fungsi panah ES2015:
[2, 4, 6].map((n) => n * 2);
…Babel akan mengubahnya menjadi versi ES5-nya:
[2, 4, 6].map(function(n) { return n * 2; });
Apa itu Transpiling PHP?
Apa yang berpotensi baru dalam pengembangan web adalah kemungkinan transpiling kode sisi server, khususnya PHP.
Transpiling PHP bekerja dengan cara yang sama seperti transpiling JavaScript: kode sumber dari versi PHP modern diubah menjadi kode yang setara untuk versi PHP yang lebih lama.
Mengikuti contoh yang sama seperti sebelumnya, fungsi panah dari PHP 7.4:
$nums = array_map(fn($n) => $n * 2, [2, 4, 6]);
…dapat ditranspilasikan ke versi PHP 7.3 yang setara:
$nums = array_map( function ($n) { return $n * 2; }, [2, 4, 6] );
Fungsi panah dapat ditranspilasikan karena merupakan gula sintaksis, yaitu sintaks baru untuk menghasilkan perilaku yang sudah ada. Ini adalah buah yang menggantung rendah.
Namun, ada juga fitur baru yang membuat perilaku baru, dan dengan demikian, tidak akan ada kode yang setara untuk versi PHP sebelumnya. Itulah yang terjadi dengan tipe serikat pekerja, yang diperkenalkan di PHP 8.0:
function someFunction(float|int $param): string|float|int|null { // ... }
Dalam situasi ini, transpiling masih dapat dilakukan selama fitur baru diperlukan untuk pengembangan tetapi tidak untuk produksi. Kemudian, kita dapat dengan mudah menghapus fitur tersebut dari kode yang ditranskripsikan tanpa konsekuensi serius.
Salah satu contohnya adalah tipe serikat pekerja. Fitur ini digunakan untuk memeriksa bahwa tidak ada ketidakcocokan antara jenis input dan nilai yang diberikan, yang membantu mencegah bug. Jika ada konflik dengan tipe, akan ada kesalahan dalam pengembangan, dan kita harus menangkapnya dan memperbaikinya sebelum kode mencapai produksi.
Oleh karena itu, kami dapat menghapus fitur dari kode untuk produksi:
function someFunction($param) { // ... }
Jika kesalahan masih terjadi dalam produksi, pesan kesalahan yang dilemparkan akan kurang tepat dibandingkan jika kita memiliki tipe serikat pekerja. Namun, potensi kerugian ini sebanding dengan kemampuan untuk menggunakan tipe serikat pekerja sejak awal.
Keuntungan Transpiling Kode PHP
Transpiling memungkinkan seseorang untuk membuat kode aplikasi menggunakan versi terbaru PHP dan menghasilkan rilis yang juga berfungsi di lingkungan yang menjalankan versi PHP yang lebih lama.
Ini bisa sangat berguna bagi pengembang yang membuat produk untuk sistem manajemen konten (CMS) lawas. WordPress, misalnya, masih secara resmi mendukung PHP 5.6 (meskipun merekomendasikan PHP 7.4+). Persentase situs WordPress yang menjalankan PHP versi 5.6 hingga 7.2 — yang semuanya End-of-Life (EOL), yang berarti mereka tidak menerima pembaruan keamanan lagi — mencapai 34,8% yang cukup besar, dan yang berjalan pada versi PHP apa pun selain 8.0 berdiri di 99,5% kekalahan:

Akibatnya, tema dan plugin WordPress yang ditargetkan untuk audiens global kemungkinan besar akan dikodekan dengan versi PHP lama untuk meningkatkan kemungkinan jangkauannya. Berkat transpiling, ini dapat dikodekan menggunakan PHP 8.0, dan masih dirilis untuk versi PHP yang lebih lama, sehingga menargetkan sebanyak mungkin pengguna.
Memang, aplikasi apa pun yang perlu mendukung versi PHP apa pun selain yang terbaru (bahkan dalam kisaran versi PHP yang saat ini didukung) dapat memperoleh manfaat.
Ini adalah kasus dengan Drupal, yang membutuhkan PHP 7.3. Berkat transpiling, pengembang dapat membuat modul Drupal yang tersedia untuk umum menggunakan PHP 8.0, dan merilisnya dengan PHP 7.3.
Contoh lain adalah ketika membuat kode kustom untuk klien yang tidak dapat menjalankan PHP 8.0 di lingkungan mereka karena satu dan lain alasan. Namun demikian, berkat transpiling, pengembang masih dapat mengkodekan kiriman mereka menggunakan PHP 8.0 dan menjalankannya di lingkungan lama tersebut.
Kapan Transpile PHP
Kode PHP selalu dapat diubah kecuali jika berisi beberapa fitur PHP yang tidak ada padanannya di versi PHP sebelumnya.
Itu mungkin kasus dengan atribut, diperkenalkan di PHP 8.0:
#[SomeAttr] function someFunc() {} #[AnotherAttr] class SomeClass {}
Pada contoh sebelumnya menggunakan fungsi panah, kode dapat ditranspilasikan karena fungsi panah adalah gula sintaksis. Atribut, sebaliknya, menciptakan perilaku yang sama sekali baru. Perilaku ini juga dapat direproduksi dengan PHP 7.4 dan di bawahnya, tetapi hanya dengan mengkodekannya secara manual, yaitu tidak secara otomatis berdasarkan alat atau proses (AI dapat memberikan solusi, tetapi kami belum sampai di sana).
Atribut yang ditujukan untuk penggunaan pengembangan, seperti #[Deprecated]
, dapat dihapus dengan cara yang sama seperti menghapus tipe serikat pekerja. Tetapi atribut yang mengubah perilaku aplikasi dalam produksi tidak dapat dihapus, dan atribut tersebut juga tidak dapat langsung ditranskripsikan.
Sampai hari ini, tidak ada transpiler yang dapat mengambil kode dengan atribut PHP 8.0 dan secara otomatis menghasilkan kode setara PHP 7.4. Akibatnya, jika kode PHP Anda perlu menggunakan atribut, maka transpiling akan sulit atau tidak layak.
Fitur PHP Yang Dapat Ditranspilasikan
Ini adalah fitur-fitur dari PHP 7.1 ke atas yang saat ini dapat ditranskripsikan. Jika kode Anda hanya menggunakan fitur-fitur ini, Anda dapat menikmati kepastian bahwa aplikasi Anda yang telah ditranskripsi akan berfungsi. Jika tidak, Anda harus menilai apakah kode yang di-transpilasikan akan menghasilkan kegagalan.
Versi PHP | Fitur |
---|---|
7.1 | Semuanya |
7.2 | – tipe object – pelebaran tipe parameter – Bendera PREG_UNMATCHED_AS_NULL di preg_match |
7.3 | – Penetapan referensi dalam list() / perusakan array ( Kecuali di dalam foreach — #4376)– Sintaks Heredoc dan Nowdoc yang fleksibel – Tanda koma di panggilan fungsi – set(raw)cookie menerima argumen $option |
7.4 | – Sifat yang diketik – Fungsi panah – Operator penugasan penggabungan nol – Membongkar di dalam array – Pemisah literal numerik – strip_tags() dengan array nama tag– tipe pengembalian kovarian dan tipe param kontravarian |
8.0 | – Jenis serikat – tipe semu mixed – tipe pengembalian static – ::class pada objek- ekspresi match – catch pengecualian hanya berdasarkan jenis– Operator aman-nol – Promosi properti konstruktor kelas – Tanda koma di daftar parameter dan daftar use penutupan |
PHP Transpiler
Saat ini, ada satu alat untuk menerjemahkan kode PHP: Rektor.
Rector adalah alat rekonstruktor PHP, yang mengubah kode PHP berdasarkan aturan yang dapat diprogram. Kami memasukkan kode sumber dan seperangkat aturan untuk dijalankan, dan Rektor akan mengubah kode tersebut.
Rektor dioperasikan melalui baris perintah, dipasang di proyek melalui Komposer. Saat dieksekusi, Rektor akan menampilkan “diff” (tambahan dalam warna hijau, penghapusan dalam warna merah) dari kode sebelum dan sesudah konversi:

Versi PHP mana yang akan di-Transpile ke
Untuk mengubah kode di seluruh versi PHP, aturan yang sesuai harus dibuat.
Saat ini, perpustakaan Rektor memuat sebagian besar aturan untuk transpiling kode dalam kisaran PHP 8.0 hingga 7.1. Oleh karena itu, kami dapat dengan andal mengubah kode PHP kami hingga versi 7.1.
Ada juga aturan untuk transpiling dari PHP 7.1 ke 7.0 dan dari 7.0 ke 5.6, tetapi ini tidak lengkap. Pekerjaan sedang dilakukan untuk menyelesaikannya, jadi pada akhirnya kami dapat mengubah kode PHP ke versi 5.6.
Transpiling vs Backporting
Backporting mirip dengan transpiling, tetapi lebih sederhana. Kode backporting tidak selalu bergantung pada fitur baru dari suatu bahasa. Alih-alih, fungsi yang sama dapat disediakan untuk versi bahasa yang lebih lama hanya dengan menyalin/menempelkan/mengadaptasi kode yang sesuai dari versi bahasa yang baru.
Misalnya, fungsi str_contains
diperkenalkan di PHP 8.0. Fungsi yang sama untuk PHP 7.4 dan di bawahnya dapat dengan mudah diimplementasikan seperti ini:
if (!defined('PHP_VERSION_ID') || (defined('PHP_VERSION_ID') && PHP_VERSION_ID < 80000)) { if (!function_exists('str_contains')) { /** * Checks if a string contains another * * @param string $haystack The string to search in * @param string $needle The string to search * @return boolean Returns TRUE if the needle was found in haystack, FALSE otherwise. */ function str_contains(string $haystack, string $needle): bool { return strpos($haystack, $needle) !== false; } } }
Karena backporting lebih sederhana daripada transpiling, kita harus memilih solusi ini setiap kali backporting berhasil.
Mengenai rentang antara PHP 8.0 hingga 7.1, kita dapat menggunakan perpustakaan polyfill Symfony:
- Polyfill PHP 7.1
- Polyfill PHP 7.2
- Polyfill PHP 7.3
- Polyfill PHP 7.4
- Polyfill PHP 8.0
Pustaka ini mendukung fungsi, kelas, konstanta, dan antarmuka berikut:
Versi PHP | Fitur |
---|---|
7.2 | Fungsi:
Konstanta:
|
7.3 | Fungsi:
Pengecualian:
|
7.4 | Fungsi:
|
8.0 | Antarmuka:
Kelas:
Konstanta:
Fungsi:
|
Contoh PHP yang Ditranskripsikan
Mari kita periksa beberapa contoh kode PHP yang ditranspilasikan, dan beberapa paket yang sedang ditranspilasikan sepenuhnya.
Kode PHP
Ekspresi match
diperkenalkan di PHP 8.0. Kode sumber ini:
function getFieldValue(string $fieldName): ?string { return match($fieldName) { 'foo' => 'foofoo', 'bar' => 'barbar', 'baz' => 'bazbaz', default => null, }; }
…akan ditranspilasikan ke versi PHP 7.4 yang setara, menggunakan operator switch
:
function getFieldValue(string $fieldName): ?string { switch ($fieldName) { case 'foo': return 'foofoo'; case 'bar': return 'barbar'; case 'baz': return 'bazbaz'; default: return null; } }
Operator nullsafe juga diperkenalkan di PHP 8.0:
public function getValue(TypeResolverInterface $typeResolver): ?string { return $this->getResolver($typeResolver)?->getValue(); }
Kode yang ditranspilasikan perlu menetapkan nilai operasi ke variabel baru terlebih dahulu, untuk menghindari eksekusi operasi dua kali:
public function getValue(TypeResolverInterface $typeResolver): ?string { return ($val = $this->getResolver($typeResolver)) ? $val->getValue() : null; }
Fitur promosi properti konstruktor, juga diperkenalkan di PHP 8.0, memungkinkan pengembang untuk menulis lebih sedikit kode:
class QueryResolver { function __construct(protected QueryFormatter $queryFormatter) { } }
Saat transpiling untuk PHP 7.4, potongan kode lengkap dihasilkan:
class QueryResolver { protected QueryFormatter $queryFormatter; function __construct(QueryFormatter $queryFormatter) { $this->queryFormatter = $queryFormatter; } }
Kode transpiled di atas berisi properti yang diketik, yang diperkenalkan di PHP 7.4. Transpiling kode itu ke PHP 7.3 menggantikannya dengan docblocks:
class QueryResolver { /** * @var QueryFormatter */ protected $queryFormatter; function __construct(QueryFormatter $queryFormatter) { $this->queryFormatter = $queryFormatter; } }
Paket PHP
Pustaka berikut sedang ditranspilasikan untuk produksi:
Perpustakaan/deskripsi | Kode/catatan |
---|---|
Rektor Alat rekonstruktor PHP yang memungkinkan transpiling | - Kode sumber – Kode yang ditranskripsikan – Catatan |
Standar Pengkodean Mudah Alat agar kode PHP mematuhi seperangkat aturan | - Kode sumber – Kode yang ditranskripsikan – Catatan |
GraphQL API untuk WordPress Plugin yang menyediakan server GraphQL untuk WordPress | - Kode sumber – Kode yang ditranskripsikan – Catatan |
Pro dan Kontra Transpiling PHP
Manfaat dari transpiling PHP telah dijelaskan: memungkinkan kode sumber untuk menggunakan PHP 8.0 (yaitu versi terbaru dari PHP), yang akan diubah ke versi yang lebih rendah untuk PHP agar produksi dapat berjalan di aplikasi atau lingkungan lama.
Ini secara efektif memungkinkan kami untuk menjadi pengembang yang lebih baik, menghasilkan kode dengan kualitas yang lebih tinggi. Ini karena kode sumber kami dapat menggunakan tipe gabungan PHP 8.0, properti yang diketik PHP 7.4, dan berbagai tipe dan tipe semu yang ditambahkan ke setiap versi baru PHP ( mixed
dari PHP 8.0, object
dari PHP 7.2), di antaranya fitur modern lainnya dari PHP.
Dengan menggunakan fitur ini, kami dapat menangkap bug dengan lebih baik selama pengembangan dan menulis kode yang lebih mudah dibaca.
Sekarang, mari kita lihat kekurangannya.
Itu Harus Dikodekan Dan Dipertahankan
Rektor dapat mengubah kode secara otomatis, tetapi prosesnya kemungkinan akan memerlukan beberapa input manual untuk membuatnya bekerja dengan pengaturan khusus kami.
Perpustakaan Pihak Ketiga Juga Harus Ditranspilasikan
Ini menjadi masalah setiap kali transpiling mereka menghasilkan kesalahan karena kita kemudian harus menyelidiki kode sumber mereka untuk mengetahui kemungkinan alasannya. Jika masalah dapat diperbaiki dan proyek bersifat open source, kami harus mengirimkan permintaan tarik. Jika perpustakaan bukan open source, kami mungkin menemui hambatan.
Rektor Tidak Memberitahu Kami Jika Kode Tidak Dapat Ditranskripsi
Jika kode sumber berisi atribut PHP 8.0 atau fitur lain apa pun yang tidak dapat diubah, kami tidak dapat melanjutkan. Namun, Rektor tidak akan memeriksa kondisi ini, sehingga harus dilakukan secara manual. Ini mungkin bukan masalah besar tentang kode sumber kita sendiri karena kita sudah terbiasa dengannya, tetapi ini bisa menjadi kendala terkait dependensi pihak ketiga.
Informasi Debugging Menggunakan Kode Transpiled, Bukan Kode Sumber
Ketika aplikasi menghasilkan pesan kesalahan dengan jejak tumpukan dalam produksi, nomor baris akan menunjuk ke kode yang ditranspilasikan. Kita perlu mengonversi kembali dari transpiled ke kode asli untuk menemukan nomor baris yang sesuai dalam kode sumber.
Kode Transpiled Juga Harus Diawali
Proyek transpiled kami dan beberapa perpustakaan lain yang juga dipasang di lingkungan produksi dapat menggunakan ketergantungan pihak ketiga yang sama. Ketergantungan pihak ketiga ini akan ditranspilasikan untuk proyek kami dan menyimpan kode sumber aslinya untuk perpustakaan lain. Oleh karena itu, versi transpiled harus diawali dengan PHP-Scoper, Strauss, atau alat lain untuk menghindari potensi konflik.
Transpiling Harus Dilakukan Selama Continuous Integration (CI)
Karena kode yang ditranspilasikan secara alami akan menimpa kode sumber, kami tidak boleh menjalankan proses transpiling di komputer pengembangan kami, atau kami akan berisiko menimbulkan efek samping. Menjalankan proses selama menjalankan CI lebih cocok (lebih lanjut tentang ini di bawah).
Cara Transpile PHP
Pertama, kita perlu menginstal Rektor di proyek kita untuk pengembangan:
composer require rector/rector --dev
Kami kemudian membuat file konfigurasi rector.php
di direktori root proyek yang berisi kumpulan aturan yang diperlukan. Untuk menurunkan kode dari PHP 8.0 ke 7.1, kami menggunakan konfigurasi ini:

use Rector\Set\ValueObject\DowngradeSetList; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $containerConfigurator->import(DowngradeSetList::PHP_80); $containerConfigurator->import(DowngradeSetList::PHP_74); $containerConfigurator->import(DowngradeSetList::PHP_73); $containerConfigurator->import(DowngradeSetList::PHP_72); };
Untuk memastikan proses berjalan seperti yang diharapkan, kita dapat menjalankan perintah process
Rektor dalam mode kering, meneruskan lokasi ke proses (dalam hal ini, semua file di bawah folder src/
):
vendor/bin/rector process src --dry-run
Untuk melakukan transpiling, kami menjalankan perintah process
Rektor, yang akan mengubah file di lokasi yang ada:
vendor/bin/rector process src
Harap diperhatikan: jika kami menjalankan rector process
di komputer pengembangan kami, kode sumber akan dikonversi di tempat, di bawah src/
. Namun, kami ingin menghasilkan kode yang dikonversi di lokasi yang berbeda untuk tidak menimpa kode sumber saat menurunkan versi kode. Untuk alasan ini, menjalankan proses paling cocok selama integrasi berkelanjutan.
Mengoptimalkan Proses Transpiling
Untuk menghasilkan kiriman transpiled untuk produksi, hanya kode untuk produksi yang harus dikonversi; kode yang diperlukan hanya untuk pengembangan dapat dilewati. Itu berarti kita dapat menghindari transpiling semua tes (untuk proyek kita dan dependensinya) dan semua dependensi untuk pengembangan.
Mengenai tes, kita sudah tahu di mana lokasi proyek kita — misalnya, di bawah folder tests/
. Kita juga harus mencari tahu di mana dependensi berada — misalnya, di bawah subfoldernya tests/
, test/
dan Test/
(untuk pustaka yang berbeda). Kemudian, kami meminta Rektor untuk melewatkan pemrosesan folder-folder ini:
return static function (ContainerConfigurator $containerConfigurator): void { // ... $parameters->set(Option::SKIP, [ // Skip tests '*/tests/*', '*/test/*', '*/Test/*', ]); };
Mengenai dependensi, Komposer tahu mana yang untuk pengembangan (yang di bawah entry require-dev
di composer.json
) dan mana yang untuk produksi (yang di entry require
).
Untuk mengambil dari Composer jalur semua dependensi untuk produksi, kami menjalankan:
composer info --path --no-dev
Perintah ini akan menghasilkan daftar dependensi dengan nama dan jalurnya, seperti ini:
brain/cortex /Users/leo/GitHub/leoloso/PoP/vendor/brain/cortex composer/installers /Users/leo/GitHub/leoloso/PoP/vendor/composer/installers composer/semver /Users/leo/GitHub/leoloso/PoP/vendor/composer/semver guzzlehttp/guzzle /Users/leo/GitHub/leoloso/PoP/vendor/guzzlehttp/guzzle league/pipeline /Users/leo/GitHub/leoloso/PoP/vendor/league/pipeline
Kita dapat mengekstrak semua jalur dan memasukkannya ke dalam perintah Rektor, yang kemudian akan memproses folder src/
proyek kita ditambah folder-folder yang berisi semua dependensi untuk produksi:
$ paths="$(composer info --path --no-dev | cut -d' ' -f2- | sed 's/ //g' | tr '\n' ' ')" $ vendor/bin/rector process src $paths
Perbaikan lebih lanjut dapat mencegah Rektor memproses dependensi yang sudah menggunakan versi PHP target. Jika pustaka telah dikodekan dengan PHP 7.1 (atau versi apa pun di bawah), maka tidak perlu mentranspilenya ke PHP 7.1.
Untuk mencapai ini, kita dapat memperoleh daftar pustaka yang membutuhkan PHP 7.2 dan yang lebih baru dan hanya memprosesnya. Kami akan mendapatkan nama semua perpustakaan ini melalui perintah why-not
Komposer, seperti ini:
composer why-not php "7.1.*" | grep -o "\S*\/\S*"
Karena perintah ini tidak bekerja dengan --no-dev
, untuk menyertakan hanya dependensi untuk produksi, pertama-tama kita harus menghapus dependensi untuk pengembangan dan membuat ulang autoloader, menjalankan perintah, lalu menambahkannya lagi:
$ composer install --no-dev $ packages=$(composer why-not php "7.1.*" | grep -o "\S*\/\S*") $ composer install
Perintah info --path
Composer mengambil jalur untuk sebuah paket, dengan format ini:
# Executing this command $ composer info psr/cache --path # Produces this response: psr/cache /Users/leo/GitHub/leoloso/PoP/vendor/psr/cache
Kami menjalankan perintah ini untuk semua item dalam daftar kami untuk mendapatkan semua jalur ke transpile:
for package in $packages do path=$(composer info $package --path | cut -d' ' -f2-) paths="$paths $path" done
Terakhir, kami memberikan daftar ini kepada Rektor (ditambah folder src/
proyek):
Butuh solusi hosting yang memberi Anda keunggulan kompetitif? Kinsta membantu Anda dengan kecepatan luar biasa, keamanan canggih, dan penskalaan otomatis. Lihat rencana kami
vendor/bin/rector process src $paths
Kesalahan yang Harus Dihindari Saat Transpiling Kode
Transpiling kode dapat dianggap sebagai seni, seringkali membutuhkan penyesuaian khusus untuk proyek tersebut. Mari kita lihat beberapa masalah yang mungkin kita hadapi.
Aturan Terrantai Tidak Selalu Diproses
Aturan berantai adalah ketika aturan perlu mengonversi kode yang dihasilkan oleh aturan sebelumnya.
Misalnya, library symfony/cache
berisi kode ini:
final class CacheItem implements ItemInterface { public function tag($tags): ItemInterface { // ... return $this; } }
Saat transpiling dari PHP 7.4 ke 7.3, tag
fungsi harus mengalami dua modifikasi:
- Tipe pengembalian
ItemInterface
harus terlebih dahulu dikonversi keself
, karena aturanDowngradeCovariantReturnTypeRector
-
self
tipe yang dikembalikan kemudian harus dihapus, karena aturanDowngradeSelfTypeDeclarationRector
Hasil akhirnya harus seperti ini:
final class CacheItem implements ItemInterface { public function tag($tags) { // ... return $this; } }
Namun, Rektor hanya mengeluarkan tahap menengah:
final class CacheItem implements ItemInterface { public function tag($tags): self { // ... return $this; } }
Persoalannya, Rektor tidak bisa selalu mengontrol urutan aturan yang diterapkan.
Solusinya adalah mengidentifikasi aturan berantai mana yang tidak diproses, dan menjalankan Rektor baru untuk menerapkannya.
Untuk mengidentifikasi aturan yang dirantai, kami menjalankan Rektor dua kali pada kode sumber, seperti ini:
$ vendor/bin/rector process src $ vendor/bin/rector process src --dry-run
Pertama kali, kami menjalankan Rektor seperti yang diharapkan, untuk mengeksekusi transpiling. Kedua kalinya, kita menggunakan flag --dry-run
untuk mengetahui apakah masih ada perubahan yang harus dilakukan. Jika ada, perintah akan keluar dengan kode kesalahan, dan output “diff” akan menunjukkan aturan mana yang masih dapat diterapkan. Itu berarti proses pertama belum selesai, dengan beberapa aturan berantai tidak diproses.

Setelah kami mengidentifikasi aturan berantai yang tidak diterapkan (atau aturan), kami kemudian dapat membuat file konfigurasi Rektor lain — misalnya, rector-chained-rule.php
akan mengeksekusi aturan yang hilang. Alih-alih memproses set lengkap aturan untuk semua file di bawah src/
, kali ini, kita dapat menjalankan aturan spesifik yang hilang pada file tertentu yang perlu diterapkan:
// rector-chained-rule.php use Rector\Core\Configuration\Option; use Rector\DowngradePhp74\Rector\ClassMethod\DowngradeSelfTypeDeclarationRector; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); $services->set(DowngradeSelfTypeDeclarationRector::class); $parameters = $containerConfigurator->parameters(); $parameters->set(Option::PATHS, [ __DIR__ . '/vendor/symfony/cache/CacheItem.php', ]); };
Terakhir, kami memberi tahu Rektor pada pass kedua untuk menggunakan file konfigurasi baru melalui input --config
:
# First pass with all modifications $ vendor/bin/rector process src # Second pass to fix a specific problem $ vendor/bin/rector process --config=rector-chained-rule.php
Dependensi Komposer Mungkin Tidak Konsisten
Perpustakaan dapat mendeklarasikan dependensi untuk dijadwalkan untuk pengembangan (yaitu di bawah require-dev
di composer.json
), namun tetap saja, referensikan beberapa kode dari mereka untuk produksi (seperti pada beberapa file di bawah src/
, bukan tests/
).
Biasanya hal ini tidak menjadi masalah karena kode tersebut mungkin tidak dimuat pada saat produksi, sehingga tidak akan pernah ada kesalahan pada aplikasi. Namun, ketika Rektor memproses kode sumber dan dependensinya, Rektor memvalidasi bahwa semua kode yang direferensikan dapat dimuat. Rektor akan membuat kesalahan jika ada file yang mereferensikan beberapa bagian kode dari perpustakaan yang tidak diinstal (karena itu dinyatakan hanya diperlukan untuk pengembangan).
Misalnya, kelas EarlyExpirationHandler
dari komponen Cache Symfony mengimplementasikan antarmuka MessageHandlerInterface
dari komponen Messenger:
class EarlyExpirationHandler implements MessageHandlerInterface { //... }
Namun, symfony/cache
mendeklarasikan symfony/messenger
sebagai dependensi untuk pengembangan. Kemudian, ketika menjalankan Rektor pada proyek yang bergantung pada symfony/cache
, itu akan menimbulkan kesalahan:
[ERROR] Could not process "vendor/symfony/cache/Messenger/EarlyExpirationHandler.php" file, due to: "Analyze error: "Class Symfony\Component\Messenger\Handler\MessageHandlerInterface not found.". Include your files in "$parameters->set(Option::AUTOLOAD_PATHS, [...]);" in "rector.php" config. See https://github.com/rectorphp/rector#configuration".
Ada tiga solusi untuk masalah ini:
- Dalam konfigurasi Rektor, lewati pemrosesan file yang mereferensikan potongan kode itu:
return static function (ContainerConfigurator $containerConfigurator): void { // ... $parameters->set(Option::SKIP, [ __DIR__ . '/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php', ]); };
- Unduh perpustakaan yang hilang dan tambahkan jalurnya untuk dimuat secara otomatis oleh Rektor:
return static function (ContainerConfigurator $containerConfigurator): void { // ... $parameters->set(Option::AUTOLOAD_PATHS, [ __DIR__ . '/vendor/symfony/messenger', ]); };
- Minta proyek Anda bergantung pada pustaka yang hilang untuk produksi:
composer require symfony/messenger
Transpiling dan Integrasi Berkelanjutan
Seperti disebutkan sebelumnya, di komputer pengembangan kita, kita harus menggunakan flag --dry-run
saat menjalankan Rektor, atau sebaliknya, kode sumber akan ditimpa dengan kode yang ditranspilasikan. Untuk alasan ini, lebih cocok untuk menjalankan proses transpiling yang sebenarnya selama continuous integration (CI), di mana kita dapat memutar runner sementara untuk menjalankan proses.
Waktu yang ideal untuk menjalankan proses transpiling adalah saat membuat rilis untuk proyek kami. Misalnya, kode di bawah ini adalah alur kerja untuk Tindakan GitHub, yang membuat rilis plugin WordPress:
name: Generate Installable Plugin and Upload as Release Asset on: release: types: [published] jobs: build: name: Build, Downgrade and Upload Release runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/[email protected] - name: Downgrade code for production (to PHP 7.1) run: | composer install vendor/bin/rector process sed -i 's/Requires PHP: 7.4/Requires PHP: 7.1/' graphql-api.php - name: Build project for production run: | composer install --no-dev --optimize-autoloader mkdir build - name: Create artifact uses: montudor/[email protected] with: args: zip -X -r build/graphql-api.zip . -x *.git* node_modules/\* .* "*/\.*" CODE_OF_CONDUCT.md CONTRIBUTING.md ISSUE_TEMPLATE.md PULL_REQUEST_TEMPLATE.md rector.php *.dist composer.* dev-helpers** build** - name: Upload artifact uses: actions/[email protected] with: name: graphql-api path: build/graphql-api.zip - name: Upload to release uses: JasonEtco/[email protected] with: args: build/graphql-api.zip application/zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Alur kerja ini berisi prosedur standar untuk merilis plugin WordPress melalui GitHub Actions. Penambahan baru, untuk mengubah kode plugin dari PHP 7.4 ke 7.1, terjadi pada langkah ini:
- name: Downgrade code for production (to PHP 7.1) run: | vendor/bin/rector process sed -i 's/Requires PHP: 7.4/Requires PHP: 7.1/' graphql-api.php
Secara keseluruhan, alur kerja ini sekarang melakukan langkah-langkah berikut:
- Periksa kode sumber untuk plugin WordPress dari repositorinya, yang ditulis dengan PHP 7.4
- Menginstal dependensi Komposernya
- Transpiles kodenya dari PHP 7.4 ke 7.1
- Memodifikasi entri "Memerlukan PHP" di header file utama plugin dari
"7.4"
menjadi"7.1"
- Menghapus dependensi yang diperlukan untuk pengembangan
- Membuat file .zip plugin, tidak termasuk semua file yang tidak dibutuhkan
- Mengunggah file .zip sebagai aset rilis (dan, sebagai tambahan, sebagai artefak ke GitHub Action)
Menguji Kode yang Ditranskripsi
Setelah kode ditranspilasikan ke PHP 7.1, bagaimana kita tahu bahwa kode itu berfungsi dengan baik? Atau, dengan kata lain, bagaimana kita tahu bahwa itu telah sepenuhnya dikonversi, dan tidak ada sisa kode PHP versi yang lebih tinggi yang tertinggal?
Mirip dengan transpiling kode, kita dapat mengimplementasikan solusi dalam proses CI. Idenya adalah untuk menyiapkan lingkungan runner dengan PHP 7.1 dan menjalankan linter pada kode yang ditranspilasikan. Jika ada bagian kode yang tidak kompatibel dengan PHP 7.1 (seperti properti yang diketik dari PHP 7.4 yang tidak dikonversi), maka linter akan menampilkan kesalahan.
Linter untuk PHP yang berfungsi dengan baik adalah PHP Parallel Lint. Kami dapat menginstal perpustakaan ini sebagai ketergantungan untuk pengembangan dalam proyek kami, atau meminta proses CI menginstalnya sebagai proyek Komposer mandiri:
composer create-project php-parallel-lint/php-parallel-lint
Setiap kali kode berisi PHP 7.2 dan di atasnya, PHP Parallel Lint akan menampilkan kesalahan seperti ini:
Run php-parallel-lint/parallel-lint layers/ vendor/ --exclude vendor/symfony/polyfill-ctype/bootstrap80.php --exclude vendor/symfony/polyfill-intl-grapheme/bootstrap80.php --exclude vendor/symfony/polyfill-intl-idn/bootstrap80.php --exclude vendor/symfony/polyfill-intl-normalizer/bootstrap80.php --exclude vendor/symfony/polyfill-mbstring/bootstrap80.php PHP 7.1.33 | 10 parallel jobs ............................................................ 60/2870 (2 %) ............................................................ 120/2870 (4 %) ... ............................................................ 660/2870 (22 %) .............X.............................................. 720/2870 (25 %) ............................................................ 780/2870 (27 %) ... ............................................................ 2820/2870 (98 %) .................................................. 2870/2870 (100 %) Checked 2870 files in 15.4 seconds Syntax error found in 1 file ------------------------------------------------------------ Parse error: layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php:55 53| '0.8.0', 54| \__('GraphQL API for WordPress', 'graphql-api'), > 55| ))) { 56| $plugin->setup(); 57| } Unexpected ')' in layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php on line 55 Error: Process completed with exit code 1.
Mari tambahkan linter ke dalam alur kerja CI kita. Langkah-langkah untuk mengeksekusi transpile kode dari PHP 8.0 ke 7.1 dan mengujinya adalah:
- Lihat kode sumbernya
- Minta lingkungan menjalankan PHP 8.0, sehingga Rektor dapat menafsirkan kode sumbernya
- Transpile kode ke PHP 7.1
- Instal alat linter PHP
- Ganti versi PHP lingkungan ke 7.1
- Jalankan linter pada kode yang ditranspilasikan
Alur kerja GitHub Action ini berfungsi:
name: Downgrade PHP tests jobs: main: name: Downgrade code to PHP 7.1 via Rector, and execute tests runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/[email protected] - name: Set-up PHP uses: shivammathur/[email protected] with: php-version: 8.0 coverage: none - name: Local packages - Downgrade PHP code via Rector run: | composer install vendor/bin/rector process # Prepare for testing on PHP 7.1 - name: Install PHP Parallel Lint run: composer create-project php-parallel-lint/php-parallel-lint --ansi - name: Switch to PHP 7.1 uses: shivammathur/[email protected] with: php-version: 7.1 coverage: none # Lint the transpiled code - name: Run PHP Parallel Lint on PHP 7.1 run: php-parallel-lint/parallel-lint src/ vendor/ --exclude vendor/symfony/polyfill-ctype/bootstrap80.php --exclude vendor/symfony/polyfill-intl-grapheme/bootstrap80.php --exclude vendor/symfony/polyfill-intl-idn/bootstrap80.php --exclude vendor/symfony/polyfill-intl-normalizer/bootstrap80.php --exclude vendor/symfony/polyfill-mbstring/bootstrap80.php
Harap perhatikan bahwa beberapa file bootstrap80.php
dari perpustakaan polyfill Symfony (yang tidak perlu ditranspilasikan) harus dikeluarkan dari linter. File-file ini berisi PHP 8.0, sehingga linter akan menimbulkan kesalahan saat memprosesnya. Namun, mengecualikan file-file ini aman karena hanya akan dimuat pada produksi saat menjalankan PHP 8.0 atau lebih tinggi:
if (\PHP_VERSION_ID >= 80000) { return require __DIR__.'/bootstrap80.php'; }
Ringkasan
Artikel ini mengajari kami cara mengubah kode PHP kami, memungkinkan kami untuk menggunakan PHP 8.0 dalam kode sumber dan membuat rilis yang berfungsi pada PHP 7.1. Transpiling dilakukan melalui Rektor, alat rekonstruktor PHP.
Transpiling kode kami membuat kami menjadi pengembang yang lebih baik karena kami dapat menangkap bug dengan lebih baik dalam pengembangan dan menghasilkan kode yang secara alami lebih mudah dibaca dan dipahami.
Transpiling juga memungkinkan kita untuk memisahkan kode kita dengan persyaratan PHP tertentu dari CMS. Sekarang kita dapat melakukannya jika kita ingin menggunakan versi terbaru PHP untuk membuat plugin WordPress atau modul Drupal yang tersedia untuk umum tanpa membatasi basis pengguna kita.
Apakah Anda memiliki pertanyaan tentang transpiling PHP? Beri tahu kami di bagian komentar!