ソースを参照

Enhance RulesProvider to conditionally include context.

Extend RulesProvider to allow the context parameter to be passed in an
opt-in basis. By using reflection we can access the last parameter's
name and if it is context pass the context in. This allows context aware
validation methods to be implemented in the core Validation class.

Refs #5746
Mark Story 11 年 前
コミット
4098e56e86

+ 25 - 8
src/Validation/RulesProvider.php

@@ -14,21 +14,29 @@
  */
 namespace Cake\Validation;
 
+use ReflectionClass;
+
 /**
  * A Proxy class used to remove any extra arguments when the user intended to call
  * a method in another class that is not aware of validation providers signature
  */
 class RulesProvider
 {
-
     /**
-     * The class to proxy, defaults to \Cake\Validation\Validation in construction
+     * The class/object to proxy.
      *
-     * @var object
+     * @var mixed
      */
     protected $_class;
 
     /**
+     * The proxied class' reflection
+     *
+     * @var \ReflectionClass
+     */
+    protected $_reflection;
+
+    /**
      * Constructor, sets the default class to use for calling methods
      *
      * @param string $class the default class to proxy
@@ -36,12 +44,16 @@ class RulesProvider
     public function __construct($class = '\Cake\Validation\Validation')
     {
         $this->_class = $class;
+        $this->_reflection = new ReflectionClass($class);
     }
 
     /**
-     * Proxies validation method calls to the Validation class, it slices
-     * the arguments array to avoid passing more arguments than required to
-     * the validation methods.
+     * Proxies validation method calls to the Validation class.
+     *
+     * The last argument (context) will be sliced off, if the validation
+     * method's last parameter is not named 'context'. This lets
+     * the various wrapped validation methods to not receive the validation
+     * context unless they need it.
      *
      * @param string $method the validation method to call
      * @param array $arguments the list of arguments to pass to the method
@@ -49,7 +61,12 @@ class RulesProvider
      */
     public function __call($method, $arguments)
     {
-        $arguments = array_slice($arguments, 0, -1);
-        return call_user_func_array([$this->_class, $method], $arguments);
+        $method = $this->_reflection->getMethod($method);
+        $argumentList = $method->getParameters();
+        if (array_pop($argumentList)->getName() !== 'context') {
+            $arguments = array_slice($arguments, 0, -1);
+        }
+        $object = is_string($this->_class) ? null : $this->_class;
+        return $method->invokeArgs($object, $arguments);
     }
 }

+ 1 - 1
tests/TestCase/Validation/RulesProviderTest.php

@@ -48,7 +48,7 @@ class RulesProviderTest extends TestCase
     {
         $mock = $this->getMock('\Cake\Validation\Validator', ['field']);
         $mock->expects($this->once())
-            ->method('Field')
+            ->method('field')
             ->with('first', null)
             ->will($this->returnValue(true));
 

+ 20 - 0
tests/TestCase/Validation/ValidatorTest.php

@@ -669,4 +669,24 @@ class ValidatorTest extends TestCase
         $this->assertInstanceOf('Cake\Validation\ValidationSet', $set);
         $this->assertCount(2, $set);
     }
+
+    /**
+     * Integration test for compareWith validator.
+     *
+     * @return void
+     */
+    public function testCompareWithIntegration()
+    {
+        $validator = new Validator;
+        $validator->add('password', [
+            'compare' => [
+                'rule' => ['compareWith', 'password_compare']
+            ],
+        ]);
+        $data = [
+            'password' => 'test',
+            'password_compare' => 'not the same'
+        ];
+        $this->assertNotEmpty($validator->errors($data), 'Validation should fail.');
+    }
 }