Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[RFC][Yaml] Add custom tags support #21185

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

Closed
GuilhemN opened this issue Jan 6, 2017 · 10 comments
Closed

[RFC][Yaml] Add custom tags support #21185

GuilhemN opened this issue Jan 6, 2017 · 10 comments
Milestone

Comments

@GuilhemN
Copy link
Contributor

GuilhemN commented Jan 6, 2017

Q A
Bug report? no
Feature request? yes
BC Break report? no
RFC? yes
Symfony version 3.3.0

Custom tags support is a very important feature imo that currently lacks to the yaml component.
It is basically allowing things like:

services:
    square_printer:
        arguments: [ !color red ]

By having a dedicated system for tags, we could centralize their management and avoid duplication (here and here).
If wanted, it could also be used in the core to remove reserved keys in config files.
For example,

services:
    _defaults:
        autowire: true

    my_service:
        class: FooBar
        arguments:
            - '@service1'
            - =iterator:
                - '@service2'
                - '@service3'

# could become
services:
    !defaults:
        autowire: true

    my_service:
        class: FooBar
        arguments:
            - '@service1'
            - !iterator
                - '@service2'
                - '@service3'

WDYT?

@nicolas-grekas
Copy link
Member

What would be the resulting PHP structure ?

@GuilhemN
Copy link
Contributor Author

GuilhemN commented Jan 6, 2017

Nothing's finished but I began this.
I made a class per tag and it resulted to:

Yaml/
    Tag/
        - BinaryTag
        - BoolTag
        - FloatTag
        - IntTag
        - NonSpecificTag
        - NullTag
        - StrTag
        - TimestampTag

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Jan 7, 2017

Would it be possible to have a much smaller diff, to only add code for supporting first level tags, !foo?
About the resulting output PHP structure, should we add a single TaggedValue class with getTag and getValue?
Would that make sense ?

@xabbuh
Copy link
Member

xabbuh commented Jan 7, 2017

The idea looks interesting and might help implementing #21071. I am just not sure how we should best implement it to allow some flexibility which will probably be needed.

@xabbuh xabbuh added this to the 3.x milestone Jan 7, 2017
@nicolas-grekas
Copy link
Member

nicolas-grekas commented Jan 7, 2017

Note that #21071 is ready and shouldn't be waiting for this one. But of course, it'd be great to leverage tags and use them instead of special keys when it's ready.

@GuilhemN
Copy link
Contributor Author

GuilhemN commented Jan 7, 2017

Would it be possible to have a much smaller diff, to only add code for supporting first level tags, !foo?

@nicolas-grekas and hardcode in the parser the defaults ?

About the resulting output PHP structure, should we add a single TaggedValue class with getTag and getValue?
Would that make sense ?

I don't think that's worth it, having it as arguments is enough imo.

The idea looks interesting and might help implementing #21071. I am just not sure how we should best implement it to allow some flexibility which will probably be needed.

@xabbuh indeed, you can still take a look at other libraries and see if their implementation inspires you (js-yaml, the php yaml extension, pyyaml, etc.).

Note that my current implementation doesn't support dynamic tags name, I don't think this is useful, so !php/object:foo would have to be deprecated in favor of !php/object foo

@xabbuh
Copy link
Member

xabbuh commented Jan 7, 2017

@nicolas-grekas I agree that it shouldn't wait. But having sort for custom tags in 3.3 would allow to update your implementation before the final release.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Jan 7, 2017

hardcode in the parser the defaults

Of course not, "foo" === "whatever" !== "hardcoded".

having it as arguments is enough imo

What does that mean? Looks like we're talking foreign languages :)

So, back to my first question, which still needs an answer: "What would be the resulting PHP structure ?", where "resulting" means "output from the parser" (and not "internal structure").

Let's take an example, in the context of the DI component:
$output = Yaml::parse('- !iterator [...]').
What should be $output?

My personal pov is that it could be well enough to just make it be:
array( new TaggedValue('iterator', [...]) )

so that eg the DI Yaml loader could then iterate over this structure and do at some point:

if ($value instanceof TaggedValue) {
    if ('iterator' === $value->getTag()) {
        $iteratedValues = $value->getValue();
        // etc. (like validation)
        $resultingValue = new IteratorArgument(...);
    }
}

