Merge branch '2.7' into 2.8

* 2.7:
  [SecurityBundle] Removing test insulations for a huge perf win
  [Validator] Use the new interface in the README
  [Filesystem] fix tests on 2.3
  [Filesystem] Recursivly widen non-executable directories
  [Form] fix #15544 when a collection type attribute "required" is false, "prototype" should too
  updated validators.bg.xlf
  [Security] Enable bcrypt validation and result length tests on all PHP versions
  [Security] Verify if a password encoded with bcrypt is no longer than 72 characters
  [Console] Avoid extra blank lines when rendering exceptions
  [Console][Table] fixed render row with multiple cells.
  [Yaml] do not remove "comments" in scalar blocks

Conflicts:
	src/Symfony/Component/Console/Application.php
	src/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt
	src/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt
	src/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt
	src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php
	src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php
	src/Symfony/Component/Yaml/Tests/ParserTest.php
This commit is contained in:
Nicolas Grekas 2015-12-22 11:25:57 +01:00
commit d70ff0abd4
31 changed files with 276 additions and 130 deletions

View File

@ -16,7 +16,6 @@ class AuthenticationCommencingTest extends WebTestCase
public function testAuthenticationIsCommencingIfAccessDeniedExceptionIsWrapped()
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'config.yml'));
$client->insulate();
$client->request('GET', '/secure-but-not-covered-by-access-control');
$this->assertRedirect($client->getResponse(), '/login');

View File

@ -19,7 +19,6 @@ class CsrfFormLoginTest extends WebTestCase
public function testFormLoginAndLogoutWithCsrfTokens($config)
{
$client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config));
$client->insulate();
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['user_login[username]'] = 'johannes';
@ -50,7 +49,6 @@ class CsrfFormLoginTest extends WebTestCase
public function testFormLoginWithInvalidCsrfToken($config)
{
$client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config));
$client->insulate();
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['user_login[_token]'] = '';
@ -68,7 +66,6 @@ class CsrfFormLoginTest extends WebTestCase
public function testFormLoginWithCustomTargetPath($config)
{
$client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config));
$client->insulate();
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['user_login[username]'] = 'johannes';
@ -89,7 +86,6 @@ class CsrfFormLoginTest extends WebTestCase
public function testFormLoginRedirectsToProtectedResourceAfterLogin($config)
{
$client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config));
$client->insulate();
$client->request('GET', '/protected-resource');
$this->assertRedirect($client->getResponse(), '/login');
@ -113,17 +109,13 @@ class CsrfFormLoginTest extends WebTestCase
);
}
protected function setUp()
public static function setUpBeforeClass()
{
parent::setUp();
$this->deleteTmpDir('CsrfFormLogin');
parent::deleteTmpDir('CsrfFormLogin');
}
protected function tearDown()
public static function tearDownAfterClass()
{
parent::tearDown();
$this->deleteTmpDir('CsrfFormLogin');
parent::deleteTmpDir('CsrfFormLogin');
}
}

View File

@ -18,7 +18,6 @@ class FirewallEntryPointTest extends WebTestCase
public function testItUsesTheConfiguredEntryPointWhenUsingUnknownCredentials()
{
$client = $this->createClient(array('test_case' => 'FirewallEntryPoint'));
$client->insulate();
$client->request('GET', '/secure/resource', array(), array(), array(
'PHP_AUTH_USER' => 'unknown',
@ -35,7 +34,6 @@ class FirewallEntryPointTest extends WebTestCase
public function testItUsesTheConfiguredEntryPointFromTheExceptionListenerWithFormLoginAndNoCredentials()
{
$client = $this->createClient(array('test_case' => 'FirewallEntryPoint', 'root_config' => 'config_form_login.yml'));
$client->insulate();
$client->request('GET', '/secure/resource');
@ -46,17 +44,13 @@ class FirewallEntryPointTest extends WebTestCase
);
}
protected function setUp()
public static function setUpBeforeClass()
{
parent::setUp();
$this->deleteTmpDir('FirewallEntryPoint');
parent::deleteTmpDir('FirewallEntryPoint');
}
protected function tearDown()
public static function tearDownAfterClass()
{
parent::tearDown();
$this->deleteTmpDir('FirewallEntryPoint');
parent::deleteTmpDir('FirewallEntryPoint');
}
}

View File

@ -19,7 +19,6 @@ class FormLoginTest extends WebTestCase
public function testFormLogin($config)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
$client->insulate();
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['_username'] = 'johannes';
@ -39,7 +38,6 @@ class FormLoginTest extends WebTestCase
public function testFormLogout($config)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
$client->insulate();
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['_username'] = 'johannes';
@ -73,7 +71,6 @@ class FormLoginTest extends WebTestCase
public function testFormLoginWithCustomTargetPath($config)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
$client->insulate();
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['_username'] = 'johannes';
@ -94,7 +91,6 @@ class FormLoginTest extends WebTestCase
public function testFormLoginRedirectsToProtectedResourceAfterLogin($config)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
$client->insulate();
$client->request('GET', '/protected_resource');
$this->assertRedirect($client->getResponse(), '/login');
@ -118,17 +114,13 @@ class FormLoginTest extends WebTestCase
);
}
protected function setUp()
public static function setUpBeforeClass()
{
parent::setUp();
$this->deleteTmpDir('StandardFormLogin');
parent::deleteTmpDir('StandardFormLogin');
}
protected function tearDown()
public static function tearDownAfterClass()
{
parent::tearDown();
$this->deleteTmpDir('StandardFormLogin');
parent::deleteTmpDir('StandardFormLogin');
}
}

View File

@ -19,7 +19,6 @@ class LocalizedRoutesAsPathTest extends WebTestCase
public function testLoginLogoutProcedure($locale)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes.yml'));
$client->insulate();
$crawler = $client->request('GET', '/'.$locale.'/login');
$form = $crawler->selectButton('login')->form();
@ -41,7 +40,6 @@ class LocalizedRoutesAsPathTest extends WebTestCase
public function testLoginFailureWithLocalizedFailurePath($locale)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_form_failure_handler.yml'));
$client->insulate();
$crawler = $client->request('GET', '/'.$locale.'/login');
$form = $crawler->selectButton('login')->form();
@ -58,7 +56,6 @@ class LocalizedRoutesAsPathTest extends WebTestCase
public function testAccessRestrictedResource($locale)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes.yml'));
$client->insulate();
$client->request('GET', '/'.$locale.'/secure/');
$this->assertRedirect($client->getResponse(), '/'.$locale.'/login');
@ -70,7 +67,6 @@ class LocalizedRoutesAsPathTest extends WebTestCase
public function testAccessRestrictedResourceWithForward($locale)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes_with_forward.yml'));
$client->insulate();
$crawler = $client->request('GET', '/'.$locale.'/secure/');
$this->assertCount(1, $crawler->selectButton('login'), (string) $client->getResponse());
@ -81,17 +77,13 @@ class LocalizedRoutesAsPathTest extends WebTestCase
return array(array('en'), array('de'));
}
protected function setUp()
public static function setUpBeforeClass()
{
parent::setUp();
$this->deleteTmpDir('StandardFormLogin');
parent::deleteTmpDir('StandardFormLogin');
}
protected function tearDown()
public static function tearDownAfterClass()
{
parent::tearDown();
$this->deleteTmpDir('StandardFormLogin');
parent::deleteTmpDir('StandardFormLogin');
}
}

View File

