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

I am taking raw Perl in a form (maybe, I might go back to file based code in this case but I'm interested in exploring it this way first). I want do the equivalent of `perl -c` on it because I want to see it's valid without evaling it; which might pollute namespaces or cause other problems. I'm at a bit of a loss where to go with this. In pseudo-code-

my $snippet = $c->request->param("filter"); my $package = __PACKAGE__ . "::" . get_uuid_name(); my $code = "package $package; use strict; use warnings;"; $code .= $snippet; $code .= ";\n1;"; # ... ? my $perl = qx/ which perl /; my $result = qx/ perl -c $code /; # CAN this work? If so, how? I'm a s +hell-tard.

Or can I "safely" eval it and remove it from the @INC by something like delete $INC{$generated_package_name}? I might like to do that so I could deparse or plumb the namespace anyway...?

I realize this is a bit of a fuzzy SOPW but I'm just exploring so looking for any bones, table scraps, or clues.

I'm also thinking of forcing it into a sub so the wrapper puts the user supplied code into "package ...; sub run { my ( $vars, $you, $get ) = @_; YOUR CODE HERE }" etc. I think is safer/easier but I want to allow the user to initialize things in the package for efficiency, sub-classing, whatnot.

Replies are listed 'Best First'.
Re: How to do a "perl -c(w)" check instead of an "eval $string" on a code string
by jdporter (Paladin) on Jan 20, 2009 at 03:41 UTC

    Well, you can set $^C at runtime (or rather, at BEGIN time):

    eval 'BEGIN { $^C = 1 } ' . $code; # where $code contains what you wan +t to test

    then test $@ for any compile error messages.

    Of course, eval has problems, as you've already discussed; so to deal with that, you could use Safe:

    use Safe; my $compartment = new Safe; $compartment->reval( 'no warnings; BEGIN { $^C = 1 } ' . $code );

    Tested. :-)

    Between the mind which plans and the hands which build, there must be a mediator... and this mediator must be the heart.

      That's hawt++.

Re: How to do a "perl -c(w)" check instead of an "eval $string" on a code string
by shmem (Chancellor) on Jan 20, 2009 at 00:18 UTC
    $code .= ";\n1;"; # ... ? my $perl = qx/ which perl /; my $result = qx/ perl -c $code /; # CAN this work? If so, how? I'm a s +hell-tard.

    Yes, it can. The my $perl = qx/ which perl / is somewhat pointless, since your are already running perl, so how you've invoked it is in $^X.

    my $result = qx/perl -c -e '$code' 2>&1/; # bundle STDOUT and STDERR

    should give you either

    -e syntax OK

    or any error which you can then examine. For simple snippets without quotes, that is! Otherwise you'd grab File::Temp, write a tempfile and examine

    my $result = qx/perl -c $tempfile 2>&1/;

    update:

    WRT evaling - you could switch to some other package before the eval

    package My::Eval::Evil; eval $code; if ($@) { .... } package main; # or whatever package you've been in

    and then throw away the symbol tables for My::Eval::Evil and the eval'ed code's package. That reduces the risk of name space pollution a bit. But the eval'ed code could still reach into your package (via examining %INC or writing into *main::), so the safest (for some value of safety) is writing a tempfile and shell out a brand new perl.

    perl -e 'sub foo {print "foo\n" } package Foo; my $code = "*main::foo += sub {print \"bar\\n\" }"; eval $code; package main; foo()' bar

      Also nice and good tips. I don't know why I didn't consider tempfiles already. I use them all the time and I've got a uuid already so it can go in the app's files. Sigh.

        If you're interested in success/failure only, you could run system LIST and check the exit code. That would fix the quoting issue of -e:

        system $^X, '-wce', $code and warn "syntax check failed\n";
Re: How to do a "perl -c(w)" check instead of an "eval $string" on a code string
by doom (Deacon) on Jan 20, 2009 at 00:14 UTC
    You're on the right track, but you need to know about the -e command line switch (try reading man perlrun). So you'll do something like this:
    my $result = qx/ perl -cw -e '$code' 2>&1/;
    Where you'll then need to scrape the $result string to find out if it passed or failed.

    There's another angle to the problem though, you'll need to run the $code through some sort of shell quoting routine, to make sure you don't confuse the shell with characters in perl that also happen to be significant to the shell. I think the cpan module String::ShellQuote works okay for this.

    (Note: updated, stealing a hint from shmem to append 2>&1 to the command, this makes sure you'll capture error messages to $result.)

      Nice. I know -e, I figured it was out because of shell quoting but you've put me on the right track to at least play with that. Of course now that I write that I realize I could just open a pipe to perl...

Re: How to do a "perl -c(w)" check instead of an "eval $string" on a code string
by merlyn (Sage) on Jan 20, 2009 at 00:26 UTC

      Yeppers. I was thinking about use but I tend to forget about BEGIN; and INIT and CHECK for that matter. And I discover this minute that there is a UNITCHECK at all... Time to do some (re)reading.