+
{{ router.targetUrl }}
{% if router.targetRoute %}(route: "{{ router.targetRoute }}"){% endif %}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php
index e5d95471ae09f..f5aa3f4ab8531 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php
@@ -82,6 +82,21 @@ public function testToolbarIsInjected()
$this->assertEquals("\nWDT\n", $response->getContent());
}
+ /**
+ * @depends testToolbarIsInjected
+ */
+ public function testToolbarIsNotInjectedOnContentDispositionAttachment()
+ {
+ $response = new Response('');
+ $response->headers->set('Content-Disposition', 'attachment; filename=test.html');
+ $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(false, 'html'), HttpKernelInterface::MASTER_REQUEST, $response);
+
+ $listener = new WebDebugToolbarListener($this->getTwigMock());
+ $listener->onKernelResponse($event);
+
+ $this->assertEquals('', $response->getContent());
+ }
+
/**
* @depends testToolbarIsInjected
* @dataProvider provideRedirects
diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
index 01207f54e6c0b..346a0e60886bc 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
@@ -42,7 +42,7 @@ function ($key, $value, $isHit) use ($defaultLifetime) {
return $item;
},
- $this,
+ null,
CacheItem::class
);
$this->mergeByLifetime = \Closure::bind(
@@ -63,7 +63,7 @@ function ($deferred, $namespace, &$expiredIds) {
return $byLifetime;
},
- $this,
+ null,
CacheItem::class
);
}
diff --git a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
index 6e55452536b7f..974348d82715e 100644
--- a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
@@ -45,7 +45,7 @@ function ($key, $value, $isHit) use ($defaultLifetime) {
return $item;
},
- $this,
+ null,
CacheItem::class
);
}
@@ -132,9 +132,9 @@ public function save(CacheItemInterface $item)
return false;
}
$item = (array) $item;
- $key = $item[CacheItem::CAST_PREFIX.'key'];
- $value = $item[CacheItem::CAST_PREFIX.'value'];
- $expiry = $item[CacheItem::CAST_PREFIX.'expiry'];
+ $key = $item["\0*\0key"];
+ $value = $item["\0*\0value"];
+ $expiry = $item["\0*\0expiry"];
if (null !== $expiry && $expiry <= time()) {
$this->deleteItem($key);
diff --git a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
index e8b9a43024948..c731e47ddd808 100644
--- a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
@@ -62,7 +62,7 @@ function ($adapter, $item) use ($maxLifetime) {
$adapter->save($item);
$item->defaultLifetime = $origDefaultLifetime;
},
- $this,
+ null,
CacheItem::class
);
}
diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
index d4e503cf8ba07..c6a7efe160d63 100644
--- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
@@ -24,23 +24,28 @@ class ProxyAdapter implements AdapterInterface
private $namespace;
private $namespaceLen;
private $createCacheItem;
+ private $poolHash;
public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defaultLifetime = 0)
{
$this->pool = $pool;
+ $this->poolHash = $poolHash = spl_object_hash($pool);
$this->namespace = '' === $namespace ? '' : $this->getId($namespace);
$this->namespaceLen = strlen($namespace);
$this->createCacheItem = \Closure::bind(
- function ($key, $value, $isHit) use ($defaultLifetime) {
+ function ($key, $innerItem) use ($defaultLifetime, $poolHash) {
$item = new CacheItem();
$item->key = $key;
- $item->value = $value;
- $item->isHit = $isHit;
+ $item->value = $innerItem->get();
+ $item->isHit = $innerItem->isHit();
$item->defaultLifetime = $defaultLifetime;
+ $item->innerItem = $innerItem;
+ $item->poolHash = $poolHash;
+ $innerItem->set(null);
return $item;
},
- $this,
+ null,
CacheItem::class
);
}
@@ -53,7 +58,7 @@ public function getItem($key)
$f = $this->createCacheItem;
$item = $this->pool->getItem($this->getId($key));
- return $f($key, $item->get(), $item->isHit());
+ return $f($key, $item);
}
/**
@@ -138,12 +143,12 @@ private function doSave(CacheItemInterface $item, $method)
return false;
}
$item = (array) $item;
- $expiry = $item[CacheItem::CAST_PREFIX.'expiry'];
- $poolItem = $this->pool->getItem($this->namespace.$item[CacheItem::CAST_PREFIX.'key']);
- $poolItem->set($item[CacheItem::CAST_PREFIX.'value']);
- $poolItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null);
+ $expiry = $item["\0*\0expiry"];
+ $innerItem = $item["\0*\0poolHash"] === $this->poolHash ? $item["\0*\0innerItem"] : $this->pool->getItem($this->namespace.$item["\0*\0key"]);
+ $innerItem->set($item["\0*\0value"]);
+ $innerItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null);
- return $this->pool->$method($poolItem);
+ return $this->pool->$method($innerItem);
}
private function generateItems($items)
@@ -155,7 +160,7 @@ private function generateItems($items)
$key = substr($key, $this->namespaceLen);
}
- yield $key => $f($key, $item->get(), $item->isHit());
+ yield $key => $f($key, $item);
}
}
diff --git a/src/Symfony/Component/Cache/CacheItem.php b/src/Symfony/Component/Cache/CacheItem.php
index e09dfafd6bdd6..2678da4a1d582 100644
--- a/src/Symfony/Component/Cache/CacheItem.php
+++ b/src/Symfony/Component/Cache/CacheItem.php
@@ -20,16 +20,13 @@
*/
final class CacheItem implements CacheItemInterface
{
- /**
- * @internal
- */
- const CAST_PREFIX = "\0Symfony\Component\Cache\CacheItem\0";
-
- private $key;
- private $value;
- private $isHit;
- private $expiry;
- private $defaultLifetime;
+ protected $key;
+ protected $value;
+ protected $isHit;
+ protected $expiry;
+ protected $defaultLifetime;
+ protected $innerItem;
+ protected $poolHash;
/**
* {@inheritdoc}
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php
index 76606e256eea6..21f2a2bf586cf 100644
--- a/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php
+++ b/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php
@@ -12,8 +12,10 @@
namespace Symfony\Component\Cache\Tests\Adapter;
use Cache\IntegrationTests\CachePoolTest;
+use Psr\Cache\CacheItemInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ProxyAdapter;
+use Symfony\Component\Cache\CacheItem;
/**
* @group time-sensitive
@@ -29,4 +31,41 @@ public function createCachePool()
{
return new ProxyAdapter(new ArrayAdapter());
}
+
+ /**
+ * @expectedException Exception
+ * @expectedExceptionMessage OK bar
+ */
+ public function testProxyfiedItem()
+ {
+ $item = new CacheItem();
+ $pool = new ProxyAdapter(new TestingArrayAdapter($item));
+
+ $proxyItem = $pool->getItem('foo');
+
+ $this->assertFalse($proxyItem === $item);
+ $pool->save($proxyItem->set('bar'));
+ }
+}
+
+class TestingArrayAdapter extends ArrayAdapter
+{
+ private $item;
+
+ public function __construct(CacheItemInterface $item)
+ {
+ $this->item = $item;
+ }
+
+ public function getItem($key)
+ {
+ return $this->item;
+ }
+
+ public function save(CacheItemInterface $item)
+ {
+ if ($item === $this->item) {
+ throw new \Exception('OK '.$item->get());
+ }
+ }
}
diff --git a/src/Symfony/Component/Console/Helper/ProgressBar.php b/src/Symfony/Component/Console/Helper/ProgressBar.php
index ca460bee132f1..3c067145e0459 100644
--- a/src/Symfony/Component/Console/Helper/ProgressBar.php
+++ b/src/Symfony/Component/Console/Helper/ProgressBar.php
@@ -42,7 +42,7 @@ class ProgressBar
private $stepWidth;
private $percent = 0.0;
private $formatLineCount;
- private $messages;
+ private $messages = array();
private $overwrite = true;
private static $formatters;
@@ -140,6 +140,16 @@ public static function getFormatDefinition($name)
return isset(self::$formats[$name]) ? self::$formats[$name] : null;
}
+ /**
+ * Associates a text with a named placeholder.
+ *
+ * The text is displayed when the progress bar is rendered but only
+ * when the corresponding placeholder is part of the custom format line
+ * (by wrapping the name with %).
+ *
+ * @param string $message The text to associate with the placeholder
+ * @param string $name The name of the placeholder
+ */
public function setMessage($message, $name = 'message')
{
$this->messages[$name] = $message;
diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php
index 24dbcfc40b58d..e66594021c777 100644
--- a/src/Symfony/Component/Console/Helper/Table.php
+++ b/src/Symfony/Component/Console/Helper/Table.php
@@ -346,7 +346,7 @@ private function renderRowSeparator()
*/
private function renderColumnSeparator()
{
- $this->output->write(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()));
+ return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar());
}
/**
@@ -363,12 +363,12 @@ private function renderRow(array $row, $cellFormat)
return;
}
- $this->renderColumnSeparator();
+ $rowContent = $this->renderColumnSeparator();
foreach ($this->getRowColumns($row) as $column) {
- $this->renderCell($row, $column, $cellFormat);
- $this->renderColumnSeparator();
+ $rowContent .= $this->renderCell($row, $column, $cellFormat);
+ $rowContent .= $this->renderColumnSeparator();
}
- $this->output->writeln('');
+ $this->output->writeln($rowContent);
}
/**
@@ -397,12 +397,13 @@ private function renderCell(array $row, $column, $cellFormat)
$style = $this->getColumnStyle($column);
if ($cell instanceof TableSeparator) {
- $this->output->write(sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)));
- } else {
- $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
- $content = sprintf($style->getCellRowContentFormat(), $cell);
- $this->output->write(sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())));
+ return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width));
}
+
+ $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
+ $content = sprintf($style->getCellRowContentFormat(), $cell);
+
+ return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType()));
}
/**
diff --git a/src/Symfony/Component/Console/Helper/TableSeparator.php b/src/Symfony/Component/Console/Helper/TableSeparator.php
index 8cbbc6613be36..8cc73e69a25f4 100644
--- a/src/Symfony/Component/Console/Helper/TableSeparator.php
+++ b/src/Symfony/Component/Console/Helper/TableSeparator.php
@@ -19,8 +19,7 @@
class TableSeparator extends TableCell
{
/**
- * @param string $value
- * @param array $options
+ * @param array $options
*/
public function __construct(array $options = array())
{
diff --git a/src/Symfony/Component/Console/Output/NullOutput.php b/src/Symfony/Component/Console/Output/NullOutput.php
index 682f9a4d432f4..218f285bfe51c 100644
--- a/src/Symfony/Component/Console/Output/NullOutput.php
+++ b/src/Symfony/Component/Console/Output/NullOutput.php
@@ -73,21 +73,33 @@ public function getVerbosity()
return self::VERBOSITY_QUIET;
}
+ /**
+ * {@inheritdoc}
+ */
public function isQuiet()
{
return true;
}
+ /**
+ * {@inheritdoc}
+ */
public function isVerbose()
{
return false;
}
+ /**
+ * {@inheritdoc}
+ */
public function isVeryVerbose()
{
return false;
}
+ /**
+ * {@inheritdoc}
+ */
public function isDebug()
{
return false;
diff --git a/src/Symfony/Component/Console/Output/Output.php b/src/Symfony/Component/Console/Output/Output.php
index 4476ffb5901d0..c12015cc8fee0 100644
--- a/src/Symfony/Component/Console/Output/Output.php
+++ b/src/Symfony/Component/Console/Output/Output.php
@@ -94,21 +94,33 @@ public function getVerbosity()
return $this->verbosity;
}
+ /**
+ * {@inheritdoc}
+ */
public function isQuiet()
{
return self::VERBOSITY_QUIET === $this->verbosity;
}
+ /**
+ * {@inheritdoc}
+ */
public function isVerbose()
{
return self::VERBOSITY_VERBOSE <= $this->verbosity;
}
+ /**
+ * {@inheritdoc}
+ */
public function isVeryVerbose()
{
return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
}
+ /**
+ * {@inheritdoc}
+ */
public function isDebug()
{
return self::VERBOSITY_DEBUG <= $this->verbosity;
diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php
index 098966286eeb5..9e6b74810d282 100644
--- a/src/Symfony/Component/Console/Output/StreamOutput.php
+++ b/src/Symfony/Component/Console/Output/StreamOutput.php
@@ -72,7 +72,7 @@ public function getStream()
*/
protected function doWrite($message, $newline)
{
- if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) {
+ if (false === @fwrite($this->stream, $message) || ($newline && (false === @fwrite($this->stream, PHP_EOL)))) {
// should never happen
throw new RuntimeException('Unable to write output.');
}
diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php
index a2781ddbeafa4..678afea5d728e 100644
--- a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php
+++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php
@@ -4,10 +4,9 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tests\Style\SymfonyStyleWithForcedLineLength;
-//Ensure that all lines are aligned to the begin of the first one and start with '//' in a very long line comment
+// ensure long words are properly wrapped in blocks
return function (InputInterface $input, OutputInterface $output) {
- $output = new SymfonyStyleWithForcedLineLength($input, $output);
- $output->comment(
- 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum'
- );
+ $word = 'Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon';
+ $sfStyle = new SymfonyStyleWithForcedLineLength($input, $output);
+ $sfStyle->block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false);
};
diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_12.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_12.php
new file mode 100644
index 0000000000000..4386e56de20da
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_12.php
@@ -0,0 +1,13 @@
+comment(
+ 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum'
+ );
+};
diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt
index 9983af832aa7d..190d784035476 100644
--- a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt
+++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt
@@ -1,6 +1,4 @@
- // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
- // aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
- // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
- // sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
+ § [CUSTOM] Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophatto
+ § peristeralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon
diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_12.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_12.txt
new file mode 100644
index 0000000000000..9983af832aa7d
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_12.txt
@@ -0,0 +1,6 @@
+
+ // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
+ // aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+ // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
+ // sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
+
diff --git a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php
index 889a9c82f2dcb..e4ce037bb8f16 100644
--- a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php
+++ b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php
@@ -54,22 +54,6 @@ public function inputCommandToOutputFilesProvider()
return array_map(null, glob($baseDir.'/command/command_*.php'), glob($baseDir.'/output/output_*.txt'));
}
-
- public function testLongWordsBlockWrapping()
- {
- $word = 'Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygovgollhjvhvljfezefeqifzeiqgiqzhrsdgihqzridghqridghqirshdghdghieridgheirhsdgehrsdvhqrsidhqshdgihrsidvqhneriqsdvjzergetsrfhgrstsfhsetsfhesrhdgtesfhbzrtfbrztvetbsdfbrsdfbrn';
- $wordLength = strlen($word);
- $maxLineLength = SymfonyStyle::MAX_LINE_LENGTH - 3;
-
- $this->command->setCode(function (InputInterface $input, OutputInterface $output) use ($word) {
- $sfStyle = new SymfonyStyleWithForcedLineLength($input, $output);
- $sfStyle->block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false);
- });
-
- $this->tester->execute(array(), array('interactive' => false, 'decorated' => false));
- $expectedCount = (int) ceil($wordLength / ($maxLineLength)) + (int) ($wordLength > $maxLineLength - 5);
- $this->assertSame($expectedCount, substr_count($this->tester->getDisplay(true), ' § '));
- }
}
/**
diff --git a/src/Symfony/Component/CssSelector/Parser/Parser.php b/src/Symfony/Component/CssSelector/Parser/Parser.php
index f94aea36a6042..3c5b2dd264b50 100644
--- a/src/Symfony/Component/CssSelector/Parser/Parser.php
+++ b/src/Symfony/Component/CssSelector/Parser/Parser.php
@@ -58,9 +58,9 @@ public function parse($source)
*
* @param Token[] $tokens
*
- * @throws SyntaxErrorException
- *
* @return array
+ *
+ * @throws SyntaxErrorException
*/
public static function parseSeries(array $tokens)
{
@@ -133,9 +133,9 @@ private function parseSelectorList(TokenStream $stream)
*
* @param TokenStream $stream
*
- * @throws SyntaxErrorException
- *
* @return Node\SelectorNode
+ *
+ * @throws SyntaxErrorException
*/
private function parserSelectorNode(TokenStream $stream)
{
@@ -173,9 +173,9 @@ private function parserSelectorNode(TokenStream $stream)
* @param TokenStream $stream
* @param bool $insideNegation
*
- * @throws SyntaxErrorException
- *
* @return array
+ *
+ * @throws SyntaxErrorException
*/
private function parseSimpleSelector(TokenStream $stream, $insideNegation = false)
{
@@ -330,9 +330,9 @@ private function parseElementNode(TokenStream $stream)
* @param Node\NodeInterface $selector
* @param TokenStream $stream
*
- * @throws SyntaxErrorException
- *
* @return Node\AttributeNode
+ *
+ * @throws SyntaxErrorException
*/
private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream)
{
diff --git a/src/Symfony/Component/CssSelector/Parser/TokenStream.php b/src/Symfony/Component/CssSelector/Parser/TokenStream.php
index 1ec727febfe0f..b1f912f9d71ba 100644
--- a/src/Symfony/Component/CssSelector/Parser/TokenStream.php
+++ b/src/Symfony/Component/CssSelector/Parser/TokenStream.php
@@ -85,9 +85,9 @@ public function freeze()
/**
* Returns next token.
*
- * @throws InternalErrorException If there is no more token
- *
* @return Token
+ *
+ * @throws InternalErrorException If there is no more token
*/
public function getNext()
{
@@ -133,9 +133,9 @@ public function getUsed()
/**
* Returns nex identifier token.
*
- * @throws SyntaxErrorException If next token is not an identifier
- *
* @return string The identifier token value
+ *
+ * @throws SyntaxErrorException If next token is not an identifier
*/
public function getNextIdentifier()
{
@@ -151,9 +151,9 @@ public function getNextIdentifier()
/**
* Returns nex identifier or star delimiter token.
*
- * @throws SyntaxErrorException If next token is not an identifier or a star delimiter
- *
* @return null|string The identifier token value or null if star found
+ *
+ * @throws SyntaxErrorException If next token is not an identifier or a star delimiter
*/
public function getNextIdentifierOrStar()
{
diff --git a/src/Symfony/Component/CssSelector/XPath/Translator.php b/src/Symfony/Component/CssSelector/XPath/Translator.php
index 8c021b3beea0c..8cbea5ca9568e 100644
--- a/src/Symfony/Component/CssSelector/XPath/Translator.php
+++ b/src/Symfony/Component/CssSelector/XPath/Translator.php
@@ -268,9 +268,9 @@ public function addPseudoClass(XPathExpr $xpath, $pseudoClass)
* @param string $attribute
* @param string $value
*
- * @throws ExpressionErrorException
- *
* @return XPathExpr
+ *
+ * @throws ExpressionErrorException
*/
public function addAttributeMatching(XPathExpr $xpath, $operator, $attribute, $value)
{
diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
index cd040a59024ff..163ef530e35d5 100644
--- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
+++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
@@ -463,7 +463,7 @@ public function testHandleErrorException()
$handler->handleException($exception);
$this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]);
- $this->assertSame("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement?", $args[0]->getMessage());
+ $this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
}
public function testHandleFatalErrorOnHHVM()
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index ce0336b3e4b73..c6660ccb6a6dd 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -1176,7 +1176,7 @@ private function hasReference($id, array $arguments, $deep = false, array &$visi
return true;
}
- if ($deep && !isset($visited[$argumentId])) {
+ if ($deep && !isset($visited[$argumentId]) && 'service_container' !== $argumentId) {
$visited[$argumentId] = true;
$service = $this->container->getDefinition($argumentId);
diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
index 409d4a307af04..bd12c672ea5d1 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
@@ -484,7 +484,9 @@ public function validateSchema(\DOMDocument $dom)
EOF
;
+ $disableEntities = libxml_disable_entity_loader(false);
$valid = @$dom->schemaValidateSource($source);
+ libxml_disable_entity_loader($disableEntities);
foreach ($tmpfiles as $tmpfile) {
@unlink($tmpfile);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 97a72b826697f..b40c835d3fba2 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -284,4 +284,15 @@ public function testDumpAutowireData()
$this->assertEquals(file_get_contents(self::$fixturesPath.'/php/services24.php'), $dumper->dump());
}
+
+ public function testInlinedDefinitionReferencingServiceContainer()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->addMethodCall('add', array(new Reference('service_container')))->setPublic(false);
+ $container->register('bar', 'stdClass')->addArgument(new Reference('foo'));
+ $container->compile();
+
+ $dumper = new PhpDumper($container);
+ $this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container');
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php
new file mode 100644
index 0000000000000..a3dcfc6986df4
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php
@@ -0,0 +1,57 @@
+services = array();
+ $this->methodMap = array(
+ 'bar' => 'getBarService',
+ );
+
+ $this->aliases = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function compile()
+ {
+ throw new LogicException('You cannot compile a dumped frozen container.');
+ }
+
+ /**
+ * Gets the 'bar' service.
+ *
+ * This service is shared.
+ * This method always returns the same instance of the service.
+ *
+ * @return \stdClass A stdClass instance.
+ */
+ protected function getBarService()
+ {
+ $a = new \stdClass();
+ $a->add($this);
+
+ return $this->services['bar'] = new \stdClass($a);
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
index 4d4d2bd2f34f7..9f04e92d787bd 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
@@ -84,6 +84,19 @@ public function testParseFile()
$this->assertInstanceOf('DOMDocument', $xml, '->parseFileToDOM() returns an SimpleXMLElement object');
}
+ public function testLoadWithExternalEntitiesDisabled()
+ {
+ $disableEntities = libxml_disable_entity_loader(true);
+
+ $containerBuilder = new ContainerBuilder();
+ $loader = new XmlFileLoader($containerBuilder, new FileLocator(self::$fixturesPath.'/xml'));
+ $loader->load('services2.xml');
+
+ libxml_disable_entity_loader($disableEntities);
+
+ $this->assertTrue(count($containerBuilder->getParameterBag()->all()) > 0, 'Parameters can be read from the config file.');
+ }
+
public function testLoadParameters()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/EventDispatcher/GenericEvent.php b/src/Symfony/Component/EventDispatcher/GenericEvent.php
index 6458180a5aac3..03cbcfe33414e 100644
--- a/src/Symfony/Component/EventDispatcher/GenericEvent.php
+++ b/src/Symfony/Component/EventDispatcher/GenericEvent.php
@@ -61,9 +61,9 @@ public function getSubject()
*
* @param string $key Key.
*
- * @throws \InvalidArgumentException If key is not found.
- *
* @return mixed Contents of array key.
+ *
+ * @throws \InvalidArgumentException If key is not found.
*/
public function getArgument($key)
{
@@ -130,9 +130,9 @@ public function hasArgument($key)
*
* @param string $key Array key.
*
- * @throws \InvalidArgumentException If key does not exist in $this->args.
- *
* @return mixed
+ *
+ * @throws \InvalidArgumentException If key does not exist in $this->args.
*/
public function offsetGet($key)
{
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
index d6467fc194ad5..2e6df405d5b6e 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php
@@ -126,7 +126,7 @@ public function reverseTransform($value)
try {
// read timestamp into DateTime object - the formatter delivers in UTC
- $dateTime = new \DateTime(sprintf('@%s UTC', $timestamp));
+ $dateTime = new \DateTime(sprintf('@%s', $timestamp));
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
}
diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php
index f306e11051043..74ff45b297dc2 100644
--- a/src/Symfony/Component/Form/Form.php
+++ b/src/Symfony/Component/Form/Form.php
@@ -1018,9 +1018,9 @@ public function createView(FormView $parent = null)
*
* @param mixed $value The value to transform
*
- * @throws TransformationFailedException If the value cannot be transformed to "normalized" format
- *
* @return mixed
+ *
+ * @throws TransformationFailedException If the value cannot be transformed to "normalized" format
*/
private function modelToNorm($value)
{
@@ -1044,9 +1044,9 @@ private function modelToNorm($value)
*
* @param string $value The value to reverse transform
*
- * @throws TransformationFailedException If the value cannot be transformed to "model" format
- *
* @return mixed
+ *
+ * @throws TransformationFailedException If the value cannot be transformed to "model" format
*/
private function normToModel($value)
{
@@ -1072,9 +1072,9 @@ private function normToModel($value)
*
* @param mixed $value The value to transform
*
- * @throws TransformationFailedException If the value cannot be transformed to "view" format
- *
* @return mixed
+ *
+ * @throws TransformationFailedException If the value cannot be transformed to "view" format
*/
private function normToView($value)
{
@@ -1107,9 +1107,9 @@ private function normToView($value)
*
* @param string $value The value to reverse transform
*
- * @throws TransformationFailedException If the value cannot be transformed to "normalized" format
- *
* @return mixed
+ *
+ * @throws TransformationFailedException If the value cannot be transformed to "normalized" format
*/
private function viewToNorm($value)
{
diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php
index 2dbf871aba902..c2c3ad21b24b3 100644
--- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php
+++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php
@@ -231,6 +231,66 @@ public function testSingleChoice()
);
}
+ public function testSingleChoiceAttributesWithMainAttributes()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
+ 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
+ 'multiple' => false,
+ 'expanded' => false,
+ 'attr' => array('class' => 'bar&baz'),
+ ));
+
+ $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
+'/select
+ [@name="name"]
+ [@class="bar&baz form-control"]
+ [not(@required)]
+ [
+ ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
+ /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
+ ]
+ [count(./option)=2]
+'
+ );
+ }
+
+ public function testSingleExpandedChoiceAttributesWithMainAttributes()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
+ 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
+ 'multiple' => false,
+ 'expanded' => true,
+ 'attr' => array('class' => 'bar&baz'),
+ ));
+
+ $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
+'/div
+ [@class="bar&baz"]
+ [
+ ./div
+ [@class="radio"]
+ [
+ ./label
+ [.=" [trans]Choice&A[/trans]"]
+ [
+ ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
+ ]
+ ]
+ /following-sibling::div
+ [@class="radio"]
+ [
+ ./label
+ [.=" [trans]Choice&B[/trans]"]
+ [
+ ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
+ ]
+ ]
+ /following-sibling::input[@type="hidden"][@id="name__token"]
+ ]
+'
+ );
+ }
+
public function testSelectWithSizeBiggerThanOneCanBeRequired()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array(
diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
index 4ff4f5407b8cc..dc280e25dd922 100644
--- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
+++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
@@ -605,6 +605,53 @@ public function testSingleChoiceAttributes()
);
}
+ public function testSingleChoiceAttributesWithMainAttributes()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
+ 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
+ 'multiple' => false,
+ 'expanded' => false,
+ 'attr' => array('class' => 'bar&baz'),
+ ));
+
+ $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
+'/select
+ [@name="name"]
+ [@class="bar&baz"]
+ [not(@required)]
+ [
+ ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
+ /following-sibling::option[@value="&b"][not(@class)][not(@selected)][.="[trans]Choice&B[/trans]"]
+ ]
+ [count(./option)=2]
+'
+ );
+ }
+
+ public function testSingleExpandedChoiceAttributesWithMainAttributes()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
+ 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
+ 'multiple' => false,
+ 'expanded' => true,
+ 'attr' => array('class' => 'bar&baz'),
+ ));
+
+ $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'bar&baz')),
+'/div
+ [@class="bar&baz"]
+ [
+ ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
+ /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"]
+ /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
+ /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
+ /following-sibling::input[@type="hidden"][@id="name__token"]
+ ]
+ [count(./input)=3]
+'
+ );
+ }
+
public function testSingleChoiceWithPreferred()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php
index 75eeefbf5d780..17fd344b8437c 100644
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php
+++ b/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php
@@ -661,6 +661,7 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'image/gif' => 'gif',
'image/ief' => 'ief',
'image/jpeg' => 'jpeg',
+ 'image/pjpeg' => 'jpeg',
'image/ktx' => 'ktx',
'image/png' => 'png',
'image/prs.btif' => 'btif',
diff --git a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
index 06534aa75bf39..1d26e86b9b17b 100644
--- a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
+++ b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
@@ -181,9 +181,9 @@ public function removeCookie($name, $path = '/', $domain = null)
*
* @param string $format
*
- * @throws \InvalidArgumentException When the $format is invalid
- *
* @return array
+ *
+ * @throws \InvalidArgumentException When the $format is invalid
*/
public function getCookies($format = self::COOKIES_FLAT)
{
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
index 48e81ee0f1d30..1aad67960a45a 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
@@ -347,7 +347,7 @@ public function write($sessionId, $data)
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$updateStmt->execute();
- // When MERGE is not supported, like in Postgres, we have to use this approach that can result in
+ // When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
// duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior).
// We can just catch such an error and re-execute the update. This is similar to a serializable
// transaction with retry logic on serialization failures but without the overhead and without possible
@@ -659,11 +659,11 @@ private function getSelectSql()
*/
private function getMergeSql()
{
- switch ($this->driver) {
- case 'mysql':
+ switch (true) {
+ case 'mysql' === $this->driver:
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
- case 'oci':
+ case 'oci' === $this->driver:
// DUAL is Oracle specific dummy table
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
@@ -674,8 +674,11 @@ private function getMergeSql()
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time;";
- case 'sqlite':
+ case 'sqlite' === $this->driver:
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
+ case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='):
+ return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
+ "ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (:data, :lifetime, :time) WHERE $this->idCol = :id";
}
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php
index 4b2f5d27aab41..206f18e77f8d9 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php
@@ -24,9 +24,9 @@ interface SessionStorageInterface
/**
* Starts the session.
*
- * @throws \RuntimeException If something goes wrong starting the session.
- *
* @return bool True if started.
+ *
+ * @throws \RuntimeException If something goes wrong starting the session.
*/
public function start();
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php
index e8795244b176e..0c579d7724846 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php
@@ -30,6 +30,10 @@ class MemcacheSessionHandlerTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcache class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+ }
+
parent::setUp();
$this->memcache = $this->getMock('Memcache');
$this->storage = new MemcacheSessionHandler(
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
index f51ec8ed63c86..75e4a20e2a8d5 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
@@ -31,6 +31,10 @@ class MemcachedSessionHandlerTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcached class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+ }
+
parent::setUp();
if (version_compare(phpversion('memcached'), '2.2.0', '>=')) {
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php
index 1af321096ebad..9612320976944 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php
@@ -135,6 +135,10 @@ public function testReadWriteReadWithNullByte()
public function testReadConvertsStreamToString()
{
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+ }
+
$pdo = new MockPdo('pgsql');
$pdo->prepareResult = $this->getMock('PDOStatement');
@@ -152,6 +156,10 @@ public function testReadConvertsStreamToString()
public function testReadLockedConvertsStreamToString()
{
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+ }
+
$pdo = new MockPdo('pgsql');
$selectStmt = $this->getMock('PDOStatement');
$insertStmt = $this->getMock('PDOStatement');
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
index 3021a12ee5266..fd66b38cd2592 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
@@ -130,7 +130,7 @@ public function collect(Request $request, Response $response, \Exception $except
unset($this->controllers[$request]);
}
- if (null !== $session) {
+ if (null !== $session && $session->isStarted()) {
if ($request->attributes->has('_redirected')) {
$this->data['redirect'] = $session->remove('sf_redirect');
}
@@ -291,7 +291,7 @@ public function onKernelController(FilterControllerEvent $event)
public function onKernelResponse(FilterResponseEvent $event)
{
- if (!$event->isMasterRequest() || !$event->getRequest()->hasSession()) {
+ if (!$event->isMasterRequest() || !$event->getRequest()->hasSession() || !$event->getRequest()->getSession()->isStarted()) {
return;
}
diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php
index 546e3ca2b3364..1c7684a91b78c 100644
--- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php
+++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php
@@ -154,6 +154,8 @@ public function getKernel()
* Gets the Surrogate instance.
*
* @return SurrogateInterface A Surrogate instance
+ *
+ * @throws \LogicException
*/
public function getSurrogate()
{
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index c26ee931803a9..5b0a1a21b051e 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -59,11 +59,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $startTime;
protected $loadClassCache;
- const VERSION = '3.1.0';
- const VERSION_ID = 30100;
+ const VERSION = '3.1.1';
+ const VERSION_ID = 30101;
const MAJOR_VERSION = 3;
const MINOR_VERSION = 1;
- const RELEASE_VERSION = 0;
+ const RELEASE_VERSION = 1;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '01/2017';
diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
index 1e5fde618aa96..7daf0daa2977f 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
@@ -128,7 +128,7 @@ public function getUndefinedControllers()
return array(
array(1, 'InvalidArgumentException', 'Unable to find controller "1".'),
array('foo', 'InvalidArgumentException', 'Unable to find controller "foo".'),
- array('foo::bar', 'InvalidArgumentException', 'Class "foo" does not exist.'),
+ array('oof::bar', 'InvalidArgumentException', 'Class "oof" does not exist.'),
array('stdClass', 'InvalidArgumentException', 'Unable to find controller "stdClass".'),
array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'),
array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'),
diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php
index a607d279dbca4..15b77e0c5be12 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php
@@ -113,7 +113,7 @@ public function testFlush()
$line = __LINE__ - 1;
ob_start();
- $collector = null;
+ $collector->__destruct();
$this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean());
}
}
diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php
index 01788b1d79228..eef00d4a024dd 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php
@@ -11,7 +11,10 @@
namespace Symfony\Component\HttpKernel\Tests\DataCollector;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector;
@@ -52,6 +55,20 @@ public function testCollect()
$this->assertSame('application/json', $c->getContentType());
}
+ public function testKernelResponseDoesNotStartSession()
+ {
+ $kernel = $this->getMock(HttpKernelInterface::class);
+ $request = new Request();
+ $session = new Session(new MockArraySessionStorage());
+ $request->setSession($session);
+ $response = new Response();
+
+ $c = new RequestDataCollector();
+ $c->onKernelResponse(new FilterResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response));
+
+ $this->assertFalse($session->isStarted());
+ }
+
/**
* Test various types of controller callables.
*/
diff --git a/src/Symfony/Component/Inflector/Inflector.php b/src/Symfony/Component/Inflector/Inflector.php
index 7b227c5ac60a8..fbe8cc8a45c72 100644
--- a/src/Symfony/Component/Inflector/Inflector.php
+++ b/src/Symfony/Component/Inflector/Inflector.php
@@ -26,7 +26,6 @@ final class Inflector
* @var array
*
* @see http://english-zone.com/spelling/plurals.html
- * @see http://www.scribd.com/doc/3271143/List-of-100-Irregular-Plural-Nouns-in-English
*/
private static $pluralMap = array(
// First entry: plural suffix, reversed
@@ -71,6 +70,15 @@ final class Inflector
// movies (movie)
array('seivom', 6, true, true, 'movie'),
+ // feet (foot)
+ array('teef', 4, true, true, 'foot'),
+
+ // geese (goose)
+ array('eseeg', 5, true, true, 'goose'),
+
+ // teeth (tooth)
+ array('hteet', 5, true, true, 'tooth'),
+
// news (news)
array('swen', 4, true, true, 'news'),
@@ -218,11 +226,6 @@ public static function singularize($plural)
}
}
- // Convert teeth to tooth, feet to foot
- if (false !== ($pos = strpos($plural, 'ee')) && strlen($plural) > 3 && 'feedback' !== $plural) {
- return substr_replace($plural, 'oo', $pos, 2);
- }
-
// Assume that plural and singular is identical
return $plural;
}
diff --git a/src/Symfony/Component/Inflector/Tests/InflectorTest.php b/src/Symfony/Component/Inflector/Tests/InflectorTest.php
index 43aa682f37565..0c0d702dd8b47 100644
--- a/src/Symfony/Component/Inflector/Tests/InflectorTest.php
+++ b/src/Symfony/Component/Inflector/Tests/InflectorTest.php
@@ -53,6 +53,7 @@ public function singularizeProvider()
array('children', 'child'),
array('circuses', array('circus', 'circuse', 'circusis')),
array('cliffs', 'cliff'),
+ array('committee', 'committee'),
array('crises', array('cris', 'crise', 'crisis')),
array('criteria', array('criterion', 'criterium')),
array('cups', 'cup'),
diff --git a/src/Symfony/Component/Ldap/LdapClient.php b/src/Symfony/Component/Ldap/LdapClient.php
index bb9c1cbd4c7e7..2bd32f6ac1066 100644
--- a/src/Symfony/Component/Ldap/LdapClient.php
+++ b/src/Symfony/Component/Ldap/LdapClient.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\Ldap;
+@trigger_error('The '.__NAMESPACE__.'\LdapClient class is deprecated since version 3.1 and will be removed in 4.0. Use the Ldap class directly instead.', E_USER_DEPRECATED);
+
/**
* @author Grégoire Pineau
* @author Francis Besset
@@ -24,14 +26,7 @@ final class LdapClient implements LdapClientInterface
public function __construct($host = null, $port = 389, $version = 3, $useSsl = false, $useStartTls = false, $optReferrals = false, LdapInterface $ldap = null)
{
- $config = array(
- 'host' => $host,
- 'port' => $port,
- 'version' => $version,
- 'useSsl' => (bool) $useSsl,
- 'useStartTls' => (bool) $useStartTls,
- 'optReferrals' => (bool) $optReferrals,
- );
+ $config = $this->normalizeConfig($host, $port, $version, $useSsl, $useStartTls, $optReferrals);
$this->ldap = null !== $ldap ? $ldap : Ldap::create('ext_ldap', $config);
}
@@ -98,4 +93,25 @@ public function escape($subject, $ignore = '', $flags = 0)
{
return $this->ldap->escape($subject, $ignore, $flags);
}
+
+ private function normalizeConfig($host, $port, $version, $useSsl, $useStartTls, $optReferrals)
+ {
+ if ((bool) $useSsl) {
+ $encryption = 'ssl';
+ } elseif ((bool) $useStartTls) {
+ $encryption = 'tls';
+ } else {
+ $encryption = 'none';
+ }
+
+ return array(
+ 'host' => $host,
+ 'port' => $port,
+ 'encryption' => $encryption,
+ 'options' => array(
+ 'protocol_version' => $version,
+ 'referrals' => (bool) $optReferrals,
+ ),
+ );
+ }
}
diff --git a/src/Symfony/Component/Ldap/Tests/LdapClientTest.php b/src/Symfony/Component/Ldap/Tests/LdapClientTest.php
index 985c09e9c3021..60cdf27a1231f 100644
--- a/src/Symfony/Component/Ldap/Tests/LdapClientTest.php
+++ b/src/Symfony/Component/Ldap/Tests/LdapClientTest.php
@@ -177,4 +177,70 @@ public function testLdapFind()
);
$this->assertEquals($expected, $this->client->find('dc=foo,dc=com', 'bar', 'baz'));
}
+
+ /**
+ * @dataProvider provideConfig
+ */
+ public function testLdapClientConfig($args, $expected)
+ {
+ $reflObj = new \ReflectionObject($this->client);
+ $reflMethod = $reflObj->getMethod('normalizeConfig');
+ $reflMethod->setAccessible(true);
+ array_unshift($args, $this->client);
+ $this->assertEquals($expected, call_user_func_array(array($reflMethod, 'invoke'), $args));
+ }
+
+ public function provideConfig()
+ {
+ return array(
+ array(
+ array('localhost', 389, 3, true, false, false),
+ array(
+ 'host' => 'localhost',
+ 'port' => 389,
+ 'encryption' => 'ssl',
+ 'options' => array(
+ 'protocol_version' => 3,
+ 'referrals' => false,
+ ),
+ ),
+ ),
+ array(
+ array('localhost', 389, 3, false, true, false),
+ array(
+ 'host' => 'localhost',
+ 'port' => 389,
+ 'encryption' => 'tls',
+ 'options' => array(
+ 'protocol_version' => 3,
+ 'referrals' => false,
+ ),
+ ),
+ ),
+ array(
+ array('localhost', 389, 3, false, false, false),
+ array(
+ 'host' => 'localhost',
+ 'port' => 389,
+ 'encryption' => 'none',
+ 'options' => array(
+ 'protocol_version' => 3,
+ 'referrals' => false,
+ ),
+ ),
+ ),
+ array(
+ array('localhost', 389, 3, false, false, false),
+ array(
+ 'host' => 'localhost',
+ 'port' => 389,
+ 'encryption' => 'none',
+ 'options' => array(
+ 'protocol_version' => 3,
+ 'referrals' => false,
+ ),
+ ),
+ ),
+ );
+ }
}
diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php
index 40690c404fdd6..53c4d8781d6ef 100644
--- a/src/Symfony/Component/Process/Process.php
+++ b/src/Symfony/Component/Process/Process.php
@@ -486,10 +486,10 @@ public function getOutput()
* In comparison with the getOutput method which always return the whole
* output, this one returns the new output since the last call.
*
+ * @return string The process output since the last call
+ *
* @throws LogicException in case the output has been disabled
* @throws LogicException In case the process is not started
- *
- * @return string The process output since the last call
*/
public function getIncrementalOutput()
{
@@ -601,10 +601,10 @@ public function getErrorOutput()
* whole error output, this one returns the new error output since the last
* call.
*
+ * @return string The process error output since the last call
+ *
* @throws LogicException in case the output has been disabled
* @throws LogicException In case the process is not started
- *
- * @return string The process error output since the last call
*/
public function getIncrementalErrorOutput()
{
diff --git a/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php
index 7c0cfc9340f49..540d998206047 100644
--- a/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php
+++ b/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php
@@ -103,7 +103,14 @@ private function getStringRepresentation($object)
}
if (!is_object($object)) {
- return sprintf('%s (%s)', gettype($object), $object);
+ if (is_bool($object)) {
+ return sprintf('%s (%s)', gettype($object), $object ? 'true' : 'false');
+ }
+ if (is_scalar($object)) {
+ return sprintf('%s (%s)', gettype($object), $object);
+ }
+
+ return gettype($object);
}
$objectClass = class_exists('Doctrine\Common\Util\ClassUtils') ? ClassUtils::getClass($object) : get_class($object);
diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/DebugAccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/DebugAccessDecisionManagerTest.php
new file mode 100644
index 0000000000000..f90f7769ba4c9
--- /dev/null
+++ b/src/Symfony/Component/Security/Core/Tests/Authorization/DebugAccessDecisionManagerTest.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Core\Tests\Authorization;
+
+use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
+use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+
+class DebugAccessDecisionManagerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider provideObjectsAndLogs
+ */
+ public function testDecideLog($expectedLog, $object)
+ {
+ $adm = new DebugAccessDecisionManager(new AccessDecisionManager());
+ $adm->decide($this->getMock(TokenInterface::class), array('ATTRIBUTE_1'), $object);
+
+ $this->assertSame($expectedLog, $adm->getDecisionLog());
+ }
+
+ public function provideObjectsAndLogs()
+ {
+ $object = new \stdClass();
+
+ yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'NULL', 'result' => false)), null);
+ yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'boolean (true)', 'result' => false)), true);
+ yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'string (jolie string)', 'result' => false)), 'jolie string');
+ yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'integer (12345)', 'result' => false)), 12345);
+ yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'resource', 'result' => false)), fopen(__FILE__, 'r'));
+ yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => 'array', 'result' => false)), array());
+ yield array(array(array('attributes' => array('ATTRIBUTE_1'), 'object' => sprintf('stdClass (object hash: %s)', spl_object_hash($object)), 'result' => false)), $object);
+ }
+}
diff --git a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php
index e722c98e6d687..5eb7668dac1f8 100644
--- a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php
+++ b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php
@@ -42,6 +42,7 @@ class LdapUserProvider implements UserProviderInterface
* @param array $defaultRoles
* @param string $uidKey
* @param string $filter
+ * @param string $passwordAttribute
*/
public function __construct(LdapInterface $ldap, $baseDn, $searchDn = null, $searchPassword = null, array $defaultRoles = array(), $uidKey = 'sAMAccountName', $filter = '({uid_key}={username})', $passwordAttribute = null)
{
diff --git a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php
index f8f17b4f723e6..352ba584a7638 100644
--- a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php
@@ -59,7 +59,7 @@ public function supportsDecoding($format)
*
* @return DecoderInterface
*
- * @throws RuntimeException if no decoder is found
+ * @throws RuntimeException If no decoder is found.
*/
private function getDecoder($format)
{
diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
index 460a6998c57ce..c9fab66558f7b 100644
--- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
@@ -464,6 +464,8 @@ private function needsCdataWrapping($val)
* @param mixed $val
*
* @return bool
+ *
+ * @throws UnexpectedValueException
*/
private function selectNodeType(\DOMNode $node, $val)
{
diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php
index 86c270299ee47..8ad1386a6cbd4 100644
--- a/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php
+++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.php
@@ -33,6 +33,8 @@ interface DenormalizableInterface
* @param string|null $format The format is optionally given to be able to denormalize differently
* based on different input formats
* @param array $context options for denormalizing
+ *
+ * @return object
*/
public function denormalize(DenormalizerInterface $denormalizer, $data, $format = null, array $context = array());
}
diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php
index b9fefe887a44a..e19fe5ce58576 100644
--- a/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php
+++ b/src/Symfony/Component/Serializer/Normalizer/NormalizableInterface.php
@@ -33,7 +33,7 @@ interface NormalizableInterface
* based on different output formats.
* @param array $context Options for normalizing this object
*
- * @return array|string|bool|int|float|null
+ * @return array|scalar
*/
public function normalize(NormalizerInterface $normalizer, $format = null, array $context = array());
}
diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php
index 2a51d631b1624..f4bd355232b0b 100644
--- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php
+++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php
@@ -25,7 +25,7 @@ interface NormalizerInterface
* @param string $format format the normalization result will be encoded as
* @param array $context Context options for the normalizer
*
- * @return array|string|bool|int|float|null
+ * @return array|scalar
*/
public function normalize($object, $format = null, array $context = array());
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php
index 5d91a9fd1e056..8a09e516cd230 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php
@@ -545,6 +545,28 @@ public function testRejectInvalidType()
$serializer->denormalize(array('date' => 'foo'), ObjectOuter::class);
}
+
+ public function testExtractAttributesRespectsFormat()
+ {
+ $normalizer = new FormatAndContextAwareNormalizer();
+
+ $data = new ObjectDummy();
+ $data->setFoo('bar');
+ $data->bar = 'foo';
+
+ $this->assertSame(array('foo' => 'bar', 'bar' => 'foo'), $normalizer->normalize($data, 'foo_and_bar_included'));
+ }
+
+ public function testExtractAttributesRespectsContext()
+ {
+ $normalizer = new FormatAndContextAwareNormalizer();
+
+ $data = new ObjectDummy();
+ $data->setFoo('bar');
+ $data->bar = 'foo';
+
+ $this->assertSame(array('foo' => 'bar', 'bar' => 'foo'), $normalizer->normalize($data, null, array('include_foo_and_bar' => true)));
+ }
}
class ObjectDummy
@@ -744,3 +766,19 @@ class ObjectInner
public $foo;
public $bar;
}
+
+class FormatAndContextAwareNormalizer extends ObjectNormalizer
+{
+ protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array())
+ {
+ if (in_array($attribute, array('foo', 'bar')) && 'foo_and_bar_included' === $format) {
+ return true;
+ }
+
+ if (in_array($attribute, array('foo', 'bar')) && isset($context['include_foo_and_bar']) && true === $context['include_foo_and_bar']) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/Symfony/Component/Stopwatch/StopwatchEvent.php b/src/Symfony/Component/Stopwatch/StopwatchEvent.php
index 930722d2f598c..957a5d0dae0a8 100644
--- a/src/Symfony/Component/Stopwatch/StopwatchEvent.php
+++ b/src/Symfony/Component/Stopwatch/StopwatchEvent.php
@@ -87,8 +87,6 @@ public function start()
/**
* Stops the last started event period.
*
- * @throws \LogicException When start wasn't called before stopping
- *
* @return StopwatchEvent The event
*
* @throws \LogicException When stop() is called without a matching call to start()
diff --git a/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php
index 196bc3341060a..57fd4938d22cf 100644
--- a/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php
+++ b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php
@@ -54,9 +54,9 @@ private function toSplFileInfo($file)
/**
* @param string $file
*
- * @throws \InvalidArgumentException
- *
* @return bool
+ *
+ * @throws \InvalidArgumentException
*/
protected function isFile($file)
{
diff --git a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
index 9c13e2df87900..62ce80eefd64b 100644
--- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
+++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
@@ -165,6 +165,8 @@ private function utf8ToCharset($content, $encoding = null)
}
/**
+ * Validates and parses the given file into a DOMDocument.
+ *
* @param string $file
* @param \DOMDocument $dom
* @param string $schema source of the schema
@@ -175,10 +177,16 @@ private function validateSchema($file, \DOMDocument $dom, $schema)
{
$internalErrors = libxml_use_internal_errors(true);
+ $disableEntities = libxml_disable_entity_loader(false);
+
if (!@$dom->schemaValidateSource($schema)) {
+ libxml_disable_entity_loader($disableEntities);
+
throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $file, implode("\n", $this->getXmlErrors($internalErrors))));
}
+ libxml_disable_entity_loader($disableEntities);
+
$dom->normalizeDocument();
libxml_clear_errors();
diff --git a/src/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php b/src/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php
index 5df3ceeea4aab..9d3bd9cdc2849 100644
--- a/src/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php
+++ b/src/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php
@@ -46,6 +46,20 @@ public function testLoadWithInternalErrorsEnabled()
libxml_use_internal_errors($internalErrors);
}
+ public function testLoadWithExternalEntitiesDisabled()
+ {
+ $disableEntities = libxml_disable_entity_loader(true);
+
+ $loader = new XliffFileLoader();
+ $resource = __DIR__.'/../fixtures/resources.xlf';
+ $catalogue = $loader->load($resource, 'en', 'domain1');
+
+ libxml_disable_entity_loader($disableEntities);
+
+ $this->assertEquals('en', $catalogue->getLocale());
+ $this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
+ }
+
public function testLoadWithResname()
{
$loader = new XliffFileLoader();
diff --git a/src/Symfony/Component/Translation/TranslatorBagInterface.php b/src/Symfony/Component/Translation/TranslatorBagInterface.php
index 6f650b5ee0f88..14fbb17f5e46e 100644
--- a/src/Symfony/Component/Translation/TranslatorBagInterface.php
+++ b/src/Symfony/Component/Translation/TranslatorBagInterface.php
@@ -23,9 +23,9 @@ interface TranslatorBagInterface
*
* @param string|null $locale The locale or null to use the default
*
- * @throws \InvalidArgumentException If the locale contains invalid characters
- *
* @return MessageCatalogueInterface
+ *
+ * @throws \InvalidArgumentException If the locale contains invalid characters
*/
public function getCatalogue($locale = null);
}
diff --git a/src/Symfony/Component/Translation/TranslatorInterface.php b/src/Symfony/Component/Translation/TranslatorInterface.php
index 869e0b900442d..6916c335bd426 100644
--- a/src/Symfony/Component/Translation/TranslatorInterface.php
+++ b/src/Symfony/Component/Translation/TranslatorInterface.php
@@ -26,9 +26,9 @@ interface TranslatorInterface
* @param string|null $domain The domain for the message or null to use the default
* @param string|null $locale The locale or null to use the default
*
- * @throws \InvalidArgumentException If the locale contains invalid characters
- *
* @return string The translated string
+ *
+ * @throws \InvalidArgumentException If the locale contains invalid characters
*/
public function trans($id, array $parameters = array(), $domain = null, $locale = null);
@@ -41,9 +41,9 @@ public function trans($id, array $parameters = array(), $domain = null, $locale
* @param string|null $domain The domain for the message or null to use the default
* @param string|null $locale The locale or null to use the default
*
- * @throws \InvalidArgumentException If the locale contains invalid characters
- *
* @return string The translated string
+ *
+ * @throws \InvalidArgumentException If the locale contains invalid characters
*/
public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null);
diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php
index 0c6cf3c20998a..8a1d574b60a3a 100644
--- a/src/Symfony/Component/Yaml/Dumper.php
+++ b/src/Symfony/Component/Yaml/Dumper.php
@@ -85,7 +85,7 @@ public function dump($input, $inline = 0, $indent = 0, $flags = 0)
if ($inline <= 0 || !is_array($input) || empty($input)) {
$output .= $prefix.Inline::dump($input, $flags);
} else {
- $isAHash = array_keys($input) !== range(0, count($input) - 1);
+ $isAHash = Inline::isHash($input);
foreach ($input as $key => $value) {
if ($inline > 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && is_string($value) && false !== strpos($value, "\n")) {
diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php
index cc994b95626e5..aab8fe3458f80 100644
--- a/src/Symfony/Component/Yaml/Inline.php
+++ b/src/Symfony/Component/Yaml/Inline.php
@@ -214,6 +214,28 @@ public static function dump($value, $flags = 0)
}
}
+ /**
+ * Check if given array is hash or just normal indexed array.
+ *
+ * @internal
+ *
+ * @param array $value The PHP array to check
+ *
+ * @return bool true if value is hash array, false otherwise
+ */
+ public static function isHash(array $value)
+ {
+ $expectedKey = 0;
+
+ foreach ($value as $key => $val) {
+ if ($key !== $expectedKey++) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Dumps a PHP array to a YAML string.
*
@@ -225,11 +247,7 @@ public static function dump($value, $flags = 0)
private static function dumpArray($value, $flags)
{
// array
- $keys = array_keys($value);
- $keysCount = count($keys);
- if ((1 === $keysCount && '0' == $keys[0])
- || ($keysCount > 1 && array_reduce($keys, function ($v, $w) { return (int) $v + $w; }, 0) === $keysCount * ($keysCount - 1) / 2)
- ) {
+ if ($value && !self::isHash($value)) {
$output = array();
foreach ($value as $val) {
$output[] = self::dump($val, $flags);
@@ -238,7 +256,7 @@ private static function dumpArray($value, $flags)
return sprintf('[%s]', implode(', ', $output));
}
- // mapping
+ // hash
$output = array();
foreach ($value as $key => $val) {
$output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php
index 2b5cb79951357..5cf5a88824905 100644
--- a/src/Symfony/Component/Yaml/Parser.php
+++ b/src/Symfony/Component/Yaml/Parser.php
@@ -29,17 +29,21 @@ class Parser
private $currentLineNb = -1;
private $currentLine = '';
private $refs = array();
+ private $skippedLineNumbers = array();
+ private $locallySkippedLineNumbers = array();
/**
* Constructor.
*
* @param int $offset The offset of YAML document (used for line numbers in error messages)
* @param int|null $totalNumberOfLines The overall number of lines being parsed
+ * @param int[] $skippedLineNumbers Number of comment lines that have been skipped by the parser
*/
- public function __construct($offset = 0, $totalNumberOfLines = null)
+ public function __construct($offset = 0, $totalNumberOfLines = null, array $skippedLineNumbers = array())
{
$this->offset = $offset;
$this->totalNumberOfLines = $totalNumberOfLines;
+ $this->skippedLineNumbers = $skippedLineNumbers;
}
/**
@@ -124,25 +128,18 @@ public function parse($value, $flags = 0)
// array
if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
- $c = $this->getRealCurrentLineNb() + 1;
- $parser = new self($c, $this->totalNumberOfLines);
- $parser->refs = &$this->refs;
- $data[] = $parser->parse($this->getNextEmbedBlock(null, true), $flags);
+ $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags);
} else {
if (isset($values['leadspaces'])
&& preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $values['value'], $matches)
) {
// this is a compact notation element, add to next block and parse
- $c = $this->getRealCurrentLineNb();
- $parser = new self($c, $this->totalNumberOfLines);
- $parser->refs = &$this->refs;
-
$block = $values['value'];
if ($this->isNextLineIndented()) {
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
}
- $data[] = $parser->parse($block, $flags);
+ $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags);
} else {
$data[] = $this->parseValue($values['value'], $flags, $context);
}
@@ -198,10 +195,7 @@ public function parse($value, $flags = 0)
} else {
$value = $this->getNextEmbedBlock();
}
- $c = $this->getRealCurrentLineNb() + 1;
- $parser = new self($c, $this->totalNumberOfLines);
- $parser->refs = &$this->refs;
- $parsed = $parser->parse($value, $flags);
+ $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
if (!is_array($parsed)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
@@ -249,10 +243,7 @@ public function parse($value, $flags = 0)
$data[$key] = null;
}
} else {
- $c = $this->getRealCurrentLineNb() + 1;
- $parser = new self($c, $this->totalNumberOfLines);
- $parser->refs = &$this->refs;
- $value = $parser->parse($this->getNextEmbedBlock(), $flags);
+ $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags);
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) {
@@ -346,6 +337,24 @@ public function parse($value, $flags = 0)
return empty($data) ? null : $data;
}
+ private function parseBlock($offset, $yaml, $flags)
+ {
+ $skippedLineNumbers = $this->skippedLineNumbers;
+
+ foreach ($this->locallySkippedLineNumbers as $lineNumber) {
+ if ($lineNumber < $offset) {
+ continue;
+ }
+
+ $skippedLineNumbers[] = $lineNumber;
+ }
+
+ $parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers);
+ $parser->refs = &$this->refs;
+
+ return $parser->parse($yaml, $flags);
+ }
+
/**
* Returns the current line number (takes the offset into account).
*
@@ -353,7 +362,17 @@ public function parse($value, $flags = 0)
*/
private function getRealCurrentLineNb()
{
- return $this->currentLineNb + $this->offset;
+ $realCurrentLineNumber = $this->currentLineNb + $this->offset;
+
+ foreach ($this->skippedLineNumbers as $skippedLineNumber) {
+ if ($skippedLineNumber > $realCurrentLineNumber) {
+ break;
+ }
+
+ ++$realCurrentLineNumber;
+ }
+
+ return $realCurrentLineNumber;
}
/**
@@ -456,6 +475,14 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false)
// we ignore "comment" lines only when we are not inside a scalar block
if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) {
+ // remember ignored comment lines (they are used later in nested
+ // parser calls to determine real line numbers)
+ //
+ // CAUTION: beware to not populate the global property here as it
+ // will otherwise influence the getRealCurrentLineNb() call here
+ // for consecutive comment lines and subsequent embedded blocks
+ $this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb();
+
continue;
}
@@ -491,10 +518,18 @@ private function moveToNextLine()
/**
* Moves the parser to the previous line.
+ *
+ * @return bool
*/
private function moveToPreviousLine()
{
+ if ($this->currentLineNb < 1) {
+ return false;
+ }
+
$this->currentLine = $this->lines[--$this->currentLineNb];
+
+ return true;
}
/**
diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php
index aadb7ff6cb7b6..fb6210dd7d630 100644
--- a/src/Symfony/Component/Yaml/Tests/InlineTest.php
+++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php
@@ -277,6 +277,24 @@ public function testParseUnquotedScalarStartingWithPercentCharacter()
$this->assertContains('Not quoting a scalar starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', $deprecations[0]);
}
+ /**
+ * @dataProvider getDataForIsHash
+ */
+ public function testIsHash($array, $expected)
+ {
+ $this->assertSame($expected, Inline::isHash($array));
+ }
+
+ public function getDataForIsHash()
+ {
+ return array(
+ array(array(), false),
+ array(array(1, 2, 3), false),
+ array(array(2 => 1, 1 => 2, 0 => 3), true),
+ array(array('foo' => 1, 'bar' => 2), true),
+ );
+ }
+
public function getTestsForParse()
{
return array(
@@ -483,6 +501,8 @@ public function getTestsForDump()
array('[foo, { bar: foo, foo: [foo, { bar: foo }] }, [foo, { bar: foo }]]', array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo')))),
array('[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']', array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, '@service_container')),
+
+ array('{ foo: { bar: { 1: 2, baz: 3 } } }', array('foo' => array('bar' => array(1 => 2, 'baz' => 3)))),
);
}
diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php
index 53fe51a09ae2f..c2f4891d4695a 100644
--- a/src/Symfony/Component/Yaml/Tests/ParserTest.php
+++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php
@@ -693,6 +693,25 @@ public function testSequenceInMappingStartedBySingleDashLine()
$this->assertSame($expected, $this->parser->parse($yaml));
}
+ public function testSequenceFollowedByCommentEmbeddedInMapping()
+ {
+ $yaml = << array(
+ 'b' => array('c'),
+ 'd' => 'e',
+ ),
+ );
+
+ $this->assertSame($expected, $this->parser->parse($yaml));
+ }
+
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/
@@ -1243,6 +1262,74 @@ public function testParseDateAsMappingValue()
$this->assertEquals(array('date' => $expectedDate), $this->parser->parse($yaml, Yaml::PARSE_DATETIME));
}
+
+ /**
+ * @param $lineNumber
+ * @param $yaml
+ * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider
+ */
+ public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml)
+ {
+ $this->setExpectedException(
+ '\Symfony\Component\Yaml\Exception\ParseException',
+ sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber)
+ );
+
+ $this->parser->parse($yaml);
+ }
+
+ public function parserThrowsExceptionWithCorrectLineNumberProvider()
+ {
+ return array(
+ array(
+ 4,
+ <<