PHP глобальный в функциях


101

Какая польза от глобального ключевого слова ?

Есть ли причины предпочесть один метод другому?

  • Безопасность?
  • Производительность?
  • Что-нибудь еще?

Способ 1:

function exempleConcat($str1, $str2)
{
  return $str1.$str2;
}

Способ 2:

function exempleConcat()
{
  global $str1, $str2;
  return $str1.$str2;
}

Когда есть смысл использовать global?

Для меня это кажется опасным ... но это может быть просто недостаток знаний. Меня интересуют задокументированные (например, с примером кода, ссылка на документацию ...) технические причины.

Заранее спасибо!


Щедрость

Это хороший общий вопрос по теме, я (@Gordon) предлагаю вознаграждение за дополнительные ответы. Согласуется ли ваш ответ с моим или дает другую точку зрения, не имеет значения. Поскольку эта globalтема возникает время от времени, мы могли бы использовать хороший «канонический» ответ для ссылки.


2
взгляните на эту ссылку: stackoverflow.com/questions/1557787 В правом нижнем углу этой страницы есть много связанных статей
JohnP 02

Это не прямой ответ на ваш вопрос, но, пожалуйста, прочтите этот старый вопрос SO .
Ólafur Waage

1
Итак, я не могу прочитать ни одного проглобального ключевого слова. 1) Почему это здесь. 2) Почему люди его используют?
Pascal Qyy 05

@ G.Qyy Почему там goto? Почему люди его используют? Они не используют его (я надеюсь, по крайней мере): P
PeeHaa

В конце прошлого года (14 декабря) кто-то проголосовал против этого вопроса. Мне очень интересно узнать, почему, потому что все точки зрения, в том числе и негативные, интересны. В этом случае как никогда! Я буду очень благодарен за любую подсказку об этом.
Паскаль Qyy

Ответы:


159

Глобалы злы

Это верно как для globalключевого слова, так и для всего остального, что простирается от локальной до глобальной (статика, синглтоны, реестры, константы). Вы не хотите их использовать. Вызов функции не должен полагаться ни на что извне, например

function fn()
{
    global $foo;              // never ever use that
    $a = SOME_CONSTANT        // do not use that
    $b = Foo::SOME_CONSTANT;  // do not use that unless self::
    $c = $GLOBALS['foo'];     // incl. any other superglobal ($_GET, …)
    $d = Foo::bar();          // any static call, incl. Singletons and Registries
}

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

Использование суперглобальных переменных может не быть очевидным недостатком, но если вы вызываете свой код из командной строки, у вас нет $_GETили $_POST. Если ваш код полагается на ввод от них, вы ограничиваетесь веб-средой. Просто абстрагируйте запрос до объекта и используйте его.

В случае связывания жестко запрограммированных имен классов (статических, констант) ваша функция также не может существовать без того, чтобы этот класс был доступен. Это меньшая проблема, когда это классы из одного и того же пространства имен, но когда вы начинаете микширование из разных пространств имен, вы создаете запутанный беспорядок.

Повторное использование серьезно затруднено из-за всего вышеперечисленного. Так что модульное тестирование .

Кроме того, ваши сигнатуры функций лгут, когда вы подключаетесь к глобальной области видимости

function fn()

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

Если вашей функции требуются аргументы для запуска, сделайте их явными и передайте:

function fn($arg1, $arg2)
{
    // do sth with $arguments
}

из подписи ясно передает, что он требует называть. Пребывание в определенном состоянии не зависит от окружающей среды. Тебе не нужно делать

$arg1 = 'foo';
$arg2 = 'bar';
fn();

Это вопрос втягивания (глобальное ключевое слово) против нажатия (аргументов). Когда вы вставляете / вводите зависимости, функция больше не полагается на внешнее. Когда вы это сделаете, fn(1)вам не нужно иметь переменную, содержащую 1 где-то снаружи. Но когда вы втягиваете global $oneвнутри функции, вы присоединяетесь к глобальной области видимости и ожидаете, что где-то будет определена переменная этого типа. Тогда функция больше не является независимой.

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

