Merge branch '2.8' into 3.3

* 2.8:
  [VarDumper] Enhance docblock to tell about AbstractDumper::dumpLine(-1)
  [Debug] Remove false-positive check in DebugClassLoader
  [Validator] Fix use of GroupSequenceProvider in child classes
  Change number PHPDoc type to int|float
  [VarDumper] Strengthen dumped JS
  [travis] Add timing info
  [Validator] Fix Greek translation
  [Console] Initialize lazily to render exceptions properly
  [Validator] Add a property tag for File::$maxSize
This commit is contained in:
Fabien Potencier 2017-08-27 07:52:21 -07:00
commit 726c567d14
13 changed files with 139 additions and 40 deletions

View File

@ -56,15 +56,38 @@ before_install:
export PHPUNIT_X="$PHPUNIT --exclude-group tty,benchmark,intl-data"
export COMPOSER_UP='composer update --no-progress --no-suggest --ansi'
nanoseconds() {
local cmd="date"
local format="+%s%N"
local os=$(uname)
if hash gdate > /dev/null 2>&1; then
cmd="gdate"
elif [[ "$os" = Darwin ]]; then
format="+%s000000000"
fi
$cmd -u $format
}
export -f nanoseconds
# tfold is a helper to create folded reports
tfold () {
title=$1
fold=$(echo $title | sed -r 's/[^-_A-Za-z\d]+/./g')
local title=$1
local fold=$(echo $title | sed -r 's/[^-_A-Za-z0-9]+/./g')
shift
echo -e "travis_fold:start:$fold\\n\\e[1;34m$title\\e[0m"
bash -xc "$*" 2>&1 &&
local id=$(printf %08x $(( RANDOM * RANDOM )))
local start=$(nanoseconds)
echo -e "travis_fold:start:$fold"
echo -e "travis_time:start:$id"
echo -e "\\e[1;34m$title\\e[0m"
bash -xc "$*" 2>&1
local ok=$?
local end=$(nanoseconds)
echo -e "\\ntravis_time:end:$id:start=$start,finish=$end,duration=$(($end-$start))"
(exit $ok) &&
echo -e "\\e[32mOK\\e[0m $title\\n\\ntravis_fold:end:$fold" ||
( echo -e "\\e[41mKO\\e[0m $title\\n" && exit 1 )
echo -e "\\e[41mKO\\e[0m $title\\n"
(exit $ok)
}
export -f tfold

View File

