diff --git a/source/lib/SihnonFramework/Config.class.php b/source/lib/SihnonFramework/Config.class.php index 50a84e6..1f804ab 100644 --- a/source/lib/SihnonFramework/Config.class.php +++ b/source/lib/SihnonFramework/Config.class.php @@ -32,6 +32,12 @@ class SihnonFramework_Config { */ const TYPE_STRING_LIST = 'array(string)'; + /** + * Hash type with string keys and mixed-type values + * @var array(string=>mixed) + */ + const TYPE_HASH = 'hash'; + /** * Backend to be used for this Config object * @var Sihnon_Config_IPlugin @@ -58,9 +64,11 @@ class SihnonFramework_Config { protected static function pack($type, $value) { switch ($type) { - case static::TYPE_STRING_LIST: { + case static::TYPE_STRING_LIST: return join("\n", $value); - } break; + + case static::TYPE_HASH: + return join("\n", array_map(function($k, $v) { return "{$k}:{$v}"; }, array_keys($value), array_values($value))); default: { return $value; @@ -70,9 +78,17 @@ class SihnonFramework_Config { protected static function unpack($type, $value) { switch ($type) { - case self::TYPE_STRING_LIST: + case static::TYPE_STRING_LIST: + // foo + // bar return array_map('trim', explode("\n", $value)); + case static::TYPE_HASH: + // foo:bar + // baz:quz + preg_match_all("/^([^:]+):(.+)$/m", $value, $pairs); + return array_combine($pairs[1], $pairs[2]); + default: return $value; } diff --git a/source/lib/SihnonFramework/Main.class.php b/source/lib/SihnonFramework/Main.class.php index 0f6917b..754dbda 100644 --- a/source/lib/SihnonFramework/Main.class.php +++ b/source/lib/SihnonFramework/Main.class.php @@ -333,32 +333,74 @@ class SihnonFramework_Main { return $default; } - public static function formatDuration($time) { - if (is_null($time)) { - return 'unknown'; - } + public static function formatDuration($seconds, $fuzziness = 0) { + if (is_null($seconds)) { + return 'indeterminate time'; + } + + $labels = array('second', 'minute', 'hour', 'day', 'week', 'month', 'year'); + $pluralLabels = array('seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'); + $limits = array(1, 60, 3600, 86400, 604800, 2592000, 31556926, PHP_INT_MAX); + $components = array(0, 0, 0, 0, 0, 0, 0); + + $workingTime = $seconds; + + $result = ""; + $ptr = count($labels) - 1; - $labels = array('seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'); - $limits = array(1, 60, 3600, 86400, 604800, 2592000, 31556926, PHP_INT_MAX); + while ($ptr >= 0 && $workingTime < $limits[$ptr]) { + --$ptr; + } + $mostSignificantPtr = $ptr; + + // Convert the value into components using the remaining labels + while ($ptr >= 0) { + $unitTime = floor($workingTime / $limits[$ptr]); + $workingTime -= $unitTime * $limits[$ptr]; + $components[$ptr] = $unitTime; + --$ptr; + } + + $componentsUsed = 0; + $approximate = false; + $lastComponent = false; + $ptr = $mostSignificantPtr; + while ($ptr >= 0) { + if ($fuzziness && $componentsUsed >= $fuzziness) { + break; + } elseif ($ptr == 0 || ($fuzziness && $componentsUsed == $fuzziness-1)) { + $lastComponent = true; + } + + $component = $components[$ptr]; + if ($component) { + // If we're going to hide the next value, take its component into account here + if ($lastComponent && $ptr > 0) { + $component += round($components[$ptr-1] / $limits[$ptr]); + $approximate = true; + } + + if ($lastComponent && $ptr < $mostSignificantPtr) { + $result .= ' and'; + } + + $result .= ' ' . $component . ' ' . ($component == 1 ? $labels[$ptr] : $pluralLabels[$ptr]); + + } + + // Increment even if we've hidden this component because it's zero + // Then we don't end up with overly precise times like '2 years and 1 second' + ++$componentsUsed; + + --$ptr; + } - $working_time = $time; + if ($approximate) { + $result = 'approximately ' . $result; + } - $result = ""; - $ptr = count($labels) - 1; - - while ($ptr >= 0 && $working_time < $limits[$ptr]) { - --$ptr; - } - - while ($ptr >= 0) { - $unit_time = floor($working_time / $limits[$ptr]); - $working_time -= $unit_time * $limits[$ptr]; - $result = $result . ' ' . $unit_time . ' ' . $labels[$ptr]; - --$ptr; - } - - return $result; - } + return $result; + } public static function formatFilesize($bytes) { if (is_null($bytes)) {