Skip to content

Commit

Permalink
Merge pull request #9012 from kenjis/feat-Services-resetServicesCache
Browse files Browse the repository at this point in the history
feat: add Services::resetServicesCache() to reset services cache
  • Loading branch information
kenjis authored Jul 6, 2024
2 parents cb39e7e + a4563e5 commit aef9e37
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 0 deletions.
3 changes: 3 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ parameters:
booleansInConditions: true
disallowedConstructs: true
matchingInheritedMethodNames: true
codeigniter:
additionalServices:
- AfterAutoloadModule\Config\Services
9 changes: 9 additions & 0 deletions system/Config/BaseService.php
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,15 @@ public static function injectMock(string $name, $mock)
static::$mocks[strtolower($name)] = $mock;
}

/**
* Resets the service cache.
*/
public static function resetServicesCache(): void
{
self::$serviceNames = [];
static::$discovered = false;
}

protected static function buildServicesCache(): void
{
if (! static::$discovered) {
Expand Down
38 changes: 38 additions & 0 deletions tests/_support/_AfterAutoloadModule/Config/Services.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace AfterAutoloadModule\Config;

use AfterAutoloadModule\Test;
use CodeIgniter\Config\BaseService;

/**
* Services for testing BaseService::resetServicesCache().
*
* This class should not be discovered by the autoloader until the test adds
* this namespace to the autoloader.
*/
class Services extends BaseService
{
/**
* Return a shared instance of the Test class for testing
*/
public static function test(bool $getShared = true): Test
{
if ($getShared) {
return static::getSharedInstance('test');
}

return new Test();
}
}
24 changes: 24 additions & 0 deletions tests/_support/_AfterAutoloadModule/Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace AfterAutoloadModule;

/**
* A simple class for testing BaseService::resetServicesCache().
*
* This class should not be discovered by the autoloader until the test adds
* this namespace to the autoloader.
*/
class Test
{
}
1 change: 1 addition & 0 deletions tests/system/CommonSingleServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public static function provideServiceNames(): iterable
'serviceExists',
'reset',
'resetSingle',
'resetServicesCache',
'injectMock',
'encrypter', // Encrypter needs a starter key
'session', // Headers already sent
Expand Down
24 changes: 24 additions & 0 deletions tests/system/Config/ServicesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace CodeIgniter\Config;

use AfterAutoloadModule\Test;
use CodeIgniter\Autoloader\Autoloader;
use CodeIgniter\Autoloader\FileLocator;
use CodeIgniter\Database\MigrationRunner;
Expand Down Expand Up @@ -350,6 +351,29 @@ public function testResetSingleCaseInsensitive(): void
$this->assertNotInstanceOf(MockResponse::class, $someService);
}

#[PreserveGlobalState(false)]
#[RunInSeparateProcess]
public function testResetServiceCache(): void
{
Services::injectMock('response', new MockResponse(new App()));
$response = service('response');
$this->assertInstanceOf(MockResponse::class, $response);
service('response')->setStatusCode(200);

Services::autoloader()->addNamespace(
'AfterAutoloadModule',
SUPPORTPATH . '_AfterAutoloadModule/'
);
Services::resetServicesCache();

$response = service('response');
$this->assertInstanceOf(MockResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());

$test = service('test');
$this->assertInstanceOf(Test::class, $test);
}

public function testFilters(): void
{
$result = Services::filters();
Expand Down
2 changes: 2 additions & 0 deletions user_guide_src/source/changelogs/v4.6.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ Others

- **Filters:** Now you can execute a filter more than once with the different
arguments in before or after.
- **Services:** Added ``BaseService::resetServicesCache()`` method to reset
the services cache. See :ref:`resetting-services-cache`.

***************
Message Changes
Expand Down
17 changes: 17 additions & 0 deletions user_guide_src/source/concepts/services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,20 @@ would simply use the framework's ``Config\Services`` class to grab your service:
.. literalinclude:: services/012.php

.. note:: If multiple Services files have the same method name, the first one found will be the instance returned.

.. _resetting-services-cache:

Resetting Services Cache
========================

.. versionadded:: 4.6.0

When the Services class is first called fairly early in the framework initialization
process, the Services classes discovered by auto-discovery are cached in a property,
and it will not be updated.

If modules are dynamically loaded later, and there are Services in the modules,
the cache must be updated.

This can be done by running ``Config\Services::resetServicesCache()``. This will
clear the cache, and force the service discovery again when needed.

0 comments on commit aef9e37

Please sign in to comment.