Zend\Session error with phpunit and php 7.2 (but not <= 7.1)

zend-mvc

#1

I’m trying to update my zend-mvc project to run with PHP 7.2, and it seems all good in the web environment, but when I run phpunit tests, I encounter errors like:

1) ApplicationTest\Controller\AuthControllerTest::testRedirectionFollowingAuthentication
Zend\ServiceManager\Exception\ServiceNotCreatedException: Service with name "Zend\Session\Config\ConfigInterface" could not be created. Reason: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time

/opt/www/court-interpreters-office/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:771
/opt/www/court-interpreters-office/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:200
/opt/www/court-interpreters-office/vendor/zendframework/zend-session/src/Service/SessionManagerFactory.php:75
/opt/www/court-interpreters-office/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:764
/opt/www/court-interpreters-office/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:200
/opt/www/court-interpreters-office/vendor/zendframework/zend-session/src/Service/ContainerAbstractServiceFactory.php:160
/opt/www/court-interpreters-office/vendor/zendframework/zend-session/src/Service/ContainerAbstractServiceFactory.php:100
/opt/www/court-interpreters-office/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:764
/opt/www/court-interpreters-office/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:200
/opt/www/court-interpreters-office/module/Admin/src/Module.php:104
/opt/www/court-interpreters-office/vendor/zendframework/zend-eventmanager/src/EventManager.php:322
/opt/www/court-interpreters-office/vendor/zendframework/zend-eventmanager/src/EventManager.php:179
/opt/www/court-interpreters-office/vendor/zendframework/zend-mvc/src/Application.php:311
/opt/www/court-interpreters-office/vendor/zendframework/zend-test/src/PHPUnit/Controller/AbstractControllerTestCase.php:310
/opt/www/court-interpreters-office/module/InterpretersOffice/test/Controller/AuthControllerTest.php:70

Caused by
PHPUnit\Framework\Error\Warning: ini_set(): Headers already sent. You cannot change the session module's ini settings at this time[...]
/opt/www/court-interpreters-office/vendor/zendframework/zend-session/src/Service/ContainerAbstractServiceFactory.php:160
/opt/www/court-interpreters-office/vendor/zendframework/zend-session/src/Service/ContainerAbstractServiceFactory.php:100
/opt/www/court-interpreters-office/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:764
/opt/www/court-interpreters-office/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:200
/opt/www/court-interpreters-office/module/Admin/src/Module.php:104
/opt/www/court-interpreters-office/vendor/zendframework/zend-eventmanager/src/EventManager.php:322
/opt/www/court-interpreters-office/vendor/zendframework/zend-eventmanager/src/EventManager.php:179
/opt/www/court-interpreters-office/vendor/zendframework/zend-mvc/src/Application.php:311
/opt/www/court-interpreters-office/vendor/zendframework/zend-test/src/PHPUnit/Controller/AbstractControllerTestCase.php:310
/opt/www/court-interpreters-office/module/InterpretersOffice/test/Controller/AuthControllerTest.php:70

I have read https://github.com/zendframework/zend-session/issues/104, but before I start trying to re-wire my session stuff, I was hoping someone could confirm that this looks like the same issue.

The line in my application code that sets it off is in a Module.php's onBootstrap() where I check authentication and authorization. The logic is pretty standard. If they’re not logged in and requesting something for which authentication is required, I save the current url in a session variable and redirect them to the login page so that on successful login I know where to send them back.

For setting up sessions I have borrowed the technique found in https://olegkrivtsov.github.io/using-zend-framework-3-book/html/en/Working_with_Sessions/Session_Manager.html. I don’t have any factory for the SessionManager registered anywhere in my configuration so I am assuming (yes I ought to understand this better) there is some abstract factory magic happening.

But, again, I’m a little confused/uncertain and hoping to confirm that the approach suggested in the discussion at https://github.com/zendframework/zend-session/issues/104 is the way to go.


#2

OK, after a full two days of struggle, I found at least the proximate cause of my heartbreak and patched it up, and now my phpunit tests are happy under php 7.[0-2]. I was accessing the SessionManager from the service manager early in the cycle (as suggested in https://olegkrivtsov.github.io/using-zend-framework-3-book/html/en/Working_with_Sessions/Session_Manager.html) because that was supposed to be a good idea, so as to make it become the default session manager. The official docs https://docs.zendframework.com/zend-session/manager/ don’t seem to support that notion. And I still don’t have a complete grasp of all this plumbing. But… onwards!


#3

The same here. I am on it for second day. I’ve had the sessionManager instantiation in Module as well. Removed it, but with phpunit I still get an error. ini_set does not work. Strangely the application works. Its just phpUnit test that dont. Did u create as special factory for sessionManager?


#4

I’ve had to do something a little ugly. In the Module.php’s onBootstrap method, I check if ('testing' != getenv('environment')) before doing the thing that was causing trouble. I don’t like it when I don’t thoroughly understand how my own project works, but this seems to be working.


#5

I don’t think the session component should initialize itself at all while in CLI mode.

You may want to swap out the default session storage with an array-backed one: https://github.com/zendframework/zend-session/blob/2cfd90e1a2f6b066b9f908599251d8f64f07021b/src/Storage/ArrayStorage.php

According to the SessionManagerFactory (the one that is failing in your trace), you can swap out the storage by declaring it as a service: https://github.com/zendframework/zend-session/blob/2cfd90e1a2f6b066b9f908599251d8f64f07021b/src/Service/SessionManagerFactory.php#L86

When running tests, that means adding an autoload config that should be used.

In my opinion, this should be a non-issue upfront though, because a test scenario that instantiates the entire framework is quite problematic, and should be avoided anyway.


#6

I definitely don’t disagree, I just have a question: if you are writing controller action tests, as in https://docs.zendframework.com/tutorials/unit-testing/, are you not instantiating the entire framework?


#7

Hmm, no, I also don’t use zendframework/zend-test.

I’m personally using mocks and stubs quite copiously, and if I need a full system test I design the test in such a way that I don’t even know that the application is running PHP (black box testing).


#8

ah, ok. let us know when you’re going to teach a course on how to do that. I’ll be the first to sign up. :slight_smile:

seriously, my tests are perhaps better than nothing but they are not the state of the art.