aguidrevitch has asked for the wisdom of the Perl Monks concerning the following question:

Here is the problem I'm experiencing
Undefined subroutine &Net::FTPServer::InMem::FileHandle called at /usr +/local/share/perl/5.18.2/Net/FTPServer/InMem/DirHandle.pm line 274, < +STDIN> line 6. at /usr/local/share/perl/5.18.2/Net/FTPServer.pm line 2368, <STDIN> l +ine 6. Net::FTPServer::__ANON__('Undefined subroutine &Net::FTPServer::In +Mem::FileHandle calle...') called at /usr/local/share/perl/5.18.2/Net +/FTPServer/InMem/DirHandle.pm line 274 Net::FTPServer::InMem::DirHandle::list_status('Net::FTPServer::InM +em::DirHandle=HASH(0x2a725a8)', undef) called at /usr/local/share/per +l/5.18.2/Net/FTPServer/DirHandle.pm line 171 Net::FTPServer::DirHandle::_list_status('Net::FTPServer::InMem::Di +rHandle=HASH(0x2a725a8)', undef) called at /usr/local/share/perl/5.18 +.2/Net/FTPServer.pm line 5899 Net::FTPServer::_LIST_command('Net::FTPServer::Opencart=HASH(0x2a3 +8908)', 'LIST', '-la') called at /usr/local/share/perl/5.18.2/Net/FTP +Server.pm line 3004 Net::FTPServer::run('Net::FTPServer::Opencart') called at ./ftp-se +rver.pl line 220
The Net::FTPServer::InMem::FileHandle is listed in %INC. If I replace
my $fileh = new Net::FTPServer::InMem::FileHandle
with
my $fileh = Net::FTPServer::InMem::FileHandle->new
right in the Net::FTPServer::InMem::DirHandle at line 274, it starts to work. What's wrong ? How can I fix that ?

Replies are listed 'Best First'.
Re: Constructor fails with Undefined subroutine when called as new Foo()
by haukex (Archbishop) on Feb 12, 2017 at 17:29 UTC

    Hi aguidrevitch,

    I think what is going on might be what is described in Indirect Object Syntax:

    Perl supports another method invocation syntax called "indirect object" notation. This syntax is called "indirect" because the method comes before the object it is being invoked on. ...

    my $file = new File $path, $data;

    We recommend that you avoid this syntax, for several reasons.

    ... When used with class methods, the problem is even worse. Because Perl allows subroutine names to be written as barewords, Perl has to guess whether the bareword after the method is a class name or subroutine name. In other words, Perl can resolve the syntax as either File->new( $path, $data ) or new( File( $path, $data ) ).

    To parse this code, Perl uses a heuristic based on what package names it has seen, what subroutines exist in the current package, what barewords it has previously seen, and other input. Needless to say, heuristics can produce very surprising results!

    The CPAN Testers Matrix shows that Net::FTPServer 1.122 from 2005 (prior to the latest "unauthorized" release 1.125 from 2012) has many problems on many different versions of Perl. At first glance it looks like the indirect object notation is used in quite a few places. Unfortunately, I'm at the moment not entirely sure that there's an easy fix, but perhaps another monk has a good idea.

    Regards,
    -- Hauke D

