Proxy PHP Xdebug: ketika fitur standar Xdebug tidak cukup

Proxy PHP Xdebug: ketika fitur standar Xdebug tidak cukup


Untuk debugging program PHP sering menggunakan Xdebug . Namun, fitur standar IDE dan Xdebug tidak selalu cukup. Beberapa masalah dapat diselesaikan dengan menggunakan proxy Xdebug - pydbgpproxy, tetapi masih belum semuanya. Oleh karena itu, saya mengimplementasikan proksi PHP Xdebug berdasarkan kerangka kerja asinkron amphp.


Di bawah potongan, saya akan memberi tahu Anda apa yang salah dengan pydbgpproxy, apa yang hilang di dalamnya dan mengapa saya tidak memodifikasinya. Saya juga akan menjelaskan bagaimana proksi PHP Xdebug bekerja dan menunjukkan cara memperpanjangnya menggunakan contoh.


Pydbgpproxy vs PHP Xdebug proxy


Proxy Xdebug adalah layanan perantara antara IDE dan Xdebug (permintaan proksi dari Xdebug ke IDE dan sebaliknya). Paling sering digunakan untuk debugging multi-pengguna . Ini adalah ketika Anda memiliki satu server web, dan beberapa pengembang.


Sebagai proxy, pydbgpxxy biasanya digunakan. Tetapi dia memiliki beberapa masalah:


  • tidak ada halaman resmi;
  • sulit menemukan tempat untuk mengunduhnya; ternyata ini dapat dilakukan di sini - tiba-tiba, Klien Debugging Jarak Jauh Python;
  • Saya tidak menemukan repositori resmi;
  • sebagai konsekuensi dari paragraf sebelumnya, tidak jelas kemana harus mengajukan permintaan tarik;
  • proxy, seperti namanya, ditulis dalam Python, yang tidak semua pengembang PHP tahu, yang berarti memperluasnya adalah masalah;
  • kelanjutan dari paragraf sebelumnya: jika ada kode dalam PHP, dan itu harus digunakan dalam proxy, maka harus porting ke Python, dan duplikasi kode selalu tidak baik.

Pencarian untuk proxy Xdebug yang ditulis dalam PHP di GitHub dan di Internet tidak membuahkan hasil. Jadi saya menulis proksi PHP Xdebug . Di bawah tenda, saya menggunakan kerangka asynchronous amphp .


Keuntungan utama proksi PHP Xdebug dibandingkan pydbgpproxy:


  • Proxy PHP Xdebug ditulis dalam bahasa yang akrab bagi pengembang PHP, yang berarti:
    • lebih mudah untuk memecahkan masalah di dalamnya;
    • lebih mudah berkembang;
  • Proxy PHP Xdebug memiliki repositori publik, yang berarti:
    • Anda dapat melakukan fork dan menyelesaikannya sesuai kebutuhan Anda;
    • Anda dapat mengirim permintaan tarik dengan fitur yang hilang atau solusi untuk masalah.

Cara bekerja dengan proksi PHP Xdebug


Instalasi


Proxy PHP Xdebug dapat diinstal sebagai dependensi dev melalui komposer :


composer.phar require mougrim/php-xdebug-proxy --dev 

Tetapi jika Anda tidak ingin menyeret dependensi tambahan ke proyek Anda, maka proksi PHP Xdebug dapat diinstal sebagai proyek melalui komposer yang sama:


 composer.phar create-project mougrim/php-xdebug-proxy cd php-xdebug-proxy 

Proxy PHP Xdebug dapat dikembangkan, tetapi ext-dom diperlukan secara default (ekstensi diaktifkan secara default di PHP) untuk mem- parsing XML dan amphp / log untuk logging asinkron:


 composer.phar require amphp/log '^1.0.0' 

Luncurkan


Proxy PHP Xdebug


Proxy dimulai sebagai berikut:


 bin/xdebug-proxy 

Proxy akan mulai dengan pengaturan default:


 Using config path /path/to/php-xdebug-proxy/config [2019-02-14 10:46:24] xdebug-proxy.NOTICE: Use default ide: 127.0.0.1:9000 array ( ) array ( ) [2019-02-14 10:46:24] xdebug-proxy.NOTICE: Use predefined ides array ( 'predefinedIdeList' => array ( 'idekey' => '127.0.0.1:9000', ), ) array ( ) [2019-02-14 10:46:24] xdebug-proxy.NOTICE: [Proxy][IdeRegistration] Listening for new connections on '127.0.0.1:9001'... array ( ) array ( ) [2019-02-14 10:46:24] xdebug-proxy.NOTICE: [Proxy][Xdebug] Listening for new connections on '127.0.0.1:9002'... array ( ) array ( ) 

