Перл:проблема с областью переменной в модулях CGI и DBI

StackOverflow https://stackoverflow.com/questions/89257

Вопрос

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

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 оценивается так, как будто она не инициализирована.Две другие переменные-заполнители работают.

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

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

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

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

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

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

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

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

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

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

Это было полезно?

Решение

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

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

Другие советы

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

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

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

use strict;.Всегда.

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

our %in;

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

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

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

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

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

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

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

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

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

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

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

Похоже, 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

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

Хорошо, попробуй это:

use CGI;
my %in;
CGI::ReadParse(\%in);

Это может помочь, поскольку на самом деле он использует объявленную вами переменную и, следовательно, может контролировать область действия (плюс это позволит вам use strict без прочей гадости, которая могла бы мутить воду)

Поскольку это начинает выглядеть как 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.Если нет, то мы нашли виновника.

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

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

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

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

Попробуй это

%in = ReadParse();

но я в этом сомневаюсь.Вы пытаетесь получить параметры запроса или что-то в этом роде?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top