Perl模块加载-防止:也许你忘了装“Bla”了

ajsxfq5m  于 2022-11-15  发布在  Perl
关注(0)|答案(2)|浏览(150)

当您运行perl -e "Bla->new"时,会出现以下已知错误:

Can't locate object method "new" via package "Bla"
(perhaps you forgot to load "Bla"?)

由于我的疏忽,前几天在Perl服务器进程中发生了这种情况。有多个脚本,其中大多数都有正确的use语句。但有一个脚本在第123行的sub blub中执行Bla->new,但在顶部缺少use Bla。并且在服务器进程之前没有加载使用Bla的任何其他脚本的情况下,单击该脚本,然后砰!
隔离地测试脚本显然是防止这种错误的方法,但是代码依赖于一个巨大的环境。您知道另一种防止这种疏忽的方法吗?
下面是一个PPI(尽管有它的优点)在Perl上的局限性的例子:

use strict;
use HTTP::Request::Common;

my $req = GET 'http://www.example.com';
$req->headers->push_header( Bla => time );

my $au=Auweia->new;

__END__
PPI::Token::Symbol          '$req'
PPI::Token::Operator        '->'
PPI::Token::Word            'headers'
PPI::Token::Operator        '->'
PPI::Token::Word            'push_header'

PPI::Token::Symbol          '$au'
PPI::Token::Operator        '='
PPI::Token::Word            'Auweia'
PPI::Token::Operator        '->'
PPI::Token::Word            'new'

设置头和赋值Auweia->new解析相同,所以我不确定如何在这样一个不稳定的基础上构建。perl.exe在运行时之前无法判断。

进一步更新

好吧,从下面@Schwern的指导性评论中,我了解到PPI只是一个标记器,如果你接受它的局限性,你 * 可以 * 在它的基础上构建。

44u64gxh

44u64gxh1#

测试是唯一值得付出努力的答案。如果代码包含诸如忘记加载类之类的错误,那么它可能还包含其他错误。不管有什么障碍,都要使它可测试。否则你就是在打补丁。
也就是说,你有两个选择。你可以使用Class::Autouse,它会尝试加载一个模块,如果它还没有加载的话。它很方便,但是因为它会影响整个过程,所以可能会产生意想不到的效果。
或者,您可以使用PPI来扫描代码并查找所有的类方法调用。PPI::Dumper对于理解PPI如何看待Perl非常方便。

use strict;
use warnings;

use PPI;
use PPI::Dumper;

my $file = shift;
my $doc = PPI::Document->new($file);

# How PPI sees a class method call.
#    PPI::Token::Word      'Class'
#    PPI::Token::Operator  '->'
#    PPI::Token::Word      'method'
$doc->find( sub {
    my($node, $class) = @_;

    # First we want a word
    return 0 unless $class->isa("PPI::Token::Word");

    # It's not a class, it's actually a method call.
    return 0 if $class->method_call;

    my $class_name = $class->literal;

    # Next to it is a -> operator
    my $op = $class->snext_sibling or return 0;
    return 0 unless $op->isa("PPI::Token::Operator") and $op->content eq '->';

    # And then another word which PPI identifies as a method call.
    my $method = $op->snext_sibling or return 0;
    return 0 unless $method->isa("PPI::Token::Word") and $method->method_call;

    my $method_name = $method->literal;

    printf "$class->$method_name seen at %s line %d.\n", $file, $class->line_number;
});
n1bvdmb6

n1bvdmb62#

你没有说你在什么服务器环境下运行,但从你所说的听起来你可以在执行任何单独的页面之前预先加载所有的模块。这不仅可以防止你所描述的问题(每个脚本都必须记住加载它使用的所有模块),而且还可以保存内存。
在预分叉服务器中(通常与mod_perl和Apache一起使用),您确实希望在服务器首次分叉 * 之前 * 加载尽可能多的代码,以便代码在写入时复制共享内存中存储一次,而不是在每个子进程中按需加载时多次存储。
有关在Apache中预加载的信息,请参见Practical mod_perl一节

相关问题