Dari log Anda dapat melihat bahwa proxy default:


  • mendengarkan 127.0.0.1:9001 untuk koneksi login IDE;
  • mendengarkan 127.0.0.1:9002 untuk koneksi Xdebug;
  • menggunakan 127.0.0.1:9000 sebagai IDE default dan IDE yang telah ditentukan sebelumnya dengan kunci idekey.

Konfigurasi


Jika Anda ingin mengonfigurasi port mendengarkan, dll., Anda dapat menentukan jalur ke folder pengaturan. Cukup salin folder konfigurasi :


 cp -r /path/to/php-xdebug-proxy/config /your/custom/path 

Ada tiga file di folder pengaturan:


  • config.php :
     <?php return [ 'xdebugServer' => [ // host:port    Xdebug 'listen' => '127.0.0.1:9002', ], 'ideServer' => [ //  proxy    IDE,     IDE  , //    IDE  ,     . // IDE   ,  proxy    . 'defaultIde' => '127.0.0.1:9000', //  IDE    'idekey' => 'host:port', //   IDE  ,     . //  IDE ,   proxy  , //           proxy. 'predefinedIdeList' => [ 'idekey' => '127.0.0.1:9000', ], ], 'ideRegistrationServer' => [ // host:port     IDE, //     IDE,     . 'listen' => '127.0.0.1:9001', ], ]; 
  • logger.php : Anda dapat mengkonfigurasi logger; file tersebut harus mengembalikan objek yang merupakan instance dari \Psr\Log\LoggerInterface , defaultnya adalah \Monolog\Logger dengan \Amp\Log\StreamHandler (untuk rekaman yang tidak menghalangi), menampilkan log ke stdout;
  • factory.php : Anda bisa mengonfigurasi kelas yang digunakan dalam proxy; file tersebut harus mengembalikan objek yang merupakan turunan dari Factory\Factory , defaultnya adalah Factory\DefaultFactory .

Setelah menyalin file, Anda dapat mengedit dan menjalankan proxy:


 bin/xdebug-proxy --configs=/your/custom/path/config 

Debugging


Banyak artikel telah ditulis tentang cara men-debug kode menggunakan Xdebug. Saya akan perhatikan poin utama.


Di php.ini, pengaturan berikut harus di bagian [xdebug] (koreksi jika berbeda dari yang standar):


  • idekey = idekey
  • remote_host = 127.0.0.1
  • remote_port = 9002
  • remote_enable = Aktif
  • remote_autostart = Hidup
  • remote_connect_back = Mati

Kemudian Anda dapat menjalankan kode PHP yang di-debug:


 php /path/to/your/script.php 

Jika Anda melakukan semuanya dengan benar, maka debugging akan dimulai dari breakpoint pertama di IDE. Debugging dalam mode php-fpm oleh beberapa pengembang berada di luar cakupan artikel ini, tetapi dijelaskan, misalnya, di sini .


Fitur proxy ekstensi


Segala sesuatu yang kami periksa di atas, pydbgpproksi juga dapat mencapai tingkat tertentu.


Sekarang mari kita bicara tentang proxy PHP Xdebug yang paling menarik. Proxy dapat diperluas menggunakan pabrik Anda sendiri (dibuat di konfigurasi factory.php , lihat di atas). Pabrik harus mengimplementasikan antarmuka Factory\Factory .


Yang paling kuat adalah yang disebut penyusun permintaan. Mereka dapat memodifikasi permintaan dari Xdebug ke IDE dan sebaliknya. Untuk menambahkan Factory\DefaultFactory::createRequestPreparers() kueri, Anda harus mengganti metode Factory\DefaultFactory::createRequestPreparers() . Metode mengembalikan array objek yang mengimplementasikan antarmuka RequestPreparer\RequestPreparer . Ketika mem-proksi permintaan dari Xdebug ke IDE, mereka dieksekusi dalam urutan langsung, ketika memproksi permintaan dari IDE ke Xdebug, itu dibalik.


Penangan kueri dapat digunakan, misalnya, untuk mengubah jalur ke file (dalam breakpoints dan file yang dapat dieksekusi).


Debug menimpa file


Untuk memberikan contoh tentang persiapan, saya akan membuat sedikit penyimpangan. Dalam pengujian unit, kami menggunakan soft-mock ( GitHub ). Soft-mock memungkinkan Anda untuk mengganti fungsi, metode statis, konstanta, dll dalam pengujian, merupakan alternatif untuk runkit dan uopz . Ini berfungsi dengan menulis ulang file PHP dengan cepat. Demikian pula, AspectMock masih berfungsi.


Tetapi fitur standar Xdebug dan IDE memungkinkan Anda untuk men-debug ditulis ulang (memiliki jalur yang berbeda), daripada file aslinya.


Mari kita lihat lebih dekat masalah debugging menggunakan soft-mock dalam pengujian. Pertama, ambil kasus di mana kode PHP dieksekusi secara lokal.


Kesulitan pertama muncul pada tahap pengaturan breakpoints (breakpoints). Dalam IDE, mereka dipasang di file asli, bukan yang ditulis ulang. Untuk meletakkan breakpoint melalui IDE, Anda perlu menemukan file yang ditulis ulang yang sebenarnya. Masalahnya diperparah oleh fakta bahwa setiap kali file asli diubah, file yang ditulis ulang baru dibuat, yaitu, untuk setiap konten file yang unik akan ada file yang ditulis ulang yang unik.


Masalah ini dapat diselesaikan dengan memanggil fungsi xdebug_break() , yang mirip dengan pengaturan breakpoint. Dalam hal ini, tidak perlu mencari file yang ditulis ulang.


Sekarang pertimbangkan situasinya lebih rumit: aplikasi berjalan pada mesin jarak jauh.


Dalam hal ini, Anda dapat memasang folder dengan file yang ditulis ulang, misalnya, melalui SSHFS. Jika jalur lokal dan jarak jauh ke folder berbeda, maka Anda masih perlu mendaftarkan pemetaan di IDE.


Dengan satu atau lain cara, metode ini sedikit berbeda dari yang biasa dan memungkinkan Anda untuk men-debug hanya file yang disalin, tetapi tidak yang asli. Tetapi saya masih ingin mengedit dan men-debug file asli yang sama.


AspectMock mengatasi masalah dengan mengaktifkan mode debug tanpa kemampuan untuk menonaktifkannya:


 public function init(array $options = []) { if (!isset($options['excludePaths'])) { $options['excludePaths'] = []; } $options['debug'] = true; $options['excludePaths'][] = __DIR__; parent::init($options); } 

Dalam contoh pengujian sederhana, mode debug lebih lambat 20 persen, tetapi saya tidak memiliki cukup tes AspectMock untuk memberikan perkiraan yang lebih akurat tentang seberapa lambatnya. Jika Anda memiliki banyak tes di AspectMock, saya akan senang jika Anda membagikan perbandingan di komentar.


Menggunakan Xdebug dengan soft-mock


Xdebug + soft-mock


Sekarang masalahnya sudah jelas, pertimbangkan bagaimana menyelesaikannya menggunakan proxy PHP Xdebug. Bagian utama adalah di kelas RequestPreparer\SoftMocksRequestPreparer .


Di konstruktor kelas, tentukan path ke skrip inisialisasi soft-mock dan jalankan (diasumsikan bahwa soft-mock terhubung sebagai dependensi, tetapi setiap jalur dapat dilewati ke konstruktor):


 public function __construct(LoggerInterface $logger, string $initScript = '') { $this->logger = $logger; if (!$initScript) { $possibleInitScriptPaths = [ // proxy   , soft-mocks —    __DIR__.'/../../vendor/badoo/soft-mocks/src/init_with_composer.php', // proxy  soft-mocks    __DIR__.'/../../../../badoo/soft-mocks/src/init_with_composer.php', ]; foreach ($possibleInitScriptPaths as $possiblInitScriptPath) { if (file_exists($possiblInitScriptPath)) { $initScript = $possiblInitScriptPath; break; } } } if (!$initScript) { throw new Error("Can't find soft-mocks init script"); } //  soft-mocks (       ..) require $initScript; } 

Xdebug + soft-mock: dari Xdebug ke IDE


Untuk menyiapkan permintaan dari Xdebug ke IDE, Anda perlu mengganti path ke file yang ditulis ulang dengan file asli:


 public function prepareRequestToIde(XmlDocument $xmlRequest, string $rawRequest): void { $context = [ 'request' => $rawRequest, ]; $root = $xmlRequest->getRoot(); if (!$root) { return; } foreach ($root->getChildren() as $child) { //         : // - 'stack': https://xdebug.org/docs-dbgp.php#stack-get // - 'xdebug:message': https://xdebug.org/docs-dbgp.php#error-notification if (!in_array($child->getName(), ['stack', 'xdebug:message'], true)) { continue; } $attributes = $child->getAttributes(); if (isset($attributes['filename'])) { //         ,      $filename = $this->getOriginalFilePath($attributes['filename'], $context); if ($attributes['filename'] !== $filename) { $this->logger->info("Change '{$attributes['filename']}' to '{$filename}'", $context); $child->addAttribute('filename', $filename); } } } } 

Xdebug + soft-mock: dari IDE ke Xdebug


Untuk menyiapkan permintaan dari IDE ke Xdebug, Anda perlu mengganti path ke file asli dengan path ke yang ditulis ulang:


 public function prepareRequestToXdebug(string $request, CommandToXdebugParser $commandToXdebugParser): string { //       [$command, $arguments] = $commandToXdebugParser->parseCommand($request); $context = [ 'request' => $request, 'arguments' => $arguments, ]; if ($command === 'breakpoint_set') { //    -f,          // . https://xdebug.org/docs-dbgp.php#id3 if (isset($arguments['-f'])) { $file = $this->getRewrittenFilePath($arguments['-f'], $context); if ($file) { $this->logger->info("Change '{$arguments['-f']}' to '{$file}'", $context); $arguments['-f'] = $file; //    $request = $commandToXdebugParser->buildCommand($command, $arguments); } } else { $this->logger->error("Command {$command} is without argument '-f'", $context); } } return $request; } 

Agar Factory\DefaultFactory kueri berfungsi, Anda perlu membuat kelas pabrik dan mewarisinya dari Factory\DefaultFactory , atau mengimplementasikan antarmuka Factory\Factory . Untuk soft- Factory\SoftMocksFactory , Factory\SoftMocksFactory terlihat seperti ini:


 class SoftMocksFactory extends DefaultFactory { public function createConfig(array $config): Config { //       return new SoftMocksConfig($config); } public function createRequestPreparers(LoggerInterface $logger, Config $config): array { $requestPreparers = parent::createRequestPreparers($logger, $config); return array_merge($requestPreparers, [$this->createSoftMocksRequestPreparer($logger, $config)]); } public function createSoftMocksRequestPreparer(LoggerInterface $logger, SoftMocksConfig $config): SoftMocksRequestPreparer { //     init-   return new SoftMocksRequestPreparer($logger, $config->getSoftMocks()->getInitScript()); } } 

Di sini Anda memerlukan kelas konfigurasi Anda sendiri sehingga Anda dapat menentukan jalur skrip init soft-mock. Apa itu, Anda bisa lihat di Config \ SoftMocksConfig .


Hanya sedikit yang tersisa: untuk membuat pabrik baru dan menunjukkan jalur ke skrip init soft-mock. Cara ini dilakukan dapat dilihat di softMocksConfig .


API non-pemblokiran


Seperti yang saya tulis di atas, proksi PHP Xdebug menggunakan amphp di bawah tenda, yang berarti bahwa API non-pemblokiran harus digunakan untuk bekerja dengan I / O. Apmphp sudah memiliki banyak komponen yang mengimplementasikan API non-pemblokiran ini. Jika Anda akan memperpanjang proxy PHP Xdebug dan menggunakannya dalam mode multi-pengguna, maka pastikan untuk menggunakan API non-pemblokiran.


Kesimpulan


Proxy PHP Xdebug masih merupakan proyek yang cukup muda, tetapi di Badoo sudah aktif digunakan untuk men-debug tes menggunakan soft-mock.


Proxy PHP Xdebug:


  • menggantikan pydbgpproxy dalam debugging multi-pengguna;
  • dapat bekerja dengan soft-mock;
  • dapat diperluas:
    • Anda dapat mengganti jalur ke file yang berasal dari IDE dan dari Xdebug;
    • statistik dapat dikumpulkan: dalam mode debug, setidaknya konteks yang dapat dieksekusi tersedia ketika debugging (nilai variabel dan baris kode yang dapat dieksekusi).

Jika Anda menggunakan proxy Xdebug untuk apa pun selain debugging multi-pengguna, maka bagikan kasus Anda dan proxy Xdebug yang Anda gunakan dalam komentar.


Jika Anda menggunakan pydbgpproksi atau proksi Xdebug lainnya, maka coba proksi PHP Xdebug, ceritakan masalah Anda, bagikan permintaan penarikan. Ayo kembangkan proyek bersama! :)


PS Terima kasih kepada kolega saya Yevgeny Makhrov alias eZH untuk gagasan proxy smdbgpproxy !


Tautan lagi



Terima kasih atas perhatian anda!


Saya akan senang memberikan komentar dan saran.


Rinat Akhmadeev, Sr. Pengembang PHP


UPD : Terjemahan artikel yang dipublikasikan ke dalam bahasa Inggris.

Source: https://habr.com/ru/post/id442504/


All Articles