Если не найти лучшего примера, рассмотрим

function fn()
{
    global $foo;
    echo $foo;     // side effect: echo'ing
    $foo = 'bar';  // side effect: changing
}

И тогда вы делаете

$foo = 'foo';
fn(); // prints foo
fn(); // prints bar <-- WTF!!

Невозможно увидеть, что $fooизменилось из этих трех строк. Почему вызов одной и той же функции с одними и теми же аргументами может внезапно изменить ее вывод или изменить значение в глобальном состоянии? Функция должна выполнять X для определенного входа Y. Всегда.

Это становится еще более серьезным при использовании ООП, потому что ООП касается инкапсуляции и, обращаясь к глобальной области видимости, вы нарушаете инкапсуляцию. Все эти синглтоны и реестры, которые вы видите во фреймворках, представляют собой запахи кода, которые следует удалить в пользу внедрения зависимостей. Отделите свой код.

Дополнительные ресурсы:


10
Почему PHP реализует такие вещи? Есть утилита? Меня всегда удивляли опасные реализации на PHP, которые многие люди используют каждый раз ... Мне трудно поверить, что нет никаких логических причин!
Pascal Qyy 05

6
Я хотел бы, чтобы вы могли сделать Глобалы злом больше.
Кермит

3
Вау, наконец-то кто-то хорошо объяснил, почему глобальные переменные - это зло ... Я всегда слышал, что это так, и видел несколько очень конкретных примеров того, почему, но это действительно хорошее и исчерпывающее объяснение общей причины. +1
Wingblade

Я действительно опоздал, и я вроде как понимаю, что вы говорите, но как насчет соединений mysqli? Должны ли они передаваться каждый раз в качестве параметра или являются глобальной ссылкой $; разрешено в твоих глазах?
мэйв

2
Вы правы, кроме констант. Они не представляют «состояние» приложения, и на них можно ссылаться из функции. Функция не «лжет», если использует константу изнутри. Я согласен с тем, что это подразумевает, что программист в какой-то момент знал о внешнем, но это очень приемлемый компромисс для того, что такое константа. Кроме того, серьезно, это не такая уж большая проблема.
Себас,

35

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

$str1 = 'foo';
$str2 = 'bar';
$str3 = exampleConcat();

vs.

$str = exampleConcat('foo', 'bar');

Требование $str1и $str2настройка в области вызова для работы функции означает, что вы вводите ненужные зависимости. Вы больше не можете переименовывать эти переменные в этой области, не переименовывая их также и в самой функции, а значит, и во всех других областях, в которых вы используете эту функцию. Вскоре это превращается в хаос, когда вы пытаетесь отслеживать имена переменных.

globalплохой шаблон даже для включения глобальных вещей, таких как $dbресурсы. Там будет наступит день , когда вы хотите переименовать , $dbно не может, потому что все ваше приложение зависит от имени.

Ограничение и разделение области переменных необходимо для написания любого полусложного приложения.


1
Извините, но зачем мне обязательно переименовывать $db? Это PDO, распространенный повсюду. Зачем его менять, если я могу обновлять информацию о подключении отдельно?
Кейси Дуэйн

3
@kcd Потому что однажды вы поймете, насколько прекрасна инъекция зависимостей, и захотите реструктурировать свое приложение? Потому что однажды вам нужно будет интегрировать свой материал с каким-то другим, в котором также используется глобальная $dbпеременная? Потому что однажды вы обнаружите модульное тестирование, и для этого вам нужно будет управлять более чем одним подключением к базе данных одновременно? Много-много причин.
deceze

35

Глобалы неизбежны.

