Skip to content

Commit

Permalink
feature #19086 [FrameworkBundle] add "mapping" configuration key at v…
Browse files Browse the repository at this point in the history
…alidation secti… (davewwww)

This PR was merged into the 3.3-dev branch.

Discussion
----------

[FrameworkBundle] add "mapping" configuration key at validation secti…

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

This feature allows you, to define additional validation files or directories which are not in the 'Bundle*/Resources/config/' directory.

``` yaml
#config.yml
framework:
  validation:
    mapping:
      paths:
        - "path/to/file/validation.yml"
        - "path/to/file/validation.xml"
        - "path/to/another/directory"
```

Commits
-------

d696cfb [FrameworkBundle] Configurable paths for validation files
60d7d43 fix merge
61475b5 Merge branch '3.2'
ba41e70 Merge branch '3.1' into 3.2
4268aba Merge branch '2.8' into 3.1
3faf655 Merge branch '2.7' into 2.8
e95fc09 fix getMock usage
482828c fix merge
ed5eb6d bug #21372 [DependencyInjection] Fixed variadic method parameter in autowired classes (brainexe)
a7f63de [DependencyInjection] Fixed variadic method parameter in autowired classes
9ef4271 minor #21371 [Validator] update German translation (xabbuh)
f920e61 update German translation
41c72ab minor #21335 [Validator] Improved error message for missing upload_tmp_dir (Breuls)
afbf227 [Validator] Improved error message for missing upload_tmp_dir
  • Loading branch information
fabpot committed Jan 23, 2017
2 parents 71b8a66 + d696cfb commit 3697e1e
Show file tree
Hide file tree
Showing 26 changed files with 173 additions and 48 deletions.
1 change: 1 addition & 0 deletions src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
Expand Up @@ -12,6 +12,7 @@ CHANGELOG
is disabled.
* Added `GlobalVariables::getToken()`
* Deprecated `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass`. Use `Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass` instead.
* Added configurable paths for validation files