@ -19,7 +19,6 @@ class SecurityRoutingIntegrationTest extends WebTestCase
public function testRoutingErrorIsNotExposedForProtectedResourceWhenAnonymous($config)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
$client->insulate();
$client->request('GET', '/protected_resource');
$this->assertRedirect($client->getResponse(), '/login');
@ -31,7 +30,6 @@ class SecurityRoutingIntegrationTest extends WebTestCase
public function testRoutingErrorIsExposedWhenNotProtected($config)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
$client->insulate();
$client->request('GET', '/unprotected_resource');
$this->assertEquals(404, $client->getResponse()->getStatusCode(), (string) $client->getResponse());
@ -43,7 +41,6 @@ class SecurityRoutingIntegrationTest extends WebTestCase
public function testRoutingErrorIsNotExposedForProtectedResourceWhenLoggedInWithInsufficientRights($config)
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config));
$client->insulate();
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['_username'] = 'johannes';
@ -120,17 +117,13 @@ class SecurityRoutingIntegrationTest extends WebTestCase
return array(array('config.yml'), array('routes_as_path.yml'));
}
protected function setUp()
public static function setUpBeforeClass()
{
parent::setUp();
$this->deleteTmpDir('StandardFormLogin');
parent::deleteTmpDir('StandardFormLogin');
}
protected function tearDown()
public static function tearDownAfterClass()
{
parent::tearDown();
$this->deleteTmpDir('StandardFormLogin');
parent::deleteTmpDir('StandardFormLogin');
}
}

View File

@ -62,7 +62,6 @@ class SwitchUserTest extends WebTestCase
{
$client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'switchuser.yml'));
$client->followRedirects(true);
$client->insulate();
$form = $client->request('GET', '/login')->selectButton('login')->form();
$form['_username'] = $username;
@ -72,17 +71,13 @@ class SwitchUserTest extends WebTestCase
return $client;
}
protected function setUp()
public static function setUpBeforeClass()
{
parent::setUp();
$this->deleteTmpDir('StandardFormLogin');
parent::deleteTmpDir('StandardFormLogin');
}
protected function tearDown()
public static function tearDownAfterClass()
{
parent::tearDown();
$this->deleteTmpDir('StandardFormLogin');
parent::deleteTmpDir('StandardFormLogin');
}
}

View File

@ -23,7 +23,7 @@ class WebTestCase extends BaseWebTestCase
self::assertEquals('http://localhost'.$location, $response->headers->get('Location'));
}
protected function deleteTmpDir($testCase)
protected static function deleteTmpDir($testCase)
{
if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$testCase)) {
return;

View File

@ -65,6 +65,18 @@ class AppKernel extends Kernel
parent::__construct($environment, $debug);
}
/**
* {@inheritdoc}
*/
public function getName()
{
if (null === $this->name) {
$this->name = parent::getName().md5($this->rootConfig);
}
return $this->name;
}
public function registerBundles()
{
if (!is_file($filename = $this->getRootDir().'/'.$this->testCase.'/bundles.php')) {

View File

@ -634,6 +634,8 @@ class Application
*/
public function renderException($e, $output)
{
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
do {
$title = sprintf(' [%s] ', get_class($e));
@ -656,7 +658,7 @@ class Application
}
}
$messages = array('', '');
$messages = array();
$messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
foreach ($lines as $line) {
@ -664,7 +666,6 @@ class Application
}
$messages[] = $emptyLine;
$messages[] = '';
$messages[] = '';
$output->writeln($messages, OutputInterface::OUTPUT_RAW | OutputInterface::VERBOSITY_QUIET);
@ -691,14 +692,12 @@ class Application
}
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
} while ($e = $e->getPrevious());
if (null !== $this->runningCommand) {
$output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
}
}

View File

