从类子例程覆盖模块子例程

时间:2017-08-14 08:28:04

标签: perl

我有一个能够定义凭证的类。

$po = PoService->new()->set_basicauth("jack", "secret");

问题是,为了做到这一点,它必须重新定义模块子程序。所以,我这样做了:

sub set_basicauth {
  my ($self, $creds_username, $creds_password) = @_;

  sub SOAP::Transport::HTTP::Client::get_basic_credentials {
    return $creds_username => $creds_password;
  }

  return $self;
}

但是,当我运行代码时,它会告诉我一条消息:

Variable "$creds_username" will not stay shared at /opt/PoService.pm line 53.
Variable "$creds_password" will not stay shared at /opt/PoService.pm line 53.

我在这里做错了什么?

2 个答案:

答案 0 :(得分:1)

问题是词汇(my)变量和嵌套子例程的混合。

内部子程序关闭它使用的两个变量。然而,作为词汇,它们在每次新调用时都被重新定义,而内部(名为)子元素忠实地保持对原始值的引用。因此,整个事情可能只在第一次调用时才能正常工作。

幸运的是,我们得到了警告。添加use diagnostics;(或查看perldiag

  

变量“$ x”不会在-e第1行(#1)保持共享       (W闭包)内部(嵌套)命名子例程引用a       在外部命名子例程中定义的词法变量。

     

当调用内部子程序时,它将看到的值       外部子例程的变量与第一个之前和期间的变量一样       调用外部子程序;在这种情况下,第一次调用之后       外子程序完成后,内子程序和外子程序都没有       更长时间共享变量的公共值。换句话说,       变量将不再被共享。

     

这个问题通常可以通过制作内部子程序来解决       匿名,使用sub {}语法。当内在的匿名潜艇那个       它们是外部子程序中的引用变量       会自动反弹到这些变量的当前值。

虽然这解释了问题,但如果我理解的话,解决方案将不适用于您的目的。

一种看似合适的方法是完全限定名称

sub SOAP::Transport::HTTP::Client::get_basic_credentials {
  return $main::$creds_username => $main::$creds_password;
}

sub set_basicauth {
  my $self = shift;

  ($main::$creds_username, $main::$creds_password) = @_;

  # ... (not sure of your purpose, presumably use SOAP::)

  return $self;
}

其中$main应替换实际的包名称(如果不同)。 sub也可以放在另一个中,但是没有任何意图,因为它被编译为任何其他命名的sub。

另一种选择是使用our使变量成为全局变量。

答案 1 :(得分:1)

没有嵌套的命名潜艇。

sub set_basicauth {
  my ($self, $username, $password) = @_;

  $self->{username} = $username;
  $self->{password} = $password;
}

sub request {    
  my ($self, ...) = @_;

  my $username = $self->{username};
  my $password = $self->{password};

  local *SOAP::Transport::HTTP::Client::get_basic_credentials = sub {
    return $username => $password;
  };

  ... code that uses SOAP::Lite ...
}