bug #15159 [2.6] Towards 100% HHVM compat (nicolas-grekas)
This PR was merged into the 2.6 branch.
Discussion
----------
[2.6] Towards 100% HHVM compat
| Q | A
| ------------- | ---
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #9917, #11418
| License | MIT
| Doc PR | -
Failing components:
- [x] HttpFoundation
- [x] HttpKernel
- [x] VarDumper
Related HHVM issues:
- https://github.com/facebook/hhvm/issues/5563
Commits
-------
8a78255
[2.6] Towards 100% HHVM compat
This commit is contained in:
commit
a83487821f
@ -95,34 +95,58 @@ class JsonResponse extends Response
|
||||
*/
|
||||
public function setData($data = array())
|
||||
{
|
||||
$errorHandler = null;
|
||||
$errorHandler = set_error_handler(function () use (&$errorHandler) {
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($errorHandler) {
|
||||
call_user_func_array($errorHandler, func_get_args());
|
||||
}
|
||||
});
|
||||
|
||||
if (defined('HHVM_VERSION')) {
|
||||
// HHVM does not trigger any warnings and let exceptions
|
||||
// thrown from a JsonSerializable object pass through.
|
||||
// If only PHP did the same...
|
||||
$data = json_encode($data, $this->encodingOptions);
|
||||
} else {
|
||||
try {
|
||||
if (PHP_VERSION_ID < 50400) {
|
||||
// PHP 5.3 triggers annoying warnings for some
|
||||
// types that can't be serialized as JSON (INF, resources, etc.)
|
||||
// but doesn't provide the JsonSerializable interface.
|
||||
set_error_handler('var_dump', 0);
|
||||
$data = @json_encode($data, $this->encodingOptions);
|
||||
} else {
|
||||
// PHP 5.4 and up wrap exceptions thrown by JsonSerializable
|
||||
// objects in a new exception that needs to be removed.
|
||||
// Fortunately, PHP 5.5 and up do not trigger any warning anymore.
|
||||
if (PHP_VERSION_ID < 50500) {
|
||||
// Clear json_last_error()
|
||||
json_encode(null);
|
||||
|
||||
$this->data = json_encode($data, $this->encodingOptions);
|
||||
|
||||
restore_error_handler();
|
||||
} catch (\Exception $exception) {
|
||||
$errorHandler = set_error_handler('var_dump');
|
||||
restore_error_handler();
|
||||
set_error_handler(function () use ($errorHandler) {
|
||||
if (JSON_ERROR_NONE === json_last_error()) {
|
||||
return $errorHandler && false !== call_user_func_array($errorHandler, func_get_args());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
$data = json_encode($data, $this->encodingOptions);
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 50500) {
|
||||
restore_error_handler();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if (PHP_VERSION_ID < 50500) {
|
||||
restore_error_handler();
|
||||
}
|
||||
if (PHP_VERSION_ID >= 50400 && 'Exception' === get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
|
||||
throw $e->getPrevious() ?: $e;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
throw new \InvalidArgumentException($this->transformJsonError());
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
|
||||
return $this->update();
|
||||
}
|
||||
|
||||
|
@ -1242,15 +1242,9 @@ class Response
|
||||
{
|
||||
$status = ob_get_status(true);
|
||||
$level = count($status);
|
||||
$flags = PHP_VERSION_ID >= 50400 ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1;
|
||||
|
||||
while ($level-- > $targetLevel
|
||||
&& (!empty($status[$level]['del'])
|
||||
|| (isset($status[$level]['flags'])
|
||||
&& ($status[$level]['flags'] & PHP_OUTPUT_HANDLER_REMOVABLE)
|
||||
&& ($status[$level]['flags'] & ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE))
|
||||
)
|
||||
)
|
||||
) {
|
||||
while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || $flags === ($s['flags'] & $flags) : $s['del'])) {
|
||||
if ($flush) {
|
||||
ob_end_flush();
|
||||
} else {
|
||||
|
@ -203,9 +203,8 @@ class JsonResponseTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessage Failed calling Symfony\Component\HttpFoundation\Tests\JsonSerializableObject::jsonSerialize()
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php#114688
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage This error is expected
|
||||
*/
|
||||
public function testSetContentJsonSerializeError()
|
||||
{
|
||||
@ -224,9 +223,7 @@ if (interface_exists('JsonSerializable')) {
|
||||
{
|
||||
public function jsonSerialize()
|
||||
{
|
||||
trigger_error('This error is expected', E_USER_WARNING);
|
||||
|
||||
return array();
|
||||
throw new \Exception('This error is expected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$bag->clearCookie('foo');
|
||||
|
||||
$this->assertContains('Set-Cookie: foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; httponly', explode("\r\n", $bag->__toString()));
|
||||
$this->assertRegExp('#^Set-Cookie: foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; httponly#m', $bag->__toString());
|
||||
}
|
||||
|
||||
public function testClearCookieSecureNotHttpOnly()
|
||||
@ -127,7 +127,7 @@ class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$bag->clearCookie('foo', '/', null, true, false);
|
||||
|
||||
$this->assertContains("Set-Cookie: foo=deleted; expires=".gmdate("D, d-M-Y H:i:s T", time() - 31536001)."; path=/; secure", explode("\r\n", $bag->__toString()));
|
||||
$this->assertRegExp('#^Set-Cookie: foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; secure#m', $bag->__toString());
|
||||
}
|
||||
|
||||
public function testReplace()
|
||||
|
@ -45,13 +45,11 @@ class PdoCasterTest extends \PHPUnit_Framework_TestCase
|
||||
'ORACLE_NULLS' => $attr['ORACLE_NULLS'],
|
||||
'CLIENT_VERSION' => $pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION),
|
||||
'SERVER_VERSION' => $pdo->getAttribute(\PDO::ATTR_SERVER_VERSION),
|
||||
'STATEMENT_CLASS' => array(
|
||||
'PDOStatement',
|
||||
array($pdo),
|
||||
),
|
||||
'STATEMENT_CLASS' => array('PDOStatement'),
|
||||
'DEFAULT_FETCH_MODE' => $attr['DEFAULT_FETCH_MODE'],
|
||||
),
|
||||
);
|
||||
unset($cast["\0~\0attributes"]['STATEMENT_CLASS'][1]);
|
||||
|
||||
$this->assertSame($xCast, $cast);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class CliDumperTest extends \PHPUnit_Framework_TestCase
|
||||
$cloner = new VarCloner();
|
||||
$cloner->addCasters(array(
|
||||
':stream' => function ($res, $a) {
|
||||
unset($a['uri']);
|
||||
unset($a['uri'], $a['wrapper_data']);
|
||||
|
||||
return $a;
|
||||
},
|
||||
@ -40,12 +40,12 @@ class CliDumperTest extends \PHPUnit_Framework_TestCase
|
||||
$out = ob_get_clean();
|
||||
$out = preg_replace('/[ \t]+$/m', '', $out);
|
||||
$intMax = PHP_INT_MAX;
|
||||
$res1 = (int) $var['res'];
|
||||
$res2 = (int) $var[8];
|
||||
$res = (int) $var['res'];
|
||||
|
||||
$r = defined('HHVM_VERSION') ? '' : '#%d';
|
||||
$this->assertStringMatchesFormat(
|
||||
<<<EOTXT
|
||||
array:25 [
|
||||
array:24 [
|
||||
"number" => 1
|
||||
0 => &1 null
|
||||
"const" => 1.1
|
||||
@ -58,7 +58,7 @@ array:25 [
|
||||
"str" => "déjà\\n"
|
||||
7 => b"é\\x00"
|
||||
"[]" => []
|
||||
"res" => stream resource {@{$res1}
|
||||
"res" => stream resource {@{$res}
|
||||
wrapper_type: "plainfile"
|
||||
stream_type: "STDIO"
|
||||
mode: "r"
|
||||
@ -69,12 +69,11 @@ array:25 [
|
||||
eof: false
|
||||
options: []
|
||||
}
|
||||
8 => Unknown resource @{$res2}
|
||||
"obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d
|
||||
+foo: "foo"
|
||||
+"bar": "bar"
|
||||
}
|
||||
"closure" => Closure {#%d
|
||||
"closure" => Closure {{$r}
|
||||
reflection: """
|
||||
Closure [ <user%S> %s Symfony\Component\VarDumper\Tests\Fixture\{closure} ] {\\n
|
||||
@@ {$var['file']} {$var['line']} - {$var['line']}\\n
|
||||
@ -93,7 +92,7 @@ array:25 [
|
||||
"recurs" => &4 array:1 [
|
||||
0 => &4 array:1 [&4]
|
||||
]
|
||||
9 => &1 null
|
||||
8 => &1 null
|
||||
"sobj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d}
|
||||
"snobj" => &3 {#%d}
|
||||
"snobj2" => {#%d}
|
||||
@ -101,6 +100,35 @@ array:25 [
|
||||
b"bin-key-é" => ""
|
||||
]
|
||||
|
||||
EOTXT
|
||||
,
|
||||
$out
|
||||
);
|
||||
}
|
||||
|
||||
public function testClosedResource()
|
||||
{
|
||||
if (defined('HHVM_VERSION') && HHVM_VERSION_ID < 30600) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
$var = fopen(__FILE__, 'r');
|
||||
fclose($var);
|
||||
|
||||
$dumper = new CliDumper('php://output');
|
||||
$dumper->setColors(false);
|
||||
$cloner = new VarCloner();
|
||||
$data = $cloner->cloneVar($var);
|
||||
|
||||
ob_start();
|
||||
$dumper->dump($data);
|
||||
$out = ob_get_clean();
|
||||
$res = (int) $var;
|
||||
|
||||
$this->assertStringMatchesFormat(
|
||||
<<<EOTXT
|
||||
Unknown resource @{$res}
|
||||
|
||||
EOTXT
|
||||
,
|
||||
$out
|
||||
@ -114,6 +142,13 @@ EOTXT
|
||||
$dumper = new CliDumper();
|
||||
$dumper->setColors(false);
|
||||
$cloner = new VarCloner();
|
||||
$cloner->addCasters(array(
|
||||
':stream' => function ($res, $a) {
|
||||
unset($a['wrapper_data']);
|
||||
|
||||
return $a;
|
||||
},
|
||||
));
|
||||
$cloner->addCasters(array(
|
||||
':stream' => function () {
|
||||
throw new \Exception('Foobar');
|
||||
@ -128,12 +163,13 @@ EOTXT
|
||||
rewind($out);
|
||||
$out = stream_get_contents($out);
|
||||
|
||||
$r = defined('HHVM_VERSION') ? '' : '#%d';
|
||||
$this->assertStringMatchesFormat(
|
||||
<<<EOTXT
|
||||
stream resource {@{$ref}
|
||||
wrapper_type: "PHP"
|
||||
stream_type: "MEMORY"
|
||||
mode: "w+b"
|
||||
mode: "%s+b"
|
||||
unread_bytes: 0
|
||||
seekable: true
|
||||
uri: "php://memory"
|
||||
@ -141,11 +177,11 @@ stream resource {@{$ref}
|
||||
blocked: true
|
||||
eof: false
|
||||
options: []
|
||||
⚠: Symfony\Component\VarDumper\Exception\ThrowingCasterException {#%d
|
||||
⚠: Symfony\Component\VarDumper\Exception\ThrowingCasterException {{$r}
|
||||
#message: "Unexpected Exception thrown from a caster: Foobar"
|
||||
trace: array:1 [
|
||||
0 => array:2 [
|
||||
"call" => "%s{closure}()"
|
||||
"call" => "%slosure%s()"
|
||||
"file" => "{$file}:{$line}"
|
||||
]
|
||||
]
|
||||
@ -173,9 +209,10 @@ EOTXT
|
||||
rewind($out);
|
||||
$out = stream_get_contents($out);
|
||||
|
||||
$r = defined('HHVM_VERSION') ? '' : '#%d';
|
||||
$this->assertStringMatchesFormat(
|
||||
<<<EOTXT
|
||||
{#%d
|
||||
{{$r}
|
||||
+"foo": &1 "foo"
|
||||
+"bar": &1 "foo"
|
||||
}
|
||||
|
@ -13,8 +13,6 @@ $foo = new DumbFoo();
|
||||
$foo->bar = 'bar';
|
||||
|
||||
$g = fopen(__FILE__, 'r');
|
||||
$h = fopen(__FILE__, 'r');
|
||||
fclose($h);
|
||||
|
||||
$var = array(
|
||||
'number' => 1, null,
|
||||
@ -22,7 +20,6 @@ $var = array(
|
||||
'str' => "déjà\n", "\xE9\x00",
|
||||
'[]' => array(),
|
||||
'res' => $g,
|
||||
$h,
|
||||
'obj' => $foo,
|
||||
'closure' => function ($a, \PDO &$b = null) {},
|
||||
'line' => __LINE__ - 1,
|
||||
@ -40,4 +37,4 @@ $var['snobj2'] = $var['nobj'][0];
|
||||
$var['file'] = __FILE__;
|
||||
$var["bin-key-\xE9"] = '';
|
||||
|
||||
unset($g, $h, $r);
|
||||
unset($g, $r);
|
||||
|
@ -29,7 +29,7 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
|
||||
$cloner = new VarCloner();
|
||||
$cloner->addCasters(array(
|
||||
':stream' => function ($res, $a) {
|
||||
unset($a['uri']);
|
||||
unset($a['uri'], $a['wrapper_data']);
|
||||
|
||||
return $a;
|
||||
},
|
||||
@ -44,12 +44,12 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
|
||||
$intMax = PHP_INT_MAX;
|
||||
preg_match('/sf-dump-\d+/', $out, $dumpId);
|
||||
$dumpId = $dumpId[0];
|
||||
$res1 = (int) $var['res'];
|
||||
$res2 = (int) $var[8];
|
||||
$res = (int) $var['res'];
|
||||
|
||||
$r = defined('HHVM_VERSION') ? '' : '<a class=sf-dump-ref>#%d</a>';
|
||||
$this->assertStringMatchesFormat(
|
||||
<<<EOTXT
|
||||
<foo></foo><bar><span class=sf-dump-note>array:25</span> [<samp>
|
||||
<foo></foo><bar><span class=sf-dump-note>array:24</span> [<samp>
|
||||
"<span class=sf-dump-key>number</span>" => <span class=sf-dump-num>1</span>
|
||||
<span class=sf-dump-key>0</span> => <a class=sf-dump-ref href=#{$dumpId}-ref01 title="2 occurrences">&1</a> <span class=sf-dump-const>null</span>
|
||||
"<span class=sf-dump-key>const</span>" => <span class=sf-dump-num>1.1</span>
|
||||
@ -62,7 +62,7 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
|
||||
"<span class=sf-dump-key>str</span>" => "<span class=sf-dump-str title="5 characters">déjà</span>\\n"
|
||||
<span class=sf-dump-key>7</span> => b"<span class=sf-dump-str title="2 binary or non-UTF-8 characters">é</span>\\x00"
|
||||
"<span class=sf-dump-key>[]</span>" => []
|
||||
"<span class=sf-dump-key>res</span>" => <span class=sf-dump-note>stream resource</span> <a class=sf-dump-ref>@{$res1}</a><samp>
|
||||
"<span class=sf-dump-key>res</span>" => <span class=sf-dump-note>stream resource</span> <a class=sf-dump-ref>@{$res}</a><samp>
|
||||
<span class=sf-dump-meta>wrapper_type</span>: "<span class=sf-dump-str title="9 characters">plainfile</span>"
|
||||
<span class=sf-dump-meta>stream_type</span>: "<span class=sf-dump-str title="5 characters">STDIO</span>"
|
||||
<span class=sf-dump-meta>mode</span>: "<span class=sf-dump-str>r</span>"
|
||||
@ -73,12 +73,11 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
|
||||
<span class=sf-dump-meta>eof</span>: <span class=sf-dump-const>false</span>
|
||||
<span class=sf-dump-meta>options</span>: []
|
||||
</samp>}
|
||||
<span class=sf-dump-key>8</span> => <span class=sf-dump-note>Unknown resource</span> <a class=sf-dump-ref>@{$res2}</a>
|
||||
"<span class=sf-dump-key>obj</span>" => <abbr title="Symfony\Component\VarDumper\Tests\Fixture\DumbFoo" class=sf-dump-note>DumbFoo</abbr> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="2 occurrences">#%d</a><samp id={$dumpId}-ref2%d>
|
||||
+<span class=sf-dump-public title="Public property">foo</span>: "<span class=sf-dump-str title="3 characters">foo</span>"
|
||||
+"<span class=sf-dump-public title="Runtime added dynamic property">bar</span>": "<span class=sf-dump-str title="3 characters">bar</span>"
|
||||
</samp>}
|
||||
"<span class=sf-dump-key>closure</span>" => <span class=sf-dump-note>Closure</span> {<a class=sf-dump-ref>#%d</a><samp>
|
||||
"<span class=sf-dump-key>closure</span>" => <span class=sf-dump-note>Closure</span> {{$r}<samp>
|
||||
<span class=sf-dump-meta>reflection</span>: """
|
||||
<span class=sf-dump-str title="%d characters">Closure [ <user%S> %s Symfony\Component\VarDumper\Tests\Fixture\{closure} ] {</span>\\n
|
||||
<span class=sf-dump-str title="%d characters"> @@ {$var['file']} {$var['line']} - {$var['line']}</span>\\n
|
||||
@ -97,7 +96,7 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
|
||||
"<span class=sf-dump-key>recurs</span>" => <a class=sf-dump-ref href=#{$dumpId}-ref04 title="2 occurrences">&4</a> <span class=sf-dump-note>array:1</span> [<samp id={$dumpId}-ref04>
|
||||
<span class=sf-dump-index>0</span> => <a class=sf-dump-ref href=#{$dumpId}-ref04 title="2 occurrences">&4</a> <span class=sf-dump-note>array:1</span> [<a class=sf-dump-ref href=#{$dumpId}-ref04 title="2 occurrences">&4</a>]
|
||||
</samp>]
|
||||
<span class=sf-dump-key>9</span> => <a class=sf-dump-ref href=#{$dumpId}-ref01 title="2 occurrences">&1</a> <span class=sf-dump-const>null</span>
|
||||
<span class=sf-dump-key>8</span> => <a class=sf-dump-ref href=#{$dumpId}-ref01 title="2 occurrences">&1</a> <span class=sf-dump-const>null</span>
|
||||
"<span class=sf-dump-key>sobj</span>" => <abbr title="Symfony\Component\VarDumper\Tests\Fixture\DumbFoo" class=sf-dump-note>DumbFoo</abbr> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="2 occurrences">#%d</a>}
|
||||
"<span class=sf-dump-key>snobj</span>" => <a class=sf-dump-ref href=#{$dumpId}-ref03 title="2 occurrences">&3</a> {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="3 occurrences">#%d</a>}
|
||||
"<span class=sf-dump-key>snobj2</span>" => {<a class=sf-dump-ref href=#{$dumpId}-ref2%d title="3 occurrences">#%d</a>}
|
||||
|
@ -81,7 +81,7 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
||||
[class] => stdClass
|
||||
[value] =>
|
||||
[cut] => 0
|
||||
[handle] => %d
|
||||
[handle] => %i
|
||||
[refCount] => 0
|
||||
[position] => 1
|
||||
)
|
||||
@ -96,7 +96,7 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
||||
[class] => stdClass
|
||||
[value] =>
|
||||
[cut] => 0
|
||||
[handle] => %d
|
||||
[handle] => %i
|
||||
[refCount] => 0
|
||||
[position] => 2
|
||||
)
|
||||
@ -107,7 +107,7 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
||||
[class] => stdClass
|
||||
[value] =>
|
||||
[cut] => 0
|
||||
[handle] => %d
|
||||
[handle] => %i
|
||||
[refCount] => 0
|
||||
[position] => 3
|
||||
)
|
||||
|
Reference in New Issue
Block a user