Skip to content

Commit 0910213

Browse files
committed
Merge remote-tracking branch 'origin/ignore-non-functions'
* origin/ignore-non-functions: Refactor php error collection to it own class Fix method names and constants Fix data provider array Trigger a E_USER_NOTICE for invalid arguments passed to then() Ignore non-function callback arguments
2 parents 8fe0e60 + 9403107 commit 0910213

9 files changed

Lines changed: 162 additions & 4 deletions

File tree

phpunit.xml.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
backupStaticAttributes="false"
55
colors="true"
66
convertErrorsToExceptions="true"
7-
convertNoticesToExceptions="true"
7+
convertNoticesToExceptions="false"
88
convertWarningsToExceptions="true"
99
processIsolation="false"
1010
stopOnFailure="false"

src/React/Promise/Deferred.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public function then($fulfilledHandler = null, $errorHandler = null, $progressHa
1818

1919
$deferred = new static();
2020

21-
if ($progressHandler) {
21+
if (is_callable($progressHandler)) {
2222
$progHandler = function ($update) use ($deferred, $progressHandler) {
2323
try {
2424
$deferred->progress(call_user_func($progressHandler, $update));
@@ -27,6 +27,10 @@ public function then($fulfilledHandler = null, $errorHandler = null, $progressHa
2727
}
2828
};
2929
} else {
30+
if (null !== $progressHandler) {
31+
trigger_error('Invalid $progressHandler argument passed to then(), must be null or callable.', E_USER_NOTICE);
32+
}
33+
3034
$progHandler = array($deferred, 'progress');
3135
}
3236

src/React/Promise/FulfilledPromise.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ public function then($fulfilledHandler = null, $errorHandler = null, $progressHa
1515
{
1616
try {
1717
$result = $this->result;
18-
if ($fulfilledHandler) {
18+
19+
if (is_callable($fulfilledHandler)) {
1920
$result = call_user_func($fulfilledHandler, $result);
21+
} elseif (null !== $fulfilledHandler) {
22+
trigger_error('Invalid $fulfilledHandler argument passed to then(), must be null or callable.', E_USER_NOTICE);
2023
}
2124

2225
return Util::promiseFor($result);

src/React/Promise/RejectedPromise.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ public function __construct($reason = null)
1414
public function then($fulfilledHandler = null, $errorHandler = null, $progressHandler = null)
1515
{
1616
try {
17-
if (!$errorHandler) {
17+
if (!is_callable($errorHandler)) {
18+
if (null !== $errorHandler) {
19+
trigger_error('Invalid $errorHandler argument passed to then(), must be null or callable.', E_USER_NOTICE);
20+
}
21+
1822
return new RejectedPromise($this->reason);
1923
}
2024

tests/React/Promise/DeferredProgressTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,4 +312,38 @@ public function shouldAllowRejectAfterProgress()
312312
->resolver()
313313
->reject(2);
314314
}
315+
316+
/**
317+
* @test
318+
* @dataProvider invalidCallbackDataProvider
319+
**/
320+
public function shouldIgnoreNonFunctionsAndTriggerPhpNotice($var)
321+
{
322+
$errorCollector = new ErrorCollector();
323+
$errorCollector->register();
324+
325+
$mock = $this->createCallableMock();
326+
$mock
327+
->expects($this->once())
328+
->method('__invoke')
329+
->with($this->identicalTo(1));
330+
331+
$d = new Deferred();
332+
$d
333+
->then(
334+
null,
335+
null,
336+
$var
337+
)
338+
->then(
339+
$this->expectCallableNever(),
340+
$this->expectCallableNever(),
341+
$mock
342+
);
343+
344+
$d->progress(1);
345+
346+
$errorCollector->assertCollectedError('Invalid $progressHandler argument passed to then(), must be null or callable.', E_USER_NOTICE);
347+
$errorCollector->unregister();
348+
}
315349
}

tests/React/Promise/DeferredRejectTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,36 @@ public function shouldForwardReasonWhenCallbackIsNull()
8585

8686
$d->reject(1);
8787
}
88+
89+
/**
90+
* @test
91+
* @dataProvider invalidCallbackDataProvider
92+
**/
93+
public function shouldIgnoreNonFunctionsAndTriggerPhpNotice($var)
94+
{
95+
$errorCollector = new ErrorCollector();
96+
$errorCollector->register();
97+
98+
$mock = $this->createCallableMock();
99+
$mock
100+
->expects($this->once())
101+
->method('__invoke')
102+
->with($this->identicalTo(1));
103+
104+
$d = new Deferred();
105+
$d
106+
->then(
107+
null,
108+
$var
109+
)
110+
->then(
111+
$this->expectCallableNever(),
112+
$mock
113+
);
114+
115+
$d->reject(1);
116+
117+
$errorCollector->assertCollectedError('Invalid $errorHandler argument passed to then(), must be null or callable.', E_USER_NOTICE);
118+
$errorCollector->unregister();
119+
}
88120
}

tests/React/Promise/DeferredResolveTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,35 @@ public function shouldForwardValueWhenCallbackIsNull()
162162

163163
$d->resolve(1);
164164
}
165+
166+
/**
167+
* @test
168+
* @dataProvider invalidCallbackDataProvider
169+
**/
170+
public function shouldIgnoreNonFunctionsAndTriggerPhpNotice($var)
171+
{
172+
$errorCollector = new ErrorCollector();
173+
$errorCollector->register();
174+
175+
$mock = $this->createCallableMock();
176+
$mock
177+
->expects($this->once())
178+
->method('__invoke')
179+
->with($this->identicalTo(1));
180+
181+
$d = new Deferred();
182+
$d
183+
->then(
184+
$var
185+
)
186+
->then(
187+
$mock,
188+
$this->expectCallableNever()
189+
);
190+
191+
$d->resolve(1);
192+
193+
$errorCollector->assertCollectedError('Invalid $fulfilledHandler argument passed to then(), must be null or callable.', E_USER_NOTICE);
194+
$errorCollector->unregister();
195+
}
165196
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace React\Promise;
4+
5+
class ErrorCollector
6+
{
7+
private $errors = array();
8+
9+
public function register()
10+
{
11+
$errors = array();
12+
13+
set_error_handler(function ($errno, $errstr, $errfile, $errline, $errcontext) use (&$errors) {
14+
$errors[] = compact('errno', 'errstr', 'errfile', 'errline', 'errcontext');
15+
});
16+
17+
$this->errors = &$errors;
18+
}
19+
20+
public function unregister()
21+
{
22+
$this->errors = array();
23+
restore_error_handler();
24+
}
25+
26+
public function assertCollectedError($errstr, $errno)
27+
{
28+
foreach ($this->errors as $error) {
29+
if ($error['errstr'] === $errstr && $error['errno'] === $errno) {
30+
return;
31+
}
32+
}
33+
34+
$message = 'Error with level ' . $errno . ' and message "' . $errstr . '" not found in ' . var_export($this->errors, true);
35+
36+
throw new \PHPUnit_Framework_AssertionFailedError($message);
37+
}
38+
}

tests/React/Promise/TestCase.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,16 @@ public function createCallableMock()
3838
{
3939
return $this->getMock('React\\Promise\Stub\CallableStub');
4040
}
41+
42+
public function invalidCallbackDataProvider()
43+
{
44+
return array(
45+
'empty string' => array(''),
46+
'true' => array(true),
47+
'false' => array(false),
48+
'object' => array(new \stdClass),
49+
'truthy' => array(1),
50+
'falsey' => array(0)
51+
);
52+
}
4153
}

0 commit comments

Comments
 (0)