Для каждого пакета в Perl создается таблица имен. Таблица имен представляет собой хеш, имя которого совпадает с именем пакета. Ключами этого хеша являются имена глобальных переменных (а так же функций, указателей файлов и т.п.), определенных в текущем пакете. Значения хеша таблицы имен - переменные типа typeglob, которые содержат значения объявленных глобальных переменных.
Новая запись в таблице имен создается в тот момент, когда происходит объявление новой
глобальной переменной. Лексические переменные и "локальные" функции в таблицу не вносятся.
Если для текущего кода пакет не объявлен (директивой package), запись будет помещена в таблицу имен %main:: .
В таблицу имен пакета, отличного от main, могут включаться только те идентификаторы, имена которых начинаются с буквы или символа подчеркивания.
Таблица имен существует только в момент работы интерпретатора, и хранится в памяти компьютера. Таблицы имен используются для доступа к используемым пакетам и их данным. Любой perl-скрипт может напрямую обращаться к содержимому таблиц имен.
Справка: Что такое пакет main?
Main - это пакет верхнего уровня.
Perl поддерживает два специальных имени пакетов:
- main - пространство имен главной программы. Используется по умолчанию.
- CORE - пространство имен встроенных функций.
Через main можно получить доступ ко всем используемым в программе пакетам.
Справка: Что такое typeglob?
typeglob - это особенный, внутренний тип данных в perl. Принадлежность к этому типу данных обозначается префиксом "*" . Префикс "*" можно рассматривать как метасимвол, вместо которого может стоять любой из префиксов "$", "@", "%", "&". Т.е. переменная типа typeglob может содержать в себе что угодно.
Переменную типа typeglob можно представить как небольшой хеш, ключами которого выступают имена типов данных. В зависимости от типа данных, размещенных в переменной typeglob, для их извлечения можно обратиться к соответствующим полям.
Для получения данных из записей таблицы имен можно использовать поля:
- PACKAGE - имя пакета, в котором определен текущий идентификатор (имя переменной).
- NAME - имя идентификатора.
- SCALAR - ссылка на скалярную переменную идентификатора (если это скалярная переменная).
- ARRAY - ссылка на массив (если переменная является массивом).
- HASH - ссылка на хеш (если переменная является хешом).
- CODE - ссылка на подпрограмму (если идентификатор был создан при объявлении функции).
- IO, FILEHANDLE - Ссылка на указатель файла.
- GLOB - Ссылка на typeglob для идентификатора.
Пример 1:
Любая глобальная переменная помещается в пространство имен своего пакета. Можно получить доступ к ее значению, используя таблицу имен. В таблице имен она будет иметь тип данных typeglob, поэтому обращаться к ней нужно через префикс "*".
|
1 2 |
$variable = 5; print ${*variable{SCALAR}}; # выведет 5 |
Пример 2:
|
1 2 3 |
use CGI; print ${*CGI::VERSION{SCALAR}}; # выведет 3.43 |
Примеры кода
Пример 1
Вывод содержимого пакета main.
|
1 2 3 4 5 |
#!/usr/local/bin/perl foreach (keys %main::) { print $_." => ".$main::{$_}."\n"; } |
Результат:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
%perl main1.pl version:: => *main::version:: / => *main::/ stderr => *main::stderr _<mro.c => *main::_<mro.c CORE:: => *main::CORE:: mro:: => *main::mro:: stdout => *main::stdout attributes:: => *main::attributes:: => *main:: stdin => *main::stdin ARGV => *main::ARGV INC => *main::INC ENV => *main::ENV Regexp:: => *main::Regexp:: UNIVERSAL:: => *main::UNIVERSAL:: _<perlio.c => *main::_<perlio.c PerlIO:: => *main::PerlIO:: 0 => *main::0 _<universal.c => *main::_<universal.c STDOUT => *main::STDOUT IO:: => *main::IO:: _ => *main::_ STDERR => *main::STDERR Internals:: => *main::Internals:: STDIN => *main::STDIN DB:: => *main::DB:: ... _<main1.pl => *main::_<main1.pl |
Пример 2
Допустим, нам хочется получить побольше информации о переменной %ENV, через таблицу имен:
|
1 2 3 4 5 |
print *main::ENV{HASH}; foreach (keys %{*main::ENV{HASH}}) { print $_." => ".${*main::ENV{HASH}}{$_}."\n"; } |
Так мы получили полный список переменных окружения и их значений:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
%perl main1.pl HASH(0x800e3e918)SSH_CLIENT => 194.85.195.166 4641 22 HOME => /home/natalie SSH_CONNECTION => 194.85.195.166 4641 194.85.61.131 22 BLOCKSIZE => K OSTYPE => FreeBSD EDITOR => vi VENDOR => unknown USER => natalie GROUP => httpd-natalie SHLVL => 2 ... |
Пример 3
Очень простой пример. Объявляем глобальную переменную $count.
Т.к. этот простой скрипт не объявлен как пакет, переменная помещается в пространство имен пакета main. А потом получаем значение переменной через таблицу имен.
|
1 2 3 |
our $count = 123; print ${*main::count}; |
Результат:
|
1 2 3 |
%perl main1.pl 123 |
В самом деле, использование полей CODE, ARRAY и др. для доступа к значению переменной совсем не обязательно. Однако, в этом случае надо точно знать, ссылку на значение какого типа вернет обращение к таблице имен. Потому что, ссылку нужно соответствующим образом разыменовать. Как это сделать, если вы не имеете никакого понятия, что подразумевается под именем переменной?
В данном примере, count мог оказаться не только скалярной переменной, но и именем массива, и функцией. Если вы работаете с переменной своего модуля - проблем не возникнет. Но если вы импортируете переменные из стороннего пакета - использовать обращение к полям CODE и пр. может оказаться удобнее.
Пример 4
Можно получить доступ не только к переменным пакета, но и к подпрограммам. Получаем ссылку на подпрограмму, разыменовываем ее и заставляем выполниться.
|
1 2 3 4 |
use Digest::MD5; print &{*Digest::MD5::md5_hex{CODE}}("dddd"); # выведет 11ddbaf3386aea1f2974eee984542152 |
Но намного интересней возможность переопределения функций, методов. Допустим, возникает ситуация, когда нужно временно заставить некую функцию выдавать определенный результат, не внося изменений в содержащий ее модуль. Например, разово отключить валидацию на время выполнения скрипта.
|
1 2 3 4 5 |
*Validator::Contacts::check_fio = \&fake_check_fio; sub fake_check_fio { return 1; } |
Причем, системе не важно - была ли переопределяемая функция обычной функцией или методом объекта. Эта функция может даже вызываться не в самом скрипте, а где-то в глубине системы.
Хорошая заметка, но хотелось бы больше пояснений. Почему в main так много "мусора", как заглянуть в typeglob загруженных пакетов.