Есть ли способ явно установить глобальную область действия require_once()?

Я ищу способ установить область require_once() в глобальную область, когда require_once() используется внутри функции. Что-то вроде следующего кода должно работать:

файл `foo.php':

<?php

$foo = 42;

фактический код:

<?php

function includeFooFile() {
    require_once("foo.php"); // scope of "foo.php" will be the function scope
}

$foo = 23;

includeFooFile();
echo($foo."\n"); // will print 23, but I want it to print 42.

Есть ли способ явно установить область действия require_once()? Есть ли хороший обходной путь?


person Stephan Kulla    schedule 23.01.2012    source источник
comment
Область require_once явно задается там, где вы определяете ее использование.   -  person hakre    schedule 23.01.2012
comment
Я могу придумать ужасный обходной путь, если хочешь...   -  person DaveRandom    schedule 23.01.2012
comment
Нет. Вам нужно явно указать переменные, которые будут псевдонимами в глобальной области видимости. Либо в функции, либо поверх вашего сценария включения.   -  person mario    schedule 23.01.2012
comment
@DaveRandom: Возможно, вам следует добавить это как ответ ^^   -  person hakre    schedule 23.01.2012
comment
Почему вы заключаете require_once() в функцию?   -  person afuzzyllama    schedule 23.01.2012
comment
@afuzzyllama: приведенный выше код является просто примером для моего вопроса. В моем приложении у меня есть функция, в которой я хочу загрузить динамически вычисляемый список исходных файлов.   -  person Stephan Kulla    schedule 23.01.2012
comment
@tampis: Вы знаете, что require_once будет иметь ограниченное использование, поскольку он будет определять переменные только один раз?   -  person hakre    schedule 24.01.2012
comment
@hake: Да, я в курсе... Спасибо за подсказку^^.   -  person Stephan Kulla    schedule 24.01.2012
comment
возможный дубликат функционального блока run в контексте глобального пространства имен в PHP   -  person outis    schedule 26.01.2012


Ответы (7)


Вы можете использовать эту хакерскую функцию, которую я написал:

/**
 * Extracts all global variables as references and includes the file.
 * Useful for including legacy plugins.
 *
 * @param string $__filename__ File to include
 * @param array  $__vars__     Extra variables to extract into local scope
 * @throws Exception
 * @return void
 */
function GlobalInclude($__filename__, &$__vars__ = null) {
    if(!is_file($__filename__)) throw new Exception('File ' . $__filename__ . ' does not exist');
    extract($GLOBALS, EXTR_REFS | EXTR_SKIP);
    if($__vars__ !== null) extract($__vars__, EXTR_REFS);
    unset($__vars__);
    include $__filename__;
    unset($__filename__);
    foreach(array_diff_key(get_defined_vars(), $GLOBALS) as $key => $val) {
        $GLOBALS[$key] = $val;
    }
}

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

person mpen    schedule 13.05.2015

Помимо "глобализации" вашей переменной, это невозможно сделать:

global $foo;
$foo = 42;

OR

$GLOBALS['foo'] = 42;

Тогда ваше значение должно быть 42, когда вы его распечатываете.

ОБНОВЛЕНИЕ

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

person Mathieu Dumoulin    schedule 23.01.2012
comment
Я только что попробовал ваш ответ, и он работал нормально. Проблем быть не должно, если файл foo.php содержит функции или классы, верно? - person Stephan Kulla; 23.01.2012
comment
Спасибо Матье. Ваше решение отлично работает для моего приложения. - person Stephan Kulla; 23.01.2012

Вам нужно будет объявить global в вашем foo.php:

<?php
 global $foo;
 $foo = 42;
?>

Иначе, наверное, нельзя.

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

$vars = get_defined_vars();
foreach($vars as $varname => $value)
{
  global $$varname; //$$ is no mistake here
}
require...

Но я не совсем уверен, попадешь ли ты туда, куда хочешь...

person bardiir    schedule 23.01.2012
comment
Ах, кстати... я упоминал, что это несколько ужасно, все здесь следует использовать только как неправильные примеры того, как НЕ делать что-то... возможно, есть очень хорошее решение для основной проблемы, которую вы пытаетесь решить, без ужасного неправильного использования глобального пространства имен;) - person bardiir; 23.01.2012

Это определенно не "хороший" обходной путь, но он сработает:

function includeFooFile() {
  require_once("foo.php");
  foreach (get_defined_vars() as $key => $value) {
    // Ignore superglobals
    if (!in_array($key, array('GLOBALS','_SERVER','_GET','_POST','_FILES','_COOKIE','_SESSION','_REQUEST','_ENV'))) {
      $GLOBALS[$key] = $value;
    }
  }
}

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

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

person DaveRandom    schedule 23.01.2012
comment
Кажется, это рабочий обходной путь для моей проблемы. теперь буду пробовать... - person Stephan Kulla; 23.01.2012
comment
@DaveRandom: это не поддерживает псевдонимы, и суперглобальные значения уже были перезаписаны, поэтому теоретически нет необходимости проверять их. Практически get_defined_vars() все равно не возвращает их в области локальной функции. - person hakre; 24.01.2012
comment
@hakre Действительно, после правильного тестирования (5.2.17/win32) это не так, когда я проверял это для целей этого ответа, я сделал это в глобальной области, где они возвращаются. Я не понимаю, почему они не будут присутствовать - по определению суперглобальные объекты определены в каждой области видимости. Я собираюсь оставить этот ответ как есть на случай, если это поведение изменится в будущей версии. Я бы предпочел проверять и не перезаписывать суперглобальные значения на случай, если я что-то не предусмотрел (особенно $GLOBALS) - как вы говорите, они уже будут перезаписаны, поэтому это должно иметь небольшое практическое значение. - person DaveRandom; 24.01.2012
comment
@hakre, вы правы (в первый раз я также подумал, что get_defined_vars() вернет все доступные переменные в текущей области). В PHP 5.3.3 он просто возвращает переменные в области действия функции... - person Stephan Kulla; 24.01.2012
comment
Если файл не может определить функции, он почти бесполезен, потому что именно поэтому вы обычно включаете их - person Tomas; 25.01.2012
comment
@Tomas, действительно, это в значительной степени верно (хотя включения также полезны для файлов шаблонов HTML), но, похоже, я все равно был дезинформирован об этом. У меня сложилось впечатление, что определение функции внутри функции приведет к ошибке синтаксического анализа, но после правильного тестирования/прочитания руководства я обнаружил, что ошибался в этом. - person DaveRandom; 25.01.2012
comment
@Tomas: требуемый файл может определять функции. - person hakre; 26.01.2012

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

function includeFooFile() {
    require_once("foo.php"); // scope of "foo.php" will be the function scope

    foreach (get_defined_vars() as $k => $v)
    {
        $GLOBALS[$k] = &$v;
    }
}

Этот пример заботится как о переменных, так и о ссылках, которые могут быть тем, что вы ищете. Демо. Обратите внимание, что require_once сработает только один раз и только один раз определит переменные.

person hakre    schedule 23.01.2012

Я не пробовал (поскольку использование глобальных переменных - плохая идея), но потенциально это может сработать:

require_once '...';
$GLOBALS = array_merge($GLOBALS, get_defined_vars());

В качестве альтернативы вы можете просто сделать это вручную:

foreach (get_defined_vars() as $k => $v) {
    $GLOBALS[$k] = $v;
}
person a sad dude    schedule 23.01.2012
comment
Почему вы считаете использование глобальных переменных плохой идеей? - person Stephan Kulla; 23.01.2012
comment
Это догма :) вы можете найти ее в Google, объяснений более чем достаточно, большинство подходит для любого языка. По сути, даже если вам нужно, чтобы данные были глобально доступны, поместите их туда, где они не могут мешать чему-то еще. В PHP хорошей альтернативой является статическая переменная класса (imho). - person a sad dude; 24.01.2012

В зависимости от ваших точных требований вы можете использовать константы. Требовать свой файл в глобальной области видимости, но внутри него установить константу.

IE файл.php:

define('MY_CONSTANT', 42);

Затем в любом месте вашего скрипта просто используйте MY_CONSTANT для ссылки на значение, вы не сможете редактировать, как только оно будет установлено. Кроме этого, вы можете глобализировать свою переменную, как сказано в другом ответе, но не на 100% ясно, чего вы пытаетесь достичь, кроме простого извлечения значения из включенного файла? В этом случае константы должны быть в порядке.

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

person Dunhamzzz    schedule 23.01.2012
comment
Я думаю, что пользователь хочет иметь возможность обновлять существующее значение, но если это просто внешние константы, то ваш метод, очевидно, лучший. - person Mathieu Dumoulin; 23.01.2012
comment
В моем приложении $foo будет объектом. Объекты не могут храниться в константах, верно? - person Stephan Kulla; 23.01.2012