Perl: переменная область видимости с модулями CGI и DBI - PullRequest
3 голосов
/ 18 сентября 2008

Я столкнулся с проблемой области видимости, с которой раньше не сталкивался. Я использую модуль Perl CGI и вызов метода DBI do (). Вот структура кода, немного упрощенная:

use DBI;
use CGI qw(:cgi-lib);
&ReadParse;
my $dbh = DBI->connect(...............);
my $test = $in{test};
$dbh->do(qq{INSERT INTO events VALUES (?,?,?)},undef,$in{test},"$in{test}",$test);

Переменная-заполнитель # 1 оценивается как неинициализированная. Две другие переменные-заполнители работают.

Вопрос: почему% в хэше недоступен в контексте do (), если я не заключу его в двойные кавычки (заполнитель # 2) или не переназначу значение в новую переменную (заполнитель # 3)

Я думаю, что это как-то связано с тем, что функция ReadParse () модуля CGI назначает область видимости для% в хэше, но я не знаю достаточно хорошо Perl, чтобы понять, почему% in доступен на верхнем уровне, но не из в моем утверждении do ().

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

Просто чтобы прояснить, мой вопрос о проблеме переменной области видимости. Я понимаю, что ReadParse () не рекомендуется для получения параметров запроса с помощью CGI.

Я использую Perl 5.8.8, CGI 3.20 и DBI 1.52. Заранее спасибо всем, кто читает это.

@ Pi & @Bob, спасибо за предложения. Предварительное объявление области для% in не имеет никакого эффекта (и я всегда использую строгое). Результат тот же, что и раньше: в БД col1 равно нулю, а для столбцов 2 и 3 установлено ожидаемое значение.

Для справки, вот функция ReadParse (см. Ниже). Это стандартная функция, которая является частью CGI.pm. Насколько я понимаю, я не собираюсь инициализировать% в хэше (кроме удовлетворения строгого) в целях установки области видимости, поскольку мне кажется, что функция справляется с этим:

sub ReadParse {
    local(*in);
    if (@_) {
      *in = $_[0];
    } else {
    my $pkg = caller();
      *in=*{"${pkg}::in"};
    }
    tie(%in,CGI);
    return scalar(keys %in);
}

Полагаю, мой вопрос в том, как лучше всего получить% в хэше в контексте do ()? Еще раз спасибо! Я надеюсь, что это правильный способ предоставить дополнительную информацию к моему первоначальному вопросу.

@ Дэн: Я слышал о синтаксисе & ReadParse. Я бы обычно использовал CGI :: ReadParse (), но в этом случае я подумал, что лучше всего придерживаться того, как в документации CGI.pm он имеет точно.

Ответы [ 12 ]

4 голосов
/ 18 сентября 2008

На самом деле не похоже, что вы используете его, как описано в документации: https://metacpan.org/pod/CGI#COMPATIBILITY-WITH-CGI-LIB.PL

Если вы должны его использовать, то CGI :: ReadParse (); кажется более разумным и менее грубым синтаксисом. Хотя я не вижу, чтобы это имело большое значение в этой ситуации, но тогда это связанная переменная, так что, черт возьми, знает, что делает;)

Есть ли конкретная причина, по которой вы не можете использовать более распространенный синтаксис $ cgi-> param ('foo')? Это немного чище, и заполняет ваше пространство имен значительно более предсказуемым образом.

3 голосов
/ 18 сентября 2008

Я не знаю, что не так, но я могу сказать вам кое-что, что не так:

  • Это не предметная область. Если бы это было так, ни один из экземпляров $in{test} не сработал бы.
  • Это не архаичный & синтаксис вызова. (Это не «правильно», но в данном случае это безвредно.)

ReadParse - неприятный фрагмент кода. Он создает таблицу символов для создания глобальной переменной% in в вызывающем пакете. Хуже всего то, что это связанная переменная, поэтому доступ к ней может (теоретически) сделать что угодно. Глядя на исходный код CGI.pm, метод FETCH просто вызывает метод params() для получения данных. Я понятия не имею, почему выборка в $dbh->do() не работает.

