Skip to content

[Yaml] parse merge keys with PARSE_OBJECT_FOR_MAP flag #24189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 28, 2017

Conversation

xabbuh
Copy link
Member

@xabbuh xabbuh commented Sep 13, 2017

Q A
Branch? 3.3
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #24133
License MIT
Doc PR

@ostrolucky
Copy link
Contributor

You use same kind of foreach loops I removed via #21756 Please consider using union operator again. It reduces big chunk of unnecessary verbosity and you get NULL handling for free. Example of usage in your patch:

@@ -257,14 +257,8 @@
 
                         $refValue = $this->refs[$refName];
 
-                        if (is_array($refValue)) {
-                            $data += $refValue; // array union
-                        } elseif ((bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags) && $refValue instanceof \stdClass) {
-                            foreach ($refValue as $refValueKey => $refValueValue) {
-                                if (!isset($data[$refValueKey])) {
-                                    $data[$refValueKey] = $refValueValue;
-                                }
-                            }
+                        if (is_array($refValue) || (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags) && $refValue instanceof \stdClass) {
+                            $data += (array) $refValue; // array union
                         } else {
                             throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                         }
@@ -294,11 +288,7 @@
                                 $data += $parsed; // array union
                             }
                         } elseif ((bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags) && $parsed instanceof \stdClass) {
-                            foreach ($parsed as $parsedKey => $parsedValue) {
-                                if (!isset($data[$parsedKey])) {
-                                    $data[$parsedKey] = $parsedValue;
-                                }
-                            }
+                            $data += (array) $parsed;
                         } else {
                             throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                         }

@xabbuh
Copy link
Member Author

xabbuh commented Sep 28, 2017

ping @symfony/deciders :)

@chalasr
Copy link
Member

chalasr commented Sep 28, 2017

@xabbuh Just tried it out, it seems to fix it for #24133 code but not for #23587.
Reproducer:

<?php
require __DIR__ .'/vendor/autoload.php';

use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Yaml;

$yaml = <<<YAML
root:
    mergekeyrefdef:
        a: foo
        <<: &quux
            b: bar
            c: baz
    mergekeyderef:
        d: quux
        <<: *quux
YAML;

$arr = (new Parser())->parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP);

@xabbuh
Copy link
Member Author

xabbuh commented Sep 28, 2017

@chalasr Thanks for checking. You are absolutely right. And the bug described in #23587 is actually not related to the PARSE_OBJECT_FOR_MAP flag, but purely to the fact that the parser fails when a reference is defined on a merge key. We need to fix that in a different PR on the 2.7 branch.

@@ -249,14 +249,18 @@ private function doParse($value, $flags)
if ('<<' === $key) {
$mergeNode = true;
$allowOverwrite = true;
if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
if (isset($values['value']) && isset($values['value'][0]) && '*' === $values['value'][0]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the first isset can be removed, as it is covered by the second one.

$refName = substr(rtrim($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 ((bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags) && $refValue instanceof \stdClass) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the explicit bool cast is useless (&& will trigger it too)

@fabpot
Copy link
Member

fabpot commented Sep 28, 2017

Thank you @xabbuh.

@fabpot fabpot merged commit 534eaed into symfony:3.3 Sep 28, 2017
fabpot added a commit that referenced this pull request Sep 28, 2017
…bbuh)

This PR was merged into the 3.3 branch.

Discussion
----------

[Yaml] parse merge keys with PARSE_OBJECT_FOR_MAP flag

| Q             | A
| ------------- | ---
| Branch?       | 3.3
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #24133
| License       | MIT
| Doc PR        |

Commits
-------

534eaed parse merge keys with PARSE_OBJECT_FOR_MAP flag
@xabbuh xabbuh deleted the issue-24133 branch September 28, 2017 23:46
@fabpot fabpot mentioned this pull request Oct 5, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants