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

发布于 2024-07-04 11:15:17 字数 1555 浏览 4 评论 0原文

我遇到了以前从未遇到过的变量范围问题。 我正在使用 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 的范围没有效果(我总是使用 strict)。 结果与之前相同:在数据库中,col1 为 null,而 cols 2 和 cols 为 null。 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 文档完全有此内容

I've run into what appears to be a variable scope issue I haven't encountered before. I'm using Perl's CGI module and a call to DBI's do() method. Here's the code structure, simplified a bit:

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);

The #1 placeholder variable evaluates as if it is uninitialized. The other two placeholder variables work.

The question: Why is the %in hash not available within the context of do(), unless I wrap it in double quotes (#2 placeholder) or reassign the value to a new variable (#3 placeholder)?

I think it's something to do with how the CGI module's ReadParse() function assigns scope to the %in hash, but I don't know Perl scoping well enough to understand why %in is available at the top level but not from within my do() statement.

If someone does understand the scoping issue, is there a better way to handle it? Wrapping all the %in references in double quotes seems a little messy. Creating new variables for each query parameter isn't realistic.

Just to be clear, my question is about the variable scoping issue. I realize that ReadParse() isn't the recommended method to grab query params with CGI.

I'm using Perl 5.8.8, CGI 3.20, and DBI 1.52. Thank you in advance to anyone reading this.

@Pi & @Bob, thanks for the suggestions. Pre-declaring the scope for %in has no effect (and I always use strict). The result is the same as before: in the db, col1 is null while cols 2 & 3 are set to the expected value.

For reference, here's the ReadParse function (see below). It's a standard function that's part of CGI.pm. The way I understand it, I'm not meant to initialize the %in hash (other than satisfying strict) for purposes of setting scope, since the function appears to me to handle that:

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

I guess my question is what is the best way to get the %in hash within the context of do()? Thanks again! I hope this is the right way to provide additional info to my original question.

@Dan: I hear ya regarding the &ReadParse syntax. I'd normally use CGI::ReadParse() but in this case I thought it was best to stick to how the CGI.pm documentation has it exactly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

浮萍、无处依 2024-07-11 11:15:17

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

As this is starting to look like a tie() problem, try the following experiment. Save this as a foo.pl and run it as perl foo.pl "x=1"

use CGI;

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

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

It should print 1 1. If it doesn't, we've found the culprit.

旧人哭 2024-07-11 11:15:17

好的,试试这个:

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

这可能会有所帮助,因为它实际上使用了您声明的变量,因此可以控制其范围(而且它可以让您使用严格,而不会出现其他可能令人困惑的麻烦水域)

Okay, try this:

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

That might help as it's actually using a variable that you've declared, and therefore can control the scope of (plus it'll let you use strict without other nastiness that could be muddying the waters)

云裳 2024-07-11 11:15:17

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

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

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

然而,如果您忽略这一点,tt 将插入一个空字符串和两个 NULL。 这是因为您将一个值插入到字符串中。 这将生成一个值为 $in{test} 的字符串,目前为 undefundef 字符串化为一个空字符串,该字符串将被插入到数据库中。

I just tried your test codce from http://www.carcomplaints.com/test/test.pl.txt, and it works right away on my computer, no problems. I get three values as expected. I didn't run it as CGI, but using:

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

I write a variable on the console (test=test) and your scripts inserts without a problem.

If however your leave this out, tt will insert an empty string and two NULLs. This is a because you interpolate a value into a string. This will makes a string with value of $in{test} which is undef at the moment. undef stringifies to an empty string, which is what is inserted into database.

情释 2024-07-11 11:15:17

试试这个

%in = ReadParse();

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

Try this

%in = ReadParse();

but i doubt that. Are you trying to get query parameters or something?

因为看清所以看轻 2024-07-11 11:15:17

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

如果一定要用的话,那么CGI::ReadParse(); 看起来更明智,语法也更简洁。 虽然我看不出它在这种情况下有多大区别,但它是一个绑定变量,所以谁到底知道它在做什么;)

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

It doesn't actually look like you're using it as described in the docs:
https://metacpan.org/pod/CGI#COMPATIBILITY-WITH-CGI-LIB.PL

If you must use it, then CGI::ReadParse(); seems more sensible and less crufty syntax. Although I can't see it making much difference in this situation, but then it is a tied variable, so who the hell knows what it's doing ;)