3.2.0
-----
Expand Down
Expand Up @@ -635,6 +635,15 @@ private function addValidationSection(ArrayNodeDefinition $rootNode)
->end()
->scalarNode('translation_domain')->defaultValue('validators')->end()
->booleanNode('strict_email')->defaultFalse()->end()
->arrayNode('mapping')
->addDefaultsIfNotSet()
->fixXmlConfig('path')
->children()
->arrayNode('paths')
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
->end()
->end()
Expand Down
Expand Up @@ -947,13 +947,16 @@ private function registerValidationConfiguration(array $config, ContainerBuilder

$container->setParameter('validator.translation_domain', $config['translation_domain']);

list($xmlMappings, $yamlMappings) = $this->getValidatorMappingFiles($container);
if (count($xmlMappings) > 0) {
$validatorBuilder->addMethodCall('addXmlMappings', array($xmlMappings));
$files = array('xml' => array(), 'yml' => array());
$this->getValidatorMappingFiles($container, $files);
$this->getValidatorMappingFilesFromConfig($config, $files);

if (!empty($files['xml'])) {
$validatorBuilder->addMethodCall('addXmlMappings', array($files['xml']));
}

if (count($yamlMappings) > 0) {
$validatorBuilder->addMethodCall('addYamlMappings', array($yamlMappings));
if (!empty($files['yml'])) {
$validatorBuilder->addMethodCall('addYamlMappings', array($files['yml']));
}

$definition = $container->findDefinition('validator.email');
Expand Down Expand Up @@ -987,41 +990,58 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
}
}

private function getValidatorMappingFiles(ContainerBuilder $container)
private function getValidatorMappingFiles(ContainerBuilder $container, array &$files)
{
$files = array(array(), array());

if (interface_exists('Symfony\Component\Form\FormInterface')) {
$reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface');
$files[0][] = dirname($reflClass->getFileName()).'/Resources/config/validation.xml';
$container->addResource(new FileResource($files[0][0]));
$files['xml'][] = $file = dirname($reflClass->getFileName()).'/Resources/config/validation.xml';
$container->addResource(new FileResource($file));
}

foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
$dirname = $bundle['path'];
if (is_file($file = $dirname.'/Resources/config/validation.xml')) {
$files[0][] = $file;

if (is_file($file = $dirname.'/Resources/config/validation.yml')) {
$files['yml'][] = $file;
$container->addResource(new FileResource($file));
}

if (is_file($file = $dirname.'/Resources/config/validation.yml')) {
$files[1][] = $file;
if (is_file($file = $dirname.'/Resources/config/validation.xml')) {
$files['xml'][] = $file;
$container->addResource(new FileResource($file));
}

if (is_dir($dir = $dirname.'/Resources/config/validation')) {
foreach (Finder::create()->followLinks()->files()->in($dir)->name('*.xml') as $file) {
$files[0][] = $file->getPathname();
}
foreach (Finder::create()->followLinks()->files()->in($dir)->name('*.yml') as $file) {
$files[1][] = $file->getPathname();
}

$this->getValidatorMappingFilesFromDir($dir, $files);
$container->addResource(new DirectoryResource($dir));
}
}
}

private function getValidatorMappingFilesFromDir($dir, array &$files)
{
foreach (Finder::create()->followLinks()->files()->in($dir)->name('/\.(xml|ya?ml)$/') as $file) {
$extension = $file->getExtension();
$files['yaml' === $extension ? 'yml' : $extension][] = $file->getRealpath();
}
}

return $files;
private function getValidatorMappingFilesFromConfig(array $config, array &$files)
{
foreach ($config['mapping']['paths'] as $path) {
if (is_dir($path)) {
$this->getValidatorMappingFilesFromDir($path, $files);
} elseif (is_file($path)) {
if (preg_match('/\.(xml|ya?ml)$/', $path, $matches)) {
$extension = $matches[1];
$files['yaml' === $extension ? 'yml' : $extension][] = $path;
} else {
throw new \RuntimeException(sprintf('Unsupported mapping type in "%s", supported types are XML & Yaml.', $path));
}
} else {
throw new \RuntimeException(sprintf('Could not open file or directory "%s".', $path));
}
}
}

private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container, $loader)
Expand Down
Expand Up @@ -176,6 +176,7 @@
<xsd:complexType name="validation">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="static-method" type="xsd:string" />
<xsd:element name="mapping" type="validation_mapping" />
</xsd:choice>

<xsd:attribute name="enabled" type="xsd:boolean" />
Expand All @@ -184,6 +185,12 @@
<xsd:attribute name="static-method" type="xsd:boolean" />
</xsd:complexType>

<xsd:complexType name="validation_mapping">
<xsd:sequence>
<xsd:element name="path" type="xsd:string" minOccurs="1" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="annotations">
<xsd:attribute name="cache" type="xsd:string" />
<xsd:attribute name="debug" type="xsd:string" />
Expand Down
Expand Up @@ -212,6 +212,9 @@ protected static function getBundleDefaultConfig()
'static_method' => array('loadValidatorMetadata'),
'translation_domain' => 'validators',
'strict_email' => false,
'mapping' => array(
'paths' => array(),
),
),
'annotations' => array(
'cache' => 'php_array',
Expand Down
@@ -0,0 +1,13 @@
<?php

$container->loadFromExtension('framework', array(
'validation' => array(
'mapping' => array(
'paths' => array(
'%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/files',
'%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml',
'%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml',
),
),
),
));
@@ -0,0 +1,16 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony">

<framework:config>
<framework:validation>
<framework:mapping>
<framework:path>%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/files</framework:path>
<framework:path>%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml</framework:path>
<framework:path>%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml</framework:path>
</framework:mapping>
</framework:validation>
</framework:config>
</container>
@@ -0,0 +1,7 @@
framework:
validation:
mapping:
paths:
- "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/files"
- "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml"
- "%kernel.root_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml"
Expand Up @@ -584,6 +584,22 @@ public function testValidationNoStaticMethod()
// no cache, no annotations, no static methods
}

public function testValidationMapping()
{
$container = $this->createContainerFromFile('validation_mapping');

$calls = $container->getDefinition('validator.builder')->getMethodCalls();

$this->assertSame('addXmlMappings', $calls[3][0]);
$this->assertCount(2, $calls[3][1][0]);

$this->assertSame('addYamlMappings', $calls[4][0]);
$this->assertCount(3, $calls[4][1][0]);
$this->assertContains('foo.yml', $calls[4][1][0][0]);
$this->assertContains('validation.yml', $calls[4][1][0][1]);
$this->assertContains('validation.yaml', $calls[4][1][0][2]);
}

