Чи вважаються глобальні змінні в PHP поганою практикою? Якщо так, то чому?


86
function foo () {
    global $var;
    // rest of code
}

У своїх невеликих PHP-проектах я зазвичай йду процедурним шляхом. Як правило, у мене є змінна, яка містить конфігурацію системи, і коли я потребую доступу до цієї змінної у функції, я це роблю global $var;.

Це погана практика?


19
глобальна змінна є синонімом поганої практики
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳


2
Спробуйте протестувати модуль / прийняття, і ви швидко зрозумієте, чому глобальні проблеми є проблемою: вони роблять ваш код ненадійним, коли ви робите речі більше одного разу.
Kzqai

Відповіді:


102

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

Часто (завжди?) Ви можете викликати функції-члени такими методами preg_replace_callback():

preg_replace_callback('!pattern!', array($obj, 'method'), $str);

Докладніше див. У розділі зворотних дзвінків .

Справа в тому, що об’єкти були закріплені на PHP болтами і певним чином призводять до незручності.

Не варто зайво займатися застосуванням стандартів або конструкцій з різних мов до PHP. Ще одна загальна помилка - спроба перетворити PHP на чисту мову ООП, наклеюючи поверх всього об’єктні моделі.

Як і будь-що інше, використовуйте "глобальні" змінні, процедурний код, певний фреймворк і ООП, оскільки це має сенс, вирішує проблему, зменшує кількість коду, який вам потрібно написати, або робить його більш зручним і легшим для розуміння, а не тому, що ви думаєте ти повинен.


8
Слід зазначити, що PHP 5.3 вирішує деякі з них за допомогою лямбда-функцій, що дозволяють уникнути використання функції, оголошеної в глобальній області для зворотних викликів. +1 для підтримуваної, читабельної поради щодо коду
Джонатан Фінгленд,

