Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

theilen's avatar

Form macro triggers CSRF filter in Codeception

So I deciced to implement form macros and I ran into a strange problem. I've got no idea where exactly the problem is, maybe someone can help:

I store the form macros in app/formMacros.php and load that file in app/start/global.php:

require app_path() . '/formMacros.php';

That works, the file is loaded, I can define form macros and use them in my views. But if I define a form macro in that file, one of my functional tests goes red.

The test looks like this:

$I = new FunctionalTester($scenario);
$I->am('authenticated user');
$I->wantTo('access a route if authorized');

$I->enableRouteFilters();

$I->haveRecord('users', ['username' => 'foo', 'password' => Hash::make('bar'), 'role_id' => '1']);
$I->haveRecord('authorizations', ['route' => 'foobar', 'role_id' => '1', 'allowed' => '1']);

$I->amOnRoute('home');
$I->fillField("form#login-form input[name='username']", 'foo');
$I->fillField("form#login-form input[name='password']", 'bar');
$I->click("form#login-form input[type='submit']");

$I->amOnRoute('foobar');
$I->dontSeeElement('div.alert-danger');
$I->seeCurrentRouteIs('foobar');

and it fails with:

1) Failed to access a route if authorized in [...]
Couldn't click "form#login-form input[type='submit']":
Illuminate\Session\TokenMismatchException: 

Scenario Steps:
8. I click "form#login-form input[type='submit']"
7. I fill field "form#login-form input[name='password']","bar"
6. I fill field "form#login-form input[name='username']","foo"
5. I am on route "home"

So it seems the CSRF filter is triggered. I load them in my routes.php:

Route::when('*', 'csrf', array('post', 'put', 'delete'));

The Exception is only thrown if there are macro definitions in formMacros.php. It doesn't matter what names these macros have. Just putting this in it is enough:

Form::macro('someRandomMacroNameWhichIsProblablyNotUsedAnyWhere', function() {});

And it doesn't matter if I actually use any of these macros. The login form that triggers the exception doesn't use any form macros at all.

If I try to login in the browser, it works just fine, no exception is thrown.

So I'm a bit lost here. Has maybe anyone an idea?

0 likes
4 replies
theilen's avatar

It seems that the CSRF filter is triggered because the _token field is empty. A var_dump(Input::all()); in the CSRF closure shows this:

array(3) {
  '_token' =>
  string(0) ""
  'username' =>
  string(3) "foo"
  'password' =>
  string(3) "bar"
}

If I remove all macro definitions from formMacros.php the _token field is filled.

Still no idea why this happens...

theilen's avatar

I was able to boil it down a little bit more by adding this to my test:

[...]
$I->amOnRoute('home');

$token = $I->grabValueFrom("form#login-form input[name='_token']");
dd($token);

The token field is empty if there is a form macro definition in formMacros.php. If I remove the macro definition, the token field is filled properly.

Still no clue why this happens though... :-)

theilen's avatar

OK, after spending a few hours digging through the depths of the source code, I still haven't got the slightest idea what goes on here and why the csrf-token disappears in codeception tests when a form macro is registered somewhere.

So for now I give up and go back to using partials instead of form macros.

But if anybody could try and reproduce this behaviour or even come up with a solution, I would greatly appreciate that... ;-)

Please or to participate in this conversation.