Repository: laravel/framework (php)

Issue:
Password validation rule fails on valid data if reused
<!-- DO NOT THROW THIS AWAY -->
<!-- Fill out the FULL versions with patch versions -->

- Laravel Version: 8.54
- PHP Version: 8.0.11
- Database Driver & Version: n/a

### Description:
If a [Password validation rule](https://github.com/laravel/framework/pull/36960) fails once and the same rule object is later reused, then it will continue to fail on any subsequent validation run even if the input is valid.

### Steps To Reproduce:

```php
$rules = [
    'password' => Password::default(),
];

$validator1 = Validator::make(['password' => '1234'], $rules);
// Validation fails as expected: Password too short.
$this->assertTrue($validator1->fails());

$validator2 = Validator::make(['password' => '12341234'], $rules);
// Should pass, but it does not. Validation fails again with: Password too short.
$this->assertFalse($validator2->fails());
```

<!-- If possible, please provide a GitHub repository to demonstrate your issue -->
https://github.com/hbgl/laravel-validation-password-stateful

<!-- laravel new bug-report --github="--public" -->

### Suggested fix

TLDR: Clear the `$messages` field [inside the Password class](https://github.com/laravel/framework/blob/ca6c099e89d471507b3169aad807ec90b25fd641/src/Illuminate/Validation/Rules/Password.php#L82) on each call to `passes`.

The Password rule internally keeps a list or messages that are populated within the [`passes` function](https://github.com/laravel/framework/blob/ca6c099e89d471507b3169aad807ec90b25fd641/src/Illuminate/Validation/Rules/Password.php#L269). The messages are, however, not reset upon repeated calls to `passes`, which causes the rule to fail on valid data with the same error messages as before. The messages should be reset each time the `passes` function is called.

I think it is generally good practice for validation rules to **not** carry observable state over multiple validation runs. That means that a rule should either be completely stateless or carefully reset itself when called again. Maybe this should also be mentioned as a warning in [the custom validation rules](https://laravel.com/docs/8.x/validation#custom-validation-rules) documentation

Pull request:
[8.x] Fix #38949
Hi,
This PR should fix #38949. I've added a test which fails without adding the following line
```diff
public function passes($attribute, $value)
{
+        $this->messages = [];
...
}
```


Patch:
diff --git a/src/Illuminate/Validation/Rules/Password.php b/src/Illuminate/Validation/Rules/Password.php
index cf13e90a6a..829c2d8231 100644
--- a/src/Illuminate/Validation/Rules/Password.php
+++ b/src/Illuminate/Validation/Rules/Password.php
@@ -268,6 +268,8 @@ public function symbols()
      */
     public function passes($attribute, $value)
     {
+        $this->messages = [];
+
         $validator = Validator::make($this->data, [
             $attribute => 'string|min:'.$this->min,
         ], $this->validator->customMessages, $this->validator->customAttributes);


Test patch:
diff --git a/tests/Validation/ValidationPasswordRuleTest.php b/tests/Validation/ValidationPasswordRuleTest.php
index d484226e2d..c9eb2fbafa 100644
--- a/tests/Validation/ValidationPasswordRuleTest.php
+++ b/tests/Validation/ValidationPasswordRuleTest.php
@@ -232,6 +232,29 @@ public function testItCannotSetDefaultUsingGivenString()
         Password::defaults('required|password');
     }
 
+    public function testItPassesWithValidDataIfTheSameValidationRulesAreReused()
+    {
+        $rules = [
+            'password' => Password::default(),
+        ];
+
+        $v = new Validator(
+            resolve('translator'),
+            ['password' => '1234'],
+            $rules
+        );
+
+        $this->assertFalse($v->passes());
+
+        $v1 = new Validator(
+            resolve('translator'),
+            ['password' => '12341234'],
+            $rules
+        );
+
+        $this->assertTrue($v1->passes());
+    }
+
     protected function passes($rule, $values)
     {
         $this->assertValidationRules($rule, $values, true, []);