feature #19086 [FrameworkBundle] add "mapping" configuration key at validation 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 | https://github.com/symfony/symfony-docs/pull/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
-------

d696cfb04c [FrameworkBundle] Configurable paths for validation files
60d7d437b5 fix merge
61475b5596 Merge branch '3.2'
ba41e706ad Merge branch '3.1' into 3.2
4268abacf4 Merge branch '2.8' into 3.1
3faf655638 Merge branch '2.7' into 2.8
e95fc09b3c fix getMock usage
482828ce29 fix merge
ed5eb6db54 bug #21372 [DependencyInjection] Fixed variadic method parameter in autowired classes (brainexe)
a7f63de414 [DependencyInjection] Fixed variadic method parameter in autowired classes
9ef427150c minor #21371 [Validator] update German translation (xabbuh)
f920e61d35 update German translation
41c72ab909 minor #21335 [Validator] Improved error message for missing upload_tmp_dir (Breuls)
afbf22746a [Validator] Improved error message for missing upload_tmp_dir
This commit is contained in:
Fabien Potencier 2017-01-23 10:23:15 -08:00
commit 3697e1e154
26 changed files with 173 additions and 48 deletions

View File

@ -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
-----

View File

@ -635,6 +635,15 @@ class Configuration implements ConfigurationInterface
->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()

View File

@ -947,13 +947,16 @@ class FrameworkExtension extends Extension
$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');
@ -987,41 +990,58 @@ class FrameworkExtension extends Extension
}
}
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));
}
}
}
return $files;
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();
}
}
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)

View File

@ -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" />
@ -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" />

View File

@ -212,6 +212,9 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
'static_method' => array('loadValidatorMetadata'),
'translation_domain' => 'validators',
'strict_email' => false,
'mapping' => array(
'paths' => array(),
),
),
'annotations' => array(
'cache' => 'php_array',

View File

@ -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',
),
),
),
));

View File

@ -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>

View File

@ -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"

View File

@ -584,6 +584,22 @@ abstract class FrameworkExtensionTest extends TestCase
// 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');

View File

@ -1,11 +1,9 @@
alias_1
~~~~~~~
### alias_1
- Service: `service_1`
- Public: yes
service_1
~~~~~~~~~
### service_1
- Class: `Full\Qualified\Class1`
- Public: yes

View File

@ -1,11 +1,9 @@
alias_2
~~~~~~~
### alias_2
- Service: `service_2`
- Public: no
service_2
~~~~~~~~~
### service_2
- Class: `Full\Qualified\Class2`
- Public: no

View File

@ -4,8 +4,7 @@ Public services
Definitions
-----------
definition_1
~~~~~~~~~~~~
### definition_1
- Class: `Full\Qualified\Class1`
- Public: yes
@ -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

View File

@ -21,15 +21,20 @@ class ExtensionPassTest extends \PHPUnit_Framework_TestCase
{
$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);

View File

@ -255,7 +255,7 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
// 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()

View File

@ -428,10 +428,11 @@ class AutowirePass implements CompilerPassInterface
$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,
);
}

View File

@ -14,6 +14,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler;
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>
@ -35,6 +36,23 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase
$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();

View File

@ -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)
{
}
}

View File

@ -156,14 +156,14 @@ class LdapUserProviderTest extends \PHPUnit_Framework_TestCase
*/
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')
@ -321,14 +321,14 @@ class LdapUserProviderTest extends \PHPUnit_Framework_TestCase
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')

View File

@ -192,7 +192,7 @@
</trans-unit>
<trans-unit id="51">
<source>No temporary folder was configured in php.ini.</source>
<target>Es wurde kein temporärer Ordner in der php.ini konfiguriert.</target>
<target>Es wurde kein temporärer Ordner in der php.ini konfiguriert oder der temporäre Ordner existiert nicht.</target>
</trans-unit>
<trans-unit id="52">
<source>Cannot write temporary file to disk.</source>

View File

@ -192,7 +192,7 @@
</trans-unit>
<trans-unit id="51">
<source>No temporary folder was configured in php.ini.</source>
<target>No temporary folder was configured in php.ini.</target>
<target>No temporary folder was configured in php.ini, or the configured folder does not exist.</target>
</trans-unit>
<trans-unit id="52">
<source>Cannot write temporary file to disk.</source>

View File

@ -192,7 +192,7 @@
</trans-unit>
<trans-unit id="51">
<source>No temporary folder was configured in php.ini.</source>
<target>Er is geen tijdelijke map geconfigureerd in php.ini.</target>
<target>Er is geen tijdelijke map geconfigureerd in php.ini, of de gespecificeerde map bestaat niet.</target>
</trans-unit>
<trans-unit id="52">
<source>Cannot write temporary file to disk.</source>

View File

@ -192,7 +192,7 @@
</trans-unit>
<trans-unit id="51">
<source>No temporary folder was configured in php.ini.</source>
<target>Nie skonfigurowano folderu tymczasowego w php.ini.</target>
<target>Nie skonfigurowano folderu tymczasowego w php.ini, lub skonfigurowany folder nie istnieje.</target>
</trans-unit>
<trans-unit id="52">
<source>Cannot write temporary file to disk.</source>