From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

use strict;
use v5.14.0;
use Safe;
use Workflow::Exception qw( configuration_error );
$Workflow::Condition::Evaluate::VERSION = '2.04';
my @FIELDS = qw( test );
__PACKAGE__->mk_accessors(@FIELDS);
# These get put into the safe compartment...
$Workflow::Condition::Evaluate::context = undef;
sub init {
my ( $self, $params ) = @_;
$self->SUPER::init( $params );
$self->test( $params->{test} );
unless ( $self->test ) {
configuration_error
"The evaluate condition must be configured with 'test'";
}
$self->log->info("Added evaluation condition with '$params->{test}'");
}
sub evaluate {
my ( $self, $wf ) = @_;
my $to_eval = $self->test;
$self->log->info("Evaluating '$to_eval' to see if it returns true...");
# Assign our local stuff to package variables...
$Workflow::Condition::Evaluate::context = $wf->context->param;
# Create the Safe compartment and safely eval the test...
my $safe = Safe->new();
$safe->share('$context');
local $@;
my $rv = $safe->reval($to_eval);
$self->log->debug( "Safe eval ran ok, returned: '",
( defined $rv ? $rv : '<undef>' ),
"'" );
return $rv ?
Workflow::Condition::IsTrue->new() :
Workflow::Condition::IsFalse->new();
}
1;
__END__
=pod
=head1 NAME
Workflow::Condition::Evaluate - Inline condition that evaluates perl code for truth
=head1 VERSION
This documentation describes version 2.04 of this package
=head1 SYNOPSIS
<state name="foo">
<action name="foo action">
<condition test="$context->{foo} =~ /^Pita chips$/" />
=head1 DESCRIPTION
If you've got a simple test you can use Perl code inline instead of
specifying a condition class. We differentiate by the 'test' attribute
-- if it's present we assume it's Perl code to be evaluated.
While it's easy to abuse something like this with:
<condition>
<test><![CDATA[
if ( $context->{foo} =~ /^Pita (chips|snacks|bread)$/" ) {
return $context->{bar} eq 'hummus';
}
else { ... }
]]>
</test>
</condition>
It should provide a good balance.
=head1 OBJECT METHODS
=head3 new( \%params )
One of the C<\%params> should be 'test', which contains the text to
evaluate for truth.
=head3 evaluate( $wf )
Evaluate the text passed into the constructor: if the evaluation
returns a true value then the condition passes; if it throws an
exception or returns a false value, the condition fails.
We use L<Safe> to provide a restricted compartment in which we
evaluate the text. This should prevent any sneaky bastards from doing
something like:
<state...>
<action...>
<condition test="system( 'rm -rf /' )" />
The text has access to one variable, for the moment:
=over 4
=item B<$context>
A hashref of all the parameters in the L<Workflow::Context> object
=back
=head1 SEE ALSO
=over
=item * L<Safe> - From some quick research this module seems to have been packaged with core Perl 5.004+, and that's sufficiently ancient for me to not worry about people having it. If this is a problem for you shoot me an email.
=back
=head1 COPYRIGHT
Copyright (c) 2004-2021 Chris Winters. All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
Please see the F<LICENSE>
=head1 AUTHORS
Please see L<Workflow>
=cut