3 голосов
/ 18 сентября 2008

use strict;. Всегда.

Попробуйте объявить

our %in;

и посмотрим, поможет ли это. В противном случае strict может привести к более полезной ошибке.

2 голосов
/ 18 сентября 2008

Из приведенного вами примера это , а не проблема с областью видимости, или ни один из параметров не будет работать.

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

Простой тест с использованием SQLite и DBI 1.53 показывает, что он работает нормально:

$ perl -MDBI -we'sub TIEHASH { bless {} } sub FETCH { "42" } tie %x, "main" or die; my $dbh = DBI->connect("dbi:SQLite:dbname=dbfile","",""); $dbh->do("create table foo (bar char(80))"); $dbh->do("insert into foo values (?)", undef, $x{foo}); print "got: " . $dbh->selectrow_array("select bar from foo") . "\n"; $dbh->do("drop table foo")'
got: 42

Хотите поделиться, какую базу данных вы используете?

2 голосов
/ 18 сентября 2008

Какую версию DBI вы используете? Из рассмотрения DBI changelog видно, что версии до 1.00 не поддерживали аргумент атрибута. Я подозреваю, что «неинициализированный» $in{test} - это на самом деле undef, который вы передаете $dbh->do().

2 голосов
/ 18 сентября 2008

Там что-то очень сломано. Область видимости Perl относительно проста, и вы вряд ли наткнетесь на что-то странное, если только вы не делаете что-то глупое. Как уже было предложено, включите строгую прагму (и предупреждения тоже. На самом деле вы должны использовать оба в любом случае).

Довольно сложно сказать, что происходит, не имея возможности увидеть, как определяется% in (это как-то связано с этим отвратительно выглядящим вызовом ReadParse? Почему вы вызываете его с ведущим &, кстати? считался мертвым и давно ушел). Я предлагаю опубликовать немного больше кода, чтобы мы могли видеть, что происходит ..

2 голосов
/ 18 сентября 2008

Во-первых, это не входит в контекст / сферу действия do. Это все еще в контексте основного или глобального. Вы не выходите из контекста, пока не введете {} каким-либо образом, относящимся к подпрограммам или различным «классам» в perl. В пределах () паренсов вы не покидаете рамки.

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

Можете ли вы дать нам более представительный пример вашего кода? Где вы устанавливаете% IN и как?

1 голос
/ 24 ноября 2008

Согласно документации DBI: в настоящее время привязка связанной переменной не работает.

DBI довольно сложен под капотом и, к сожалению, проходит через некоторые движения, чтобы быть эффективными, которые вызывают вашу проблему. Я согласен со всеми, кто говорит избавиться от уродливого старого кода в стиле cgi-lib. Достаточно неприятно делать CGI без хорошей платформы (иди с Catalyst), не говоря уже о том, что устарело на протяжении десятилетия.

0 голосов
/ 25 сентября 2008

Я только что попробовал ваш тестовый код из http://www.carcomplaints.com/test/test.pl.txt,, и он сразу работает на моем компьютере, никаких проблем. Я получаю три значения, как и ожидалось. Я не запускал его как CGI, но использовал:

...
use CGI qw/-debug/;
...

Я пишу переменную на консоли (test=test), и ваши скрипты вставляются без проблем.

Если вы не указали это, tt вставит пустую строку и два значения NULL. Это потому, что вы интерполируете значение в строку. Это создаст строку со значением $in{test}, которое на данный момент составляет undef. undef преобразуется в пустую строку, которая вставляется в базу данных.

0 голосов
/ 18 сентября 2008

Поскольку это выглядит как проблема tie(), попробуйте следующий эксперимент. Сохраните это как foo.pl и запустите как perl foo.pl "x=1"

use CGI;

CGI::ReadParse();
p($in{x}, "$in{x}");

sub p { my @a = @_; print "@a\n" }

Следует напечатать 1 1. Если этого не произойдет, мы нашли виновника.

...