Это старое обсуждение, но я все же хотел бы добавить некоторые мысли, потому что мне их не хватает в вышеупомянутых ответах. Эти ответы упрощают то, что такое глобальное, и представляют решения, которые вовсе не являются решениями проблемы. Проблема в том, как правильно работать с глобальной переменной и использовать ключевое слово global? Для этого мы сначала должны изучить и описать, что такое глобальный.

Взгляните на этот код Zend - и, пожалуйста, поймите, что я не считаю, что Zend написан плохо:

class DecoratorPluginManager extends AbstractPluginManager
{
/**
 * Default set of decorators
 *
 * @var array
 */
protected $invokableClasses = array(
    'htmlcloud' => 'Zend\Tag\Cloud\Decorator\HtmlCloud',
    'htmltag'   => 'Zend\Tag\Cloud\Decorator\HtmlTag',
    'tag'       => 'Zend\Tag\Cloud\Decorator\HtmlTag',
   );

Здесь много невидимых зависимостей. Эти константы на самом деле являются классами. Вы также можете увидеть require_once на некоторых страницах этого фреймворка. Require_once - это глобальная зависимость, следовательно, создаются внешние зависимости. Это неизбежно для фреймворка. Как можно создать такой класс, как DecoratorPluginManager, без большого количества внешнего кода, от которого он зависит? Он не может функционировать без множества дополнений. Вы когда-нибудь меняли реализацию интерфейса, используя Zend framework? Интерфейс на самом деле глобальный.

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

/**
 * @file
 * Initiates a browser-based installation of Drupal.
 */

/**
 * Root directory of Drupal installation.
 */
define('DRUPAL_ROOT', getcwd());

/**
 * Global flag to indicate that site is in installation mode.
 */
define('MAINTENANCE_MODE', 'install');

// Exit early if running an incompatible PHP version to avoid fatal errors.
if (version_compare(PHP_VERSION, '5.2.4') < 0) {
  print 'Your PHP installation is too old. Drupal requires at least PHP 5.2.4. See the     <a     href="http://drupal.org/requirements">system requirements</a> page for more     information.';
  exit;
}

// Start the installer.
require_once DRUPAL_ROOT . '/includes/install.core.inc';
install_drupal();

Вы когда-нибудь писали перенаправление на страницу входа? Это меняет глобальную ценность. (И тогда вы не говорите «WTF», что я считаю хорошей реакцией на плохую документацию вашего приложения.) Проблема с глобальными переменными не в том, что они глобальные, они нужны вам для того, чтобы иметь значимое приложение. Проблема заключается в сложности приложения в целом, с которым может быть просто кошмарно работать. Сеансы являются глобальными, $ _POST - глобальными, DRUPAL_ROOT - глобальными, include / install.core.inc '- неизменяемыми глобальными. За пределами любой функции существует большой мир, который требуется для того, чтобы эта функция выполняла свою работу.

Ответ Гордона неверен, потому что он переоценивает независимость функции и называя функцию лжецом, упрощает ситуацию. Функции не лгут, и когда вы посмотрите на его пример, функция спроектирована неправильно - его пример - ошибка. (Между прочим, я согласен с выводом о том, что код следует разделять.) Ответ обмана - это не совсем правильное определение ситуации. Функции всегда работают в более широком объеме, и его пример слишком упрощен. Мы все согласимся с ним в том, что эта функция совершенно бесполезна, потому что она возвращает константу. В любом случае эта функция - плохой дизайн. Если вы хотите показать, что практика плохая, приведите соответствующий пример. Переименование переменных в приложении - не проблема, если у вас есть хорошая IDE (или инструмент). Вопрос в области видимости переменной, а не в ее разнице с функцией. Существует подходящее время для того, чтобы функция выполняла свою роль в процессе (именно поэтому она создается в первую очередь), и в этот надлежащий момент она может влиять на функционирование приложения в целом, а значит, также работать с глобальными переменными. . Ответ xzyfer - утверждение без аргументов. Глобальные объекты также присутствуют в приложении, если у вас есть процедурные функции или дизайн ООП. Следующие два способа изменить значение глобального по существу одинаковы: следовательно, мы также работаем с глобальными переменными. Ответ xzyfer - утверждение без аргументов. Глобальные объекты также присутствуют в приложении, если у вас есть процедурные функции или дизайн ООП. Следующие два способа изменить значение глобального по существу одинаковы: следовательно, мы также работаем с глобальными переменными. Ответ xzyfer - утверждение без аргументов. Глобальные объекты также присутствуют в приложении, если у вас есть процедурные функции или дизайн ООП. Следующие два способа изменить значение глобального по существу одинаковы:

function xzy($var){
 global $z;
 $z = $var;
}

function setZ($var){
 $this->z = $var;
}

В обоих случаях значение $ z изменяется в рамках определенной функции. При обоих способах программирования вы можете вносить эти изменения в кучу других мест кода. Можно сказать, что с помощью global вы можете вызвать $ z где угодно и изменить там. Да, ты можешь. Но ты будешь? И если это сделано в неподходящих местах, не следует ли тогда называть это ошибкой?

Боб Фангер комментирует xzyfer.

Должен ли кто-нибудь тогда просто использовать что-нибудь, особенно ключевое слово «глобальный»? Нет, но, как и любой другой дизайн, постарайтесь проанализировать, от чего он зависит, а что от него. Постарайтесь узнать, когда оно меняется и как меняется. Изменение глобальных значений должно происходить только с теми переменными, которые могут изменяться при каждом запросе / ответе. То есть только к тем переменным, которые относятся к функциональному потоку процесса, а не к его технической реализации. Перенаправление URL-адреса на страницу входа принадлежит функциональному потоку процесса, классу реализации, используемому для интерфейса к технической реализации. Вы можете изменить последнее в разных версиях приложения, но не должны изменять их при каждом запросе / ответе.

Чтобы лучше понять, когда возникает проблема при работе с глобальными объектами и ключевым словом global, а когда нет, я представлю следующее предложение, которое исходит от Вима де Би, когда он пишет о блогах: «Личное да, личное нет». Когда функция изменяет значение глобальной переменной ради собственного функционирования, я буду называть это частное использование глобальной переменной и ошибкой. Но когда изменение глобальной переменной выполняется для правильной обработки приложения в целом, например, перенаправление пользователя на страницу входа в систему, то, на мой взгляд, возможно, хороший дизайн, а не по определению плохой и, конечно, не антипаттерн.

Оглядываясь назад на ответы Gordon, deceze и xzyfer: у всех них в качестве примеров есть «личное да» (и ошибки). Вот почему они против использования глобальных переменных. Я бы тоже. Однако в них нет примеров типа «личное да, частное нет», как я уже несколько раз делал в этом ответе.


Пример кода Drupal не использует глобальные переменные, он использует константы. Очень важное отличие состоит в том, что константу нельзя переопределить после того, как она была определена. Также нельзя просто сравнивать функции xyzи setZ. Первый изменяет глобальное состояние, второй является методом класса и изменяет только состояние экземпляра, для которого он был вызван.
Arjan

@Arjen: если вы будете искать ключевое слово global в Drupal 7.14, вы получите сотни результатов. Это старая проблема с общедоступными сеттерами: вы не контролируете место, где они меняются, после того, как сделали их общедоступными. Было рекомендовано не использовать их вообще или объявить их частными, чтобы их нельзя было добавить позже.
Лук Бергман

@Arjan: из-за моей ошибки с написанием вашего имени вы не получили никакого уведомления о моем ответе вам. Теперь будешь. :-)
Loek Bergman

@LoekBergman: В globaldrupal 7.26 (последняя версия) есть около 400 совпадений для этого слова , некоторые из них находятся в комментариях, а некоторые другие, похоже, находятся в коде, который не изменялся в течение многих лет. Я очень надеюсь, что они не будут использовать globals в drupal 8.
Арджан

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

15

Проще говоря, globalв современном PHP-коде редко есть причина и никогда не бывает хорошей. ИМХО. Особенно, если вы используете PHP 5. И особенно, если вы разрабатываете объектно-ориентированный код.

Глобалы негативно влияют на ремонтопригодность, читаемость и тестируемость кода. Многие варианты использования globalмогут и должны быть заменены внедрением зависимостей или простой передачей глобального объекта в качестве параметра.

function getCustomer($db, $id) {
    $row = $db->fetchRow('SELECT * FROM customer WHERE id = '.$db->quote($id));
    return $row;
}

10

Не бойтесь использовать глобальное ключевое слово внутри функций в PHP. Особенно не берите людей, которые диковинно проповедуют / кричат, что глобалы «злы» и тому подобное.

Во-первых, потому что то, что вы используете, полностью зависит от ситуации и проблемы, и НЕТ одного решения / способа что-либо сделать в кодировании. Полностью оставив в стороне ошибочность неопределенных, субъективных, религиозных прилагательных, таких как «зло», в уравнение.

