function coder_upgrade_convert_return in Coder 7.2
Same name and namespace in other branches
- 7 coder_upgrade/conversions/tool.inc \coder_upgrade_convert_return()
Initiates the transformation of array assignments in a hook.
Applies to: hook_action_info(), hook_hook_info(), hook_node_info(), hook_theme().
NOTE In general, there are 3 typical cases (or code styles):
- return array('key1' => array('key2' => ...);
- $var = array('key1' => array('key2' => ...); return $var;
- $var = array(); $var['key1'] = array('key2' => ...); return $var;
The inner array to modify is 3 levels deep in the first 2 cases, but only 2 levels deep in the third. In the first 2 cases, we can loop on the key1 arrays. In the third, the loop is on assignment statements.
This new routine was failing on hook_hook_info() because the keys are not distinguishable. Ie, we can not tell what level of the array we are on if the array is inline and has 3 levels. Previously, return_case1() would strip off the first layer and get to the second level. There is no guarantee of nice code anyway.
This new routine was failing on capturing drupal_get_form() callbacks on case 5, like example_admin_form3() defined in $items[$admin_path]['page arguments'][] = 'example_admin_form3'. This again relates to a depth parameter and that we are always looking for an array.
Should we pass a depth parameter, or figure it dynamically?
Parameters
PGPList $body: List of statements in a block.
string $hook: Name of the hook being modified.
string $callback: A string of the callback function for the hook.
integer $start_depth: The starting depth of nested arrays to traverse.
integer $remaining_depth: The remaining depth of nested arrays to traverse. When equal to zero, stop.
6 calls to coder_upgrade_convert_return()
- coder_upgrade_cache_info_hooks in coder_upgrade/
conversions/ begin.inc - Caches hook_theme() and hook_menu() entries for the modules being converted.
- coder_upgrade_upgrade_hook_action_info_alter in coder_upgrade/
conversions/ function.inc - Implements hook_upgrade_hook_action_info_alter().
- coder_upgrade_upgrade_hook_hook_info_alter in coder_upgrade/
conversions/ function.inc - Implements hook_upgrade_hook_hook_info_alter().
- coder_upgrade_upgrade_hook_node_info_alter in coder_upgrade/
conversions/ function.inc - Implements hook_upgrade_hook_node_info_alter().
- coder_upgrade_upgrade_hook_perm_alter in coder_upgrade/
conversions/ function.inc - Implements hook_upgrade_hook_perm_alter().
File
- coder_upgrade/
conversions/ tool.inc, line 320 - Provides tools to assist with conversion routines.
Code
function coder_upgrade_convert_return(&$body, $hook, $callback = '', $start_depth = 0, $remaining_depth = -1) {
// DONE
cdp("inside " . __FUNCTION__);
// Initialize.
$editor = PGPEditor::getInstance();
$callback = $callback == '' ? "coder_upgrade_callback_{$hook}" : $callback;
$msg = '// TODO The array elements in this hook function need to be changed.';
// Get a list of return statements in the body statements.
$nodes = $body
->searchAll('PGPFunctionCall', 'name', 'value', 'return', TRUE);
// Keep track of return variables to avoid redundant searching.
$already_searched = array();
while (!empty($nodes)) {
cdp('while (!empty($nodes)) ' . __FUNCTION__);
$return_node = array_shift($nodes);
$return = $return_node->data;
// Evaluate the return operand.
if (get_class($return) == 'PGPFunctionCall') {
$value = $return
->getParameter();
$depth = 0;
}
elseif (get_class($return) == 'PGPAssignment') {
// Get the operands to the right of the assignment operator.
$value = $return
->getValue();
cdp($return
->toString(), '$return AFTER');
// Evaluate the "depth" of the assignment based on number of indices in
// the assignment variable.
// Examples: the operand on the RHS is at
// level 1: $var = array(key => array(..), ..)
// level 2: $var[key1] = array(..)
// level 3: $var[key1][key2] = array(..)
// This mimics what was done in return_caseN(). We went down to level 2
// from case1 to case3.
$assign_variable = $return->values
->getElement()
->getType('operand')
->stripComments();
// @todo This should handle most cases, but will fail depending on code style.
// If depth is > start_depth (like case5), then reconstruct an array at
// the desired depth using toString() and reparsing (see case5).
$depth = $assign_variable
->countType('index');
if ($depth > $start_depth) {
// @todo This works in some use cases (menu stuff in begin.inc). If we
// need to change the original expression, this fails because $value is
// now disjoint from the original expression.
$value = coder_upgrade_reconstruct_array($assign_variable, $value);
$depth = 1;
// $assign_variable->countType('index');
}
}
cdp($value
->toString(), '$value');
$occurrence = 1;
// Loop on all operands in expression (e.g. $array + array(..)).
while ($occurrence < $value
->countType('operand') + 1) {
$operand = $value
->getType('operand', $occurrence);
if ($operand) {
cdp('inside if ($operand)');
if (!is_object($operand)) {
// @todo This hits stuff like $items[$admin_path]['page arguments'][] = 'example_admin_form3';
cdp('!is_object($operand)');
cdp($operand, '$operand');
$occurrence++;
continue;
}
cdp($operand
->toString(), '$operand');
if (get_class($operand) == 'PGPArray') {
// Use case 1 - returns array directly.
$operand
->traverse2($return_node, $hook, $callback, $start_depth - $depth, $remaining_depth);
}
elseif (get_class($operand) == 'PGPOperand') {
// Avoid redundant searching.
if (in_array($operand
->stripComments()
->toString(), $already_searched)) {
$occurrence++;
continue;
}
/*
* Search body statements for all assignments to the return variable.
* The assignment could be to an array element like $info['node_type_name'] = array(...).
* Or directly to the variable like $info = array('node_type_name' => array(...)).
*/
$already_searched[] = $return_variable = $operand
->stripComments()
->toString();
// @todo Limit search to nodes preceding the statement.
$nodes = array_merge($nodes, $body
->searchAll('PGPAssignment', 'values', 0, $return_variable, TRUE));
}
else {
// @todo This message is not accurate -- this error hits on the array value being a string or function call.
clp("ERROR: operand of return statement is not an array or variable in hook_{$hook}");
cdp("ERROR: operand of return statement is not an array or variable in hook_{$hook}");
cdp($operand, '$operand');
$body
->insertFirst($editor
->commentToStatement($msg), 'comment');
}
}
$occurrence++;
}
}
}