Довідка: Що таке змінна область, які змінні доступні звідки і що таке "невизначені змінні" помилки?


167

Примітка. Це питання, що стосується довідки для роботи зі змінною сферою застосування в PHP. Будь ласка, закрийте будь-яке з багатьох питань, що відповідають цьому шаблону, як дублікат цього.

Що таке "змінна область" у PHP? Чи доступні змінні з одного .php-файлу в іншому? Чому іноді я отримую помилки "невизначеної змінної" ?


1
Якщо ви назвали це "Невизначеною змінною", ви отримаєте ще багато показів :) Хороша робота, хоча
Дейл

@Dale, насправді ні. 2k перегляди за 2 роки - це ....
Pacerier

7
@Pacerier ... про правильний час, щоб залишити випадковий коментар?
Дейл

@Pacerier Я дійсно не впевнений у тому, що ти намагаєшся сказати цим коментарем. "Є ...." ... що ?! : P
деге

@Dale, зараз правильний час: ок, навіть якщо питання застоювались протягом 2 років, після того, як слово " без громадянства " було додано до GoogleDex, його частота показів буквально 3-х разів лише за 6 місяців.
Pacerier

Відповіді:


188

Що таке "змінна область"?

Змінні мають обмежений "діапазон" або "місця, з яких вони доступні". Тільки тому, що ви $foo = 'bar';один раз писали десь у своїй заявці, це не означає, що на неї можна звертатися $fooз будь-якого іншого пункту програми. Змінна $fooмає певну область, в межах якої вона дійсна, і лише код у тій же області має доступ до змінної.

Як визначається область застосування в PHP?

Дуже просто: PHP має область функцій . Це єдиний вид сепаратора областей, який існує в PHP. Змінні всередині функції доступні лише всередині цієї функції. Змінні поза функціями доступні в будь-якому місці поза межами функцій, але не в межах будь-якої функції. Це означає, що в PHP є одна особлива сфера: глобальна область. Будь-яка змінна, оголошена поза будь-якою функцією, знаходиться в цьому глобальному масштабі.

Приклад:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;
}

$fooзнаходиться в глобальному масштабі, $bazзнаходиться в локальному масштабі всередині myFunc. Тільки код всередині myFuncмає доступ до $baz. Тільки код зовні myFunc має доступ до $foo. Жоден з них не має доступу до іншого:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;

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

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

Обсяг та файли, що входять у комплект

Межі файлів не розділяють область застосування:

a.php

<?php

$foo = 'bar';

b.php

<?php

include 'a.php';

echo $foo;  // works!

Для includeкоду d застосовуються ті самі правила, що і для будь-якого іншого коду: тільки functions окрема область застосування. Для сфери застосування ви можете подумати про включення таких файлів, як копія та вставлення коду:

c.php

<?php

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

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!

У наведеному вище прикладі a.phpбуло включено всередину myFunc, будь-які змінні всередині a.phpмають лише локальну область функцій. Тільки тому, що вони, схоже, знаходяться в глобальній області a.php, не обов'язково означають, що вони є, це насправді залежить від того, в який контекст цей код включений / виконаний.

Що з функціями всередині функцій та класів?

Кожна нова functionдекларація вводить нову сферу застосування, вона така проста.

(анонімні) функції всередині функцій

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

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

    // no access to $baz
}

заняття

$foo = 'foo';

class Bar {

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

}

// no access to $baz

Для чого корисний розмах?

Справа з питаннями визначення масштабів може здатися прикрою, але обмежена змінна сфера є важливою для написання складних додатків! Якщо кожна змінна, яку ви заявляєте, буде доступна звідусіль у вашій програмі, ви переходите до всіх своїх змінних, не маючи реального способу відстежувати, що змінюється. Є лише стільки розумних імен, які ви можете надати своїм змінним, імовірно, ви хочете використовувати змінну " $name" у більш ніж одному місці. Якщо ви могли б мати це унікальне ім’я змінної лише один раз у вашому додатку, вам доведеться вдатися до дійсно складних схем іменування, щоб переконатися, що ваші змінні є унікальними, і що ви не змінюєте неправильну змінну з неправильної частини коду.

