Referensi: Apa itu lingkup variabel, variabel mana yang dapat diakses dari mana dan apa kesalahan "variabel tidak terdefinisi"?

167

Catatan: Ini adalah pertanyaan referensi untuk berurusan dengan ruang lingkup variabel dalam PHP. Harap tutup salah satu dari banyak pertanyaan yang cocok dengan pola ini sebagai duplikat dari yang ini.

Apa itu "ruang lingkup variabel" di PHP? Apakah variabel dari satu file .php dapat diakses di yang lain? Mengapa saya terkadang mendapatkan kesalahan "variabel tidak terdefinisi" ?

tipuan
sumber
1
Jika Anda memberi judul ini sebagai "Variabel tidak terdefinisi", Anda akan mendapatkan lebih banyak hit :) pekerjaan yang bagus
Dale
@Dale, Sebenarnya tidak. 2k pemandangan dalam 2 tahun adalah ....
Pacerier
7
@Pacerier ... tentang waktu yang tepat untuk meninggalkan komentar acak?
Dale
@Pacerier Saya benar-benar tidak yakin apa yang Anda katakan dengan komentar itu. "Apakah ...." ... apa ?! : P
deceze
@Dale, Sekarang adalah waktu yang tepat: wow meskipun pertanyaannya mandek selama 2 tahun, setelah kata " stateless " ditambahkan ke GoogleDex-nya, hit rate-nya 3x-ed hanya dalam 6 bulan.
Pacerier

Jawaban:

188

Apa itu "ruang lingkup variabel"?

Variabel memiliki "ruang lingkup" terbatas, atau "tempat dari mana mereka dapat diakses". Hanya karena Anda $foo = 'bar';pernah menulis di suatu tempat di aplikasi Anda tidak berarti Anda dapat merujuk $foodari mana saja di dalam aplikasi. Variabel $foomemiliki ruang lingkup tertentu yang valid dan hanya kode dalam lingkup yang sama memiliki akses ke variabel.

Bagaimana ruang lingkup didefinisikan dalam PHP?

Sangat sederhana: PHP memiliki lingkup fungsi . Itulah satu-satunya jenis pemisah lingkup yang ada di PHP. Variabel di dalam suatu fungsi hanya tersedia di dalam fungsi itu. Variabel di luar fungsi tersedia di mana saja di luar fungsi, tetapi tidak di dalam fungsi apa pun. Ini berarti ada satu ruang lingkup khusus dalam PHP: ruang lingkup global . Variabel apa pun yang dinyatakan di luar fungsi apa pun berada dalam lingkup global ini.

Contoh:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;
}

$fooberada dalam lingkup global , $bazberada dalam lingkup lokal di dalamnya myFunc. Hanya kode di dalam yang myFuncmemiliki akses $baz. Hanya kode di luar yang myFunc memiliki akses $foo. Tidak ada yang memiliki akses ke yang lain:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;

    echo $foo;  // doesn't work
    echo $baz;  // works
}

echo $foo;  // works
echo $baz;  // doesn't work

Cakupan dan file yang disertakan

Batas file tidak memisahkan ruang lingkup:

a.php

<?php

$foo = 'bar';

b.php

<?php

include 'a.php';

echo $foo;  // works!

Aturan yang sama berlaku untuk includekode d sebagaimana berlaku untuk kode lain: hanya functionruang lingkup yang terpisah. Untuk tujuan cakupan, Anda mungkin berpikir untuk memasukkan file seperti menyalin dan menempelkan kode:

cph

<?php