Re: Constructor fails with Undefined subroutine when called as new Foo()
by choroba (Cardinal) on Feb 12, 2017 at 17:46 UTC
    What code triggers the error? If you call the constructor with the indirect object syntax, the package must have been parsed already when parsing the line if a method of the same name exists in the current package. Cf.
    #!/usr/bin/perl use warnings; use strict; { package Known; sub new { bless {}, shift } } { package NotOO; my $m1 = new Known (); my $m2 = new Unknown (); } { package OO; sub new { bless {}, shift } my $m1 = new Known (); my $m2 = new Unknown (); # Undefined subroutine &OO:Unknown called } { package Unknown; sub new { bless {}, shift } }

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      Here is the code:
      #!/usr/bin/perl { package Net::FTPServer::Atrex::DirHandle; use strict; use warnings; use Net::FTPServer::Full::DirHandle; use Net::FTPServer::Full::FileHandle; use Net::FTPServer::InMem::DirHandle; use Net::FTPServer::InMem::FileHandle; use Data::Dumper; our @ISA = qw(Net::FTPServer::Full::DirHandle); sub open { my $self = shift; my $filename = shift; my $mode = shift; if ($filename =~ /^atx.*\.txt$/ || $mode eq 'r') { return $self->SUPER::open($filename, $mode); } my $content = ""; return new IO::Scalar (\$content); } } { package Net::FTPServer::Virtual::DirHandle; use strict; use warnings; use Net::FTPServer::Full::DirHandle; use Net::FTPServer::Full::FileHandle; use Net::FTPServer::InMem::DirHandle; use Net::FTPServer::InMem::FileHandle; use Data::Dumper; use vars qw(%dirs %files); our @ISA = qw(Net::FTPServer::InMem::DirHandle); sub get { my $self = shift; my $filename = shift; warn $filename; if ($filename eq 'images') { return new Net::FTPServer::Full::DirHandle($self->{ftps}, +$self->{ftps}->config("image directory")); } elsif ($filename eq 'atrex') { return new Net::FTPServer::Atrex::DirHandle($self->{ftps}, + $self->{ftps}->config("import directory")); } return $self->SUPER::get($filename); } } { package Net::FTPServer::Opencart; use strict; use warnings; use Cwd; use File::Slurp; use Digest::MD5 qw(md5_hex); use Net::FTPServer::InMem::Server; use Net::FTPServer::InMem::DirHandle; use Net::FTPServer::InMem::FileHandle; use Data::Dumper; our @ISA = qw(Net::FTPServer::InMem::Server); sub post_configuration_hook { my $self = shift; if (my $external_ip = $self->config("external ip")) { my $subname = "_PASV_command"; $self->{command_table}{PASV} = \&$subname; $external_ip =~ s/\./\,/g; $self->{external_ip} = $external_ip; } } sub authentication_hook { my $self = shift; my $username = shift; my $password = shift; my $user_is_anon = shift; # Deny anonymous access. return -1 if $user_is_anon; # Verify access against our config username/password combinati +ons. return 0 if $username eq $self->config("username") && md5_hex( +$password) eq $self->config("password"); # Unsuccessful login. return -1; } sub user_login_hook { } sub root_directory_hook { my $self = shift; if (!$self->{__root_directory}) { my $root = $self->{__root_directory} = Net::FTPServer::Vir +tual::DirHandle->new($self); $root->mkdir('atrex'); $root->mkdir('images'); $root->mkdir('includes'); my $includes = $root->get('includes'); if (my $handle = $includes->open("version.php", "w")) { my $content = <<'CONTENT'; ... CONTENT $handle->print($content); close($handle); } if (my $handle = $includes->open("configure.php", "w")) { my $content = <<'CONTENT'; ... CONTENT $content =~ s/_HTTP_SERVER_/$self->config("catalog url +")/e; $handle->print($content); close($handle); } } return $self->{__root_directory}; } sub _PASV_command { no warnings qw(redefine); my $self = shift; my $cmd = shift; my $rest = shift; my $reply = \&Net::FTPServer::reply; local *Net::FTPServer::reply = sub { my $self = shift; my $code = shift; my $message = shift; $message =~ s/\d+,\d+,\d+,\d+(,\d+,\d+)/$self->{external_i +p}$1/; $reply->($self, $code, $message); }; $self->SUPER::_PASV_command($cmd, $rest); } } { package main; use strict; use warnings; my $ftpd = Net::FTPServer::Opencart->run(); }
      Basically, the error gets triggered when I'm trying to cwd into includes/ directory (which is InMem::DirHandle). The rest works perfectly.