diff --git a/.travis.yml b/.travis.yml index e746bb7935..0be40d36e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,6 @@ php: matrix: allow_failures: - - php: 5.6 - php: hhvm-nightly services: mongodb diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueValidatorTest.php index 57eaf3295a..c96418de4a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueValidatorTest.php @@ -63,8 +63,12 @@ class UniqueValidatorTest extends \PHPUnit_Framework_TestCase ->method('hasField') ->will($this->returnValue(true)) ; - $refl = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionProperty') + $reflParser = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionParser') ->disableOriginalConstructor() + ->getMock() + ; + $refl = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionProperty') + ->setConstructorArgs(array($reflParser, 'property-name')) ->setMethods(array('getValue')) ->getMock() ; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php index 1b4c8d084a..89d22367a6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php @@ -110,7 +110,12 @@ abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase $options['raw_output'] = true; $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); $this->getDescriptor()->describe($output, $describedObject, $options); - $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); + + if ('json' === $this->getFormat()) { + $this->assertEquals(json_decode($expectedDescription), json_decode($output->fetch())); + } else { + $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); + } } private function getDescriptionTestData(array $objects) diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 49e32a193e..3dd1a7218f 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -25,7 +25,8 @@ "symfony/dependency-injection": "~2.0", "symfony/config": "~2.2", "symfony/routing": "~2.1", - "symfony/templating": "~2.1" + "symfony/templating": "~2.1", + "symfony/framework-bundle": "~2.1" }, "autoload": { "psr-0": { "Symfony\\Bundle\\TwigBundle\\": "" } diff --git a/src/Symfony/Component/ClassLoader/ClassMapGenerator.php b/src/Symfony/Component/ClassLoader/ClassMapGenerator.php index 59c99d0f1b..7f83dbc559 100644 --- a/src/Symfony/Component/ClassLoader/ClassMapGenerator.php +++ b/src/Symfony/Component/ClassLoader/ClassMapGenerator.php @@ -11,6 +11,10 @@ namespace Symfony\Component\ClassLoader; +if (!defined('T_TRAIT')) { + define('T_TRAIT', 0); +} + /** * ClassMapGenerator * @@ -84,7 +88,6 @@ class ClassMapGenerator { $contents = file_get_contents($path); $tokens = token_get_all($contents); - $T_TRAIT = version_compare(PHP_VERSION, '5.4', '<') ? -1 : T_TRAIT; $classes = array(); @@ -111,7 +114,7 @@ class ClassMapGenerator break; case T_CLASS: case T_INTERFACE: - case $T_TRAIT: + case T_TRAIT: // Find the classname while (($t = $tokens[++$i]) && is_array($t)) { if (T_STRING === $t[0]) { diff --git a/src/Symfony/Component/Config/Util/XmlUtils.php b/src/Symfony/Component/Config/Util/XmlUtils.php index 6b3e5f7370..dd68634cc0 100644 --- a/src/Symfony/Component/Config/Util/XmlUtils.php +++ b/src/Symfony/Component/Config/Util/XmlUtils.php @@ -80,7 +80,8 @@ class XmlUtils $valid = false; } } elseif (!is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) { - $valid = @$dom->schemaValidate($schemaOrCallable); + $schemaSource = file_get_contents((string) $schemaOrCallable); + $valid = @$dom->schemaValidateSource($schemaSource); } else { libxml_use_internal_errors($internalErrors); diff --git a/src/Symfony/Component/Console/Helper/DialogHelper.php b/src/Symfony/Component/Console/Helper/DialogHelper.php index 26e9474a9c..1ce4192df6 100644 --- a/src/Symfony/Component/Console/Helper/DialogHelper.php +++ b/src/Symfony/Component/Console/Helper/DialogHelper.php @@ -157,7 +157,7 @@ class DialogHelper extends InputAwareHelper $c .= fread($inputStream, 2); // A = Up Arrow. B = Down Arrow - if ('A' === $c[2] || 'B' === $c[2]) { + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { if ('A' === $c[2] && -1 === $ofs) { $ofs = 0; } diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 7ac2afa0ac..0500b415e6 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -212,7 +212,7 @@ class QuestionHelper extends Helper $c .= fread($inputStream, 2); // A = Up Arrow. B = Down Arrow - if ('A' === $c[2] || 'B' === $c[2]) { + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { if ('A' === $c[2] && -1 === $ofs) { $ofs = 0; } diff --git a/src/Symfony/Component/Console/Question/Question.php b/src/Symfony/Component/Console/Question/Question.php index e47bfb1e92..3003f19c55 100644 --- a/src/Symfony/Component/Console/Question/Question.php +++ b/src/Symfony/Component/Console/Question/Question.php @@ -10,7 +10,6 @@ */ namespace Symfony\Component\Console\Question; -use Doctrine\Common\Proxy\Exception\InvalidArgumentException; /** * Represents a Question. @@ -184,7 +183,7 @@ class Question * * @return Question The current instance * - * @throws InvalidArgumentException In case the number of attempts is invalid. + * @throws \InvalidArgumentException In case the number of attempts is invalid. */ public function setMaxAttempts($attempts) { diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php b/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php index 65ab287770..2231265942 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php @@ -24,6 +24,8 @@ interface ExtensionInterface /** * Returns node translators. * + * These callables will receive the node as first argument and the translator as second argument. + * * @return callable[] */ public function getNodeTranslators(); diff --git a/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php b/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php index f86f2b9672..d71baaa96b 100644 --- a/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php +++ b/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php @@ -29,11 +29,6 @@ class NodeExtension extends AbstractExtension const ATTRIBUTE_NAME_IN_LOWER_CASE = 2; const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4; - /** - * @var Translator - */ - private $translator; - /** * @var int */ @@ -42,12 +37,10 @@ class NodeExtension extends AbstractExtension /** * Constructor. * - * @param Translator $translator - * @param int $flags + * @param int $flags */ - public function __construct(Translator $translator, $flags = 0) + public function __construct($flags = 0) { - $this->translator = $translator; $this->flags = $flags; } @@ -100,33 +93,36 @@ class NodeExtension extends AbstractExtension /** * @param Node\SelectorNode $node + * @param Translator $translator * * @return XPathExpr */ - public function translateSelector(Node\SelectorNode $node) + public function translateSelector(Node\SelectorNode $node, Translator $translator) { - return $this->translator->nodeToXPath($node->getTree()); + return $translator->nodeToXPath($node->getTree()); } /** * @param Node\CombinedSelectorNode $node + * @param Translator $translator * * @return XPathExpr */ - public function translateCombinedSelector(Node\CombinedSelectorNode $node) + public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator) { - return $this->translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector()); + return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector()); } /** * @param Node\NegationNode $node + * @param Translator $translator * * @return XPathExpr */ - public function translateNegation(Node\NegationNode $node) + public function translateNegation(Node\NegationNode $node, Translator $translator) { - $xpath = $this->translator->nodeToXPath($node->getSelector()); - $subXpath = $this->translator->nodeToXPath($node->getSubSelector()); + $xpath = $translator->nodeToXPath($node->getSelector()); + $subXpath = $translator->nodeToXPath($node->getSubSelector()); $subXpath->addNameTest(); if ($subXpath->getCondition()) { @@ -138,34 +134,37 @@ class NodeExtension extends AbstractExtension /** * @param Node\FunctionNode $node + * @param Translator $translator * * @return XPathExpr */ - public function translateFunction(Node\FunctionNode $node) + public function translateFunction(Node\FunctionNode $node, Translator $translator) { - $xpath = $this->translator->nodeToXPath($node->getSelector()); + $xpath = $translator->nodeToXPath($node->getSelector()); - return $this->translator->addFunction($xpath, $node); + return $translator->addFunction($xpath, $node); } /** * @param Node\PseudoNode $node + * @param Translator $translator * * @return XPathExpr */ - public function translatePseudo(Node\PseudoNode $node) + public function translatePseudo(Node\PseudoNode $node, Translator $translator) { - $xpath = $this->translator->nodeToXPath($node->getSelector()); + $xpath = $translator->nodeToXPath($node->getSelector()); - return $this->translator->addPseudoClass($xpath, $node->getIdentifier()); + return $translator->addPseudoClass($xpath, $node->getIdentifier()); } /** * @param Node\AttributeNode $node + * @param Translator $translator * * @return XPathExpr */ - public function translateAttribute(Node\AttributeNode $node) + public function translateAttribute(Node\AttributeNode $node, Translator $translator) { $name = $node->getAttribute(); $safe = $this->isSafeName($name); @@ -181,37 +180,39 @@ class NodeExtension extends AbstractExtension $attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name)); $value = $node->getValue(); - $xpath = $this->translator->nodeToXPath($node->getSelector()); + $xpath = $translator->nodeToXPath($node->getSelector()); if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) { $value = strtolower($value); } - return $this->translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value); + return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value); } /** * @param Node\ClassNode $node + * @param Translator $translator * * @return XPathExpr */ - public function translateClass(Node\ClassNode $node) + public function translateClass(Node\ClassNode $node, Translator $translator) { - $xpath = $this->translator->nodeToXPath($node->getSelector()); + $xpath = $translator->nodeToXPath($node->getSelector()); - return $this->translator->addAttributeMatching($xpath, '~=', '@class', $node->getName()); + return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName()); } /** * @param Node\HashNode $node + * @param Translator $translator * * @return XPathExpr */ - public function translateHash(Node\HashNode $node) + public function translateHash(Node\HashNode $node, Translator $translator) { - $xpath = $this->translator->nodeToXPath($node->getSelector()); + $xpath = $translator->nodeToXPath($node->getSelector()); - return $this->translator->addAttributeMatching($xpath, '=', '@id', $node->getId()); + return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId()); } /** diff --git a/src/Symfony/Component/CssSelector/XPath/Translator.php b/src/Symfony/Component/CssSelector/XPath/Translator.php index 5a8eb99017..4676677ea4 100644 --- a/src/Symfony/Component/CssSelector/XPath/Translator.php +++ b/src/Symfony/Component/CssSelector/XPath/Translator.php @@ -76,7 +76,7 @@ class Translator implements TranslatorInterface $this->mainParser = $parser ?: new Parser(); $this - ->registerExtension(new Extension\NodeExtension($this)) + ->registerExtension(new Extension\NodeExtension()) ->registerExtension(new Extension\CombinationExtension()) ->registerExtension(new Extension\FunctionExtension()) ->registerExtension(new Extension\PseudoClassExtension()) @@ -207,7 +207,7 @@ class Translator implements TranslatorInterface throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName())); } - return call_user_func($this->nodeTranslators[$node->getNodeName()], $node); + return call_user_func($this->nodeTranslators[$node->getNodeName()], $node, $this); } /** diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 54b5216ffb..0b7200ace4 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -674,8 +674,8 @@ class Crawler extends \SplObjectStorage { $translate = 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")'; $xpath = sprintf('descendant-or-self::input[((contains(%s, "submit") or contains(%s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', $translate, $translate, static::xpathLiteral(' '.$value.' ')). - sprintf('or (contains(%s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id="%s" or @name="%s"] ', $translate, static::xpathLiteral(' '.$value.' '), $value, $value). - sprintf('| descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id="%s" or @name="%s"]', static::xpathLiteral(' '.$value.' '), $value, $value); + sprintf('or (contains(%s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id=%s or @name=%s] ', $translate, static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)). + sprintf('| descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id=%s or @name=%s]', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)); return $this->filterRelativeXPath($xpath); } diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index 15bcb7dde7..37b8d836f5 100644 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -569,6 +569,48 @@ EOF $this->assertEquals(1, $crawler->selectButton('FooBarName')->count(), '->selectButton() selects buttons with form attribute too'); } + public function testSelectButtonWithSingleQuotesInNameAttribute() + { + $html = << + + +
+ Login +
+
+ +
+ + +HTML; + + $crawler = new Crawler($html); + + $this->assertCount(1, $crawler->selectButton('Click \'Here\'')); + } + + public function testSelectButtonWithDoubleQuotesInNameAttribute() + { + $html = << + + +
+ Login +
+
+ +
+ + +HTML; + + $crawler = new Crawler($html); + + $this->assertCount(1, $crawler->selectButton('Click "Here"')); + } + public function testLink() { $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo'); diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php index b4b2230189..106c13fb50 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php @@ -96,6 +96,8 @@ class FilesystemTestCase extends \PHPUnit_Framework_TestCase if ($datas = posix_getgrgid($infos['gid'])) { return $datas['name']; } + + $this->markTestSkipped('Unable to retrieve file group name'); } protected function markAsSkippedIfSymlinkIsMissing() diff --git a/src/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php b/src/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php index 01940385de..5c7206240e 100644 --- a/src/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php +++ b/src/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php @@ -256,6 +256,7 @@ abstract class AbstractFindAdapter extends AbstractAdapter case '!=': $command->add('-size -'.$size->getTarget().'c'); $command->add('-size +'.$size->getTarget().'c'); + break; case '<': default: $command->add('-size -'.$size->getTarget().'c'); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php index 0227af4f23..96ec0c30a8 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php @@ -78,7 +78,7 @@ class FileTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { $file = $this ->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') - ->disableOriginalConstructor() + ->setConstructorArgs(array(__DIR__.'/../../../Fixtures/foo', 'foo')) ->getMock() ; $file diff --git a/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php b/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php index 2d5cf7764b..cf5d63d90e 100644 --- a/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Form\Tests\Extension\HttpFoundation; use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; use Symfony\Component\Form\Tests\AbstractRequestHandlerTest; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\File\UploadedFile; /** * @author Bernhard Schussek @@ -47,8 +48,6 @@ class HttpFoundationRequestHandlerTest extends AbstractRequestHandlerTest protected function getMockFile() { - return $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') - ->disableOriginalConstructor() - ->getMock(); + return new UploadedFile(__DIR__.'/../../Fixtures/foo', 'foo'); } } diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index e79ad63c82..f59edf5327 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -358,6 +358,7 @@ class Request if (!isset($server['CONTENT_TYPE'])) { $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; } + // no break case 'PATCH': $request = $parameters; $query = array(); @@ -955,7 +956,13 @@ class Request } if ($host = $this->headers->get('HOST')) { - if (false !== $pos = strrpos($host, ':')) { + if ($host[0] === '[') { + $pos = strpos($host, ':', strrpos($host, ']')); + } else { + $pos = strrpos($host, ':'); + } + + if (false !== $pos) { return intval(substr($host, $pos + 1)); } diff --git a/src/Symfony/Component/HttpFoundation/Resources/stubs/FakeFile.php b/src/Symfony/Component/HttpFoundation/Resources/stubs/FakeFile.php new file mode 100644 index 0000000000..0aecc20b08 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Resources/stubs/FakeFile.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Resources\stubs; + +use Symfony\Component\HttpFoundation\File\File as OrigFile; + +class FakeFile extends OrigFile +{ + private $realpath; + + public function __construct($realpath, $path) + { + $this->realpath = $realpath; + parent::__construct($path, false); + } + + public function isReadable() + { + return true; + } + + public function getRealpath() + { + return $this->realpath; + } + + public function getSize() + { + return 42; + } + + public function getMTime() + { + return time(); + } +} diff --git a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php index 69b099885a..397192b885 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\HttpFoundation\Tests; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\Resources\stubs\FakeFile; class BinaryFileResponseTest extends ResponseTestCase { @@ -179,18 +180,7 @@ class BinaryFileResponseTest extends ResponseTestCase $request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect'); $request->headers->set('X-Accel-Mapping', $mapping); - $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\File') - ->disableOriginalConstructor() - ->getMock(); - $file->expects($this->any()) - ->method('getRealPath') - ->will($this->returnValue($realpath)); - $file->expects($this->any()) - ->method('isReadable') - ->will($this->returnValue(true)); - $file->expects($this->any()) - ->method('getMTime') - ->will($this->returnValue(time())); + $file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test'); BinaryFileResponse::trustXSendFileTypeHeader(); $response = new BinaryFileResponse($file); diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 556c9dde8a..ea9e0ceb84 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -163,6 +163,14 @@ class RequestTest extends \PHPUnit_Framework_TestCase $this->assertEquals(90, $request->getPort()); $this->assertTrue($request->isSecure()); + $request = Request::create('https://[::1]/foo'); + $this->assertEquals('https://[::1]/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('[::1]', $request->getHost()); + $this->assertEquals('[::1]', $request->getHttpHost()); + $this->assertEquals(443, $request->getPort()); + $this->assertTrue($request->isSecure()); + $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}'; $request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json); $this->assertEquals($json, $request->getContent()); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php index d907ce4a90..2feb6a83c0 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -34,7 +34,6 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; $this->mongo = $this->getMockBuilder($mongoClass) - ->disableOriginalConstructor() ->getMock(); $this->options = array( @@ -76,9 +75,7 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase public function testWrite() { - $collection = $this->getMockBuilder('MongoCollection') - ->disableOriginalConstructor() - ->getMock(); + $collection = $this->createMongoCollectionMock(); $this->mongo->expects($this->once()) ->method('selectCollection') @@ -105,9 +102,7 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase public function testReplaceSessionData() { - $collection = $this->getMockBuilder('MongoCollection') - ->disableOriginalConstructor() - ->getMock(); + $collection = $this->createMongoCollectionMock(); $this->mongo->expects($this->once()) ->method('selectCollection') @@ -130,9 +125,7 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase public function testDestroy() { - $collection = $this->getMockBuilder('MongoCollection') - ->disableOriginalConstructor() - ->getMock(); + $collection = $this->createMongoCollectionMock(); $this->mongo->expects($this->once()) ->method('selectCollection') @@ -148,9 +141,7 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase public function testGc() { - $collection = $this->getMockBuilder('MongoCollection') - ->disableOriginalConstructor() - ->getMock(); + $collection = $this->createMongoCollectionMock(); $this->mongo->expects($this->once()) ->method('selectCollection') @@ -178,4 +169,19 @@ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf($mongoClass, $method->invoke($this->storage)); } + + private function createMongoCollectionMock() + { + + $mongoClient = $this->getMockBuilder('MongoClient') + ->getMock(); + $mongoDb = $this->getMockBuilder('MongoDB') + ->setConstructorArgs(array($mongoClient, 'database-name')) + ->getMock(); + $collection = $this->getMockBuilder('MongoCollection') + ->setConstructorArgs(array($mongoDb, 'collection-name')) + ->getMock(); + + return $collection; + } } diff --git a/src/Symfony/Component/Process/ExecutableFinder.php b/src/Symfony/Component/Process/ExecutableFinder.php index 5cc99c7692..44f1605fa9 100644 --- a/src/Symfony/Component/Process/ExecutableFinder.php +++ b/src/Symfony/Component/Process/ExecutableFinder.php @@ -53,7 +53,7 @@ class ExecutableFinder public function find($name, $default = null, array $extraDirs = array()) { if (ini_get('open_basedir')) { - $searchPath = explode(PATH_SEPARATOR, getenv('open_basedir')); + $searchPath = explode(PATH_SEPARATOR, ini_get('open_basedir')); $dirs = array(); foreach ($searchPath as $path) { if (is_dir($path)) { diff --git a/src/Symfony/Component/Process/ProcessBuilder.php b/src/Symfony/Component/Process/ProcessBuilder.php index 40b8d7011a..125da2593e 100644 --- a/src/Symfony/Component/Process/ProcessBuilder.php +++ b/src/Symfony/Component/Process/ProcessBuilder.php @@ -219,7 +219,7 @@ class ProcessBuilder /** * Disables fetching output and error output from the underlying process. * - * @return Process + * @return ProcessBuilder */ public function disableOutput() { @@ -231,7 +231,7 @@ class ProcessBuilder /** * Enables fetching output and error output from the underlying process. * - * @return Process + * @return ProcessBuilder */ public function enableOutput() { diff --git a/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php new file mode 100644 index 0000000000..abaa47d8dd --- /dev/null +++ b/src/Symfony/Component/Process/Tests/ExecutableFinderTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\ExecutableFinder; + +/** + * @author Chris Smith + */ +class ExecutableFinderTest extends \PHPUnit_Framework_TestCase +{ + private $path; + + public function tearDown() + { + if ($this->path) { + // Restore path if it was changed. + putenv('PATH='.$this->path); + } + } + + private function setPath($path) + { + $this->path = getenv('PATH'); + putenv('PATH='.$path); + } + + public function testFind() + { + if (!defined('PHP_BINARY')) { + $this->markTestSkipped('Requires the PHP_BINARY constant'); + } + + if (ini_get('open_basedir')) { + $this->markTestSkipped('Cannot test when open_basedir is set'); + } + + $this->setPath(dirname(PHP_BINARY)); + + $finder = new ExecutableFinder; + $result = $finder->find(basename(PHP_BINARY)); + + $this->assertEquals($result, PHP_BINARY); + } + + public function testFindWithDefault() + { + if (ini_get('open_basedir')) { + $this->markTestSkipped('Cannot test when open_basedir is set'); + } + + $expected = 'defaultValue'; + + $this->setPath(''); + + $finder = new ExecutableFinder; + $result = $finder->find('foo', $expected); + + $this->assertEquals($expected, $result); + } + + public function testFindWithExtraDirs() + { + if (!defined('PHP_BINARY')) { + $this->markTestSkipped('Requires the PHP_BINARY constant'); + } + + if (ini_get('open_basedir')) { + $this->markTestSkipped('Cannot test when open_basedir is set'); + } + + $this->setPath(''); + + $extraDirs = array(dirname(PHP_BINARY)); + + $finder = new ExecutableFinder; + $result = $finder->find(basename(PHP_BINARY), null, $extraDirs); + + $this->assertEquals(PHP_BINARY, $result); + } + + public function testFindWithOpenBaseDir() + { + if (!defined('PHP_BINARY')) { + $this->markTestSkipped('Requires the PHP_BINARY constant'); + } + + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Cannot run test on windows'); + } + + if (ini_get('open_basedir')) { + $this->markTestSkipped('Cannot test when open_basedir is set'); + } + + ini_set('open_basedir', dirname(PHP_BINARY).PATH_SEPARATOR.'/'); + + $finder = new ExecutableFinder; + $result = $finder->find(basename(PHP_BINARY)); + + $this->assertEquals(PHP_BINARY, $result); + } +} diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php index f63ef79c2d..df25874d1e 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php @@ -129,7 +129,7 @@ class AuthenticationProviderManagerTest extends \PHPUnit_Framework_TestCase } elseif (null !== $exception) { $provider->expects($this->once()) ->method('authenticate') - ->will($this->throwException($this->getMock($exception, null, array(), '', true))) + ->will($this->throwException($this->getMock($exception, null, array(), ''))) ; } diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index 1f45bf053b..9f2374266c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -143,7 +143,9 @@ class GetSetMethodNormalizer extends SerializerAwareNormalizer implements Normal $params[] = $data[$paramName]; // don't run set for a parameter passed to the constructor unset($data[$paramName]); - } elseif (!$constructorParameter->isOptional()) { + } elseif ($constructorParameter->isOptional()) { + $params[] = $constructorParameter->getDefaultValue(); + } else { throw new RuntimeException( 'Cannot create an instance of '.$class. ' from serialized data because its constructor requires '. diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php index 99a2e37c4c..2ec68c0a0d 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php @@ -109,6 +109,16 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase $this->assertTrue($obj->isBaz()); } + public function testConstructorDenormalizeWithMissingOptionalArgument() + { + $obj = $this->normalizer->denormalize( + array('foo' => 'test', 'baz' => array(1, 2, 3)), + __NAMESPACE__.'\GetConstructorOptionalArgsDummy', 'any'); + $this->assertEquals('test', $obj->getFoo()); + $this->assertEquals(array(), $obj->getBar()); + $this->assertEquals(array(1, 2, 3), $obj->getBaz()); + } + /** * @dataProvider provideCallbacks */ @@ -336,3 +346,37 @@ class GetConstructorDummy abstract class SerializerNormalizer implements SerializerInterface, NormalizerInterface { } + +class GetConstructorOptionalArgsDummy +{ + protected $foo; + private $bar; + private $baz; + + public function __construct($foo, $bar = array(), $baz = array()) + { + $this->foo = $foo; + $this->bar = $bar; + $this->baz = $baz; + } + + public function getFoo() + { + return $this->foo; + } + + public function getBar() + { + return $this->bar; + } + + public function getBaz() + { + return $this->baz; + } + + public function otherMethod() + { + throw new \RuntimeException("Dummy::otherMethod() should not be called"); + } +} diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf index 41b0e3b19a..58737da20f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.lt.xlf @@ -278,6 +278,30 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. Ši reikšmė neturi būti identiška {{ compared_value_type }} {{ compared_value }}. + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + Nuotraukos santykis yra per didelis ({{ ratio }}). Didžiausias leistinas santykis yra {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + Nuotraukos santykis yra per mažas ({{ ratio }}). Mažiausias leistinas santykis yra {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + Nuotrauka yra kvadratinė ({{ width }}x{{ height }}px). Kvadratinės nuotraukos nėra leistinos. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + Nuotrauka orientuota į plotį ({{ width }}x{{ height }}px). Nuotraukos orientuotos į plotį nėra leistinos. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + Nuotrauka orientuota į aukštį ({{ width }}x{{ height }}px). Nuotraukos orientuotos į aukštį nėra leistinos. + + + An empty file is not allowed. + Failas negali būti tuščias. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf index 1caed4eef7..65aef859ad 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf @@ -278,6 +278,26 @@ This value should not be identical to {{ compared_value }}. Deze waarde mag niet identiek zijn aan {{ compared_value }}. + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + De afbeeldingsverhouding is te groot ({{ ratio }}). Maximale verhouding is {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + De afbeeldingsverhouding is te klein ({{ ratio }}). Minimale verhouding is {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + De afbeelding is vierkant ({{ width }}x{{ height }}px). Vierkante afbeeldingen zijn niet toegestaan. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + De afbeelding is liggend ({{ width }}x{{ height }}px). Liggende afbeeldingen zijn niet toegestaan. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + De afbeelding is staand ({{ width }}x{{ height }}px). Staande afbeeldingen zijn niet toegestaan. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf index 354ecc6cc9..6332c4886f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pt.xlf @@ -278,6 +278,30 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. Este valor não deve ser idêntico a {{ compared_value_type }} {{ compared_value }}. + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + O formato da imagem é muito grande ({{ ratio }}). O formato máximo é {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + O formato da imagem é muito pequeno ({{ ratio }}). O formato mínimo esperado é {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + A imagem é um quadrado ({{ width }}x{{ height }}px). Imagens quadradas não são permitidas. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + A imagem está orientada à paisagem ({{ width }}x{{ height }}px). Imagens orientadas à paisagem não são permitidas. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + A imagem está orientada ao retrato ({{ width }}x{{ height }}px). Imagens orientadas ao retrato não são permitidas. + + + An empty file is not allowed. + Ficheiro vazio não é permitido. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf index f8b0d9b248..f74fe90615 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf @@ -278,6 +278,30 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. Este valor não deve ser idêntico a {{ compared_value_type }} {{ compared_value }}. + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + O formato da imagem é muito grande ({{ ratio }}). O formato máximo é {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + O formato da imagem é muito pequeno ({{ ratio }}). O formato mínimo esperado é {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + A imagem está num formato quadrado ({{ width }}x{{ height }}px). Imagens com formato quadrado não são permitidas. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + A imagem está orientada à paisagem ({{ width }}x{{ height }}px). Imagens orientadas à paisagem não são permitidas. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + A imagem está orientada ao retrato ({{ width }}x{{ height }}px). Imagens orientadas ao retrato não são permitidas. + + + An empty file is not allowed. + Ficheiro vazio não é permitido. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf index 6205891ce3..0dd06e7897 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf @@ -278,6 +278,26 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. Значение не должно быть идентичным {{ compared_value_type }} {{ compared_value }}. + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + Соотношение сторон изображения слишком велико ({{ ratio }}). Максимальное соотношение сторон {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + Соотношение сторон изображения слишком мало ({{ ratio }}). Минимальное соотношение сторон {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + Изображение квадратное ({{ width }}x{{ height }}px). Квадратные изображения не разрешены. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + Изображение в альбомной ориентации ({{ width }}x{{ height }}px). Изображения в альбомной ориентации не разрешены. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + Изображение в портретной ориентации ({{ width }}x{{ height }}px). Изображения в портретной ориентации не разрешены. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf index 5286002d5d..04c6335a97 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf @@ -278,6 +278,30 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. Táto hodnota by nemala byť typu {{ compared_value_type }} a zároveň by nemala byť rovná {{ compared_value }}. + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + Pomer strán obrázku je príliš veľký ({{ ratio }}). Maximálny povolený pomer strán obrázku je {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + Pomer strán obrázku je príliš malý ({{ ratio }}). Minimálny povolený pomer strán obrázku je {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + Strany obrázku sú štvorcové ({{ width }}x{{ height }}px). Štvorcové obrázky nie sú povolené. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + Obrázok je orientovaný na šírku ({{ width }}x{{ height }}px). Obrázky orientované na šírku nie sú povolené. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + Obrázok je orientovaný na výšku ({{ width }}x{{ height }}px). Obrázky orientované na výšku nie sú povolené. + + + An empty file is not allowed. + Súbor nesmie byť prázdny. + diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php index d2d2bcb34f..8b9ce99b80 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php @@ -199,7 +199,7 @@ abstract class FileValidatorTest extends \PHPUnit_Framework_TestCase { $file = $this ->getMockBuilder('Symfony\Component\HttpFoundation\File\File') - ->disableOriginalConstructor() + ->setConstructorArgs(array(__DIR__.'/Fixtures/foo')) ->getMock() ; $file @@ -227,7 +227,7 @@ abstract class FileValidatorTest extends \PHPUnit_Framework_TestCase { $file = $this ->getMockBuilder('Symfony\Component\HttpFoundation\File\File') - ->disableOriginalConstructor() + ->setConstructorArgs(array(__DIR__.'/Fixtures/foo')) ->getMock() ; $file @@ -255,7 +255,7 @@ abstract class FileValidatorTest extends \PHPUnit_Framework_TestCase { $file = $this ->getMockBuilder('Symfony\Component\HttpFoundation\File\File') - ->disableOriginalConstructor() + ->setConstructorArgs(array(__DIR__.'/Fixtures/foo')) ->getMock() ; $file @@ -289,7 +289,7 @@ abstract class FileValidatorTest extends \PHPUnit_Framework_TestCase { $file = $this ->getMockBuilder('Symfony\Component\HttpFoundation\File\File') - ->disableOriginalConstructor() + ->setConstructorArgs(array(__DIR__.'/Fixtures/foo')) ->getMock() ; $file diff --git a/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/foo b/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/foo new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 9539e36aa3..0577b5e885 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -66,6 +66,7 @@ class Parser $data = array(); $context = null; + $allowOverwrite = false; while ($this->moveToNextLine()) { if ($this->isCurrentLineEmpty()) { continue; @@ -76,7 +77,7 @@ class Parser throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } - $isRef = $isInPlace = $isProcessed = false; + $isRef = $mergeNode = false; if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) { if ($context && 'mapping' == $context) { throw new ParseException('You cannot define a sequence item when in a mapping'); @@ -132,10 +133,24 @@ class Parser } if ('<<' === $key) { + $mergeNode = true; + $allowOverwrite = true; if (isset($values['value']) && 0 === strpos($values['value'], '*')) { - $isInPlace = substr($values['value'], 1); - if (!array_key_exists($isInPlace, $this->refs)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $isInPlace), $this->getRealCurrentLineNb() + 1, $this->currentLine); + $refName = substr($values['value'], 1); + if (!array_key_exists($refName, $this->refs)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + $refValue = $this->refs[$refName]; + + if (!is_array($refValue)) { + throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + foreach ($refValue as $key => $value) { + if (!isset($data[$key])) { + $data[$key] = $value; + } } } else { if (isset($values['value']) && $values['value'] !== '') { @@ -148,40 +163,49 @@ class Parser $parser->refs =& $this->refs; $parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport); - $merged = array(); if (!is_array($parsed)) { throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } elseif (isset($parsed[0])) { - // Numeric array, merge individual elements - foreach (array_reverse($parsed) as $parsedItem) { + } + + if (isset($parsed[0])) { + // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes + // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier + // in the sequence override keys specified in later mapping nodes. + foreach ($parsed as $parsedItem) { if (!is_array($parsedItem)) { throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); } - $merged = array_merge($parsedItem, $merged); + + foreach ($parsedItem as $key => $value) { + if (!isset($data[$key])) { + $data[$key] = $value; + } + } } } else { - // Associative array, merge - $merged = array_merge($merged, $parsed); + // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the + // current mapping, unless the key already exists in it. + foreach ($parsed as $key => $value) { + if (!isset($data[$key])) { + $data[$key] = $value; + } + } } - - $isProcessed = $merged; } } elseif (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $values['value'] = $matches['value']; } - if ($isProcessed) { + if ($mergeNode) { // Merge keys - $data = $isProcessed; - // hash } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { + // hash // if next line is less indented or equal, then it means that the current value is null if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { // Spec: Keys MUST be unique; first one wins. - // Parser cannot abort this mapping earlier, since lines - // are processed sequentially. - if (!isset($data[$key])) { + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { $data[$key] = null; } } else { @@ -190,23 +214,17 @@ class Parser $parser->refs =& $this->refs; $value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport); // Spec: Keys MUST be unique; first one wins. - // Parser cannot abort this mapping earlier, since lines - // are processed sequentially. - if (!isset($data[$key])) { + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { $data[$key] = $value; } } } else { - if ($isInPlace) { - $data = $this->refs[$isInPlace]; - } else { - $value = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);; - // Spec: Keys MUST be unique; first one wins. - // Parser cannot abort this mapping earlier, since lines - // are processed sequentially. - if (!isset($data[$key])) { - $data[$key] = $value; - } + $value = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport); + // 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])) { + $data[$key] = $value; } } } else { diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml index 3eec4f877d..fd9910174d 100644 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml @@ -10,9 +10,19 @@ yaml: | a: Steve b: Clark c: Brian - bar: &bar + bar: + a: before + d: other <<: *foo + b: new x: Oren + c: + foo: bar + foo: ignore + bar: foo + duplicate: + foo: bar + foo: ignore foo2: &foo2 a: Ballmer ding: &dong [ fi, fei, fo, fam] @@ -24,4 +34,12 @@ yaml: | head: <<: [ *foo , *dong , *foo2 ] php: | - array('foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian'), 'bar' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'x' => 'Oren'), 'foo2' => array('a' => 'Ballmer'), 'ding' => array('fi', 'fei', 'fo', 'fam'), 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), 'head' => array('a' => 'Ballmer', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam')) + array( + 'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian'), + 'bar' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), + 'duplicate' => array('foo' => 'bar'), + 'foo2' => array('a' => 'Ballmer'), + 'ding' => array('fi', 'fei', 'fo', 'fam'), + 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), + 'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam') + )