Plugin Directory

Changeset 1068480


Ignore:
Timestamp:
01/15/2015 10:20:15 AM (11 years ago)
Author:
aercolino
Message:

Spread usage of Enzymes3::grammar_rule()
Reintroduced output capturing, but only for automatically logging to the javascript console.
Made the custom error handler return true, so that fatal errors are silenced.
Improved Enzymes3::safe_eval(), also by catching and reporting parse (fatal) errors by Enzymes3::php_lint().
Added context information when logging to the javascript console.
All green tests.

Location:
enzymes/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • enzymes/trunk/src/Enzymes3.php

    r1067352 r1068480  
    3232
    3333    /**
    34      * @var bool
    35      */
    36     public $debug_on = false;
    37 
    38     /**
    39      * @param mixed $something
    40      */
    41     public
    42     function debug_print( $something )
    43     {
    44         if ( ! $this->debug_on ) {
    45             return;
    46         }
    47         fwrite(STDERR, "\n" . print_r($something, true) . "\n");
    48     }
     34     * Current sequence.
     35     *
     36     * @var string
     37     */
     38    protected $current_sequence;
     39
     40    /**
     41     * Current enzyme.
     42     *
     43     * @var string
     44     */
     45    protected $current_enzyme;
    4946
    5047    /**
     
    308305     *
    309306     * @param string $rule
    310      * @param bool $same_name
     307     * @param bool   $same_name
    311308     *
    312309     * @return string
     
    327324     * Echo a script HTML tag to write data to the javascript console of the browser.
    328325     *
    329      * @param $data
     326     * @param mixed $data
    330327     */
    331328    protected
    332329    function console_log( $data )
    333330    {
    334         $json   = json_encode($data);
    335         $output = <<<SCRIPT
    336 \n<script>
    337     (function( data ) {
    338         console = window.console || {};
    339         console.log = console.log || function(data){};
    340         console.log(data);
    341     })( $json );
    342 </script>
    343 SCRIPT;
     331        $json   = json_encode((is_array($data) || is_object($data))
     332                ? $data
     333                : trim($data));
     334        $output = "<script>if(window.console){if(window.console.log){window.console.log($json);}}</script>";
    344335        echo $output;
    345336    }
     
    359350    {
    360351        $this->last_eval_error = array('type' => $type, 'message' => $message, 'file' => $file, 'line' => $line);
    361         return false;
     352        return true;
     353    }
     354
     355    /**
     356     * Check the syntax of a code snippet.
     357     *
     358     * @param $code
     359     *
     360     * @return mixed|null|string
     361     */
     362    protected
     363    function php_lint( $code )
     364    {
     365        $result = null;
     366        if ( ! function_exists('shell_exec') ) {
     367            return $result;
     368        }
     369        $temp     = tmpfile();
     370        $meta     = stream_get_meta_data($temp);
     371        $filename = $meta['uri'];
     372        fwrite($temp, "<?php $code");
     373        $result = shell_exec("php -n -l $filename");  // -n = no ini, -l = only lint
     374        fclose($temp);
     375
     376        $result = trim($result);
     377        $result = str_replace($filename, 'enzyme code', $result);
     378        $result = str_replace("\nErrors parsing enzyme code", '', $result);
     379        return $result;
    362380    }
    363381
     
    377395    function safe_eval( $code, array $arguments = array() )
    378396    {
    379         $this->last_eval_error = null;
    380         $previous_scream       = ini_set('scream.enabled', false);
     397        $error        = $output = $exception = null;
     398        $previous_ini = array();
     399
     400        $previous_ini['scream.enabled'] = ini_set('scream.enabled', false);
    381401        set_error_handler(array($this, 'set_last_eval_error'), E_ALL);
    382402        ob_start();
    383         $result = @eval($code);
    384         ob_end_clean();
     403        try {
     404            $this->last_eval_error = null;
     405            // ---------------------------------------------------------------------------------------------------------
     406            $result = @eval($code);
     407            // ---------------------------------------------------------------------------------------------------------
     408            $error                 = $this->last_eval_error;
     409            $this->last_eval_error = null;
     410        } catch ( Exception $e ) {
     411            $result    = false;  // Let's force the same error treatment
     412            $exception = $e;     // but remember the exception now.
     413        }
     414        $output = ob_get_clean();
    385415        restore_error_handler();
    386         ini_set('scream.enabled', $previous_scream);
    387         list($error, $this->last_eval_error) = array($this->last_eval_error, null);
    388         return array($result, $error);
     416        ini_set('scream.enabled', $previous_ini['scream.enabled']);
     417
     418        if ( false === $result ) {
     419            if ( null === $error && null === $exception ) {
     420                $error = $this->php_lint($code);
     421            }
     422            if ( null === $error ) {
     423                $error = $exception;
     424            }
     425        }
     426        // Notice that error can be null, array, string, or an Exception descendant.
     427        return array($result, $error, $output);
    389428    }
    390429
     
    596635              $this->injection_author_can(EnzymesCapabilities::use_others_custom_fields))
    597636        ) {
    598             list($result, $error) = $this->safe_eval($code, $arguments);
    599             if ( is_array($error) ) {
     637            list($result, $error, $output) = $this->safe_eval($code, $arguments);
     638            if ( $error ) {
     639                $this->console_log(__('ENZYMES ERROR'));
     640                $this->console_log(sprintf(__('post: %1$s - enzyme: %3$s - injection: {[%2$s]}'),
     641                        $this->injection_post->ID, $this->current_sequence, $this->current_enzyme));
    600642                $this->console_log($error);
    601643                $result = null;
    602644            }
     645            if ( $output ) {
     646                $this->console_log(__('ENZYMES OUTPUT'));
     647                $this->console_log(sprintf(__('post: %1$s - enzyme: %3$s - injection: {[%2$s]}'),
     648                        $this->injection_post->ID, $this->current_sequence, $this->current_enzyme));
     649                $this->console_log($output);
     650            }
    603651        } else {
    604652            $result = null;
     
    621669        $this->debug_print('executing post_item');
    622670        // match again to be able to access groups by name...
    623         $expression = $this->grammar['post_item']->wrapper_set('@@')
    624                                                  ->expression(true);
    625         preg_match($expression, $post_item, $matches);
     671        preg_match($this->grammar_rule('post_item'), $post_item, $matches);
    626672        $post_object = $this->wp_post($matches);
    627673        if ( ! $post_object instanceof WP_Post ) {
     
    649695    {
    650696        $this->debug_print('executing author_item');
    651         $expression = $this->grammar['author_item']->wrapper_set('@@')
    652                                                    ->expression(true);
    653         preg_match($expression, $author_item, $matches);
     697        preg_match($this->grammar_rule('author_item'), $author_item, $matches);
    654698        $post_object = $this->wp_post($matches);
    655699        if ( ! $post_object instanceof WP_Post ) {
     
    675719    function do_execution( $execution )
    676720    {
     721        $this->current_enzyme = $execution;
    677722        preg_match($this->grammar_rule('execution'), $execution, $matches);
    678723        $post_item   = @$matches['post_item'];
     
    741786    {
    742787        $this->debug_print('transcluding post_item');
    743         $expression = $this->grammar['post_item']->wrapper_set('@@')
    744                                                  ->expression(true);
    745         preg_match($expression, $post_item, $matches);
     788        preg_match($this->grammar_rule('post_item'), $post_item, $matches);
    746789        $code   = $this->wp_post_field($post_object, $matches);
    747790        $result = $this->transclude_code($code, $post_object);
     
    762805    {
    763806        $this->debug_print('transcluding author_item');
    764         $expression = $this->grammar['author_item']->wrapper_set('@@')
    765                                                    ->expression(true);
    766         preg_match($expression, $author_item, $matches);
     807        preg_match($this->grammar_rule('author_item'), $author_item, $matches);
    767808        $user_object = $this->wp_author($post_object);
    768809        $code        = $this->wp_user_field($user_object, $matches);
     
    789830             $this->injection_author_can(EnzymesCapabilities::use_others_attributes)
    790831        ) {
    791             $expression = $this->grammar['post_attr']->wrapper_set('@@')
    792                                                      ->expression(true);
    793             preg_match($expression, $post_attr, $matches);
     832            preg_match($this->grammar_rule('post_attr'), $post_attr, $matches);
    794833            $result = $this->wp_post_attribute($post_object, $matches);
    795834        } else {
     
    817856             $this->injection_author_can(EnzymesCapabilities::use_others_attributes)
    818857        ) {
    819             $expression = $this->grammar['author_attr']->wrapper_set('@@')
    820                                                        ->expression(true);
    821             preg_match($expression, $author_attr, $matches);
     858            preg_match($this->grammar_rule('author_attr'), $author_attr, $matches);
    822859            $user_object = $this->wp_author($post_object);
    823860            $result      = $this->wp_user_attribute($user_object, $matches);
     
    838875    function do_transclusion( $transclusion )
    839876    {
     877        $this->current_enzyme = $transclusion;
    840878        preg_match($this->grammar_rule('transclusion'), $transclusion, $matches);
    841879        $post_item   = @$matches['post_item'];
     
    9801018            // after stripping out the forced version from $sequence, it any
    9811019        } else {
    982             $this->catalyzed = new EnzymesSequence();
    983             $rest            = $sequence;
     1020            $this->current_sequence = $could_be_sequence;
     1021            $this->catalyzed        = new EnzymesSequence();
     1022            $rest                   = $sequence;
    9841023            while (preg_match($this->e_sequence_start, $rest, $matches)) {
    9851024                $execution    = @$matches['execution'];
     
    10961135        return $result;
    10971136    }
     1137
     1138    // -----------------------------------------------------------------------------------------------------------------
     1139
     1140    /**
     1141     * @var bool
     1142     */
     1143    public $debug_on = false;
     1144
     1145    /**
     1146     * @param mixed $something
     1147     */
     1148    public
     1149    function debug_print( $something )
     1150    {
     1151        if ( ! $this->debug_on ) {
     1152            return;
     1153        }
     1154        fwrite(STDERR, "\n" . print_r($something, true) . "\n");
     1155    }
    10981156}
  • enzymes/trunk/tests/test-Enzymes3.php

    r1067352 r1068480  
    3333    }
    3434
     35    /**
     36     * @param      $name
     37     * @param null $args
     38     * @param null $object
     39     *
     40     * @return mixed
     41     * @throws Exception
     42     */
    3543    function call_method( $name, $args = null, $object = null )
    3644    {
     
    127135    function test_safe_eval_no_error()
    128136    {
    129         $code = 'list($name) = $arguments;
    130         echo $name;
    131         return $name;';
     137        $code = '
     138            list($name) = $arguments;
     139            echo $name;
     140            return $name;';
    132141        $name = 'Andrea';
    133142        list($result, $error) = $this->call_method('safe_eval', array($code, array($name)));
     
    137146    }
    138147
    139     function test_safe_eval_error()
    140     {
    141         $code = 'list($name) = $arguments;
    142         echo name;  // notice the error here
    143         return $name;';
     148    function test_safe_eval_undefined_error()
     149    {
     150        $code = '
     151            list($name) = $arguments;
     152            echo name;  // notice the error here
     153            return $name;';
    144154        $name = 'Andrea';
    145155        list($result, $error) = $this->call_method('safe_eval', array($code, array($name)));
    146156        $this->assertArrayHasKey('message', $error);
    147         $message = "Use of undefined constant name - assumed 'name'";
    148         $this->assertEquals($message, $error['message']);
     157        $this->assertEquals("Use of undefined constant name - assumed 'name'", $error['message']);
    149158        $this->assertEquals($name, $result);
     159        $this->expectOutputString('');
     160    }
     161
     162    function test_safe_eval_parse_error()
     163    {
     164        $code = '
     165            echo $foo
     166            echo $foo;';
     167        $enzymes = new Enzymes3();
     168        list(, $error, $output) = $this->call_method('safe_eval', array($code, array()), $enzymes);
     169        $this->assertRegExp('@^Parse error:.+(T_ECHO).+on line 3$@m', $error);
     170        $this->assertEquals('', $output);
     171        $this->expectOutputString('');
     172    }
     173
     174    function test_safe_eval_bubbling_exception()
     175    {
     176        $code = '
     177            throw new Exception("What did you expect?");';
     178        $enzymes = new Enzymes3();
     179        list(, $error, $output) = $this->call_method('safe_eval', array($code, array()), $enzymes);
     180        $this->assertInstanceOf('Exception', $error);
     181        $this->assertEquals('What did you expect?', $error->getMessage());
     182        $this->assertEquals('', $output);
     183        $this->expectOutputString('');
     184    }
     185
     186    function test_safe_eval_user_error()
     187    {
     188        $code = '
     189            trigger_error("What did you expect?", E_USER_ERROR);';
     190        $enzymes = new Enzymes3();
     191        list(, $error, $output) = $this->call_method('safe_eval', array($code, array()), $enzymes);
     192        $this->assertArrayHasKey('message', $error);
     193        $this->assertEquals("What did you expect?", $error['message']);
     194//        $this->assertEquals('', print_r($error, true));
     195        $this->assertEquals('', $output);
     196        $this->expectOutputString('');
     197    }
     198
     199    function test_safe_eval_parse_error2()
     200    {
     201        $code = '
     202            function my_function()
     203                return "hello from my function";
     204            }
     205            return my_function();';
     206        $enzymes = new Enzymes3();
     207        list(, $error, $output) = $this->call_method('safe_eval', array($code, array()), $enzymes);
     208        $this->assertRegExp('@^Parse error:.+(T_RETURN).+on line 3$@m', $error);
     209//        $this->assertEquals('', print_r($error, true));
     210        $this->assertEquals('', $output);
    150211        $this->expectOutputString('');
    151212    }
Note: See TracChangeset for help on using the changeset viewer.