@ -395,7 +395,6 @@ class Table
// Remove any new line breaks and replace it with a new line
foreach ($rows[$rowKey] as $column => $cell) {
$rows[$rowKey] = $this->fillCells($rows[$rowKey], $column);
if (!strstr($cell, "\n")) {
continue;
}
@ -415,7 +414,7 @@ class Table
$tableRows = array();
foreach ($rows as $rowKey => $row) {
$tableRows[] = $row;
$tableRows[] = $this->fillCells($row);
if (isset($unmergedRows[$rowKey])) {
$tableRows = array_merge($tableRows, $unmergedRows[$rowKey]);
}
@ -481,21 +480,23 @@ class Table
* fill cells for a row that contains colspan > 1.
*
* @param array $row
* @param int $column
*
* @return array
*/
private function fillCells($row, $column)
private function fillCells($row)
{
$cell = $row[$column];
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
// insert empty value into rows at column position
array_splice($row, $position, 0, '');
$newRow = array();
foreach ($row as $column => $cell) {
$newRow[] = $cell;
if ($cell instanceof TableCell && $cell->getColspan() > 1) {
foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
// insert empty value at column position
$newRow[] = '';
}
}
}
return $row;
return $newRow ?: $row;
}
/**

View File

@ -1,8 +1,6 @@
[Symfony\Component\Console\Exception\CommandNotFoundException]
Command "foo" is not defined.

View File

@ -1,11 +1,8 @@
[Symfony\Component\Console\Exception\InvalidOptionException]
The "--foo" option does not exist.
list [--xml] [--raw] [--format FORMAT] [--] [<namespace>]

View File

@ -1,27 +1,18 @@
[Exception]
Third exception comment
[Exception]
Second exception comment
[Exception]
First exception <p>this is html</p>
foo3:bar

View File

@ -1,27 +1,18 @@
 
 [Exception] 
 Third exception comment 
 
 
 [Exception] 
 Second exception comment 
 
 
 [Exception] 
 First exception <p>this is html</p> 
 
foo3:bar

View File

@ -1,9 +1,7 @@
[Symfony\Component\Console\Exception\CommandNotFoundException]
Command "foo" is not define
d.

View File

@ -1,11 +1,8 @@
[Exception]
エラーメッセージ
foo

View File

@ -1,11 +1,8 @@
 
 [Exception] 
 エラーメッセージ 
 
foo

View File

@ -1,12 +1,9 @@
[Exception]
コマンドの実行中にエラーが
発生しました。
foo

View File

@ -459,6 +459,24 @@ TABLE
| ISBN | Title | Author |
+------+-------+--------+
TABLE
),
'Row with multiple cells' => array(
array(),
array(
array(
new TableCell('1', array('colspan' => 3)),
new TableCell('2', array('colspan' => 2)),
new TableCell('3', array('colspan' => 2)),
new TableCell('4', array('colspan' => 2)),
),
),
'default',
<<<TABLE
+--+--+--+--+--+--+--+--+--+
| 1 | 2 | 3 | 4 |
+--+--+--+--+--+--+--+--+--+
TABLE
),
);

View File

@ -192,12 +192,12 @@ class Filesystem
public function chmod($files, $mode, $umask = 0000, $recursive = false)
{
foreach ($this->toIterator($files) as $file) {
if ($recursive && is_dir($file) && !is_link($file)) {
$this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
}
if (true !== @chmod($file, $mode & ~$umask)) {
throw new IOException(sprintf('Failed to chmod file "%s".', $file), 0, null, $file);
}
if ($recursive && is_dir($file) && !is_link($file)) {
$this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
}
}
}

View File

@ -483,6 +483,22 @@ class FilesystemTest extends FilesystemTestCase
$this->assertFilePermissions(753, $directory);
}
public function testChmodChangesZeroModeOnSubdirectoriesOnRecursive()
{
$this->markAsSkippedIfChmodIsMissing();
$directory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
$subdirectory = $directory.DIRECTORY_SEPARATOR.'subdirectory';
mkdir($directory);
mkdir($subdirectory);
chmod($subdirectory, 0000);
$this->filesystem->chmod($directory, 0753, 0000, true);
$this->assertFilePermissions(753, $subdirectory);
}
public function testChown()
{
$this->markAsSkippedIfPosixIsMissing();

View File

@ -28,6 +28,7 @@ class CollectionType extends AbstractType
{
if ($options['allow_add'] && $options['prototype']) {
$prototypeOptions = array_replace(array(
'required' => $options['required'],
'label' => $options['prototype_name'].'label__',
), $options['options']);

View File

@ -332,4 +332,30 @@ class CollectionTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
));
$this->assertSame('bar', $form->createView()->vars['prototype']->vars['value']);
}
public function testPrototypeDefaultRequired()
{
$form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array(
'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FileType',
'allow_add' => true,
'prototype' => true,
'prototype_name' => '__test__',
));
$this->assertTrue($form->createView()->vars['prototype']->vars['required']);
}
public function testPrototypeSetNotRequired()
{
$form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array(
'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FileType',
'allow_add' => true,
'prototype' => true,
'prototype_name' => '__test__',
'required' => false,
));
$this->assertFalse($form->createView()->vars['required'], 'collection is not required');
$this->assertFalse($form->createView()->vars['prototype']->vars['required'], '"prototype" should not be required');
}
}

View File

@ -19,6 +19,8 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException;
*/
class BCryptPasswordEncoder extends BasePasswordEncoder
{
const MAX_PASSWORD_LENGTH = 72;
/**
* @var string
*/

View File

@ -93,6 +93,6 @@ abstract class BasePasswordEncoder implements PasswordEncoderInterface
*/
protected function isPasswordTooLong($password)
{
return strlen($password) > self::MAX_PASSWORD_LENGTH;
return strlen($password) > static::MAX_PASSWORD_LENGTH;
}
}

View File

@ -67,13 +67,15 @@ class BCryptPasswordEncoderTest extends \PHPUnit_Framework_TestCase
{
$encoder = new BCryptPasswordEncoder(self::VALID_COST);
$encoder->encodePassword(str_repeat('a', 5000), 'salt');
$encoder->encodePassword(str_repeat('a', 73), 'salt');
}
public function testCheckPasswordLength()
{
$encoder = new BCryptPasswordEncoder(self::VALID_COST);
$result = $encoder->encodePassword(str_repeat('a', 72), null);
$this->assertFalse($encoder->isPasswordValid('encoded', str_repeat('a', 5000), 'salt'));
$this->assertFalse($encoder->isPasswordValid($result, str_repeat('a', 73), 'salt'));
$this->assertTrue($encoder->isPasswordValid($result, str_repeat('a', 72), 'salt'));
}
}

