Page MenuHomePhabricator

D21720.diff
No OneTemporary

D21720.diff

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
@@ -632,6 +632,8 @@
'LinesOfALargeFile' => 'filesystem/linesofalarge/LinesOfALargeFile.php',
'LinesOfALargeFileTestCase' => 'filesystem/linesofalarge/__tests__/LinesOfALargeFileTestCase.php',
'MFilterTestHelper' => 'utils/__tests__/MFilterTestHelper.php',
+ 'MethodCallFuture' => 'future/MethodCallFuture.php',
+ 'MethodCallFutureTestCase' => 'future/__tests__/MethodCallFutureTestCase.php',
'NoseTestEngine' => 'unit/engine/NoseTestEngine.php',
'PHPASTParserTestCase' => 'parser/xhpast/__tests__/PHPASTParserTestCase.php',
'PhageAction' => 'phage/action/PhageAction.php',
@@ -1695,6 +1697,8 @@
'LinesOfALargeFile' => 'LinesOfALarge',
'LinesOfALargeFileTestCase' => 'PhutilTestCase',
'MFilterTestHelper' => 'Phobject',
+ 'MethodCallFuture' => 'Future',
+ 'MethodCallFutureTestCase' => 'PhutilTestCase',
'NoseTestEngine' => 'ArcanistUnitTestEngine',
'PHPASTParserTestCase' => 'PhutilTestCase',
'PhageAction' => 'Phobject',
diff --git a/src/future/MethodCallFuture.php b/src/future/MethodCallFuture.php
new file mode 100644
--- /dev/null
+++ b/src/future/MethodCallFuture.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * Degenerate future which resolves by calling a method.
+ *
+ * $future = new MethodCallFuture($calculator, 'add', 1, 2);
+ *
+ * This future is similar to @{class:ImmediateFuture}, but may make it easier
+ * to implement exception behavior correctly. See T13666.
+ */
+final class MethodCallFuture extends Future {
+
+ private $callObject;
+ private $callMethod;
+ private $callArgv;
+
+ public function __construct($object, $method /* , ...*/ ) {
+ $argv = func_get_args();
+
+ $this->callObject = $object;
+ $this->callMethod = $method;
+ $this->callArgv = array_slice($argv, 2);
+ }
+
+ public function isReady() {
+
+ $call = array($this->callObject, $this->callMethod);
+ $argv = $this->callArgv;
+
+ $result = call_user_func_array($call, $argv);
+ $this->setResult($result);
+
+ return true;
+ }
+
+}
diff --git a/src/future/__tests__/MethodCallFutureTestCase.php b/src/future/__tests__/MethodCallFutureTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/future/__tests__/MethodCallFutureTestCase.php
@@ -0,0 +1,57 @@
+<?php
+
+final class MethodCallFutureTestCase extends PhutilTestCase {
+
+ public function testMethodCallFutureSums() {
+ $future = new MethodCallFuture($this, 'getSum', 1, 2, 3);
+ $result = $future->resolve();
+
+ $this->assertEqual(6, $result, pht('MethodCallFuture: getSum(1, 2, 3)'));
+
+ $future = new MethodCallFuture($this, 'getSum');
+ $result = $future->resolve();
+
+ $this->assertEqual(0, $result, pht('MethodCallFuture: getSum()'));
+ }
+
+ public function testMethodCallFutureExceptions() {
+ $future = new MethodCallFuture($this, 'raiseException');
+
+ // See T13666. Using "FutureIterator" to advance the future until it is
+ // ready to resolve should NOT throw an exception.
+
+ foreach (new FutureIterator(array($future)) as $resolvable) {
+ // Continue below...
+ }
+
+ $caught = null;
+ try {
+ $future->resolve();
+ } catch (PhutilMethodNotImplementedException $ex) {
+ $caught = $ex;
+ }
+
+ $this->assertTrue(
+ ($caught instanceof PhutilMethodNotImplementedException),
+ pht('MethodCallFuture: exceptions raise at resolution.'));
+ }
+
+ public function getSum(/* ... */) {
+ $args = func_get_args();
+
+ $sum = 0;
+ foreach ($args as $arg) {
+ $sum += $arg;
+ }
+
+ return $sum;
+ }
+
+ public function raiseException() {
+ // We just want to throw any narrow exception so the test isn't catching
+ // too broad an exception type. This is simulating some exception during
+ // future resolution.
+ throw new PhutilMethodNotImplementedException();
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Thu, Nov 28, 5:57 PM (19 h, 27 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6801615
Default Alt Text
D21720.diff (3 KB)

Event Timeline