Дело в точке :

Wordpress и его экосистема используют в своих функциях ключевое слово global. Будь то код ООП или не ООП.

И на данный момент Wordpress составляет в основном 18,9% Интернета, и на нем работают огромные мегасайты / приложения бесчисленных гигантов, от Reuters до Sony, от NYT до CNN.

И у него это хорошо получается.

Использование глобального ключевого слова внутри функций освобождает Wordpress от МАССОВОГО раздувания, которое могло бы произойти с учетом его огромной экосистемы. Представьте, что каждая функция запрашивает / передает любую переменную, которая требуется от другого плагина, ядра и возвращает. Добавлены взаимозависимости плагинов, которые закончились бы кошмаром переменных или кошмаром массивов, передаваемых как переменные. Ад за отслеживанием, ад за отладку, ад за разработку. Безумно большой объем памяти из-за раздувания кода и переменных. Писать тоже сложнее.

Могут быть люди, которые придумывают и критикуют Wordpress, его экосистему, их методы и то, что происходит в этих частях.

Бессмысленно, поскольку эта экосистема составляет примерно 20% всего интернета. Судя по всему, он ДЕЙСТВИТЕЛЬНО работает, он выполняет свою работу и многое другое. Это означает то же самое для глобального ключевого слова.

Еще один хороший пример - фундаментализм «iframe - зло». Десять лет назад использование iframe было ересью. И тысячи людей проповедовали против них в Интернете. Затем идет facebook, затем идут социальные сети, теперь iframe повсюду, от ящиков с отметками «Нравится» до аутентификации, и вуаля - все заткнулись. Есть те, кто до сих пор не заткнулся - справедливо или несправедливо. Но знаете что, жизнь продолжается, несмотря на такие мнения, и даже те, кто выступал против iframe десять лет назад, теперь вынуждены использовать их для интеграции различных социальных приложений в собственные приложения своей организации, не говоря ни слова.

......

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

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

Просто используйте то, что лучше всего подходит для решения проблемы, СЕЙЧАС, с соответствующими соображениями на ближайшее, среднесрочное и долгосрочное будущее. Не уклоняйтесь от использования какой-либо функции или подхода, потому что они имеют безудержную идеологическую враждебность по отношению к любому данному подмножеству кодеров.

Они не сделают твою работу. Ты будешь. Действуйте в соответствии с вашими обстоятельствами.


3
+1 за антифундаментализм и так далее, но просто сказать, что «многие люди используют это / это работает / и т. Д.» - это просто «аргумент в пользу населения», базовый софизм. тот факт, что большинство людей думают или делают что-то одно, не доказывает их правоту! в толпе, если возникнет опасность, большинство людей будет делать глупости, и одни умрут, растоптанные другими. Правы ли они, поставив ногу на лицо этой пятилетней маленькой девочки только потому, что они думают, что должны обязательно толкнуть дверь, которая открывается только в том случае, если ее тянут, чтобы избежать огня? Я так не думаю…
Паскаль Qyy 02

