diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index a8bb2ec3d079b..8ffdb294bfb68 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -116,7 +116,7 @@ public function run(InputInterface $input = null, OutputInterface $output = null } $statusCode = $e->getCode(); - $statusCode = is_numeric($statusCode) && $statusCode ? $statusCode : 1; + $statusCode = $statusCode ? (is_numeric($statusCode) ? (int) $statusCode : 1) : 0; } if ($this->autoExit) { @@ -193,7 +193,7 @@ public function doRun(InputInterface $input, OutputInterface $output) $statusCode = $command->run($input, $output); $this->runningCommand = null; - return is_numeric($statusCode) ? $statusCode : 0; + return $statusCode; } /** diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index f526b85a03aac..963e777dfee0d 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -242,7 +242,7 @@ public function run(InputInterface $input, OutputInterface $output) $statusCode = $this->execute($input, $output); } - return is_numeric($statusCode) ? $statusCode : 0; + return is_numeric($statusCode) ? (int) $statusCode : 0; } /** diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 2d4f6c43be61c..4511e90c9e9dd 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -514,6 +514,21 @@ public function testRun() $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); } + public function testRunReturnsIntegerExitCode() + { + $exception = new \Exception('', 4); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception'); + } + /** * @expectedException \LogicException * @dataProvider getAddingAlreadySetDefinitionElementData diff --git a/src/Symfony/Component/Console/Tests/Command/CommandTest.php b/src/Symfony/Component/Console/Tests/Command/CommandTest.php index 8300f8dfe33b0..885afed382493 100644 --- a/src/Symfony/Component/Console/Tests/Command/CommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -244,6 +244,20 @@ public function testRun() } } + public function testRunReturnsIntegerExitCode() + { + $command = new \TestCommand(); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)'); + + $command = $this->getMock('TestCommand', array('execute')); + $command->expects($this->once()) + ->method('execute') + ->will($this->returnValue('2.3')); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)'); + } + public function testRunReturnsAlwaysInteger() { $command = new \TestCommand(); diff --git a/src/Symfony/Component/CssSelector/Node/FunctionNode.php b/src/Symfony/Component/CssSelector/Node/FunctionNode.php index 8736c27c05fc2..4cc3a949d482e 100644 --- a/src/Symfony/Component/CssSelector/Node/FunctionNode.php +++ b/src/Symfony/Component/CssSelector/Node/FunctionNode.php @@ -100,43 +100,46 @@ protected function _xpath_nth_child($xpath, $expr, $last = false, $addNameTest = $xpath->addStarPrefix(); if ($a == 0) { if ($last) { - $b = sprintf('last() - %s', $b); + $b = sprintf('last() - %s', $b - 1); } $xpath->addCondition(sprintf('position() = %s', $b)); return $xpath; } - if ($last) { - // FIXME: I'm not sure if this is right - $a = -$a; - $b = -$b; - } + if ($a < 0) { + if ($b < 1) { + $xpath->addCondition('false()'); - if ($b > 0) { - $bNeg = -$b; + return $xpath; + } + + $sign = '<='; } else { - $bNeg = sprintf('+%s', -$b); + $sign = '>='; } - if ($a != 1) { - $expr = array(sprintf('(position() %s) mod %s = 0', $bNeg, $a)); - } else { - $expr = array(); + $expr = 'position()'; + + if ($last) { + $expr = 'last() - '.$expr; + $b--; } - if ($b >= 0) { - $expr[] = sprintf('position() >= %s', $b); - } elseif ($b < 0 && $last) { - $expr[] = sprintf('position() < (last() %s)', $b); + if (0 !== $b) { + $expr .= ' - '.$b; } - $expr = implode($expr, ' and '); + + $conditions = array(sprintf('%s %s 0', $expr, $sign)); - if ($expr) { - $xpath->addCondition($expr); + if (1 !== $a && -1 !== $a) { + $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a); } + $xpath->addCondition(implode(' and ', $conditions)); + return $xpath; + /* FIXME: handle an+b, odd, even an+b means every-a, plus b, e.g., 2n+1 means odd 0n+b means b diff --git a/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php b/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php index 0d9ca851aac0e..12f93fc035ee6 100644 --- a/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php +++ b/src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php @@ -54,7 +54,7 @@ public function getCssSelectors() array('h1', "h1"), array('foo|h1', "foo:h1"), array('h1, h2, h3', "h1 | h2 | h3"), - array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and ((position() -1) mod 3 = 0 and position() >= 1)]"), + array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]"), array('h1 > p', "h1/p"), array('h1#foo', "h1[@id = 'foo']"), array('h1.foo', "h1[contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), diff --git a/src/Symfony/Component/CssSelector/Tests/Node/FunctionNodeTest.php b/src/Symfony/Component/CssSelector/Tests/Node/FunctionNodeTest.php index 9654402ae0235..23442f1dfa939 100644 --- a/src/Symfony/Component/CssSelector/Tests/Node/FunctionNodeTest.php +++ b/src/Symfony/Component/CssSelector/Tests/Node/FunctionNodeTest.php @@ -36,12 +36,12 @@ public function testToXpath() // h1:nth-child(odd) $element2 = new ElementNode('*', new Token('Symbol', 'odd', -1)); $function = new FunctionNode($element, ':', 'nth-child', $element2); - $this->assertEquals("*/*[name() = 'h1' and ((position() -1) mod 2 = 0 and position() >= 1)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); + $this->assertEquals("*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 2 = 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); // h1:nth-child(even) $element2 = new ElementNode('*', new Token('Symbol', 'even', -1)); $function = new FunctionNode($element, ':', 'nth-child', $element2); - $this->assertEquals("*/*[name() = 'h1' and ((position() +0) mod 2 = 0 and position() >= 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); + $this->assertEquals("*/*[name() = 'h1' and (position() >= 0 and (position()) mod 2 = 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); // h1:nth-child(n) $element2 = new ElementNode('*', new Token('Symbol', 'n', -1)); @@ -51,12 +51,12 @@ public function testToXpath() // h1:nth-child(3n+1) $element2 = new ElementNode('*', new Token('Symbol', '3n+1', -1)); $function = new FunctionNode($element, ':', 'nth-child', $element2); - $this->assertEquals("*/*[name() = 'h1' and ((position() -1) mod 3 = 0 and position() >= 1)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); + $this->assertEquals("*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); // h1:nth-child(n+1) $element2 = new ElementNode('*', new Token('Symbol', 'n+1', -1)); $function = new FunctionNode($element, ':', 'nth-child', $element2); - $this->assertEquals("*/*[name() = 'h1' and (position() >= 1)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); + $this->assertEquals("*/*[name() = 'h1' and (position() - 1 >= 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); // h1:nth-child(1) $element2 = new ElementNode('*', new Token('Symbol', '2', -1)); @@ -66,16 +66,16 @@ public function testToXpath() // h1:nth-child(2n) $element2 = new ElementNode('*', new Token('Symbol', '2n', -1)); $function = new FunctionNode($element, ':', 'nth-child', $element2); - $this->assertEquals("*/*[name() = 'h1' and ((position() +0) mod 2 = 0 and position() >= 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); + $this->assertEquals("*/*[name() = 'h1' and (position() >= 0 and (position()) mod 2 = 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); // h1:nth-child(-n) $element2 = new ElementNode('*', new Token('Symbol', '-n', -1)); $function = new FunctionNode($element, ':', 'nth-child', $element2); - $this->assertEquals("*/*[name() = 'h1' and ((position() +0) mod -1 = 0 and position() >= 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); + $this->assertEquals("*/*[name() = 'h1' and (false())]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); // h1:nth-last-child(2) $function = new FunctionNode($element, ':', 'nth-last-child', 2); - $this->assertEquals("*/*[name() = 'h1' and (position() = last() - 2)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); + $this->assertEquals("*/*[name() = 'h1' and (position() = last() - 1)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); // h1:nth-of-type(2) $function = new FunctionNode($element, ':', 'nth-of-type', 2); @@ -83,7 +83,7 @@ public function testToXpath() // h1:nth-last-of-type(2) $function = new FunctionNode($element, ':', 'nth-last-of-type', 2); - $this->assertEquals("*/h1[position() = last() - 2]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); + $this->assertEquals("*/h1[position() = last() - 1]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node'); /* // h1:not(p) diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 56a84f57730d3..c56ba2afe2eaf 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -432,7 +432,9 @@ public function children() throw new \InvalidArgumentException('The current node list is empty.'); } - return new static($this->sibling($this->getNode(0)->firstChild), $this->uri); + $node = $this->getNode(0)->firstChild; + + return new static($node ? $this->sibling($node) : array(), $this->uri); } /** diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index 8aac5d082fa64..4411c7d443bf0 100644 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -536,6 +536,14 @@ public function testChildren() } catch (\InvalidArgumentException $e) { $this->assertTrue(true, '->children() throws an \InvalidArgumentException if the node list is empty'); } + + try { + $crawler = new Crawler('
'); + $crawler->filter('p')->children(); + $this->assertTrue(true, '->children() does not trigger a notice if the node has no children'); + } catch (\PHPUnit_Framework_Error_Notice $e) { + $this->fail('->children() does not trigger a notice if the node has no children'); + } } public function testParents() diff --git a/src/Symfony/Component/Process/ProcessBuilder.php b/src/Symfony/Component/Process/ProcessBuilder.php index c4ef31d3112ef..151340d336e4c 100644 --- a/src/Symfony/Component/Process/ProcessBuilder.php +++ b/src/Symfony/Component/Process/ProcessBuilder.php @@ -151,6 +151,9 @@ public function getProcess() $env = $this->env; } + // Process can not handle env values that are arrays + $env = $env ? array_filter($env, function ($value) { if (!is_array($value)) { return true; } }) : $env; + return new Process($script, $this->cwd, $env, $this->stdin, $this->timeout, $options); } } diff --git a/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php b/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php index 53e403605b45a..1d46d71a57de0 100644 --- a/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessBuilderTest.php @@ -45,6 +45,22 @@ public function testProcessShouldInheritAndOverrideEnvironmentVars() $_ENV = $snapshot; } + public function testProcessBuilderShouldNotPassEnvArrays() + { + $snapshot = $_ENV; + $_ENV = array('a' => array('b', 'c'), 'd' => 'e', 'f' => 'g'); + $expected = array('d' => 'e', 'f' => 'g'); + + $pb = new ProcessBuilder(); + $pb->add('a')->inheritEnvironmentVariables() + ->setEnv('d', 'e'); + $proc = $pb->getProcess(); + + $this->assertEquals($expected, $proc->getEnv(), '->inheritEnvironmentVariables() removes array values from $_ENV'); + + $_ENV = $snapshot; + } + public function testInheritEnvironmentVarsByDefault() { $pb = new ProcessBuilder();