3

我遇到了以前从未遇到过的变量范围问题。我正在使用 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 占位符变量评估为好像它未初始化。其他两个占位符变量有效。

问题:为什么 %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 的范围没有效果(我总是使用严格的)。结果与之前相同:在 db 中,col1 为空,而 cols 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);
}

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

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

4

12 回答 12

4

它实际上看起来不像您在使用它,如文档中所述: https ://metacpan.org/pod/CGI#COMPATIBILITY-WITH-CGI-LIB.PL

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

您不能使用更常见的 $cgi->param('foo') 语法是否有特殊原因?它更干净一点,并且以一种更可预测的方式弄脏了你的命名空间。

于 2008-09-18T02:19:09.327 回答
3

use strict;. 总是。

尝试声明

our %in;

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

于 2008-09-18T01:48:10.370 回答
3

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

  • 这不是范围界定问题。如果是,那么没有一个实例$in{test}将起作用。
  • 这不是古老的&调用语法。(这不是“正确的”,但在这种情况下它是无害的。)

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

于 2008-09-18T02:54:38.790 回答
2

首先,这不在 do 的上下文/范围内。它仍然是在 main 或 global 的上下文中。在以某种与 perl 中的子例程或不同“类”相关的方式输入 {} 之前,您不会离开上下文。在 () 括号内,您不会离开范围。

你给我们的样本是一个未初始化的哈希,正如 Pi 所建议的,使用 strict 肯定会阻止那些发生。

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

于 2008-09-18T01:54:44.283 回答
2

那里的东西很坏。Perl 的作用域相对简单,除非你在做一些愚蠢的事情,否则你不太可能偶然发现任何奇怪的东西。正如建议的那样,打开严格的编译指示(以及警告。事实上,无论如何你都应该同时使用)。

如果无法看到 %in 是如何定义的,就很难判断发生了什么(这与那个看起来很讨厌的 ReadParse 调用有关吗?你为什么要用前导 & 来调用它,顺便说一句?那个语法被认为是死的走了很长时间)。我建议发布更多代码,这样我们就可以看到发生了什么......

于 2008-09-18T02:07:27.157 回答
2

您使用的是什么版本的 DBI?从DBI 变更日志来看,1.00 之前的版本似乎不支持属性参数。我怀疑“未初始化”$in{test}实际上undef是您传递给$dbh->do().

于 2008-09-18T03:19:11.350 回答
2

从您给出的示例中,这不是范围问题,或者任何参数都不起作用。

看起来 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

愿意分享您使用的数据库吗?

于 2008-09-18T09:35:59.543 回答
1

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

DBI 在引擎盖下非常复杂,不幸的是,为了提高效率,DBI 经历了一些导致您的问题的过程。我同意其他所有说要摆脱丑陋的旧 cgi-lib 样式代码的人的观点。在没有好的框架(去 Catalyst)的情况下做 CGI 已经够令人不快的了,更不用说已经过时了十年的东西了。

于 2008-11-24T18:31:57.780 回答
0

好的,试试这个:

使用 CGI;
我的%;
CGI::ReadParse(\%in);

这可能会有所帮助,因为它实际上使用了您已声明的变量,因此可以控制范围(另外它会让您use strict没有其他可能使水变得混乱的肮脏)

于 2008-09-18T02:49:55.180 回答
0

由于这开始看起来像一个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。如果没有,我们已经找到了罪魁祸首。

于 2008-09-18T04:00:50.533 回答
0

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

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

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

但是,如果您忽略它, tt 将插入一个空字符串和两个 NULL。这是因为您将值插入到字符串中。这将生成一个字符串,$in{test}其值为undef当前。undef字符串化为一个空字符串,这是插入数据库的内容。

于 2008-09-25T08:23:52.300 回答
-1

试试这个

%in = ReadParse();

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

于 2008-09-18T02:21:36.700 回答