Считаются ли глобальные переменные в PHP плохой практикой? Если да, то почему?

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

В своих небольших PHP-проектах я обычно использую процедурный путь. Обычно у меня есть переменная, содержащая конфигурацию системы, и когда мне нужно получить доступ к этой переменной в функции, я делаю это global $var;.

Это плохая практика?

KRTac
источник
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 в чистый ООП-язык, наклеивая объектные модели поверх всего.

Как и все остальное, используйте «глобальные» переменные, процедурный код, конкретную структуру и ООП, потому что это имеет смысл, решает проблему, уменьшает объем кода, который вам нужно написать, или делает его более удобным в обслуживании и легким для понимания, а не потому, что вы думаете вам следует.

Cletus
источник
8
Следует отметить, что PHP 5.3 решает некоторые из этих проблем с помощью лямбда-функций, позволяющих избежать использования функции, объявленной в глобальной области видимости для обратных вызовов. +1 за поддерживаемый, читаемый совет по коду
Джонатан Фингланд,
Вы не можете использовать обратный вызов формы array ($obj, 'callbackMethod')при обращении к preg_replace_callback()? (Я знаю, я стал жертвой этой ловушки ООП ...)
гроссфогель 01
25
Вопрос не в том, «должны ли вообще использоваться глобальные переменные?». Ответом на это будет «иногда, если потребуется». Вопрос в том, это плохая практика. Ответ - «да, иногда». Для крошечного проекта плаката ничего плохого из этого не выйдет - однако для более крупных проектов с большим количеством членов команды и большим количеством движущихся частей интенсивное использование глобальных переменных затруднит отладку кода, сделает его практически невозможным для рефакторинга и даже затруднит его выполнение. читать. Можете ли вы использовать их иногда, .. конечно - они кина отстой, .. да!
eddiemoya
@eddiemoya Хорошо сказал Эдди. Так много людей оправдывают такие плохие практики, как использование глобальных переменных. Вам следует избегать их, как чумы. Любая достойная степень в области разработки программного обеспечения проникнет в вас ... лекторы не просто говорят вам, черт возьми ... они знают это из многолетнего опыта. По возможности вы должны использовать функции-члены для доступа к нужным вам значениям, например get_query_var () в Wordpress и т. Д.
27

При неправильном использовании глобальные переменные могут затруднить поиск проблем. Допустим, вы запрашиваете скрипт php и получаете предупреждение о том, что вы пытаетесь получить доступ к индексу массива, который не существует в какой-либо функции.

Если массив, к которому вы пытаетесь получить доступ, является локальным для функции, вы проверяете функцию, чтобы увидеть, не допустили ли вы там ошибки. Это может быть проблема с вводом в функцию, поэтому вы проверяете места, где функция вызывается.

Но если этот массив является глобальным, вам нужно проверить все места, где вы используете эту глобальную переменную, и не только это, вы должны выяснить, в каком порядке осуществляется доступ к этим ссылкам на глобальную переменную.

Если у вас есть глобальная переменная в фрагменте кода, это затрудняет изолирование функциональности этого кода. Почему вы хотите изолировать функциональность? Так что вы можете протестировать его и повторно использовать в другом месте. Если у вас есть код, который не нужно тестировать и не нужно повторно использовать, тогда можно использовать глобальные переменные.

Рохока
источник
Но ошибка в основном показывает, в каком файле / строке скрипт ломается, поэтому я не вижу здесь проблемы
samayo
8
Место, где скрипт сломался! = Место, где была сделана ошибка.
HonoredMule
16

я согласен с cletus. я бы добавил две вещи:

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

С уважением, Дон

Дон Дикинсон
источник
1
Да, я всегда префикс переменных, которые собираюсь использовать глобально, с подчеркиванием.
KRTac
9
@KRTac, но $ _testVariable обычно понимается как частная переменная - это неформальный стандарт для определения частных переменных, а не глобальных.
Aditya MP
6
Распространенной практикой является определение глобальных переменных с использованием ВСЕХ ЗАГЛАВНЫХ букв. Пример:$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');вашей работе, поддержание такого кода становится экспоненциально сложнее, чем больше файлов вы добавляете.

Как нам избежать глобалов?

Лучший способ избежать глобальных переменных - это философия под названием Dependency Injection . Здесь мы передаем необходимые нам инструменты в функцию или класс.

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
Просто имейте в виду, что синглтоны считаются антипаттерном . Dependency Injection позволяет избежать этих ловушек
Machavity
2
В этом сообщении, на которое вы ссылаетесь, существует определенная степень разногласий (например, самый популярный комментарий к принятому ответу и второй по количеству голосов ответ категорически не согласен с принятым ответом), что заставляет меня утверждать, что использование синглтонов должно рассматриваться в индивидуальном порядке, а не отклоняться сразу же.
Скутс
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';

НЕ является ошибкой, hypens не будут конфликтовать с "общими" пользовательскими переменными, например $pagenow. И использование ВЕРХНЕГО регистра указывает на использование суперглобала, который легко обнаружить в коде или отследить с помощью поиска в файлах.

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

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

Но в случаях более широкого использования я использую ОДИН глобал как массив:

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

Последнее для меня является хорошей практикой в отношении целей или использования «колы-лайта» вместо беспорядка с одноэлементными классами каждый раз для «кэширования» некоторых данных. Пожалуйста, прокомментируйте, если я ошибаюсь или упускаю что-то глупое ...

Йонас Лундман
источник