feature #22382 [config] Add abbitily to deprecate a node (Nyholm, fabpot, sanpii)

This PR was merged into the 4.0-dev branch.

Discussion
----------

[config] Add abbitily to deprecate a node

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | maybe
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | https://github.com/symfony/symfony-docs/pull/7794

For BC breaks, I don’t know if modifying the Xml and Yaml dumper output is considering as a BC break (it’s just a comment).

Commits
-------

31d2250f4c [config] Add abbitily to deprecate a node
3b6442f8b2 feature #23947 [Translation] Adding the ability do load <notes> in xliff2.0 (Nyholm)
b0cdb53d67 [Translation] Adding the ability do load <notes> in xliff2.0
This commit is contained in:
Fabien Potencier 2017-08-26 08:01:55 -07:00
commit 9a6d3e5557
15 changed files with 152 additions and 1 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
3.4.0
-----
* added `setDeprecated()` method to indicate a deprecated node
3.3.0
-----

View File

@ -234,6 +234,10 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
}
foreach ($this->children as $name => $child) {
if ($child->isDeprecated()) {
@trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED);
}
if (!array_key_exists($name, $value)) {
if ($child->isRequired()) {
$msg = sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath());

View File

@ -29,6 +29,7 @@ abstract class BaseNode implements NodeInterface
protected $finalValidationClosures = array();
protected $allowOverwrite = true;
protected $required = false;
protected $deprecationMessage = null;
protected $equivalentValues = array();
protected $attributes = array();
@ -141,6 +142,19 @@ abstract class BaseNode implements NodeInterface
$this->required = (bool) $boolean;
}
/**
* Sets this node as deprecated.
*
* You can use %node% and %path% placeholders in your message to display,
* respectively, the node name and its complete path.
*
* @param string|null $message Deprecated message
*/
public function setDeprecated($message)
{
$this->deprecationMessage = $message;
}
/**
* Sets if this node can be overridden.
*
@ -181,6 +195,29 @@ abstract class BaseNode implements NodeInterface
return $this->required;
}
/**
* Checks if this node is deprecated.
*
* @return bool
*/
public function isDeprecated()
{
return null !== $this->deprecationMessage;
}
/**
* Returns the deprecated message.
*
* @param string $node the configuration node name
* @param string $path the path of the node
*
* @return string
*/
public function getDeprecationMessage($node, $path)
{
return strtr($this->deprecationMessage, array('%node%' => $node, '%path%' => $path));
}
/**
* Returns the name of this node.
*

View File

@ -27,6 +27,7 @@ abstract class NodeDefinition implements NodeParentInterface
protected $defaultValue;
protected $default = false;
protected $required = false;
protected $deprecationMessage = null;
protected $merge;
protected $allowEmptyValue = true;
protected $nullEquivalent;
@ -168,6 +169,23 @@ abstract class NodeDefinition implements NodeParentInterface
return $this;
}
/**
* Sets the node as deprecated.
*
* You can use %node% and %path% placeholders in your message to display,
* respectively, the node name and its complete path.
*
* @param string $message Deprecation message
*
* @return $this
*/
public function setDeprecated($message = 'The child node "%node%" at path "%path%" is deprecated.')
{
$this->deprecationMessage = $message;
return $this;
}
/**
* Sets the equivalent value used when the node contains null.
*

View File

@ -54,6 +54,7 @@ class VariableNodeDefinition extends NodeDefinition
$node->addEquivalentValue(true, $this->trueEquivalent);
$node->addEquivalentValue(false, $this->falseEquivalent);
$node->setRequired($this->required);
$node->setDeprecated($this->deprecationMessage);
if (null !== $this->validation) {
$node->setFinalValidationClosures($this->validation->rules);

View File

@ -153,6 +153,10 @@ class XmlReferenceDumper
$comments[] = 'Required';
}
if ($child->isDeprecated()) {
$comments[] = sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $child->getPath()));
}
if ($child instanceof EnumNode) {
$comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues()));
}

View File

@ -123,6 +123,11 @@ class YamlReferenceDumper
$comments[] = 'Required';
}
// deprecated?
if ($node->isDeprecated()) {
$comments[] = sprintf('Deprecated (%s)', $node->getDeprecationMessage($node->getName(), $node->getPath()));
}
// example
if ($example && !is_array($example)) {
$comments[] = 'Example: '.$example;

View File

@ -38,6 +38,8 @@ class XmlReferenceDumperTest extends TestCase
return str_replace("\n", PHP_EOL, <<<'EOL'
<!-- Namespace: http://example.org/schema/dic/acme_root -->
<!-- scalar-required: Required -->
<!-- scalar-deprecated: Deprecated (The child node "scalar_deprecated" at path "acme_root.scalar_deprecated" is deprecated.) -->
<!-- scalar-deprecated-with-message: Deprecated (Deprecation custom message for "scalar_deprecated_with_message" at "acme_root.scalar_deprecated_with_message") -->
<!-- enum-with-default: One of "this"; "that" -->
<!-- enum: One of "this"; "that" -->
<config
@ -50,6 +52,8 @@ class XmlReferenceDumperTest extends TestCase
scalar-array-empty=""
scalar-array-defaults="elem1,elem2"
scalar-required=""
scalar-deprecated=""
scalar-deprecated-with-message=""
node-with-a-looong-name=""
enum-with-default="this"
enum=""

View File

@ -98,6 +98,8 @@ acme_root:
- elem1
- elem2
scalar_required: ~ # Required
scalar_deprecated: ~ # Deprecated (The child node "scalar_deprecated" at path "acme_root.scalar_deprecated" is deprecated.)
scalar_deprecated_with_message: ~ # Deprecated (Deprecation custom message for "scalar_deprecated_with_message" at "acme_root.scalar_deprecated_with_message")
node_with_a_looong_name: ~
enum_with_default: this # One of "this"; "that"
enum: ~ # One of "this"; "that"

View File

@ -35,6 +35,8 @@ class ExampleConfiguration implements ConfigurationInterface
->scalarNode('scalar_array_empty')->defaultValue(array())->end()
->scalarNode('scalar_array_defaults')->defaultValue(array('elem1', 'elem2'))->end()
->scalarNode('scalar_required')->isRequired()->end()
->scalarNode('scalar_deprecated')->setDeprecated()->end()
->scalarNode('scalar_deprecated_with_message')->setDeprecated('Deprecation custom message for "%node%" at "%path%"')->end()
->scalarNode('node_with_a_looong_name')->end()
->enumNode('enum_with_default')->values(array('this', 'that'))->defaultValue('this')->end()
->enumNode('enum')->values(array('this', 'that'))->end()

View File

@ -13,6 +13,7 @@ CHANGELOG
* Added `TranslationExtractorPass`
* Added `TranslatorPass`
* Added <notes> section to the Xliff 2.0 dumper.
* Improved Xliff 2.0 loader to load <notes> section.
* Added `TranslationWriterInterface`
* Deprecated `TranslationWriter::writeTranslations` in favor of `TranslationWriter::write`

View File

@ -127,7 +127,8 @@ class XliffFileLoader implements LoaderInterface
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0');
foreach ($xml->xpath('//xliff:unit/xliff:segment') as $segment) {
foreach ($xml->xpath('//xliff:unit') as $unit) {
$segment = $unit->segment;
$source = $segment->source;
// If the xlf file has another encoding specified, try to convert it because
@ -144,6 +145,18 @@ class XliffFileLoader implements LoaderInterface
}
}
if (isset($unit->notes)) {
$metadata['notes'] = array();
foreach ($unit->notes->note as $noteNode) {
$note = array();
foreach ($noteNode->attributes() as $key => $value) {
$note[$key] = (string) $value;
}
$note['content'] = (string) $noteNode;
$metadata['notes'][] = $note;
}
}
$catalogue->setMetadata((string) $source, $metadata, $domain);
}
}

View File

@ -93,12 +93,17 @@ class XliffFileDumperTest extends TestCase
$catalogue = new MessageCatalogue('en_US');
$catalogue->add(array(
'foo' => 'bar',
'baz' => 'biz',
));
$catalogue->setMetadata('foo', array('notes' => array(
array('category' => 'state', 'content' => 'new'),
array('category' => 'approved', 'content' => 'true'),
array('category' => 'section', 'content' => 'user login', 'priority' => '1'),
)));
$catalogue->setMetadata('baz', array('notes' => array(
array('id' => 'x', 'content' => 'x_content'),
array('appliesTo' => 'target', 'category' => 'quality', 'content' => 'Fuzzy'),
)));
$dumper = new XliffFileDumper();

View File

@ -188,4 +188,44 @@ class XliffFileLoaderTest extends TestCase
// target attributes
$this->assertEquals(array('target-attributes' => array('order' => 1)), $catalogue->getMetadata('bar', 'domain1'));
}
public function testLoadVersion2WithNoteMeta()
{
$loader = new XliffFileLoader();
$resource = __DIR__.'/../fixtures/resources-notes-meta.xlf';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals(array(new FileResource($resource)), $catalogue->getResources());
$this->assertSame(array(), libxml_get_errors());
// test for "foo" metadata
$this->assertTrue($catalogue->defines('foo', 'domain1'));
$metadata = $catalogue->getMetadata('foo', 'domain1');
$this->assertNotEmpty($metadata);
$this->assertCount(3, $metadata['notes']);
$this->assertEquals('state', $metadata['notes'][0]['category']);
$this->assertEquals('new', $metadata['notes'][0]['content']);
$this->assertEquals('approved', $metadata['notes'][1]['category']);
$this->assertEquals('true', $metadata['notes'][1]['content']);
$this->assertEquals('section', $metadata['notes'][2]['category']);
$this->assertEquals('1', $metadata['notes'][2]['priority']);
$this->assertEquals('user login', $metadata['notes'][2]['content']);
// test for "baz" metadata
$this->assertTrue($catalogue->defines('baz', 'domain1'));
$metadata = $catalogue->getMetadata('baz', 'domain1');
$this->assertNotEmpty($metadata);
$this->assertCount(2, $metadata['notes']);
$this->assertEquals('x', $metadata['notes'][0]['id']);
$this->assertEquals('x_content', $metadata['notes'][0]['content']);
$this->assertEquals('target', $metadata['notes'][1]['appliesTo']);
$this->assertEquals('quality', $metadata['notes'][1]['category']);
$this->assertEquals('Fuzzy', $metadata['notes'][1]['content']);
}
}

View File

@ -12,5 +12,15 @@
<target>bar</target>
</segment>
</unit>
<unit id="uqWglk0">
<notes>
<note id="x">x_content</note>
<note appliesTo="target" category="quality">Fuzzy</note>
</notes>
<segment>
<source>baz</source>
<target>biz</target>
</segment>
</unit>
</file>
</xliff>