@ -72,6 +72,7 @@ class Application
private $terminal;
private $defaultCommand;
private $singleCommand;
private $initialized;
/**
* @param string $name The name of the application
@ -83,12 +84,6 @@ class Application
$this->version = $version;
$this->terminal = new Terminal();
$this->defaultCommand = 'list';
$this->helperSet = $this->getDefaultHelperSet();
$this->definition = $this->getDefaultInputDefinition();
foreach ($this->getDefaultCommands() as $command) {
$this->add($command);
}
}
public function setDispatcher(EventDispatcherInterface $dispatcher)
@ -195,10 +190,11 @@ class Application
if (!$name) {
$name = $this->defaultCommand;
$this->definition->setArguments(array_merge(
$this->definition->getArguments(),
$definition = $this->getDefinition();
$definition->setArguments(array_merge(
$definition->getArguments(),
array(
'command' => new InputArgument('command', InputArgument::OPTIONAL, $this->definition->getArgument('command')->getDescription(), $name),
'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
)
));
}
@ -248,6 +244,10 @@ class Application
*/
public function getHelperSet()
{
if (!$this->helperSet) {
$this->helperSet = $this->getDefaultHelperSet();
}
return $this->helperSet;
}
@ -268,6 +268,10 @@ class Application
*/
public function getDefinition()
{
if (!$this->definition) {
$this->definition = $this->getDefaultInputDefinition();
}
if ($this->singleCommand) {
$inputDefinition = $this->definition;
$inputDefinition->setArguments();
@ -424,6 +428,8 @@ class Application
*/
public function add(Command $command)
{
$this->init();
$command->setApplication($this);
if (!$command->isEnabled()) {
@ -456,6 +462,8 @@ class Application
*/
public function get($name)
{
$this->init();
if (!isset($this->commands[$name])) {
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
}
@ -483,6 +491,8 @@ class Application
*/
public function has($name)
{
$this->init();
return isset($this->commands[$name]);
}
@ -560,6 +570,8 @@ class Application
*/
public function find($name)
{
$this->init();
$allCommands = array_keys($this->commands);
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
$commands = preg_grep('{^'.$expr.'}', $allCommands);
@ -626,6 +638,8 @@ class Application
*/
public function all($namespace = null)
{
$this->init();
if (null === $namespace) {
return $this->commands;
}
@ -1139,4 +1153,16 @@ class Application
return $namespaces;
}
private function init()
{
if ($this->initialized) {
return;
}
$this->initialized = true;
foreach ($this->getDefaultCommands() as $command) {
$this->add($command);
}
}
}

View File

@ -26,6 +26,7 @@ class DebugClassLoader
{
private $classLoader;
private $isFinder;
private $loaded = array();
private static $caseCheck;
private static $final = array();
private static $finalMethods = array();
@ -139,9 +140,10 @@ class DebugClassLoader
ErrorHandler::stackErrors();
try {
if ($this->isFinder) {
if ($this->isFinder && !isset($this->loaded[$class])) {
$this->loaded[$class] = true;
if ($file = $this->classLoader[0]->findFile($class)) {
require_once $file;
require $file;
}
} else {
call_user_func($this->classLoader, $class);

View File

@ -59,6 +59,23 @@ class DebugClassLoaderTest extends TestCase
$this->fail('DebugClassLoader did not register');
}
/**
* @expectedException \Exception
* @expectedExceptionMessage boo
*/
public function testThrowingClass()
{
try {
class_exists(__NAMESPACE__.'\Fixtures\Throwing');
$this->fail('Exception expected');
} catch (\Exception $e) {
$this->assertSame('boo', $e->getMessage());
}
// the second call also should throw
class_exists(__NAMESPACE__.'\Fixtures\Throwing');
}
public function testUnsilencing()
{
if (\PHP_VERSION_ID >= 70000) {
@ -124,6 +141,7 @@ class DebugClassLoaderTest extends TestCase
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Case mismatch between loaded and declared class names
*/
public function testNameCaseMismatch()
{
@ -145,6 +163,7 @@ class DebugClassLoaderTest extends TestCase
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Case mismatch between loaded and declared class names
*/
public function testPsr4CaseMismatch()
{

View File

@ -0,0 +1,3 @@
<?php
throw new \Exception('boo');

View File

@ -357,9 +357,9 @@ class NumberFormatter
/**
* Format a number.
*
* @param number $value The value to format
* @param int $type Type of the formatting, one of the format type constants
* Only type NumberFormatter::TYPE_DEFAULT is currently supported.
* @param int|float $value The value to format
* @param int $type Type of the formatting, one of the format type constants
* Only type NumberFormatter::TYPE_DEFAULT is currently supported.
*
* @return bool|string The formatted value or false on error
*

View File

@ -18,6 +18,8 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*
* @property int $maxSize
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class File extends Constraint

View File

@ -339,6 +339,10 @@ class ClassMetadata extends GenericMetadata implements ClassMetadataInterface
*/
public function mergeConstraints(ClassMetadata $source)
{
if ($source->isGroupSequenceProvider()) {
$this->setGroupSequenceProvider(true);
}
foreach ($source->getConstraints() as $constraint) {
$this->addConstraint(clone $constraint);
}

View File

@ -100,7 +100,7 @@
</trans-unit>
<trans-unit id="25">
<source>This value is not valid.</source>
<target>Αυτή η τιμή δεν είναι έκγυρη.</target>
<target>Αυτή η τιμή δεν είναι έγκυρη.</target>
</trans-unit>
<trans-unit id="26">
<source>This value is not a valid time.</source>
@ -136,11 +136,11 @@
</trans-unit>
<trans-unit id="37">
<source>This is not a valid IP address.</source>
<target>Αυτό δεν είναι μια έκγυρη διεύθυνση IP.</target>
<target>Αυτό δεν είναι μια έγκυρη διεύθυνση IP.</target>
</trans-unit>
<trans-unit id="38">
<source>This value is not a valid language.</source>
<target>Αυτή η τιμή δεν αντιστοιχεί σε μια έκγυρη γλώσσα.</target>
<target>Αυτή η τιμή δεν αντιστοιχεί σε μια έγκυρη γλώσσα.</target>
</trans-unit>
<trans-unit id="39">
<source>This value is not a valid locale.</source>
@ -148,7 +148,7 @@
</trans-unit>
<trans-unit id="40">
<source>This value is not a valid country.</source>
<target>Αυτή η τιμή δεν αντιστοιχεί σε μια έκγυρη χώρα.</target>
<target>Αυτή η τιμή δεν αντιστοιχεί σε μια έγκυρη χώρα.</target>
</trans-unit>
<trans-unit id="41">
<source>This value is already used.</source>

View File

@ -0,0 +1,16 @@
<?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\Validator\Tests\Fixtures;
class GroupSequenceProviderChildEntity extends GroupSequenceProviderEntity
{
}

View File

@ -24,6 +24,7 @@ class ClassMetadataTest extends TestCase
const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity';
const PARENTCLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent';
const PROVIDERCLASS = 'Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderEntity';
const PROVIDERCHILDCLASS = 'Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderChildEntity';
protected $metadata;
@ -301,6 +302,17 @@ class ClassMetadataTest extends TestCase
$this->assertTrue($metadata->isGroupSequenceProvider());
}
public function testMergeConstraintsMergesGroupSequenceProvider()
{
$parent = new ClassMetadata(self::PROVIDERCLASS);
$parent->setGroupSequenceProvider(true);
$metadata = new ClassMetadata(self::PROVIDERCHILDCLASS);
$metadata->mergeConstraints($parent);
$this->assertTrue($metadata->isGroupSequenceProvider());
}
/**
* https://github.com/symfony/symfony/issues/11604.
*/
@ -309,13 +321,3 @@ class ClassMetadataTest extends TestCase
$this->assertCount(0, $this->metadata->getPropertyMetadata('foo'), '->getPropertyMetadata() returns an empty collection if no metadata is configured for the given property');
}
}
class ParentClass
{
public $example = 0;
}
class ChildClass extends ParentClass
{
public $example = 1; // overrides parent property of same name
}

View File

@ -159,7 +159,8 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
/**
* Dumps the current line.
*
* @param int $depth The recursive depth in the dumped structure for the line being dumped
* @param int $depth The recursive depth in the dumped structure for the line being dumped,
* or -1 to signal the end-of-dump to the line dumper callable
*/
protected function dumpLine($depth)
{

View File

@ -155,10 +155,10 @@ if (!doc.addEventListener) {
function toggle(a, recursive) {
var s = a.nextSibling || {}, oldClass = s.className, arrow, newClass;
if ('sf-dump-compact' == oldClass) {
if (/\bsf-dump-compact\b/.test(oldClass)) {
arrow = '▼';
newClass = 'sf-dump-expanded';
} else if ('sf-dump-expanded' == oldClass) {
} else if (/\bsf-dump-expanded\b/.test(oldClass)) {
arrow = '▶';
newClass = 'sf-dump-compact';
} else {
@ -166,13 +166,13 @@ function toggle(a, recursive) {
}
a.lastChild.innerHTML = arrow;
s.className = newClass;
s.className = s.className.replace(/\bsf-dump-(compact|expanded)\b/, newClass);
if (recursive) {
try {
a = s.querySelectorAll('.'+oldClass);
for (s = 0; s < a.length; ++s) {
if (a[s].className !== newClass) {
if (-1 == a[s].className.indexOf(newClass)) {
a[s].className = newClass;
a[s].previousSibling.lastChild.innerHTML = arrow;
}
@ -334,7 +334,7 @@ return function (root, x) {
if (f && t && f[0] !== t[0]) {
r.innerHTML = r.innerHTML.replace(new RegExp('^'+f[0].replace(rxEsc, '\\$1'), 'mg'), t[0]);
}
if ('sf-dump-compact' == r.className) {
if (/\bsf-dump-compact\b/.test(r.className)) {
toggle(s, isCtrlKey(e));
}
}
@ -378,6 +378,7 @@ return function (root, x) {
a.title = (a.title ? a.title+'\n[' : '[')+keyHint+'+click] Expand all children';
a.innerHTML += '<span>▼</span>';
a.className += ' sf-dump-toggle';
x = 1;
if ('sf-dump' != elt.parentNode.className) {
x += elt.parentNode.getAttribute('data-depth')/1;
@ -386,7 +387,7 @@ return function (root, x) {
if (x > options.maxDepth) {
toggle(a);
}
} else if ('sf-dump-ref' == elt.className && (a = elt.getAttribute('href'))) {
} else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) {
a = a.substr(1);
elt.className += ' '+a;