(sorry for my english)

/**
 * Evaluate a string of PHP code.
 *
 * This is a wrapper around PHP's eval(). It uses output buffering to capture both returned value and printed text.
 * Allows to use variables with the given code.
 * Using this wrapper also ensures that the PHP code which is evaluated can not overwrite any variables in the calling code,
 * unlike a regular eval() call. In other words, we evaluate the code with independent variable scope.
 * Use example: list($result, $output) = drupal_eval($code_str, $variables);
 *
 * @param $code_str
 *   The code to evaluate.
 * @param $variables
 *   Variables to import to local variable scope.
 * @return
 *   An array containing the returned value and the printed output of the code.
 */
function drupal_eval($code_str, $variables = array()) {
  extract($variables);
  ob_start();
  $result = eval($code_str);
  $output = ob_get_clean();
  return array($result, $output);
}

Comments

EvanDonovan’s picture

Version: 6.x-dev » 7.x-dev

What would be the advantage of this over the current php_eval() in Drupal 7?

New features would be added to 7.x, so I'm changing the version number.

liquixis’s picture

I didn't know about php_eval(), because there is not any link to it on http://api.drupal.org/api/function/drupal_eval/6

What would be the advantage of this over the current php_eval() in Drupal 7?

This one will return result and output separately. So we could get result and treat output if it exists as an error message.
drupal_eval() initially and php_eval() now was developed afaik for theming, this version has no such specificity and usefull for any cases.
Code passed as is, without need to surround it with .

In my version there are no manipulations with $theme_path, so may be it needs to be added.
Also, if code in php_eval() will use (set) variable with name "old_theme_path", php_eval() will not work properly.

So, I think we need rename php_eval() to something associated with theming and add new drupal_eval() separately.

chx’s picture

http://api.drupal.org/api/function/php_eval/7 we develop for Drupal 7 so see this. All PHP related functionality goes in PHP module. Check http://drupal.org/node/320 on how to check out HEAD.

liquixis’s picture

http://api.drupal.org/api/function/php_eval/7 we develop for Drupal 7 so see this. All PHP related functionality goes in PHP module.

I have found http://api.drupal.org/api/function/php_eval/7 already.

Check http://drupal.org/node/320 on how to check out HEAD.

Thanks, but I already know how to check out (I use Eclipse). But I don't understand why I have to use check out for this case, to make a patch?

chx’s picture

Yes. Your original patch was against Drupal 6 but features go into Drupal 7. Also it was in common.inc and now it needs to be in php module

sivaji_ganesh_jojodae’s picture

attached is a patch which tries to merge the previous patch and php_eval in php.module.

sivaji_ganesh_jojodae’s picture

StatusFileSize
new1.29 KB

Status: Needs review » Needs work

The last submitted patch failed testing.

danielb’s picture

+1 for the idea of passing $variables to make available to the user
several of my modules already use a custom version of drupal_eval() because it was basically useless for a lot of cases if you had no data available to work with.
Anyone know what went wrong in the test? Was it the changed return value?

sun’s picture

Title: New drupal_eval() » Allow to pass local variables to php_eval()
Component: base system » php.module
Status: Needs work » Needs review
StatusFileSize
new3.05 KB

Re-rolled against HEAD.

+ added a couple of tests, including simple variable scope security test.

Status: Needs review » Needs work

The last submitted patch failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new4.08 KB

Even better. Even more useful. Even more secure.

sun’s picture

Yay, tests pass! :)

With fields + stuff in core, this patch becomes a bit more important.

danielb’s picture

Great, thanks for taking an interest here sun. I should mention it would be convenient if the php filter format could be given vars, and the filter_form() could explain available variables to the user. Haven't given that much thought though.

sun’s picture

@danielb: Please forget about passing variables to the PHP code filter. The filter system, as of now, does not support any kind of context.

This patch is about php_eval(), not about the PHP filter. I know of a couple of contributed modules that could not use php_eval() before, because the evaluated PHP code needed access to variables in the context, such as $node.

In terms of Drupal core, this facility would be very helpful for Block page visibility settings, where you can enter PHP code in case the PHP module is enabled.

chx’s picture

Why not make the default array() and spare the isset? array $foo = NULL is weird, I say.

danielb’s picture

@sun, I know the filter system doesn't support this now, but it absolutely should. Using filter formats is an ideal and consistent way to allow php input, and making the variables available there is just as important as making them available in a custom php textarea. Giving users the ability to use variables is the central vibe of this issue, and that will have to be the next step. A further benefit is that we need a consistent standard help interface for explaining to the user what variables are available for them to use, similar to the tokens fieldset.

cdale’s picture

Would it be worth while changing the function to something like this so that it can also be used for modules like CCK where they want to get an actual PHP variable back, and not HTML. I've been using the following on some of my custom projects for some time now.

function eval_php($code, $drupal_eval = TRUE, $variables = NULL) {
  global $theme_path, $theme_info, $conf;

  // Store current theme path.
  $old_theme_path = $theme_path;

  // Restore theme_path to the theme, as long as php_eval() executes,
  // so code evaluated will not see the caller module as the current theme.
  // If theme info is not initialized get the path from theme_default.
  if (!isset($theme_info)) {
    $theme_path = drupal_get_path('theme', $conf['theme_default']);
  }
  else {
    $theme_path = dirname($theme_info->filename);
  }

  ob_start();
  // We need to get tricky here, as when display errors is off and
  // there is a syntax error, PHP returns a 500 status code instead
  // of a 200. To stop this, we turn on output buffering, enable the display
  // of errors, then run our code, then reset the state of display errors.
  ob_start();
  $display_errors = ini_get('display_errors');
  ini_set('display_errors', '1');

  // Allow the code to utilise passed in variables by reference, but
  // don't overwrite existing variables.
  if (isset($variables)) {
    extract($variables, EXTR_SKIP | EXTR_REFS);
  }
  if ($drupal_eval) {
    print eval('? >'. $code); <----------------------------- REMOVE SPACE BETWEEN ? > BEFORE USING CODE!
    $output = ob_get_contents();
  }
  else {
    $output = eval($code);
  }
  ini_set('display_errors', $display_errors);
  ob_end_clean();

  // Recover original theme path.
  $theme_path = $old_theme_path;

  return $output;
}

Basically, the $drupal_eval flag modifies whether we get back php values, or a string of output. Of course, any errors that occur when returning PHP values will be silently lost. Whether this is good or bad is up for someone else to decide.

MichaelCole’s picture

#12: drupal.php-eval.12.patch queued for re-testing.

ff1’s picture

Version: 7.x-dev » 8.x-dev

Bumping to D8...

kscheirer’s picture

#12: drupal.php-eval.12.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, drupal.php-eval.12.patch, failed testing.

swentel’s picture

Project: Drupal core » PHP
Version: 8.x-dev » 8.x-1.x-dev
Component: php.module » Code
hass’s picture

Issue summary: View changes

This requires an update as some things have changed in tests.