-
Notifications
You must be signed in to change notification settings - Fork 11.7k
Description
- Laravel Version: 5.6.29
- PHP Version: 7.2.7-1
- Database Driver & Version: MySQL / not relevant
Container calls afterResolving callback twice when a contract gets resolved
Demonstrated here by using FormRequests, which validate twice as a result of the problem.
(Also see ContainerTest below.)
A request that is built via reflection gets validated once - as expected.
A request that is built via resolving a registered contract gets validated twice.
Steps To Reproduce:
Create the following classes in their default directories
in a fresh Laravel installation.
(I installed via composer)
- Create a DemoRule
class DemoRule implements Rule
{
public function passes($attribute, $value)
{
echo "Verified rule with $attribute: and value: $value";
echo '<br/><br/>';
return true;
}
public function message()
{
return [];
}
}
- Create a ResolveViaServiceContract
interface ResolveViaServiceContract
{
}
- Create ResolveViaServiceContractRequest
class ResolveViaServiceContractRequest extends FormRequest implements ResolveViaServiceContract
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'demoAttribute' => [new DemoRule()]
];
}
}
- To demonstrate that resolution via reflection validates once as expected
create a ResolveViaReflectionRequest
class ResolveViaReflectionRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'demoAttribute' => [new DemoRule()]
];
}
}
- Register the contract in your AppServiceProvider
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('App\Http\Requests\ResolveViaServiceContract', ResolveViaServiceContractRequest::class);
}
}
- Add forms to your welcome.blade
<body>
<form action="/reflection" method="post">
@csrf
<input type="text" name="demoAttribute">
<input type="submit" value="Via Reflection">
</form>
<form action="/service" method="post">
@csrf
<input type="text" name="demoAttribute">
<input type="submit" value="Via ServiceContract">
</form>
...
</body>
- Extend the web routes
Route::get('/', function () {return view('welcome');});
Route::post('/reflection', 'DemoController@resolveViaReflection');
Route::post('/service', 'DemoController@resolveViaServiceContract');
- Add a DemoController to handle the routes and to have the requests built
class DemoController extends Controller
{
public function resolveViaReflection(ResolveViaReflectionRequest $request)
{
return "Done via reflection";
}
public function resolveViaServiceContract(ResolveViaServiceContract $request)
{
return "Done via service contract";
}
}
-
Start the app
http://localhost -
Hit the "Via Reflection" button
Observe "Verified rule with demoAttribute: and value:" is printed once - as expected. -
Hit the "Via Service Contract" button
Observe "Verified rule with demoAttribute: and value:" is printed twice.
Expected the request to also be validated only once.
Conclusion
While this validates the request more often than needed, it is also a problem when you have a validation rule that evaluates to true the first time, but cannot be executed a second time and evaluates to false. As in my case with Google reCaptcha.