Skip to content

Conversation

@michalsn
Copy link
Member

Description
This PR completes the implementation of the Superglobals class and migrates the entire codebase to use it consistently.

I started work on this about a month ago, and it turned out to be a tedious task. The scope of changes is relatively large, as this migration could not be done incrementally.

Since this class is internal and not intended for end-user consumption, no changelog entry has been added.

See: #7866

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@michalsn michalsn added enhancement PRs that improve existing functionalities refactor Pull requests that refactor code 4.7 labels Dec 28, 2025
Copy link
Contributor

@neznaika0 neznaika0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I patched the latest commit 4.7 - there is an error.

1) CodeIgniter\Models\PaginateModelTest::testMultiplePager
Failed asserting that '<!-- DEBUG-VIEW START 1 SYSTEMPATH/Pager/Views/default_full.php -->\n
...
<!-- DEBUG-VIEW ENDED 1 SYSTEMPATH/Pager/Views/default_full.php -->\n
' [ASCII](length: 843) contains "?page_valid=1"" [ASCII](length: 14).

/development/tests/system/Models/PaginateModelTest.php:103

A lot of changes - I couldn't see them all at once.

UPD: Fixes #9392

Comment on lines 71 to 85
public function __construct(
?array $server = null,
?array $get = null,
?array $post = null,
?array $cookie = null,
?array $files = null,
?array $request = null,
) {
$this->server = $server ?? $_SERVER;
$this->get = $get ?? $_GET;
$this->post = $post ?? $_POST;
$this->cookie = $cookie ?? $_COOKIE;
$this->files = $files ?? $_FILES;
$this->request = $request ?? $_REQUEST;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use constructor property promotion here and instead of null default we use directly the superglobals.

Suggested change
public function __construct(
?array $server = null,
?array $get = null,
?array $post = null,
?array $cookie = null,
?array $files = null,
?array $request = null,
) {
$this->server = $server ?? $_SERVER;
$this->get = $get ?? $_GET;
$this->post = $post ?? $_POST;
$this->cookie = $cookie ?? $_COOKIE;
$this->files = $files ?? $_FILES;
$this->request = $request ?? $_REQUEST;
}
public function __construct(
private array $server = $_SERVER,
private array $get = $_GET,
private array $post = $_POST,
private array $cookie = $_COOKIE,
private array $files = $_FILES,
private array $request = $_REQUEST,
) {
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly, we can't do that with superglobals, but I introduced property promotion.

*
* @return array<array-key, mixed>|float|int|string|null
*/
public function server(string $key): array|float|int|string|null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the getters, how about the idea of introducing a default parameter, liked mixed $default = null so that on invocation we can define already the default value in case missing instead of doing ?? <default>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

private array $files;

/**
* @param array<string, array|float|int|string>|null $server
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's currently a disconnect in this PR for the types a superglobal can set vs the types it can get. The set types is currently less precise than the get types. To make sync easier, I suggest to add phpstan-types in the class declaration phpdoc for each superglobal, something like:

/**
 * @phpstan-type server_items array<array-key, mixed>|float|int|string
 * etc.
 */
class Superglobals

Then here and the setters and getters below we can simply plug the phpstan type, like so:

Suggested change
* @param array<string, array|float|int|string>|null $server
* @param array<string, server_items>|null $server

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done... I think. Please verify when you have time.

@michalsn
Copy link
Member Author

@neznaika0 What do you mean by saying "I patched the latest commit 4.7"? Have you cloned this branch and run tests locally? This branch is working on the latest 4.7.

Anyway, I found the potential issue and hopefully fixed it. The strange thing is that it didn't pop up during tests.

@neznaika0
Copy link
Contributor

@michalsn I use few git features - it's easy to lose changes. I downloaded https://github.com/codeigniter4/CodeIgniter4/pull/9858.patch and I applied it on 4.7 at local.

@michalsn
Copy link
Member Author

@neznaika0 Ok, please verify if the issue still exists after recent changes - thanks.

* @return server_items|null
*/
public function server(string $key): array|float|int|string|null
public function server(string $key, mixed $default = null): array|float|int|string|null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have an mixed argument, since this should match server_items (and other places), you can add PHPDoc.

I'm sorry if this looks like a complication. You can leave it as it is

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point - fixed.

@neznaika0
Copy link
Contributor

Good. Error is missing

$this->superglobals->setGlobalArray('invalid', ['key' => 'value']);

// Should not throw exception, just does nothing
$this->assertTrue(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't make sense. Maybe you should check:

Suggested change
$this->assertTrue(true);
$this->assertSame([], $this->superglobals->getGlobalArray('invalid'));

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better idea is probably throwing an exception for an invalid superglobal. I've sent the changes.

Comment on lines 42 to 44
$_GET = [];
$_POST = [];
$_COOKIE = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look necessary.

Services::injectMock('superglobals', new Superglobals([], [], [], []));

*
* @param server_items $value
*/
public function setServer(string $key, array|float|int|string $value): void
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a fluid interface for all setServer(), setPost(),...? This shortens the service('superglobals') call further in the code:

$superglobals = service('superglobals');
$superglobals->setGetArray([]);
$superglobals->setPostArray([]);
$superglobals->setCookieArray([]);

$superglobals = service('superglobals')
    ->setGetArray([])
    ->setPostArray([])
    ->setCookieArray([]);

If you agree, you will need to specify the return type self.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And unset*() too

Comment on lines 39 to 40
$_POST = [];
$_GET = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look necessary


putenv("CONTENT_TYPE={$originalEnv}");
$_SERVER = $original; // restore so code coverage doesn't break
$superglobals->setServerArray($original); // restore so code coverage doesn't break
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far, we can't globally enable backupGlobals in the config, but it works for the file. Here and further in the file you can delete $original.

#[Group('Others')]
#[BackupGlobals(true)]
final class MessageTest extends CIUnitTestCase

Comment on lines 37 to 38
$_POST = [];
$_GET = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look necessary.

Services::injectMock('superglobals', new Superglobals([], [], []));

{
parent::setUp();

$_COOKIE = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look necessary.

Services::injectMock('superglobals', new Superglobals([], [], [], []));

{
parent::setUp();

$_COOKIE = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look necessary. As above

{
parent::setUp();

$_COOKIE = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look necessary. As above

parent::setUp();

// Clear superglobals before each test
$_SERVER = $_GET = $_POST = $_COOKIE = $_FILES = $_REQUEST = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look necessary. As above

Comment on lines +58 to +63
$this->server ??= $_SERVER;
$this->get ??= $_GET;
$this->post ??= $_POST;
$this->cookie ??= $_COOKIE;
$this->files ??= $_FILES;
$this->request ??= $_REQUEST;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out the comments below. It seems that we need the Superglobals::createDummy() method with empty values for testing.

If you do not agree, you should set all zeroing of global variables in the tests as Services::injectMock.('superglobals', new Superglobals([], [], [], [], [], [])) or call setServerArray([]), setPostArray([])...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.7 enhancement PRs that improve existing functionalities refactor Pull requests that refactor code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants