There is no problem with string eval if your program controls the string that is going to be evaluated.
Sometimes, you actually need to evaluate strings hard coded in the program. As an example, let's start with a simple iterator in the spirit of Dominus's Higher Order Perl:
sub create_iterator{
my $val = shift;
return sub { return $val += 2;}
}
my $iter_even = create_iterator(0);
my $iter_odd = create_iterator(1);
This creates two closures which iterate on even or odd numbers.
Suppose now that you want to create a generic iterator in which you could pass an arbitrary function. If you try to do that, it might look like this:
sub create_iter {
my ($code_ref, @rest) = @_;
return sub {$code_ref->();}
}
and be called with a callback function like this:
my $even = create_iter(sub {my $c; $c += 2; return $c;}, 0); # Doe
+s NOT work
That does not work because, since the $c variable is created within the callback subroutine passed to the create_iter subroutine, create_iter will not close over the variable which is defined elsewhere.
Basically, this cannot work because the callback function is not defined within the environment that would make it a closure.
As far as I can tell, there is no solution with the techniques described above. So, we cannot make a generic iterator? Yes we can, if we use eval to create the callback function at the right place, using a string passed as a parameter. For example, the following code defines a generic iterator used to generate one by one Fibonacci numbers:
use strict;
use warnings;
sub create_iter {
my ($code_string, @rest) = @_;
eval $code_string;
}
my $fibonacci = create_iter (
' return sub {my ($c, $d) = @rest;
my $e = $c + $d; @rest = ($d, $e);
return $e;}
',
1, 1);
print "Fibonacci numbers: \n";
print $fibonacci->(), " ", for 1..7;
which will print:
Fibonacci:
2 3 5 8 13 21 34
The point is that using eval makes it possible to create the closure code in the right place for the subroutine to close over the variable.
I can now use the same generic iterator to generate factorials:
my $fact = create_iter (<<'EOS'
return sub { $rest[0]++; $rest[1] *= $rest[0];};
EOS
, 1, 1);
print "\n\nFact: \n";
print $fact->(), " ", for 1..5;
which will print:
Fact:
2 6 24 120 720
The example might seem somewhat contrived, but that's the only way that I found to easily create a generic iterator when I needed one.
So, to me, string eval can in some cases be a very useful technique, and, as far as I can tell, there is no danger of malicious abuse, because the string used is actually hard-coded in the program.
Another example of useful string eval can be found in the Benchmark module, which offers the possibility to pass the function whose performance should be measured in the form of a string that is (I think) evaluated with eval.
In brief, my point is that string eval can be very useful and should not be ruled out, but, of course, can be dangerous if used, for example, with user input.
|