Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1013 diff -u -p -r1.1013 common.inc --- includes/common.inc 11 Oct 2009 06:05:53 -0000 1.1013 +++ includes/common.inc 12 Oct 2009 21:44:24 -0000 @@ -4247,52 +4247,51 @@ function drupal_system_listing($mask, $d } /** - * Hands off structured Drupal arrays to type-specific *_alter implementations. + * Hands off alterable variables to type-specific *_alter implementations. * - * This dispatch function hands off structured Drupal arrays to type-specific - * *_alter implementations. It ensures a consistent interface for all altering - * operations. + * This dispatch function hands off the passed in variables to type-specific + * hook_TYPE_alter() implementations in modules. It ensures a consistent + * interface for all altering operations. + * + * A maximum of 2 alterable arguments is supported. In case more arguments need + * to be passed and alterable, modules provide additional variables assigned by + * reference in the last $context argument: + * @code + * $context = array( + * 'alterable' => &$alterable, + * 'unalterable' => $unalterable, + * 'foo' => 'bar', + * ); + * drupal_alter('mymodule_data', $alterable1, $alterable2, $context); + * @endcode + * + * Note that objects are always passed by reference in PHP5. If it is absolutely + * required that no implementation alters a passed object in $context, then an + * object needs to be cloned: + * @code + * $context = array( + * 'unalterable_object' => clone $object, + * ); + * drupal_alter('mymodule_data', $data, $context); + * @endcode * * @param $type - * The data type of the structured array. 'form', 'links', + * A string describing the data type of the alterable $data. 'form', 'links', * 'node_content', and so on are several examples. - * @param $data - * The structured array to be altered. - * @param ... - * Any additional params will be passed on to the called - * hook_$type_alter functions. - */ -function drupal_alter($type, &$data) { - // PHP's func_get_args() always returns copies of params, not references, so - // drupal_alter() can only manipulate data that comes in via the required first - // param. For the edge case functions that must pass in an arbitrary number of - // alterable parameters (hook_form_alter() being the best example), an array of - // those params can be placed in the __drupal_alter_by_ref key of the $data - // array. This is somewhat ugly, but is an unavoidable consequence of a flexible - // drupal_alter() function, and the limitations of func_get_args(). - // @todo: Remove this in Drupal 7. - if (is_array($data) && isset($data['__drupal_alter_by_ref'])) { - $by_ref_parameters = $data['__drupal_alter_by_ref']; - unset($data['__drupal_alter_by_ref']); - } - - // Hang onto a reference to the data array so that it isn't blown away later. - // Also, merge in any parameters that need to be passed by reference. - $args = array(&$data); - if (isset($by_ref_parameters)) { - $args = array_merge($args, $by_ref_parameters); - } - - // Now, use func_get_args() to pull in any additional parameters passed into - // the drupal_alter() call. - $additional_args = func_get_args(); - array_shift($additional_args); - array_shift($additional_args); - $args = array_merge($args, $additional_args); - - foreach (module_implements($type . '_alter') as $module) { - $function = $module . '_' . $type . '_alter'; - call_user_func_array($function, $args); + * @param &$data + * The primary data to be altered. + * @param &$context1 + * (optional) An additional variable that is passed by reference. + * @param &$context2 + * (optional) An additional variable that is passed by reference. If more + * context needs to be provided to implementations, then this should be an + * keyed array as described above. + */ +function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { + $hook = $type . '_alter'; + foreach (module_implements($hook) as $module) { + $function = $module . '_' . $hook; + $function($data, $context1, $context2); } } Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.382 diff -u -p -r1.382 form.inc --- includes/form.inc 10 Oct 2009 16:48:37 -0000 1.382 +++ includes/form.inc 12 Oct 2009 21:44:24 -0000 @@ -670,20 +670,11 @@ function drupal_prepare_form($form_id, & } } - // Normally, we would call drupal_alter($form_id, $form, $form_state). - // However, drupal_alter() normally supports just one byref parameter. Using - // the __drupal_alter_by_ref key, we can store any additional parameters - // that need to be altered, and they'll be split out into additional params - // for the hook_form_alter() implementations. - // @todo: Remove this in Drupal 7. - $data = &$form; - $data['__drupal_alter_by_ref'] = array(&$form_state); - drupal_alter('form_' . $form_id, $data); - - // __drupal_alter_by_ref is unset in the drupal_alter() function, we need - // to repopulate it to ensure both calls get the data. - $data['__drupal_alter_by_ref'] = array(&$form_state); - drupal_alter('form', $data, $form_id); + // Invoke hook_form_FORM_ID_alter() implementations. + drupal_alter('form_' . $form_id, $form, $form_state); + + // Invoke hook_form_alter() implementations. + drupal_alter('form', $form, $form_state, $form_id); } Index: modules/field/field.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.api.php,v retrieving revision 1.40 diff -u -p -r1.40 field.api.php --- modules/field/field.api.php 10 Oct 2009 21:39:02 -0000 1.40 +++ modules/field/field.api.php 12 Oct 2009 21:53:49 -0000 @@ -932,15 +932,15 @@ function hook_field_attach_presave($obj_ * This hook is invoked while preprocessing the field.tpl.php template file. * * @param $variables - * The variables array is passed by reference and will be populated with field values. - * @param $obj_type - * The type of $object; e.g. 'node' or 'user'. - * @param $object - * The object with fields to render. - * @param $element - * The structured array containing the values ready for rendering. + * The variables array is passed by reference and will be populated with field + * values. + * @param $context + * An associative array containing: + * - obj_type: The type of $object; e.g. 'node' or 'user'. + * - object: The object with fields to render. + * - element: The structured array containing the values ready for rendering. */ -function hook_field_attach_preprocess_alter(&$variables, $obj_type, $object, $element) { +function hook_field_attach_preprocess_alter(&$variables, $context) { } /** @@ -1042,18 +1042,16 @@ function hook_field_attach_delete_revisi * * This hook is invoked after the field module has performed the operation. * - * @param $output - * The structured content array tree for all of $object's fields. - * @param $obj_type - * The type of $object; e.g. 'node' or 'user'. - * @param $object - * The object with fields to render. - * @param $build_mode - * Build mode, e.g. 'full', 'teaser'... - * @param $langcode - * The language in which the field values will be displayed. + * @param &$output + * The structured content array tree for all of $object's fields. + * @param $context + * An associative array containing: + * - obj_type: The type of $object; e.g. 'node' or 'user'. + * - object: The object with fields to render. + * - build_mode: Build mode, e.g. 'full', 'teaser'... + * - langcode: The language in which the field values will be displayed. */ -function hook_field_attach_view_alter($output, $obj_type, $object, $build_mode, $langcode) { +function hook_field_attach_view_alter(&$output, $context) { } /** Index: modules/field/field.attach.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v retrieving revision 1.51 diff -u -p -r1.51 field.attach.inc --- modules/field/field.attach.inc 2 Oct 2009 14:39:43 -0000 1.51 +++ modules/field/field.attach.inc 12 Oct 2009 21:53:56 -0000 @@ -1193,7 +1193,13 @@ function field_attach_view($obj_type, $o $output['#extra_fields'] = field_extra_fields($bundle); // Let other modules make changes after rendering the view. - drupal_alter('field_attach_view', $output, $obj_type, $object, $build_mode, $langcode); + $context = array( + 'obj_type' => $obj_type, + 'object' => $object, + 'build_mode' => $build_mode, + 'langcode' => $langcode, + ); + drupal_alter('field_attach_view', $output, $context); return $output; } @@ -1228,7 +1234,12 @@ function field_attach_preprocess($obj_ty } // Let other modules make changes to the $variables array. - drupal_alter('field_attach_preprocess', $variables, $obj_type, $object, $element); + $context = array( + 'obj_type' => $obj_type, + 'object' => $object, + 'element' => $element, + ); + drupal_alter('field_attach_preprocess', $variables, $context); } /** Index: modules/simpletest/tests/common.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v retrieving revision 1.83 diff -u -p -r1.83 common.test --- modules/simpletest/tests/common.test 11 Oct 2009 02:14:43 -0000 1.83 +++ modules/simpletest/tests/common.test 12 Oct 2009 21:44:25 -0000 @@ -2,6 +2,59 @@ // $Id: common.test,v 1.83 2009/10/11 02:14:43 webchick Exp $ /** + * @file + * Tests for common.inc functionality. + */ + +/** + * Tests for URL generation functions. + */ +class DrupalAlterTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'drupal_alter() tests', + 'description' => 'Confirm that alteration of arguments passed to drupal_alter() works correctly.', + 'group' => 'System', + ); + } + + function setUp() { + parent::setUp('common_test'); + } + + function testDrupalAlter() { + $array = array('foo' => 'bar'); + $object = new stdClass; + $object->foo = 'bar'; + + // Verify alteration of a single argument. + $array_copy = $array; + $array_expected = array('foo' => 'Drupal'); + drupal_alter('drupal_alter', $array_copy); + $this->assertEqual($array_copy, $array_expected, t('Single array was altered.')); + + $object_copy = clone $object; + $object_expected = clone $object; + $object_expected->foo = 'Drupal'; + drupal_alter('drupal_alter', $object_copy); + $this->assertEqual($object_copy, $object_expected, t('Single object was altered.')); + + // Verify alteration of multiple arguments. + $array_copy = $array; + $array_expected = array('foo' => 'Drupal'); + $object_copy = clone $object; + $object_expected = clone $object; + $object_expected->foo = 'Drupal'; + $array2_copy = $array; + $array2_expected = array('foo' => 'Drupal'); + drupal_alter('drupal_alter', $array_copy, $object_copy, $array2_copy); + $this->assertEqual($array_copy, $array_expected, t('First argument to drupal_alter() was altered.')); + $this->assertEqual($object_copy, $object_expected, t('Second argument to drupal_alter() was altered.')); + $this->assertEqual($array2_copy, $array2_expected, t('Third argument to drupal_alter() was altered.')); + } +} + +/** * Tests for URL generation functions. */ class CommonURLUnitTest extends DrupalUnitTestCase { Index: modules/simpletest/tests/common_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common_test.module,v retrieving revision 1.4 diff -u -p -r1.4 common_test.module --- modules/simpletest/tests/common_test.module 9 Oct 2009 01:00:03 -0000 1.4 +++ modules/simpletest/tests/common_test.module 12 Oct 2009 21:44:25 -0000 @@ -10,7 +10,6 @@ * Implement hook_menu(). */ function common_test_menu() { - $items = array(); $items['common-test/drupal_goto'] = array( 'title' => 'Drupal Goto', 'page callback' => 'common_test_drupal_goto_land', @@ -71,6 +70,37 @@ function common_test_drupal_goto_alter(& } /** + * Implement hook_TYPE_alter(). + */ +function common_test_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) { + // Alter first argument. + if (is_array($data)) { + $data['foo'] = 'Drupal'; + } + elseif (is_object($data)) { + $data->foo = 'Drupal'; + } + // Alter second argument, if present. + if (isset($arg2)) { + if (is_array($arg2)) { + $arg2['foo'] = 'Drupal'; + } + elseif (is_object($arg2)) { + $arg2->foo = 'Drupal'; + } + } + // Try to alter third argument, if present. + if (isset($arg3)) { + if (is_array($arg3)) { + $arg3['foo'] = 'Drupal'; + } + elseif (is_object($arg3)) { + $arg3->foo = 'Drupal'; + } + } +} + +/** * Implement hook_theme(). */ function common_test_theme() {