function myFunc() {
    include 'a.php';

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!

Dalam contoh di atas, a.phpdimasukkan ke dalam myFunc, variabel apa pun di dalam a.phphanya memiliki lingkup fungsi lokal. Hanya karena mereka tampaknya berada dalam lingkup global a.phptidak berarti mereka berada, itu sebenarnya tergantung pada konteks mana kode dimasukkan / dijalankan.

Bagaimana dengan fungsi di dalam fungsi dan kelas?

Setiap functiondeklarasi baru memperkenalkan ruang lingkup baru, sesederhana itu.

(anonim) fungsi di dalam fungsi

function foo() {
    $foo = 'bar';

    $bar = function () {
        // no access to $foo
        $baz = 'baz';
    };

    // no access to $baz
}

kelas

$foo = 'foo';

class Bar {

    public function baz() {
        // no access to $foo
        $baz = 'baz';
    }

}

// no access to $baz

Untuk apa ruang lingkup?

Menangani masalah pelingkupan mungkin tampak menjengkelkan, tetapi ruang lingkup variabel terbatas sangat penting untuk menulis aplikasi yang kompleks! Jika setiap variabel yang Anda deklarasikan akan tersedia dari mana saja di dalam aplikasi Anda, Anda akan melangkahi semua variabel Anda tanpa cara nyata untuk melacak apa yang mengubah apa. Hanya ada begitu banyak nama masuk akal yang dapat Anda berikan kepada variabel Anda, Anda mungkin ingin menggunakan variabel " $name" di lebih dari satu tempat. Jika Anda hanya dapat memiliki nama variabel yang unik ini sekali di aplikasi Anda, Anda harus menggunakan skema penamaan yang sangat rumit untuk memastikan variabel Anda unik dan bahwa Anda tidak mengubah variabel yang salah dari potongan kode yang salah.

Mengamati:

function foo() {
    echo $bar;
}

Jika tidak ada ruang lingkup, apa fungsi fungsi di atas? Dari mana datangnya $bar? Status apa yang dimilikinya? Apakah bahkan diinisialisasi? Apakah Anda harus memeriksa setiap waktu? Ini tidak bisa dipertahankan. Yang membawa kita ke ...

Melintasi batas ruang lingkup

Cara yang benar: meneruskan variabel masuk dan keluar

function foo($bar) {
    echo $bar;
    return 42;
}

Variabel $barsecara eksplisit masuk ke lingkup ini sebagai argumen fungsi. Hanya dengan melihat fungsi ini, jelas dari mana nilai itu bekerja. Kemudian secara eksplisit mengembalikan nilai. Penelepon memiliki kepercayaan diri untuk mengetahui variabel apa yang akan berfungsi dengan fungsi dan dari mana nilai pengembaliannya berasal:

$baz   = 'baz';
$blarg = foo($baz);

Memperluas lingkup variabel menjadi fungsi anonim

$foo = 'bar';

$baz = function () use ($foo) {
    echo $foo;
};

$baz();

Fungsi anonim secara eksplisit termasuk $foodari ruang lingkup di sekitarnya. Perhatikan bahwa ini tidak sama dengan ruang lingkup global .

Jalan yang salah: global

Seperti yang dikatakan sebelumnya, ruang lingkup global agak spesial, dan fungsi dapat secara eksplisit mengimpor variabel dari itu:

$foo = 'bar';

function baz() {
    global $foo;
    echo $foo;
    $foo = 'baz';
}

Fungsi ini menggunakan dan memodifikasi variabel global $foo. Jangan lakukan ini! (Kecuali jika Anda benar-benar benar-benar benar-benar tahu apa yang Anda lakukan, dan bahkan kemudian: jangan!)

Semua penelepon dari fungsi ini melihat ini:

baz(); // outputs "bar"
unset($foo);
baz(); // no output, WTF?!
baz(); // outputs "baz", WTF?!?!!

Tidak ada indikasi bahwa fungsi ini memiliki efek samping , namun tetap berfungsi . Ini sangat mudah menjadi kekacauan yang kusut karena beberapa fungsi terus memodifikasi dan membutuhkan beberapa negara global. Anda ingin fungsi menjadi stateless , hanya bekerja pada inputnya dan mengembalikan output yang ditentukan, namun sering kali Anda memanggilnya.

Anda harus menghindari penggunaan ruang lingkup global dengan cara apa pun sebanyak mungkin; pastinya Anda tidak harus "menarik" variabel dari lingkup global ke lingkup lokal.

tipuan
sumber
Anda baru saja mengatakan cara yang salahglobal , jadi tolong beri tahu kami kapan kami harus menggunakan global? Dan tolong jelaskan (sedikit) apa itu static..?
@ tumpukan Tidak ada cara "benar" untuk global. Itu selalu salah. Melewati parameter fungsi benar. staticdijelaskan dengan baik dalam manual dan tidak ada hubungannya dengan ruang lingkup. Singkatnya dapat dianggap sebagai "variabel global yang dicakup". Saya sedikit memperluas penggunaannya di sini kunststube.net/static .
tipuan
Pemikiran sederhana saya adalah jika variabel php cukup penting untuk mendapatkan status global, itu layak kolom dalam database. Mungkin ini adalah pembunuhan yang berlebihan, tetapi ini adalah pendekatan yang sangat mudah untuk dilakukan yang cocok dengan pemrograman saya yang biasa-biasa saja
Arthur Tarasov
@Arthur Ada begitu banyak yang harus dibongkar di sana ... ಠ_ಠ Ini pasti bukan pendekatan yang akan saya sarankan.
deceze
@menerima wee sedikit argumen yang terjadi di sini hari ini stackoverflow.com/q/51409392 - di mana OP menyebutkan bahwa duplikat (di sini) tidak menyebutkan tentang include_oncedan mungkin require_oncejuga harus ditambahkan di suatu tempat; hanya mengatakan. OP juga memilih untuk membuka kembali pertanyaan mereka. Apakah posting mereka menjadi kasus khusus dan apa yang harus dilakukan?
Funk Forty Niner
10

Meskipun variabel yang didefinisikan di dalam lingkup fungsi tidak dapat diakses dari luar, itu tidak berarti Anda tidak dapat menggunakan nilainya setelah fungsi tersebut selesai. PHP memiliki statickata kunci terkenal yang banyak digunakan dalam PHP berorientasi objek untuk mendefinisikan metode dan properti statis tetapi harus diingat bahwa staticmungkin juga digunakan di dalam fungsi untuk mendefinisikan variabel statis.

Apa itu 'variabel statis'?

Variabel statis berbeda dari variabel biasa yang didefinisikan dalam lingkup fungsi jika tidak kehilangan nilai ketika eksekusi program meninggalkan ruang lingkup ini. Mari kita perhatikan contoh penggunaan variabel statis berikut:

function countSheep($num) {
 static $counter = 0;
 $counter += $num;
 echo "$counter sheep jumped over fence";
}

countSheep(1);
countSheep(2);
countSheep(3);

Hasil:

1 sheep jumped over fence
3 sheep jumped over fence
6 sheep jumped over fence

Jika kita mendefinisikan $countertanpa staticmaka setiap kali nilai gema akan sama dengan $numparameter yang diteruskan ke fungsi. Menggunakan staticmemungkinkan untuk membangun penghitung sederhana ini tanpa solusi tambahan.

Variabel statis use-cases

  1. Untuk menyimpan nilai antara panggilan konsekuen ke fungsi.
  2. Untuk menyimpan nilai antara panggilan rekursif ketika tidak ada cara (atau tidak ada tujuan) untuk meneruskannya sebagai params.
  3. Untuk nilai cache yang biasanya lebih baik untuk mengambil sekali. Misalnya, hasil membaca file yang tidak dapat diubah di server.

Trik

Variabel statis hanya ada dalam lingkup fungsi lokal. Ini tidak dapat diakses di luar fungsi yang telah ditetapkan. Jadi, Anda mungkin yakin bahwa nilainya tidak akan berubah hingga panggilan berikutnya ke fungsi tersebut.

Variabel statis hanya dapat didefinisikan sebagai skalar atau sebagai ekspresi skalar (sejak PHP 5.6). Menetapkan nilai-nilai lain padanya pasti menyebabkan kegagalan setidaknya pada saat artikel ini ditulis. Namun demikian Anda dapat melakukannya hanya di baris kode berikutnya:

function countSheep($num) {
  static $counter = 0;
  $counter += sqrt($num);//imagine we need to take root of our sheep each time
  echo "$counter sheep jumped over fence";
}

Hasil:

2 sheep jumped over fence
5 sheep jumped over fence
9 sheep jumped over fence

Fungsi statis agak 'dibagi' antara metode objek dari kelas yang sama. Mudah dimengerti dengan melihat contoh berikut:

class SomeClass {
  public function foo() {
    static $x = 0;
    echo ++$x;
  }
}

$object1 = new SomeClass;
$object2 = new SomeClass;

$object1->foo(); // 1
$object2->foo(); // 2 oops, $object2 uses the same static $x as $object1
$object1->foo(); // 3 now $object1 increments $x
$object2->foo(); // 4 and now his twin brother

Ini hanya berfungsi dengan objek dari kelas yang sama. Jika objek berasal dari kelas yang berbeda (bahkan saling memperluas satu sama lain) perilaku statis statis akan seperti yang diharapkan.

Apakah variabel statis satu-satunya cara untuk menjaga nilai antara panggilan ke suatu fungsi?

Cara lain untuk menjaga nilai antara panggilan fungsi adalah dengan menggunakan penutupan. Penutupan diperkenalkan di PHP 5.3. Dalam dua kata mereka memungkinkan Anda untuk membatasi akses ke beberapa set variabel dalam lingkup fungsi ke fungsi anonim lain yang akan menjadi satu-satunya cara untuk mengaksesnya. Berada dalam variabel penutupan dapat meniru (kurang lebih berhasil) konsep OOP seperti 'konstanta kelas' (jika mereka lulus dalam nilai penutupan) atau 'properti pribadi' (jika disahkan oleh referensi) dalam pemrograman terstruktur.

Yang terakhir ini sebenarnya memungkinkan untuk menggunakan closure daripada variabel statis. Apa yang harus digunakan selalu tergantung pada pengembang untuk memutuskan tetapi harus disebutkan bahwa variabel statis pasti berguna ketika bekerja dengan rekursi dan layak untuk diperhatikan oleh devs.

Alex Myznikov
sumber
2

Saya tidak akan memposting jawaban yang lengkap untuk pertanyaan, karena yang sudah ada dan manual PHP melakukan pekerjaan yang baik untuk menjelaskan sebagian besar dari ini.

Tapi satu subjek yang tidak terjawab adalah bahwa dari superglobals , termasuk yang umum digunakan $_POST, $_GET, $_SESSION, dll Variabel ini array yang selalu tersedia, dalam lingkup apapun, tanpa globaldeklarasi.

Misalnya, fungsi ini akan mencetak nama pengguna yang menjalankan skrip PHP. Variabel tersedia untuk fungsi tanpa masalah.

<?php
function test() {
    echo $_ENV["user"];
}

Aturan umum "global buruk" biasanya diubah dalam PHP menjadi "global buruk tetapi superglobals baik-baik saja," selama seseorang tidak menyalahgunakannya. (Semua variabel ini dapat ditulisi, sehingga mereka dapat digunakan untuk menghindari injeksi ketergantungan jika Anda benar-benar mengerikan.)

Variabel-variabel ini tidak dijamin hadir; seorang administrator dapat menonaktifkan beberapa atau semuanya menggunakan variables_orderarahan dalam php.ini, tetapi ini bukan perilaku umum.


Daftar superglobals saat ini:

  • $GLOBALS - Semua variabel global dalam skrip saat ini
  • $_SERVER - Informasi tentang server dan lingkungan eksekusi
  • $_GET - Nilai yang dikirimkan dalam string kueri URL, terlepas dari metode HTTP yang digunakan untuk permintaan
  • $_POST- Nilai yang dikirimkan dalam permintaan HTTP POST dengan application/x-www-form-urlencodedatau multipart/form-datatipe MIME
  • $_FILES- File yang dikirimkan dalam permintaan POST HTTP dengan multipart/form-datatipe MIME
  • $_COOKIE - Cookie berlalu dengan permintaan saat ini
  • $_SESSION - Variabel sesi disimpan secara internal oleh PHP
  • $_REQUEST- Biasanya kombinasi dari $_GETdan $_POST, tetapi kadang-kadang $_COOKIES. Konten ditentukan oleh request_orderarahan dalam php.ini.
  • $_ENV - Variabel lingkungan dari skrip saat ini
miken32
sumber