向perl应用程序添加动态代码的最佳方法
我知道此问题的具体实例之前已得到回答:
> How can I dynamically include Perl modules without using eval? Perl Monks也有很好的答案: > Writing a Perl module that dynamically loads other modules. 但是我想要一个强大的方法来为Perl应用程序添加功能: >高效:如果不需要代码,则不应编译. 为了说明这一点,以下是一些可以从一个好的解决方案中受益的例子: >一组用于从不同应用程序移动数据的脚本.例如,将数据从OpenCart移动到Prestashop,其中数据模型中的每个实体都有一个特定的“添加”来处理输入或输出;然后中间数据模型负责数据的转换.这可用于在任何方向上移动数据,甚至可以在同一电子商务的不同版本之间移动数据. 以下是我使用过的一些例子. 在运行时加载函数并输出可能的编译错误: eval `cat $file_with_function`; if( $@ ) { print STDERR $@,"n"; die "Errors at file $file_with_functionn"; } 或者使用File :: Slurp更健壮: eval read_file("$file_with_function",binmode => ':utf8'); 检查是否已定义某个功能: if( !defined &myfunction ) { die "myfunction is not definedn"; } 可以从那里调用该函数.这对一个功能很好,但对很多人来说并不适用. 如果函数放在模块中: require $file_with_function; # needs the ".pm" extension,i.e. addon/func.pm $name_of_module->import(); # need to know the module name,i.e. Addon::Func $name_of_module->myfunction(...); 如果需求可以在eval中保护,然后像以前一样使用$@. 随着Module::Load: load $name_of_module; 其次是导入和使用方式相同.安全性不应该是一个问题,因为可以假设动态代码来自受信任的地方.还有更好的方法吗?哪种方式被视为良好做法? 如果它有帮助,我将在Dancer框架内使用解决方案(在其他地方,但不是唯一的). 编辑:鉴于评论,我添加了一些更多的信息.我想到的所有案例都有一个共同点: >有一个以上的动态代码.可能很多开始. 解决方法
鉴于评论和缺乏回应,我做了一些研究来回答我自己的问题.欢迎提出评论或其他答案!
动态代码 动态代码我指的是在运行时评估的代码.一般来说,我认为编译应用程序会更好,这样在开始执行之前就可以检查Perl编译器可以提供的所有错误.添加使用严格和使用警告,你可以捕获许多常见的错误.那么为什么要使用动态代码呢?这些是我考虑的原因: >应用程序根据执行的上下文执行许多不同的操作.例如,应用程序从文件中提取某些属性.提取它们的方式取决于文件类型,我们希望处理许多文件类型,但我们不想为我们添加的每种新文件类型更改应用程序.我们还希望应用程序能够快速启动. 我们该怎么做呢? 鉴于Perl提供的可能性,添加动态代码的解决方案有两种形式:使用eval和使用require.然后有一些模块可以帮助以更容易或更易维护的方式做事. 快速而肮脏的方式
eval read_file("$file_with_code",binmode => ':utf8'); if( $@ ) { die "$file_with_code: error $@n"; } if( !defined &myfunction ) { die "myfunction is not defined at $file_with_coden"; } 将字符集指定为read_file可确保正确解释该文件.检查编译是否正确以及我们期望的函数是否已定义也是很好的.所以在$file_with_code中,我们将: sub myfunction(...) { # Do whatever; maybe return something } 然后你可以正常调用函数.该功能将根据加载的文件而不同.简单而有活力. 模块化方式(推荐) 我考虑可维护性的方式是使用 my $mymodule = 'MyCompany::MyModule'; # The module name ends up in $mymodule require $mymodule; 与使用不同,require将加载模块但不会执行导入.因此我们可以使用模块内的任何函数,这些函数名称不会调用调用命名空间.要访问该功能,我们需要使用: $mymodule->myfunction($a,$b); 请参阅下文,了解如何传递参数.这种调用函数的方法将在$a和$b之前添加一个通常名为$self的参数.如果您对面向对象一无所知,可以忽略它. 根据要求将尝试加载模块并且模块可能不存在或者可能无法编译,为了捕获错误,最好使用: eval "require $mymodule"; 然后$@可用于检查加载编译过程中的错误.我们还可以检查函数是否已定义为: if( $mymodule->can('myfunction') ) { die "myfunction is not defined at module $mymodulen"; } 在这种情况下,我们需要为模块创建一个目录,并为每个模块创建一个扩展名为.pm的文件: MyCompany MyModule.pm 在MyModule.pm里面,我们将: package MyCompany::MyModule; sub myfunction { my ($self,$a,$b); # Do whatever; maybe return something # $self will be 'MyCompany::MyModule' } 1;
如果我们想通过使用其他可以调用调用者名称空间的库来实现该模块,我们可以使用namespace::clean模块.该模块将确保调用者不会从我们定义的模块中获得对命名空间的任何添加.它以这种方式使用: package MyCompany::MyModule; # Definitions by these modules will not be available to the code doing the require use Library1 qw(def1 def2); use Library2 qw(def3 def4); ... # Private functions go here and will not be visible from the code doing the require sub private_function1 { ... } ... use namespace::clean; # myfunction will be available sub myfunction { # Do whatever; maybe return something } ... 1; 如果我们不止一次包含模块会怎样? 简短的回答是什么. Perl会跟踪已加载的模块以及使用%INC变量的位置. use和require都不会加载两次库. use会将任何导出的名称添加到调用者名称空间.要求也不会这样做.如果您想检查模块是否已经加载,您可以使用%INC或更好,您可以使用module::loaded,这是现代Perl版本的核心部分: use Module::Loaded; if( !is_loaded( $mymodule ) { eval "require $mymodule" ); ... } 如何确保Perl找到我的模块文件? 使用和要求Perl使用@INC变量来定义将用于查找库的目录列表.通过将其添加到PERL5LIB环境变量或使用以下命令,可以(通过其他方式)添加新目录. use lib '/the/path/to/my/libs'; 帮手图书馆 我找到了一些库,可用于使使用动态机制的代码更易于维护.他们是: > if模块:将根据条件加载模块:如果CONDITION,MODULE =>使用ARGUMENTS ;.也可用于卸载模块. 取自Module :: Load :: Conditional文档: use Module::Load::Conditional qw(can_load); my $use_list = { CPANPLUS => 0.05,LWP => 5.60,'Test::More' => undef,}; print can_load( modules => $use_list ) ? 'all modules loaded successfully' : 'failed to load required modules'; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |