Does this code use Safe.pm?

In our company, we used this code (given at the end) for about 10 years, and it worked fine.

A few days ago we encountered some problems, and we had to transcode the complete package, we decided to replace this code with the Switch Damian module (to improve the readability of the code).

Everything works great for us.

I later found in Perlmonks that Damian put this module under

Damian modules that you should not use in production, since their purpose is to study and prototype future functions of the main language.

But it works great for us, because we do not encounter the limitations of this module (I think).

Now I ask you guys to take a look at both implementations (nested if else vs switch), and let me know if using Switch is good in the new version, or are we creating some future problems for you? Is the use of Switch in the code below accurate or are there hidden errors / issues?

I already read about the errors and reviews of this module on CPAN and Perlmonks, and I think our code is far from getting into these errors (I think so).

We are using Perl 5.8.5 .

PS: I know the alternatives to Switch, we have given/when in Perl 5.10, we can use the dispatch table and other solutions that are listed here , but right now we just want to compare the new implementation that uses Switch.

Using nested if else

 if ($command =~ /^enter$/) { $self->show_main_frames(); } elsif ($command =~ /^XYZ_MENU/i) { $self->show_main_menu($manual, $dbot); } elsif ($command =~ /^DBOT/i) { $dbot->process(); } # XML is used for the reminders-history: Request 2666 elsif ($command =~ /^XML_DBOT/i) { $dbot->process(); } elsif ($command =~ /^UGS/i) { $ugsui->process(); } elsif ($command eq "kill") { my $login = $self->{COMMON_HASH}{login} || ""; my $su_login = $self->{CONF}->get("start", "SU_LOGIN"); if ($login eq $su_login) { # usually only certain user with certain permission will be # able to do this. $self->do_error("Daemon was killed by ".$login); $self->db_connection->disconnect(); $self->{LOG}->write("User $login killed the daemon", 0); exit; # this 'exit' actually kill the daemon } else { $self->do_error("User $login tried to kill the daemon. ". "This incident will be reported"); $self->{LOG}->write("User $login tried to kill the daemon", 2); } } elsif ($command eq "logout") { # check if we should delete the password cookie my $forget_me = $self->{CGI}->param("forget_me") || 0; if ($forget_me) { $self->{DB_PASSWORD_COOKIE}->delete_cookie(); } $ugsui->do_logout(); # Cliff edit remove id from logged_in $session->remove_session($session->login()); # delete the session of the user delete $self->{SESSIONS}{$session->id()}; if ($self->{CACHE_TO_FILE}) { my $session_data_path = XYZ_DIR ."/code/cache/session_data" .$session->id(); unlink($session_data_path); } } # if we just login we should create all the main frames elsif ($command eq "login") { # if extra_param holds "command*XXX" the XXX will be placed instead of # the command. extra_param holds pairs that are astrics-separated my $extra_param = $cgi->param("extra_param"); $extra_param = "" if (!defined($extra_param)); $extra_param =~ /command\*([^\*]+)/i; my $other_command = defined($1) ? $1 : ""; if ($other_command =~ /^dbot/i) { # meanwhile - works only on dbot # commands $command = $other_command; # now we will get the other parameters from the extra_param # (actually including the command that is still in the # $extra_param) while ($extra_param =~ /^\*?([^\*]+)\*([^\*]+)(.*)/) { $extra_param = $3; my $name = $1; my $value = $2; $cgi->param(-name => $name, -value => $value); }#end while }#end if else{ $self->show_main_frames(); } }#end elsif else { $self->show_main_frames(); }#end outer else 

Using switch

 switch ($command) { case /^enter$/ { $self->show_main_frames() } case /^XYZ_MENU/i { $self->show_main_menu($manual, $dbot) } case /^DBOT/i { $dbot->process() } case /^XML_DBOT/i { $dbot->process() } case /^UGS/i { $ugsui->process() } case "kill" { my $login = $self->{COMMON_HASH}{login} || ""; my $su_login = $self->{CONF}->get("start", "SU_LOGIN"); if ($login eq $su_login) { # usually only certain user with certain permission will be # able to do this. $self->do_error("Daemon was killed by ".$login); $self->db_connection->disconnect(); $self->{LOG}->write("User $login killed the daemon", 0); exit; # this 'exit' actually kill the daemon } else { $self->do_error("User $login tried to kill the daemon. ". "This incident will be reported"); $self->{LOG}->write("User $login tried to kill the daemon", 2); } } case "logout" { # check if we should delete the password cookie my $forget_me = $self->{CGI}->param("forget_me") || 0; if ($forget_me) { $self->{DB_PASSWORD_COOKIE}->delete_cookie(); } $ugsui->do_logout(); # Cliff edit remove id from logged_in $session->remove_session($session->login()); # delete the session of the user delete $self->{SESSIONS}{$session->id()}; if ($self->{CACHE_TO_FILE}) { my $session_data_path = XYZ_DIR ."/code/cache/session_data" .$session->id(); unlink($session_data_path); } } case "login" { # if extra_param holds "command*XXX" the XXX will be placed instead of # the command. extra_param holds pairs that are astrics-separated my $extra_param = $cgi->param("extra_param"); $extra_param = "" if (!defined($extra_param)); $extra_param =~ /command\*([^\*]+)/i; my $other_command = defined($1) ? $1 : ""; if ($other_command =~ /^dbot/i) { # meanwhile - works only on dbot # commands $command = $other_command; # now we will get the other parameters from the extra_param # (actually including the command that is still in the # $extra_param) while ($extra_param =~ /^\*?([^\*]+)\*([^\*]+)(.*)/) { $extra_param = $3; my $name = $1; my $value = $2; $cgi->param(-name => $name, -value => $value); }#end while }#end if else {$self->show_main_frames();} } else {$self->show_main_frames();} } # end switch 
+4
source share
3 answers

In fact, the Switch module does not provide you with any “killer function”; the same can be done with the elsif instruction, which is safe, stable, and without the drawbacks that Switch makes. Here are the problems with Switch that I got in my project (and I no longer use it):

The switch is configured for Perl filtering. This method has the following limits:

  • The source code is actually rewritten on the fly and is replaced by the elsif sequence.
  • Some Perl error reports will refer to an invalid string; some of them show code that you do not have in your source (auto-generated code).

Filter limit, but module limit:

  • If the file (.pl or .pm) in which you are calling , use Swtich that is larger than 1 MB, this can lead to "mysterious errors" (as written in the document). I can confirm these errors without leading to the Switch module, and it is absolutely impossible, so you can have a hard debug time after several weeks of coding / documentation.

I recommend using the elsif or given..when , which are available with Perl 5.10. Therefore, if you use perl 5.8.x - use elsif.

You can also read the “Limitations” paragraph for Switch documentation.

+7
source

The switch performs its own source code analysis. This can make it difficult to diagnose errors in code that uses it directly. The problems associated with creating the switch are not intermittent, so if your code works, you have nothing to worry about.

But actually it does not add much.

With switch:

 switch ($command) { case /^enter$/ { $self->show_main_frames() } case /^XYZ_MENU/i { $self->show_main_menu($manual, $dbot) } case /^DBOT/i { $dbot->process() } case /^XML_DBOT/i { $dbot->process() } case /^UGS/i { $ugsui->process() } case "kill" { my $login = $self->{COMMON_HASH}{login} || ""; 

Without switch:

 for ($command) { if (/^enter$/) { $self->show_main_frames() } elsif (/^XYZ_MENU/i) { $self->show_main_menu($manual, $dbot) } elsif (/^DBOT/i) { $dbot->process() } elsif (/^XML_DBOT/i) { $dbot->process() } elsif (/^UGS/i) { $ugsui->process() } elsif ($_ eq "kill") { my $login = $self->{COMMON_HASH}{login} || ""; 

( elsif (/^kill\z/) will also work.)

+8
source

Because Switch performs its own parsing of the source code, it does not work at all under certain circumstances. For example, you cannot use it with mod_perl.

However, if you have Perl 5.10 or later, there is a much better replacement with effective, identical functionality: given / when

 use v5.10; given ($var) { when (/^abc/) { $abc = 1 } when (/^def/) { $def = 1 } when (/^xyz/) { $xyz = 1 } default { $nothing = 1 } } 

given supported by the Perl kernel (and works everywhere, including mod_perl) - you just use v5.10; and it is instantly available to you.

+4
source

Source: https://habr.com/ru/post/1447657/


All Articles