public function testFormsCanBeEnabledWithoutCsrfProtection()
{
$container = $this->createContainerFromFile('form_no_csrf');
Expand Down
@@ -1,11 +1,9 @@
alias_1
~~~~~~~
### alias_1

- Service: `service_1`
- Public: yes

service_1
~~~~~~~~~
### service_1

- Class: `Full\Qualified\Class1`
- Public: yes
Expand Down
@@ -1,11 +1,9 @@
alias_2
~~~~~~~
### alias_2

- Service: `service_2`
- Public: no

service_2
~~~~~~~~~
### service_2

- Class: `Full\Qualified\Class2`
- Public: no
Expand Down
Expand Up @@ -4,8 +4,7 @@ Public services
Definitions
-----------

definition_1
~~~~~~~~~~~~
### definition_1

- Class: `Full\Qualified\Class1`
- Public: yes
Expand All @@ -22,14 +21,12 @@ definition_1
Aliases
-------

alias_1
~~~~~~~
### alias_1

- Service: `service_1`
- Public: yes

alias_2
~~~~~~~
### alias_2

- Service: `service_2`
- Public: no
Expand Down
Expand Up @@ -21,15 +21,20 @@ public function testProcessDoesNotDropExistingFileLoaderMethodCalls()
{
$container = new ContainerBuilder();
$container->setParameter('kernel.debug', false);
$container->setParameter('kernel.root_dir', __DIR__);

$container->register('twig.app_variable', '\Symfony\Bridge\Twig\AppVariable');
$container->register('templating', '\Symfony\Bundle\TwigBundle\TwigEngine');
$container->register('twig.extension.yaml');
$container->register('twig.extension.debug.stopwatch');
$container->register('twig.extension.expression');

$nativeTwigLoader = new Definition('\Twig_Loader_Filesystem');
$nativeTwigLoader->addMethodCall('addPath', array());
$container->setDefinition('twig.loader.native_filesystem', $nativeTwigLoader);

$filesystemLoader = new Definition('\Symfony\Bundle\TwigBundle\Loader\FilesystemLoader');
$filesystemLoader->setArguments(array(null, null, null));
$filesystemLoader->addMethodCall('addPath', array());
$container->setDefinition('twig.loader.filesystem', $filesystemLoader);

Expand Down
Expand Up @@ -255,7 +255,7 @@ function () {},

// assertEquals() does not like NAN values.
$this->assertEquals($array[$i][0], 'float');
$this->assertNan($array[$i++][1]);
$this->assertTrue(is_nan($array[$i++][1]));
}

public function testRecursionInArguments()
Expand Down
Expand Up @@ -428,10 +428,11 @@ private static function getResourceMetadataForMethod(\ReflectionMethod $method)
$class = false;
}

$isVariadic = method_exists($parameter, 'isVariadic') && $parameter->isVariadic();
$methodArgumentsMetadata[] = array(
'class' => $class,
'isOptional' => $parameter->isOptional(),
'defaultValue' => $parameter->isOptional() ? $parameter->getDefaultValue() : null,
'defaultValue' => ($parameter->isOptional() && !$isVariadic) ? $parameter->getDefaultValue() : null,
);
}

Expand Down
Expand Up @@ -14,6 +14,7 @@
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic;

/**
* @author Kévin Dunglas <dunglas@gmail.com>
Expand All @@ -35,6 +36,23 @@ public function testProcess()
$this->assertEquals('foo', (string) $container->getDefinition('bar')->getArgument(0));
}

/**
* @requires PHP 5.6
*/
public function testProcessVariadic()
{
$container = new ContainerBuilder();
$container->register('foo', Foo::class);
$definition = $container->register('fooVariadic', FooVariadic::class);
$definition->setAutowired(true);

$pass = new AutowirePass();
$pass->process($container);

$this->assertCount(1, $container->getDefinition('fooVariadic')->getArguments());
$this->assertEquals('foo', (string) $container->getDefinition('fooVariadic')->getArgument(0));
}

public function testProcessAutowireParent()
{
$container = new ContainerBuilder();
Expand Down
@@ -0,0 +1,16 @@
<?php

namespace Symfony\Component\DependencyInjection\Tests\Fixtures\includes;

use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;

class FooVariadic
{
public function __construct(Foo $foo)
{
}

public function bar(...$arguments)
{
}
}
Expand Up @@ -156,14 +156,14 @@ public function testLoadUserByUsernameFailsIfMoreThanOneLdapPasswordsInEntry()
*/
public function testLoadUserByUsernameFailsIfEntryHasNoUidKeyAttribute()
{
$result = $this->getMock(CollectionInterface::class);
$query = $this->getMock(QueryInterface::class);
$result = $this->getMockBuilder(CollectionInterface::class)->getMock();
$query = $this->getMockBuilder(QueryInterface::class)->getMock();
$query
->expects($this->once())
->method('execute')
->will($this->returnValue($result))
;
$ldap = $this->getMock(LdapInterface::class);
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
$result
->expects($this->once())
->method('offsetGet')
Expand Down Expand Up @@ -321,14 +321,14 @@ public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttributeAndWro

public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute()
{
$result = $this->getMock(CollectionInterface::class);
$query = $this->getMock(QueryInterface::class);
$result = $this->getMockBuilder(CollectionInterface::class)->getMock();
$query = $this->getMockBuilder(QueryInterface::class)->getMock();
$query
->expects($this->once())
->method('execute')
->will($this->returnValue($result))
;
$ldap = $this->getMock(LdapInterface::class);
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
$result
->expects($this->once())
->method('offsetGet')
Expand Down

0 comments on commit 3697e1e

Please sign in to comment.