Is there a particular reason you can't use the more-common $cgi->param('foo') syntax? It's a little bit cleaner, and filths up your namespace in a considerably more predictable manner..

溇涏 2024-07-11 11:15:17

使用严格;。 总是。

尝试声明

our %in;

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

use strict;. Always.

Try declaring

our %in;

and seeing if that helps. Failing that, strict may produce a more useful error.

空袭的梦i 2024-07-11 11:15:17

我不知道出了什么问题,但我可以告诉你一些事实并非如此:

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

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

I don't know what's wrong, but I can tell you some things that aren't:

  • It's not a scoping issue. If it were then none of the instances of $in{test} would work.
  • It's not the archaic & calling syntax. (It's not "right" but it's harmless in this case.)

ReadParse is a nasty bit of code. It munges the symbol table to create the global variable %in in the calling package. What's worse is that it's a tied variable, so accessing it could (theoretically) do anything. Looking at the source code for CGI.pm, the FETCH method just invokes the params() method to get the data. I have no idea why the fetch in the $dbh->do() isn't working.

久夏青 2024-07-11 11:15:17

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

您给我们的示例是未初始化的哈希,正如 Pi 所建议的,使用 strict 肯定会阻止这些情况发生。

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

Firstly, that is not in the context/scope of do. It is still in the context of main or global. You dont leave context until you enter {} in some way relating to subroutines or different 'classes' in perl. Within () parens you are not leaving scope.

The sample you gave us is of an uninitialized hash and as Pi has suggested, using strict will certainly keep those from occuring.

Can you give us a more representative example of your code? Where are you setting %IN and how?

泪是无色的血 2024-07-11 11:15:17

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

如果无法看到 %in 是如何定义的,就很难判断发生了什么(这与那个令人讨厌的 ReadParse 调用有关吗?为什么你用前导 & 来调用它,顺便说一句?该语法已被考虑过)死了并且消失了很长一段时间)。 我建议发布更多代码,以便我们可以看到发生了什么。

Something's very broken there. Perl's scoping is relatively simple, and you're unlikely to stumble upon anything odd like that unless you're doing something daft. As has been suggested, switch on the strict pragma (and warnings, too. In fact you should be using both anyway).

It's pretty hard to tell what's going on without being able to see how %in is defined (is it something to do with that nasty-looking ReadParse call? why are you calling it with the leading &, btw? that syntax has been considered dead and gone for a long time). I suggest posting a bit more code, so we can see what's going on..

千仐 2024-07-11 11:15:17

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

DBI 在幕后相当复杂,不幸的是,为了提高效率,要经历一些旋转,从而导致您的问题。 我同意其他人所说的要摆脱丑陋的旧 cgi-lib 风格代码的观点。 在没有一个好的框架(使用 Catalyst)的情况下进行 CGI 已经够令人不快的了,更不用说那些已经过时了十年的东西了。

Per the DBI documentation: Binding a tied variable doesn't work, currently.

DBI is pretty complicated under the hood, and unfortunately goes through some gyrations to be efficient that are causing your problem. I agree with everyone else who says to get rid of the ugly old cgi-lib style code. It's unpleasant enough to do CGI without a nice framework (go Catalyst), let alone something that's been obsolete for a decade.

不寐倦长更 2024-07-11 11:15:17

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

What version of DBI are you using? From looking at the DBI changelog it appears that versions prior to 1.00 didn't support the attribute argument. I suspect that the "uninitialized" $in{test} is actually the undef that you're passing to $dbh->do().

新雨望断虹 2024-07-11 11:15:17

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

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

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

From the example you gave, this is not a scoping issue, or none of the parameters would work.

Looks like DBI (or a DBD, not sure where bind parameters are used) isn't honoring tie magic.
The workaround would be to stringize or copy what you pass to it, like your second and third parameters do.

A simple test using SQLite and DBI 1.53 shows it working ok:

$ 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

Care to share what database you are using?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文