diff --git a/.travis.yml b/.travis.yml index da1bacca59..711fa49c74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 8fa8bf5e11..66b42ffcd8 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -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); + } + } } diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 2e1d71808e..3a8b7d247e 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -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); diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php index f1e3fb7c61..af88d17fd2 100644 --- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php +++ b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php @@ -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() { diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/Throwing.php b/src/Symfony/Component/Debug/Tests/Fixtures/Throwing.php new file mode 100644 index 0000000000..21e0aba17d --- /dev/null +++ b/src/Symfony/Component/Debug/Tests/Fixtures/Throwing.php @@ -0,0 +1,3 @@ + */ class File extends Constraint diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index af32dae4ae..3381076f3e 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -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); } diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf index 4fa0d42220..a3199bcc9d 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf @@ -100,7 +100,7 @@ This value is not valid. - Αυτή η τιμή δεν είναι έκγυρη. + Αυτή η τιμή δεν είναι έγκυρη. This value is not a valid time. @@ -136,11 +136,11 @@ This is not a valid IP address. - Αυτό δεν είναι μια έκγυρη διεύθυνση IP. + Αυτό δεν είναι μια έγκυρη διεύθυνση IP. This value is not a valid language. - Αυτή η τιμή δεν αντιστοιχεί σε μια έκγυρη γλώσσα. + Αυτή η τιμή δεν αντιστοιχεί σε μια έγκυρη γλώσσα. This value is not a valid locale. @@ -148,7 +148,7 @@ This value is not a valid country. - Αυτή η τιμή δεν αντιστοιχεί σε μια έκγυρη χώρα. + Αυτή η τιμή δεν αντιστοιχεί σε μια έγκυρη χώρα. This value is already used. diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/GroupSequenceProviderChildEntity.php b/src/Symfony/Component/Validator/Tests/Fixtures/GroupSequenceProviderChildEntity.php new file mode 100644 index 0000000000..be7191f9b6 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/GroupSequenceProviderChildEntity.php @@ -0,0 +1,16 @@ + + * + * 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 +{ +} diff --git a/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php index 9a23e8cf35..a3f8a8c1ec 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php @@ -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 -} diff --git a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php index 83f6aa7c31..0fbd213abe 100644 --- a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php @@ -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) { diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 02ec543b5a..90596f2551 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -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 += ''; 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;