Дотримуйтесь:

function foo() {
    echo $bar;
}

Якби не було сфери, що б робила вищезазначена функція? Звідки береться $bar? Який стан він має? Це навіть ініціалізовано? Чи потрібно перевіряти кожен раз? Це не піддається ремонту. Що приводить нас до ...

Перетинання меж рамки

Правильний шлях: передача змінних в і зовні

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

Змінна $barявно надходить у цю область як аргумент функції. Якщо подивитися на цю функцію, зрозуміло, звідки беруться значення, з якими вона працює. Потім явно повертає значення. Абонент може впевнено знати, з якими змінними функціонуватиме функція та звідки беруться її зворотні значення:

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

Розширення сфери застосування змінних на анонімні функції

$foo = 'bar';

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

$baz();

Анонімна функція явно включає в себе $fooїї оточуючу область. Зауважте, що це не те саме, що глобальна сфера.

Невірний шлях: global

Як було сказано раніше, глобальний обсяг є дещо особливим, і функції можуть явно імпортувати змінні з нього:

$foo = 'bar';

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

Ця функція використовує та змінює глобальну змінну $foo. Не роби цього! (Якщо тільки ви насправді не дуже-то знаєте, що робите, і навіть тоді: не робіть!)

Все, хто викликає цю функцію, бачить це:

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

Немає жодних ознак того, що ця функція має будь-які побічні ефекти , але вона є. Це дуже легко стає заплутаним безладом, оскільки деякі функції продовжують змінювати та вимагати деякого глобального стану. Ви хочете, щоб функції були без стану , діючи лише на їх входах і повертаючи заданий вихід, скільки б разів ви їх не викликали.

Вам слід уникати використання глобальної сфери якомога більше; напевно, вам не слід «витягувати» змінні з глобальної сфери в локальну область.


Ви просто сказали неправильний шляхglobal , тому, будь ласка, повідомте нам, коли нам користуватися global? І будь ласка, поясніть (трохи) що таке static?

@stack Не існує "правильного" способу для global. Це завжди неправильно. Параметри функції передачі правильні. staticце добре пояснено в посібнику і не має великого відношення до сфери застосування. Коротше кажучи, це можна розглядати як "загальну глобальну змінну". Я трохи розширюю його використання тут kunststube.net/static .
деге

Моя проста думка: якщо змінна php є достатньо важливою, щоб заслужити глобальний статус, вона заслуговує на колонку в базі даних. Можливо, це зайвий набір, але це нерозумний підхід, який відповідає моїй посередній дотепності програмування
Артур Тарасов

@Arthur Є так багато, щоб розпакувати там ... ಠ_ಠ Це, звичайно, не такий підхід, який я б схвалив.
деге

@deceze wee трохи аргументу, що відбувається сьогодні тут, stackoverflow.com/q/51409392 - де ОП зазначає, що дублікат (тут) не згадує про це include_onceі, можливо, require_onceйого також слід десь додати; просто кажу. ОП також проголосували за повторне відкриття свого питання. Чи буде їх посада окремим випадком і що з цим робити?
Funk Forty Niner

10

Хоча змінні, визначені всередині області функції, не можуть бути доступні зовні, це не означає, що ви не можете використовувати їх значення після завершення функції. PHP має добре відоме staticключове слово, яке широко використовується в об'єктно-орієнтованому PHP для визначення статичних методів та властивостей, але слід пам’ятати, що staticтакож можна використовувати всередині функцій для визначення статичних змінних.

Що це "статична змінна"?

Статична змінна відрізняється від звичайної змінної, визначеної в області функції, якщо вона не втрачає значення, коли виконання програми залишає цю область. Розглянемо наступний приклад використання статичних змінних:

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

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

Результат:

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

Якби ми визначилися $counterбез staticцього, кожне значення відлуння було б таким самим, як $numпараметр, переданий функції. Використання staticдозволяє створити цей простий лічильник без додаткового обходу.

