Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14763541
D21293.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
D21293.id.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -612,6 +612,7 @@
'PhutilArgumentUsageException' => 'parser/argument/exception/PhutilArgumentUsageException.php',
'PhutilArgumentWorkflow' => 'parser/argument/workflow/PhutilArgumentWorkflow.php',
'PhutilArray' => 'utils/PhutilArray.php',
+ 'PhutilArrayCheck' => 'utils/PhutilArrayCheck.php',
'PhutilArrayTestCase' => 'utils/__tests__/PhutilArrayTestCase.php',
'PhutilArrayWithDefaultValue' => 'utils/PhutilArrayWithDefaultValue.php',
'PhutilAsanaFuture' => 'future/asana/PhutilAsanaFuture.php',
@@ -1619,6 +1620,7 @@
'ArrayAccess',
'Iterator',
),
+ 'PhutilArrayCheck' => 'Phobject',
'PhutilArrayTestCase' => 'PhutilTestCase',
'PhutilArrayWithDefaultValue' => 'PhutilArray',
'PhutilAsanaFuture' => 'FutureProxy',
diff --git a/src/utils/PhutilArrayCheck.php b/src/utils/PhutilArrayCheck.php
new file mode 100644
--- /dev/null
+++ b/src/utils/PhutilArrayCheck.php
@@ -0,0 +1,251 @@
+<?php
+
+final class PhutilArrayCheck
+ extends Phobject {
+
+ private $instancesOf;
+ private $uniqueMethod;
+ private $context;
+
+ private $object;
+ private $method;
+
+ public function setInstancesOf($instances_of) {
+ $this->instancesOf = $instances_of;
+ return $this;
+ }
+
+ public function getInstancesOf() {
+ return $this->instancesOf;
+ }
+
+ public function setUniqueMethod($unique_method) {
+ $this->uniqueMethod = $unique_method;
+ return $this;
+ }
+
+ public function getUniqueMethod() {
+ return $this->uniqueMethod;
+ }
+
+ public function setContext($object, $method) {
+ if (!is_object($object) && !is_string($object)) {
+ throw new Exception(
+ pht(
+ 'Expected an object or string for "object" context, got "%s".',
+ phutil_describe_type($object)));
+ }
+
+ if (!is_string($method)) {
+ throw new Exception(
+ pht(
+ 'Expected a string for "method" context, got "%s".',
+ phutil_describe_type($method)));
+ }
+
+ $argv = func_get_args();
+ $argv = array_slice($argv, 2);
+
+ $this->context = array(
+ 'object' => $object,
+ 'method' => $method,
+ 'argv' => $argv,
+ );
+
+ return $this;
+ }
+
+ public function checkValues($maps) {
+ foreach ($maps as $idx => $map) {
+ $maps[$idx] = $this->checkValue($map);
+ }
+
+ $unique = $this->getUniqueMethod();
+ if ($unique === null) {
+ $result = array();
+
+ foreach ($maps as $map) {
+ foreach ($map as $value) {
+ $result[] = $value;
+ }
+ }
+ } else {
+ $items = array();
+ foreach ($maps as $idx => $map) {
+ foreach ($map as $key => $value) {
+ $items[$key][$idx] = $value;
+ }
+ }
+
+ foreach ($items as $key => $values) {
+ if (count($values) === 1) {
+ continue;
+ }
+ $this->raiseValueException(
+ pht(
+ 'Unexpected return value from calls to "%s(...)". More than one '.
+ 'object returned a value with unique key "%s". This key was '.
+ 'returned by objects with indexes: %s.',
+ $unique,
+ $key,
+ implode(', ', array_keys($values))));
+ }
+
+ $result = array();
+ foreach ($items as $key => $values) {
+ $result[$key] = head($values);
+ }
+ }
+
+ return $result;
+ }
+
+ public function checkValue($items) {
+ if (!$this->context) {
+ throw new PhutilInvalidStateException('setContext');
+ }
+
+ if (!is_array($items)) {
+ $this->raiseValueException(
+ pht(
+ 'Expected value to be a list, got "%s".',
+ phutil_describe_type($items)));
+ }
+
+ $instances_of = $this->getInstancesOf();
+ if ($instances_of !== null) {
+ foreach ($items as $idx => $item) {
+ if (!($item instanceof $instances_of)) {
+ $this->raiseValueException(
+ pht(
+ 'Expected value to be a list of objects which are instances of '.
+ '"%s", but item with index "%s" is "%s".',
+ $instances_of,
+ $idx,
+ phutil_describe_type($item)));
+ }
+ }
+ }
+
+ $unique = $this->getUniqueMethod();
+ if ($unique !== null) {
+ if ($instances_of === null) {
+ foreach ($items as $idx => $item) {
+ if (!is_object($item)) {
+ $this->raiseValueException(
+ pht(
+ 'Expected value to be a list of objects to support calling '.
+ '"%s" to generate unique keys, but item with index "%s" is '.
+ '"%s".',
+ $unique,
+ $idx,
+ phutil_describe_type($item)));
+ }
+ }
+ }
+
+ $map = array();
+
+ foreach ($items as $idx => $item) {
+ $key = call_user_func(array($item, $unique));
+
+ if (!is_string($key) && !is_int($key)) {
+ $this->raiseValueException(
+ pht(
+ 'Expected method "%s->%s()" to return a string or integer for '.
+ 'use as a unique key, got "%s" from object at index "%s".',
+ get_class($item),
+ $unique,
+ phutil_describe_type($key),
+ $idx));
+ }
+
+ $key = phutil_string_cast($key);
+
+ $map[$key][$idx] = $item;
+ }
+
+ $result = array();
+ foreach ($map as $key => $values) {
+ if (count($values) === 1) {
+ $result[$key] = head($values);
+ continue;
+ }
+
+ $classes = array();
+ foreach ($values as $value) {
+ $classes[] = get_class($value);
+ }
+ $classes = array_fuse($classes);
+
+ if (count($classes) === 1) {
+ $class_display = head($classes);
+ } else {
+ $class_display = sprintf(
+ '[%s]',
+ implode(', ', $classes));
+ }
+
+ $index_display = array();
+ foreach ($values as $idx => $value) {
+ $index_display[] = pht(
+ '"%s" (%s)',
+ $idx,
+ get_class($value));
+ }
+ $index_display = implode(', ', $index_display);
+
+ $this->raiseValueException(
+ pht(
+ 'Expected method "%s->%s()" to return a unique key, got "%s" '.
+ 'from %s object(s) at indexes: %s.',
+ $class_display,
+ $unique,
+ $key,
+ phutil_count($values),
+ $index_display));
+ }
+
+ $items = $result;
+ }
+
+ return $items;
+ }
+
+ private function raiseValueException($message) {
+ $context = $this->context;
+
+ $object = $context['object'];
+ $method = $context['method'];
+ $argv = $context['argv'];
+
+ if (is_object($object)) {
+ $object_display = sprintf(
+ '%s->%s',
+ get_class($object),
+ $method);
+ } else {
+ $object_display = sprintf(
+ '%s::%s',
+ $object,
+ $method);
+ }
+
+ if (count($argv)) {
+ $call_display = sprintf(
+ '%s(...)',
+ $object_display);
+ } else {
+ $call_display = sprintf(
+ '%s()',
+ $object_display);
+ }
+
+ throw new Exception(
+ pht(
+ 'Unexpected return value from call to "%s": %s.',
+ $call_display,
+ $message));
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jan 24, 11:41 AM (18 h, 20 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7040194
Default Alt Text
D21293.id.diff (7 KB)
Attached To
Mode
D21293: Add a support class to simplify typechecking list-of-objects return values
Attached
Detach File
Event Timeline
Log In to Comment