Область видимости переменной
Область видимости переменной — это контекст, в котором эта переменная определена. В большинстве случаев все переменные PHP имеют только одну область видимости. Эта единая область видимости охватывает также включаемые (include) и требуемые (require) файлы. Например:
Здесь переменная $a будет доступна внутри включенного скрипта b.inc . Однако определение (тело) пользовательской функции задает локальную область видимости данной функции. Любая используемая внутри функции переменная по умолчанию ограничена локальной областью видимости функции. Например:
$a = 1 ; /* глобальная область видимости */
?php
function test ()
<
echo $a ; /* ссылка на переменную локальной области видимости */
>
Этот скрипт не сгенерирует никакого вывода, поскольку выражение echo указывает на локальную версию переменной $a , а в пределах этой области видимости ей не было присвоено значение. Возможно вы заметили, что это немного отличается от языка C в том, что глобальные переменные в C автоматически доступны функциям, если только они не были перезаписаны локальным определением. Это может вызвать некоторые проблемы, поскольку люди могут нечаянно изменить глобальную переменную. В PHP, если глобальная переменная будет использоваться внутри функции, она должна быть объявлена глобальной внутри определения функции.
Ключевое слово global
Сначала пример использования global:
Пример #1 Использование global
function Sum ()
global $a , $b ;
Вышеприведенный скрипт выведет 3. После определения $a и $b внутри функции как global все ссылки на любую из этих переменных будут указывать на их глобальную версию. Не существует никаких ограничений на количество глобальных переменных, которые могут обрабатываться функцией.
Второй способ доступа к переменным глобальной области видимости — использование специального, определяемого PHP массива $GLOBALS . Предыдущий пример может быть переписан так:
Пример #2 Использование $GLOBALS вместо global
function Sum ()
$GLOBALS [ ‘b’ ] = $GLOBALS [ ‘a’ ] + $GLOBALS [ ‘b’ ];
>
$GLOBALS — это ассоциативный массив, ключом которого является имя, а значением — содержимое глобальной переменной. Обратите внимание, что $GLOBALS существует в любой области видимости, это объясняется тем, что $GLOBALS является суперглобальным. Ниже приведен пример, демонстрирующий возможности суперглобальных переменных:
Пример #3 Суперглобальные переменные и область видимости
function test_global ()
// Большинство предопределенных переменных не являются
// «супер», и чтобы быть доступными в локальной области
// видимости, функции требуют указания ‘global’.
global $HTTP_POST_VARS ;
?php
echo $HTTP_POST_VARS [ ‘name’ ];
// Суперглобальные переменные доступны в любой области
// видимости и не требуют указания ‘global’.
// Суперглобальные переменные доступны, начиная с PHP 4.1.0, а
// использование HTTP_POST_VARS считается устаревшим.
echo $_POST [ ‘name’ ];
>
?>
Замечание:
Использование ключевого слова global вне функции не является ошибкой. Оно может быть использовано в файле, которые включается внутрь функции.
Использование статических (static) переменных
Другой важной особенностью области видимости переменной является статическая переменная. Статическая переменная существует только в локальной области видимости функции, но не теряет своего значения, когда выполнение программы выходит из этой области видимости. Рассмотрим следующий пример:
Пример #4 Демонстрация необходимости статических переменных
Эта функция довольно бесполезна, поскольку при каждом вызове она устанавливает $a в 0 и выводит 0. Инкремент переменной $a ++ здесь не играет роли, так как при выходе из функции переменная $a исчезает. Чтобы написать полезную считающую функцию, которая не будет терять текущего значения счетчика, переменная $a объявляется как static:
Пример #5 Пример использования статических переменных
Теперь $a будет проинициализирована только при первом вызове функции, а каждый вызов функции test() будет выводить значение $a и инкрементировать его.
Статические переменные также дают возможность работать с рекурсивными функциями. Рекурсивной является функция, вызывающая саму себя. При написании рекурсивной функции нужно быть внимательным, поскольку есть вероятность сделать рекурсию бесконечной. Вы должны убедиться, что существует адекватный способ завершения рекурсии. Следующая простая функция рекурсивно считает до 10, используя для определения момента остановки статическую переменную $count :
Пример #6 Статические переменные и рекурсивные функции
Замечание:
Статические переменные могут быть объявлены так, как показано в предыдущем примере. Попытка присвоить этим переменным значения, являющиеся результатом выражений, вызовет ошибку обработки.
Пример #7 Объявление статических переменных
function foo () static $int = 0 ; // верно
static $int = 1 + 2 ; // неверно (поскольку это выражение)
static $int = sqrt ( 121 ); // неверно (поскольку это тоже выражение)
?php
Замечание:
Статические объявления вычисляются во время компиляции скрипта.
Ссылки с глобальными (global) и статическими (static) переменными
Движок Zend Engine 1, лежащий в основе PHP 4, оперирует модификаторами переменных static и global как ссылками. Например, реальная глобальная переменная, внедренная в область видимости функции указанием ключевого слова global, в действительности создает ссылку на глобальную переменную. Это может привести к неожиданному поведению, как это показано в следующем примере:
function test_global_noref () global $obj ;
$obj = new stdclass ;
>
test_global_ref ();
var_dump ( $obj );
test_global_noref ();
var_dump ( $obj );
?>
Результат выполнения данного примера:
Аналогично ведет себя и выражение static. Ссылки не хранятся статично:
echo ‘Статический объект: ‘ ;
var_dump ( $obj );
if (!isset( $obj )) // Присвоить ссылку статической переменной
$obj = &new stdclass ;
>
$obj -> property ++;
return $obj ;
>
echo ‘Статический объект: ‘ ;
var_dump ( $obj );
if (!isset( $obj )) // Присвоить объект статической переменной
$obj = new stdclass ;
>
$obj -> property ++;
return $obj ;
>
$obj1 = get_instance_ref ();
$still_obj1 = get_instance_ref ();
echo «\n» ;
$obj2 = get_instance_noref ();
$still_obj2 = get_instance_noref ();
?>
Результат выполнения данного примера:
Статический объект: NULL
Статический объект: NULL
Статический объект: NULL
Статический объект: object(stdClass)(1) [«property»]=>
int(1)
>
Этот пример демонстрирует, что при присвоении ссылки статической переменной она не запоминается, когда вы вызываете функцию &get_instance_ref() во второй раз.
Глобальные объекты в PHP
В сообщении пойдет речь о фундаментальном в программировании — глобальных объектах. Я бы сказал, это научный вопрос, который хотелось бы обсудить. Итак, чтобы не «отстрелить себе ноги», программисты не программируют в глобальной области. Ок, все понятно и предельно просто, остановимся на этом? Не в этом материале. Как известно любое действие вызывает цепь событий и логических следствий.
Во первых, зачем создавать догму, которую можно не создавать? Вместо нее, давайте создадим функцию stop_globals(), например для языка PHP. Фреймворк, вначале выполнения своего кода, может ее выполнить, а дальнейшие попытки работы с глобальной областью, будут вызывать ошибки PHP. Хорошо ли данное решение?
Это еще далеко не все, что можно было бы обсудить.
Главная причина существования вышеуказанной догмы в том, что имеется возможность случайно затереть значения глобальных переменных, а это, в свою очередь, может привести, в том числе к трудно локализующимся ошибкам в программе. А если иметь возможность пользоваться глобальными переменными на чтение, но не на запись?
Давайте обратим внимание на окружающую нас Вселенную. В ней существуют глобальные объекты: пространство, время, материя, энергия, возможно темная материя и энергия. Подобным образом, исходя из моего опыта в веб-программировании, есть ряд объектов, для которых неудобно использовать Dependency Injection и такие объекты по своей сути, есть глобальные. Это объект связи с базой данных, объект `USER` и другие. Для работы с такими объектами, в PHP, можно было бы ввести функцию super(‘sky’, ‘user’), которая делала бы переменные $sky и $user суперглобальными, подобно как $_GET или $_POST.
Такое решение не хуже традиционной догмы «в глобальной области программировать нельзя», так как, запись данных в такие переменные сразу обнаружится и PHP выдаст ошибку, а преимущество в следующем:
- Концептуально глобальные объекты остаются глобальными, программа выглядит проще
- Такой подход более производителен. Намного быстрее обратиться к переменной напрямую, чем предварительно передавать ее значение через стек. Имеется ввиду, стек процессора, который используется компиляторами для передачи параметров функций. Так или иначе реализация паттерна DI, имеет накладные расходы на используемые ресурсы
- Поддерживаете ли вы введение экспериментальных функций super() и stop_globals()?
- Что думаете о вышеописанной идее в целом?
В заключении, хотел бы отметить свое наблюдение в отношении laravel и вреде паттернов программирования. Как известно laravel называют «кладязем антипаттернов». Считаю именно этот факт, а также свободное мышление его автора, позволило этому проекту стать столь популярным, каким он есть. Паттерны хороши для общения программистов, чтобы указать на некоторую программную сущность. Но они и вредят, путая программистов, не позволяя делать программы эффективными. Давайте делать программирование проще.
Установить объект PHP глобальным?
Я только начал переводить свой проект из mysql в PDO. В моем проекте новый объект PDO создается более или менее правильно в начале программы.
$dbh_pdo = new PDO("mysql:host=$db_url;dbname=$db_database_name", $db_user, $db_password);
Теперь я хотел бы использовать этот обработчик (это правильное имя?) В некоторых функциях и классах. Есть ли способ сделать объекты глобальными, как переменные, или я пытаюсь сделать что-то невыразимо глупо, потому что я не мог найти ничего при поиске в Интернете …
Да, вы можете сделать объекты глобальными, как и любая другая переменная:
$pdo = new PDO('something'); function foo() < global $pdo; $pdo->prepare('. '); >
Вы также можете проверить шаблон Singleton, который в основном представляет собой глобальный OO-стиль.
При этом я бы рекомендовал не использовать глобальные переменные . Они могут быть больно при отладке и тестировании, потому что трудно сказать, кто изменил / использовал / получил доступ к нему, потому что все может. Их использование обычно считается плохой практикой. Подумайте о том, чтобы немного рассмотреть ваш дизайн.
Я не знаю, как выглядит ваше приложение, но скажите, что вы делали это:
class TableCreator < public function createFromId($id) < global $pdo; $stmt = $pdo->prepare('SELECT * FROM mytable WHERE $stmt->execute(array($id)); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($rows as $row) < // do stuff >> >
Вы должны сделать это вместо этого:
class TableCreator < protected $pdo; public function __construct(PDO $pdo) < $this->pdo = $pdo; > public function createFromId($id) < $stmt = $this->pdo->prepare('SELECT * FROM mytable WHERE $stmt->execute(array($id)); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($rows as $row) < // do stuff >> >
Так как класс TableCreator требует, чтобы объект PDO работал правильно, имеет смысл передать его ему при создании экземпляра.
Вы будете использовать $GLOBALS[‘dbh_pdo’] вместо $dbh_pdo внутри любых функций. Или вы можете использовать ключевое слово global и использовать $dbh_pdo (то есть global $dbh_pdo ).
Вы также можете попробовать использовать Singleton, чтобы передать вам объект PDO. Таким образом, у вас будет только один объект PDO (и одно соединение с базой данных) в любом запросе, который сохраняет ресурсы памяти / сервера.