$info) { // Allow modules to alter pipe steps. drupal_alter('pipes_step', $info, $class); } } return $list; } /** * Load a specific step through invoking hook_pipes_steps. * @param $class * The class of the step to load. * @return * The specified step, or FALSE on failure. */ function pipes_step_load($class) { $steps = pipes_list_steps(); if (!isset($steps[$class])) { return FALSE; } return $steps[$class]; } /** * List all pipes. * @param $pid * The pid of a a specific pipe to load. If not set, all will be loaded. * @param $module * The module whose pipe(s) should be loaded. If not set, all will be loaded. * @param $refresh * If TRUE, will re-load the data even if already stored in the static variable. * @return * Either a specific pipe in the array or an array of pipes, depending on whether $pid was set or not. */ function pipes_list($pid = NULL, $module = NULL, $refresh = FALSE) { static $list; if (!is_null($pid)) { if (isset($list[$pid])) { return $list[$pid]; } } $regenerate = ($refresh || (is_null($pid) ? !isset($list) : (!isset($list[$pid])))); if ($regenerate) { $list = array(); $query = 'SELECT p.* psd.* psh.* FROM {pipes} p INNER JOIN {pipes_step_data} psd ON psd.pid = p.pid LEFT JOIN {pipes_step_hierarchy} psh ON psh.sid = p.sid'; $where = array(); $vars = array(); if (!is_null($pid)) { $where[] = 'p.pid = %d'; $vars[] = $pid; } if (!is_null($module)) { $where[] = 'p.module = %s'; $vars[] = $module; } if (!empty($where)) { $query .= ' WHERE '. implode(' AND ', $where) .' ORDER BY psd.weight'; $result = call_user_func_array('db_query', array_merge(array($query), $vars)); } else { $result = db_query($query .' ORDER BY psd.weight'); } while ($pipe = db_fetch_array($result)) { foreach (array('name', 'module', 'pid') as $element) { if (!isset($list[$pipe['p.pid']][$element])) { $list[$pipe['p.pid']][$element] = $pipe['p.'. $element]; } } // Multiple steps per pipe. $data = array(); $data['class'] = $pipe['psd.class']; $data['step_data'] = pipes_step_load($pipe['psd.class']); $data['destination'][] = array('step' => $pipe['psh.destination'], 'param' => $pipe['psh.param_id'], 'sid' => $pipe['psh.sid'], 'rid' => $pipe['psh.rid']); $data['settings'] = unserialize($pipe['psd.settings']); $data['sid'] = $pipe['psd.sid']; $data['weight'] = $pipe['psd.weight']; $list[$pipe['p.pid']][$pipe['psd.sid']] = $data; } } if (!is_null($pid)) { if (isset($list[$pid])) { return $list[$pid]; } return FALSE; } return $list; } /** * Fetch a pipe based on a given pipe id. * @param $pid * The pid of the pipe to load. * @return * The specified step, or FALSE on failure. */ function pipes_load($pid) { return pipes_list($pid); } /** * Main pipe callback. Executes an array of steps. * @param $steps * Either an array of steps, or the id of a pipe to load. * @param $reorder * Defaults to FALSE. If set to TRUE, will cause the steps to be re-ordered. * @return * Return value of the pipe. Unknown. */ function pipes_execute($steps, $reorder = FALSE) { if (is_numeric($steps)) { // It's a pid, so load the pipe. $steps = pipes_load($steps); } // Convert to an array of necessary. $steps = array($steps); // Reorder the pipes if called for. if ($reorder) { $steps = pipes_order_steps($steps); } // Trim irrelevant data from the steps array in order to protect it during foreach() loop. pipes_trim_steps($steps); $values = array(); $advance = TRUE; reset($steps); list($sid, $info) = each($steps); // Cycle through each of the steps in the pipe. while ($sid) { foreach ($info['step_data']['defaults'] as $key => $var) { if (is_int($var)) { $info['step_data']['defaults'][$key] = arg($var); } } $value = call_user_func_array($info['class'], ($values[$sid] + $info['settings'] + $info['step_data']['defaults'])); $result = FALSE; if (isset($info['step_data']['decision'])) { foreach ($info['step_data']['decision'] as $evaluator => $doer) { switch ($evaluator) { // empty() and isset() are language constructs, not functions, so they cannot be called the same way. case 'empty': $result = empty($value); break; case 'isset': $result = isset($value); break; default: $result = $evaluator($value); break; } if ($result) { // First, determine whether $doer is a function or a value. $matches = array(); preg_match('/(\w+)(\(\))$/', $doer, $matches); if ($matches[2] == '()') { // It's a function. $function = $matches[1]; // Pass it all the information it wants. $doer = $function($value, $steps, $values); } // Now it's definitely a value, so could either be (1) the sid of a step to go to, or (2) a return value. if (is_int($doer)) { // It's an sid. Set the appropriate step as the current step. reset($steps); while ((list($sid, $info) = each($steps)) && $sid != $doer); $advance = FALSE; } else { // It's just a simple value to return. return $doer; } if ($advance) { // We need to advance the array. reset($steps); list($sid, $info) = each($steps); } break; } } } // Terminate if this is a terminating step. if (!empty($info['terminate'])) { return $value; } if (isset($info['destination'])) { foreach ($info['destination'] as $destination_data) { $values[$destination_data['step']][$destination_data['param']] = $value; } } } // Return final value, if we reach here. return $value; } /** * Removes non-step data from a pipe array. * @param &$steps * An array of steps that need non-step data trimmed. Passed in by reference. */ function pipes_trim_steps(&$steps) { unset($steps['name']); unset($steps['module']); unset($steps['pid']); } /** * Orders steps based on which step needs which information. * @param $steps * An array of steps to order. */ function pipes_order_steps($steps) { $dependencies = array(); $new_steps = array(); foreach ($steps as $sid => $step) { // The step gives data to its destination, thus the destination is dependent on it. $dependencies[$step->destination][] = $sid; } $being_productive = TRUE; while ($being_productive && !empty($steps)) { $being_productive = FALSE; foreach ($steps as $sid => $step) { if (empty($dependencies[$sid])) { // It has no unhandled dependencies, so might as well put it in. $new_steps[$sid] = $step; // We no longer have to worry about it. unset($steps[$sid]); // We just made a change, so we are being productive. $being_productive = TRUE; // Cycle through dependencies and remove this one. foreach ($dependencies as $destination => $children) { if (($key = array_search($sid, $children, TRUE)) !== FALSE) { unset($dependencies[$destination][$key]); } } } } } if (!$being_productive) { // Circular dependency. return FALSE; } return $new_steps; } /** * Saves a pipe in the same format as received from pipe_load. * @param $pipe * The pipe to save. * @return * The pipe id of the pipe. */ function pipes_save($pipe) { $db_object = array(); foreach (array('name', 'module', 'pid') as $element) { if (isset($pipe[$element])) { $db_object[$element] = $pipe[$element]; unset($pipe[$element]); } } $db_object = (object)$db_object; if (isset($db_object->pid)) { drupal_write_record('pipes', $db_object, 'pid'); } else { drupal_write_record('pipes', $db_object); } $pid = $db_object->pid; $weight = -10; foreach ($pipe as $step => $data) { // We don't really care about step data. unset($data['step_data']); if (isset($data['destination'])) { foreach ($data['destination'] as $key => $destination_data) { $db_object = array(); $db_object['param_id'] = $destination_data['param_id']; $db_object['destination'] = $destination_data['step']; $db_object['sid'] = $destination_data['sid']; if (isset($destination_data['rid'])) { $db_object['rid'] = $destination_data['rid']; $db_object = (object)$db_object; drupal_write_record('pipes_step_hierarchy', $db_object, 'rid'); } else { $db_object = (object)$db_object; drupal_write_record('pipes_step_hierarchy', $db_object); } // It's dealt with, we don't really care about it any more. unset($pipe[$step]['destination'][$key]); } } $db_object = array(); $db_object['class'] = $data['class']; $db_object['pid'] = $pid; $db_object['weight'] = $weight; $weight++; $db_object['settings'] = serialize($data['settings']); if (isset($data['sid'])) { $db_object['sid'] = $data['sid']; $db_object = (object)$db_object; drupal_write_record('pipes_step_data', $db_object, 'sid'); } else { $db_object = (object)$db_object; drupal_write_record('pipes_step_data', $db_object); } unset($pipe[$step]); } return $pid; }