8000 [Config] Introduce concept of deprecating nodes by ro0NL · Pull Request #21051 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Config] Introduce concept of deprecating nodes #21051

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
wants to merge 1 commit into from
Closed

[Config] Introduce concept of deprecating nodes #21051

wants to merge 1 commit into from

Conversation

ro0NL
Copy link
Contributor
@ro0NL ro0NL commented Dec 25, 2016
Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? not updated
Fixed tickets #...
License MIT
Doc PR symfony/symfony-docs#...

This introduces an out-the-box solution for deprecating nodes in a configuration tree, avoid doing it user-land and repetitively.

It works like

// Configuration.php

$someArrayNode
    // trigger notice if given, continue as usual
    ->deprecate(['foo'])

    // trigger single notice if at least one is given
    // specifying a handler means these keys are unset afterwards
    ->deprecate(['bar', 'baz'], function (array $v) { 
        $v['new_bar_baz'] = ($v['bar'] ?? '') . ($v['baz'] ?? '');

        return $v;
    })

    // custom message
    ->deprecate(['a'], null, 'Use "foo" instead')

    // continue as usual
    ->childeren()
        ->scalarNode('foo')->end()
         ->scalarNode('new_bar_baz')->end()
    ->end();

Child nodes must be referenced once in mulitple deprecations. meaning you cant do

$array->deprecate(['a']);
$array->deprecate(['a', 'b']);

PR Goal

Utilize (fluent interface) the impleme 8000 ntation of trigger_error($message, E_USER_DEPRECATED). So that users only have to care about business value; bridging the old scenario to the new one. Aiming to provide consistent deprecation messages by default (again, to not bother users with it). And doing it rather fast.

The bridging scenario is either a simple case or a complex one, it doesnt differ when doing it with beforeNormalization and is not what this PR is about nor aiming to solve. Although simple scenarios can be utilized.

@trigger_error($message, E_USER_DEPRECATED);

if (null !== $handler) {
$normalized = $handler($this->remapXml($normalized));
Copy link
Contributor Author
@ro0NL ro0NL Dec 25, 2016

Choose a reason for hiding this comment

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

Oh, and on a side note; it was a really weird experience noticing that xml remapping is applied after any normalization.

I totally expected this to be done before normalization. Like normalizing keys...

Maybe we shouldnt do it even here.. for consistency. But it's really convenient :)

@ro0NL
Copy link
Contributor Author
ro0NL commented Dec 26, 2016

Re-considered some things;

  • a deprecated node can never be referenced in child nodes
  • a deprecation should always have a handler
    • by default we could allow to just remove a key (ie. if it's being unused at this point)
    • for convenience we could provide a remap handler built from a string (the new node name)
  • a deprecation always unsets the old keys

This makes the behavior more consistent imo. If you only want a notice and preserve nodes you can do that yourself easily.

@ro0NL
Copy link
Contributor Author
ro0NL commented Dec 26, 2016

Im even thinking expression builder now..

What about

$array
  ->deprecate(['foo', 'bar']) // if part (if any given)
    ->preserve() // optional
    ->bridge($callable) // then part
    ->renameTo('new_%s') // then part
    ->withMessage('...')
  ->end()

@GuilhemN
Copy link
Contributor

I wonder if this is worth it, isn't it simple enough to do:

$node
    ->beforeNormalization()
        ->always(function ($v) {
            if (isset($v['foo']) {
                @trigger_error('Foo is deprecated', E_USER_DEPRECATED);
            }
        })
    ->end();

?

@ro0NL
Copy link
Contributor Author
ro0NL commented Dec 26, 2016

Yes and no.

It's simple, but cumbersome. The strategy is always the same here (bridging old values to new ones, removing the old ones).

And im not 100% sure yet, but i believe we should use array_key_exists to truly check if a user passed a certain node.

So imagine, deprecating multiple fields... we need checks like array_key_exists('foo', $v) || array_key_exists('bar', $v) and unset() calls in user land, while trying to keep deprecation messages consistent.

Besides, without #21052 this approach (nor beforeNormalization()) is not waterproof anyway.

edit: even considering ->deprecate()->since('1.0'), to provide proper/consistent messages by default.

@nicolas-grekas nicolas-grekas added this to the 3.x milestone Dec 26, 2016
@ro0NL
Copy link
Contributor Author
ro0NL commented Dec 26, 2016

@GuilhemN updated PR description with an explicit goal, why i think it's important.

@nicolas-grekas
Copy link
Member

I think I'm 👎 here because it adds a feature that is quite easily implemented directly in the code, and that is not needed by userland (well, could be for bundle authors, but that's quite rare anyway.)

@ro0NL
Copy link
Contributor Author
ro0NL commented Dec 27, 2016

Imo. it could help SF a lot, for contributors to do it intuitively themselves, saving time on reviews. And indeed may help other libs.

From a pure component perspective it may benefit many. At least, that were my considerations in the first place.

@ro0NL
Copy link
Contributor Author
ro0NL commented Dec 27, 2016
$node->deprecate(['foo', 'bar'])
   ->since('1.0')
   ->renameTo('new_%s') // or closure
->end()

I prefer that by all means over writing the same type of normalization closures all the time. Deprecations should not be error prone, and bridge strategies should be explicit. We standardized it for DI services as well.

@GuilhemN
Copy link
Contributor
GuilhemN commented Dec 27, 2016

@ro0NL I understand the goal but I agree with @nicolas-grekas, it is too elaborated for its uses.
It is not common to deprecate a config option, I don't think it is worth dedicated a part of the component to it.

Maybe a simple utility method based on the current features would be enough, without being a nightmare to maintain ?

public function deprecate(string[] $keys, string $message = 'Option "%s" is deprecated.')
{
    return $this->beforeNormalization()
        ->always(function ($v) use ($keys) {
            foreach ($keys as $key) {
                if (array_key_exists($v, $key)) {
                     @trigger_error(sprintf($message, $key), E_USER_DEPRECATED);
                }
            }
            
            return $v;
        })
    ->end();
}

@ro0NL
Copy link
Contributor Author
ro0NL commented Dec 28, 2016

It is not common to deprecate a config option

Agree

I don't think it is worth dedicated a part of the component to it.

Here's where we disagree. I think the component could solve a common case here. My project has few deprecations, many projects have many deprecations.

Or at least i see a opportunity for the component to provide a standard here. Deprecations in configs are a real issue, and this could aim for better config management and best practices.

Now, i dont want to push anything here and i understand the motivation against it. Always happy to close if i cant convince :)

@nicolas-grekas
Copy link
Member

In the hope to save everyones' time, I'm 👎 here :)

@fabpot
Copy link
Member
fabpot commented Dec 28, 2016

I don't see the need for end user projects to deprecate options, so I'm 👎 as well

@ro0NL
Copy link
Contributor Author
ro0NL commented Dec 28, 2016

Well.. then it's 4-4.. making me already less convinced :)

Hope the point is clear this is not about adding features, for the sake of it. The case is tedious, and the component goes beyond "end user projects".

👍

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.

5 participants
0