View File

@ -18,7 +18,7 @@ use Symfony\Component\Validator\Constraints\Length;
$validator = Validation::createValidator();
$violations = $validator->validateValue('Bernhard', new Length(array('min' => 10)));
$violations = $validator->validate('Bernhard', new Length(array('min' => 10)));
```
This validation will fail because the given string is shorter than ten
@ -46,7 +46,7 @@ $constraint = new Assert\Collection(array(
'password' => new Assert\Length(array('min' => 60)),
));
$violations = $validator->validateValue($input, $constraint);
$violations = $validator->validate($input, $constraint);
```
Again, the validator returns the list of violations.

View File

@ -278,6 +278,42 @@
<source>This value should not be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>Стойността не трябва да бъде идентична с {{ compared_value_type }} {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="73">
<source>The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.</source>
<target>Изображението е с твърде голяма пропорция ({{ ratio }}). Максималната пропорция трябва да е {{ max_ratio }}.</target>
</trans-unit>
<trans-unit id="74">
<source>The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.</source>
<target>Изображението е с твърде малка пропорция ({{ ratio }}). Минималната пропорция трябва да е {{ min_ratio }}.</target>
</trans-unit>
<trans-unit id="75">
<source>The image is square ({{ width }}x{{ height }}px). Square images are not allowed.</source>
<target>Изображението е квадрат ({{ width }}x{{ height }}px). Такива изображения не са разрешени.</target>
</trans-unit>
<trans-unit id="76">
<source>The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.</source>
<target>Изображението е с пейзажна ориентация ({{ width }}x{{ height }}px). Изображения с такава ориентация не са разрешени.</target>
</trans-unit>
<trans-unit id="77">
<source>The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.</source>
<target>Изображението е с портретна ориентация ({{ width }}x{{ height }}px). Изображения с такава ориентация не са разрешени.</target>
</trans-unit>
<trans-unit id="78">
<source>An empty file is not allowed.</source>
<target>Празни файлове не са разрешени.</target>
</trans-unit>
<trans-unit id="79">
<source>The host could not be resolved.</source>
<target>Хостът е недостъпен.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Стойността не съвпада с {{ charset }}.</target>
</trans-unit>
<trans-unit id="81">
<source>This is not a valid Business Identifier Code (BIC).</source>
<target>Невалиден бизнес идентификационен код (BIC).</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -339,6 +339,7 @@ class Parser
private function getNextEmbedBlock($indentation = null, $inSequence = false)
{
$oldLineIndentation = $this->getCurrentLineIndentation();
$insideBlockScalar = $this->isBlockScalarHeader();
if (!$this->moveToNextLine()) {
return;
@ -375,17 +376,21 @@ class Parser
$isItUnindentedCollection = $this->isStringUnIndentedCollectionItem();
// Comments must not be removed inside a block scalar
$removeCommentsPattern = '~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~';
$removeComments = !preg_match($removeCommentsPattern, $this->currentLine);
if (!$insideBlockScalar) {
$insideBlockScalar = $this->isBlockScalarHeader();
}
$previousLineIndentation = $this->getCurrentLineIndentation();
while ($this->moveToNextLine()) {
$indent = $this->getCurrentLineIndentation();
if ($indent === $newIndent) {
$removeComments = !preg_match($removeCommentsPattern, $this->currentLine);
if (!$insideBlockScalar && $indent === $previousLineIndentation) {
$insideBlockScalar = $this->isBlockScalarHeader();
}
$previousLineIndentation = $indent;
if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
$this->moveToPreviousLine();
break;
@ -396,7 +401,8 @@ class Parser
continue;
}
if ($removeComments && $this->isCurrentLineComment()) {
// we ignore "comment" lines only when we are not inside a scalar block
if (!$insideBlockScalar && $this->isCurrentLineComment()) {
continue;
}
@ -719,4 +725,14 @@ class Parser
{
return 0 === strpos($this->currentLine, '- ');
}
/**
* Tests whether or not the current line is the header of a block scalar.
*
* @return bool
*/
private function isBlockScalarHeader()
{
return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
}
}

View File

@ -818,6 +818,100 @@ EOT;
$this->assertSame(array('foo' => array('bar' => 'foobar')), $this->parser->parse($yaml));
}
/**
* @dataProvider getCommentLikeStringInScalarBlockData
*/
public function testCommentLikeStringsAreNotStrippedInBlockScalars($yaml, $expectedParserResult)
{
$this->assertSame($expectedParserResult, $this->parser->parse($yaml));
}
public function getCommentLikeStringInScalarBlockData()
{
$yaml1 = <<<EOT
pages:
-
title: some title
content: |
# comment 1
header
# comment 2
<body>
<h1>title</h1>
</body>
footer # comment3
EOT;
$expected1 = array(
'pages' => array(
array(
'title' => 'some title',
'content' => <<<EOT
# comment 1
header
# comment 2
<body>
<h1>title</h1>
</body>
footer # comment3
EOT
,
),
),
);
$yaml2 = <<<EOT
test: |
foo
# bar
baz
collection:
- one: |
foo
# bar
baz
- two: |
foo
# bar
baz
EOT;
$expected2 = array(
'test' => <<<EOT
foo
# bar
baz
EOT
,
'collection' => array(
array(
'one' => <<<EOT
foo
# bar
baz
EOT
,
),
array(
'two' => <<<EOT
foo
# bar
baz
EOT
,
),
),
);
return array(
array($yaml1, $expected1),
array($yaml2, $expected2),
);
}
}
class B