Чи не можете ви використовувати зворотний дзвінок форми array ($obj, 'callbackMethod')під час дзвінків до preg_replace_callback()? (Я знаю, я став жертвою цієї
підводної камені

24
Питання було не в тому, "чи слід коли-небудь використовувати глобальні змінні?". Відповідь на це була б, "обов'язково з нагоди, якщо потрібно". Питання в тому, чи є вони поганою практикою. Відповідь - так, інколи. Для плакатного крихітного проекту з цього не може вийти нічого поганого - однак для великих проектів з великою кількістю членів команди та безліччю рухомих частин інтенсивне використання глобальних змінних ускладнить налагодження коду, майже неможливе його рефакторинг і біль навіть читати. Чи можете ви іноді ними користуватися, .. звичайно - вони кіна смоктать, .. так!
eddiemoya

@eddiemoya Добре сказав Едді. Є так багато людей, які виправдовують погані практики, такі як використання глобальних змінних. Слід уникати їх, як чуми. Будь-який пристойний ступінь інженера-програміста навчить це вам ... лектори не просто говорять вам про біс ... вони знають з десятиліття досвіду. Вам слід використовувати функції членів, де це можливо, для доступу до значень, які вам потрібні, наприклад get_query_var () у Wordpress тощо

27

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

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

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

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


Але помилка здебільшого показує, в якому файлі / рядку сценарій
порушується,

8
Місце, де сценарій зламався! = Місце, де було допущено помилку.
ПочеснийMule

16

я згоден з клетусом. я додав би дві речі:

  1. використовуйте префікс, щоб ви могли негайно визначити його як глобальний (наприклад, $ g_)
  2. заявляйте про них в одному місці, не розбризкуйте їх по коду.

З найкращими побажаннями, доне


1
Так, я завжди додаю до змінних, які я маю намір використовувати в усьому світі, з підкресленням.
KRTac

9
@KRTac, але під $ _testVariable зазвичай розуміють приватну змінну - це неформальний стандарт для визначення приватних змінних, а не глобальних.
Aditya MP

6
Типовою практикою є визначення глобальних варів за допомогою ALL CAPS. приклад:$DB = 'foo';
pixeline

7

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

Може бути погано використовувати глобальні змінні, навіть якщо вони використовуються лише в глобальній області програми, але не будемо забувати про тих, хто просто хоче розважитися і змусити щось працювати .

Якщо це означає використання кількох змінних (<10) у глобальному просторі імен, які використовуються лише у глобальній області програми, нехай буде так. Так, так, MVC, введення залежностей, зовнішній код, бла, бла, бла, бла. Але якщо ви вмістили 99,99% коду у простори імен та класи, а зовнішній код укладено в середовище, світ не закінчиться (повторюю, світ не закінчиться), якщо ви використовуєте глобальну змінну.

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

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

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


3

Опубліковано з завершеної бета-документації SO

Ми можемо проілюструвати цю проблему наступним псевдокодом

function foo() {
     global $bob;
     $bob->doSomething();
}

Ваше перше запитання тут очевидне

Звідки $bobвзявся?

Ви розгублені? Добре. Ви щойно дізналися, чому глобальний світ заплутаний і вважається поганою практикою. Якби це була справжня програма, наступним задоволенням буде відстежити всі екземпляри $bobта сподіватися, що ви знайдете потрібну (це погіршується, якщо $bobїї використовувати всюди). Гірше того, якщо хтось інший піде і визначить $bob(або ви забули та повторно використали цю змінну), ваш код може зламатися (у наведеному вище прикладі коду наявність неправильного об'єкта або його відсутність може спричинити фатальну помилку). Оскільки практично всі програми PHP використовують такий код, як include('file.php');ваш код, який підтримує роботу, як це стає експоненціально складніше, чим більше файлів ви додаєте.

Як нам уникнути глобалів?

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

function foo(\Bar $bob) {
    $bob->doSomething();
}

Це набагато легше зрозуміти та підтримати. Немає здогадок, де це $bobбуло встановлено, оскільки абонент відповідає за те, що це знає (він передає нам те, що нам потрібно знати). А ще краще, ми можемо використовувати декларації типу, щоб обмежити те, що передається. Отже, ми знаємо, що $bobце або екземпляр Barкласу, або екземпляр дочірнього елемента Bar, тобто ми знаємо, що можемо використовувати методи цього класу. У поєднанні зі стандартним автозавантажувачем (доступний з PHP 5.3), тепер ми можемо відстежувати, де Barце визначено. PHP 7.0 або новішої версії включає розширені декларації типів, де ви також можете використовувати скалярні типи (наприклад, intабо string).


Альтернативою необхідності передавати $ bob скрізь є зробити клас Bar єдиним, зберігаючи екземпляр Bar статично всередині самого Bar і використовуючи статичний метод для створення екземпляра / отримання об'єкта. Тоді ви можете просто, $bob = Bar::instance();коли вам це потрібно.
Scoots

1
Тільки майте на увазі, що Сінглтон вважається анти-шаблоном . Ін’єкція залежності уникає цих підводних каменів
Machavity

2
У цій публікації є певна суперечка (наприклад, найкращий коментар до прийнятої відповіді та друга найбільш прихильна відповідь - категорично не згодні з прийнятою відповіддю), що змусило мене стверджувати, що використання Singleton має розглядати в кожному конкретному випадку, а не відмовлятись від короткого звільнення.
Scoots

0

Як:

global $my_global; 
$my_global = 'Transport me between functions';
Equals $GLOBALS['my_global']

це погана практика (як Wordpress $pagenow) ... ммм

Прийміть це:

$my-global = 'Transport me between functions';

- помилка PHP але:

$GLOBALS['my-global'] = 'Transport me between functions';

НЕ є помилкою, хипени не зіткнуться із "загальними" змінами, оголошеними користувачем, наприклад $pagenow. А використання UPPERCASE вказує на суперглобаль у використанні, який легко виявити в коді або відстежити за допомогою пошуку у файлах

Я використовую дефіси, якщо мені лінь будувати класи всього для одного рішення, наприклад:

$GLOBALS['PREFIX-MY-GLOBAL'] = 'Transport me ... ';

Але у випадках більш широкого використання я використовую ОДИН глобальний список як масив:

$GLOBALS['PREFIX-MY-GLOBAL']['context-something'] = 'Transport me ... ';
$GLOBALS['PREFIX-MY-GLOBAL']['context-something-else']['numbers'][] = 'Transport me ... ';

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.