there is another possibility which could be to make Yaml able to hydrate an IteratorArgument directly, so that $output could directly be array( new IteratorArgument(...) ).
But that looks way too much complexity to add to me.

@GuilhemN
Copy link
Contributor Author

GuilhemN commented Jan 7, 2017

hardcode in the parser the defaults

Of course not, "foo" === "whatever" !== "hardcoded".

@nicolas-grekas did you mean having this kind of diff?

having it as arguments is enough imo

What does that mean? Looks like we're talking foreign languages :)

I was talking about having an interface for tags (and manage them in the parser):

interface TagInterface
{
    /**
     * @param string $value a plain scalar
     *
     * @return mixed
     */
    public function construct($value);
}

But it seems you were talking about having a special class for the output 😛

So, back to my first question, which still needs an answer: "What would be the resulting PHP structure ?", where "resulting" means "output from the parser" (and not "internal structure").

 Yaml::parse('- !iterator [...]') === array( new TaggedValue('iterator', [...]) );

It seems weird to me; core tags (eg !!binary, !!str, etc.) would be directly transformed in the parser but not custom tags?
That would also force people using custom tags to iterate over the entire output to find these tags.
One last argument, it would be a bc break for scalars (!iterator foo currently returns "!iterator foo").

I think it is much more powerful to pass this responsibility to the parser, you tell it to convert !iterator to IteratorArgument and that's all. It took me ~170 lines of code to support it.

@nicolas-grekas
Copy link
Member

About the diff, yes!

core tags would be directly transformed in the parser but not custom tags

custom vs core, that's enough a difference to me, so yes

force people using custom tags to iterate over the entire output to find these tags

sure, but people are already iterating over the resulting structure to just read what's inside of it, so that doesn't shock me.

more powerful to pass this responsibility to the parser

responsibility = complexity. Given that we don't want to build a full hydrating system here, we can only support simple types here (similar to doctrine annotations). Which means "more powerful": maybe, but powerful enough: not sure.

Thus, my personal preference is simplicity here. But that doesn't prevent you from trying your way :)

symfony-splitter pushed a commit to symfony/dependency-injection that referenced this issue Feb 10, 2017
This PR was squashed before being merged into the 3.3-dev branch (closes #21194).

Discussion
----------

[Yaml] Add tags support

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

This PR adds custom tags support to the Yaml component.
Symfony tags (`!!binary`, `!str`, etc.) are still managed in the parser to have a lighter diff but we'll be able to convert them later if we want to.

The primary addition of this PR is the `TagInterface`:
```php
interface TagInterface
{
    public function construct(mixed $value): mixed;
}
```

It can be used to register custom tags. An example that could be used to convert [the syntax `=iterator`](symfony/symfony#20907 (comment)) to a tag:
```php
final class IteratorTag implements TagInterface
{
    public function construct(mixed $value): mixed
    {
        return new IteratorArgument($value);
    }
}

$parser = new Parser(['iterator' => new IteratorTag()]);
```

If you think this is too complex, @nicolas-grekas [proposed an alternative](symfony/symfony#21185 (comment)) to my proposal externalizing this support by introducing a new class `TaggedValue`.

Commits
-------

4744107 [Yaml] Add tags support
nicolas-grekas added a commit that referenced this issue Feb 10, 2017
This PR was squashed before being merged into the 3.3-dev branch (closes #21194).

Discussion
----------

[Yaml] Add tags support

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

This PR adds custom tags support to the Yaml component.
Symfony tags (`!!binary`, `!str`, etc.) are still managed in the parser to have a lighter diff but we'll be able to convert them later if we want to.

The primary addition of this PR is the `TagInterface`:
```php
interface TagInterface
{
    public function construct(mixed $value): mixed;
}
```

It can be used to register custom tags. An example that could be used to convert [the syntax `=iterator`](#20907 (comment)) to a tag:
```php
final class IteratorTag implements TagInterface
{
    public function construct(mixed $value): mixed
    {
        return new IteratorArgument($value);
    }
}

$parser = new Parser(['iterator' => new IteratorTag()]);
```

If you think this is too complex, @nicolas-grekas [proposed an alternative](#21185 (comment)) to my proposal externalizing this support by introducing a new class `TaggedValue`.

Commits
-------

4744107 [Yaml] Add tags support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants