diff --git a/.github/build-packages.php b/.github/build-packages.php
new file mode 100644
index 0000000000..2a2b4a396c
--- /dev/null
+++ b/.github/build-packages.php
@@ -0,0 +1,74 @@
+ $_SERVER['argc']) {
+ echo "Usage: branch version dir1 dir2 ... dirN\n";
+ exit(1);
+}
+chdir(dirname(__DIR__));
+
+$dirs = $_SERVER['argv'];
+array_shift($dirs);
+$mergeBase = trim(shell_exec(sprintf('git merge-base %s HEAD', array_shift($dirs))));
+$version = array_shift($dirs);
+
+$packages = array();
+$flags = PHP_VERSION_ID >= 50400 ? JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE : 0;
+
+foreach ($dirs as $k => $dir) {
+ if (!system("git diff --name-only $mergeBase -- $dir", $exitStatus)) {
+ if ($exitStatus) {
+ exit($exitStatus);
+ }
+ unset($dirs[$k]);
+ continue;
+ }
+ echo "$dir\n";
+
+ $json = ltrim(file_get_contents($dir.'/composer.json'));
+ if (null === $package = json_decode($json)) {
+ passthru("composer validate $dir/composer.json");
+ exit(1);
+ }
+
+ $package->repositories = array(array(
+ 'type' => 'composer',
+ 'url' => 'file://'.str_replace(DIRECTORY_SEPARATOR, '/', dirname(__DIR__)).'/',
+ ));
+ if (false === strpos($json, "\n \"repositories\": [\n")) {
+ $json = rtrim(json_encode(array('repositories' => $package->repositories), $flags), "\n}").','.substr($json, 1);
+ file_put_contents($dir.'/composer.json', $json);
+ }
+ passthru("cd $dir && tar -cf package.tar --exclude='package.tar' *");
+
+ $package->version = $version.'.999';
+ $package->dist['type'] = 'tar';
+ $package->dist['url'] = 'file://'.str_replace(DIRECTORY_SEPARATOR, '/', dirname(__DIR__))."/$dir/package.tar";
+
+ $packages[$package->name][$package->version] = $package;
+
+ $versions = file_get_contents('https://packagist.org/packages/'.$package->name.'.json');
+ $versions = json_decode($versions);
+
+ foreach ($versions->package->versions as $v => $package) {
+ $packages[$package->name] += array($v => $package);
+ }
+}
+
+file_put_contents('packages.json', json_encode(compact('packages'), $flags));
+
+if ($dirs) {
+ $json = ltrim(file_get_contents('composer.json'));
+ if (null === $package = json_decode($json)) {
+ passthru("composer validate $dir/composer.json");
+ exit(1);
+ }
+
+ $package->repositories = array(array(
+ 'type' => 'composer',
+ 'url' => 'file://'.str_replace(DIRECTORY_SEPARATOR, '/', dirname(__DIR__)).'/',
+ ));
+ if (false === strpos($json, "\n \"repositories\": [\n")) {
+ $json = rtrim(json_encode(array('repositories' => $package->repositories), $flags), "\n}").','.substr($json, 1);
+ file_put_contents('composer.json', $json);
+ }
+}
diff --git a/.github/travis.php b/.github/travis.php
deleted file mode 100644
index daaa858dc7..0000000000
--- a/.github/travis.php
+++ /dev/null
@@ -1,54 +0,0 @@
- $_SERVER['argc']) {
- echo "Usage: branch version dir1 dir2 ... dirN\n";
- exit(1);
-}
-chdir(dirname(__DIR__));
-
-$dirs = $_SERVER['argv'];
-array_shift($dirs);
-$branch = array_shift($dirs);
-$version = array_shift($dirs);
-
-$packages = array();
-$flags = PHP_VERSION_ID >= 50400 ? JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE : 0;
-
-foreach ($dirs as $dir) {
- if (!system("git diff --name-only $branch...HEAD -- $dir", $exitStatus)) {
- if ($exitStatus) {
- exit($exitStatus);
- }
- continue;
- }
- echo "$dir\n";
-
- $json = ltrim(file_get_contents($dir.'/composer.json'));
- if (null === $package = json_decode($json)) {
- passthru("composer validate $dir/composer.json");
- exit(1);
- }
-
- $package->repositories = array(array(
- 'type' => 'composer',
- 'url' => 'file://'.dirname(__DIR__).'/',
- ));
- $json = rtrim(json_encode(array('repositories' => $package->repositories), $flags), "\n}").','.substr($json, 1);
- file_put_contents($dir.'/composer.json', $json);
- passthru("cd $dir && tar -cf package.tar --exclude='package.tar' *");
-
- $package->version = 'master' !== $version ? $version.'.999' : 'dev-master';
- $package->dist['type'] = 'tar';
- $package->dist['url'] = 'file://'.dirname(__DIR__)."/$dir/package.tar";
-
- $packages[$package->name][$package->version] = $package;
-
- $versions = @file_get_contents('https://packagist.org/packages/'.$package->name.'.json') ?: '{"package":{"versions":[]}}';
- $versions = json_decode($versions);
-
- foreach ($versions->package->versions as $v => $package) {
- $packages[$package->name] += array($v => $package);
- }
-}
-
-file_put_contents('packages.json', json_encode(compact('packages'), $flags));
diff --git a/.travis.yml b/.travis.yml
index 6980630039..d408596fbd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -64,24 +64,29 @@ before_install:
- if [[ ! $skip && ! $PHP = hhvm* ]]; then echo extension = redis.so >> $INI_FILE; fi;
- if [[ ! $skip && ! $PHP = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi
- if [[ ! $skip ]]; then [ -d ~/.composer ] || mkdir ~/.composer; cp .composer/* ~/.composer/; fi
- - if [[ ! $skip ]]; then ./phpunit install; fi
- if [[ ! $skip ]]; then export PHPUNIT=$(readlink -f ./phpunit); fi
- if [[ ! $skip ]]; then ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/base.ldif; fi
- if [[ ! $skip ]]; then ldapadd -h localhost:3389 -D cn=admin,dc=symfony,dc=com -w symfony -f src/Symfony/Component/Ldap/Tests/Fixtures/data/fixtures.ldif; fi
install:
+ - if [[ ! $skip && $deps ]]; then cp composer.json composer.json.orig; fi
+ - if [[ ! $skip && $deps ]]; then echo -e '{\n"require":{'"$(grep phpunit-bridge composer.json)"'"php":"*"},"minimum-stability":"dev"}' > composer.json; fi
- if [[ ! $skip ]]; then COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); fi
# Create local composer packages for each patched components and reference them in composer.json files when cross-testing components
- - if [[ ! $skip && $deps ]]; then git fetch origin $TRAVIS_BRANCH && php .github/travis.php FETCH_HEAD $TRAVIS_BRANCH $COMPONENTS; fi
+ - if [[ ! $skip && $deps ]]; then php .github/build-packages.php HEAD^ $TRAVIS_BRANCH $COMPONENTS; fi
+ - if [[ ! $skip && $deps ]]; then mv composer.json composer.json.phpunit; mv composer.json.orig composer.json; fi
+ - if [[ ! $skip && ! $deps ]]; then php .github/build-packages.php HEAD^ $TRAVIS_BRANCH src/Symfony/Bridge/PhpUnit; fi
# For the master branch when deps=high, the version before master is checked out and tested with the locally patched components
- if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then SYMFONY_VERSION=$(git ls-remote --heads | grep -o '/[1-9].*' | tail -n 1 | sed s/.//); else SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*'); fi
- - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then git fetch origin $SYMFONY_VERSION; git checkout -m FETCH_HEAD; COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); ./phpunit install; fi
+ - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then git fetch origin $SYMFONY_VERSION; git checkout -m FETCH_HEAD; COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); fi
# Legacy tests are skipped when deps=high and when the current branch version has not the same major version number than the next one
- if [[ $deps = high && ${SYMFONY_VERSION%.*} != $(git show $(git ls-remote --heads | grep -FA1 /$SYMFONY_VERSION | tail -n 1):composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9]*' | head -n 1) ]]; then LEGACY=,legacy; fi
- export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev
- - if [[ ! $deps ]]; then composer update; else export SYMFONY_DEPRECATIONS_HELPER=weak; fi
- - if [[ $TRAVIS_BRANCH = master ]]; then export SYMFONY_PHPUNIT_OVERLOAD=1; fi
- - if [[ ! $PHP = hhvm* ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi
+ - if [[ ! $skip && $deps ]]; then export SYMFONY_DEPRECATIONS_HELPER=weak; fi
+ - if [[ ! $skip && $deps ]]; then mv composer.json.phpunit composer.json; fi
+ - if [[ ! $skip ]]; then composer update; fi
+ - if [[ ! $skip ]]; then COMPOSER_ROOT_VERSION= ./phpunit install; fi
+ - if [[ ! $skip && ! $PHP = hhvm* ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi
script:
- if [[ $skip ]]; then echo -e "\\n\\e[1;34mIntermediate PHP version $PHP is skipped for pull requests.\\e[0m"; fi
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index e10ae01026..7cbed979c4 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -16,8 +16,8 @@ Symfony is the result of the work of many people who made the code better
- Kris Wallsmith (kriswallsmith)
- Jakub Zalas (jakubzalas)
- Ryan Weaver (weaverryan)
- - Kévin Dunglas (dunglas)
- Javier Eguiluz (javier.eguiluz)
+ - Kévin Dunglas (dunglas)
- Hugo Hamon (hhamon)
- Abdellatif Ait boudad (aitboudad)
- Pascal Borreli (pborreli)
@@ -52,8 +52,8 @@ Symfony is the result of the work of many people who made the code better
- Konstantin Kudryashov (everzet)
- Bilal Amarni (bamarni)
- Florin Patan (florinpatan)
- - Peter Rehm (rpet)
- Ener-Getick (energetick)
+ - Peter Rehm (rpet)
- Iltar van der Berg (kjarli)
- Kevin Bond (kbond)
- Andrej Hudec (pulzarraider)
@@ -315,6 +315,7 @@ Symfony is the result of the work of many people who made the code better
- JhonnyL
- hossein zolfi (ocean)
- Clément Gautier (clementgautier)
+ - James Halsall (jaitsu)
- Eduardo Gulias (egulias)
- giulio de donato (liuggio)
- Stéphane PY (steph_py)
@@ -396,7 +397,6 @@ Symfony is the result of the work of many people who made the code better
- Christian Wahler
- Mathieu Lemoine
- Gintautas Miselis
- - James Halsall (jaitsu)
- David Badura (davidbadura)
- Zander Baldwin
- Adam Harvey
@@ -1302,6 +1302,7 @@ Symfony is the result of the work of many people who made the code better
- Muriel (metalmumu)
- Michael Pohlers (mick_the_big)
- Cayetano Soriano Gallego (neoshadybeat)
+ - Ondrej Machulda (ondram)
- Patrick McDougle (patrick-mcdougle)
- Pablo Monterde Perez (plebs)
- Jimmy Leger (redpanda)
diff --git a/appveyor.yml b/appveyor.yml
index 07cdec2952..7f3b017616 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -46,18 +46,20 @@ install:
- echo curl.cainfo=c:\php\cacert.pem >> php.ini-max
- copy /Y php.ini-max php.ini
- cd c:\projects\symfony
- - IF NOT EXIST composer.phar (appveyor DownloadFile https://getcomposer.org/download/1.2.0/composer.phar)
+ - IF NOT EXIST composer.phar (appveyor DownloadFile https://getcomposer.org/download/1.2.1/composer.phar)
- php composer.phar self-update
- copy /Y .composer\* %APPDATA%\Composer\
- - php phpunit install
+ - php .github/build-packages.php "HEAD^" %APPVEYOR_REPO_BRANCH% src\Symfony\Bridge\PhpUnit
- IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev)
- php composer.phar update --no-progress --ansi
+ - SET COMPOSER_ROOT_VERSION=
+ - php phpunit install
test_script:
- cd c:\projects\symfony
- SET X=0
- copy /Y c:\php\php.ini-min c:\php\php.ini
- - php phpunit symfony --exclude-group benchmark,intl-data || SET X=!errorlevel!
+ - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel!
- copy /Y c:\php\php.ini-max c:\php\php.ini
- - php phpunit symfony --exclude-group benchmark,intl-data || SET X=!errorlevel!
+ - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel!
- exit %X%
diff --git a/composer.json b/composer.json
index e314ccf6d8..b6016a6d0f 100644
--- a/composer.json
+++ b/composer.json
@@ -87,6 +87,7 @@
"ocramius/proxy-manager": "~0.4|~1.0|~2.0",
"predis/predis": "~1.0",
"egulias/email-validator": "~1.2,>=1.2.8|~2.0",
+ "symfony/phpunit-bridge": "~3.2",
"symfony/polyfill-apcu": "~1.1",
"symfony/security-acl": "~2.8|~3.0",
"phpdocumentor/reflection-docblock": "^3.0"
@@ -115,11 +116,6 @@
"**/Tests/"
]
},
- "autoload-dev": {
- "psr-4": {
- "Symfony\\Bridge\\PhpUnit\\": "src/Symfony/Bridge/PhpUnit/"
- }
- },
"minimum-stability": "dev",
"extra": {
"branch-alias": {
diff --git a/phpunit b/phpunit
new file mode 100755
index 0000000000..f9243bcbf9
--- /dev/null
+++ b/phpunit
@@ -0,0 +1,9 @@
+#!/usr/bin/env php
+getDomains() as $domain) {
$newKeys = array_keys($operation->getNewMessages($domain));
$allKeys = array_keys($operation->getMessages($domain));
- $domainMessagesCount = count($newKeys) + count($allKeys);
- $io->section(sprintf('Messages extracted for domain "%s" (%d messages)', $domain, $domainMessagesCount));
-
- $io->listing(array_merge(
+ $list = array_merge(
array_diff($allKeys, $newKeys),
array_map(function ($id) {
return sprintf('%s>', $id);
@@ -178,7 +175,12 @@ EOF
array_map(function ($id) {
return sprintf('%s>', $id);
}, array_keys($operation->getObsoleteMessages($domain)))
- ));
+ );
+
+ $domainMessagesCount = count($list);
+
+ $io->section(sprintf('Messages extracted for domain "%s" (%d message%s)', $domain, $domainMessagesCount, $domainMessagesCount > 1 ? 's' : ''));
+ $io->listing($list);
$extractedMessagesCount += $domainMessagesCount;
}
@@ -187,7 +189,7 @@ EOF
$io->comment('Xliff output version is 1.2');
}
- $resultMessage = sprintf('%d messages were successfully extracted', $extractedMessagesCount);
+ $resultMessage = sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was');
}
if ($input->getOption('no-backup') === true) {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/body.css b/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/body.css
index c50c1a54c7..9a1cbb8cc2 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/body.css
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/body.css
@@ -39,7 +39,7 @@ build: 56
font-family: Georgia, "Times New Roman", Times, serif;
font-size: 20px;
color: #313131;
- word-break: break-all;
+ word-wrap: break-word;
}
.sf-reset li {
padding-bottom: 10px;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php
index d815c9d610..e5f930af93 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php
@@ -28,6 +28,15 @@ class TranslationUpdateCommandTest extends \PHPUnit_Framework_TestCase
$tester = $this->createCommandTester($this->getContainer(array('messages' => array('foo' => 'foo'))));
$tester->execute(array('command' => 'translation:update', 'locale' => 'en', 'bundle' => 'foo', '--dump-messages' => true, '--clean' => true));
$this->assertRegExp('/foo/', $tester->getDisplay());
+ $this->assertRegExp('/1 message was successfully extracted/', $tester->getDisplay());
+ }
+
+ public function testDumpTwoMessagesAndClean()
+ {
+ $tester = $this->createCommandTester($this->getContainer(array('foo' => 'foo', 'bar' => 'bar')));
+ $tester->execute(array('command' => 'translation:update', 'locale' => 'en', 'bundle' => 'foo', '--dump-messages' => true, '--clean' => true));
+ $this->assertRegExp('/foo/', $tester->getDisplay());
+ $this->assertRegExp('/bar/', $tester->getDisplay());
$this->assertRegExp('/2 messages were successfully extracted/', $tester->getDisplay());
}
diff --git a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php
index 3f7b4a639c..60745ec8e6 100644
--- a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php
+++ b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php
@@ -55,7 +55,12 @@ class ClassCollectionLoader
$classes = array_unique($classes);
- $cache = $cacheDir.'/'.$name.$extension;
+ // cache the core classes
+ if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
+ throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir));
+ }
+ $cacheDir = rtrim(realpath($cacheDir), '/'.DIRECTORY_SEPARATOR);
+ $cache = $cacheDir.DIRECTORY_SEPARATOR.$name.$extension;
// auto-reload
$reload = false;
@@ -119,6 +124,16 @@ class ClassCollectionLoader
$declared[$class->getName()] = true;
}
+ // cache the core classes
+ $cacheDir = dirname($cache);
+ if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
+ throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir));
+ }
+
+ $c = '(?:\s*+(?:(?:#|//)[^\n]*+\n|/\*(?:(?getName()] = true;
- $files[$class->getName()] = $class->getFileName();
+ $files[$class->getName()] = $file = $class->getFileName();
+ $c = file_get_contents($file);
- $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($class->getFileName()));
+ if (preg_match($strictTypesRegex, $c)) {
+ $file = explode(DIRECTORY_SEPARATOR, $file);
- // fakes namespace declaration for global code
- if (!$class->inNamespace()) {
- $c = "\nnamespace\n{\n".$c."\n}\n";
+ for ($i = 0; isset($file[$i], $cacheDir[$i]); ++$i) {
+ if ($file[$i] !== $cacheDir[$i]) {
+ break;
+ }
+ }
+ if (1 >= $i) {
+ $file = var_export(implode(DIRECTORY_SEPARATOR, $file), true);
+ } else {
+ $file = array_slice($file, $i);
+ $file = str_repeat('..'.DIRECTORY_SEPARATOR, count($cacheDir) - $i).implode(DIRECTORY_SEPARATOR, $file);
+ $file = '__DIR__.'.var_export(DIRECTORY_SEPARATOR.$file, true);
+ }
+
+ $c = "\nnamespace {require $file;}";
+ } else {
+ $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', $c);
+
+ // fakes namespace declaration for global code
+ if (!$class->inNamespace()) {
+ $c = "\nnamespace\n{\n".$c."\n}\n";
+ }
+
+ $c = self::fixNamespaceDeclarations(' realpath(__DIR__).'/Fixtures/Namespaced/Foo.php',
'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php',
'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php',
+ 'Namespaced\WithStrictTypes' => realpath(__DIR__).'/Fixtures/Namespaced/WithStrictTypes.php',
),
),
array(__DIR__.'/Fixtures/beta/NamespaceCollision', array(
diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithStrictTypes.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithStrictTypes.php
new file mode 100644
index 0000000000..3c7870592b
--- /dev/null
+++ b/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithStrictTypes.php
@@ -0,0 +1,13 @@
+commandShouldRun()) {
try {
+ $e = null;
$exitCode = $command->run($input, $output);
- } catch (\Exception $e) {
+ } catch (\Exception $x) {
+ $e = $x;
+ } catch (\Throwable $x) {
+ $e = new FatalThrowableError($x);
+ }
+ if (null !== $e) {
$event = new ConsoleExceptionEvent($command, $input, $output, $e, $e->getCode());
$this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
- $e = $event->getException();
+ if ($e !== $event->getException()) {
+ $x = $e = $event->getException();
+ }
$event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode());
$this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
- throw $e;
+ throw $x;
}
} else {
$exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php
index 3ef33f3794..c221430630 100644
--- a/src/Symfony/Component/Console/Tests/ApplicationTest.php
+++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php
@@ -942,6 +942,62 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$this->assertContains('before.foo.caught.after.', $tester->getDisplay());
}
+ /**
+ * @expectedException \LogicException
+ * @expectedExceptionMessage caught
+ */
+ public function testRunWithErrorAndDispatcher()
+ {
+ $application = new Application();
+ $application->setDispatcher($this->getDispatcher());
+ $application->setAutoExit(false);
+ $application->setCatchExceptions(false);
+
+ $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) {
+ $output->write('dym.');
+
+ throw new \Error('dymerr');
+ });
+
+ $tester = new ApplicationTester($application);
+ $tester->run(array('command' => 'dym'));
+ $this->assertContains('before.dym.caught.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
+ }
+
+ public function testRunDispatchesAllEventsWithError()
+ {
+ $application = new Application();
+ $application->setDispatcher($this->getDispatcher());
+ $application->setAutoExit(false);
+
+ $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) {
+ $output->write('dym.');
+
+ throw new \Error('dymerr');
+ });
+
+ $tester = new ApplicationTester($application);
+ $tester->run(array('command' => 'dym'));
+ $this->assertContains('before.dym.caught.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
+ }
+
+ public function testRunWithErrorFailingStatusCode()
+ {
+ $application = new Application();
+ $application->setDispatcher($this->getDispatcher());
+ $application->setAutoExit(false);
+
+ $application->register('dus')->setCode(function (InputInterface $input, OutputInterface $output) {
+ $output->write('dus.');
+
+ throw new \Error('duserr');
+ });
+
+ $tester = new ApplicationTester($application);
+ $tester->run(array('command' => 'dus'));
+ $this->assertSame(1, $tester->getStatusCode(), 'Status code should be 1');
+ }
+
public function testRunWithDispatcherSkippingCommand()
{
$application = new Application();
diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json
index cb73a00b8d..d19fd7f881 100644
--- a/src/Symfony/Component/Console/composer.json
+++ b/src/Symfony/Component/Console/composer.json
@@ -17,7 +17,8 @@
],
"require": {
"php": ">=5.5.9",
- "symfony/polyfill-mbstring": "~1.0"
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/debug": "~2.7,>=2.7.2"
},
"require-dev": {
"symfony/event-dispatcher": "~2.8|~3.0",
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php
index 508a8978ea..59d4e0a767 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php
@@ -56,14 +56,12 @@ class RepeatedPass implements CompilerPassInterface
*/
public function process(ContainerBuilder $container)
{
- $this->repeat = false;
- foreach ($this->passes as $pass) {
- $pass->process($container);
- }
-
- if ($this->repeat) {
- $this->process($container);
- }
+ do {
+ $this->repeat = false;
+ foreach ($this->passes as $pass) {
+ $pass->process($container);
+ }
+ } while ($this->repeat);
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
index 997d06f41b..438f351067 100644
--- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
@@ -351,7 +351,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
{
$id = strtolower($id);
- if ($this->isFrozen() && (!isset($this->definitions[$id]) || !$this->definitions[$id]->isSynthetic())) {
+ if ($this->isFrozen() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
+ // setting a synthetic service on a frozen container is alright
throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a frozen container is not allowed.', $id));
}
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php
index c569232f20..75f87dc7fc 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php
@@ -128,7 +128,7 @@ class GraphvizDumper extends Dumper
*
* @return array An array of edges
*/
- private function findEdges($id, $arguments, $required, $name)
+ private function findEdges($id, array $arguments, $required, $name)
{
$edges = array();
foreach ($arguments as $argument) {
@@ -244,7 +244,7 @@ class GraphvizDumper extends Dumper
*
* @return string A comma separated list of attributes
*/
- private function addAttributes($attributes)
+ private function addAttributes(array $attributes)
{
$code = array();
foreach ($attributes as $k => $v) {
@@ -261,7 +261,7 @@ class GraphvizDumper extends Dumper
*
* @return string A space separated list of options
*/
- private function addOptions($options)
+ private function addOptions(array $options)
{
$code = array();
foreach ($options as $k => $v) {
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 089d3053cf..c87a7e6b92 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -374,7 +374,7 @@ class PhpDumper extends Dumper
* @throws InvalidArgumentException
* @throws RuntimeException
*/
- private function addServiceInstance($id, $definition)
+ private function addServiceInstance($id, Definition $definition)
{
$class = $definition->getClass();
@@ -422,7 +422,7 @@ class PhpDumper extends Dumper
*
* @return bool
*/
- private function isSimpleInstance($id, $definition)
+ private function isSimpleInstance($id, Definition $definition)
{
foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
@@ -446,7 +446,7 @@ class PhpDumper extends Dumper
*
* @return string
*/
- private function addServiceMethodCalls($id, $definition, $variableName = 'instance')
+ private function addServiceMethodCalls($id, Definition $definition, $variableName = 'instance')
{
$calls = '';
foreach ($definition->getMethodCalls() as $call) {
@@ -461,7 +461,7 @@ class PhpDumper extends Dumper
return $calls;
}
- private function addServiceProperties($id, $definition, $variableName = 'instance')
+ private function addServiceProperties($id, Definition $definition, $variableName = 'instance')
{
$code = '';
foreach ($definition->getProperties() as $name => $value) {
@@ -481,7 +481,7 @@ class PhpDumper extends Dumper
*
* @throws ServiceCircularReferenceException when the container contains a circular reference
*/
- private function addServiceInlinedDefinitionsSetup($id, $definition)
+ private function addServiceInlinedDefinitionsSetup($id, Definition $definition)
{
$this->referenceVariables[$id] = new Variable('instance');
@@ -525,7 +525,7 @@ class PhpDumper extends Dumper
*
* @return string
*/
- private function addServiceConfigurator($id, $definition, $variableName = 'instance')
+ private function addServiceConfigurator($id, Definition $definition, $variableName = 'instance')
{
if (!$callable = $definition->getConfigurator()) {
return '';
@@ -561,7 +561,7 @@ class PhpDumper extends Dumper
*
* @return string
*/
- private function addService($id, $definition)
+ private function addService($id, Definition $definition)
{
$this->definitionVariables = new \SplObjectStorage();
$this->referenceVariables = array();
@@ -1063,7 +1063,7 @@ EOF;
*
* @throws InvalidArgumentException
*/
- private function exportParameters($parameters, $path = '', $indent = 12)
+ private function exportParameters(array $parameters, $path = '', $indent = 12)
{
$php = array();
foreach ($parameters as $key => $value) {
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
index 68ce5005d0..bd51e82d83 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
@@ -271,7 +271,7 @@ class XmlDumper extends Dumper
* @param \DOMElement $parent
* @param string $keyAttribute
*/
- private function convertParameters($parameters, $type, \DOMElement $parent, $keyAttribute = 'key')
+ private function convertParameters(array $parameters, $type, \DOMElement $parent, $keyAttribute = 'key')
{
$withKeys = array_keys($parameters) !== range(0, count($parameters) - 1);
foreach ($parameters as $key => $value) {
@@ -317,7 +317,7 @@ class XmlDumper extends Dumper
*
* @return array
*/
- private function escape($arguments)
+ private function escape(array $arguments)
{
$args = array();
foreach ($arguments as $k => $v) {
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
index 24ec562a40..1a3efdd395 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
@@ -307,7 +307,7 @@ class YamlDumper extends Dumper
*
* @return array
*/
- private function prepareParameters($parameters, $escape = true)
+ private function prepareParameters(array $parameters, $escape = true)
{
$filtered = array();
foreach ($parameters as $key => $value) {
@@ -330,7 +330,7 @@ class YamlDumper extends Dumper
*
* @return array
*/
- private function escape($arguments)
+ private function escape(array $arguments)
{
$args = array();
foreach ($arguments as $k => $v) {
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index 98e78efce8..7679a709b8 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -452,9 +452,9 @@ class YamlFileLoader extends FileLoader
{
if (is_array($value)) {
$value = array_map(array($this, 'resolveServices'), $value);
- } elseif (is_string($value) && 0 === strpos($value, '@=')) {
+ } elseif (is_string($value) && 0 === strpos($value, '@=')) {
return new Expression(substr($value, 2));
- } elseif (is_string($value) && 0 === strpos($value, '@')) {
+ } elseif (is_string($value) && 0 === strpos($value, '@')) {
if (0 === strpos($value, '@@')) {
$value = substr($value, 1);
$invalidBehavior = null;
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index eb7890b506..8a76789277 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -708,14 +708,12 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$container->set('a', new \stdClass());
}
- /**
- * @expectedException \BadMethodCallException
- */
public function testThrowsExceptionWhenAddServiceOnAFrozenContainer()
{
$container = new ContainerBuilder();
$container->compile();
- $container->set('a', new \stdClass());
+ $container->set('a', $foo = new \stdClass());
+ $this->assertSame($foo, $container->get('a'));
}
public function testNoExceptionWhenSetSyntheticServiceOnAFrozenContainer()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
index 837bceeb2a..09fabe0f62 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
@@ -211,7 +211,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
- * @expectedExcepionMessage You have requested a synthetic service ("request"). The DIC does not know how to construct this service.
+ * @expectedExceptionMessage You have requested a synthetic service ("request"). The DIC does not know how to construct this service.
*/
public function testGetSyntheticServiceAlwaysThrows()
{
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
index 22d655f60e..8434f8932b 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
@@ -133,7 +133,10 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
}
// read timestamp into DateTime object - the formatter delivers a timestamp
- $dateTime = new \DateTime(sprintf('@%s', $timestamp), new \DateTimeZone($this->outputTimezone));
+ $dateTime = new \DateTime(sprintf('@%s', $timestamp));
+ // set timezone separately, as it would be ignored if set via the constructor,
+ // see http://php.net/manual/en/datetime.construct.php
+ $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php
index ff8d0b4fde..86894f1adc 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php
@@ -142,6 +142,13 @@ class FormType extends BaseType
};
};
+ // Wrap "post_max_size_message" in a closure to translate it lazily
+ $uploadMaxSizeMessage = function (Options $options) {
+ return function () use ($options) {
+ return $options['post_max_size_message'];
+ };
+ };
+
// For any form that is not represented by a single HTML control,
// errors should bubble up by default
$errorBubbling = function (Options $options) {
@@ -172,9 +179,11 @@ class FormType extends BaseType
'action' => '',
'attr' => array(),
'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.',
+ 'upload_max_size_message' => $uploadMaxSizeMessage, // internal
));
$resolver->setAllowedTypes('label_attr', 'array');
+ $resolver->setAllowedTypes('upload_max_size_message', array('callable'));
}
/**
diff --git a/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php b/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php
index d1e5eece7b..004f4778f9 100644
--- a/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php
+++ b/src/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php
@@ -78,7 +78,7 @@ class HttpFoundationRequestHandler implements RequestHandlerInterface
$form->submit(null, false);
$form->addError(new FormError(
- $form->getConfig()->getOption('post_max_size_message'),
+ call_user_func($form->getConfig()->getOption('upload_max_size_message')),
null,
array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize())
));
diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php
index d89a326f77..6f1f632118 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Type/UploadValidatorExtension.php
@@ -42,9 +42,10 @@ class UploadValidatorExtension extends AbstractTypeExtension
{
$translator = $this->translator;
$translationDomain = $this->translationDomain;
-
- $resolver->setNormalizer('post_max_size_message', function (Options $options, $errorMessage) use ($translator, $translationDomain) {
- return $translator->trans($errorMessage, array(), $translationDomain);
+ $resolver->setNormalizer('upload_max_size_message', function (Options $options, $message) use ($translator, $translationDomain) {
+ return function () use ($translator, $translationDomain, $message) {
+ return $translator->trans(call_user_func($message), array(), $translationDomain);
+ };
});
}
diff --git a/src/Symfony/Component/Form/NativeRequestHandler.php b/src/Symfony/Component/Form/NativeRequestHandler.php
index 5541e96ad5..3607feb99c 100644
--- a/src/Symfony/Component/Form/NativeRequestHandler.php
+++ b/src/Symfony/Component/Form/NativeRequestHandler.php
@@ -86,7 +86,7 @@ class NativeRequestHandler implements RequestHandlerInterface
$form->submit(null, false);
$form->addError(new FormError(
- $form->getConfig()->getOption('post_max_size_message'),
+ call_user_func($form->getConfig()->getOption('upload_max_size_message')),
null,
array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize())
));
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php
index 1ac2dc5ea9..b54c0d50ef 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php
@@ -134,6 +134,23 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
$this->assertEquals($dateTime->format('d.m.Y, H:i'), $transformer->transform($input));
}
+ public function testReverseTransformWithNoConstructorParameters()
+ {
+ $tz = date_default_timezone_get();
+ date_default_timezone_set('Europe/Rome');
+
+ $transformer = new DateTimeToLocalizedStringTransformer();
+
+ $dateTime = new \DateTime('2010-02-03 04:05');
+
+ $this->assertEquals(
+ $dateTime->format('c'),
+ $transformer->reverseTransform('03.02.2010, 04:05')->format('c')
+ );
+
+ date_default_timezone_set($tz);
+ }
+
public function testTransformWithDifferentPatterns()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'MM*yyyy*dd HH|mm|ss');
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
index 95a11a78c7..dbe13a2745 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/UploadValidatorExtensionTest.php
@@ -13,6 +13,7 @@ namespace Symfony\Component\Form\Tests\Extension\Validator\Type;
use Symfony\Component\Form\Extension\Validator\Type\UploadValidatorExtension;
use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\OptionsResolver\Options;
class UploadValidatorExtensionTest extends TypeTestCase
{
@@ -29,10 +30,15 @@ class UploadValidatorExtensionTest extends TypeTestCase
$resolver = new OptionsResolver();
$resolver->setDefault('post_max_size_message', 'old max {{ max }}!');
+ $resolver->setDefault('upload_max_size_message', function (Options $options, $message) {
+ return function () use ($options) {
+ return $options['post_max_size_message'];
+ };
+ });
$extension->configureOptions($resolver);
$options = $resolver->resolve();
- $this->assertEquals('translated max {{ max }}!', $options['post_max_size_message']);
+ $this->assertEquals('translated max {{ max }}!', call_user_func($options['upload_max_size_message']));
}
}
diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
index ba4d6af5a8..0641486b7a 100644
--- a/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
+++ b/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php
@@ -58,6 +58,7 @@ abstract class Voter implements VoterInterface
/**
* Perform a single access check operation on a given attribute, subject and token.
+ * It is safe to assume that $attribute and $subject already passed the "supports()" method check.
*
* @param string $attribute
* @param mixed $subject
diff --git a/src/Symfony/Component/Security/Core/Role/RoleHierarchy.php b/src/Symfony/Component/Security/Core/Role/RoleHierarchy.php
index 793007e7f5..95e76ee279 100644
--- a/src/Symfony/Component/Security/Core/Role/RoleHierarchy.php
+++ b/src/Symfony/Component/Security/Core/Role/RoleHierarchy.php
@@ -65,9 +65,17 @@ class RoleHierarchy implements RoleHierarchyInterface
}
$visited[] = $role;
- $this->map[$main] = array_unique(array_merge($this->map[$main], $this->hierarchy[$role]));
- $additionalRoles = array_merge($additionalRoles, array_diff($this->hierarchy[$role], $visited));
+
+ foreach ($this->hierarchy[$role] as $roleToAdd) {
+ $this->map[$main][] = $roleToAdd;
+ }
+
+ foreach (array_diff($this->hierarchy[$role], $visited) as $additionalRole) {
+ $additionalRoles[] = $additionalRole;
+ }
}
+
+ $this->map[$main] = array_unique($this->map[$main]);
}
}
}