Index: modules/php/php.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/php/php.module,v
retrieving revision 1.22
diff -u -p -r1.22 php.module
--- modules/php/php.module	1 Nov 2009 21:26:44 -0000	1.22
+++ modules/php/php.module	11 Nov 2009 06:34:49 -0000
@@ -33,25 +33,36 @@ function php_permission() {
 }
 
 /**
+ * Filter process callback for PHP code filter.
+ *
+ * @see php_eval()
+ */
+function _php_eval($text) {
+  return php_eval($text);
+}
+
+/**
  * Evaluate a string of PHP code.
  *
  * This is a wrapper around PHP's eval(). It uses output buffering to capture both
  * returned and printed text. Unlike eval(), we require code to be surrounded by
- * <?php ?> tags; in other words, we evaluate the code as if it were a stand-alone
- * PHP file.
+ * &lt;?php ?&gt; tags; in other words, we evaluate the code as if it were a
+ * stand-alone PHP file.
  *
  * 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.
  *
  * @param $code
  *   The code to evaluate.
+ * @param $variables
+ *   Variables to extract into local variable scope.
  * @return
  *   A string containing the printed output of the code, followed by the returned
  *   output of the code.
  *
  * @ingroup php_wrappers
  */
-function php_eval($code) {
+function php_eval($code, array $variables = NULL) {
   global $theme_path, $theme_info, $conf;
 
   // Store current theme path.
@@ -68,6 +79,11 @@ function php_eval($code) {
   }
 
   ob_start();
+  // Allow the evaluated code to alter passed in variables by reference, but do
+  // not extract variables that already exist in the symbol table.
+  if (isset($variables)) {
+    extract($variables, EXTR_SKIP | EXTR_REFS);
+  }
   print eval('?>' . $code);
   $output = ob_get_contents();
   ob_end_clean();
@@ -128,7 +144,7 @@ function php_filter_info() {
   $filters['php_code'] = array(
     'title' => t('PHP evaluator'),
     'description' => t('Executes a piece of PHP code. The usage of this filter should be restricted to administrators only!'),
-    'process callback' => 'php_eval',
+    'process callback' => '_php_eval',
     'tips callback' => '_php_filter_tips',
     'cache' => FALSE,
   );
Index: modules/php/php.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/php/php.test,v
retrieving revision 1.18
diff -u -p -r1.18 php.test
--- modules/php/php.test	11 Oct 2009 03:07:19 -0000	1.18
+++ modules/php/php.test	11 Nov 2009 06:30:50 -0000
@@ -105,3 +105,43 @@ class PHPAccessTestCase extends PHPTestC
     $this->assertNoRaw('<option value="' . $this->php_code_format . '">', t('PHP code format not available.'));
   }
 }
+
+/**
+ * Tests PHP code evaluation.
+ */
+class PHPEvalTestCase extends PHPTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'PHP code evaluation',
+      'description' => 'Verify functionality of php_eval().',
+      'group' => 'PHP',
+    );
+  }
+
+  /**
+   * Test php_eval() functionality.
+   */
+  function testPHPEval() {
+    // Verify that content can be returned.
+    $result = php_eval('<?php echo "Simple string";');
+    $this->assertEqual($result, 'Simple string');
+
+    // Verify that variables can be passed.
+    $variables = array('string' => 'Passed string');
+    $result = php_eval('<?php echo $string;', $variables);
+    $this->assertEqual($result, $variables['string']);
+
+    // Verify that passed variables can be altered.
+    $original_string = 'Passed string';
+    $variables = array('string' => &$original_string);
+    $result = php_eval('<?php $string = "Another string"; echo $string;', $variables);
+    $this->assertNotEqual('Passed string', $original_string);
+    $this->assertEqual($result, $original_string);
+    $this->assertEqual($result, 'Another string');
+
+    // Verify that existing variables are not overwritten.
+    $variables = array('theme_path' => 'any/path');
+    $result = php_eval('<?php echo $theme_path;', $variables);
+    $this->assertNotEqual($result, 'any/path');
+  }
+}
