Laravel 5.6 updates

Travis config update

Removed HHVM script as Laravel no longer support HHVM after releasing 5.3
This commit is contained in:
Manish Verma
2018-08-06 20:08:55 +05:30
parent 126fbb0255
commit 1ac0f42a58
2464 changed files with 65239 additions and 46734 deletions

View File

@@ -1,6 +1,25 @@
CHANGELOG
=========
4.1.0
-----
* added a `ServerDumper` to send serialized Data clones to a server
* added a `ServerDumpCommand` and `DumpServer` to run a server collecting
and displaying dumps on a single place with multiple formats support
* added `CliDescriptor` and `HtmlDescriptor` descriptors for `server:dump` CLI and HTML formats support
4.0.0
-----
* support for passing `\ReflectionClass` instances to the `Caster::castObject()`
method has been dropped, pass class names as strings instead
* the `Data::getRawData()` method has been removed
* the `VarDumperTestTrait::assertDumpEquals()` method expects a 3rd `$context = null`
argument and moves `$message = ''` argument at 4th position.
* the `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$context = null`
argument and moves `$message = ''` argument at 4th position.
3.4.0
-----

View File

@@ -22,7 +22,7 @@ class ArgsStub extends EnumStub
{
private static $parameters = array();
public function __construct(array $args, $function, $class)
public function __construct(array $args, string $function, ?string $class)
{
list($variadic, $params) = self::getParameters($function, $class);
@@ -68,7 +68,7 @@ class ArgsStub extends EnumStub
if ($v->isPassedByReference()) {
$k = '&'.$k;
}
if (method_exists($v, 'isVariadic') && $v->isVariadic()) {
if ($v->isVariadic()) {
$variadic .= $k;
} else {
$params[] = $k;

View File

@@ -48,11 +48,6 @@ class Caster
*/
public static function castObject($obj, $class, $hasDebugInfo = false)
{
if ($class instanceof \ReflectionClass) {
@trigger_error(sprintf('Passing a ReflectionClass to "%s()" is deprecated since Symfony 3.3 and will be unsupported in 4.0. Pass the class name as string instead.', __METHOD__), E_USER_DEPRECATED);
$hasDebugInfo = $class->hasMethod('__debugInfo');
$class = $class->name;
}
if ($hasDebugInfo) {
$a = $obj->__debugInfo();
} elseif ($obj instanceof \Closure) {

View File

@@ -22,7 +22,7 @@ class ClassStub extends ConstStub
* @param string A PHP identifier, e.g. a class, method, interface, etc. name
* @param callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier
*/
public function __construct($identifier, $callable = null)
public function __construct(string $identifier, $callable = null)
{
$this->value = $identifier;

View File

@@ -20,7 +20,7 @@ use Symfony\Component\VarDumper\Cloner\Stub;
*/
class ConstStub extends Stub
{
public function __construct($name, $value)
public function __construct(string $name, $value)
{
$this->class = $name;
$this->value = $value;

View File

@@ -20,6 +20,8 @@ use Symfony\Component\VarDumper\Cloner\Stub;
*/
class DateCaster
{
private const PERIOD_LIMIT = 3;
public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, $isNested, $filter)
{
$prefix = Caster::PREFIX_VIRTUAL;
@@ -61,12 +63,7 @@ class DateCaster
$format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : '');
}
if (\PHP_VERSION_ID >= 70100 && isset($i->f)) {
$format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : '';
} else {
$format .= $i->h || $i->i || $i->s ? '%H:%I:%S' : '';
}
$format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : '';
$format = '%R ' === $format ? '0s' : $format;
return $i->format(rtrim($format));
@@ -76,7 +73,7 @@ class DateCaster
{
$location = $timeZone->getLocation();
$formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P');
$title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code'], \Locale::getDefault()) : '';
$title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : '';
$z = array(Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title));
@@ -85,14 +82,10 @@ class DateCaster
public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, $isNested, $filter)
{
if (\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) { // see https://bugs.php.net/bug.php?id=71635
return $a;
}
$dates = array();
if (\PHP_VERSION_ID >= 70107) { // see https://bugs.php.net/bug.php?id=74639
foreach (clone $p as $i => $d) {
if (3 === $i) {
if (self::PERIOD_LIMIT === $i) {
$now = new \DateTimeImmutable();
$dates[] = sprintf('%s more', ($end = $p->getEndDate())
? ceil(($end->format('U.u') - $d->format('U.u')) / ($now->add($p->getDateInterval())->format('U.u') - $now->format('U.u')))

View File

@@ -22,7 +22,7 @@ class EnumStub extends Stub
{
public $dumpKeys = true;
public function __construct(array $values, $dumpKeys = true)
public function __construct(array $values, bool $dumpKeys = true)
{
$this->value = $values;
$this->dumpKeys = $dumpKeys;

View File

@@ -21,7 +21,7 @@ class FrameStub extends EnumStub
public $keepArgs;
public $inTraceStub;
public function __construct(array $frame, $keepArgs = true, $inTraceStub = false)
public function __construct(array $frame, bool $keepArgs = true, bool $inTraceStub = false)
{
$this->value = $frame;
$this->keepArgs = $keepArgs;

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
/**
* Casts GMP objects to array representation.
*
* @author Hamza Amrouche <hamza.simperfit@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
class GmpCaster
{
public static function castGmp(\GMP $gmp, array $a, Stub $stub, $isNested, $filter): array
{
$a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp));
return $a;
}
}

View File

@@ -23,7 +23,7 @@ class LinkStub extends ConstStub
private static $vendorRoots;
private static $composerRoots;
public function __construct($label, $line = 0, $href = null)
public function __construct($label, int $line = 0, $href = null)
{
$this->value = $label;

View File

@@ -1,38 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
@trigger_error('The '.__NAMESPACE__.'\MongoCaster class is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
/**
* Casts classes from the MongoDb extension to array representation.
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @deprecated since version 3.4, to be removed in 4.0.
*/
class MongoCaster
{
public static function castCursor(\MongoCursorInterface $cursor, array $a, Stub $stub, $isNested)
{
if ($info = $cursor->info()) {
foreach ($info as $k => $v) {
$a[Caster::PREFIX_VIRTUAL.$k] = $v;
}
}
$a[Caster::PREFIX_VIRTUAL.'dead'] = $cursor->dead();
return $a;
}
}

View File

@@ -30,15 +30,6 @@ class RedisCaster
{
$prefix = Caster::PREFIX_VIRTUAL;
if (\defined('HHVM_VERSION_ID')) {
if (isset($a[Caster::PREFIX_PROTECTED.'serializer'])) {
$ser = $a[Caster::PREFIX_PROTECTED.'serializer'];
$a[Caster::PREFIX_PROTECTED.'serializer'] = isset(self::$serializer[$ser]) ? new ConstStub(self::$serializer[$ser], $ser) : $ser;
}
return $a;
}
if (!$connected = $c->isConnected()) {
return $a + array(
$prefix.'isConnected' => $connected,

View File

@@ -36,7 +36,6 @@ class ReflectionCaster
$prefix = Caster::PREFIX_VIRTUAL;
$c = new \ReflectionFunction($c);
$stub->class = 'Closure'; // HHVM generates unique class names for closures
$a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter);
if (isset($a[$prefix.'parameters'])) {
@@ -86,7 +85,7 @@ class ReflectionCaster
$prefix = Caster::PREFIX_VIRTUAL;
$a += array(
$prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : $c->__toString(),
$prefix.'name' => $c->getName(),
$prefix.'allowsNull' => $c->allowsNull(),
$prefix.'isBuiltin' => $c->isBuiltin(),
);
@@ -173,7 +172,7 @@ class ReflectionCaster
if (isset($a[$prefix.'returnType'])) {
$v = $a[$prefix.'returnType'];
$v = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString();
$v = $v->getName();
$a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, array(class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', ''));
}
if (isset($a[$prefix.'class'])) {
@@ -185,7 +184,7 @@ class ReflectionCaster
foreach ($c->getParameters() as $v) {
$k = '$'.$v->name;
if (method_exists($v, 'isVariadic') && $v->isVariadic()) {
if ($v->isVariadic()) {
$k = '...'.$k;
}
if ($v->isPassedByReference()) {
@@ -213,9 +212,6 @@ class ReflectionCaster
self::addExtra($a, $c);
}
// Added by HHVM
unset($a[Caster::PREFIX_DYNAMIC.'static']);
return $a;
}
@@ -230,9 +226,6 @@ class ReflectionCaster
{
$prefix = Caster::PREFIX_VIRTUAL;
// Added by HHVM
unset($a['info']);
self::addMap($a, $c, array(
'position' => 'getPosition',
'isVariadic' => 'isVariadic',
@@ -240,12 +233,8 @@ class ReflectionCaster
'allowsNull' => 'allowsNull',
));
if (method_exists($c, 'getType')) {
if ($v = $c->getType()) {
$a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString();
}
} elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $c, $v)) {
$a[$prefix.'typeHint'] = $v[1];
if ($v = $c->getType()) {
$a[$prefix.'typeHint'] = $v->getName();
}
if (isset($a[$prefix.'typeHint'])) {
@@ -257,17 +246,13 @@ class ReflectionCaster
try {
$a[$prefix.'default'] = $v = $c->getDefaultValue();
if (method_exists($c, 'isDefaultValueConstant') && $c->isDefaultValueConstant()) {
if ($c->isDefaultValueConstant()) {
$a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v);
}
if (null === $v) {
unset($a[$prefix.'allowsNull']);
}
} catch (\ReflectionException $e) {
if (isset($a[$prefix.'typeHint']) && $c->allowsNull() && !class_exists('ReflectionNamedType', false)) {
$a[$prefix.'default'] = null;
unset($a[$prefix.'allowsNull']);
}
}
return $a;

View File

@@ -25,7 +25,7 @@ class TraceStub extends Stub
public $sliceLength;
public $numberingOffset;
public function __construct(array $trace, $keepArgs = true, $sliceOffset = 0, $sliceLength = null, $numberingOffset = 0)
public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, int $sliceLength = null, int $numberingOffset = 0)
{
$this->value = $trace;
$this->keepArgs = $keepArgs;

View File

@@ -105,8 +105,6 @@ abstract class AbstractCloner implements ClonerInterface
'SplPriorityQueue' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'),
'OuterIterator' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'),
'MongoCursorInterface' => array('Symfony\Component\VarDumper\Caster\MongoCaster', 'castCursor'),
'Redis' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'),
'RedisArray' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'),
@@ -115,6 +113,8 @@ abstract class AbstractCloner implements ClonerInterface
'DateTimeZone' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'),
'DatePeriod' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'),
'GMP' => array('Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'),
':curl' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'),
':dba' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'),
':dba persistent' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'),
@@ -134,7 +134,6 @@ abstract class AbstractCloner implements ClonerInterface
protected $maxItems = 2500;
protected $maxString = -1;
protected $minDepth = 1;
protected $useExt;
private $casters = array();
private $prevErrorHandler;
@@ -152,7 +151,6 @@ abstract class AbstractCloner implements ClonerInterface
$casters = static::$defaultCasters;
}
$this->addCasters($casters);
$this->useExt = \extension_loaded('symfony_debug');
}
/**

View File

@@ -165,18 +165,6 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate
return sprintf('%s (count=%d)', $this->getType(), \count($value));
}
/**
* @return array The raw data structure
*
* @deprecated since version 3.3. Use array or object access instead.
*/
public function getRawData()
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the array or object access instead.', __METHOD__));
return $this->data;
}
/**
* Returns a depth limited clone of $this.
*

View File

@@ -17,8 +17,6 @@ namespace Symfony\Component\VarDumper\Cloner;
class VarCloner extends AbstractCloner
{
private static $gid;
private static $hashMask = 0;
private static $hashOffset = 0;
private static $arrayCache = array();
/**
@@ -31,10 +29,10 @@ class VarCloner extends AbstractCloner
$refsCounter = 0; // Hard references counter
$queue = array(array($var)); // This breadth-first queue is the return value
$indexedArrays = array(); // Map of queue indexes that hold numerically indexed arrays
$hardRefs = array(); // Map of original zval hashes to stub objects
$hardRefs = array(); // Map of original zval ids to stub objects
$objRefs = array(); // Map of original object handles to their stub object couterpart
$resRefs = array(); // Map of original resource handles to their stub object couterpart
$values = array(); // Map of stub objects' hashes to original values
$values = array(); // Map of stub objects' ids to original values
$maxItems = $this->maxItems;
$maxString = $this->maxString;
$minDepth = $this->minDepth;
@@ -46,13 +44,9 @@ class VarCloner extends AbstractCloner
$stub = null; // Stub capturing the main properties of an original item value
// or null if the original value is used directly
if (!self::$hashMask) {
self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
self::initHashMask();
if (!$gid = self::$gid) {
$gid = self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
}
$gid = self::$gid;
$hashMask = self::$hashMask;
$hashOffset = self::$hashOffset;
$arrayStub = new Stub();
$arrayStub->type = Stub::TYPE_ARRAY;
$fromObjCast = false;
@@ -89,7 +83,7 @@ class VarCloner extends AbstractCloner
if ($zvalIsRef = $vals[$k] === $cookie) {
$vals[$k] = &$stub; // Break hard references to make $queue completely
unset($stub); // independent from the original structure
if ($v instanceof Stub && isset($hardRefs[\spl_object_hash($v)])) {
if ($v instanceof Stub && isset($hardRefs[\spl_object_id($v)])) {
$vals[$k] = $refs[$k] = $v;
if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) {
++$v->value->refCount;
@@ -99,7 +93,7 @@ class VarCloner extends AbstractCloner
}
$refs[$k] = $vals[$k] = new Stub();
$refs[$k]->value = $v;
$h = \spl_object_hash($refs[$k]);
$h = \spl_object_id($refs[$k]);
$hardRefs[$h] = &$refs[$k];
$values[$h] = $v;
$vals[$k]->handle = ++$refsCounter;
@@ -178,7 +172,7 @@ class VarCloner extends AbstractCloner
case \is_object($v):
case $v instanceof \__PHP_Incomplete_Class:
if (empty($objRefs[$h = $hashMask ^ \hexdec(\substr(\spl_object_hash($v), $hashOffset, \PHP_INT_SIZE))])) {
if (empty($objRefs[$h = \spl_object_id($v)])) {
$stub = new Stub();
$stub->type = Stub::TYPE_OBJECT;
$stub->class = \get_class($v);
@@ -189,8 +183,7 @@ class VarCloner extends AbstractCloner
if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) {
break;
}
$h = $hashMask ^ \hexdec(\substr(\spl_object_hash($stub->value), $hashOffset, \PHP_INT_SIZE));
$stub->handle = $h;
$stub->handle = $h = \spl_object_id($stub->value);
}
$stub->value = null;
if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) {
@@ -297,31 +290,4 @@ class VarCloner extends AbstractCloner
return $queue;
}
private static function initHashMask()
{
$obj = (object) array();
self::$hashOffset = 16 - PHP_INT_SIZE;
self::$hashMask = -1;
if (\defined('HHVM_VERSION')) {
self::$hashOffset += 16;
} else {
// check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below
$obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush');
foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) {
if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) {
$frame['line'] = 0;
break;
}
}
if (!empty($frame['line'])) {
ob_start();
debug_zval_dump($obj);
self::$hashMask = (int) substr(ob_get_clean(), 17);
}
}
self::$hashMask ^= hexdec(substr(spl_object_hash($obj), self::$hashOffset, PHP_INT_SIZE));
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Command\Descriptor;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\CliDumper;
/**
* Describe collected data clones for cli output.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*
* @final
*/
class CliDescriptor implements DumpDescriptorInterface
{
private $dumper;
private $lastIdentifier;
public function __construct(CliDumper $dumper)
{
$this->dumper = $dumper;
}
public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void
{
$io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput(array()), $output);
$this->dumper->setColors($output->isDecorated());
$rows = array(array('date', date('r', $context['timestamp'])));
$lastIdentifier = $this->lastIdentifier;
$this->lastIdentifier = $clientId;
$section = "Received from client #$clientId";
if (isset($context['request'])) {
$request = $context['request'];
$this->lastIdentifier = $request['identifier'];
$section = sprintf('%s %s', $request['method'], $request['uri']);
if ($controller = $request['controller']) {
$rows[] = array('controller', rtrim($this->dumper->dump($controller, true), "\n"));
}
} elseif (isset($context['cli'])) {
$this->lastIdentifier = $context['cli']['identifier'];
$section = '$ '.$context['cli']['command_line'];
}
if ($this->lastIdentifier !== $lastIdentifier) {
$io->section($section);
}
if (isset($context['source'])) {
$source = $context['source'];
$rows[] = array('source', sprintf('%s on line %d', $source['name'], $source['line']));
$file = $source['file_relative'] ?? $source['file'];
$rows[] = array('file', $file);
$fileLink = $source['file_link'] ?? null;
}
$io->table(array(), $rows);
if (isset($fileLink)) {
$io->writeln(array('<info>Open source in your IDE/browser:</info>', $fileLink));
$io->newLine();
}
$this->dumper->dump($data);
$io->newLine();
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Command\Descriptor;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarDumper\Cloner\Data;
/**
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
interface DumpDescriptorInterface
{
public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void;
}

View File

@@ -0,0 +1,119 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Command\Descriptor;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
/**
* Describe collected data clones for html output.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*
* @final
*/
class HtmlDescriptor implements DumpDescriptorInterface
{
private $dumper;
private $initialized = false;
public function __construct(HtmlDumper $dumper)
{
$this->dumper = $dumper;
}
public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void
{
if (!$this->initialized) {
$styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css');
$scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js');
$output->writeln("<style>$styles</style><script>$scripts</script>");
$this->initialized = true;
}
$title = '-';
if (isset($context['request'])) {
$request = $context['request'];
$controller = "<span class='dumped-tag'>{$this->dumper->dump($request['controller'], true, array('maxDepth' => 0))}</span>";
$title = sprintf('<code>%s</code> <a href="%s">%s</a>', $request['method'], $uri = $request['uri'], $uri);
$dedupIdentifier = $request['identifier'];
} elseif (isset($context['cli'])) {
$title = '<code>$ </code>'.$context['cli']['command_line'];
$dedupIdentifier = $context['cli']['identifier'];
} else {
$dedupIdentifier = uniqid('', true);
}
$sourceDescription = '';
if (isset($context['source'])) {
$source = $context['source'];
$projectDir = $source['project_dir'];
$sourceDescription = sprintf('%s on line %d', $source['name'], $source['line']);
if (isset($source['file_link'])) {
$sourceDescription = sprintf('<a href="%s">%s</a>', $source['file_link'], $sourceDescription);
}
}
$isoDate = $this->extractDate($context, 'c');
$tags = array_filter(array(
'controller' => $controller ?? null,
'project dir' => $projectDir ?? null,
));
$output->writeln(<<<HTML
<article data-dedup-id="$dedupIdentifier">
<header>
<div class="row">
<h2 class="col">$title</h2>
<time class="col text-small" title="$isoDate" datetime="$isoDate">
{$this->extractDate($context)}
</time>
</div>
{$this->renderTags($tags)}
</header>
<section class="body">
<p class="text-small">
$sourceDescription
</p>
{$this->dumper->dump($data, true)}
</section>
</article>
HTML
);
}
private function extractDate(array $context, string $format = 'r'): string
{
return date($format, $context['timestamp']);
}
private function renderTags(array $tags): string
{
if (!$tags) {
return '';
}
$renderedTags = '';
foreach ($tags as $key => $value) {
$renderedTags .= sprintf('<li><span class="badge">%s</span>%s</li>', $key, $value);
}
return <<<HTML
<div class="row">
<ul class="tags">
$renderedTags
</ul>
</div>
HTML;
}
}

View File

@@ -0,0 +1,99 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor;
use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface;
use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\Server\DumpServer;
/**
* Starts a dump server to collect and output dumps on a single place with multiple formats support.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*
* @final
*/
class ServerDumpCommand extends Command
{
protected static $defaultName = 'server:dump';
private $server;
/** @var DumpDescriptorInterface[] */
private $descriptors;
public function __construct(DumpServer $server, array $descriptors = array())
{
$this->server = $server;
$this->descriptors = $descriptors + array(
'cli' => new CliDescriptor(new CliDumper()),
'html' => new HtmlDescriptor(new HtmlDumper()),
);
parent::__construct();
}
protected function configure()
{
$availableFormats = implode(', ', array_keys($this->descriptors));
$this
->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', $availableFormats), 'cli')
->setDescription('Starts a dump server that collects and displays dumps in a single place')
->setHelp(<<<'EOF'
<info>%command.name%</info> starts a dump server that collects and displays
dumps in a single place for debugging you application:
<info>php %command.full_name%</info>
You can consult dumped data in HTML format in your browser by providing the <comment>--format=html</comment> option
and redirecting the output to a file:
<info>php %command.full_name% --format="html" > dump.html</info>
EOF
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$format = $input->getOption('format');
if (!$descriptor = $this->descriptors[$format] ?? null) {
throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format));
}
$errorIo = $io->getErrorStyle();
$errorIo->title('Symfony Var Dumper Server');
$this->server->start();
$errorIo->success(sprintf('Server listening on %s', $this->server->getHost()));
$errorIo->comment('Quit the server with CONTROL-C.');
$this->server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) {
$descriptor->describe($io, $data, $context, $clientId);
});
}
}

View File

@@ -42,9 +42,9 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
* @param string $charset The default character encoding to use for non-UTF8 strings
* @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation
*/
public function __construct($output = null, $charset = null, $flags = 0)
public function __construct($output = null, string $charset = null, int $flags = 0)
{
$this->flags = (int) $flags;
$this->flags = $flags;
$this->setCharset($charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8');
$this->decimalPoint = localeconv();
$this->decimalPoint = $this->decimalPoint['decimal_point'];

View File

@@ -58,7 +58,7 @@ class CliDumper extends AbstractDumper
/**
* {@inheritdoc}
*/
public function __construct($output = null, $charset = null, $flags = 0)
public function __construct($output = null, string $charset = null, int $flags = 0)
{
parent::__construct($output, $charset, $flags);

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Dumper\ContextProvider;
/**
* Tries to provide context on CLI.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
final class CliContextProvider implements ContextProviderInterface
{
public function getContext(): ?array
{
if ('cli' !== \PHP_SAPI) {
return null;
}
return array(
'command_line' => $commandLine = implode(' ', $_SERVER['argv']),
'identifier' => hash('crc32b', $commandLine.$_SERVER['REQUEST_TIME_FLOAT']),
);
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Dumper\ContextProvider;
/**
* Interface to provide contextual data about dump data clones sent to a server.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
interface ContextProviderInterface
{
/**
* @return array|null Context data or null if unable to provide any context
*/
public function getContext(): ?array;
}

View File

@@ -0,0 +1,49 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Dumper\ContextProvider;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\VarDumper\Cloner\VarCloner;
/**
* Tries to provide context from a request.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
final class RequestContextProvider implements ContextProviderInterface
{
private $requestStack;
private $cloner;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
$this->cloner = new VarCloner();
$this->cloner->setMaxItems(0);
}
public function getContext(): ?array
{
if (null === $request = $this->requestStack->getCurrentRequest()) {
return null;
}
$controller = $request->attributes->get('_controller');
return array(
'uri' => $request->getUri(),
'method' => $request->getMethod(),
'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller,
'identifier' => spl_object_hash($request),
);
}
}

View File

@@ -0,0 +1,126 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Dumper\ContextProvider;
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\VarDumper;
use Twig\Template;
/**
* Tries to provide context from sources (class name, file, line, code excerpt, ...).
*
* @author Nicolas Grekas <p@tchwork.com>
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
final class SourceContextProvider implements ContextProviderInterface
{
private $limit;
private $charset;
private $projectDir;
private $fileLinkFormatter;
public function __construct(string $charset = null, string $projectDir = null, FileLinkFormatter $fileLinkFormatter = null, int $limit = 9)
{
$this->charset = $charset;
$this->projectDir = $projectDir;
$this->fileLinkFormatter = $fileLinkFormatter;
$this->limit = $limit;
}
public function getContext(): ?array
{
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit);
$file = $trace[1]['file'];
$line = $trace[1]['line'];
$name = false;
$fileExcerpt = false;
for ($i = 2; $i < $this->limit; ++$i) {
if (isset($trace[$i]['class'], $trace[$i]['function'])
&& 'dump' === $trace[$i]['function']
&& VarDumper::class === $trace[$i]['class']
) {
$file = $trace[$i]['file'];
$line = $trace[$i]['line'];
while (++$i < $this->limit) {
if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) {
$file = $trace[$i]['file'];
$line = $trace[$i]['line'];
break;
} elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) {
$template = $trace[$i]['object'];
$name = $template->getTemplateName();
$src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false);
$info = $template->getDebugInfo();
if (isset($info[$trace[$i - 1]['line']])) {
$line = $info[$trace[$i - 1]['line']];
$file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null;
if ($src) {
$src = explode("\n", $src);
$fileExcerpt = array();
for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) {
$fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>';
}
$fileExcerpt = '<ol start="'.max($line - 3, 1).'">'.implode("\n", $fileExcerpt).'</ol>';
}
}
break;
}
}
break;
}
}
if (false === $name) {
$name = str_replace('\\', '/', $file);
$name = substr($name, strrpos($name, '/') + 1);
}
$context = array('name' => $name, 'file' => $file, 'line' => $line);
$context['file_excerpt'] = $fileExcerpt;
if (null !== $this->projectDir) {
$context['project_dir'] = $this->projectDir;
if (0 === strpos($file, $this->projectDir)) {
$context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR);
}
}
if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) {
$context['file_link'] = $fileLink;
}
return $context;
}
private function htmlEncode(string $s): string
{
$html = '';
$dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset);
$dumper->setDumpHeader('');
$dumper->setDumpBoundaries('', '');
$cloner = new VarCloner();
$dumper->dump($cloner->cloneVar($s));
return substr(strip_tags($html), 1, -1);
}
}

View File

@@ -56,7 +56,7 @@ class HtmlDumper extends CliDumper
/**
* {@inheritdoc}
*/
public function __construct($output = null, $charset = null, $flags = 0)
public function __construct($output = null, string $charset = null, int $flags = 0)
{
AbstractDumper::__construct($output, $charset, $flags);
$this->dumpId = 'sf-dump-'.mt_rand();

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Dumper;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
use Symfony\Component\VarDumper\Server\Connection;
/**
* ServerDumper forwards serialized Data clones to a server.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
class ServerDumper implements DataDumperInterface
{
private $connection;
private $wrappedDumper;
/**
* @param string $host The server host
* @param DataDumperInterface|null $wrappedDumper A wrapped instance used whenever we failed contacting the server
* @param ContextProviderInterface[] $contextProviders Context providers indexed by context name
*/
public function __construct(string $host, DataDumperInterface $wrappedDumper = null, array $contextProviders = array())
{
$this->connection = new Connection($host, $contextProviders);
$this->wrappedDumper = $wrappedDumper;
}
public function getContextProviders(): array
{
return $this->connection->getContextProviders();
}
/**
* {@inheritdoc}
*/
public function dump(Data $data)
{
if (!$this->connection->write($data) && $this->wrappedDumper) {
$this->wrappedDumper->dump($data);
}
}
}

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Starts a dump server to collect and output dumps on a single place with multiple formats support.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\VarDumper\Command\ServerDumpCommand;
use Symfony\Component\VarDumper\Server\DumpServer;
function includeIfExists(string $file): bool
{
return file_exists($file) && include $file;
}
if (
!includeIfExists(__DIR__ . '/../../../../autoload.php') &&
!includeIfExists(__DIR__ . '/../../vendor/autoload.php') &&
!includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php')
) {
fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL);
exit(1);
}
if (!class_exists(Application::class)) {
fwrite(STDERR, 'You need the "symfony/console" component in order to run the VarDumper server.'.PHP_EOL);
exit(1);
}
$input = new ArgvInput();
$output = new ConsoleOutput();
$defaultHost = '127.0.0.1:9912';
$host = $input->getParameterOption(['--host'], $_SERVER['VAR_DUMPER_SERVER'] ?? $defaultHost, true);
$logger = interface_exists(LoggerInterface::class) ? new ConsoleLogger($output->getErrorOutput()) : null;
$app = new Application();
$app->getDefinition()->addOption(
new InputOption('--host', null, InputOption::VALUE_REQUIRED, 'The address the server should listen to', $defaultHost)
);
$app->add($command = new ServerDumpCommand(new DumpServer($host, $logger)))
->getApplication()
->setDefaultCommand($command->getName(), true)
->run($input, $output)
;

View File

@@ -0,0 +1,129 @@
body {
display: flex;
flex-direction: column-reverse;
justify-content: flex-end;
max-width: 1140px;
margin: auto;
padding: 15px;
word-wrap: break-word;
background-color: #F9F9F9;
color: #222;
font-family: Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.4;
}
p {
margin: 0;
}
a {
color: #218BC3;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.text-small {
font-size: 12px !important;
}
article {
margin: 5px;
margin-bottom: 10px;
}
article > header > .row {
display: flex;
flex-direction: row;
align-items: baseline;
margin-bottom: 10px;
}
article > header > .row > .col {
flex: 1;
display: flex;
align-items: baseline;
}
article > header > .row > h2 {
font-size: 14px;
color: #222;
font-weight: normal;
font-family: "Lucida Console", monospace, sans-serif;
word-break: break-all;
margin: 20px 5px 0 0;
user-select: all;
}
article > header > .row > h2 > code {
white-space: nowrap;
user-select: none;
color: #cc2255;
background-color: #f7f7f9;
border: 1px solid #e1e1e8;
border-radius: 3px;
margin-right: 5px;
padding: 0 3px;
}
article > header > .row > time.col {
flex: 0;
text-align: right;
white-space: nowrap;
color: #999;
font-style: italic;
}
article > header ul.tags {
list-style: none;
padding: 0;
margin: 0;
font-size: 12px;
}
article > header ul.tags > li {
user-select: all;
margin-bottom: 2px;
}
article > header ul.tags > li > span.badge {
display: inline-block;
padding: .25em .4em;
margin-right: 5px;
border-radius: 4px;
background-color: #6c757d3b;
color: #524d4d;
font-size: 12px;
text-align: center;
font-weight: 700;
line-height: 1;
white-space: nowrap;
vertical-align: baseline;
user-select: none;
}
article > section.body {
border: 1px solid #d8d8d8;
background: #FFF;
padding: 10px;
border-radius: 3px;
}
pre.sf-dump {
border-radius: 3px;
margin-bottom: 0;
}
.hidden {
display: none; !important
}
.dumped-tag > .sf-dump {
display: inline-block;
margin: 0;
padding: 1px 5px;
line-height: 1.4;
vertical-align: top;
background-color: transparent;
user-select: auto;
}
.dumped-tag > pre.sf-dump,
.dumped-tag > .sf-dump-default {
color: #CC7832;
background: none;
}
.dumped-tag > .sf-dump .sf-dump-str { color: #629755; }
.dumped-tag > .sf-dump .sf-dump-private,
.dumped-tag > .sf-dump .sf-dump-protected,
.dumped-tag > .sf-dump .sf-dump-public { color: #262626; }
.dumped-tag > .sf-dump .sf-dump-note { color: #6897BB; }
.dumped-tag > .sf-dump .sf-dump-key { color: #789339; }
.dumped-tag > .sf-dump .sf-dump-ref { color: #6E6E6E; }
.dumped-tag > .sf-dump .sf-dump-ellipsis { color: #CC7832; max-width: 100em; }
.dumped-tag > .sf-dump .sf-dump-ellipsis-path { max-width: 5em; }

View File

@@ -15,9 +15,11 @@ if (!function_exists('dump')) {
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
function dump($var)
function dump($var, ...$moreVars)
{
foreach (func_get_args() as $var) {
VarDumper::dump($var);
foreach ($moreVars as $var) {
VarDumper::dump($var);
}
@@ -28,3 +30,16 @@ if (!function_exists('dump')) {
return $var;
}
}
if (!function_exists('dd')) {
function dd($var, ...$moreVars)
{
VarDumper::dump($var);
foreach ($moreVars as $var) {
VarDumper::dump($var);
}
exit(1);
}
}

View File

@@ -0,0 +1,10 @@
document.addEventListener('DOMContentLoaded', function() {
let prev = null;
Array.from(document.getElementsByTagName('article')).reverse().forEach(function (article) {
const dedupId = article.dataset.dedupId;
if (dedupId === prev) {
article.getElementsByTagName('header')[0].classList.add('hidden');
}
prev = dedupId;
});
});

View File

@@ -0,0 +1,97 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Server;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
/**
* Forwards serialized Data clones to a server.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*/
class Connection
{
private $host;
private $contextProviders;
private $socket;
/**
* @param string $host The server host
* @param ContextProviderInterface[] $contextProviders Context providers indexed by context name
*/
public function __construct(string $host, array $contextProviders = array())
{
if (false === strpos($host, '://')) {
$host = 'tcp://'.$host;
}
$this->host = $host;
$this->contextProviders = $contextProviders;
}
public function getContextProviders(): array
{
return $this->contextProviders;
}
public function write(Data $data): bool
{
$socketIsFresh = !$this->socket;
if (!$this->socket = $this->socket ?: $this->createSocket()) {
return false;
}
$context = array('timestamp' => microtime(true));
foreach ($this->contextProviders as $name => $provider) {
$context[$name] = $provider->getContext();
}
$context = array_filter($context);
$encodedPayload = base64_encode(serialize(array($data, $context)))."\n";
set_error_handler(array(self::class, 'nullErrorHandler'));
try {
if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
return true;
}
if (!$socketIsFresh) {
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
fclose($this->socket);
$this->socket = $this->createSocket();
}
if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
return true;
}
} finally {
restore_error_handler();
}
return false;
}
private static function nullErrorHandler($t, $m)
{
// no-op
}
private function createSocket()
{
set_error_handler(array(self::class, 'nullErrorHandler'));
try {
return stream_socket_client($this->host, $errno, $errstr, 3, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT);
} finally {
restore_error_handler();
}
return $socket;
}
}

View File

@@ -0,0 +1,107 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Server;
use Psr\Log\LoggerInterface;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\Stub;
/**
* A server collecting Data clones sent by a ServerDumper.
*
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
*
* @final
*/
class DumpServer
{
private $host;
private $socket;
private $logger;
public function __construct(string $host, LoggerInterface $logger = null)
{
if (false === strpos($host, '://')) {
$host = 'tcp://'.$host;
}
$this->host = $host;
$this->logger = $logger;
}
public function start(): void
{
if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) {
throw new \RuntimeException(sprintf('Server start failed on "%s": %s %s.', $this->host, $errstr, $errno));
}
}
public function listen(callable $callback): void
{
if (null === $this->socket) {
$this->start();
}
foreach ($this->getMessages() as $clientId => $message) {
$payload = @unserialize(base64_decode($message), array('allowed_classes' => array(Data::class, Stub::class)));
// Impossible to decode the message, give up.
if (false === $payload) {
if ($this->logger) {
$this->logger->warning('Unable to decode a message from {clientId} client.', array('clientId' => $clientId));
}
continue;
}
if (!\is_array($payload) || \count($payload) < 2 || !$payload[0] instanceof Data || !\is_array($payload[1])) {
if ($this->logger) {
$this->logger->warning('Invalid payload from {clientId} client. Expected an array of two elements (Data $data, array $context)', array('clientId' => $clientId));
}
continue;
}
list($data, $context) = $payload;
$callback($data, $context, $clientId);
}
}
public function getHost(): string
{
return $this->host;
}
private function getMessages(): iterable
{
$sockets = array((int) $this->socket => $this->socket);
$write = array();
while (true) {
$read = $sockets;
stream_select($read, $write, $write, null);
foreach ($read as $stream) {
if ($this->socket === $stream) {
$stream = stream_socket_accept($this->socket);
$sockets[(int) $stream] = $stream;
} elseif (feof($stream)) {
unset($sockets[(int) $stream]);
fclose($stream);
} else {
yield (int) $stream => fgets($stream);
}
}
}
}
}

View File

@@ -19,26 +19,14 @@ use Symfony\Component\VarDumper\Dumper\CliDumper;
*/
trait VarDumperTestTrait
{
public function assertDumpEquals($dump, $data, $filter = 0, $message = '')
public function assertDumpEquals($expected, $data, $filter = 0, $message = '')
{
if (\is_string($filter)) {
@trigger_error(sprintf('The $message argument of the "%s()" method at the 3rd position is deprecated since Symfony 3.4 and will be moved at the 4th position in 4.0.', __METHOD__), E_USER_DEPRECATED);
$message = $filter;
$filter = 0;
}
$this->assertSame(rtrim($dump), $this->getDump($data, null, $filter), $message);
$this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message);
}
public function assertDumpMatchesFormat($dump, $data, $filter = 0, $message = '')
public function assertDumpMatchesFormat($expected, $data, $filter = 0, $message = '')
{
if (\is_string($filter)) {
@trigger_error(sprintf('The $message argument of the "%s()" method at the 3rd position is deprecated since Symfony 3.4 and will be moved at the 4th position in 4.0.', __METHOD__), E_USER_DEPRECATED);
$message = $filter;
$filter = 0;
}
$this->assertStringMatchesFormat(rtrim($dump), $this->getDump($data, null, $filter), $message);
$this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message);
}
protected function getDump($data, $key = null, $filter = 0)
@@ -57,4 +45,13 @@ trait VarDumperTestTrait
return rtrim($dumper->dump($data, true));
}
private function prepareExpectation($expected, $filter)
{
if (!\is_string($expected)) {
$expected = $this->getDump($expected, null, $filter);
}
return rtrim($expected);
}
}

View File

@@ -151,9 +151,6 @@ class CasterTest extends TestCase
);
}
/**
* @requires PHP 7.0
*/
public function testAnonymousClass()
{
$c = eval('return new class extends stdClass { private $foo = "foo"; };');

View File

@@ -29,10 +29,6 @@ class DateCasterTest extends TestCase
*/
public function testDumpDateTime($time, $timezone, $xDate, $xTimestamp)
{
if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && preg_match('/[-+]\d{2}:\d{2}/', $timezone)) {
$this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.');
}
$date = new \DateTime($time, new \DateTimeZone($timezone));
$xDump = <<<EODUMP
@@ -49,10 +45,6 @@ EODUMP;
*/
public function testCastDateTime($time, $timezone, $xDate, $xTimestamp, $xInfos)
{
if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && preg_match('/[-+]\d{2}:\d{2}/', $timezone)) {
$this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.');
}
$stub = new Stub();
$date = new \DateTime($time, new \DateTimeZone($timezone));
$cast = DateCaster::castDateTime($date, array('foo' => 'bar'), $stub, false, 0);
@@ -181,35 +173,32 @@ EODUMP;
public function provideIntervals()
{
$i = new \DateInterval('PT0S');
$ms = ($withMs = \PHP_VERSION_ID >= 70100 && isset($i->f)) ? '.0' : '';
return array(
array('PT0S', 0, 0, '0s', '0s'),
array('PT0S', 0.1, 0, $withMs ? '+ 00:00:00.100' : '0s', '%is'),
array('PT1S', 0, 0, '+ 00:00:01'.$ms, '%is'),
array('PT2M', 0, 0, '+ 00:02:00'.$ms, '%is'),
array('PT3H', 0, 0, '+ 03:00:00'.$ms, '%ss'),
array('PT0S', 0.1, 0, '+ 00:00:00.100', '%is'),
array('PT1S', 0, 0, '+ 00:00:01.0', '%is'),
array('PT2M', 0, 0, '+ 00:02:00.0', '%is'),
array('PT3H', 0, 0, '+ 03:00:00.0', '%ss'),
array('P4D', 0, 0, '+ 4d', '%ss'),
array('P5M', 0, 0, '+ 5m', null),
array('P6Y', 0, 0, '+ 6y', null),
array('P1Y2M3DT4H5M6S', 0, 0, '+ 1y 2m 3d 04:05:06'.$ms, null),
array('PT1M60S', 0, 0, '+ 00:02:00'.$ms, null),
array('PT1H60M', 0, 0, '+ 02:00:00'.$ms, null),
array('P1Y2M3DT4H5M6S', 0, 0, '+ 1y 2m 3d 04:05:06.0', null),
array('PT1M60S', 0, 0, '+ 00:02:00.0', null),
array('PT1H60M', 0, 0, '+ 02:00:00.0', null),
array('P1DT24H', 0, 0, '+ 2d', null),
array('P1M32D', 0, 0, '+ 1m 32d', null),
array('PT0S', 0, 1, '0s', '0s'),
array('PT0S', 0.1, 1, $withMs ? '- 00:00:00.100' : '0s', '%is'),
array('PT1S', 0, 1, '- 00:00:01'.$ms, '%is'),
array('PT2M', 0, 1, '- 00:02:00'.$ms, '%is'),
array('PT3H', 0, 1, '- 03:00:00'.$ms, '%ss'),
array('PT0S', 0.1, 1, '- 00:00:00.100', '%is'),
array('PT1S', 0, 1, '- 00:00:01.0', '%is'),
array('PT2M', 0, 1, '- 00:02:00.0', '%is'),
array('PT3H', 0, 1, '- 03:00:00.0', '%ss'),
array('P4D', 0, 1, '- 4d', '%ss'),
array('P5M', 0, 1, '- 5m', null),
array('P6Y', 0, 1, '- 6y', null),
array('P1Y2M3DT4H5M6S', 0, 1, '- 1y 2m 3d 04:05:06'.$ms, null),
array('PT1M60S', 0, 1, '- 00:02:00'.$ms, null),
array('PT1H60M', 0, 1, '- 02:00:00'.$ms, null),
array('P1Y2M3DT4H5M6S', 0, 1, '- 1y 2m 3d 04:05:06.0', null),
array('PT1M60S', 0, 1, '- 00:02:00.0', null),
array('PT1H60M', 0, 1, '- 02:00:00.0', null),
array('P1DT24H', 0, 1, '- 2d', null),
array('P1M32D', 0, 1, '- 1m 32d', null),
);
@@ -220,10 +209,6 @@ EODUMP;
*/
public function testDumpTimeZone($timezone, $expected)
{
if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && !preg_match('/\w+\/\w+/', $timezone)) {
$this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.');
}
$timezone = new \DateTimeZone($timezone);
$xDump = <<<EODUMP
@@ -240,10 +225,6 @@ EODUMP;
*/
public function testDumpTimeZoneExcludingVerbosity($timezone, $expected)
{
if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && !preg_match('/\w+\/\w+/', $timezone)) {
$this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.');
}
$timezone = new \DateTimeZone($timezone);
$xDump = <<<EODUMP
@@ -260,10 +241,6 @@ EODUMP;
*/
public function testCastTimeZone($timezone, $xTimezone, $xRegion)
{
if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && !preg_match('/\w+\/\w+/', $timezone)) {
$this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.');
}
$timezone = new \DateTimeZone($timezone);
$stub = new Stub();
@@ -325,10 +302,6 @@ EODUMP;
*/
public function testDumpPeriod($start, $interval, $end, $options, $expected)
{
if (\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) {
$this->markTestSkipped();
}
$p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end), $options);
$xDump = <<<EODUMP
@@ -345,10 +318,6 @@ EODUMP;
*/
public function testCastPeriod($start, $interval, $end, $options, $xPeriod, $xDates)
{
if (\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) {
$this->markTestSkipped();
}
$p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end), $options);
$stub = new Stub();
@@ -380,9 +349,6 @@ EODUMP;
public function providePeriods()
{
$i = new \DateInterval('PT0S');
$ms = \PHP_VERSION_ID >= 70100 && isset($i->f) ? '.0' : '';
$periods = array(
array('2017-01-01', 'P1D', '2017-01-03', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0', '1) 2017-01-01%a2) 2017-01-02'),
array('2017-01-01', 'P1D', 1, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01%a2) 2017-01-02'),
@@ -399,8 +365,8 @@ EODUMP;
array('2017-01-01 01:00:00', 'P1D', '2017-01-03 01:00:00', 0, 'every + 1d, from 2017-01-01 01:00:00.0 (included) to 2017-01-03 01:00:00.0', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'),
array('2017-01-01 01:00:00', 'P1D', 1, 0, 'every + 1d, from 2017-01-01 01:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'),
array('2017-01-01', 'P1DT1H', '2017-01-03', 0, "every + 1d 01:00:00$ms, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0", '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'),
array('2017-01-01', 'P1DT1H', 1, 0, "every + 1d 01:00:00$ms, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s", '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'),
array('2017-01-01', 'P1DT1H', '2017-01-03', 0, 'every + 1d 01:00:00.0, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0', '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'),
array('2017-01-01', 'P1DT1H', 1, 0, 'every + 1d 01:00:00.0, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'),
array('2017-01-01', 'P1D', '2017-01-04', \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from 2017-01-01 00:00:00.0 (excluded) to 2017-01-04 00:00:00.0', '1) 2017-01-02%a2) 2017-01-03'),
array('2017-01-01', 'P1D', 2, \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from 2017-01-01 00:00:00.0 (excluded) recurring 2 time/s', '1) 2017-01-02%a2) 2017-01-03'),
@@ -416,9 +382,7 @@ EODUMP;
private function createInterval($intervalSpec, $ms, $invert)
{
$interval = new \DateInterval($intervalSpec);
if (\PHP_VERSION_ID >= 70100 && isset($interval->f)) {
$interval->f = $ms;
}
$interval->f = $ms;
$interval->invert = $invert;
return $interval;

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Tests\Caster;
use PHPUnit\Framework\TestCase;
use Symfony\Component\VarDumper\Caster\GmpCaster;
use Symfony\Component\VarDumper\Cloner\Stub;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
class GmpCasterTest extends TestCase
{
use VarDumperTestTrait;
/**
* @requires extension gmp
*/
public function testCastGmp()
{
$gmpString = gmp_init('1234');
$gmpOctal = gmp_init(010);
$gmp = gmp_init('01101');
$gmpDump = <<<EODUMP
array:1 [
"\\x00~\\x00value" => %s
]
EODUMP;
$this->assertDumpEquals(sprintf($gmpDump, $gmpString), GmpCaster::castGmp($gmpString, array(), new Stub(), false, 0));
$this->assertDumpEquals(sprintf($gmpDump, $gmpOctal), GmpCaster::castGmp($gmpOctal, array(), new Stub(), false, 0));
$this->assertDumpEquals(sprintf($gmpDump, $gmp), GmpCaster::castGmp($gmp, array(), new Stub(), false, 0));
$dump = <<<EODUMP
GMP {
value: 577
}
EODUMP;
$this->assertDumpEquals($dump, $gmp);
}
}

View File

@@ -26,20 +26,11 @@ class RedisCasterTest extends TestCase
{
$redis = new \Redis();
if (\defined('HHVM_VERSION_ID')) {
$xCast = <<<'EODUMP'
Redis {
#host: ""
%A
}
EODUMP;
} else {
$xCast = <<<'EODUMP'
$xCast = <<<'EODUMP'
Redis {
isConnected: false
}
EODUMP;
}
$this->assertDumpMatchesFormat($xCast, $redis);
}
@@ -52,15 +43,7 @@ EODUMP;
self::markTestSkipped($e['message']);
}
if (\defined('HHVM_VERSION_ID')) {
$xCast = <<<'EODUMP'
Redis {
#host: "127.0.0.1"
%A
}
EODUMP;
} else {
$xCast = <<<'EODUMP'
$xCast = <<<'EODUMP'
Redis {%A
isConnected: true
host: "127.0.0.1"
@@ -77,7 +60,6 @@ Redis {%A
}
}
EODUMP;
}
$this->assertDumpMatchesFormat($xCast, $redis);
}

View File

@@ -116,9 +116,6 @@ EOTXT
);
}
/**
* @requires PHP 7.0
*/
public function testReflectionParameterScalar()
{
$f = eval('return function (int $a) {};');
@@ -136,9 +133,6 @@ EOTXT
);
}
/**
* @requires PHP 7.0
*/
public function testReturnType()
{
$f = eval('return function ():int {};');
@@ -158,9 +152,6 @@ EOTXT
);
}
/**
* @requires PHP 7.0
*/
public function testGenerator()
{
if (\extension_loaded('xdebug')) {

View File

@@ -166,9 +166,6 @@ EOTXT;
public function testCastArrayObject()
{
if (\defined('HHVM_VERSION')) {
$this->markTestSkipped('HHVM as different internal details.');
}
$var = new \ArrayObject(array(123));
$var->foo = 234;
@@ -188,9 +185,6 @@ EOTXT;
public function testArrayIterator()
{
if (\defined('HHVM_VERSION')) {
$this->markTestSkipped('HHVM as different internal details.');
}
$var = new MyArrayIterator(array(234));
$expected = <<<EOTXT

View File

@@ -48,7 +48,6 @@ class CliDumperTest extends TestCase
$intMax = PHP_INT_MAX;
$res = (int) $var['res'];
$r = \defined('HHVM_VERSION') ? '' : '#%d';
$this->assertStringMatchesFormat(
<<<EOTXT
array:24 [
@@ -76,9 +75,9 @@ array:24 [
+foo: "foo"
+"bar": "bar"
}
"closure" => Closure {{$r}
"closure" => Closure {#%d
class: "Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest"
this: Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest {{$r} …}
this: Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest {#%d …}
parameters: {
\$a: {}
&\$b: {
@@ -290,10 +289,6 @@ EOTXT
public function testClosedResource()
{
if (\defined('HHVM_VERSION') && HHVM_VERSION_ID < 30600) {
$this->markTestSkipped();
}
$var = fopen(__FILE__, 'r');
fclose($var);
@@ -384,11 +379,10 @@ EOTXT
$dumper->dump($data, $out);
$out = stream_get_contents($out, -1, 0);
$r = \defined('HHVM_VERSION') ? '' : '#%d';
$this->assertStringMatchesFormat(
<<<EOTXT
stream resource {@{$ref}
⚠: Symfony\Component\VarDumper\Exception\ThrowingCasterException {{$r}
⚠: Symfony\Component\VarDumper\Exception\ThrowingCasterException {#%d
#message: "Unexpected Exception thrown from a caster: Foobar"
trace: {
%sTwig.php:2 {
@@ -429,10 +423,9 @@ EOTXT
$data = $cloner->cloneVar($var);
$out = $dumper->dump($data, true);
$r = \defined('HHVM_VERSION') ? '' : '#%d';
$this->assertStringMatchesFormat(
<<<EOTXT
{{$r}
{#%d
+"foo": &1 "foo"
+"bar": &1 "foo"
}
@@ -446,7 +439,6 @@ EOTXT
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
* @requires PHP 5.6
*/
public function testSpecialVars56()
{
@@ -477,7 +469,7 @@ EOTXT
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testGlobalsNoExt()
public function testGlobals()
{
$var = $this->getSpecialVars();
unset($var[0]);
@@ -491,10 +483,6 @@ EOTXT
$dumper->setColors(false);
$cloner = new VarCloner();
$refl = new \ReflectionProperty($cloner, 'useExt');
$refl->setAccessible(true);
$refl->setValue($cloner, false);
$data = $cloner->cloneVar($var);
$dumper->dump($data);
@@ -509,47 +497,6 @@ array:2 [
2 => &1 array:1 [&1]
]
EOTXT
,
$out
);
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testBuggyRefs()
{
if (\PHP_VERSION_ID >= 50600) {
$this->markTestSkipped('PHP 5.6 fixed refs counting');
}
$var = $this->getSpecialVars();
$var = $var[0];
$dumper = new CliDumper();
$dumper->setColors(false);
$cloner = new VarCloner();
$data = $cloner->cloneVar($var)->withMaxDepth(3);
$out = '';
$dumper->dump($data, function ($line, $depth) use (&$out) {
if ($depth >= 0) {
$out .= str_repeat(' ', $depth).$line."\n";
}
});
$this->assertSame(
<<<'EOTXT'
array:1 [
0 => array:1 [
0 => array:1 [
0 => array:1 [ …1]
]
]
]
EOTXT
,
$out

View File

@@ -51,7 +51,6 @@ class HtmlDumperTest extends TestCase
$dumpId = $dumpId[0];
$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:24</span> [<samp>
@@ -79,10 +78,10 @@ class HtmlDumperTest extends TestCase
+<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> {{$r}<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-meta>class</span>: "<span class=sf-dump-str title="Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest
55 characters"><span class="sf-dump-ellipsis sf-dump-ellipsis-class">Symfony\Component\VarDumper\Tests\Dumper</span><span class=sf-dump-ellipsis>\</span>HtmlDumperTest</span>"
<span class=sf-dump-meta>this</span>: <abbr title="Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest" class=sf-dump-note>HtmlDumperTest</abbr> {{$r} &%s;}
<span class=sf-dump-meta>this</span>: <abbr title="Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest" class=sf-dump-note>HtmlDumperTest</abbr> {<a class=sf-dump-ref>#%d</a> &%s;}
<span class=sf-dump-meta>parameters</span>: {<samp>
<span class=sf-dump-meta>\$a</span>: {}
<span class=sf-dump-meta>&amp;\$b</span>: {<samp>

View File

@@ -0,0 +1,95 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Tests\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Process\PhpProcess;
use Symfony\Component\Process\Process;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
use Symfony\Component\VarDumper\Dumper\ServerDumper;
class ServerDumperTest extends TestCase
{
private const VAR_DUMPER_SERVER = 'tcp://127.0.0.1:9913';
public function testDumpForwardsToWrappedDumperWhenServerIsUnavailable()
{
$wrappedDumper = $this->getMockBuilder(DataDumperInterface::class)->getMock();
$dumper = new ServerDumper(self::VAR_DUMPER_SERVER, $wrappedDumper);
$cloner = new VarCloner();
$data = $cloner->cloneVar('foo');
$wrappedDumper->expects($this->once())->method('dump')->with($data);
$dumper->dump($data);
}
public function testDump()
{
$wrappedDumper = $this->getMockBuilder(DataDumperInterface::class)->getMock();
$wrappedDumper->expects($this->never())->method('dump'); // test wrapped dumper is not used
$cloner = new VarCloner();
$data = $cloner->cloneVar('foo');
$dumper = new ServerDumper(self::VAR_DUMPER_SERVER, $wrappedDumper, array(
'foo_provider' => new class() implements ContextProviderInterface {
public function getContext(): ?array
{
return array('foo');
}
},
));
$dumped = null;
$process = $this->getServerProcess();
$process->start(function ($type, $buffer) use ($process, &$dumped, $dumper, $data) {
if (Process::ERR === $type) {
$process->stop();
$this->fail();
} elseif ("READY\n" === $buffer) {
$dumper->dump($data);
} else {
$dumped .= $buffer;
}
});
$process->wait();
$this->assertTrue($process->isSuccessful());
$this->assertStringMatchesFormat(<<<'DUMP'
(3) "foo"
[
"timestamp" => %d.%d
"foo_provider" => [
(3) "foo"
]
]
%d
DUMP
, $dumped);
}
private function getServerProcess(): Process
{
$process = new PhpProcess(file_get_contents(__DIR__.'/../Fixtures/dump_server.php'), null, array(
'COMPONENT_ROOT' => __DIR__.'/../../',
'VAR_DUMPER_SERVER' => self::VAR_DUMPER_SERVER,
));
$process->inheritEnvironmentVariables(true);
return $process->setTimeout(9);
}
}

View File

@@ -0,0 +1,38 @@
<?php
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Server\DumpServer;
use Symfony\Component\VarDumper\VarDumper;
$componentRoot = $_SERVER['COMPONENT_ROOT'];
if (!is_file($file = $componentRoot.'/vendor/autoload.php')) {
$file = $componentRoot.'/../../../../vendor/autoload.php';
}
require $file;
$cloner = new VarCloner();
$cloner->setMaxItems(-1);
$dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_STRING_LENGTH);
$dumper->setColors(false);
VarDumper::setHandler(function ($var) use ($cloner, $dumper) {
$data = $cloner->cloneVar($var)->withRefHandles(false);
$dumper->dump($data);
});
$server = new DumpServer(getenv('VAR_DUMPER_SERVER'));
$server->start();
echo "READY\n";
$server->listen(function (Data $data, array $context, $clientId) {
dump((string) $data, $context, $clientId);
exit(0);
});

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Tests\Server;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Process\PhpProcess;
use Symfony\Component\Process\Process;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
use Symfony\Component\VarDumper\Server\Connection;
class ConnectionTest extends TestCase
{
private const VAR_DUMPER_SERVER = 'tcp://127.0.0.1:9913';
public function testDump()
{
$cloner = new VarCloner();
$data = $cloner->cloneVar('foo');
$connection = new Connection(self::VAR_DUMPER_SERVER, array(
'foo_provider' => new class() implements ContextProviderInterface {
public function getContext(): ?array
{
return array('foo');
}
},
));
$dumped = null;
$process = $this->getServerProcess();
$process->start(function ($type, $buffer) use ($process, &$dumped, $connection, $data) {
if (Process::ERR === $type) {
$process->stop();
$this->fail();
} elseif ("READY\n" === $buffer) {
$connection->write($data);
} else {
$dumped .= $buffer;
}
});
$process->wait();
$this->assertTrue($process->isSuccessful());
$this->assertStringMatchesFormat(<<<'DUMP'
(3) "foo"
[
"timestamp" => %d.%d
"foo_provider" => [
(3) "foo"
]
]
%d
DUMP
, $dumped);
}
public function testNoServer()
{
$cloner = new VarCloner();
$data = $cloner->cloneVar('foo');
$connection = new Connection(self::VAR_DUMPER_SERVER);
$start = microtime(true);
$this->assertFalse($connection->write($data));
$this->assertLessThan(1, microtime(true) - $start);
}
private function getServerProcess(): Process
{
$process = new PhpProcess(file_get_contents(__DIR__.'/../Fixtures/dump_server.php'), null, array(
'COMPONENT_ROOT' => __DIR__.'/../../',
'VAR_DUMPER_SERVER' => self::VAR_DUMPER_SERVER,
));
$process->inheritEnvironmentVariables(true);
return $process->setTimeout(9);
}
}

View File

@@ -38,4 +38,9 @@ EODUMP;
$this->assertDumpEquals($expected, $data);
}
public function testAllowsNonScalarExpectation()
{
$this->assertDumpEquals(new \ArrayObject(array('bim' => 'bam')), new \ArrayObject(array('bim' => 'bam')));
}
}

View File

@@ -16,20 +16,23 @@
}
],
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/polyfill-mbstring": "~1.0"
"php": "^7.1.3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php72": "~1.5"
},
"require-dev": {
"ext-iconv": "*",
"symfony/process": "~3.4|~4.0",
"twig/twig": "~1.34|~2.4"
},
"conflict": {
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
"symfony/console": "<3.4"
},
"suggest": {
"ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
"ext-intl": "To show region name in time zone dump",
"ext-symfony_debug": ""
"symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
},
"autoload": {
"files": [ "Resources/functions/dump.php" ],
@@ -38,10 +41,13 @@
"/Tests/"
]
},
"bin": [
"Resources/bin/var-dump-server"
],
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
"dev-master": "4.1-dev"
}
}
}