首页 / 知识

Perl:CGI和DBI模块的变量范围问题

2023-04-16 22:49:00

Perl:CGI和DBI模块的变量范围问题

Perl: variable scope issue with CGI & DBI modules

我遇到了一个以前从未遇到过的变量范围问题。我正在使用Perl的CGI模块和对DBI的do()方法的调用。这是代码结构,简化了一点:

1
2
3
4
5
6
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占位符)?

我认为这与CGI模块的ReadParse()函数如何将范围分配给%in哈希有关,但是我不知道Perl的作用域是否足以理解为什么%in在顶级可用,而在我自己的工作范围内不可用()声明。

如果有人确实了解范围问题,是否有更好的方法来解决?用双引号将所有%in引用包装起来似乎有些混乱。为每个查询参数创建新变量是不现实的。

明确地说,我的问题是关于变量范围界定问题。我意识到,不建议使用ReadParse()使用CGI来获取查询参数。

我正在使用Perl 5.8.8,CGI 3.20和DBI 1.52。预先感谢任何阅读此书的人。

@Pi和@Bob,感谢您的建议。预先声明%in的范围无效(并且我始终使用strict)。结果与之前相同:在数据库中,col1为null,而cols 2和3设置为期望值。

供参考,这里是ReadParse函数(见下文)。这是CGI.pm的一部分的标准功能。以我的理解,我不是为了设置作用域而初始化哈希中的%(不是满足严格条件),因为在我看来该函数可以处理该问题:

1
2
3
4
5
6
7
8
9
10
11
sub ReadParse {
    local(*in);
    if (@_) {
      *in = $_[0];
    } else {
    my $pkg = caller();
      *in=*{"${pkg}::in"};
    }
    tie(%in,CGI);
    return scalar(keys %in);
}

我想我的问题是在do()上下文中获取%in哈希的最佳方法是什么?再次感谢!我希望这是为我的原始问题提供更多信息的正确方法。

@丹:我听说有关&ReadParse语法。我通常使用CGI :: ReadParse(),但在这种情况下,我认为最好坚持使用CGI.pm文档的确切方式。


它实际上并不像您在使用文档中所述的那样:
https://metacpan.org/pod/CGI#COMPATIBILITY-WITH-CGI-LIB.PL

如果必须使用它,那么CGI :: ReadParse();似乎更明智,语法也更简洁。虽然我看不到它在这种情况下有什么大的不同,但是那是一个绑定变量,所以谁知道该怎么做;)

您是否有特定原因无法使用更常见的$ cgi-> param('foo')语法?它稍微干净一点,并且以相当可预测的方式隐藏了您的命名空间。


我不知道出了什么问题,但是我可以告诉您一些不是的事情:

  • 这不是范围问题。如果不是,则$in{test}的所有实例都不起作用。
  • 这不是古老的&调用语法。 (这不是"正确",但在这种情况下是无害的。)

ReadParse是令人讨厌的代码。它修改符号表以在调用包中创建全局变量%in。更糟糕的是,它是一个绑定变量,因此访问它(理论上)可以执行任何操作。查看CGI.pm的源代码,FETCH方法仅调用params()方法来获取数据。我不知道为什么$dbh->do()中的提取不起作用。


use strict;。总是。

尝试声明

1
our %in;

看看是否有帮助。如果失败,则strict可能会产生更有用的错误。


从您给出的示例来看,这不是范围问题,或者所有参数都不起作用。

看起来DBI(或DBD,不确定在何处使用绑定参数)没有兑现领带魔术。
解决方法是像传递第二个和第三个参数一样,对传递给它的内容进行字符串化或复制。

使用SQLite和DBI 1.53进行的简单测试表明它可以正常工作:

1
2
3
$ 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") ."
"; $dbh->do("drop table foo")'

got: 42

想要共享您正在使用的数据库吗?


您正在使用哪个版本的DBI?从DBI changelog看,似乎1.00之前的版本不支持attribute参数。我怀疑"未初始化的" $in{test}实际上是您要传递给$dbh->do()undef


那里的东西很破。 Perl的作用域相对简单,除非您做些愚蠢的事情,否则您不太可能会偶然发现类似的事情。如建议的那样,打开严格的编译指示(以及警告。实际上,无论如何您都应同时使用两者)。

在不能够看到%in是如何定义的情况下很难说出是怎么回事(这与看上去讨厌的ReadParse调用有关吗?为什么用前导&,btw调用它?该语法已被认为是无效的)并消失了很长时间)。我建议发布更多代码,以便我们了解发生了什么。


首先,这不在做的上下文/范围内。它仍然处于主要或全局范围内。除非以与Perl中的子例程或不同"类"相关的某种方式输入{},否则您不会离开上下文。在()内,您不会超出范围。

您提供给我们的样本是未初始化的哈希,并且正如Pi所建议的那样,使用strict肯定会阻止这些事件的发生。

您能给我们一个更具代表性的代码示例吗?您在哪里设置%IN以及如何设置?


根据DBI文档:当前,绑定绑定变量不起作用。

DBI的内幕非常复杂,不幸的是,经过一些回转才能有效地引起问题。我同意其他人所说的摆脱丑陋的cgi-lib风格代码的说法。如果没有很好的框架(去Catalyst),做CGI就很不愉快了,更不用说十年来已经过时的东西。


我刚刚从http://www.carcomplaints.com/test/test.pl.txt尝试了您的测试密码,它可以立即在我的计算机上运行,??没有问题。我得到了三个预期值。我没有将其作为CGI运行,而是使用:

1
2
3
...
use CGI qw/-debug/;
...

我在控制台上写了一个变量(test=test),并且您的脚本插入没有问题。

但是,如果您不进行此操作,则tt将插入一个空字符串和两个NULL。这是因为您将值插值到字符串中。这将创建一个值为$in{test}的字符串,该字符串当前为undefundef字符串化为一个空字符串,这是插入数据库的内容。


由于这开始看起来像是tie()问题,请尝试以下实验。将其另存为foo.pl并以perl foo.pl"x=1"身份运行

1
2
3
4
5
6
7
use CGI;

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

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

它应该打印1 1。如果没有,我们就找到了罪魁祸首。


好的,试试这个:

1
2
3
use CGI;
my %in;
CGI::ReadParse(\%in);

这可能会有所帮助,因为它实际上使用的是您声明的变量,因此可以控制的范围(另外,它还会让您use strict而不出现其他可能使您感到困惑的麻烦)。


尝试这个

%in = ReadParse();

但我对此表示怀疑。您是否要获取查询参数或其他内容?


模块变量调用方法

最新内容

相关内容

猜你喜欢