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:
Jika Anda menjalankan skrip
index.php , maka PHP akan terhubung dan menjalankan semua ini secara berurutan:
$a = 0; $a++; $a++; echo $a;
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();
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
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.
TugasAnda 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:
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);
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
TugasTulis 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 :
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:
File yang menghubungkan kelas ini:
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:
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:
- Setiap perpustakaan harus hidup di namespace-nya sendiri (disebut vendor namespace)
- Setiap namespace harus memiliki folder sendiri.
- Di dalam namespace mungkin ada subruang - juga di folder terpisah
- Satu kelas - satu file
- Nama file dengan ekstensi
.php
harus sama persis dengan nama kelas
Contoh dari manual:
Nama kelas lengkap | Namespace | Direktori basis | Jalan penuh |
---|
\ Acme \ Log \ Writer \ File_Writer | Acme \ Log \ Writer | ./acme-log-writer/lib/ | ./acme-log-writer/lib/File_Writer.php |
\ Aura \ Web \ Response \ Status | Aura \ Web | / path / ke / aura-web / src / | /path/to/aura-web/src/Response/Status.php |
\ Symfony \ Core \ Request | Symfony \ core | ./vendor/Symfony/Core/ | ./vendor/Symfony/Core/Request.php |
\ Zend \ Acl | Zend | / 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:
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.
TugasTulis 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.