Welcome Guest, Not a member yet? Register   Sign In
Superglobal testing
#1

Hello everyone.
I want to draw your attention to superglobal arrays. Currently, classes that work with superglobals access them directly within those classes.
It seems to me that this approach is not entirely correct. Which in particular can directly affect testing.
Check out this example.

PHP Code:
    public function testStoresPreviousURL()
    {
        $_SERVER['argv'] = ['index.php''/'];
        $_SERVER['argc'] = 2;

        // Inject mock router.
        $router Services::router(nullServices::request(), false);
        Services::injectMock('router'$router);

        ob_start();
        $this->codeigniter->useSafeOutput(true)->run();
        ob_get_clean();

        $this->assertArrayHasKey('_ci_previous_url'$_SESSION);
        $this->assertSame('http://example.com/index.php'$_SESSION['_ci_previous_url']);
    }

    public function testNotStoresPreviousURL()
    {
        $_SERVER['argv'] = ['index.php''example'];
        $_SERVER['argc'] = 2;

        $_SERVER['REQUEST_URI']    '/example';
        $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $_SERVER['REQUEST_METHOD']  'GET';

        // Inject mock router.
        $routes Services::routes();
        $routes->addRedirect('example''pages/notset'301);

        $router Services::router($routesServices::request());
        Services::injectMock('router'$router);

        ob_start();
        $this->codeigniter->useSafeOutput(true)->run();
        ob_get_clean();

        $this->assertArrayNotHasKey('_ci_previous_url'$_SESSION);
    
To simulate conditions for the test, you need to fill in the superglobal array manually. The problem can arise when the data of one test can affect the execution of another test, since superglobal arrays will be available in all tests.

I encountered a similar situation when I used a shared RouteCollection instance for a test. What affected other tests.

It seems to me that it would be reasonable to reconsider the behavior of classes in such a way that superglobal arrays are passed to the class constructor. For example, how is it done in Symfony.

PHP Code:
    /**
    * @param array                $query      The GET parameters
    * @param array                $request    The POST parameters
    * @param array                $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
    * @param array                $cookies    The COOKIE parameters
    * @param array                $files      The FILES parameters
    * @param array                $server    The SERVER parameters
    * @param string|resource|null $content    The raw body data
    */
    public function __construct(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content null

Having implemented similar work with superglobal arrays, it is possible to conduct tests in an "isolated environment" when the settings/conditions of one test will not affect another test.

As for working inside a class with superglobal arrays.
For example, although there is a RequestTrait::getServer() method, the IncomingRequest class accesses the superglobal array directly.

PHP Code:
    public function isSecure(): bool
    
{
        if (! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
            return true;
        
Reply
#2

Yes, all the superglobals in CI4 classes should be removed.
But too many superglobals are there, so it is not easy.
Reply
#3

With respect to testing, isn't this done by `@backupGlobals` of PHPUnit?
https://phpunit.readthedocs.io/en/9.5/an...kupglobals
Reply
#4

(02-03-2022, 04:41 AM)paulbalandan Wrote: With respect to testing, isn't this done by `@backupGlobals` of PHPUnit?
https://phpunit.readthedocs.io/en/9.5/an...kupglobals

Thanks for pointing to the documentation. It was interesting )

Initially, I wanted to devote this post to the direct dependence of classes on superglobal arrays.
But I decided that an example with tests would be more suitable for explaining the position.
Reply
#5

Superglobal dependency is tricky since some official PHP class actually interact with the superglobal on behalf of the implementation (like session handlers). I feel fairly certain that the HTTP layer could have a single “consumption” phase of those superglobals and then never reference them again, but that might be work best left for version 5 when we have more flexibility.
Reply
#6

I wrote this post so that we can discuss the possibility and, based on the results, include / not include in the v5 roadmap.
Of course, the chance that this would have been changed in the 4.x version is zero.
Reply




Theme © iAndrew 2016 - Forum software by © MyBB