262588213843476 · Gist

<?php
function num_val(&$n, $keyMode)
{
$ncopy = $n;
if (!is_scalar($n)) {
$ncopy = gettype($ncopy);
/* (string)array() throws a notice on 5.4 */
} elseif ((string)$n === (string)(int)$n) {
$n = (int)$n;
return true;
} elseif ($keyMode && is_string($n)) {
return true;
}
trigger_error("Not a valid "
. ($keyMode ? 'array key' : 'integer') . ": '$ncopy'", E_USER_WARNING);
return false;
}
function conv_part($arr, $n, $keyMode)
{
if (!is_array($arr)) {
trigger_error("The depth of the part specification is too large",
E_USER_WARNING);
return false;
}
if (empty($arr)) {
trigger_error("Tried to access part of empty array", E_USER_WARNING);
return false;
}
if (!$keyMode) {
$count = count($arr);
if ($n >= 0) {
if ($n >= $count) {
trigger_error("Index '$n' is too large for the input", E_USER_WARNING);
return false;
}
return $n;
} else {
if (-($n + 1) >= count($arr)) {
trigger_error("Index '$n' is too large in absolute value "
. "for the input", E_USER_WARNING);
return false;
}
return count($arr) + $n;
}
} else {
if ($n === false) {
return 0;
}
if ($n === true) {
return count($arr) - 1;
}
$keys = array_keys($arr);
$search = array_search($n, $keys, true);
if ($search === false) {
trigger_error("Index '$n' not found in input", E_USER_WARNING);
}
return $search;
}
}
function do_unit($read, &$write, &$newReadTargets, &$newWriteTargets,
$index, $nextType)
{
$value = array_values($read)[$index];
if ($nextType === 'none') {
$write[] = $value;
} elseif ($nextType === 'multiple') {
$newSubArray = array();
$write[] =& $newSubArray;
$newReadTargets[] = $value;
$newWriteTargets[] =& $newSubArray;
} else { //single
$newReadTargets[] = $value;
$newWriteTargets[] =& $write;
}
}
function span_is_all($start, $end, $step, $keyMode)
{
return ($step === 1 &&
(($keyMode && $start === false && $end === true)
||
(!$keyMode && $start === 0 && $end === -1)))
||
($step === -1 &&
(($keyMode && $start === true && $end === false)
||
(!$keyMode && $start === -1 && $end === 0)));
}
function do_work_span(&$readTargets, &$writeTargets,
$start, $end, $step, $type, $keyMode)
{
$newReadTargets = array();
$newWriteTargets = array();
if (($count = count($readTargets)) !== count($writeTargets)) {
trigger_error("Logic error in the algorithm", E_USER_ERROR);
}
for ($i = 0; $i < $count; $i++) {
$r = $readTargets[$i];
$w =& $writeTargets[$i];
/* As an exception, if we request all elements allow empty arrays */
if ($r === array() && span_is_all($start, $end, $step, $keyMode)) {
continue;
}
$s = conv_part($r, $start, $keyMode);
$e = conv_part($r, $end, $keyMode); /* inclusive */
if ($s === false || $e === false)
return false;
for ($j = $s;
$step > 0 && $j <= $e || $step < 0 && $j >= $e;
$j += $step)
{
do_unit($r, $w, $newReadTargets, $newWriteTargets, $j, $type);
}
}
$readTargets = $newReadTargets;
$writeTargets = $newWriteTargets;
return true;
}
function do_work_indexes(&$readTargets, &$writeTargets,
$indexes, $type, $keyMode)
{
$newReadTargets = array();
$newWriteTargets = array();
if (($count = count($readTargets)) !== count($writeTargets)) {
trigger_error("Logic error in the algorithm", E_USER_ERROR);
}
for ($i = 0; $i < $count; $i++) {
$r = $readTargets[$i];
$w =& $writeTargets[$i];
foreach ($indexes as $ind) {
$j = conv_part($r, $ind, $keyMode);
if ($j === false)
return false;
do_unit($r, $w, $newReadTargets, $newWriteTargets, $j, $type);
}
}
$readTargets = $newReadTargets;
$writeTargets = $newWriteTargets;
return true;
}
function do_work_single_index(&$readTargets, &$writeTargets,
$index, $type, $keyMode)
{
$newReadTargets = array();
$newWriteTargets = array();
if (($count = count($readTargets)) !== count($writeTargets)) {
trigger_error("Logic error in the algorithm", E_USER_ERROR);
}
for ($i = 0; $i < $count; $i++) {
$r = $readTargets[$i];
$w =& $writeTargets[$i];
$j = conv_part($r, $index, $keyMode);
if ($j === false) {
return false;
}
// var_dump('pre_do_unit', $r, $w);
do_unit($r, $w, $newReadTargets, $newWriteTargets, $j, $type);
}
$readTargets = $newReadTargets;
$writeTargets = $newWriteTargets;
return true;
}
function array_part($arr, array $definition, $keyMode = false)
{
$result = array();
$readTargets = array($arr);
$firstPart = reset($definition);
if (is_array($firstPart)) {
$result[] = array();
$writeTargets = array(&$result[0]);
} else {
$writeTargets = array(&$result);
}
for ($depth = 0;
($partSpec = current($definition)) !== false;
$depth++) {
$nextPartSpec = next($definition);
if ($nextPartSpec === false)
$nextPartSpec = 'none';
elseif (is_array($nextPartSpec))
$nextPartSpec = 'multiple';
else
$nextPartSpec = 'single';
// var_dump($readTargets, $writeTargets, $nextPartSpec);
if (is_array($partSpec)) {
if (key_exists('start', $partSpec) ||
key_exists('end', $partSpec)) {
$start = $keyMode ? false /* special value */ : 0;
$end = $keyMode ? true /* special value */ : -1;
$step = 1;
foreach ($partSpec as $k => $v) {
if ($v !== null && !num_val($v, $keyMode)) {
return false;
}
if ($k === 'start') {
if ($v === null) {
continue; /* allow null default to 1st element */
}
$start = $v;
} elseif ($k === 'end') {
if ($v === null) {
continue; /* allow null default to last element */
}
$end = $v;
} elseif ($k === 'step') {
if (!num_val($v, false)) {
return false;
}
$step = $v;
if ($step === 0) {
trigger_error("Step cannot be 0", E_USER_WARNING);
return false;
}
} else {
trigger_error("Span definitions should only include "
. "elements with keys 'start', 'end' and 'step', "
. "given '$k", E_USER_WARNING);
return false;
}
}
if ($step < 0) {
/* revert defaults for start and end
* if we're moving backwards */
if ($start === false) {
$start = true;
}
if ($end === true) {
$end = false;
}
}
if (!do_work_span($readTargets, $writeTargets,
$start, $end, $step, $nextPartSpec, $keyMode)) {
return false;
}
} else {
$previousKey = -1;
foreach ($partSpec as $k => $v) {
if ($previousKey + 1 !== $k) {
trigger_error("Part definition at depth "
. ($depth + 1) . " is not a sequential array");
}
if (!num_val($v, $keyMode)) {
return false;
}
}
if (!do_work_indexes($readTargets, $writeTargets,
$partSpec, $nextPartSpec, $keyMode)) {
return false;
}
}
} else {
if (!num_val($partSpec, $keyMode)) {
return false;
}
if (!do_work_single_index($readTargets, $writeTargets,
$partSpec, $nextPartSpec, $keyMode))
return false;
}
}
return $result[0];
}

Read the original on gist.github.com ↗