1
большинство, делающее что-либо, само по себе, конечно, ничего не подтверждает. впрочем, дело программное. и если большинство делает это, и большинство приложений и сервисов, созданных этими людьми, хорошо себя чувствуют (wordpress для многих других), то это означает, что их можно использовать.
unity100

7

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

  1. Основная цель глобальных объектов заключалась в обмене информацией между функциями. когда еще не было ничего подобного классу, код php состоял из набора функций. Иногда вам может потребоваться обмен информацией между функциями. Обычно для этого использовалось глобальное значение, чтобы не повредить данные, сделав их глобальными.

    Теперь, прежде чем какой-нибудь счастливый, удачливый простак начнет комментировать инъекцию зависимостей, я хотел бы спросить вас, как пользователь функции, такой как пример, get_post(1)может знать все зависимости функции. Также учтите, что зависимости могут отличаться от
    версии к версии и от сервера к серверу. Основная проблема с внедрением зависимостей заключается в том, что зависимости необходимо знать заранее. В ситуации, когда это невозможно или нежелательные глобальные переменные были единственным способом достичь этой цели.

    Благодаря созданию класса, теперь общие функции можно легко сгруппировать в класс и обмениваться данными. Через такие реализации, как Mediators, даже несвязанные объекты могут обмениваться информацией. В этом больше нет необходимости.

  2. Другое использование глобальных переменных - это конфигурация. Чаще всего в начале сценария до того, как будут загружены автозагрузчики, установлены соединения с базой данных и т. Д.

    Во время загрузки ресурсов глобальные переменные могут использоваться для настройки данных (т.е. какую базу данных использовать, где находятся файлы библиотеки, URL-адрес сервера и т. Д.). Лучший способ сделать это - использовать define()функцию, поскольку эти значения не будут часто меняться и могут быть легко помещены в файл конфигурации.

  3. Конечное использование глобальных переменных - это хранение общих данных (например, CRLF, IMAGE_DIR, IMAGE_DIR_URL), удобочитаемых флагов состояния (например, ITERATOR_IS_RECURSIVE). Здесь глобальные переменные используются для хранения информации, предназначенной для использования в масштабе всего приложения, что позволяет изменять их и вносить изменения в приложение.

  4. Шаблон singleton стал популярным в php во время работы php4, когда каждый экземпляр объекта занимал память. Синглтон помог сэкономить оперативную память, позволив создать только один экземпляр объекта. До появления ссылок даже внедрение зависимости было бы плохой идеей.

    Новая реализация объектов на PHP из PHP 5.4+ решает большинство этих проблем, вы можете безопасно передавать объекты с минимальными штрафами или без них. В этом больше нет необходимости.

    Еще одно использование синглтонов - это особый экземпляр, в котором одновременно должен существовать только один экземпляр объекта, этот экземпляр может существовать до / после выполнения скрипта, и этот объект используется совместно разными скриптами / серверами / языками и т. Д. Здесь одноэлементный шаблон решает решение довольно хорошо.

Итак, в заключение, если вы находитесь в позиции 1, 2 или 3, тогда использование global было бы разумным. Однако в других ситуациях следует использовать Метод 1.

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


6

Нет смысла создавать функцию concat с использованием ключевого слова global.

Он используется для доступа к глобальным переменным, таким как объект базы данных.

Пример:

function getCustomer($id) {
  global $db;
  $row = $db->fetchRow('SELECT * FROM customer WHERE id = '.$db->quote($id));
  return $row;
}

Его можно использовать как вариацию паттерна Синглтон.


«это не имеет смысла» - на самом деле это так: пример будет реализовывать поисковую таблицу без использования ООП.
Нир Альфаси
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.