first import
This commit is contained in:
@@ -0,0 +1,571 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Helper function to export features overrides.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses the identifier into indivudal parts.
|
||||
*
|
||||
* As the keys may have a period in them, cannot use explode or similair ways.
|
||||
*
|
||||
* @param $identifier
|
||||
* A string in the form <comonent>.<element>.<keys> or <component>.<element>.
|
||||
* @return
|
||||
* An array of component, element, and keys string
|
||||
* @see features_override_make_key()
|
||||
*/
|
||||
function features_override_parse_identifier($identifier) {
|
||||
$first_period = strpos($identifier, '.');
|
||||
$component = substr($identifier, 0, $first_period);
|
||||
if ($second_period = strpos($identifier, '.', $first_period + 1)) {
|
||||
$element = substr($identifier, $first_period + 1, $second_period - $first_period - 1);
|
||||
$keys = substr($identifier, $second_period + 1);
|
||||
}
|
||||
else {
|
||||
$element = substr($identifier, $first_period + 1);
|
||||
$keys = FALSE;
|
||||
}
|
||||
return array($component, $element, $keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a distinct string key from an array of keys.
|
||||
*
|
||||
* @param $keys
|
||||
* An array of keys.
|
||||
* @return
|
||||
* A string representation of the keys.
|
||||
*/
|
||||
function features_override_make_key($keys) {
|
||||
if (is_array($keys)) {
|
||||
$return_keys = array();
|
||||
foreach ($keys as $key) {
|
||||
$return_keys[] = $key['key'];
|
||||
}
|
||||
return implode('|', $return_keys);
|
||||
}
|
||||
else {
|
||||
return $keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of keys to be ignored for various exportables
|
||||
* @param $component
|
||||
* The component to retrieve ignore_keys from.
|
||||
*/
|
||||
function features_get_ignore_keys($component) {
|
||||
static $cache;
|
||||
if (!isset($cache[$component])) {
|
||||
$cache[$component] = module_invoke_all('features_override_ignore', $component);
|
||||
}
|
||||
return $cache[$component];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculautes what overrides exist for by component/element.
|
||||
*
|
||||
* @param $component_key
|
||||
* A component key that's defined via hook_features_api.
|
||||
* @param $element_key
|
||||
* A key identifieing an element that's been overriden.
|
||||
* @param $reset
|
||||
* Reset the internal cache of overrides gathered.
|
||||
* @param $all
|
||||
* If TRUE, return all overrides, otherwise only overrides not yet in an override feature
|
||||
* */
|
||||
function features_override_get_overrides($component_key = FALSE, $element_key = FALSE, $reset = FALSE, $all = TRUE) {
|
||||
static $cache;
|
||||
if (!isset($cache) || $reset) {
|
||||
$cache = array();
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
features_include();
|
||||
foreach (features_get_components() as $component => $info) {
|
||||
if (empty($info['default_hook']) || $component == 'features_override_items' || $component == 'features_overrides' || !features_get_default_alter_hook($component) | !features_hook($component, 'features_export_render')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
features_include_defaults($component);
|
||||
foreach (module_implements($info['default_hook']) as $module) {
|
||||
if ($differences = array_filter(features_override_module_component_overrides($module, $component, $reset, $all))) {
|
||||
$cache[$component] = isset($cache[$component]) ? array_merge($differences, $cache[$component]) : $differences;
|
||||
}
|
||||
}
|
||||
$cache[$component] = isset($cache[$component]) ? array_filter($cache[$component]) : array();
|
||||
}
|
||||
}
|
||||
|
||||
if ($component_key && $element_key) {
|
||||
return !empty($cache[$component_key][$element_key]) ? $cache[$component_key][$element_key] : array();
|
||||
}
|
||||
elseif ($component_key) {
|
||||
return !empty($cache[$component_key]) ? $cache[$component_key] : array();
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get overrides for specific module/component.
|
||||
*
|
||||
* @param $module
|
||||
* An enabled module to find overrides for it's components.
|
||||
* @param $component
|
||||
* A type of component to find overrides for.
|
||||
* @param $reset
|
||||
* Reset the internal cache of overrides gathered.
|
||||
* @param $all
|
||||
* If TRUE, return all overrides, otherwise only overrides not yet in an override feature
|
||||
* @return
|
||||
* An array of overrides found.
|
||||
*/
|
||||
function features_override_module_component_overrides($module, $component, $reset = FALSE, $all = TRUE) {
|
||||
static $cache = array();
|
||||
|
||||
if (isset($cache[$module][$component]) && !$reset) {
|
||||
return $cache[$module][$component];
|
||||
}
|
||||
module_load_include('inc', 'features_override', 'features_override.hooks');
|
||||
features_include();
|
||||
features_include_defaults($component);
|
||||
|
||||
// Allows overriding non-feature controlled code.
|
||||
$default_hook = features_get_default_hooks($component);
|
||||
if ($all) {
|
||||
// call hooks directly
|
||||
// could also do
|
||||
// $default = features_get_default($component, $module, FALSE, $reset);
|
||||
// but this is more efficient
|
||||
$default = module_invoke($module, $default_hook);
|
||||
}
|
||||
else {
|
||||
$default = features_get_default($component, $module, TRUE, $reset);
|
||||
}
|
||||
$normal = features_get_normal($component, $module, $reset);
|
||||
|
||||
// This indicates it is likely not controlled by features, so fetch manually.
|
||||
if (!$normal && is_array($default)) {
|
||||
$code = array_pop(features_invoke($component, 'features_export_render', $module, array_keys($default), NULL));
|
||||
if (!$code) {
|
||||
return FALSE;
|
||||
}
|
||||
else {
|
||||
$normal = eval($code);
|
||||
}
|
||||
}
|
||||
|
||||
$context = array(
|
||||
'component' => $component,
|
||||
'module' => $module,
|
||||
);
|
||||
|
||||
// Can't use _features_sanitize as that resets some keys.
|
||||
_features_override_sanitize($normal);
|
||||
_features_override_sanitize($default);
|
||||
// make a deep copy of data to prevent problems when removing recursion later
|
||||
$default_copy = unserialize(serialize($default));
|
||||
$normal_copy = unserialize(serialize($normal));
|
||||
|
||||
$ignore_keys = features_get_ignore_keys($component);
|
||||
// remove keys to be ignored
|
||||
// doing this now allows us to better control which recursive parts are removed
|
||||
if (count($ignore_keys)) {
|
||||
_features_override_remove_ignores($default_copy, $ignore_keys);
|
||||
_features_override_remove_ignores($normal_copy, $ignore_keys);
|
||||
}
|
||||
// now remove any remaining recursion
|
||||
features_override_remove_recursion($default_copy);
|
||||
features_override_remove_recursion($normal_copy);
|
||||
|
||||
$component_overrides = array();
|
||||
if ($normal && is_array($normal) || is_object($normal)) {
|
||||
foreach ($normal as $name => $properties) {
|
||||
$component_overrides[$name] = array(
|
||||
'additions' => array(),
|
||||
'deletions' => array(),
|
||||
);
|
||||
if (isset($default_copy[$name])) {
|
||||
drupal_alter('features_override_component_overrides', $default_copy[$name], $normal_copy[$name], $context);
|
||||
_features_override_set_additions($default_copy[$name], $normal_copy[$name], $component_overrides[$name]['additions'], $ignore_keys);
|
||||
_features_override_set_deletions($default_copy[$name], $normal_copy[$name], $component_overrides[$name]['deletions'], $ignore_keys);
|
||||
}
|
||||
if (!array_filter($component_overrides[$name])) {
|
||||
$component_overrides[$name] = FALSE;
|
||||
}
|
||||
}
|
||||
// now check for any elements that are in $default but not in $normal that we didn't process yet
|
||||
foreach ($default as $name => $properties) {
|
||||
if (!isset($normal_copy[$name])) {
|
||||
$_keys = array(array('type' => 'array', 'key' => $name));
|
||||
$component_overrides[$name]['deletions'][features_override_make_key($name)] = array(
|
||||
'keys' => $name,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
$cache[$module][$component] = $component_overrides;
|
||||
return $component_overrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an array by its keys (assoc) or values (non-assoc).
|
||||
*
|
||||
* @param $array
|
||||
* An array that needs to be sorted.
|
||||
*/
|
||||
function _features_override_sanitize(&$array) {
|
||||
if (is_array($array)) {
|
||||
$is_assoc = (array_keys($array) !== range(0, count($array) - 1));
|
||||
if ($is_assoc) {
|
||||
ksort($array);
|
||||
}
|
||||
else {
|
||||
sort($array);
|
||||
}
|
||||
foreach ($array as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
_features_override_sanitize($array[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the additions between default and normal features.
|
||||
*
|
||||
* @param $default
|
||||
* The default defination of a component.
|
||||
* @param $normal
|
||||
* The current defination of a component.
|
||||
* @param $additions
|
||||
* An array of currently gathered additions.
|
||||
* @param $ignore_keys
|
||||
* Keys to ignore while processing element.
|
||||
* @param $level
|
||||
* How many levels deep into object.
|
||||
* @param $keys
|
||||
* The keys for this level.
|
||||
*/
|
||||
function _features_override_set_additions(&$default, &$normal, &$additions, $ignore_keys = array(), $level = 0, $keys = array()) {
|
||||
$object = is_object($normal);
|
||||
foreach ($normal as $key => $value) {
|
||||
if (isset($ignore_keys[$key]) && ($level == $ignore_keys[$key])) {
|
||||
continue;
|
||||
}
|
||||
if ($object) {
|
||||
if (!is_object($default) || !property_exists($default, $key) || (is_scalar($value) && ($default->$key !== $value))) {
|
||||
$_keys = array_merge($keys, array(array('type' => 'object', 'key' => $key)));
|
||||
$additions[features_override_make_key($_keys)] = array(
|
||||
'keys' => $_keys,
|
||||
'value' => $value,
|
||||
'original' => (is_scalar($value) && isset($default->$key)) ? $default->$key : '',
|
||||
);
|
||||
}
|
||||
elseif (property_exists($default, $key) && ($default->$key !== $value)) {
|
||||
_features_override_set_additions($default->$key, $value, $additions, $ignore_keys, $level+1, array_merge($keys, array(array('type' => 'object', 'key' => $key))));
|
||||
}
|
||||
}
|
||||
elseif (is_array($normal)) {
|
||||
if (!is_array($default) || !array_key_exists($key, $default) || (is_scalar($value) && ($default[$key] !== $value))) {
|
||||
$_keys = array_merge($keys, array(array('type' => 'array', 'key' => $key)));
|
||||
$additions[features_override_make_key($_keys)] = array(
|
||||
'keys' => $_keys,
|
||||
'value' => $value,
|
||||
'original' => (is_scalar($value) && isset($default[$key])) ? $default[$key] : '',
|
||||
);
|
||||
}
|
||||
elseif (array_key_exists($key, $default) && (!is_null($value) && ($default[$key] !== $value))) {
|
||||
_features_override_set_additions($default[$key], $value, $additions, $ignore_keys, $level+1, array_merge($keys, array(array('type' => 'array', 'key' => $key))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the deletions between default and normal features.
|
||||
*
|
||||
* @param $default
|
||||
* The default defination of a component.
|
||||
* @param $normal
|
||||
* The current defination of a component.
|
||||
* @param $deletions
|
||||
* An array of currently gathered deletions.
|
||||
* @param $ignore_keys
|
||||
* Keys to ignore while processing element.
|
||||
* @param $level
|
||||
* How many levels deep into object.
|
||||
* @param $keys
|
||||
* The keys for this level.
|
||||
*/
|
||||
function _features_override_set_deletions(&$default, &$normal, &$deletions, $ignore_keys = array(), $level = 0, $keys = array()) {
|
||||
$object = is_object($default);
|
||||
|
||||
foreach ($default as $key => $value) {
|
||||
if (isset($ignore_keys[$key]) && ($level == $ignore_keys[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($object) {
|
||||
if (!property_exists($normal, $key)) {
|
||||
$_keys = array_merge($keys, array(array('type' => 'object', 'key' => $key)));
|
||||
$deletions[features_override_make_key($_keys)] = array(
|
||||
'keys' => $_keys,
|
||||
);
|
||||
}
|
||||
elseif (property_exists($normal, $key) && (is_array($value) || is_object($value))) {
|
||||
_features_override_set_deletions($value, $normal->$key, $deletions, $ignore_keys, $level+1, array_merge($keys, array(array('type' => 'object', 'key' => $key))));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!array_key_exists($key, $normal)) {
|
||||
$_keys = array_merge($keys, array(array('type' => 'array', 'key' => $key)));
|
||||
$deletions[features_override_make_key($_keys)] = array(
|
||||
'keys' => $_keys,
|
||||
);
|
||||
}
|
||||
elseif (array_key_exists($key, $normal) && (is_array($value) || is_object($value))) {
|
||||
_features_override_set_deletions($value, $normal[$key], $deletions, $ignore_keys, $level+1, array_merge($keys, array(array('type' => 'array', 'key' => $key))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of an array of keys.
|
||||
*
|
||||
* @param $keys
|
||||
* An array of keys with their associate types.
|
||||
*
|
||||
* @return
|
||||
* A string representation of the keys.
|
||||
*/
|
||||
function features_override_export_keys($keys) {
|
||||
$line = '';
|
||||
if (is_array($keys)) {
|
||||
foreach ($keys as $key) {
|
||||
if ($key['type'] == 'object') {
|
||||
$line .= "->{$key['key']}";
|
||||
}
|
||||
else {
|
||||
$line .= "['{$key['key']}']";
|
||||
}
|
||||
}
|
||||
}
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes recursion from an object or array.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
*/
|
||||
function features_override_remove_recursion(&$item) {
|
||||
_features_override_remove_recursion($item);
|
||||
_features_override_remove_recursion_markers($item);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to removes recursion from an object/array.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
*/
|
||||
function _features_override_remove_recursion(&$item) {
|
||||
$is_object = is_object($item);
|
||||
if ($is_object) {
|
||||
$item->{FEATURES_OVERRIDE_RECURSION_MARKER} = 1;
|
||||
}
|
||||
else {
|
||||
$item[FEATURES_OVERRIDE_RECURSION_MARKER] = 1;
|
||||
}
|
||||
foreach ($item as $key => $value) {
|
||||
if (is_array($value) || is_object($value)) {
|
||||
$remove = is_array($value) ? !empty($value[FEATURES_OVERRIDE_RECURSION_MARKER]) : !empty($value->{FEATURES_OVERRIDE_RECURSION_MARKER});
|
||||
if ($remove) {
|
||||
if ($is_object) {
|
||||
unset($item->$key);
|
||||
}
|
||||
else {
|
||||
unset($item[$key]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
features_override_remove_recursion($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to removes recursion from an object/array.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
*/
|
||||
function _features_override_remove_recursion_markers(&$item) {
|
||||
$is_object = is_object($item);
|
||||
foreach ($item as $key => $value) {
|
||||
if ($key === FEATURES_OVERRIDE_RECURSION_MARKER) {
|
||||
if ($is_object) {
|
||||
unset($item->$key);
|
||||
}
|
||||
else {
|
||||
unset($item[$key]);
|
||||
}
|
||||
}
|
||||
elseif (is_array($value) || is_object($value)) {
|
||||
_features_override_remove_recursion_markers($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to removes a set of keys an object/array.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
* @param $ignore_keys
|
||||
* Array of keys to be ignored. Values are the level of the key.
|
||||
* @param $level
|
||||
* Level of key to remove. Up to 2 levels deep because $item can still be
|
||||
* recursive
|
||||
*/
|
||||
function _features_override_remove_ignores(&$item, $ignore_keys, $level = -1) {
|
||||
$is_object = is_object($item);
|
||||
foreach ($item as $key => $value) {
|
||||
if (isset($ignore_keys[$key]) && ($ignore_keys[$key] == $level)) {
|
||||
if ($is_object) {
|
||||
unset($item->$key);
|
||||
}
|
||||
else {
|
||||
unset($item[$key]);
|
||||
}
|
||||
}
|
||||
elseif (($level < 2) && (is_array($value) || is_object($value))) {
|
||||
_features_override_remove_ignores($value, $ignore_keys, $level+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drupal-friendly var_export().
|
||||
*
|
||||
* @param $var
|
||||
* The variable to export.
|
||||
* @param $prefix
|
||||
* A prefix that will be added at the beginning of every lines of the output.
|
||||
* @return
|
||||
* The variable exported in a way compatible to Drupal's coding standards.
|
||||
*/
|
||||
function features_override_var_export($var, $prefix = '') {
|
||||
if (is_array($var) || is_object($var)) {
|
||||
// Special causing array so calls features_override_var_export instead of
|
||||
// features_var_export.
|
||||
if (is_array($var)) {
|
||||
if (empty($var)) {
|
||||
$output = 'array()';
|
||||
}
|
||||
else {
|
||||
$output = "array(\n";
|
||||
foreach ($var as $key => $value) {
|
||||
// Using normal var_export on the key to ensure correct quoting.
|
||||
$output .= " " . var_export($key, TRUE) . " => " . features_override_var_export($value, ' ', FALSE) . ",\n";
|
||||
}
|
||||
$output .= ')';
|
||||
}
|
||||
}
|
||||
// Objects do not export cleanily.
|
||||
else {
|
||||
if (method_exists($var, 'export')) {
|
||||
$output = $var->export();
|
||||
}
|
||||
elseif (get_class($var) === 'stdClass') {
|
||||
$output = '(object) ' . features_override_var_export((array) $var, $prefix);
|
||||
}
|
||||
elseif (!method_exists($var, '__set_state')) {
|
||||
// Ugly, but custom object with no clue how to export.without
|
||||
// __set_state class and var_export produces unusable code.
|
||||
$output = 'unserialize(' . var_export(serialize($var), TRUE) . ')';
|
||||
}
|
||||
else {
|
||||
$output = var_export($var, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
$output = features_var_export($var);
|
||||
}
|
||||
|
||||
if ($prefix) {
|
||||
$output = str_replace("\n", "\n$prefix", $output);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the addition/change to an element.
|
||||
*/
|
||||
function features_override_features_export_render_addition($alter, $element, $component, $is_change = TRUE) {
|
||||
module_load_include('inc', 'features_override', 'features_override.hooks');
|
||||
if (features_hook($component, 'features_override_export_render_addition')) {
|
||||
return features_invoke($component, 'features_override_export_render_addition', $alter, $element);
|
||||
}
|
||||
else {
|
||||
$code = array();
|
||||
$component_start = "\$data['$element']";
|
||||
$code_line = features_override_export_keys($alter['keys']);
|
||||
$value_export = features_override_var_export($alter['value'], ' ');
|
||||
if ($is_change) {
|
||||
$original_export = (isset($alter['original'])) ? ' /* WAS: ' . features_override_var_export($alter['original'], ' ') . ' */' : '';
|
||||
}
|
||||
else {
|
||||
$original_export = '';
|
||||
}
|
||||
$code[] = " " . $component_start . $code_line . ' = ' . $value_export . ';' . $original_export;
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the deletion to an element.
|
||||
*/
|
||||
function features_override_features_export_render_deletion($alter, $element, $component) {
|
||||
module_load_include('inc', 'features_override', 'features_override.hooks');
|
||||
if (features_hook($component, 'features_override_export_render_deletion')) {
|
||||
return features_invoke($component, 'features_override_export_render_deletion', $alter, $element);
|
||||
}
|
||||
else {
|
||||
$code = array();
|
||||
$component_start = "\$data['$element']";
|
||||
$code_line = features_override_export_keys($alter['keys']);
|
||||
$code[] = ' unset(' . $component_start . $code_line . ');';
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a string for use as option.
|
||||
|
||||
* @see features_dom_encode_options()
|
||||
* @param $string
|
||||
* A string to encode.
|
||||
* @return
|
||||
* An encoded string for use as option value.
|
||||
*/
|
||||
function features_override_encode_string($string) {
|
||||
$replacements = array(
|
||||
':' => '__'. ord(':') .'__',
|
||||
'/' => '__'. ord('/') .'__',
|
||||
',' => '__'. ord(',') .'__',
|
||||
'.' => '__'. ord('.') .'__',
|
||||
'<' => '__'. ord('<') .'__',
|
||||
'>' => '__'. ord('>') .'__',
|
||||
);
|
||||
return strtr($string, $replacements);
|
||||
}
|
||||
Reference in New Issue
Block a user