Застосування статистичних змінних

  1. Збереження значень між послідовними дзвінками до функції.
  2. Зберігати значення між рекурсивними дзвінками, коли немає можливості (або немає мети) передавати їх як парами.
  3. Для кешування значення, яке, як правило, краще отримати один раз. Наприклад, результат зчитування незмінного файлу на сервері.

Витівки

Статична змінна існує лише в локальній області функцій. До нього не можна отримати доступ за межами функції, для якої було визначено. Отже, ви можете бути впевнені, що воно збереже його значення незмінним до наступного виклику цієї функції.

Статична змінна може бути визначена лише як скалярний або як скалярний вираз (починаючи з PHP 5.6). Присвоєння йому інших значень неминуче призводить до невдачі принаймні в той момент, коли ця стаття була написана. Тим не менш, ви можете це зробити лише в наступному рядку коду:

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";
}

Результат:

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

Статична функція свого роду «поділяється» між методами об'єктів одного класу. Це легко зрозуміти, переглянувши наступний приклад:

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

Це працює лише з об'єктами одного класу. Якщо об’єкти з різних класів (навіть розширення один одного), поведінка статичних змін буде такою, як очікувалося.

Чи є статична змінна єдиним способом зберігати значення між дзвінками до функції?

Ще один спосіб зберігати значення між викликами функцій - це використання закривань. Закриття було введено в PHP 5.3. У двох словах вони дозволяють обмежити доступ до деякого набору змінних у межах функції до іншої анонімної функції, яка буде єдиним способом доступу до них. Перебування в змінних закриття може імітувати (більш-менш успішно) такі поняття OOP, як "константи класу" (якщо вони були передані у закритті за значенням) або "приватні властивості" (якщо передаються за посиланням) в структурованому програмуванні.

Останнє фактично дозволяє використовувати закриття замість статичних змінних. Що використовувати, завжди вирішувати розробник, але слід зазначити, що статичні змінні, безумовно, корисні при роботі з рекурсіями і заслуговують на те, щоб їх помітили розробники.


2

Я не публікую повної відповіді на питання, оскільки існуючі та посібник із PHP чудово працюють із поясненням більшості цього.

Але одне питання , який був пропущений, що з суперглобальних , в тому числі широко використовуються $_POST, $_GET, $_SESSIONі т.д. Ці змінні є масиви, які доступні завжди, в будь-якій області, без globalдекларації.

Наприклад, ця функція буде роздруковувати ім'я користувача, який працює з сценарієм PHP. Змінна доступна для функції без проблем.

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

Загальне правило "глобальні люди погані", як правило, в PHP змінюється на "глобальні - це погано, але суперглобали - це добре", доки хто не зловживає ними. (Усі ці змінні підлягають запису, тому їх можна використовувати, щоб уникнути ін'єкції залежності, якщо ви справді жахливі.)

Ці змінні не гарантовано є; адміністратор може відключити деяких або всіх, використовуючи variables_orderдирективу в php.ini, але це не звичайна поведінка.


Список поточних суперглобалів:

  • $GLOBALS - Усі глобальні змінні в поточному сценарії
  • $_SERVER - Інформація про сервер та середовище виконання
  • $_GET - Значення, передані в рядку запиту URL-адреси, незалежно від методу HTTP, який використовується для запиту
  • $_POST- Значення, передані у запиті HTTP POST з типами application/x-www-form-urlencodedабо multipart/form-dataMIME
  • $_FILES- Файли передані в HTTP POST-запиті multipart/form-dataтипу MIME
  • $_COOKIE - Файли cookie передані з поточним запитом
  • $_SESSION - Змінні сесії, що зберігаються внутрішньо PHP
  • $_REQUEST- Зазвичай комбінація $_GETта $_POST, але іноді $_COOKIES. Зміст визначається request_orderдирективою в php.ini.
  • $_ENV - Змінні середовища поточного сценарію
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.