+ */
+class ConsoleHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testConstructor()
+ {
+ $handler = new ConsoleHandler(null, false);
+ $this->assertFalse($handler->getBubble(), 'the bubble parameter gets propagated');
+ }
+
+ public function testIsHandling()
+ {
+ $handler = new ConsoleHandler();
+ $this->assertFalse($handler->isHandling(array()), '->isHandling returns false when no output is set');
+ }
+
+ /**
+ * @dataProvider provideVerbosityMappingTests
+ */
+ public function testVerbosityMapping($verbosity, $level, $isHandling, array $map = array())
+ {
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+ $output
+ ->expects($this->atLeastOnce())
+ ->method('getVerbosity')
+ ->will($this->returnValue($verbosity))
+ ;
+ $handler = new ConsoleHandler($output, true, $map);
+ $this->assertSame($isHandling, $handler->isHandling(array('level' => $level)),
+ '->isHandling returns correct value depending on console verbosity and log level'
+ );
+ }
+
+ public function provideVerbosityMappingTests()
+ {
+ return array(
+ array(OutputInterface::VERBOSITY_QUIET, Logger::ERROR, false),
+ array(OutputInterface::VERBOSITY_NORMAL, Logger::WARNING, true),
+ array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, false),
+ array(OutputInterface::VERBOSITY_VERBOSE, Logger::NOTICE, true),
+ array(OutputInterface::VERBOSITY_VERBOSE, Logger::INFO, false),
+ array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::INFO, true),
+ array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::DEBUG, false),
+ array(OutputInterface::VERBOSITY_DEBUG, Logger::DEBUG, true),
+ array(OutputInterface::VERBOSITY_DEBUG, Logger::EMERGENCY, true),
+ array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, true, array(
+ OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE,
+ )),
+ array(OutputInterface::VERBOSITY_DEBUG, Logger::NOTICE, true, array(
+ OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE,
+ )),
+ );
+ }
+
+ public function testVerbosityChanged()
+ {
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+ $output
+ ->expects($this->at(0))
+ ->method('getVerbosity')
+ ->will($this->returnValue(OutputInterface::VERBOSITY_QUIET))
+ ;
+ $output
+ ->expects($this->at(1))
+ ->method('getVerbosity')
+ ->will($this->returnValue(OutputInterface::VERBOSITY_DEBUG))
+ ;
+ $handler = new ConsoleHandler($output);
+ $this->assertFalse($handler->isHandling(array('level' => Logger::NOTICE)),
+ 'when verbosity is set to quiet, the handler does not handle the log'
+ );
+ $this->assertTrue($handler->isHandling(array('level' => Logger::NOTICE)),
+ 'since the verbosity of the output increased externally, the handler is now handling the log'
+ );
+ }
+
+ public function testGetFormatter()
+ {
+ $handler = new ConsoleHandler();
+ $this->assertInstanceOf('Symfony\Bridge\Monolog\Formatter\ConsoleFormatter', $handler->getFormatter(),
+ '-getFormatter returns ConsoleFormatter by default'
+ );
+ }
+
+ public function testWritingAndFormatting()
+ {
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+ $output
+ ->expects($this->any())
+ ->method('getVerbosity')
+ ->will($this->returnValue(OutputInterface::VERBOSITY_DEBUG))
+ ;
+ $output
+ ->expects($this->once())
+ ->method('write')
+ ->with('[2013-05-29 16:21:54] app.INFO: My info message '."\n")
+ ;
+
+ $handler = new ConsoleHandler(null, false);
+ $handler->setOutput($output);
+
+ $infoRecord = array(
+ 'message' => 'My info message',
+ 'context' => array(),
+ 'level' => Logger::INFO,
+ 'level_name' => Logger::getLevelName(Logger::INFO),
+ 'channel' => 'app',
+ 'datetime' => new \DateTime('2013-05-29 16:21:54'),
+ 'extra' => array(),
+ );
+
+ $this->assertTrue($handler->handle($infoRecord), 'The handler finished handling the log as bubble is false.');
+ }
+
+ public function testLogsFromListeners()
+ {
+ $output = new BufferedOutput();
+ $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
+
+ $handler = new ConsoleHandler(null, false);
+
+ $logger = new Logger('app');
+ $logger->pushHandler($handler);
+
+ $dispatcher = new EventDispatcher();
+ $dispatcher->addListener(ConsoleEvents::COMMAND, function () use ($logger) {
+ $logger->addInfo('Before command message.');
+ });
+ $dispatcher->addListener(ConsoleEvents::TERMINATE, function () use ($logger) {
+ $logger->addInfo('Before terminate message.');
+ });
+
+ $dispatcher->addSubscriber($handler);
+
+ $dispatcher->addListener(ConsoleEvents::COMMAND, function () use ($logger) {
+ $logger->addInfo('After command message.');
+ });
+ $dispatcher->addListener(ConsoleEvents::TERMINATE, function () use ($logger) {
+ $logger->addInfo('After terminate message.');
+ });
+
+ $event = new ConsoleCommandEvent(new Command('foo'), $this->getMock('Symfony\Component\Console\Input\InputInterface'), $output);
+ $dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
+ $this->assertContains('Before command message.', $out = $output->fetch());
+ $this->assertContains('After command message.', $out);
+
+ $event = new ConsoleTerminateEvent(new Command('foo'), $this->getMock('Symfony\Component\Console\Input\InputInterface'), $output, 0);
+ $dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
+ $this->assertContains('Before terminate message.', $out = $output->fetch());
+ $this->assertContains('After terminate message.', $out);
+ }
+}
diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php
new file mode 100644
index 000000000000..48bddc99a5ee
--- /dev/null
+++ b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Monolog\Tests\Handler\FingersCrossed;
+
+use Symfony\Bridge\Monolog\Handler\FingersCrossed\NotFoundActivationStrategy;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+use Monolog\Logger;
+
+class NotFoundActivationStrategyTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider isActivatedProvider
+ */
+ public function testIsActivated($url, $record, $expected)
+ {
+ $requestStack = new RequestStack();
+ $requestStack->push(Request::create($url));
+
+ $strategy = new NotFoundActivationStrategy($requestStack, array('^/foo', 'bar'), Logger::WARNING);
+
+ $this->assertEquals($expected, $strategy->isHandlerActivated($record));
+ }
+
+ public function isActivatedProvider()
+ {
+ return array(
+ array('/test', array('level' => Logger::DEBUG), false),
+ array('/foo', array('level' => Logger::DEBUG, 'context' => $this->getContextException(404)), false),
+ array('/baz/bar', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false),
+ array('/foo', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false),
+ array('/foo', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true),
+
+ array('/test', array('level' => Logger::ERROR), true),
+ array('/baz', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), true),
+ array('/baz', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true),
+ );
+ }
+
+ protected function getContextException($code)
+ {
+ return array('exception' => new HttpException($code));
+ }
+}
diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php
index 56a40b1a50c5..143f63b5371b 100644
--- a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php
+++ b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php
@@ -14,7 +14,6 @@
use Monolog\Logger;
use Symfony\Bridge\Monolog\Processor\WebProcessor;
use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
class WebProcessorTest extends \PHPUnit_Framework_TestCase
{
@@ -92,8 +91,8 @@ private function createRequestEvent($additionalServerParameters = array())
->disableOriginalConstructor()
->getMock();
$event->expects($this->any())
- ->method('getRequestType')
- ->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST));
+ ->method('isMasterRequest')
+ ->will($this->returnValue(true));
$event->expects($this->any())
->method('getRequest')
->will($this->returnValue($request));
diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json
index c38fe461fdcf..5bf272afe7c6 100644
--- a/src/Symfony/Bridge/Monolog/composer.json
+++ b/src/Symfony/Bridge/Monolog/composer.json
@@ -16,21 +16,29 @@
}
],
"require": {
- "php": ">=5.3.3",
- "symfony/http-kernel": "~2.2",
- "monolog/monolog": "~1.3"
+ "php": ">=5.3.9",
+ "monolog/monolog": "~1.11",
+ "symfony/http-kernel": "~2.4"
},
+ "require-dev": {
+ "symfony/console": "~2.4",
+ "symfony/event-dispatcher": "~2.2"
+ },
+ "suggest": {
+ "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.",
+ "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings. You need version ~2.3 of the console for it.",
+ "symfony/event-dispatcher": "Needed when using log messages in console commands."
+ },
"autoload": {
- "psr-0": { "Symfony\\Bridge\\Monolog\\": "" },
+ "psr-4": { "Symfony\\Bridge\\Monolog\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
- "target-dir": "Symfony/Bridge/Monolog",
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "2.3-dev"
+ "dev-master": "2.7-dev"
}
}
}
diff --git a/src/Symfony/Bridge/Propel1/.gitignore b/src/Symfony/Bridge/PhpUnit/.gitignore
similarity index 100%
rename from src/Symfony/Bridge/Propel1/.gitignore
rename to src/Symfony/Bridge/PhpUnit/.gitignore
diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
new file mode 100644
index 000000000000..21fbc30ee68d
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
@@ -0,0 +1,172 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit;
+
+/**
+ * Catch deprecation notices and print a summary report at the end of the test suite.
+ *
+ * @author Nicolas Grekas
+ */
+class DeprecationErrorHandler
+{
+ private static $isRegistered = false;
+
+ public static function register($mode = false)
+ {
+ if (self::$isRegistered) {
+ return;
+ }
+
+ $getMode = function () use ($mode) {
+ static $memoizedMode = false;
+
+ if (false !== $memoizedMode) {
+ return $memoizedMode;
+ }
+ if (false === $mode) {
+ $mode = getenv('SYMFONY_DEPRECATIONS_HELPER') ?: '';
+ }
+
+ return $memoizedMode = $mode;
+ };
+
+ $deprecations = array(
+ 'unsilencedCount' => 0,
+ 'remainingCount' => 0,
+ 'legacyCount' => 0,
+ 'otherCount' => 0,
+ 'unsilenced' => array(),
+ 'remaining' => array(),
+ 'legacy' => array(),
+ 'other' => array(),
+ );
+ $deprecationHandler = function ($type, $msg, $file, $line, $context) use (&$deprecations, $getMode) {
+ if (E_USER_DEPRECATED !== $type) {
+ return \PHPUnit_Util_ErrorHandler::handleError($type, $msg, $file, $line, $context);
+ }
+
+ $mode = $getMode();
+ $trace = debug_backtrace(true);
+ $group = 'other';
+
+ $i = count($trace);
+ while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_')))) {
+ // No-op
+ }
+
+ if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) {
+ $class = isset($trace[$i]['object']) ? get_class($trace[$i]['object']) : $trace[$i]['class'];
+ $method = $trace[$i]['function'];
+
+ if (0 !== error_reporting()) {
+ $group = 'unsilenced';
+ } elseif (0 === strpos($method, 'testLegacy')
+ || 0 === strpos($method, 'provideLegacy')
+ || 0 === strpos($method, 'getLegacy')
+ || strpos($class, '\Legacy')
+ || in_array('legacy', \PHPUnit_Util_Test::getGroups($class, $method), true)
+ ) {
+ $group = 'legacy';
+ } else {
+ $group = 'remaining';
+ }
+
+ if ('legacy' !== $group && 'weak' !== $mode) {
+ $ref = &$deprecations[$group][$msg]['count'];
+ ++$ref;
+ $ref = &$deprecations[$group][$msg][$class.'::'.$method];
+ ++$ref;
+ }
+ } elseif ('weak' !== $mode) {
+ $ref = &$deprecations[$group][$msg]['count'];
+ ++$ref;
+ }
+ ++$deprecations[$group.'Count'];
+ };
+ $oldErrorHandler = set_error_handler($deprecationHandler);
+
+ if (null !== $oldErrorHandler) {
+ restore_error_handler();
+ if (array('PHPUnit_Util_ErrorHandler', 'handleError') === $oldErrorHandler) {
+ restore_error_handler();
+ self::register($mode);
+ }
+ } else {
+ self::$isRegistered = true;
+ if (self::hasColorSupport()) {
+ $colorize = function ($str, $red) {
+ $color = $red ? '41;37' : '43;30';
+
+ return "\x1B[{$color}m{$str}\x1B[0m";
+ };
+ } else {
+ $colorize = function ($str) {return $str;};
+ }
+ register_shutdown_function(function () use ($getMode, &$deprecations, $deprecationHandler, $colorize) {
+ $mode = $getMode();
+ $currErrorHandler = set_error_handler('var_dump');
+ restore_error_handler();
+
+ if ('weak' === $mode) {
+ $colorize = function ($str) {return $str;};
+ }
+ if ($currErrorHandler !== $deprecationHandler) {
+ echo "\n", $colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n";
+ }
+
+ $cmp = function ($a, $b) {
+ return $b['count'] - $a['count'];
+ };
+
+ foreach (array('unsilenced', 'remaining', 'legacy', 'other') as $group) {
+ if ($deprecations[$group.'Count']) {
+ echo "\n", $colorize(sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), 'legacy' !== $group), "\n";
+
+ uasort($deprecations[$group], $cmp);
+
+ foreach ($deprecations[$group] as $msg => $notices) {
+ echo "\n", rtrim($msg, '.'), ': ', $notices['count'], "x\n";
+
+ arsort($notices);
+
+ foreach ($notices as $method => $count) {
+ if ('count' !== $method) {
+ echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n";
+ }
+ }
+ }
+ }
+ }
+ if (!empty($notices)) {
+ echo "\n";
+ }
+
+ if ('weak' !== $mode && ($deprecations['unsilenced'] || $deprecations['remaining'] || $deprecations['other'])) {
+ exit(1);
+ }
+ });
+ }
+ }
+
+ private static function hasColorSupport()
+ {
+ if ('\\' === DIRECTORY_SEPARATOR) {
+ return
+ 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD)
+ || false !== getenv('ANSICON')
+ || 'ON' === getenv('ConEmuANSI')
+ || 'xterm' === getenv('TERM');
+ }
+
+ return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT);
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/LICENSE b/src/Symfony/Bridge/PhpUnit/LICENSE
new file mode 100644
index 000000000000..39fa189d2b5f
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014-2016 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/Symfony/Bridge/PhpUnit/README.md b/src/Symfony/Bridge/PhpUnit/README.md
new file mode 100644
index 000000000000..8c4e6e59ccb4
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/README.md
@@ -0,0 +1,13 @@
+PHPUnit Bridge
+==============
+
+Provides utilities for PHPUnit, especially user deprecation notices management.
+
+Resources
+---------
+
+ * [Documentation](https://symfony.com/doc/current/components/phpunit_bridge.html)
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+ * [Report issues](https://github.com/symfony/symfony/issues) and
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
+ in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt
new file mode 100644
index 000000000000..fac5c53ae748
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt
@@ -0,0 +1,69 @@
+--TEST--
+Test DeprecationErrorHandler in default mode
+--FILE--
+testLegacyFoo();
+$foo->testNonLegacyBar();
+
+?>
+--EXPECTF--
+Unsilenced deprecation notices (3)
+
+unsilenced foo deprecation: 2x
+ 2x in FooTestCase::testLegacyFoo
+
+unsilenced bar deprecation: 1x
+ 1x in FooTestCase::testNonLegacyBar
+
+Remaining deprecation notices (1)
+
+silenced bar deprecation: 1x
+ 1x in FooTestCase::testNonLegacyBar
+
+Legacy deprecation notices (1)
+
+Other deprecation notices (1)
+
+root deprecation: 1x
+
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak.phpt
new file mode 100644
index 000000000000..9e78d96e70ef
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Test DeprecationErrorHandler in weak mode
+--FILE--
+testLegacyFoo();
+
+?>
+--EXPECTF--
+Unsilenced deprecation notices (1)
+
+Legacy deprecation notices (1)
+
+Other deprecation notices (1)
+
diff --git a/src/Symfony/Bridge/PhpUnit/TextUI/Command.php b/src/Symfony/Bridge/PhpUnit/TextUI/Command.php
new file mode 100644
index 000000000000..5243044c6b92
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/TextUI/Command.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\TextUI;
+
+/**
+ * {@inheritdoc}
+ */
+class Command extends \PHPUnit_TextUI_Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function handleBootstrap($filename)
+ {
+ parent::handleBootstrap($filename);
+
+ // By default, we want PHPUnit's autoloader before Symfony's one
+ if (!getenv('SYMFONY_PHPUNIT_OVERLOAD')) {
+ $filename = realpath(stream_resolve_include_path($filename));
+ $symfonyLoader = realpath(dirname(PHPUNIT_COMPOSER_INSTALL).'/../../../vendor/autoload.php');
+
+ if ($filename === $symfonyLoader) {
+ $symfonyLoader = require $symfonyLoader;
+ $symfonyLoader->unregister();
+ $symfonyLoader->register(false);
+ }
+ }
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/bootstrap.php b/src/Symfony/Bridge/PhpUnit/bootstrap.php
new file mode 100644
index 000000000000..bb98a9296162
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/bootstrap.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Doctrine\Common\Annotations\AnnotationRegistry;
+use Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
+
+// Detect if we're loaded by an actual run of phpunit
+if (!defined('PHPUNIT_COMPOSER_INSTALL') && !class_exists('PHPUnit_TextUI_Command', false)) {
+ return;
+}
+
+// Enforce a consistent locale
+setlocale(LC_ALL, 'C');
+
+if (class_exists('Doctrine\Common\Annotations\AnnotationRegistry')) {
+ AnnotationRegistry::registerLoader('class_exists');
+}
+
+DeprecationErrorHandler::register(getenv('SYMFONY_DEPRECATIONS_HELPER'));
diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json
new file mode 100644
index 000000000000..39eb9ef2180f
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/composer.json
@@ -0,0 +1,39 @@
+{
+ "name": "symfony/phpunit-bridge",
+ "type": "symfony-bridge",
+ "description": "Symfony PHPUnit Bridge",
+ "keywords": [],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.3 EVEN ON LATEST SYMFONY VERSIONS TO ALLOW USING",
+ "php": "THIS BRIDGE WHEN TESTING LOWEST SYMFONY VERSIONS.",
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader"
+ },
+ "autoload": {
+ "files": [ "bootstrap.php" ],
+ "psr-4": { "Symfony\\Bridge\\PhpUnit\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist b/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist
new file mode 100644
index 000000000000..9b64b02947c0
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+ ./Tests/
+ ./Tests/DeprecationErrorHandler/
+
+
+
+
+
+ ./
+
+ ./Tests
+ ./vendor
+
+
+
+
diff --git a/src/Symfony/Bridge/Propel1/CHANGELOG.md b/src/Symfony/Bridge/Propel1/CHANGELOG.md
deleted file mode 100644
index 242d576c33b7..000000000000
--- a/src/Symfony/Bridge/Propel1/CHANGELOG.md
+++ /dev/null
@@ -1,15 +0,0 @@
-CHANGELOG
-=========
-
-2.2.0
------
-
- * added a collection type for the I18n behavior
- * added an optional PropertyAccessorInterface parameter to ModelType and
- ModelChoiceList
- * [BC BREAK] ModelType now has a constructor
-
-2.1.0
------
-
- * added the bridge
diff --git a/src/Symfony/Bridge/Propel1/DataCollector/PropelDataCollector.php b/src/Symfony/Bridge/Propel1/DataCollector/PropelDataCollector.php
deleted file mode 100644
index bc91564a5717..000000000000
--- a/src/Symfony/Bridge/Propel1/DataCollector/PropelDataCollector.php
+++ /dev/null
@@ -1,147 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\DataCollector;
-
-use Symfony\Bridge\Propel1\Logger\PropelLogger;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpKernel\DataCollector\DataCollector;
-
-/**
- * The PropelDataCollector collector class collects information.
- *
- * @author William Durand
- */
-class PropelDataCollector extends DataCollector
-{
- /**
- * Propel logger.
- *
- * @var PropelLogger
- */
- private $logger;
-
- /**
- * Propel configuration.
- *
- * @var \PropelConfiguration
- */
- protected $propelConfiguration;
-
- /**
- * Constructor.
- *
- * @param PropelLogger $logger A Propel logger.
- * @param \PropelConfiguration $propelConfiguration The Propel configuration object.
- */
- public function __construct(PropelLogger $logger, \PropelConfiguration $propelConfiguration)
- {
- $this->logger = $logger;
- $this->propelConfiguration = $propelConfiguration;
- }
-
- /**
- * {@inheritdoc}
- */
- public function collect(Request $request, Response $response, \Exception $exception = null)
- {
- $this->data = array(
- 'queries' => $this->buildQueries(),
- 'querycount' => $this->countQueries(),
- );
- }
-
- /**
- * Returns the collector name.
- *
- * @return string
- */
- public function getName()
- {
- return 'propel';
- }
-
- /**
- * Returns the collected stats for all executed SQL queries.
- *
- * @return array
- */
- public function getQueries()
- {
- return $this->data['queries'];
- }
-
- /**
- * Returns the query count.
- *
- * @return int
- */
- public function getQueryCount()
- {
- return $this->data['querycount'];
- }
-
- /**
- * Returns the total time spent on running all queries.
- *
- * @return float
- */
- public function getTime()
- {
- $time = 0;
- foreach ($this->data['queries'] as $query) {
- $time += (float) $query['time'];
- }
-
- return $time;
- }
-
- /**
- * Computes the stats of all executed SQL queries.
- *
- * @return array
- */
- private function buildQueries()
- {
- $queries = array();
-
- $outerGlue = $this->propelConfiguration->getParameter('debugpdo.logging.outerglue', ' | ');
- $innerGlue = $this->propelConfiguration->getParameter('debugpdo.logging.innerglue', ': ');
-
- foreach ($this->logger->getQueries() as $q) {
- $parts = explode($outerGlue, $q, 4);
-
- $times = explode($innerGlue, $parts[0]);
- $con = explode($innerGlue, $parts[2]);
- $memories = explode($innerGlue, $parts[1]);
-
- $sql = trim($parts[3]);
- $con = trim($con[1]);
- $time = trim($times[1]);
- $memory = trim($memories[1]);
-
- $queries[] = array('connection' => $con, 'sql' => $sql, 'time' => $time, 'memory' => $memory);
- }
-
- return $queries;
- }
-
- /**
- * Returns the total count of SQL queries.
- *
- * @return int
- */
- private function countQueries()
- {
- return count($this->logger->getQueries());
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/DependencyInjection/Security/UserProvider/PropelFactory.php b/src/Symfony/Bridge/Propel1/DependencyInjection/Security/UserProvider/PropelFactory.php
deleted file mode 100644
index eb8e2e4a7295..000000000000
--- a/src/Symfony/Bridge/Propel1/DependencyInjection/Security/UserProvider/PropelFactory.php
+++ /dev/null
@@ -1,69 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\DependencyInjection\Security\UserProvider;
-
-use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
-use Symfony\Component\Config\Definition\Builder\NodeDefinition;
-use Symfony\Component\DependencyInjection\DefinitionDecorator;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-
-/**
- * PropelFactory creates services for Propel user provider.
- *
- * @author William Durand
- */
-class PropelFactory implements UserProviderFactoryInterface
-{
- private $key;
- private $providerId;
-
- /**
- * Constructor.
- *
- * @param string $key
- * @param string $providerId
- */
- public function __construct($key, $providerId)
- {
- $this->key = $key;
- $this->providerId = $providerId;
- }
-
- public function create(ContainerBuilder $container, $id, $config)
- {
- $container
- ->setDefinition($id, new DefinitionDecorator($this->providerId))
- ->addArgument($config['class'])
- ->addArgument($config['property'])
- ;
- }
-
- public function getKey()
- {
- return $this->key;
- }
-
- public function addConfiguration(NodeDefinition $node)
- {
- $node
- ->children()
- ->scalarNode('class')
- ->isRequired()
- ->cannotBeEmpty()
- ->end()
- ->scalarNode('property')
- ->defaultNull()
- ->end()
- ->end()
- ;
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Form/ChoiceList/ModelChoiceList.php b/src/Symfony/Bridge/Propel1/Form/ChoiceList/ModelChoiceList.php
deleted file mode 100644
index cc4d76cf6482..000000000000
--- a/src/Symfony/Bridge/Propel1/Form/ChoiceList/ModelChoiceList.php
+++ /dev/null
@@ -1,490 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Form\ChoiceList;
-
-use Symfony\Bridge\Propel1\Form\Type\ModelType;
-use Symfony\Component\Form\Exception\StringCastException;
-use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList;
-use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer;
-use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer;
-use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
-use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
-use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
-
-/**
- * A choice list for object choices based on Propel model.
- *
- * @author William Durand
- * @author Toni Uebernickel
- */
-class ModelChoiceList extends ObjectChoiceList
-{
- /**
- * The fields of which the identifier of the underlying class consists.
- *
- * This property should only be accessed through identifier.
- *
- * @var array
- */
- protected $identifier = array();
-
- /**
- * The query to retrieve the choices of this list.
- *
- * @var \ModelCriteria
- */
- protected $query;
-
- /**
- * The query to retrieve the preferred choices for this list.
- *
- * @var \ModelCriteria
- */
- protected $preferredQuery;
-
- /**
- * Whether the model objects have already been loaded.
- *
- * @var bool
- */
- protected $loaded = false;
-
- /**
- * Whether to use the identifier for index generation.
- *
- * @var bool
- */
- private $identifierAsIndex = false;
-
- /**
- * Constructor.
- *
- * @see ModelType How to use the preferred choices.
- *
- * @param string $class The FQCN of the model class to be loaded.
- * @param string $labelPath A property path pointing to the property used for the choice labels.
- * @param array $choices An optional array to use, rather than fetching the models.
- * @param \ModelCriteria $queryObject The query to use retrieving model data from database.
- * @param string $groupPath A property path pointing to the property used to group the choices.
- * @param array|\ModelCriteria $preferred The preferred items of this choice.
- * Either an array if $choices is given,
- * or a \ModelCriteria to be merged with the $queryObject.
- * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths.
- *
- * @throws MissingOptionsException when no model class is given
- * @throws InvalidOptionsException when the model class cannot be found
- */
- public function __construct($class, $labelPath = null, $choices = null, $queryObject = null, $groupPath = null, $preferred = array(), PropertyAccessorInterface $propertyAccessor = null)
- {
- $this->class = $class;
-
- $queryClass = $this->class.'Query';
- if (!class_exists($queryClass)) {
- if (empty($this->class)) {
- throw new MissingOptionsException('The "class" parameter is empty, you should provide the model class');
- }
- throw new InvalidOptionsException(sprintf('The query class "%s" is not found, you should provide the FQCN of the model class', $queryClass));
- }
-
- $query = new $queryClass();
-
- $this->query = $queryObject ?: $query;
- $this->identifier = $this->query->getTableMap()->getPrimaryKeys();
- $this->loaded = is_array($choices) || $choices instanceof \Traversable;
-
- if ($preferred instanceof \ModelCriteria) {
- $this->preferredQuery = $preferred->mergeWith($this->query);
- }
-
- if (!$this->loaded) {
- // Make sure the constraints of the parent constructor are
- // fulfilled
- $choices = array();
- $preferred = array();
- }
-
- if (1 === count($this->identifier) && $this->isScalar(current($this->identifier))) {
- $this->identifierAsIndex = true;
- }
-
- parent::__construct($choices, $labelPath, $preferred, $groupPath, null, $propertyAccessor);
- }
-
- /**
- * Returns the class name of the model.
- *
- * @return string
- */
- public function getClass()
- {
- return $this->class;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getChoices()
- {
- $this->load();
-
- return parent::getChoices();
- }
-
- /**
- * {@inheritdoc}
- */
- public function getValues()
- {
- $this->load();
-
- return parent::getValues();
- }
-
- /**
- * {@inheritdoc}
- */
- public function getPreferredViews()
- {
- $this->load();
-
- return parent::getPreferredViews();
- }
-
- /**
- * {@inheritdoc}
- */
- public function getRemainingViews()
- {
- $this->load();
-
- return parent::getRemainingViews();
- }
-
- /**
- * {@inheritdoc}
- */
- public function getChoicesForValues(array $values)
- {
- if (empty($values)) {
- return array();
- }
-
- /*
- * This performance optimization reflects a common scenario:
- * * A simple select of a model entry.
- * * The choice option "expanded" is set to false.
- * * The current request is the submission of the selected value.
- *
- * @see ChoicesToValuesTransformer::reverseTransform()
- * @see ChoiceToValueTransformer::reverseTransform()
- */
- if (!$this->loaded) {
- if (1 === count($this->identifier)) {
- $filterBy = 'filterBy'.current($this->identifier)->getPhpName();
-
- // The initial query is cloned, so all additional filters are applied correctly.
- $query = clone $this->query;
- $result = (array) $query
- ->$filterBy($values)
- ->find();
-
- // Preserve the keys as provided by the values.
- $models = array();
- foreach ($values as $index => $value) {
- foreach ($result as $eachModel) {
- if ($value === $this->createValue($eachModel)) {
- // Make sure to convert to the right format
- $models[$index] = $this->fixChoice($eachModel);
-
- // If all values have been assigned, skip further loops.
- unset($values[$index]);
- if (0 === count($values)) {
- break 2;
- }
- }
- }
- }
-
- return $models;
- }
- }
-
- $this->load();
-
- return parent::getChoicesForValues($values);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getValuesForChoices(array $models)
- {
- if (empty($models)) {
- return array();
- }
-
- if (!$this->loaded) {
- /*
- * This performance optimization assumes the validation of the respective values will be done by other means.
- *
- * It correlates with the performance optimization in {@link ModelChoiceList::getChoicesForValues()}
- * as it won't load the actual entries from the database.
- *
- * @see ChoicesToValuesTransformer::transform()
- * @see ChoiceToValueTransformer::transform()
- */
- if (1 === count($this->identifier)) {
- $values = array();
- foreach ($models as $index => $model) {
- if ($model instanceof $this->class) {
- // Make sure to convert to the right format
- $values[$index] = $this->fixValue(current($this->getIdentifierValues($model)));
- }
- }
-
- return $values;
- }
- }
-
- $this->load();
-
- $values = array();
- $availableValues = $this->getValues();
-
- /*
- * Overwriting default implementation.
- *
- * The two objects may represent the same entry in the database,
- * but if they originated from different queries, there are not the same object within the code.
- *
- * This happens when using m:n relations with either sides model as data_class of the form.
- * The choicelist will retrieve the list of available related models with a different query, resulting in different objects.
- */
- $choices = $this->fixChoices($models);
- foreach ($choices as $i => $givenChoice) {
- if (null === $givenChoice) {
- continue;
- }
-
- foreach ($this->getChoices() as $j => $choice) {
- if ($this->isEqual($choice, $givenChoice)) {
- $values[$i] = $availableValues[$j];
-
- // If all choices have been assigned, skip further loops.
- unset($choices[$i]);
- if (0 === count($choices)) {
- break 2;
- }
- }
- }
- }
-
- return $values;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getIndicesForChoices(array $models)
- {
- if (empty($models)) {
- return array();
- }
-
- $this->load();
-
- $indices = array();
-
- /*
- * Overwriting default implementation.
- *
- * The two objects may represent the same entry in the database,
- * but if they originated from different queries, there are not the same object within the code.
- *
- * This happens when using m:n relations with either sides model as data_class of the form.
- * The choicelist will retrieve the list of available related models with a different query, resulting in different objects.
- */
- $choices = $this->fixChoices($models);
- foreach ($choices as $i => $givenChoice) {
- if (null === $givenChoice) {
- continue;
- }
-
- foreach ($this->getChoices() as $j => $choice) {
- if ($this->isEqual($choice, $givenChoice)) {
- $indices[$i] = $j;
-
- // If all choices have been assigned, skip further loops.
- unset($choices[$i]);
- if (0 === count($choices)) {
- break 2;
- }
- }
- }
- }
-
- return $indices;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getIndicesForValues(array $values)
- {
- if (empty($values)) {
- return array();
- }
-
- $this->load();
-
- return parent::getIndicesForValues($values);
- }
-
- /**
- * Creates a new unique index for this model.
- *
- * If the model has a single-field identifier, this identifier is used.
- *
- * Otherwise a new integer is generated.
- *
- * @param mixed $model The choice to create an index for
- *
- * @return int|string A unique index containing only ASCII letters,
- * digits and underscores.
- */
- protected function createIndex($model)
- {
- if ($this->identifierAsIndex) {
- return current($this->getIdentifierValues($model));
- }
-
- return parent::createIndex($model);
- }
-
- /**
- * Creates a new unique value for this model.
- *
- * If the model has a single-field identifier, this identifier is used.
- *
- * Otherwise a new integer is generated.
- *
- * @param mixed $model The choice to create a value for
- *
- * @return int|string A unique value without character limitations.
- */
- protected function createValue($model)
- {
- if ($this->identifierAsIndex) {
- return (string) current($this->getIdentifierValues($model));
- }
-
- return parent::createValue($model);
- }
-
- /**
- * Loads the complete choice list entries, once.
- *
- * If data has been loaded the choice list is initialized with the retrieved data.
- */
- private function load()
- {
- if ($this->loaded) {
- return;
- }
-
- $models = (array) $this->query->find();
-
- $preferred = array();
- if ($this->preferredQuery instanceof \ModelCriteria) {
- $preferred = (array) $this->preferredQuery->find();
- }
-
- try {
- // The second parameter $labels is ignored by ObjectChoiceList
- parent::initialize($models, array(), $preferred);
-
- $this->loaded = true;
- } catch (StringCastException $e) {
- throw new StringCastException(str_replace('argument $labelPath', 'option "property"', $e->getMessage()), null, $e);
- }
- }
-
- /**
- * Returns the values of the identifier fields of a model.
- *
- * Propel must know about this model, that is, the model must already
- * be persisted or added to the idmodel map before. Otherwise an
- * exception is thrown.
- *
- * @param object $model The model for which to get the identifier
- *
- * @return array
- */
- private function getIdentifierValues($model)
- {
- if (!$model instanceof $this->class) {
- return array();
- }
-
- if ($model instanceof \Persistent) {
- return array($model->getPrimaryKey());
- }
-
- // readonly="true" models do not implement \Persistent.
- if ($model instanceof \BaseObject && method_exists($model, 'getPrimaryKey')) {
- return array($model->getPrimaryKey());
- }
-
- if (!method_exists($model, 'getPrimaryKeys')) {
- return array();
- }
-
- return $model->getPrimaryKeys();
- }
-
- /**
- * Whether this column contains scalar values (to be used as indices).
- *
- * @param \ColumnMap $column
- *
- * @return bool
- */
- private function isScalar(\ColumnMap $column)
- {
- return in_array($column->getPdoType(), array(
- \PDO::PARAM_BOOL,
- \PDO::PARAM_INT,
- \PDO::PARAM_STR,
- ));
- }
-
- /**
- * Check the given choices for equality.
- *
- * @param mixed $choice
- * @param mixed $givenChoice
- *
- * @return bool
- */
- private function isEqual($choice, $givenChoice)
- {
- if ($choice === $givenChoice) {
- return true;
- }
-
- if ($this->getIdentifierValues($choice) === $this->getIdentifierValues($givenChoice)) {
- return true;
- }
-
- return false;
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Form/DataTransformer/CollectionToArrayTransformer.php b/src/Symfony/Bridge/Propel1/Form/DataTransformer/CollectionToArrayTransformer.php
deleted file mode 100644
index 09ff457aee9b..000000000000
--- a/src/Symfony/Bridge/Propel1/Form/DataTransformer/CollectionToArrayTransformer.php
+++ /dev/null
@@ -1,55 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Form\DataTransformer;
-
-use PropelObjectCollection;
-use Symfony\Component\Form\DataTransformerInterface;
-use Symfony\Component\Form\Exception\TransformationFailedException;
-
-/**
- * CollectionToArrayTransformer class.
- *
- * @author William Durand
- * @author Pierre-Yves Lebecq
- */
-class CollectionToArrayTransformer implements DataTransformerInterface
-{
- public function transform($collection)
- {
- if (null === $collection) {
- return array();
- }
-
- if (!$collection instanceof PropelObjectCollection) {
- throw new TransformationFailedException('Expected a \PropelObjectCollection.');
- }
-
- return $collection->getData();
- }
-
- public function reverseTransform($array)
- {
- $collection = new PropelObjectCollection();
-
- if ('' === $array || null === $array) {
- return $collection;
- }
-
- if (!is_array($array)) {
- throw new TransformationFailedException('Expected an array.');
- }
-
- $collection->setData($array);
-
- return $collection;
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationCollectionFormListener.php b/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationCollectionFormListener.php
deleted file mode 100644
index 1652dfa73ff3..000000000000
--- a/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationCollectionFormListener.php
+++ /dev/null
@@ -1,103 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Form\EventListener;
-
-use Symfony\Component\Form\FormEvents;
-use Symfony\Component\Form\Exception\UnexpectedTypeException;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\Form\FormEvent;
-
-/**
- * listener class for propel1_translatable_collection.
- *
- * @author Patrick Kaufmann
- */
-class TranslationCollectionFormListener implements EventSubscriberInterface
-{
- private $i18nClass;
- private $languages;
-
- public function __construct($languages, $i18nClass)
- {
- $this->i18nClass = $i18nClass;
- $this->languages = $languages;
- }
-
- public static function getSubscribedEvents()
- {
- return array(
- FormEvents::PRE_SET_DATA => array('preSetData', 1),
- );
- }
-
- public function preSetData(FormEvent $event)
- {
- $form = $event->getForm();
- $data = $event->getData();
-
- if (null === $data) {
- return;
- }
-
- if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) {
- throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
- }
-
- //get the class name of the i18nClass
- $temp = explode('\\', $this->i18nClass);
- $dataClass = end($temp);
-
- $rootData = $form->getRoot()->getData();
- $foundData = false;
-
- $addFunction = 'add'.$dataClass;
-
- //add a database row for every needed language
- foreach ($this->languages as $lang) {
- $found = false;
-
- foreach ($data as $i18n) {
- if (!method_exists($i18n, 'getLocale')) {
- throw new UnexpectedTypeException($i18n, 'Propel i18n object');
- }
-
- if ($i18n->getLocale() == $lang) {
- $found = true;
- break;
- }
- }
-
- if (!$found) {
- $currentForm = $form;
- while (!$foundData) {
- if (method_exists($rootData, $addFunction)) {
- $foundData = true;
- break;
- } elseif ($currentForm->hasParent()) {
- $currentForm = $currentForm->getParent();
- $rootData = $currentForm->getData();
- } else {
- break;
- }
- }
- if (!$foundData) {
- throw new UnexpectedTypeException($rootData, 'Propel i18n object');
- }
-
- $newTranslation = new $this->i18nClass();
- $newTranslation->setLocale($lang);
-
- $rootData->$addFunction($newTranslation);
- }
- }
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationFormListener.php b/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationFormListener.php
deleted file mode 100644
index 0bc8734249ed..000000000000
--- a/src/Symfony/Bridge/Propel1/Form/EventListener/TranslationFormListener.php
+++ /dev/null
@@ -1,82 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Form\EventListener;
-
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\Form\FormEvent;
-use Symfony\Component\Form\FormEvents;
-
-/**
- * Event Listener class for propel1_translation.
- *
- * @author Patrick Kaufmann
- */
-class TranslationFormListener implements EventSubscriberInterface
-{
- private $columns;
- private $dataClass;
-
- public function __construct($columns, $dataClass)
- {
- $this->columns = $columns;
- $this->dataClass = $dataClass;
- }
-
- public static function getSubscribedEvents()
- {
- return array(
- FormEvents::PRE_SET_DATA => array('preSetData', 1),
- );
- }
-
- public function preSetData(FormEvent $event)
- {
- $form = $event->getForm();
- $data = $event->getData();
-
- if (!$data instanceof $this->dataClass) {
- return;
- }
-
- //loop over all columns and add the input
- foreach ($this->columns as $column => $options) {
- if (is_string($options)) {
- $column = $options;
- $options = array();
- }
- if (null === $options) {
- $options = array();
- }
-
- $type = 'text';
- if (array_key_exists('type', $options)) {
- $type = $options['type'];
- }
- $label = $column;
- if (array_key_exists('label', $options)) {
- $label = $options['label'];
- }
-
- $customOptions = array();
- if (array_key_exists('options', $options)) {
- $customOptions = $options['options'];
- }
- $options = array(
- 'label' => $label.' '.strtoupper($data->getLocale()),
- );
-
- $options = array_merge($options, $customOptions);
-
- $form->add($column, $type, $options);
- }
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Form/PropelExtension.php b/src/Symfony/Bridge/Propel1/Form/PropelExtension.php
deleted file mode 100644
index d333f7990378..000000000000
--- a/src/Symfony/Bridge/Propel1/Form/PropelExtension.php
+++ /dev/null
@@ -1,37 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Form;
-
-use Symfony\Component\Form\AbstractExtension;
-use Symfony\Component\PropertyAccess\PropertyAccess;
-
-/**
- * Represents the Propel form extension, which loads the Propel functionality.
- *
- * @author Joseph Rouff
- */
-class PropelExtension extends AbstractExtension
-{
- protected function loadTypes()
- {
- return array(
- new Type\ModelType(PropertyAccess::createPropertyAccessor()),
- new Type\TranslationCollectionType(),
- new Type\TranslationType(),
- );
- }
-
- protected function loadTypeGuesser()
- {
- return new PropelTypeGuesser();
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.php b/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.php
deleted file mode 100644
index 3db34b53e3f7..000000000000
--- a/src/Symfony/Bridge/Propel1/Form/PropelTypeGuesser.php
+++ /dev/null
@@ -1,180 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Form;
-
-use Symfony\Component\Form\FormTypeGuesserInterface;
-use Symfony\Component\Form\Guess\Guess;
-use Symfony\Component\Form\Guess\TypeGuess;
-use Symfony\Component\Form\Guess\ValueGuess;
-
-/**
- * Propel Type guesser.
- *
- * @author Fabien Potencier
- */
-class PropelTypeGuesser implements FormTypeGuesserInterface
-{
- private $cache = array();
-
- /**
- * {@inheritdoc}
- */
- public function guessType($class, $property)
- {
- if (!$table = $this->getTable($class)) {
- return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
- }
-
- foreach ($table->getRelations() as $relation) {
- if ($relation->getType() === \RelationMap::MANY_TO_ONE) {
- if (strtolower($property) === strtolower($relation->getName())) {
- return new TypeGuess('model', array(
- 'class' => $relation->getForeignTable()->getClassName(),
- 'multiple' => false,
- ), Guess::HIGH_CONFIDENCE);
- }
- } elseif ($relation->getType() === \RelationMap::ONE_TO_MANY) {
- if (strtolower($property) === strtolower($relation->getPluralName())) {
- return new TypeGuess('model', array(
- 'class' => $relation->getForeignTable()->getClassName(),
- 'multiple' => true,
- ), Guess::HIGH_CONFIDENCE);
- }
- } elseif ($relation->getType() === \RelationMap::MANY_TO_MANY) {
- if (strtolower($property) == strtolower($relation->getPluralName())) {
- return new TypeGuess('model', array(
- 'class' => $relation->getLocalTable()->getClassName(),
- 'multiple' => true,
- ), Guess::HIGH_CONFIDENCE);
- }
- }
- }
-
- if (!$column = $this->getColumn($class, $property)) {
- return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
- }
-
- switch ($column->getType()) {
- case \PropelColumnTypes::BOOLEAN:
- case \PropelColumnTypes::BOOLEAN_EMU:
- return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE);
- case \PropelColumnTypes::TIMESTAMP:
- case \PropelColumnTypes::BU_TIMESTAMP:
- return new TypeGuess('datetime', array(), Guess::HIGH_CONFIDENCE);
- case \PropelColumnTypes::DATE:
- case \PropelColumnTypes::BU_DATE:
- return new TypeGuess('date', array(), Guess::HIGH_CONFIDENCE);
- case \PropelColumnTypes::TIME:
- return new TypeGuess('time', array(), Guess::HIGH_CONFIDENCE);
- case \PropelColumnTypes::FLOAT:
- case \PropelColumnTypes::REAL:
- case \PropelColumnTypes::DOUBLE:
- case \PropelColumnTypes::DECIMAL:
- return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE);
- case \PropelColumnTypes::TINYINT:
- case \PropelColumnTypes::SMALLINT:
- case \PropelColumnTypes::INTEGER:
- case \PropelColumnTypes::BIGINT:
- case \PropelColumnTypes::NUMERIC:
- return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE);
- case \PropelColumnTypes::ENUM:
- case \PropelColumnTypes::CHAR:
- if ($column->getValueSet()) {
- //check if this is mysql enum
- $choices = $column->getValueSet();
- $labels = array_map('ucfirst', $choices);
-
- return new TypeGuess('choice', array('choices' => array_combine($choices, $labels)), Guess::MEDIUM_CONFIDENCE);
- }
- case \PropelColumnTypes::VARCHAR:
- return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE);
- case \PropelColumnTypes::LONGVARCHAR:
- case \PropelColumnTypes::BLOB:
- case \PropelColumnTypes::CLOB:
- case \PropelColumnTypes::CLOB_EMU:
- return new TypeGuess('textarea', array(), Guess::MEDIUM_CONFIDENCE);
- default:
- return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function guessRequired($class, $property)
- {
- if ($column = $this->getColumn($class, $property)) {
- return new ValueGuess($column->isNotNull(), Guess::HIGH_CONFIDENCE);
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function guessMaxLength($class, $property)
- {
- if ($column = $this->getColumn($class, $property)) {
- if ($column->isText()) {
- return new ValueGuess($column->getSize(), Guess::HIGH_CONFIDENCE);
- }
- switch ($column->getType()) {
- case \PropelColumnTypes::FLOAT:
- case \PropelColumnTypes::REAL:
- case \PropelColumnTypes::DOUBLE:
- case \PropelColumnTypes::DECIMAL:
- return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE);
- }
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function guessPattern($class, $property)
- {
- if ($column = $this->getColumn($class, $property)) {
- switch ($column->getType()) {
- case \PropelColumnTypes::FLOAT:
- case \PropelColumnTypes::REAL:
- case \PropelColumnTypes::DOUBLE:
- case \PropelColumnTypes::DECIMAL:
- return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE);
- }
- }
- }
-
- protected function getTable($class)
- {
- if (isset($this->cache[$class])) {
- return $this->cache[$class];
- }
-
- if (class_exists($queryClass = $class.'Query')) {
- $query = new $queryClass();
-
- return $this->cache[$class] = $query->getTableMap();
- }
- }
-
- protected function getColumn($class, $property)
- {
- if (isset($this->cache[$class.'::'.$property])) {
- return $this->cache[$class.'::'.$property];
- }
-
- $table = $this->getTable($class);
-
- if ($table && $table->hasColumn($property)) {
- return $this->cache[$class.'::'.$property] = $table->getColumn($property);
- }
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Form/Type/ModelType.php b/src/Symfony/Bridge/Propel1/Form/Type/ModelType.php
deleted file mode 100644
index aa500928a1b7..000000000000
--- a/src/Symfony/Bridge/Propel1/Form/Type/ModelType.php
+++ /dev/null
@@ -1,126 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Form\Type;
-
-use Symfony\Bridge\Propel1\Form\ChoiceList\ModelChoiceList;
-use Symfony\Bridge\Propel1\Form\DataTransformer\CollectionToArrayTransformer;
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\OptionsResolver\Options;
-use Symfony\Component\OptionsResolver\OptionsResolverInterface;
-use Symfony\Component\PropertyAccess\PropertyAccess;
-use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
-
-/**
- * ModelType class.
- *
- * @author William Durand
- * @author Toni Uebernickel
- *
- * Example using the preferred_choices option.
- *
- *
- * public function buildForm(FormBuilderInterface $builder, array $options)
- * {
- * $builder
- * ->add('product', 'model', array(
- * 'class' => 'Model\Product',
- * 'query' => ProductQuery::create()
- * ->filterIsActive(true)
- * ->useI18nQuery($options['locale'])
- * ->orderByName()
- * ->endUse()
- * ,
- * 'preferred_choices' => ProductQuery::create()
- * ->filterByIsTopProduct(true)
- * ,
- * ))
- * ;
- * }
- *
- */
-class ModelType extends AbstractType
-{
- /**
- * @var PropertyAccessorInterface
- */
- private $propertyAccessor;
-
- /**
- * Constructor.
- *
- * @param PropertyAccessorInterface|null $propertyAccessor
- */
- public function __construct(PropertyAccessorInterface $propertyAccessor = null)
- {
- $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
- }
-
- /**
- * {@inheritdoc}
- */
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- if ($options['multiple']) {
- $builder->addViewTransformer(new CollectionToArrayTransformer(), true);
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function setDefaultOptions(OptionsResolverInterface $resolver)
- {
- $propertyAccessor = $this->propertyAccessor;
-
- $choiceList = function (Options $options) use ($propertyAccessor) {
- return new ModelChoiceList(
- $options['class'],
- $options['property'],
- $options['choices'],
- $options['query'],
- $options['group_by'],
- $options['preferred_choices'],
- $propertyAccessor
- );
- };
-
- $resolver->setDefaults(array(
- 'template' => 'choice',
- 'multiple' => false,
- 'expanded' => false,
- 'class' => null,
- 'property' => null,
- 'query' => null,
- 'choices' => null,
- 'choice_list' => $choiceList,
- 'group_by' => null,
- 'by_reference' => false,
- ));
- }
-
- /**
- * {@inheritdoc}
- */
- public function getParent()
- {
- return 'choice';
- }
-
- /**
- * {@inheritdoc}
- */
- public function getName()
- {
- return 'model';
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Form/Type/TranslationCollectionType.php b/src/Symfony/Bridge/Propel1/Form/Type/TranslationCollectionType.php
deleted file mode 100644
index 8aaa120b55ad..000000000000
--- a/src/Symfony/Bridge/Propel1/Form/Type/TranslationCollectionType.php
+++ /dev/null
@@ -1,78 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Form\Type;
-
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
-use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\OptionsResolver\OptionsResolverInterface;
-use Symfony\Bridge\Propel1\Form\EventListener\TranslationCollectionFormListener;
-
-/**
- * form type for i18n-columns in propel.
- *
- * @author Patrick Kaufmann
- */
-class TranslationCollectionType extends AbstractType
-{
- /**
- * {@inheritdoc}
- */
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- if (!isset($options['options']['data_class']) || null === $options['options']['data_class']) {
- throw new MissingOptionsException('data_class must be set');
- }
- if (!isset($options['options']['columns']) || null === $options['options']['columns']) {
- throw new MissingOptionsException('columns must be set');
- }
-
- $listener = new TranslationCollectionFormListener($options['languages'], $options['options']['data_class']);
- $builder->addEventSubscriber($listener);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getParent()
- {
- return 'collection';
- }
-
- /**
- * {@inheritdoc}
- */
- public function getName()
- {
- return 'propel1_translation_collection';
- }
-
- /**
- * {@inheritdoc}
- */
- public function setDefaultOptions(OptionsResolverInterface $resolver)
- {
- $resolver->setRequired(array(
- 'languages',
- ));
-
- $resolver->setDefaults(array(
- 'type' => 'propel1_translation',
- 'allow_add' => false,
- 'allow_delete' => false,
- 'options' => array(
- 'data_class' => null,
- 'columns' => null,
- ),
- ));
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Form/Type/TranslationType.php b/src/Symfony/Bridge/Propel1/Form/Type/TranslationType.php
deleted file mode 100644
index 7fed076fe090..000000000000
--- a/src/Symfony/Bridge/Propel1/Form/Type/TranslationType.php
+++ /dev/null
@@ -1,54 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Form\Type;
-
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\OptionsResolver\OptionsResolverInterface;
-use Symfony\Bridge\Propel1\Form\EventListener\TranslationFormListener;
-
-/**
- * Translation type class.
- *
- * @author Patrick Kaufmann
- */
-class TranslationType extends AbstractType
-{
- /**
- * {@inheritdoc}
- */
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- $builder->addEventSubscriber(
- new TranslationFormListener($options['columns'], $options['data_class'])
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function getName()
- {
- return 'propel1_translation';
- }
-
- /**
- * {@inheritdoc}
- */
- public function setDefaultOptions(OptionsResolverInterface $resolver)
- {
- $resolver->setRequired(array(
- 'data_class',
- 'columns',
- ));
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Logger/PropelLogger.php b/src/Symfony/Bridge/Propel1/Logger/PropelLogger.php
deleted file mode 100644
index b651935afd0e..000000000000
--- a/src/Symfony/Bridge/Propel1/Logger/PropelLogger.php
+++ /dev/null
@@ -1,170 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Logger;
-
-use Symfony\Component\Stopwatch\Stopwatch;
-use Psr\Log\LoggerInterface;
-
-/**
- * PropelLogger.
- *
- * @author Fabien Potencier
- * @author William Durand
- */
-class PropelLogger
-{
- /**
- * @var LoggerInterface
- */
- protected $logger;
-
- /**
- * @var array
- */
- protected $queries;
-
- /**
- * @var Stopwatch
- */
- protected $stopwatch;
-
- private $isPrepared;
-
- /**
- * Constructor.
- *
- * @param LoggerInterface $logger A LoggerInterface instance
- * @param Stopwatch $stopwatch A Stopwatch instance
- */
- public function __construct(LoggerInterface $logger = null, Stopwatch $stopwatch = null)
- {
- $this->logger = $logger;
- $this->queries = array();
- $this->stopwatch = $stopwatch;
- $this->isPrepared = false;
- }
-
- /**
- * A convenience function for logging an alert event.
- *
- * @param mixed $message the message to log.
- */
- public function alert($message)
- {
- if (null !== $this->logger) {
- $this->logger->alert($message);
- }
- }
-
- /**
- * A convenience function for logging a critical event.
- *
- * @param mixed $message the message to log.
- */
- public function crit($message)
- {
- if (null !== $this->logger) {
- $this->logger->critical($message);
- }
- }
-
- /**
- * A convenience function for logging an error event.
- *
- * @param mixed $message the message to log.
- */
- public function err($message)
- {
- if (null !== $this->logger) {
- $this->logger->error($message);
- }
- }
-
- /**
- * A convenience function for logging a warning event.
- *
- * @param mixed $message the message to log.
- */
- public function warning($message)
- {
- if (null !== $this->logger) {
- $this->logger->warning($message);
- }
- }
-
- /**
- * A convenience function for logging an critical event.
- *
- * @param mixed $message the message to log.
- */
- public function notice($message)
- {
- if (null !== $this->logger) {
- $this->logger->notice($message);
- }
- }
-
- /**
- * A convenience function for logging an critical event.
- *
- * @param mixed $message the message to log.
- */
- public function info($message)
- {
- if (null !== $this->logger) {
- $this->logger->info($message);
- }
- }
-
- /**
- * A convenience function for logging a debug event.
- *
- * @param mixed $message the message to log.
- */
- public function debug($message)
- {
- $add = true;
-
- if (null !== $this->stopwatch) {
- $trace = debug_backtrace();
- $method = $trace[2]['args'][2];
-
- $watch = 'Propel Query '.(count($this->queries) + 1);
- if ('PropelPDO::prepare' === $method) {
- $this->isPrepared = true;
- $this->stopwatch->start($watch, 'propel');
-
- $add = false;
- } elseif ($this->isPrepared) {
- $this->isPrepared = false;
- $this->stopwatch->stop($watch);
- }
- }
-
- if ($add) {
- $this->queries[] = $message;
- if (null !== $this->logger) {
- $this->logger->debug($message);
- }
- }
- }
-
- /**
- * Returns queries.
- *
- * @return array Queries
- */
- public function getQueries()
- {
- return $this->queries;
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Security/User/PropelUserProvider.php b/src/Symfony/Bridge/Propel1/Security/User/PropelUserProvider.php
deleted file mode 100644
index 44a55593d585..000000000000
--- a/src/Symfony/Bridge/Propel1/Security/User/PropelUserProvider.php
+++ /dev/null
@@ -1,103 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Security\User;
-
-use Symfony\Component\Security\Core\User\UserInterface;
-use Symfony\Component\Security\Core\User\UserProviderInterface;
-use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
-use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
-
-/**
- * Provides easy to use provisioning for Propel model users.
- *
- * @author William DURAND
- */
-class PropelUserProvider implements UserProviderInterface
-{
- /**
- * A Model class name.
- *
- * @var string
- */
- protected $class;
-
- /**
- * A Query class name.
- *
- * @var string
- */
- protected $queryClass;
-
- /**
- * A property to use to retrieve the user.
- *
- * @var string
- */
- protected $property;
-
- /**
- * Default constructor.
- *
- * @param string $class The User model class.
- * @param string|null $property The property to use to retrieve a user.
- */
- public function __construct($class, $property = null)
- {
- $this->class = $class;
- $this->queryClass = $class.'Query';
- $this->property = $property;
- }
-
- /**
- * {@inheritdoc}
- */
- public function loadUserByUsername($username)
- {
- $queryClass = $this->queryClass;
- $query = $queryClass::create();
-
- if (null !== $this->property) {
- $filter = 'filterBy'.ucfirst($this->property);
- $query->$filter($username);
- } else {
- $query->filterByUsername($username);
- }
-
- if (null === $user = $query->findOne()) {
- throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
- }
-
- return $user;
- }
-
- /**
- * {@inheritdoc}
- */
- public function refreshUser(UserInterface $user)
- {
- if (!$user instanceof $this->class) {
- throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
- }
-
- $queryClass = $this->queryClass;
-
- return $queryClass::create()->findPk($user->getPrimaryKey());
- }
-
- /**
- * {@inheritdoc}
- */
- public function supportsClass($class)
- {
- return $class === $this->class;
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/DataCollector/PropelDataCollectorTest.php b/src/Symfony/Bridge/Propel1/Tests/DataCollector/PropelDataCollectorTest.php
deleted file mode 100644
index f92e43e598e0..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/DataCollector/PropelDataCollectorTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\DataCollector;
-
-use Symfony\Bridge\Propel1\DataCollector\PropelDataCollector;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Bridge\Propel1\Tests\Propel1TestCase;
-
-class PropelDataCollectorTest extends Propel1TestCase
-{
- public function testCollectWithoutData()
- {
- $c = $this->createCollector(array());
- $c->collect(new Request(), new Response());
-
- $this->assertEquals(array(), $c->getQueries());
- $this->assertEquals(0, $c->getQueryCount());
- }
-
- public function testCollectWithData()
- {
- $queries = array(
- "time: 0.000 sec | mem: 1.4 MB | connection: default | SET NAMES 'utf8'",
- );
-
- $c = $this->createCollector($queries);
- $c->collect(new Request(), new Response());
-
- $this->assertEquals(array(
- array(
- 'sql' => "SET NAMES 'utf8'",
- 'time' => '0.000 sec',
- 'connection' => 'default',
- 'memory' => '1.4 MB',
- ),
- ), $c->getQueries());
- $this->assertEquals(1, $c->getQueryCount());
- }
-
- public function testCollectWithMultipleData()
- {
- $queries = array(
- "time: 0.000 sec | mem: 1.4 MB | connection: default | SET NAMES 'utf8'",
- 'time: 0.012 sec | mem: 2.4 MB | connection: default | SELECT tags.NAME, image.FILENAME FROM tags LEFT JOIN image ON tags.IMAGEID = image.ID WHERE image.ID = 12',
- "time: 0.012 sec | mem: 2.4 MB | connection: default | INSERT INTO `table` (`some_array`) VALUES ('| 1 | 2 | 3 |')",
- );
-
- $c = $this->createCollector($queries);
- $c->collect(new Request(), new Response());
-
- $this->assertEquals(array(
- array(
- 'sql' => "SET NAMES 'utf8'",
- 'time' => '0.000 sec',
- 'connection' => 'default',
- 'memory' => '1.4 MB',
- ),
- array(
- 'sql' => 'SELECT tags.NAME, image.FILENAME FROM tags LEFT JOIN image ON tags.IMAGEID = image.ID WHERE image.ID = 12',
- 'time' => '0.012 sec',
- 'connection' => 'default',
- 'memory' => '2.4 MB',
- ),
- array(
- 'sql' => "INSERT INTO `table` (`some_array`) VALUES ('| 1 | 2 | 3 |')",
- 'time' => '0.012 sec',
- 'connection' => 'default',
- 'memory' => '2.4 MB',
- ),
- ), $c->getQueries());
- $this->assertEquals(3, $c->getQueryCount());
- $this->assertEquals(0.024, $c->getTime());
- }
-
- private function createCollector($queries)
- {
- $config = $this->getMock('\PropelConfiguration');
-
- $config
- ->expects($this->any())
- ->method('getParameter')
- ->will($this->returnArgument(1))
- ;
-
- $logger = $this->getMock('\Symfony\Bridge\Propel1\Logger\PropelLogger');
- $logger
- ->expects($this->any())
- ->method('getQueries')
- ->will($this->returnValue($queries))
- ;
-
- return new PropelDataCollector($logger, $config);
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Fixtures/Column.php b/src/Symfony/Bridge/Propel1/Tests/Fixtures/Column.php
deleted file mode 100644
index 431f8e137356..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Fixtures/Column.php
+++ /dev/null
@@ -1,58 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Fixtures;
-
-class Column
-{
- private $name;
- private $type;
-
- public function __construct($name, $type)
- {
- $this->name = $name;
- $this->type = $type;
- }
-
- public function getType()
- {
- return $this->type;
- }
-
- public function isText()
- {
- if (!$this->type) {
- return false;
- }
-
- switch ($this->type) {
- case \PropelColumnTypes::CHAR:
- case \PropelColumnTypes::VARCHAR:
- case \PropelColumnTypes::LONGVARCHAR:
- case \PropelColumnTypes::BLOB:
- case \PropelColumnTypes::CLOB:
- case \PropelColumnTypes::CLOB_EMU:
- return true;
- }
-
- return false;
- }
-
- public function getSize()
- {
- return $this->isText() ? 255 : 0;
- }
-
- public function isNotNull()
- {
- return 'id' === $this->name;
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Fixtures/Item.php b/src/Symfony/Bridge/Propel1/Tests/Fixtures/Item.php
deleted file mode 100644
index faca7df59e3e..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Fixtures/Item.php
+++ /dev/null
@@ -1,103 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Fixtures;
-
-class Item implements \Persistent
-{
- private $id;
- private $value;
- private $groupName;
- private $price;
-
- public function __construct($id = null, $value = null, $groupName = null, $price = null)
- {
- $this->id = $id;
- $this->value = $value;
- $this->groupName = $groupName;
- $this->price = $price;
- }
-
- public function getId()
- {
- return $this->id;
- }
-
- public function setId($id)
- {
- $this->id = $id;
- }
-
- public function getValue()
- {
- return $this->value;
- }
-
- public function getGroupName()
- {
- return $this->groupName;
- }
-
- public function getPrice()
- {
- return $this->price;
- }
-
- public function getPrimaryKey()
- {
- return $this->getId();
- }
-
- public function setPrimaryKey($primaryKey)
- {
- $this->setId($primaryKey);
- }
-
- public function isModified()
- {
- return false;
- }
-
- public function isColumnModified($col)
- {
- return false;
- }
-
- public function isNew()
- {
- return false;
- }
-
- public function setNew($b)
- {
- }
-
- public function resetModified()
- {
- }
-
- public function isDeleted()
- {
- return false;
- }
-
- public function setDeleted($b)
- {
- }
-
- public function delete(\PropelPDO $con = null)
- {
- }
-
- public function save(\PropelPDO $con = null)
- {
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Fixtures/ItemQuery.php b/src/Symfony/Bridge/Propel1/Tests/Fixtures/ItemQuery.php
deleted file mode 100644
index cae25694461c..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Fixtures/ItemQuery.php
+++ /dev/null
@@ -1,102 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Fixtures;
-
-class ItemQuery
-{
- private $map = array(
- 'id' => \PropelColumnTypes::INTEGER,
- 'value' => \PropelColumnTypes::VARCHAR,
- 'price' => \PropelColumnTypes::FLOAT,
- 'is_active' => \PropelColumnTypes::BOOLEAN,
- 'enabled' => \PropelColumnTypes::BOOLEAN_EMU,
- 'updated_at' => \PropelColumnTypes::TIMESTAMP,
- );
-
- public static $result = array();
-
- public function find()
- {
- return self::$result;
- }
-
- public function filterById($id)
- {
- return $this;
- }
-
- public function getTableMap()
- {
- // Allows to define methods in this class
- // to avoid a lot of mock classes
- return $this;
- }
-
- public function getPrimaryKeys()
- {
- $cm = new \ColumnMap('id', new \TableMap());
- $cm->setType('INTEGER');
- $cm->setPhpName('Id');
-
- return array('id' => $cm);
- }
-
- /**
- * Method from the TableMap API.
- */
- public function hasColumn($column)
- {
- return in_array($column, array_keys($this->map));
- }
-
- /**
- * Method from the TableMap API.
- */
- public function getColumn($column)
- {
- if ($this->hasColumn($column)) {
- return new Column($column, $this->map[$column]);
- }
- }
-
- /**
- * Method from the TableMap API.
- */
- public function getRelations()
- {
- // table maps
- $authorTable = new \TableMap();
- $authorTable->setClassName('\Foo\Author');
-
- $resellerTable = new \TableMap();
- $resellerTable->setClassName('\Foo\Reseller');
-
- // relations
- $mainAuthorRelation = new \RelationMap('MainAuthor');
- $mainAuthorRelation->setType(\RelationMap::MANY_TO_ONE);
- $mainAuthorRelation->setForeignTable($authorTable);
-
- $authorRelation = new \RelationMap('Author');
- $authorRelation->setType(\RelationMap::ONE_TO_MANY);
- $authorRelation->setForeignTable($authorTable);
-
- $resellerRelation = new \RelationMap('Reseller');
- $resellerRelation->setType(\RelationMap::MANY_TO_MANY);
- $resellerRelation->setLocalTable($resellerTable);
-
- return array(
- $mainAuthorRelation,
- $authorRelation,
- $resellerRelation,
- );
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Fixtures/ReadOnlyItemQuery.php b/src/Symfony/Bridge/Propel1/Tests/Fixtures/ReadOnlyItemQuery.php
deleted file mode 100644
index 0e77c26fcfc8..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Fixtures/ReadOnlyItemQuery.php
+++ /dev/null
@@ -1,30 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Fixtures;
-
-class ReadOnlyItemQuery
-{
- public function getTableMap()
- {
- // Allows to define methods in this class
- // to avoid a lot of mock classes
- return $this;
- }
-
- public function getPrimaryKeys()
- {
- $cm = new \ColumnMap('id', new \TableMap());
- $cm->setType('INTEGER');
-
- return array('id' => $cm);
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItem.php b/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItem.php
deleted file mode 100644
index 8c1c2e2dc723..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItem.php
+++ /dev/null
@@ -1,125 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Fixtures;
-
-class TranslatableItem implements \Persistent
-{
- private $id;
- private $currentTranslations;
- private $groupName;
- private $price;
-
- public function __construct($id = null, $translations = array())
- {
- $this->id = $id;
- $this->currentTranslations = $translations;
- }
-
- public function getId()
- {
- return $this->id;
- }
-
- public function setId($id)
- {
- $this->id = $id;
- }
-
- public function getGroupName()
- {
- return $this->groupName;
- }
-
- public function getPrice()
- {
- return $this->price;
- }
-
- public function getPrimaryKey()
- {
- return $this->getId();
- }
-
- public function setPrimaryKey($primaryKey)
- {
- $this->setId($primaryKey);
- }
-
- public function isModified()
- {
- return false;
- }
-
- public function isColumnModified($col)
- {
- return false;
- }
-
- public function isNew()
- {
- return false;
- }
-
- public function setNew($b)
- {
- }
-
- public function resetModified()
- {
- }
-
- public function isDeleted()
- {
- return false;
- }
-
- public function setDeleted($b)
- {
- }
-
- public function delete(\PropelPDO $con = null)
- {
- }
-
- public function save(\PropelPDO $con = null)
- {
- }
-
- public function getTranslation($locale = 'de', \PropelPDO $con = null)
- {
- if (!isset($this->currentTranslations[$locale])) {
- $translation = new TranslatableItemI18n();
- $translation->setLocale($locale);
- $this->currentTranslations[$locale] = $translation;
- }
-
- return $this->currentTranslations[$locale];
- }
-
- public function addTranslatableItemI18n(TranslatableItemI18n $i)
- {
- if (!in_array($i, $this->currentTranslations)) {
- $this->currentTranslations[$i->getLocale()] = $i;
- $i->setItem($this);
- }
- }
-
- public function removeTranslatableItemI18n(TranslatableItemI18n $i)
- {
- unset($this->currentTranslations[$i->getLocale()]);
- }
-
- public function getTranslatableItemI18ns()
- {
- return $this->currentTranslations;
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItemI18n.php b/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItemI18n.php
deleted file mode 100644
index 76c546c0b3d5..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Fixtures/TranslatableItemI18n.php
+++ /dev/null
@@ -1,128 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Fixtures;
-
-class TranslatableItemI18n implements \Persistent
-{
- private $id;
- private $locale;
- private $value;
- private $value2;
- private $item;
-
- public function __construct($id = null, $locale = null, $value = null)
- {
- $this->id = $id;
- $this->locale = $locale;
- $this->value = $value;
- }
-
- public function getId()
- {
- return $this->id;
- }
-
- public function setId($id)
- {
- $this->id = $id;
- }
-
- public function getPrimaryKey()
- {
- return $this->getId();
- }
-
- public function setPrimaryKey($primaryKey)
- {
- $this->setId($primaryKey);
- }
-
- public function isModified()
- {
- return false;
- }
-
- public function isColumnModified($col)
- {
- return false;
- }
-
- public function isNew()
- {
- return false;
- }
-
- public function setNew($b)
- {
- }
-
- public function resetModified()
- {
- }
-
- public function isDeleted()
- {
- return false;
- }
-
- public function setDeleted($b)
- {
- }
-
- public function delete(\PropelPDO $con = null)
- {
- }
-
- public function save(\PropelPDO $con = null)
- {
- }
-
- public function setLocale($locale)
- {
- $this->locale = $locale;
- }
-
- public function getLocale()
- {
- return $this->locale;
- }
-
- public function getItem()
- {
- return $this->item;
- }
-
- public function setItem($item)
- {
- $this->item = $item;
- }
-
- public function setValue($value)
- {
- $this->value = $value;
- }
-
- public function getValue()
- {
- return $this->value;
- }
-
- public function setValue2($value2)
- {
- $this->value2 = $value2;
- }
-
- public function getValue2()
- {
- return $this->value2;
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/CompatModelChoiceListTest.php b/src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/CompatModelChoiceListTest.php
deleted file mode 100644
index 2148330ea28c..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/CompatModelChoiceListTest.php
+++ /dev/null
@@ -1,127 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Form\ChoiceList;
-
-use Symfony\Bridge\Propel1\Form\ChoiceList\ModelChoiceList;
-use Symfony\Bridge\Propel1\Tests\Fixtures\Item;
-use Symfony\Bridge\Propel1\Tests\Fixtures\ItemQuery;
-use Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest;
-
-class CompatModelChoiceListTest extends AbstractChoiceListTest
-{
- const ITEM_CLASS = '\Symfony\Bridge\Propel1\Tests\Fixtures\Item';
-
- /**
- * @var \PHPUnit_Framework_MockObject_MockObject|\Symfony\Bridge\Propel1\Tests\Fixtures\ItemQuery
- */
- protected $query;
-
- protected $item1;
- protected $item2;
- protected $item3;
- protected $item4;
-
- public function testGetChoicesForValues()
- {
- $this->query
- ->expects($this->once())
- ->method('filterById')
- ->with(array(1, 2))
- ->will($this->returnSelf())
- ;
-
- ItemQuery::$result = array(
- $this->item2,
- $this->item1,
- );
-
- parent::testGetChoicesForValues();
- }
-
- protected function setUp()
- {
- $this->query = $this->getMock('Symfony\Bridge\Propel1\Tests\Fixtures\ItemQuery', array(
- 'filterById',
- ), array(), '', true, true, true, false, true);
-
- $this->query
- ->expects($this->any())
- ->method('filterById')
- ->with($this->anything())
- ->will($this->returnSelf())
- ;
-
- $this->createItems();
-
- ItemQuery::$result = array(
- $this->item1,
- $this->item2,
- $this->item3,
- $this->item4,
- );
-
- parent::setUp();
- }
-
- protected function createItems()
- {
- $this->item1 = new Item(1, 'Foo');
- $this->item2 = new Item(2, 'Bar');
- $this->item3 = new Item(3, 'Baz');
- $this->item4 = new Item(4, 'Cuz');
- }
-
- protected function createChoiceList()
- {
- return new ModelChoiceList(self::ITEM_CLASS, 'value', null, $this->query);
- }
-
- protected function getChoices()
- {
- return array(
- 1 => $this->item1,
- 2 => $this->item2,
- 3 => $this->item3,
- 4 => $this->item4,
- );
- }
-
- protected function getLabels()
- {
- return array(
- 1 => 'Foo',
- 2 => 'Bar',
- 3 => 'Baz',
- 4 => 'Cuz',
- );
- }
-
- protected function getValues()
- {
- return array(
- 1 => '1',
- 2 => '2',
- 3 => '3',
- 4 => '4',
- );
- }
-
- protected function getIndices()
- {
- return array(
- 1,
- 2,
- 3,
- 4,
- );
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/ModelChoiceListTest.php b/src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/ModelChoiceListTest.php
deleted file mode 100644
index 4c306bec3186..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/ModelChoiceListTest.php
+++ /dev/null
@@ -1,249 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Form\ChoiceList;
-
-use Symfony\Bridge\Propel1\Form\ChoiceList\ModelChoiceList;
-use Symfony\Bridge\Propel1\Tests\Propel1TestCase;
-use Symfony\Bridge\Propel1\Tests\Fixtures\Item;
-use Symfony\Bridge\Propel1\Tests\Fixtures\ItemQuery;
-use Symfony\Bridge\Propel1\Tests\Fixtures\ReadOnlyItem;
-use Symfony\Component\Form\Extension\Core\View\ChoiceView;
-
-class ModelChoiceListTest extends Propel1TestCase
-{
- const ITEM_CLASS = '\Symfony\Bridge\Propel1\Tests\Fixtures\Item';
-
- protected function setUp()
- {
- ItemQuery::$result = array();
- }
-
- public function testEmptyChoicesReturnsEmpty()
- {
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- array()
- );
-
- $this->assertSame(array(), $choiceList->getChoices());
- }
-
- public function testReadOnlyIsValidChoice()
- {
- $item = new ReadOnlyItem();
- $choiceList = new ModelChoiceList(
- '\Symfony\Bridge\Propel1\Tests\Fixtures\ReadOnlyItem',
- 'name',
- array(
- $item,
- )
- );
-
- $this->assertSame(array(42 => $item), $choiceList->getChoices());
- }
-
- public function testFlattenedChoices()
- {
- $item1 = new Item(1, 'Foo');
- $item2 = new Item(2, 'Bar');
-
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- array(
- $item1,
- $item2,
- )
- );
-
- $this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices());
- }
-
- public function testFlattenedPreferredChoices()
- {
- $item1 = new Item(1, 'Foo');
- $item2 = new Item(2, 'Bar');
-
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- array(
- $item1,
- $item2,
- ),
- null,
- null,
- array(
- $item1,
- )
- );
-
- $this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices());
- $this->assertEquals(array(1 => new ChoiceView($item1, '1', 'Foo')), $choiceList->getPreferredViews());
- }
-
- public function testNestedChoices()
- {
- $item1 = new Item(1, 'Foo');
- $item2 = new Item(2, 'Bar');
-
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- array(
- 'group1' => array($item1),
- 'group2' => array($item2),
- )
- );
-
- $this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices());
- $this->assertEquals(array(
- 'group1' => array(1 => new ChoiceView($item1, '1', 'Foo')),
- 'group2' => array(2 => new ChoiceView($item2, '2', 'Bar')),
- ), $choiceList->getRemainingViews());
- }
-
- public function testGroupBySupportsString()
- {
- $item1 = new Item(1, 'Foo', 'Group1');
- $item2 = new Item(2, 'Bar', 'Group1');
- $item3 = new Item(3, 'Baz', 'Group2');
- $item4 = new Item(4, 'Boo!', null);
-
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- array(
- $item1,
- $item2,
- $item3,
- $item4,
- ),
- null,
- 'groupName'
- );
-
- $this->assertEquals(array(1 => $item1, 2 => $item2, 3 => $item3, 4 => $item4), $choiceList->getChoices());
- $this->assertEquals(array(
- 'Group1' => array(1 => new ChoiceView($item1, '1', 'Foo'), 2 => new ChoiceView($item2, '2', 'Bar')),
- 'Group2' => array(3 => new ChoiceView($item3, '3', 'Baz')),
- 4 => new ChoiceView($item4, '4', 'Boo!'),
- ), $choiceList->getRemainingViews());
- }
-
- public function testGroupByInvalidPropertyPathReturnsFlatChoices()
- {
- $item1 = new Item(1, 'Foo', 'Group1');
- $item2 = new Item(2, 'Bar', 'Group1');
-
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- array(
- $item1,
- $item2,
- ),
- null,
- 'child.that.does.not.exist'
- );
-
- $this->assertEquals(array(
- 1 => $item1,
- 2 => $item2,
- ), $choiceList->getChoices());
- }
-
- public function testGetValuesForChoices()
- {
- $item1 = new Item(1, 'Foo');
- $item2 = new Item(2, 'Bar');
-
- ItemQuery::$result = array(
- $item1,
- $item2,
- );
-
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- null,
- null,
- null,
- null
- );
-
- $this->assertEquals(array(1, 2), $choiceList->getValuesForChoices(array($item1, $item2)));
- $this->assertEquals(array(1, 2), $choiceList->getIndicesForChoices(array($item1, $item2)));
- }
-
- public function testDifferentEqualObjectsAreChosen()
- {
- $item = new Item(1, 'Foo');
-
- ItemQuery::$result = array(
- $item,
- );
-
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- array($item)
- );
-
- $chosenItem = new Item(1, 'Foo');
-
- $this->assertEquals(array(1), $choiceList->getIndicesForChoices(array($chosenItem)));
- $this->assertEquals(array('1'), $choiceList->getValuesForChoices(array($chosenItem)));
- }
-
- public function testGetIndicesForNullChoices()
- {
- $item = new Item(1, 'Foo');
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- array($item)
- );
-
- $this->assertEquals(array(), $choiceList->getIndicesForChoices(array(null)));
- }
-
- public function testDontAllowInvalidChoiceValues()
- {
- $item = new Item(1, 'Foo');
- $choiceList = new ModelChoiceList(
- self::ITEM_CLASS,
- 'value',
- array($item)
- );
-
- $this->assertEquals(array(), $choiceList->getValuesForChoices(array(new Item(2, 'Bar'))));
- $this->assertEquals(array(), $choiceList->getChoicesForValues(array(2)));
- }
-
- /**
- * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
- */
- public function testEmptyClass()
- {
- new ModelChoiceList('');
- }
-
- /**
- * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
- */
- public function testInvalidClass()
- {
- new ModelChoiceList('Foo\Bar\DoesNotExistClass');
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php b/src/Symfony/Bridge/Propel1/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php
deleted file mode 100644
index 9426d5c09b6e..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php
+++ /dev/null
@@ -1,106 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Form\DataTransformer;
-
-use Symfony\Bridge\Propel1\Form\DataTransformer\CollectionToArrayTransformer;
-use Symfony\Bridge\Propel1\Tests\Propel1TestCase;
-
-class CollectionToArrayTransformerTest extends Propel1TestCase
-{
- private $transformer;
-
- protected function setUp()
- {
- $this->transformer = new CollectionToArrayTransformer();
- }
-
- public function testTransform()
- {
- $result = $this->transformer->transform(new \PropelObjectCollection());
-
- $this->assertInternalType('array', $result);
- $this->assertCount(0, $result);
- }
-
- public function testTransformWithNull()
- {
- $result = $this->transformer->transform(null);
-
- $this->assertInternalType('array', $result);
- $this->assertCount(0, $result);
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
- */
- public function testTransformThrowsExceptionIfNotPropelObjectCollection()
- {
- $this->transformer->transform(new DummyObject());
- }
-
- public function testTransformWithData()
- {
- $coll = new \PropelObjectCollection();
- $coll->setData(array('foo', 'bar'));
-
- $result = $this->transformer->transform($coll);
-
- $this->assertInternalType('array', $result);
- $this->assertCount(2, $result);
- $this->assertEquals('foo', $result[0]);
- $this->assertEquals('bar', $result[1]);
- }
-
- public function testReverseTransformWithNull()
- {
- $result = $this->transformer->reverseTransform(null);
-
- $this->assertInstanceOf('\PropelObjectCollection', $result);
- $this->assertCount(0, $result->getData());
- }
-
- public function testReverseTransformWithEmptyString()
- {
- $result = $this->transformer->reverseTransform('');
-
- $this->assertInstanceOf('\PropelObjectCollection', $result);
- $this->assertCount(0, $result->getData());
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
- */
- public function testReverseTransformThrowsExceptionIfNotArray()
- {
- $this->transformer->reverseTransform(new DummyObject());
- }
-
- public function testReverseTransformWithData()
- {
- $inputData = array('foo', 'bar');
-
- $result = $this->transformer->reverseTransform($inputData);
- $data = $result->getData();
-
- $this->assertInstanceOf('\PropelObjectCollection', $result);
-
- $this->assertInternalType('array', $data);
- $this->assertCount(2, $data);
- $this->assertEquals('foo', $data[0]);
- $this->assertEquals('bar', $data[1]);
- $this->assertsame($inputData, $data);
- }
-}
-
-class DummyObject
-{
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.php b/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.php
deleted file mode 100644
index 1d9e6bdb39c5..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Form/PropelTypeGuesserTest.php
+++ /dev/null
@@ -1,133 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Form;
-
-use Symfony\Bridge\Propel1\Form\PropelTypeGuesser;
-use Symfony\Bridge\Propel1\Tests\Propel1TestCase;
-use Symfony\Component\Form\Guess\Guess;
-
-class PropelTypeGuesserTest extends Propel1TestCase
-{
- const CLASS_NAME = 'Symfony\Bridge\Propel1\Tests\Fixtures\Item';
- const UNKNOWN_CLASS_NAME = 'Symfony\Bridge\Propel1\Tests\Fixtures\UnknownItem';
-
- private $guesser;
-
- protected function setUp()
- {
- $this->guesser = new PropelTypeGuesser();
- }
-
- protected function tearDown()
- {
- $this->guesser = null;
- }
-
- public function testGuessMaxLengthWithText()
- {
- $value = $this->guesser->guessMaxLength(self::CLASS_NAME, 'value');
-
- $this->assertNotNull($value);
- $this->assertEquals(255, $value->getValue());
- }
-
- public function testGuessMaxLengthWithFloat()
- {
- $value = $this->guesser->guessMaxLength(self::CLASS_NAME, 'price');
-
- $this->assertNotNull($value);
- $this->assertNull($value->getValue());
- }
-
- public function testGuessMinLengthWithText()
- {
- $value = $this->guesser->guessPattern(self::CLASS_NAME, 'value');
-
- $this->assertNull($value);
- }
-
- public function testGuessMinLengthWithFloat()
- {
- $value = $this->guesser->guessPattern(self::CLASS_NAME, 'price');
-
- $this->assertNotNull($value);
- $this->assertNull($value->getValue());
- }
-
- public function testGuessRequired()
- {
- $value = $this->guesser->guessRequired(self::CLASS_NAME, 'id');
-
- $this->assertNotNull($value);
- $this->assertTrue($value->getValue());
- }
-
- public function testGuessRequiredWithNullableColumn()
- {
- $value = $this->guesser->guessRequired(self::CLASS_NAME, 'value');
-
- $this->assertNotNull($value);
- $this->assertFalse($value->getValue());
- }
-
- public function testGuessTypeWithoutTable()
- {
- $value = $this->guesser->guessType(self::UNKNOWN_CLASS_NAME, 'property');
-
- $this->assertNotNull($value);
- $this->assertEquals('text', $value->getType());
- $this->assertEquals(Guess::LOW_CONFIDENCE, $value->getConfidence());
- }
-
- public function testGuessTypeWithoutColumn()
- {
- $value = $this->guesser->guessType(self::CLASS_NAME, 'property');
-
- $this->assertNotNull($value);
- $this->assertEquals('text', $value->getType());
- $this->assertEquals(Guess::LOW_CONFIDENCE, $value->getConfidence());
- }
-
- /**
- * @dataProvider dataProviderForGuessType
- */
- public function testGuessType($property, $type, $confidence, $multiple = null)
- {
- $value = $this->guesser->guessType(self::CLASS_NAME, $property);
-
- $this->assertNotNull($value);
- $this->assertEquals($type, $value->getType());
- $this->assertEquals($confidence, $value->getConfidence());
-
- if ($type === 'model') {
- $options = $value->getOptions();
-
- $this->assertSame($multiple, $options['multiple']);
- }
- }
-
- public static function dataProviderForGuessType()
- {
- return array(
- array('is_active', 'checkbox', Guess::HIGH_CONFIDENCE),
- array('enabled', 'checkbox', Guess::HIGH_CONFIDENCE),
- array('id', 'integer', Guess::MEDIUM_CONFIDENCE),
- array('value', 'text', Guess::MEDIUM_CONFIDENCE),
- array('price', 'number', Guess::MEDIUM_CONFIDENCE),
- array('updated_at', 'datetime', Guess::HIGH_CONFIDENCE),
-
- array('Authors', 'model', Guess::HIGH_CONFIDENCE, true),
- array('Resellers', 'model', Guess::HIGH_CONFIDENCE, true),
- array('MainAuthor', 'model', Guess::HIGH_CONFIDENCE, false),
- );
- }
-}
diff --git a/src/Symfony/Bridge/Propel1/Tests/Form/Type/TranslationCollectionTypeTest.php b/src/Symfony/Bridge/Propel1/Tests/Form/Type/TranslationCollectionTypeTest.php
deleted file mode 100644
index cc2006c3ab92..000000000000
--- a/src/Symfony/Bridge/Propel1/Tests/Form/Type/TranslationCollectionTypeTest.php
+++ /dev/null
@@ -1,151 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\Propel1\Tests\Form\Type;
-
-use Symfony\Bridge\Propel1\Tests\Fixtures\Item;
-use Symfony\Bridge\Propel1\Form\PropelExtension;
-use Symfony\Bridge\Propel1\Tests\Fixtures\TranslatableItemI18n;
-use Symfony\Bridge\Propel1\Tests\Fixtures\TranslatableItem;
-use Symfony\Component\Form\Test\TypeTestCase;
-
-class TranslationCollectionTypeTest extends TypeTestCase
-{
- const TRANSLATION_CLASS = 'Symfony\Bridge\Propel1\Tests\Fixtures\TranslatableItem';
- const TRANSLATABLE_I18N_CLASS = 'Symfony\Bridge\Propel1\Tests\Fixtures\TranslatableItemI18n';
- const NON_TRANSLATION_CLASS = 'Symfony\Bridge\Propel1\Tests\Fixtures\Item';
-
- protected function getExtensions()
- {
- return array(new PropelExtension());
- }
-
- public function testTranslationsAdded()
- {
- $item = new TranslatableItem();
- $item->addTranslatableItemI18n(new TranslatableItemI18n(1, 'fr', 'val1'));
- $item->addTranslatableItemI18n(new TranslatableItemI18n(2, 'en', 'val2'));
-
- $builder = $this->factory->createBuilder('form', null, array(
- 'data_class' => self::TRANSLATION_CLASS,
- ));
-
- $builder->add('translatableItemI18ns', 'propel1_translation_collection', array(
- 'languages' => array('en', 'fr'),
- 'options' => array(
- 'data_class' => self::TRANSLATABLE_I18N_CLASS,
- 'columns' => array('value', 'value2' => array('label' => 'Label', 'type' => 'textarea')),
- ),
- ));
- $form = $builder->getForm();
- $form->setData($item);
- $translations = $form->get('translatableItemI18ns');
-
- $this->assertCount(2, $translations);
- $this->assertInstanceOf('Symfony\Component\Form\Form', $translations['en']);
- $this->assertInstanceOf('Symfony\Component\Form\Form', $translations['fr']);
-
- $this->assertInstanceOf(self::TRANSLATABLE_I18N_CLASS, $translations['en']->getData());
- $this->assertInstanceOf(self::TRANSLATABLE_I18N_CLASS, $translations['fr']->getData());
-
- $this->assertEquals($item->getTranslation('en'), $translations['en']->getData());
- $this->assertEquals($item->getTranslation('fr'), $translations['fr']->getData());
-
- $columnOptions = $translations['fr']->getConfig()->getOption('columns');
- $this->assertEquals('value', $columnOptions[0]);
- $this->assertEquals('textarea', $columnOptions['value2']['type']);
- $this->assertEquals('Label', $columnOptions['value2']['label']);
- }
-
- public function testNotPresentTranslationsAdded()
- {
- $item = new TranslatableItem();
-
- $this->assertCount(0, $item->getTranslatableItemI18ns());
-
- $builder = $this->factory->createBuilder('form', null, array(
- 'data_class' => self::TRANSLATION_CLASS,
- ));
- $builder->add('translatableItemI18ns', 'propel1_translation_collection', array(
- 'languages' => array('en', 'fr'),
- 'options' => array(
- 'data_class' => self::TRANSLATABLE_I18N_CLASS,
- 'columns' => array('value', 'value2' => array('label' => 'Label', 'type' => 'textarea')),
- ),
- ));
-
- $form = $builder->getForm();
- $form->setData($item);
-
- $this->assertCount(2, $item->getTranslatableItemI18ns());
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
- */
- public function testNoArrayGiven()
- {
- $item = new Item(null, 'val');
-
- $builder = $this->factory->createBuilder('form', null, array(
- 'data_class' => self::NON_TRANSLATION_CLASS,
- ));
- $builder->add('value', 'propel1_translation_collection', array(
- 'languages' => array('en', 'fr'),
- 'options' => array(
- 'data_class' => self::TRANSLATABLE_I18N_CLASS,
- 'columns' => array('value', 'value2' => array('label' => 'Label', 'type' => 'textarea')),
- ),
- ));
-
- $form = $builder->getForm();
- $form->setData($item);
- }
-
- /**
- * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
- */
- public function testNoDataClassAdded()
- {
- $this->factory->createNamed('itemI18ns', 'propel1_translation_collection', null, array(
- 'languages' => array('en', 'fr'),
- 'options' => array(
- 'columns' => array('value', 'value2'),
- ),
- ));
- }
-
- /**
- * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
- */
- public function testNoLanguagesAdded()
- {
- $this->factory->createNamed('itemI18ns', 'propel1_translation_collection', null, array(
- 'options' => array(
- 'data_class' => self::TRANSLATABLE_I18N_CLASS,
- 'columns' => array('value', 'value2'),
- ),
- ));
- }
-
- /**
- * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
- */
- public function testNoColumnsAdded()
- {
- $this->factory->createNamed('itemI18ns', 'propel1_translation_collection', null, array(
- 'languages' => array('en', 'fr'),
- 'options' => array(
- 'data_class' => self::TRANSLATABLE_I18N_CLASS,
- ),
- ));
- }
-}
diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php
index cbe96c1d5a4d..69e1b9b3e701 100644
--- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php
+++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php
@@ -105,6 +105,8 @@ public function __set($name, $value)
/**
* @param string $name
+ *
+ * @return bool
*/
public function __isset($name)
{
diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json
index 71059cc35bd2..667d7b50c983 100644
--- a/src/Symfony/Bridge/ProxyManager/composer.json
+++ b/src/Symfony/Bridge/ProxyManager/composer.json
@@ -16,26 +16,23 @@
}
],
"require": {
- "php": ">=5.3.3",
+ "php": ">=5.3.9",
"symfony/dependency-injection": "~2.3",
- "ocramius/proxy-manager": "~0.3.1|~1.0|~2.0"
+ "ocramius/proxy-manager": "~0.4|~1.0|~2.0"
},
"require-dev": {
"symfony/config": "~2.3"
},
"autoload": {
- "psr-0": {
- "Symfony\\Bridge\\ProxyManager\\": ""
- },
+ "psr-4": { "Symfony\\Bridge\\ProxyManager\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
- "target-dir": "Symfony/Bridge/ProxyManager",
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "2.3-dev"
+ "dev-master": "2.7-dev"
}
}
}
diff --git a/src/Symfony/Bridge/Swiftmailer/DataCollector/MessageDataCollector.php b/src/Symfony/Bridge/Swiftmailer/DataCollector/MessageDataCollector.php
index 2e08c273539e..9e1d75ee9401 100644
--- a/src/Symfony/Bridge/Swiftmailer/DataCollector/MessageDataCollector.php
+++ b/src/Symfony/Bridge/Swiftmailer/DataCollector/MessageDataCollector.php
@@ -11,6 +11,8 @@
namespace Symfony\Bridge\Swiftmailer\DataCollector;
+@trigger_error(__CLASS__.' class is deprecated since version 2.4 and will be removed in 3.0. Use the Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector class from SwiftmailerBundle instead. Require symfony/swiftmailer-bundle package to download SwiftmailerBundle with Composer.', E_USER_DEPRECATED);
+
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -21,6 +23,9 @@
*
* @author Fabien Potencier
* @author Clément JOBEILI
+ *
+ * @deprecated since version 2.4, to be removed in 3.0.
+ * Use the MessageDataCollector from SwiftmailerBundle instead.
*/
class MessageDataCollector extends DataCollector
{
diff --git a/src/Symfony/Bridge/Swiftmailer/composer.json b/src/Symfony/Bridge/Swiftmailer/composer.json
index ad203340e3f4..81b1d570eadc 100644
--- a/src/Symfony/Bridge/Swiftmailer/composer.json
+++ b/src/Symfony/Bridge/Swiftmailer/composer.json
@@ -16,23 +16,22 @@
}
],
"require": {
- "php": ">=5.3.3",
+ "php": ">=5.3.9",
"swiftmailer/swiftmailer": ">=4.2.0,<6.0-dev"
},
"suggest": {
"symfony/http-kernel": ""
},
"autoload": {
- "psr-0": { "Symfony\\Bridge\\Swiftmailer\\": "" },
+ "psr-4": { "Symfony\\Bridge\\Swiftmailer\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
- "target-dir": "Symfony/Bridge/Swiftmailer",
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "2.3-dev"
+ "dev-master": "2.7-dev"
}
}
}
diff --git a/src/Symfony/Bridge/Twig/AppVariable.php b/src/Symfony/Bridge/Twig/AppVariable.php
new file mode 100644
index 000000000000..88d9835ce239
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/AppVariable.php
@@ -0,0 +1,172 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Core\SecurityContext;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Exposes some Symfony parameters and services as an "app" global variable.
+ *
+ * @author Fabien Potencier
+ */
+class AppVariable
+{
+ private $container;
+ private $tokenStorage;
+ private $requestStack;
+ private $environment;
+ private $debug;
+
+ /**
+ * @deprecated since version 2.7, to be removed in 3.0.
+ */
+ public function setContainer(ContainerInterface $container)
+ {
+ $this->container = $container;
+ }
+
+ public function setTokenStorage(TokenStorageInterface $tokenStorage)
+ {
+ $this->tokenStorage = $tokenStorage;
+ }
+
+ public function setRequestStack(RequestStack $requestStack)
+ {
+ $this->requestStack = $requestStack;
+ }
+
+ public function setEnvironment($environment)
+ {
+ $this->environment = $environment;
+ }
+
+ public function setDebug($debug)
+ {
+ $this->debug = (bool) $debug;
+ }
+
+ /**
+ * Returns the security context service.
+ *
+ * @deprecated since version 2.6, to be removed in 3.0.
+ *
+ * @return SecurityContext|null The security context
+ */
+ public function getSecurity()
+ {
+ @trigger_error('The "app.security" variable is deprecated since version 2.6 and will be removed in 3.0.', E_USER_DEPRECATED);
+
+ if (null === $this->container) {
+ throw new \RuntimeException('The "app.security" variable is not available.');
+ }
+
+ if ($this->container->has('security.context')) {
+ return $this->container->get('security.context');
+ }
+ }
+
+ /**
+ * Returns the current user.
+ *
+ * @return mixed
+ *
+ * @see TokenInterface::getUser()
+ */
+ public function getUser()
+ {
+ if (null === $this->tokenStorage) {
+ if (null === $this->container) {
+ throw new \RuntimeException('The "app.user" variable is not available.');
+ } elseif (!$this->container->has('security.context')) {
+ return;
+ }
+
+ $this->tokenStorage = $this->container->get('security.context');
+ }
+
+ if (!$token = $this->tokenStorage->getToken()) {
+ return;
+ }
+
+ $user = $token->getUser();
+ if (is_object($user)) {
+ return $user;
+ }
+ }
+
+ /**
+ * Returns the current request.
+ *
+ * @return Request|null The HTTP request object
+ */
+ public function getRequest()
+ {
+ if (null === $this->requestStack) {
+ if (null === $this->container) {
+ throw new \RuntimeException('The "app.request" variable is not available.');
+ }
+
+ $this->requestStack = $this->container->get('request_stack');
+ }
+
+ return $this->requestStack->getCurrentRequest();
+ }
+
+ /**
+ * Returns the current session.
+ *
+ * @return Session|null The session
+ */
+ public function getSession()
+ {
+ if (null === $this->requestStack && null === $this->container) {
+ throw new \RuntimeException('The "app.session" variable is not available.');
+ }
+
+ if ($request = $this->getRequest()) {
+ return $request->getSession();
+ }
+ }
+
+ /**
+ * Returns the current app environment.
+ *
+ * @return string The current environment string (e.g 'dev')
+ */
+ public function getEnvironment()
+ {
+ if (null === $this->environment) {
+ throw new \RuntimeException('The "app.environment" variable is not available.');
+ }
+
+ return $this->environment;
+ }
+
+ /**
+ * Returns the current app debug mode.
+ *
+ * @return bool The current debug mode
+ */
+ public function getDebug()
+ {
+ if (null === $this->debug) {
+ throw new \RuntimeException('The "app.debug" variable is not available.');
+ }
+
+ return $this->debug;
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md
index ad22216e40b8..b4df596fa2c0 100644
--- a/src/Symfony/Bridge/Twig/CHANGELOG.md
+++ b/src/Symfony/Bridge/Twig/CHANGELOG.md
@@ -1,6 +1,24 @@
CHANGELOG
=========
+2.7.0
+-----
+
+ * added LogoutUrlExtension (provides `logout_url` and `logout_path`)
+ * added an HttpFoundation extension (provides the `absolute_url` and the `relative_path` functions)
+ * added AssetExtension (provides the `asset` and `asset_version` functions)
+ * Added possibility to extract translation messages from a file or files besides extracting from a directory
+
+2.5.0
+-----
+
+ * moved command `twig:lint` from `TwigBundle`
+
+2.4.0
+-----
+
+ * added stopwatch tag to time templates with the WebProfilerBundle
+
2.3.0
-----
diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php
new file mode 100644
index 000000000000..4bf52c5dd3e3
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php
@@ -0,0 +1,221 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Lists twig functions, filters, globals and tests present in the current project.
+ *
+ * @author Jordi Boggiano
+ */
+class DebugCommand extends Command
+{
+ private $twig;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct($name = 'debug:twig')
+ {
+ parent::__construct($name);
+ }
+
+ /**
+ * Sets the twig environment.
+ *
+ * @param \Twig_Environment $twig
+ */
+ public function setTwigEnvironment(\Twig_Environment $twig)
+ {
+ $this->twig = $twig;
+ }
+
+ /**
+ * @return \Twig_Environment $twig
+ */
+ protected function getTwigEnvironment()
+ {
+ return $this->twig;
+ }
+
+ protected function configure()
+ {
+ $this
+ ->setDefinition(array(
+ new InputArgument('filter', InputArgument::OPTIONAL, 'Show details for all entries matching this filter'),
+ new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (text or json)', 'text'),
+ ))
+ ->setDescription('Shows a list of twig functions, filters, globals and tests')
+ ->setHelp(<<%command.name% command outputs a list of twig functions,
+filters, globals and tests. Output can be filtered with an optional argument.
+
+ php %command.full_name%
+
+The command lists all functions, filters, etc.
+
+ php %command.full_name% date
+
+The command lists everything that contains the word date.
+
+ php %command.full_name% --format=json
+
+The command lists everything in a machine readable json format.
+EOF
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $twig = $this->getTwigEnvironment();
+
+ if (null === $twig) {
+ $output->writeln('The Twig environment needs to be set. ');
+
+ return 1;
+ }
+
+ $types = array('functions', 'filters', 'tests', 'globals');
+
+ if ($input->getOption('format') === 'json') {
+ $data = array();
+ foreach ($types as $type) {
+ foreach ($twig->{'get'.ucfirst($type)}() as $name => $entity) {
+ $data[$type][$name] = $this->getMetadata($type, $entity);
+ }
+ }
+ $data['tests'] = array_keys($data['tests']);
+ $output->writeln(json_encode($data));
+
+ return 0;
+ }
+
+ $filter = $input->getArgument('filter');
+
+ foreach ($types as $index => $type) {
+ $items = array();
+ foreach ($twig->{'get'.ucfirst($type)}() as $name => $entity) {
+ if (!$filter || false !== strpos($name, $filter)) {
+ $items[$name] = $name.$this->getPrettyMetadata($type, $entity);
+ }
+ }
+
+ if (!$items) {
+ continue;
+ }
+ if ($index > 0) {
+ $output->writeln('');
+ }
+ $output->writeln(''.ucfirst($type).' ');
+ ksort($items);
+ foreach ($items as $item) {
+ $output->writeln(' '.$item);
+ }
+ }
+
+ return 0;
+ }
+
+ private function getMetadata($type, $entity)
+ {
+ if ($type === 'globals') {
+ return $entity;
+ }
+ if ($type === 'tests') {
+ return;
+ }
+ if ($type === 'functions' || $type === 'filters') {
+ $args = array();
+ $cb = $entity->getCallable();
+ if (is_null($cb)) {
+ return;
+ }
+ if (is_array($cb)) {
+ if (!method_exists($cb[0], $cb[1])) {
+ return;
+ }
+ $refl = new \ReflectionMethod($cb[0], $cb[1]);
+ } elseif (is_object($cb) && method_exists($cb, '__invoke')) {
+ $refl = new \ReflectionMethod($cb, '__invoke');
+ } elseif (function_exists($cb)) {
+ $refl = new \ReflectionFunction($cb);
+ } elseif (is_string($cb) && preg_match('{^(.+)::(.+)$}', $cb, $m) && method_exists($m[1], $m[2])) {
+ $refl = new \ReflectionMethod($m[1], $m[2]);
+ } else {
+ throw new \UnexpectedValueException('Unsupported callback type');
+ }
+
+ // filter out context/environment args
+ $args = array_filter($refl->getParameters(), function ($param) use ($entity) {
+ if ($entity->needsContext() && $param->getName() === 'context') {
+ return false;
+ }
+
+ return !$param->getClass() || $param->getClass()->getName() !== 'Twig_Environment';
+ });
+
+ // format args
+ $args = array_map(function ($param) {
+ if ($param->isDefaultValueAvailable()) {
+ return $param->getName().' = '.json_encode($param->getDefaultValue());
+ }
+
+ return $param->getName();
+ }, $args);
+
+ if ($type === 'filters') {
+ // remove the value the filter is applied on
+ array_shift($args);
+ }
+
+ return $args;
+ }
+ }
+
+ private function getPrettyMetadata($type, $entity)
+ {
+ if ($type === 'tests') {
+ return '';
+ }
+
+ try {
+ $meta = $this->getMetadata($type, $entity);
+ if ($meta === null) {
+ return '(unknown?)';
+ }
+ } catch (\UnexpectedValueException $e) {
+ return ' '.$e->getMessage().' ';
+ }
+
+ if ($type === 'globals') {
+ if (is_object($meta)) {
+ return ' = object('.get_class($meta).')';
+ }
+
+ return ' = '.substr(@json_encode($meta), 0, 50);
+ }
+
+ if ($type === 'functions') {
+ return '('.implode(', ', $meta).')';
+ }
+
+ if ($type === 'filters') {
+ return $meta ? '('.implode(', ', $meta).')' : '';
+ }
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php
new file mode 100644
index 000000000000..95d550a058bc
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php
@@ -0,0 +1,248 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Finder\Finder;
+
+/**
+ * Command that will validate your template syntax and output encountered errors.
+ *
+ * @author Marc Weistroff
+ * @author Jérôme Tamarelle
+ */
+class LintCommand extends Command
+{
+ private $twig;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct($name = 'lint:twig')
+ {
+ parent::__construct($name);
+ }
+
+ /**
+ * Sets the twig environment.
+ *
+ * @param \Twig_Environment $twig
+ */
+ public function setTwigEnvironment(\Twig_Environment $twig)
+ {
+ $this->twig = $twig;
+ }
+
+ /**
+ * @return \Twig_Environment $twig
+ */
+ protected function getTwigEnvironment()
+ {
+ return $this->twig;
+ }
+
+ protected function configure()
+ {
+ $this
+ ->setAliases(array('twig:lint'))
+ ->setDescription('Lints a template and outputs encountered errors')
+ ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
+ ->addArgument('filename', InputArgument::IS_ARRAY)
+ ->setHelp(<<%command.name% command lints a template and outputs to STDOUT
+the first encountered syntax error.
+
+You can validate the syntax of contents passed from STDIN:
+
+ cat filename | php %command.full_name%
+
+Or the syntax of a file:
+
+ php %command.full_name% filename
+
+Or of a whole directory:
+
+ php %command.full_name% dirname
+ php %command.full_name% dirname --format=json
+
+EOF
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ if (false !== strpos($input->getFirstArgument(), ':l')) {
+ $output->writeln('The use of "twig:lint" command is deprecated since version 2.7 and will be removed in 3.0. Use the "lint:twig" instead. ');
+ }
+
+ $twig = $this->getTwigEnvironment();
+
+ if (null === $twig) {
+ $output->writeln('The Twig environment needs to be set. ');
+
+ return 1;
+ }
+
+ $filenames = $input->getArgument('filename');
+
+ if (0 === count($filenames)) {
+ if (0 !== ftell(STDIN)) {
+ throw new \RuntimeException('Please provide a filename or pipe template content to STDIN.');
+ }
+
+ $template = '';
+ while (!feof(STDIN)) {
+ $template .= fread(STDIN, 1024);
+ }
+
+ return $this->display($input, $output, array($this->validate($twig, $template, uniqid('sf_'))));
+ }
+
+ $filesInfo = $this->getFilesInfo($twig, $filenames);
+
+ return $this->display($input, $output, $filesInfo);
+ }
+
+ private function getFilesInfo(\Twig_Environment $twig, array $filenames)
+ {
+ $filesInfo = array();
+ foreach ($filenames as $filename) {
+ foreach ($this->findFiles($filename) as $file) {
+ $filesInfo[] = $this->validate($twig, file_get_contents($file), $file);
+ }
+ }
+
+ return $filesInfo;
+ }
+
+ protected function findFiles($filename)
+ {
+ if (is_file($filename)) {
+ return array($filename);
+ } elseif (is_dir($filename)) {
+ return Finder::create()->files()->in($filename)->name('*.twig');
+ }
+
+ throw new \RuntimeException(sprintf('File or directory "%s" is not readable', $filename));
+ }
+
+ private function validate(\Twig_Environment $twig, $template, $file)
+ {
+ $realLoader = $twig->getLoader();
+ try {
+ $temporaryLoader = new \Twig_Loader_Array(array((string) $file => $template));
+ $twig->setLoader($temporaryLoader);
+ $nodeTree = $twig->parse($twig->tokenize($template, (string) $file));
+ $twig->compile($nodeTree);
+ $twig->setLoader($realLoader);
+ } catch (\Twig_Error $e) {
+ $twig->setLoader($realLoader);
+
+ return array('template' => $template, 'file' => $file, 'valid' => false, 'exception' => $e);
+ }
+
+ return array('template' => $template, 'file' => $file, 'valid' => true);
+ }
+
+ private function display(InputInterface $input, OutputInterface $output, $files)
+ {
+ switch ($input->getOption('format')) {
+ case 'txt':
+ return $this->displayTxt($output, $files);
+ case 'json':
+ return $this->displayJson($output, $files);
+ default:
+ throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format')));
+ }
+ }
+
+ private function displayTxt(OutputInterface $output, $filesInfo)
+ {
+ $errors = 0;
+
+ foreach ($filesInfo as $info) {
+ if ($info['valid'] && $output->isVerbose()) {
+ $output->writeln('OK '.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
+ } elseif (!$info['valid']) {
+ ++$errors;
+ $this->renderException($output, $info['template'], $info['exception'], $info['file']);
+ }
+ }
+
+ $output->writeln(sprintf('%d/%d valid files ', count($filesInfo) - $errors, count($filesInfo)));
+
+ return min($errors, 1);
+ }
+
+ private function displayJson(OutputInterface $output, $filesInfo)
+ {
+ $errors = 0;
+
+ array_walk($filesInfo, function (&$v) use (&$errors) {
+ $v['file'] = (string) $v['file'];
+ unset($v['template']);
+ if (!$v['valid']) {
+ $v['message'] = $v['exception']->getMessage();
+ unset($v['exception']);
+ ++$errors;
+ }
+ });
+
+ $output->writeln(json_encode($filesInfo, defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0));
+
+ return min($errors, 1);
+ }
+
+ private function renderException(OutputInterface $output, $template, \Twig_Error $exception, $file = null)
+ {
+ $line = $exception->getTemplateLine();
+
+ if ($file) {
+ $output->writeln(sprintf('KO in %s (line %s)', $file, $line));
+ } else {
+ $output->writeln(sprintf('KO (line %s)', $line));
+ }
+
+ foreach ($this->getContext($template, $line) as $no => $code) {
+ $output->writeln(sprintf(
+ '%s %-6s %s',
+ $no == $line ? '>> ' : ' ',
+ $no,
+ $code
+ ));
+ if ($no == $line) {
+ $output->writeln(sprintf('>> %s ', $exception->getRawMessage()));
+ }
+ }
+ }
+
+ private function getContext($template, $line, $context = 3)
+ {
+ $lines = explode("\n", $template);
+
+ $position = max(0, $line - $context);
+ $max = min(count($lines), $line - 1 + $context);
+
+ $result = array();
+ while ($position < $max) {
+ $result[$position + 1] = $lines[$position];
+ ++$position;
+ }
+
+ return $result;
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php
new file mode 100644
index 000000000000..763a5ec4b31e
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php
@@ -0,0 +1,143 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\DataCollector;
+
+use Symfony\Component\HttpKernel\DataCollector\DataCollector;
+use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * TwigDataCollector.
+ *
+ * @author Fabien Potencier
+ */
+class TwigDataCollector extends DataCollector implements LateDataCollectorInterface
+{
+ private $profile;
+ private $computed;
+
+ public function __construct(\Twig_Profiler_Profile $profile)
+ {
+ $this->profile = $profile;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function collect(Request $request, Response $response, \Exception $exception = null)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function lateCollect()
+ {
+ $this->data['profile'] = serialize($this->profile);
+ }
+
+ public function getTime()
+ {
+ return $this->getProfile()->getDuration() * 1000;
+ }
+
+ public function getTemplateCount()
+ {
+ return $this->getComputedData('template_count');
+ }
+
+ public function getTemplates()
+ {
+ return $this->getComputedData('templates');
+ }
+
+ public function getBlockCount()
+ {
+ return $this->getComputedData('block_count');
+ }
+
+ public function getMacroCount()
+ {
+ return $this->getComputedData('macro_count');
+ }
+
+ public function getHtmlCallGraph()
+ {
+ $dumper = new \Twig_Profiler_Dumper_Html();
+
+ return new \Twig_Markup($dumper->dump($this->getProfile()), 'UTF-8');
+ }
+
+ public function getProfile()
+ {
+ if (null === $this->profile) {
+ $this->profile = unserialize($this->data['profile']);
+ }
+
+ return $this->profile;
+ }
+
+ private function getComputedData($index)
+ {
+ if (null === $this->computed) {
+ $this->computed = $this->computeData($this->getProfile());
+ }
+
+ return $this->computed[$index];
+ }
+
+ private function computeData(\Twig_Profiler_Profile $profile)
+ {
+ $data = array(
+ 'template_count' => 0,
+ 'block_count' => 0,
+ 'macro_count' => 0,
+ );
+
+ $templates = array();
+ foreach ($profile as $p) {
+ $d = $this->computeData($p);
+
+ $data['template_count'] += ($p->isTemplate() ? 1 : 0) + $d['template_count'];
+ $data['block_count'] += ($p->isBlock() ? 1 : 0) + $d['block_count'];
+ $data['macro_count'] += ($p->isMacro() ? 1 : 0) + $d['macro_count'];
+
+ if ($p->isTemplate()) {
+ if (!isset($templates[$p->getTemplate()])) {
+ $templates[$p->getTemplate()] = 1;
+ } else {
+ ++$templates[$p->getTemplate()];
+ }
+ }
+
+ foreach ($d['templates'] as $template => $count) {
+ if (!isset($templates[$template])) {
+ $templates[$template] = $count;
+ } else {
+ $templates[$template] += $count;
+ }
+ }
+ }
+ $data['templates'] = $templates;
+
+ return $data;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return 'twig';
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Extension/AssetExtension.php b/src/Symfony/Bridge/Twig/Extension/AssetExtension.php
new file mode 100644
index 000000000000..a72f4503dd86
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Extension/AssetExtension.php
@@ -0,0 +1,149 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\Asset\Packages;
+use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy;
+
+/**
+ * Twig extension for the Symfony Asset component.
+ *
+ * @author Fabien Potencier
+ */
+class AssetExtension extends \Twig_Extension
+{
+ private $packages;
+ private $foundationExtension;
+
+ /**
+ * Passing an HttpFoundationExtension instance as a second argument must not be relied on
+ * as it's only there to maintain BC with older Symfony version. It will be removed in 3.0.
+ */
+ public function __construct(Packages $packages, HttpFoundationExtension $foundationExtension = null)
+ {
+ $this->packages = $packages;
+ $this->foundationExtension = $foundationExtension;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFunctions()
+ {
+ return array(
+ new \Twig_SimpleFunction('asset', array($this, 'getAssetUrl')),
+ new \Twig_SimpleFunction('asset_version', array($this, 'getAssetVersion')),
+ new \Twig_SimpleFunction('assets_version', array($this, 'getAssetsVersion'), array('deprecated' => true, 'alternative' => 'asset_version')),
+ );
+ }
+
+ /**
+ * Returns the public url/path of an asset.
+ *
+ * If the package used to generate the path is an instance of
+ * UrlPackage, you will always get a URL and not a path.
+ *
+ * @param string $path A public path
+ * @param string $packageName The name of the asset package to use
+ *
+ * @return string The public path of the asset
+ */
+ public function getAssetUrl($path, $packageName = null, $absolute = false, $version = null)
+ {
+ // BC layer to be removed in 3.0
+ if (2 < $count = func_num_args()) {
+ @trigger_error('Generating absolute URLs with the Twig asset() function was deprecated in 2.7 and will be removed in 3.0. Please use absolute_url() instead.', E_USER_DEPRECATED);
+ if (4 === $count) {
+ @trigger_error('Forcing a version with the Twig asset() function was deprecated in 2.7 and will be removed in 3.0.', E_USER_DEPRECATED);
+ }
+
+ $args = func_get_args();
+
+ return $this->getLegacyAssetUrl($path, $packageName, $args[2], isset($args[3]) ? $args[3] : null);
+ }
+
+ return $this->packages->getUrl($path, $packageName);
+ }
+
+ /**
+ * Returns the version of an asset.
+ *
+ * @param string $path A public path
+ * @param string $packageName The name of the asset package to use
+ *
+ * @return string The asset version
+ */
+ public function getAssetVersion($path, $packageName = null)
+ {
+ return $this->packages->getVersion($path, $packageName);
+ }
+
+ public function getAssetsVersion($packageName = null)
+ {
+ @trigger_error('The Twig assets_version() function was deprecated in 2.7 and will be removed in 3.0. Please use asset_version() instead.', E_USER_DEPRECATED);
+
+ return $this->packages->getVersion('/', $packageName);
+ }
+
+ private function getLegacyAssetUrl($path, $packageName = null, $absolute = false, $version = null)
+ {
+ if ($version) {
+ $package = $this->packages->getPackage($packageName);
+
+ $v = new \ReflectionProperty('Symfony\Component\Asset\Package', 'versionStrategy');
+ $v->setAccessible(true);
+
+ $currentVersionStrategy = $v->getValue($package);
+
+ if (property_exists($currentVersionStrategy, 'format')) {
+ $f = new \ReflectionProperty($currentVersionStrategy, 'format');
+ $f->setAccessible(true);
+
+ $format = $f->getValue($currentVersionStrategy);
+
+ $v->setValue($package, new StaticVersionStrategy($version, $format));
+ } else {
+ $v->setValue($package, new StaticVersionStrategy($version));
+ }
+ }
+
+ try {
+ $url = $this->packages->getUrl($path, $packageName);
+ } catch (\Exception $e) {
+ if ($version) {
+ $v->setValue($package, $currentVersionStrategy);
+ }
+
+ throw $e;
+ }
+
+ if ($version) {
+ $v->setValue($package, $currentVersionStrategy);
+ }
+
+ if ($absolute) {
+ return $this->foundationExtension->generateAbsoluteUrl($url);
+ }
+
+ return $url;
+ }
+
+ /**
+ * Returns the name of the extension.
+ *
+ * @return string The extension name
+ */
+ public function getName()
+ {
+ return 'asset';
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
index d9ba2c05bd6c..b7c3605d9572 100644
--- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
+++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
@@ -31,8 +31,8 @@ class CodeExtension extends \Twig_Extension
*/
public function __construct($fileLinkFormat, $rootDir, $charset)
{
- $this->fileLinkFormat = empty($fileLinkFormat) ? ini_get('xdebug.file_link_format') : $fileLinkFormat;
- $this->rootDir = str_replace('\\', '/', $rootDir).'/';
+ $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
+ $this->rootDir = str_replace('/', DIRECTORY_SEPARATOR, dirname($rootDir)).DIRECTORY_SEPARATOR;
$this->charset = $charset;
}
@@ -160,12 +160,14 @@ public function fileExcerpt($file, $line)
*/
public function formatFile($file, $line, $text = null)
{
+ $file = trim($file);
+
if (null === $text) {
- $file = trim($file);
- $text = $file;
+ $text = str_replace('/', DIRECTORY_SEPARATOR, $file);
if (0 === strpos($text, $this->rootDir)) {
- $text = str_replace($this->rootDir, '', str_replace('\\', '/', $text));
- $text = sprintf('kernel.root_dir /%s', $this->rootDir, $text);
+ $text = substr($text, strlen($this->rootDir));
+ $text = explode(DIRECTORY_SEPARATOR, $text, 2);
+ $text = sprintf('%s %s', $this->rootDir, $text[0], isset($text[1]) ? DIRECTORY_SEPARATOR.$text[1] : '');
}
}
diff --git a/src/Symfony/Bridge/Twig/Extension/DumpExtension.php b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php
new file mode 100644
index 000000000000..30318ecac6d0
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Bridge\Twig\TokenParser\DumpTokenParser;
+use Symfony\Component\VarDumper\Cloner\ClonerInterface;
+use Symfony\Component\VarDumper\Dumper\HtmlDumper;
+
+/**
+ * Provides integration of the dump() function with Twig.
+ *
+ * @author Nicolas Grekas
+ */
+class DumpExtension extends \Twig_Extension
+{
+ private $cloner;
+
+ public function __construct(ClonerInterface $cloner)
+ {
+ $this->cloner = $cloner;
+ }
+
+ public function getFunctions()
+ {
+ return array(
+ new \Twig_SimpleFunction('dump', array($this, 'dump'), array('is_safe' => array('html'), 'needs_context' => true, 'needs_environment' => true)),
+ );
+ }
+
+ public function getTokenParsers()
+ {
+ return array(new DumpTokenParser());
+ }
+
+ public function getName()
+ {
+ return 'dump';
+ }
+
+ public function dump(\Twig_Environment $env, $context)
+ {
+ if (!$env->isDebug()) {
+ return;
+ }
+
+ if (2 === func_num_args()) {
+ $vars = array();
+ foreach ($context as $key => $value) {
+ if (!$value instanceof \Twig_Template) {
+ $vars[$key] = $value;
+ }
+ }
+
+ $vars = array($vars);
+ } else {
+ $vars = func_get_args();
+ unset($vars[0], $vars[1]);
+ }
+
+ $dump = fopen('php://memory', 'r+b');
+ $dumper = new HtmlDumper($dump);
+
+ foreach ($vars as $value) {
+ $dumper->dump($this->cloner->cloneVar($value));
+ }
+ rewind($dump);
+
+ return stream_get_contents($dump);
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php b/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php
new file mode 100644
index 000000000000..6b30a279419b
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\ExpressionLanguage\Expression;
+
+/**
+ * ExpressionExtension gives a way to create Expressions from a template.
+ *
+ * @author Fabien Potencier
+ */
+class ExpressionExtension extends \Twig_Extension
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getFunctions()
+ {
+ return array(
+ new \Twig_SimpleFunction('expression', array($this, 'createExpression')),
+ );
+ }
+
+ public function createExpression($expression)
+ {
+ return new Expression($expression);
+ }
+
+ /**
+ * Returns the name of the extension.
+ *
+ * @return string The extension name
+ */
+ public function getName()
+ {
+ return 'expression';
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php
index e972ac4354ba..3f0a42333731 100644
--- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php
+++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php
@@ -61,7 +61,7 @@ public function getTokenParsers()
public function getFunctions()
{
return array(
- new \Twig_SimpleFunction('form_enctype', null, array('node_class' => 'Symfony\Bridge\Twig\Node\FormEnctypeNode', 'is_safe' => array('html'))),
+ new \Twig_SimpleFunction('form_enctype', null, array('node_class' => 'Symfony\Bridge\Twig\Node\FormEnctypeNode', 'is_safe' => array('html'), 'deprecated' => true, 'alternative' => 'form_start')),
new \Twig_SimpleFunction('form_widget', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))),
new \Twig_SimpleFunction('form_errors', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))),
new \Twig_SimpleFunction('form_label', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))),
diff --git a/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php b/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php
new file mode 100644
index 000000000000..69d6d326f4d0
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php
@@ -0,0 +1,129 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * Twig extension for the Symfony HttpFoundation component.
+ *
+ * @author Fabien Potencier
+ */
+class HttpFoundationExtension extends \Twig_Extension
+{
+ private $requestStack;
+ private $requestContext;
+
+ public function __construct(RequestStack $requestStack, RequestContext $requestContext = null)
+ {
+ $this->requestStack = $requestStack;
+ $this->requestContext = $requestContext;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFunctions()
+ {
+ return array(
+ new \Twig_SimpleFunction('absolute_url', array($this, 'generateAbsoluteUrl')),
+ new \Twig_SimpleFunction('relative_path', array($this, 'generateRelativePath')),
+ );
+ }
+
+ /**
+ * Returns the absolute URL for the given absolute or relative path.
+ *
+ * This method returns the path unchanged if no request is available.
+ *
+ * @param string $path The path
+ *
+ * @return string The absolute URL
+ *
+ * @see Request::getUriForPath()
+ */
+ public function generateAbsoluteUrl($path)
+ {
+ if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
+ return $path;
+ }
+
+ if (!$request = $this->requestStack->getMasterRequest()) {
+ if (null !== $this->requestContext && '' !== $host = $this->requestContext->getHost()) {
+ $scheme = $this->requestContext->getScheme();
+ $port = '';
+
+ if ('http' === $scheme && 80 != $this->requestContext->getHttpPort()) {
+ $port = ':'.$this->requestContext->getHttpPort();
+ } elseif ('https' === $scheme && 443 != $this->requestContext->getHttpsPort()) {
+ $port = ':'.$this->requestContext->getHttpsPort();
+ }
+
+ if ('/' !== $path[0]) {
+ $path = rtrim($this->requestContext->getBaseUrl(), '/').'/'.$path;
+ }
+
+ return $scheme.'://'.$host.$port.$path;
+ }
+
+ return $path;
+ }
+
+ if (!$path || '/' !== $path[0]) {
+ $prefix = $request->getPathInfo();
+ $last = strlen($prefix) - 1;
+ if ($last !== $pos = strrpos($prefix, '/')) {
+ $prefix = substr($prefix, 0, $pos).'/';
+ }
+
+ return $request->getUriForPath($prefix.$path);
+ }
+
+ return $request->getSchemeAndHttpHost().$path;
+ }
+
+ /**
+ * Returns a relative path based on the current Request.
+ *
+ * This method returns the path unchanged if no request is available.
+ *
+ * @param string $path The path
+ *
+ * @return string The relative path
+ *
+ * @see Request::getRelativeUriForPath()
+ */
+ public function generateRelativePath($path)
+ {
+ if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
+ return $path;
+ }
+
+ if (!$request = $this->requestStack->getMasterRequest()) {
+ return $path;
+ }
+
+ return $request->getRelativeUriForPath($path);
+ }
+
+ /**
+ * Returns the name of the extension.
+ *
+ * @return string The extension name
+ */
+ public function getName()
+ {
+ return 'request';
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php
new file mode 100644
index 000000000000..7fc278758eb3
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php
@@ -0,0 +1,73 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
+
+/**
+ * LogoutUrlHelper provides generator functions for the logout URL to Twig.
+ *
+ * @author Jeremy Mikola
+ */
+class LogoutUrlExtension extends \Twig_Extension
+{
+ private $generator;
+
+ public function __construct(LogoutUrlGenerator $generator)
+ {
+ $this->generator = $generator;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFunctions()
+ {
+ return array(
+ new \Twig_SimpleFunction('logout_url', array($this, 'getLogoutUrl')),
+ new \Twig_SimpleFunction('logout_path', array($this, 'getLogoutPath')),
+ );
+ }
+
+ /**
+ * Generates the relative logout URL for the firewall.
+ *
+ * @param string|null $key The firewall key or null to use the current firewall key
+ *
+ * @return string The relative logout URL
+ */
+ public function getLogoutPath($key = null)
+ {
+ return $this->generator->getLogoutPath($key, UrlGeneratorInterface::ABSOLUTE_PATH);
+ }
+
+ /**
+ * Generates the absolute logout URL for the firewall.
+ *
+ * @param string|null $key The firewall key or null to use the current firewall key
+ *
+ * @return string The absolute logout URL
+ */
+ public function getLogoutUrl($key = null)
+ {
+ return $this->generator->getLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return 'logout_url';
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php b/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php
new file mode 100644
index 000000000000..648a6c8036d7
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\Stopwatch\Stopwatch;
+
+/**
+ * @author Fabien Potencier
+ */
+class ProfilerExtension extends \Twig_Extension_Profiler
+{
+ private $stopwatch;
+ private $events;
+
+ public function __construct(\Twig_Profiler_Profile $profile, Stopwatch $stopwatch = null)
+ {
+ parent::__construct($profile);
+
+ $this->stopwatch = $stopwatch;
+ $this->events = new \SplObjectStorage();
+ }
+
+ public function enter(\Twig_Profiler_Profile $profile)
+ {
+ if ($this->stopwatch && $profile->isTemplate()) {
+ $this->events[$profile] = $this->stopwatch->start($profile->getName(), 'template');
+ }
+
+ parent::enter($profile);
+ }
+
+ public function leave(\Twig_Profiler_Profile $profile)
+ {
+ parent::leave($profile);
+
+ if ($this->stopwatch && $profile->isTemplate()) {
+ $this->events[$profile]->stop();
+ unset($this->events[$profile]);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return 'native_profiler';
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php b/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php
index 49863a4e3f1c..b13d7c0f8533 100644
--- a/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php
+++ b/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php
@@ -12,7 +12,7 @@
namespace Symfony\Bridge\Twig\Extension;
use Symfony\Component\Security\Acl\Voter\FieldVote;
-use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
/**
* SecurityExtension exposes security context features.
@@ -21,16 +21,16 @@
*/
class SecurityExtension extends \Twig_Extension
{
- private $context;
+ private $securityChecker;
- public function __construct(SecurityContextInterface $context = null)
+ public function __construct(AuthorizationCheckerInterface $securityChecker = null)
{
- $this->context = $context;
+ $this->securityChecker = $securityChecker;
}
public function isGranted($role, $object = null, $field = null)
{
- if (null === $this->context) {
+ if (null === $this->securityChecker) {
return false;
}
@@ -38,7 +38,7 @@ public function isGranted($role, $object = null, $field = null)
$object = new FieldVote($object, $field);
}
- return $this->context->isGranted($role, $object);
+ return $this->securityChecker->isGranted($role, $object);
}
/**
diff --git a/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php b/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php
new file mode 100644
index 000000000000..52af92324c22
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Extension;
+
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Bridge\Twig\TokenParser\StopwatchTokenParser;
+
+/**
+ * Twig extension for the stopwatch helper.
+ *
+ * @author Wouter J
+ */
+class StopwatchExtension extends \Twig_Extension
+{
+ private $stopwatch;
+
+ /**
+ * @var bool
+ */
+ private $enabled;
+
+ public function __construct(Stopwatch $stopwatch = null, $enabled = true)
+ {
+ $this->stopwatch = $stopwatch;
+ $this->enabled = $enabled;
+ }
+
+ public function getStopwatch()
+ {
+ return $this->stopwatch;
+ }
+
+ public function getTokenParsers()
+ {
+ return array(
+ /*
+ * {% stopwatch foo %}
+ * Some stuff which will be recorded on the timeline
+ * {% endstopwatch %}
+ */
+ new StopwatchTokenParser($this->stopwatch !== null && $this->enabled),
+ );
+ }
+
+ public function getName()
+ {
+ return 'stopwatch';
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Form/TwigRenderer.php b/src/Symfony/Bridge/Twig/Form/TwigRenderer.php
index 72798d103fa1..ac139e44a133 100644
--- a/src/Symfony/Bridge/Twig/Form/TwigRenderer.php
+++ b/src/Symfony/Bridge/Twig/Form/TwigRenderer.php
@@ -12,7 +12,6 @@
namespace Symfony\Bridge\Twig\Form;
use Symfony\Component\Form\FormRenderer;
-use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
/**
* @author Bernhard Schussek
@@ -24,9 +23,9 @@ class TwigRenderer extends FormRenderer implements TwigRendererInterface
*/
private $engine;
- public function __construct(TwigRendererEngineInterface $engine, CsrfProviderInterface $csrfProvider = null)
+ public function __construct(TwigRendererEngineInterface $engine, $csrfTokenManager = null)
{
- parent::__construct($engine, $csrfProvider);
+ parent::__construct($engine, $csrfTokenManager);
$this->engine = $engine;
}
diff --git a/src/Symfony/Bridge/Twig/Node/DumpNode.php b/src/Symfony/Bridge/Twig/Node/DumpNode.php
new file mode 100644
index 000000000000..522497ba655d
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Node/DumpNode.php
@@ -0,0 +1,84 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Node;
+
+/**
+ * @author Julien Galenski
+ */
+class DumpNode extends \Twig_Node
+{
+ private $varPrefix;
+
+ public function __construct($varPrefix, \Twig_Node $values = null, $lineno, $tag = null)
+ {
+ parent::__construct(array('values' => $values), array(), $lineno, $tag);
+ $this->varPrefix = $varPrefix;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function compile(\Twig_Compiler $compiler)
+ {
+ $compiler
+ ->write("if (\$this->env->isDebug()) {\n")
+ ->indent();
+
+ $values = $this->getNode('values');
+
+ if (null === $values) {
+ // remove embedded templates (macros) from the context
+ $compiler
+ ->write(sprintf('$%svars = array();'."\n", $this->varPrefix))
+ ->write(sprintf('foreach ($context as $%1$skey => $%1$sval) {'."\n", $this->varPrefix))
+ ->indent()
+ ->write(sprintf('if (!$%sval instanceof \Twig_Template) {'."\n", $this->varPrefix))
+ ->indent()
+ ->write(sprintf('$%1$svars[$%1$skey] = $%1$sval;'."\n", $this->varPrefix))
+ ->outdent()
+ ->write("}\n")
+ ->outdent()
+ ->write("}\n")
+ ->addDebugInfo($this)
+ ->write(sprintf('\Symfony\Component\VarDumper\VarDumper::dump($%svars);'."\n", $this->varPrefix));
+ } elseif (1 === $values->count()) {
+ $compiler
+ ->addDebugInfo($this)
+ ->write('\Symfony\Component\VarDumper\VarDumper::dump(')
+ ->subcompile($values->getNode(0))
+ ->raw(");\n");
+ } else {
+ $compiler
+ ->addDebugInfo($this)
+ ->write('\Symfony\Component\VarDumper\VarDumper::dump(array('."\n")
+ ->indent();
+ foreach ($values as $node) {
+ $compiler->addIndentation();
+ if ($node->hasAttribute('name')) {
+ $compiler
+ ->string($node->getAttribute('name'))
+ ->raw(' => ');
+ }
+ $compiler
+ ->subcompile($node)
+ ->raw(",\n");
+ }
+ $compiler
+ ->outdent()
+ ->write("));\n");
+ }
+
+ $compiler
+ ->outdent()
+ ->write("}\n");
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/Node/FormEnctypeNode.php b/src/Symfony/Bridge/Twig/Node/FormEnctypeNode.php
index fc2e53b5303d..14811e6d9749 100644
--- a/src/Symfony/Bridge/Twig/Node/FormEnctypeNode.php
+++ b/src/Symfony/Bridge/Twig/Node/FormEnctypeNode.php
@@ -14,8 +14,7 @@
/**
* @author Bernhard Schussek
*
- * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use
- * the helper "form_start()" instead.
+ * @deprecated since version 2.3, to be removed in 3.0. Use the helper "form_start()" instead.
*/
class FormEnctypeNode extends SearchAndRenderBlockNode
{
diff --git a/src/Symfony/Bridge/Twig/Node/StopwatchNode.php b/src/Symfony/Bridge/Twig/Node/StopwatchNode.php
new file mode 100644
index 000000000000..06eeb492728c
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Node/StopwatchNode.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\Twig\Node;
+
+/**
+ * Represents a stopwatch node.
+ *
+ * @author Wouter J
+ */
+class StopwatchNode extends \Twig_Node
+{
+ public function __construct(\Twig_Node $name, $body, \Twig_Node_Expression_AssignName $var, $lineno = 0, $tag = null)
+ {
+ parent::__construct(array('body' => $body, 'name' => $name, 'var' => $var), array(), $lineno, $tag);
+ }
+
+ public function compile(\Twig_Compiler $compiler)
+ {
+ $compiler
+ ->addDebugInfo($this)
+ ->write('')
+ ->subcompile($this->getNode('var'))
+ ->raw(' = ')
+ ->subcompile($this->getNode('name'))
+ ->write(";\n")
+ ->write("\$this->env->getExtension('stopwatch')->getStopwatch()->start(")
+ ->subcompile($this->getNode('var'))
+ ->raw(", 'template');\n")
+ ->subcompile($this->getNode('body'))
+ ->write("\$this->env->getExtension('stopwatch')->getStopwatch()->stop(")
+ ->subcompile($this->getNode('var'))
+ ->raw(");\n")
+ ;
+ }
+}
diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php
index ad7a7362f227..f9333bf683d1 100644
--- a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php
+++ b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php
@@ -24,12 +24,12 @@ class Scope
/**
* @var array
*/
- private $data;
+ private $data = array();
/**
* @var bool
*/
- private $left;
+ private $left = false;
/**
* @param Scope $parent
@@ -37,8 +37,6 @@ class Scope
public function __construct(Scope $parent = null)
{
$this->parent = $parent;
- $this->left = false;
- $this->data = array();
}
/**
@@ -69,9 +67,9 @@ public function leave()
* @param string $key
* @param mixed $value
*
- * @throws \LogicException
- *
* @return Scope Current scope
+ *
+ * @throws \LogicException
*/
public function set($key, $value)
{
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
new file mode 100644
index 000000000000..5de20b1b8f18
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
@@ -0,0 +1,81 @@
+{% use "bootstrap_3_layout.html.twig" %}
+
+{% block form_start -%}
+ {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-horizontal')|trim}) %}
+ {{- parent() -}}
+{%- endblock form_start %}
+
+{# Labels #}
+
+{% block form_label -%}
+{% spaceless %}
+ {% if label is same as(false) %}
+
+ {% else %}
+ {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ block('form_label_class'))|trim}) %}
+ {{- parent() -}}
+ {% endif %}
+{% endspaceless %}
+{%- endblock form_label %}
+
+{% block form_label_class -%}
+col-sm-2
+{%- endblock form_label_class %}
+
+{# Rows #}
+
+{% block form_row -%}
+
+{%- endblock form_row %}
+
+{% block checkbox_row -%}
+ {{- block('checkbox_radio_row') -}}
+{%- endblock checkbox_row %}
+
+{% block radio_row -%}
+ {{- block('checkbox_radio_row') -}}
+{%- endblock radio_row %}
+
+{% block checkbox_radio_row -%}
+{% spaceless %}
+
+{% endspaceless %}
+{%- endblock checkbox_radio_row %}
+
+{% block submit_row -%}
+{% spaceless %}
+
+{% endspaceless %}
+{% endblock submit_row %}
+
+{% block reset_row -%}
+{% spaceless %}
+
+{% endspaceless %}
+{% endblock reset_row %}
+
+{% block form_group_class -%}
+col-sm-10
+{%- endblock form_group_class %}
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig
new file mode 100644
index 000000000000..ef2035ab43f0
--- /dev/null
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig
@@ -0,0 +1,245 @@
+{% use "form_div_layout.html.twig" %}
+
+{# Widgets #}
+
+{% block form_widget_simple -%}
+ {% if type is not defined or type not in ['file', 'hidden'] %}
+ {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) -%}
+ {% endif %}
+ {{- parent() -}}
+{%- endblock form_widget_simple %}
+
+{% block textarea_widget -%}
+ {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) %}
+ {{- parent() -}}
+{%- endblock textarea_widget %}
+
+{% block button_widget -%}
+ {% set attr = attr|merge({class: (attr.class|default('btn-default') ~ ' btn')|trim}) %}
+ {{- parent() -}}
+{%- endblock %}
+
+{% block money_widget -%}
+
+ {% set prepend = '{{' == money_pattern[0:2] %}
+ {% if not prepend %}
+ {{ money_pattern|replace({ '{{ widget }}':''}) }}
+ {% endif %}
+ {{- block('form_widget_simple') -}}
+ {% if prepend %}
+ {{ money_pattern|replace({ '{{ widget }}':''}) }}
+ {% endif %}
+
+{%- endblock money_widget %}
+
+{% block percent_widget -%}
+
+ {{- block('form_widget_simple') -}}
+ %
+
+{%- endblock percent_widget %}
+
+{% block datetime_widget -%}
+ {% if widget == 'single_text' %}
+ {{- block('form_widget_simple') -}}
+ {% else -%}
+ {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) -%}
+
+ {{- form_errors(form.date) -}}
+ {{- form_errors(form.time) -}}
+ {{- form_widget(form.date, { datetime: true } ) -}}
+ {{- form_widget(form.time, { datetime: true } ) -}}
+
+ {%- endif %}
+{%- endblock datetime_widget %}
+
+{% block date_widget -%}
+ {% if widget == 'single_text' %}
+ {{- block('form_widget_simple') -}}
+ {% else -%}
+ {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) -%}
+ {% if datetime is not defined or not datetime -%}
+
+ {%- endif %}
+ {{- date_pattern|replace({
+ '{{ year }}': form_widget(form.year),
+ '{{ month }}': form_widget(form.month),
+ '{{ day }}': form_widget(form.day),
+ })|raw -}}
+ {% if datetime is not defined or not datetime -%}
+
+ {%- endif -%}
+ {% endif %}
+{%- endblock date_widget %}
+
+{% block time_widget -%}
+ {% if widget == 'single_text' %}
+ {{- block('form_widget_simple') -}}
+ {% else -%}
+ {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) -%}
+ {% if datetime is not defined or false == datetime -%}
+
+ {%- endif -%}
+ {{- form_widget(form.hour) }}{% if with_minutes %}:{{ form_widget(form.minute) }}{% endif %}{% if with_seconds %}:{{ form_widget(form.second) }}{% endif %}
+ {% if datetime is not defined or false == datetime -%}
+
+ {%- endif -%}
+ {% endif %}
+{%- endblock time_widget %}
+
+{% block choice_widget_collapsed -%}
+ {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) %}
+ {{- parent() -}}
+{%- endblock %}
+
+{% block choice_widget_expanded -%}
+ {% if '-inline' in label_attr.class|default('') -%}
+ {%- for child in form %}
+ {{- form_widget(child, {
+ parent_label_class: label_attr.class|default(''),
+ translation_domain: choice_translation_domain,
+ }) -}}
+ {% endfor -%}
+ {%- else -%}
+
+ {%- for child in form %}
+ {{- form_widget(child, {
+ parent_label_class: label_attr.class|default(''),
+ translation_domain: choice_translation_domain,
+ }) -}}
+ {% endfor -%}
+
+ {%- endif %}
+{%- endblock choice_widget_expanded %}
+
+{% block checkbox_widget -%}
+ {%- set parent_label_class = parent_label_class|default(label_attr.class|default('')) -%}
+ {% if 'checkbox-inline' in parent_label_class %}
+ {{- form_label(form, null, { widget: parent() }) -}}
+ {% else -%}
+
+ {{- form_label(form, null, { widget: parent() }) -}}
+
+ {%- endif %}
+{%- endblock checkbox_widget %}
+
+{% block radio_widget -%}
+ {%- set parent_label_class = parent_label_class|default(label_attr.class|default('')) -%}
+ {% if 'radio-inline' in parent_label_class %}
+ {{- form_label(form, null, { widget: parent() }) -}}
+ {% else -%}
+
+ {{- form_label(form, null, { widget: parent() }) -}}
+
+ {%- endif %}
+{%- endblock radio_widget %}
+
+{# Labels #}
+
+{% block form_label -%}
+ {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' control-label')|trim}) -%}
+ {{- parent() -}}
+{%- endblock form_label %}
+
+{% block choice_label -%}
+ {# remove the checkbox-inline and radio-inline class, it's only useful for embed labels #}
+ {%- set label_attr = label_attr|merge({class: label_attr.class|default('')|replace({'checkbox-inline': '', 'radio-inline': ''})|trim}) -%}
+ {{- block('form_label') -}}
+{% endblock %}
+
+{% block checkbox_label -%}
+ {{- block('checkbox_radio_label') -}}
+{%- endblock checkbox_label %}
+
+{% block radio_label -%}
+ {{- block('checkbox_radio_label') -}}
+{%- endblock radio_label %}
+
+{% block checkbox_radio_label %}
+ {# Do not display the label if widget is not defined in order to prevent double label rendering #}
+ {% if widget is defined %}
+ {% if required %}
+ {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %}
+ {% endif %}
+ {% if parent_label_class is defined %}
+ {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|trim}) %}
+ {% endif %}
+ {% if label is not same as(false) and label is empty %}
+ {%- if label_format is not empty -%}
+ {% set label = label_format|replace({
+ '%name%': name,
+ '%id%': id,
+ }) %}
+ {%- else -%}
+ {% set label = name|humanize %}
+ {%- endif -%}
+ {% endif %}
+
+ {{- widget|raw }} {{ label is not same as(false) ? (translation_domain is same as(false) ? label : label|trans({}, translation_domain)) -}}
+
+ {% endif %}
+{% endblock checkbox_radio_label %}
+
+{# Rows #}
+
+{% block form_row -%}
+
+ {{- form_label(form) -}}
+ {{- form_widget(form) -}}
+ {{- form_errors(form) -}}
+
+{%- endblock form_row %}
+
+{% block button_row -%}
+
+ {{- form_widget(form) -}}
+
+{%- endblock button_row %}
+
+{% block choice_row -%}
+ {% set force_error = true %}
+ {{- block('form_row') }}
+{%- endblock choice_row %}
+
+{% block date_row -%}
+ {% set force_error = true %}
+ {{- block('form_row') }}
+{%- endblock date_row %}
+
+{% block time_row -%}
+ {% set force_error = true %}
+ {{- block('form_row') }}
+{%- endblock time_row %}
+
+{% block datetime_row -%}
+ {% set force_error = true %}
+ {{- block('form_row') }}
+{%- endblock datetime_row %}
+
+{% block checkbox_row -%}
+
+ {{- form_widget(form) -}}
+ {{- form_errors(form) -}}
+
+{%- endblock checkbox_row %}
+
+{% block radio_row -%}
+
+ {{- form_widget(form) -}}
+ {{- form_errors(form) -}}
+
+{%- endblock radio_row %}
+
+{# Errors #}
+
+{% block form_errors -%}
+ {% if errors|length > 0 -%}
+ {% if form.parent %}{% else %}{% endif %}
+
+ {%- for error in errors -%}
+ {{ error.message }}
+ {%- endfor -%}
+
+ {% if form.parent %}{% else %}
{% endif %}
+ {%- endif %}
+{%- endblock form_errors %}
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
index 6a98f65f9e1e..4e5b9dd59830 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
@@ -44,20 +44,20 @@
{%- block choice_widget_expanded -%}
- {% for child in form %}
+ {%- for child in form %}
{{- form_widget(child) -}}
- {{- form_label(child) -}}
- {% endfor %}
+ {{- form_label(child, null, {translation_domain: choice_translation_domain}) -}}
+ {% endfor -%}
{%- endblock choice_widget_expanded -%}
{%- block choice_widget_collapsed -%}
- {%- if required and empty_value is none and not empty_value_in_choices and not multiple -%}
+ {%- if required and placeholder is none and not placeholder_in_choices and not multiple and (attr.size is not defined or attr.size <= 1) -%}
{% set required = false %}
{%- endif -%}
- {%- if empty_value is not none -%}
- {{ empty_value != '' ? empty_value|trans({}, translation_domain) }}
+ {%- if placeholder is not none -%}
+ {{ placeholder != '' ? placeholder|trans({}, translation_domain) }}
{%- endif -%}
{%- if preferred_choices|length > 0 -%}
{% set options = preferred_choices %}
@@ -74,12 +74,13 @@
{%- block choice_widget_options -%}
{% for group_label, choice in options %}
{%- if choice is iterable -%}
-
+
{% set options = choice %}
{{- block('choice_widget_options') -}}
{%- else -%}
- {{ choice.label|trans({}, translation_domain) }}
+ {% set attr = choice.attr %}
+ {{ choice_translation_domain is same as(false) ? choice.label : choice.label|trans({}, choice_translation_domain) }}
{%- endif -%}
{% endfor %}
{%- endblock choice_widget_options -%}
@@ -137,8 +138,8 @@
{%- endblock number_widget -%}
{%- block integer_widget -%}
- {% set type = type|default('number') %}
- {{- block('form_widget_simple') -}}
+ {%- set type = type|default('number') -%}
+ {{ block('form_widget_simple') }}
{%- endblock integer_widget -%}
{%- block money_widget -%}
@@ -177,7 +178,14 @@
{%- block button_widget -%}
{%- if label is empty -%}
- {% set label = name|humanize %}
+ {%- if label_format is not empty -%}
+ {% set label = label_format|replace({
+ '%name%': name,
+ '%id%': id,
+ }) %}
+ {%- else -%}
+ {% set label = name|humanize %}
+ {%- endif -%}
{%- endif -%}
{{ label|trans({}, translation_domain) }}
{%- endblock button_widget -%}
@@ -195,17 +203,24 @@
{# Labels #}
{%- block form_label -%}
- {% if label is not same as(false) %}
- {%- if not compound -%}
+ {% if label is not same as(false) -%}
+ {% if not compound -%}
{% set label_attr = label_attr|merge({'for': id}) %}
{%- endif -%}
- {%- if required -%}
+ {% if required -%}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
{%- endif -%}
- {%- if label is empty -%}
- {% set label = name|humanize %}
+ {% if label is empty -%}
+ {%- if label_format is not empty -%}
+ {% set label = label_format|replace({
+ '%name%': name,
+ '%id%': id,
+ }) %}
+ {%- else -%}
+ {% set label = name|humanize %}
+ {%- endif -%}
{%- endif -%}
- {{ label|trans({}, translation_domain) }}
+ {{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}
{%- endif -%}
{%- endblock form_label -%}
@@ -254,7 +269,7 @@
{%- else -%}
{% set form_method = "POST" %}
{%- endif -%}
-