【问题标题】:Perl: variable scope issue with CGI & DBI modulesPerl:CGI 和 DBI 模块的变量范围问题
【发布时间】:2008-09-18 01:42:44
【问题描述】:

我遇到了一个似乎以前没有遇到过的变量范围问题。我正在使用 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 在顶层可用但不能从在我的 do() 语句中。

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

为了清楚起见,我的问题是关于变量范围的问题。我意识到 ReadParse() 不是使用 CGI 获取查询参数的推荐方法。

我正在使用 Perl 5.8.8、CGI 3.20 和 DBI 1.52。提前感谢任何阅读本文的人。

@Pi 和@Bob,感谢您的建议。预先声明 %in 的范围没有效果(我总是使用严格的)。结果和之前一样:在 db 中,col1 为 null,而 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() 但在这种情况下,我认为最好坚持使用 the CGI.pm documentation has it 的确切方式。

【问题讨论】:

  • 在与您的呼叫相同的上下文中将其声明为我的。您也没有将其分配给任何东西。当您只需要哈希时,为什么要使用 *in?这是一个非常复杂的子程序。也许我可以与您一起清理或更好地了解您的需求。你能告诉我们你在哪里调用这个子吗?

标签: perl module variables scope cgi


【解决方案1】:

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

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

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

【讨论】:

  • 我正在努力更新遗留代码.. ReadParse / %in 废话肯定会被罐头:) 我只是好奇,因为我不知道关于变量作用域的一些基本知识。 . BTW 试过 CGI::ReadParse(); .. 没有区别,但感谢您指出这一点。
  • 关于不使用 $query->param() 语法,我在原始帖子中提到了这一点。虽然这回避了问题,但它并没有解释正在发生的事情,这是我问题的症结所在。代码是否错误,我想了解发生了什么,这样它就不会在其他情况下再次发生。
  • 这个答案真的太糟糕了,因为1)函数调用语法的差异不是问题,2)我的问题特别不是关于获取查询参数的最佳方法。对 Dan 没有任何冒犯 - 只是希望获得更多关于主题的答案。
【解决方案2】:

use strict;。总是。

尝试声明

our %in;

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

【讨论】:

  • 这对初学者程序员来说是个好建议,但我不止于此。我的错,我没有在我的问题中说清楚“简化一点”意味着我遗漏了严格 pragma 所需的基本声明。
【解决方案3】:

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

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

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

【讨论】:

  • 我还没有能力提高你的答案,否则我会的,这绝对是迄今为止最接近的。如果没有其他人可以添加任何内容,我会稍等一下并选择它作为答案。再次感谢您的宝贵时间,我很感激。
【解决方案4】:

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

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

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

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

    • 我希望我偶然发现了一些奇怪的东西:) 我试图在发布之前消除所有愚蠢的东西。记录在案:严格和警告已开启。 %in 在 ReadParse() 中定义和管理,它是 CGI.pm 的一部分(标准 Perl 模块,不是我的代码)。
    【解决方案6】:

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

    【讨论】:

    • 我在原来的帖子中隐藏了这一点,但是:> 我使用的是 Perl 5.8.8、CGI 3.20 和 DBI 1.52。
    • 另外我想我可以肯定地说这不是正在发生的事情,因为如果我尝试切换占位符变量顺序: $dbh->do(qq{INSERT INTO testing VALUES (?,?,?) },undef,"$in{test}",$in{test},$test); .. 行中的第二列获取空值。
    • 我认为您之前在正确的轨道上使用 munged 符号表/绑定变量导致了问题。如果我放弃 ReadParse() 调用并将其替换为: my %in = ( 'test', 'hello world' ); ...所有三列都在数据库中正确设置。
    • 这个答案应该只是对这个问题的评论。我会在它被否决之前将其删除。
    • 错过了版本号,抱歉。如果交换带引号/不带引号的参数会解决问题,那么 FETCH 出了问题。
    【解决方案7】:

    从您给出的示例来看,这不是范围界定问题,否则任何参数都不起作用。

    看起来 DBI(或 DBD,不确定在哪里使用绑定参数)不尊重 tie 魔术。 解决方法是字符串化或复制您传递给它的内容,就像您的第二个和第三个参数一样。

    使用 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
    

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

    【讨论】:

      【解决方案8】:

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

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

      【讨论】:

      • "绑定是使用 Perl 别名在低级别执行的。每当从数据库中获取一行时,$var_to_bind 似乎会自动更新,因为它现在引用与对应列相同的内存位置值。目前,绑定绑定变量不起作用。"
      • 不错的发现。记录在案,来自search.cpan.org/~timb/DBI/DBI.pm#bind_col
      【解决方案9】:

      好的,试试这个:

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

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

      【讨论】:

      • 不! ReadParse 正在安装到符号表中。如果您将参数传递给它,它需要一个类型团,而不是对词法的引用。
      • 没有骰子。令人难以置信的是可变参数不起作用。哇!令人抓狂的是,将最后两行替换为: my %in = ( 'test', 'hello world' ); ..效果很好。它必须与 ReadParse 函数绑定变量范围?那是我不熟悉的。谢谢
      • 对词法的引用可以正常工作,因为在分配对 typeglob 的引用时会涉及到魔法。试试看: perl -MTie::Hash -wle'sub foo { local(*foo); *foo = $_[0]; tie %foo, "Tie::StdHash" 或死亡;返回 } 我的 %foo;富(\%富);打印绑定 %foo'
      【解决方案10】:

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

      【讨论】:

      • 抱歉耽搁了,走题了。它打印 1 1. Ack。如果有帮助,我可以发布我的整个测试文件,它不会比示例代码多多少。
      • 如果有帮助,完整的测试文件在这里:carcomplaints.com/test/test.pl.txt
      • 另外我在办公室有一个运行 Perl 5.10 和最新版本 CGI/DBI 的盒子,我明天会试一试以防万一。谢谢。暂时签收。再次感谢,祝你好运。
      • 那么它一定是 DBI 中的东西。在调试器中运行您的程序,进入 do()(您必须先通过 CGI 的 FETCH)并检查参数的值。
      • 我实际上从未使用过 Perl 调试器。可能是我做的时候了。
      【解决方案11】:

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

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

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

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

      【讨论】:

      • 嗨,彼得,我很欣赏您的评论,但您所描述的并不是正在发生的事情(插入空字符串和两个 NULL)。套用我原来的问题,只有 $in{test} 参数无法插入预期的值。其他两个参数工作正常并插入作为测试提供的任何值。
      【解决方案12】:

      试试这个

      %in = ReadParse();

      但我对此表示怀疑。您是在尝试获取查询参数还是什么?

      【讨论】:

      • 错误:在(等)的散列分配中的奇数个元素。 ...ReadParse() 返回标量(keys %in) 所以我不认为将函数结果分配给哈希会起作用。
      • 我正在更新旧代码并且绝对不会保留 ReadParse() 和 %in 废话!主要是我只是担心我不了解有关变量范围的基本知识。明白我的意思了吗?这是问题的原理。典型的 $query->param('x') 方法可以正常工作。再次感谢
      • ReadParse 适用于使用旧 cgi-lib.pl API 的遗留系统。使用现代的东西! :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-20
      • 1970-01-01
      • 2019-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多