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

问题描述:

我遇到了似乎是我之前没有遇到的变量范围问题。我正在使用Perl的CGI模块并调用DBI的do()方法。以下是代码结构,简化了一下:Perl:CGI和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占位符变量的计算结果就好像它是未初始化的。其他两个占位符变量有效。

问题:除非我用双引号(#2占位符)包装它,或者将该值重新分配给一个新变量(#3占位符),为什么在do()的上下文中不能使用哈希%?

我认为这是与CGI模块的ReadParse()函数如何将范围指定给散列中的%有关,但我不知道Perl的范围足够了解了为什么%in在*可用,但而不是来自我的do()声明中。

如果有人确实了解范围问题,有没有更好的方法来处理它?用双引号包含引用中的所有%似乎有点混乱。为每个查询参数创建新变量是不现实的。

只是要清楚,我的问题是关于变量范围问题。我意识到ReadParse()不是用CGI抓取查询参数的推荐方法。

我使用Perl 5.8.8,CGI 3.20和DBI 1.52。提前感谢任何阅读此内容的人。

@Pi & @Bob,感谢您的建议。预先声明%in的范围不起作用(并且我总是使用strict)。结果与之前一样:在数据库中,col1为空,而cols 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(),但在这种情况下,我认为最好坚持如何精确地使用the CGI.pm documentation has it

+0

将其声明为与您的呼叫相同。你也没有把它分配给任何东西。当你只需要一个散列时,你为什么使用*?这是一个非常复杂的子程序。也许我可以和你一起清理或更好地理解你的需求。你能告诉我们你在哪里叫这个子吗? – 2008-09-18 02:12:54

根据DBI文档:绑定绑定变量目前无效。

DBI在引擎盖下非常复杂,不幸的是经历了一些回转以高效率导致您的问题。我同意所有其他人说,摆脱丑陋的旧cgi-lib风格的代码。如果没有一个好的框架(去催化剂)做CGI是不够的,更不用说已经过了十年的东西了。

use strict;。总是。

尝试宣告

our %in; 

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

+0

这对初学者程序员来说是一个很好的建议,但我超越了这一点。我的错,在我的问题中没有说清楚“简化了一点”意味着我忽略了严格编译指示所需的基本声明。 – Wick 2008-09-19 10:39:52

首先,这不是在做的上下文/范围。它仍然处于主要或全球范围内。除非您以某种方式在perl中输入子例程或不同'类',否则您不会离开上下文。在()内您不会离开示波器。

样品你给我们的是一个未初始化的哈希和有价证券投资建议,采用严格的肯定会保留那些从发生的历史。

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

那里有什么东西坏了。 Perl的范围是相对简单的,除非你做了一些愚蠢的事情,否则你不可能偶然发现任何奇怪的东西。如上所述,打开严格的编译指示(也是警告,实际上你应该同时使用)。

很难说出发生了什么,却无法看到%in是如何定义的(这是否与该令人讨厌的ReadParse调用有关?为什么要用前导&来调用它,btw?已经被认为已经死了很久了)。我建议张贴多一点的代码,所以我们可以看到这是怎么回事..

+0

我希望我偶然发现了一些奇怪的东西:)我试图在发布之前消除所有的愚蠢行为。 备案:严格和警告处于打开状态。 %in是在ReadParse()中定义和管理的,它是CGI.pm(标准Perl模块,而不是我的代码)的一部分。 – Wick 2008-09-18 03:33:42

实际上它并不像你使用它作为文档描述: https://metacpan.org/pod/CGI#COMPATIBILITY-WITH-CGI-LIB.PL

如果你必须使用它,那么CGI :: ReadParse();似乎更明智,更不重要的语法。虽然我看不出它在这种情况下有多大的区别,但它是一个系列变量,所以谁会知道它在做什么;)

是否有一个特定的原因,你不能使用更常见$ cgi-> param('foo')语法?这是一个有点清洁,并在相当多的可预测的方式filths你的命名空间..

+0

我正在更新遗留代码..垃圾ReadParse /%肯定是罐头:) 我只是好奇,因为有一些基本的变量范围,我不知道..顺便说一句,尝试CGI :: ReadParse(); ..没有区别,但谢谢指出。 – Wick 2008-09-18 02:32:39

+0

就不使用$ query-> param()语法而言,我在我的原始文章中提到了这一点。虽然这避开了这个问题,但它并没有解释发生了什么,这是我的问题的关键。坏的代码与否,我想了解发生了什么,所以在其他情况下不会再发生。 – Wick 2008-09-18 03:20:17

+0

这真的太糟糕了,因为1)函数调用语法的差异不是问题,2)我的问题并不是关于抓取查询参数的最佳方法。对丹来说毫无进展 - 只是希望得到更多的话题答案。 – Wick 2008-09-19 10:34:11

在= ReadParse试试这个

%();

但我怀疑。你想获得查询参数或什么?

+0

错误:在(等)哈希分配中的奇数元素。 ... ReadParse()返回标量(键%in),所以我不认为将函数结果分配给哈希将工作。 – Wick 2008-09-18 02:45:20

+0

我正在更新遗留代码,并且绝对不会保留ReadParse()和%垃圾!主要是我只关心我不了解变量范围的基本原理。明白我的意思了吗?这是问题的原理。典型的$ query-> param('x')方法工作正常。再次感谢 – Wick 2008-09-18 02:46:50

+1

ReadParse适用于使用旧版cgi-lib.pl API的旧版系统。使用现代的东西! :) – 2008-09-21 22:25:47

好了,试试这个:

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

,因为它的实际使用,您已经声明的变量这可能会帮助,因此可以控制的范围内(再加上它会让你use strict没有其他龌龊的是可以污浊水)

+0

不! ReadParse正在安装到符号表中。如果你通过它的话,它会期望一个typeglob,而不是一个词汇的引用。 – 2008-09-18 02:56:07

+0

没有骰子。令人难以置信的是,变量参数不起作用。 WTF!令人生气的是,用你的最后两行代替: my%in =('test','hello world'); ..works很棒。它必须是带有变量作用域的ReadParse函数的东西?这是我不熟悉的。谢谢 – Wick 2008-09-18 03:05:56

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

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

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

您使用的是哪个版本的DBI?从查看DBI changelog看来,1.00之前的版本不支持属性参数。我怀疑“未初始化”$in{test}实际上是undef,您将传递给$dbh->do()

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

从您给出的例子来看,这是而不是一个范围问题,或者没有参数可以工作。

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

关怀,分享你所使用的数据库?

我仍然想追查这个原因。再次,你使用的是哪个数据库(哪个DBD模块和它的哪个版本?)

我刚刚试过你的测试编码http://www.carcomplaints.com/test/test.pl.txt,它可以马上在我的电脑上运行,没有问题。按预期得到三个值。我没有运行它的CGI,但使用:

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

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

但是如果你不写,tt会插入一个空字符串和两个NULL值。这是因为你在一个字符串中插入一个值。这将产生一个值为$in{test}的字符串,此时为undefundef将字符串化为一个空字符串,这是插入数据库的内容。