Page MenuHomePhabricator

D20213.id48272.diff
No OneTemporary

D20213.id48272.diff

diff --git a/src/__phutil_library_init__.php b/src/__phutil_library_init__.php
--- a/src/__phutil_library_init__.php
+++ b/src/__phutil_library_init__.php
@@ -26,10 +26,9 @@
$class_name,
pht('class or interface'),
pht(
- "the class or interface '%s' is not defined in the library ".
- "map for any loaded %s library.",
- $class_name,
- 'phutil'));
+ 'The class or interface "%s" is not defined in the library '.
+ 'map of any loaded library.',
+ $class_name));
}
} catch (PhutilMissingSymbolException $ex) {
$should_throw = true;
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
@@ -542,6 +542,7 @@
'phutil_date_format' => 'utils/viewutils.php',
'phutil_decode_mime_header' => 'utils/utils.php',
'phutil_deprecated' => 'moduleutils/moduleutils.php',
+ 'phutil_describe_type' => 'utils/utils.php',
'phutil_error_listener_example' => 'error/phlog.php',
'phutil_escape_html' => 'markup/render.php',
'phutil_escape_html_newlines' => 'markup/render.php',
@@ -564,6 +565,7 @@
'phutil_implode_html' => 'markup/render.php',
'phutil_ini_decode' => 'utils/utils.php',
'phutil_is_hiphop_runtime' => 'utils/utils.php',
+ 'phutil_is_natural_list' => 'utils/utils.php',
'phutil_is_system_locale_available' => 'utils/utf8.php',
'phutil_is_utf8' => 'utils/utf8.php',
'phutil_is_utf8_slowly' => 'utils/utf8.php',
diff --git a/src/conduit/ConduitClient.php b/src/conduit/ConduitClient.php
--- a/src/conduit/ConduitClient.php
+++ b/src/conduit/ConduitClient.php
@@ -348,7 +348,7 @@
$out = array();
if (is_array($data)) {
- if (!$data || (array_keys($data) == range(0, count($data) - 1))) {
+ if (phutil_is_natural_list($data)) {
$out[] = 'A';
$out[] = count($data);
$out[] = ':';
diff --git a/src/moduleutils/PhutilBootloader.php b/src/moduleutils/PhutilBootloader.php
--- a/src/moduleutils/PhutilBootloader.php
+++ b/src/moduleutils/PhutilBootloader.php
@@ -62,6 +62,15 @@
$this->registeredLibraries[$name] = $path;
+ // If we're loading libphutil itself, load the utility functions first so
+ // we can safely call functions like "id()" when handling errors. In
+ // particular, this improves error behavior when "utils.php" itself can
+ // not load.
+ if ($name === 'phutil') {
+ $root = $this->getLibraryRoot('phutil');
+ $this->executeInclude($root.'/utils/utils.php');
+ }
+
// For libphutil v2 libraries, load all functions when we load the library.
if (!class_exists('PhutilSymbolLoader', false)) {
diff --git a/src/parser/PhutilJSON.php b/src/parser/PhutilJSON.php
--- a/src/parser/PhutilJSON.php
+++ b/src/parser/PhutilJSON.php
@@ -118,7 +118,7 @@
*/
private function encodeFormattedValue($value, $depth) {
if (is_array($value)) {
- if (empty($value) || array_keys($value) === range(0, count($value) - 1)) {
+ if (phutil_is_natural_list($value)) {
return $this->encodeFormattedArray($value, $depth);
} else {
return $this->encodeFormattedObject($value, $depth);
diff --git a/src/parser/PhutilTypeSpec.php b/src/parser/PhutilTypeSpec.php
--- a/src/parser/PhutilTypeSpec.php
+++ b/src/parser/PhutilTypeSpec.php
@@ -93,7 +93,7 @@
if (!is_array($value)) {
throw new PhutilTypeCheckException($this, $value, $name);
}
- if ($value && (array_keys($value) !== range(0, count($value) - 1))) {
+ if ($value && !phutil_is_natural_list($value)) {
throw new PhutilTypeCheckException($this, $value, $name);
}
try {
@@ -209,7 +209,7 @@
return get_class($value);
} else if (is_array($value)) {
$vtype = self::getTypeOfVector($value);
- if ($value && (array_keys($value) === range(0, count($value) - 1))) {
+ if ($value && phutil_is_natural_list($value)) {
return 'list<'.$vtype.'>';
} else {
$ktype = self::getTypeOfVector(array_keys($value));
diff --git a/src/symbols/PhutilSymbolLoader.php b/src/symbols/PhutilSymbolLoader.php
--- a/src/symbols/PhutilSymbolLoader.php
+++ b/src/symbols/PhutilSymbolLoader.php
@@ -386,11 +386,11 @@
$load_failed = null;
if ($is_function) {
if (!function_exists($name)) {
- $load_failed = pht('function');
+ $load_failed = 'function';
}
} else {
if (!class_exists($name, false) && !interface_exists($name, false)) {
- $load_failed = pht('class or interface');
+ $load_failed = 'class/interface';
}
}
@@ -400,14 +400,13 @@
$name,
$load_failed,
pht(
- "the symbol map for library '%s' (at '%s') claims this %s is ".
- "defined in '%s', but loading that source file did not cause the ".
- "%s to become defined.",
+ 'The symbol map for library "%s" (at "%s") claims this symbol '.
+ '(of type "%s") is defined in "%s", but loading that source file '.
+ 'did not cause the symbol to become defined.',
$lib_name,
$lib_path,
$load_failed,
- $where,
- $load_failed));
+ $where));
}
}
diff --git a/src/symbols/exception/PhutilMissingSymbolException.php b/src/symbols/exception/PhutilMissingSymbolException.php
--- a/src/symbols/exception/PhutilMissingSymbolException.php
+++ b/src/symbols/exception/PhutilMissingSymbolException.php
@@ -5,22 +5,24 @@
public function __construct($symbol, $type, $reason) {
parent::__construct(
pht(
- "Failed to load %s '%s': %s\n\n".
- "If you are not a developer, this almost always means that a library ".
- "is out of date. For example, you may have upgraded `phabricator` ".
- "without upgrading `libphutil`, or vice versa. It might also mean ".
- "that you need to restart Apache or PHP-FPM. Make sure all libraries ".
- "are up to date and all services have been restarted.\n\n".
- "If you are a developer and this symbol was recently added or moved, ".
- "your library map may need to be rebuilt. You can rebuild the map by ".
- "running '%s'. For more information, see:\n\n".
- "%s",
+ 'Failed to load %s "%s".'.
+ "\n\n".
+ '%s'.
+ "\n\n".
+ 'If you are not a developer, this almost always means that a library '.
+ 'is out of date. For example, you may have upgraded "phabricator/" '.
+ 'without upgrading "libphutil/", or vice versa. It might also mean '.
+ 'that you need to restart Apache or PHP-FPM. Make sure all libraries '.
+ 'are up to date and all services have been restarted.'.
+ "\n\n".
+ 'If you are a developer and this symbol was recently added or '.
+ 'moved, your library map may need to be rebuilt. You can rebuild '.
+ 'the map by running "arc liberate".'.
+ "\n\n".
+ 'For more information, see: https://phurl.io/newclasses',
$type,
$symbol,
- $reason,
- 'arc liberate',
- 'https://secure.phabricator.com/book/phabcontrib/article/'.
- 'adding_new_classes/'));
+ $reason));
}
}
diff --git a/src/utils/__tests__/PhutilUtilsTestCase.php b/src/utils/__tests__/PhutilUtilsTestCase.php
--- a/src/utils/__tests__/PhutilUtilsTestCase.php
+++ b/src/utils/__tests__/PhutilUtilsTestCase.php
@@ -918,4 +918,21 @@
}
}
+ public function testNaturalList() {
+ $cases = array(
+ array(true, array()),
+ array(true, array(0 => true, 1 => true, 2 => true)),
+ array(true, array('a', 'b', 'c')),
+ array(false, array(0 => true, 2 => true, 1 => true)),
+ array(false, array(1 => true)),
+ array(false, array('sound' => 'quack')),
+ );
+
+ foreach ($cases as $case) {
+ list($expect, $value) = $case;
+ $this->assertEqual($expect, phutil_is_natural_list($value));
+ }
+ }
+
+
}
diff --git a/src/utils/utils.php b/src/utils/utils.php
--- a/src/utils/utils.php
+++ b/src/utils/utils.php
@@ -1476,7 +1476,7 @@
}
// Don't show keys for non-associative arrays.
- $show_keys = (array_keys($var) !== range(0, count($var) - 1));
+ $show_keys = !phutil_is_natural_list($var);
$output = array();
$output[] = 'array(';
@@ -1757,3 +1757,40 @@
return (string)$value;
}
+
+
+/**
+ * Return a short, human-readable description of an object's type.
+ *
+ * This is mostly useful for raising errors like "expected x() to return a Y,
+ * but it returned a Z".
+ *
+ * This is similar to "get_type()", but describes objects and arrays in more
+ * detail.
+ *
+ * @param wild Anything.
+ * @return string Human-readable description of the value's type.
+ */
+function phutil_describe_type($value) {
+ return PhutilTypeSpec::getTypeOf($value);
+}
+
+
+/**
+ * Test if a list has the natural numbers (1, 2, 3, and so on) as keys, in
+ * order.
+ *
+ * @return bool True if the list is a natural list.
+ */
+function phutil_is_natural_list(array $list) {
+ $expect = 0;
+
+ foreach ($list as $key => $item) {
+ if ($key !== $expect) {
+ return false;
+ }
+ $expect++;
+ }
+
+ return true;
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Dec 4, 6:52 AM (2 h, 37 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6823441
Default Alt Text
D20213.id48272.diff (9 KB)

Event Timeline