Skip to content

feat: Adding BaseService::updateServicesCache. Adds ablity to update Services Cache at runtime. #8896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
31 changes: 31 additions & 0 deletions system/Config/BaseService.php
Original file line number Diff line number Diff line change
Expand Up @@ -413,4 +413,35 @@ protected static function buildServicesCache(): void
static::$discovered = true;
}
}

/**
* Update the services cache.
*/
public static function updateServicesCache(): void
{
if ((new Modules())->shouldDiscover('services')) {
$locator = static::locator();
$files = $locator->search('Config/Services');

$systemPath = static::autoloader()->getNamespace('CodeIgniter')[0];

// Get instances of all service classes and cache them locally.
foreach ($files as $file) {
// Does not search `CodeIgniter` namespace to prevent from loading twice.
if (str_starts_with($file, $systemPath)) {
continue;
}

$classname = $locator->findQualifiedNameFromPath($file);

if ($classname === false) {
continue;
}

if ($classname !== Services::class && ! in_array($classname, self::$serviceNames, true)) {
self::$serviceNames[] = $classname;
}
}
}
}
}
37 changes: 37 additions & 0 deletions tests/_support/Test/AfterAutoloadModule/Config/Services.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?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::updateServicesCache()
*
* 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();
}
}
23 changes: 23 additions & 0 deletions tests/_support/Test/AfterAutoloadModule/Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?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::updateServicesCache()
*
* 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 @@ -111,6 +111,7 @@ public static function provideServiceNames(): iterable
'reset',
'resetSingle',
'injectMock',
'updateServicesCache',
'encrypter', // Encrypter needs a starter key
'session', // Headers already sent
];
Expand Down
21 changes: 21 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,26 @@ public function testResetSingleCaseInsensitive(): void
$this->assertNotInstanceOf(MockResponse::class, $someService);
}

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

Services::autoloader()->addNamespace('AfterAutoloadModule', TESTPATH . '_support/Test/AfterAutoloadModule/');
Services::updateServicesCache();

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

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

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

Others
======
- **Services:** Added ``BaseService::updateServicesCache`` method to allow
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- **Services:** Added ``BaseService::updateServicesCache`` method to allow
+
- **Services:** Added ``BaseService::updateServicesCache()`` method to allow

updating the services cache file post autoloading. This will only look for
new services and add them to the class name cache.

***************
Message Changes
Expand Down
3 changes: 3 additions & 0 deletions user_guide_src/source/concepts/services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,6 @@ 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.

There may be times when you need to have Service Discovery refresh it's cache after the inital autoload proccess. This can be done by running :php:meth:`Config\\Services::updateServicesCache()`.
This will force the service discovery to re-scan the directories for any new services files.
Comment on lines +175 to +176
Copy link
Member

@kenjis kenjis May 16, 2024

Choose a reason for hiding this comment

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

How about this?

Suggested change
There may be times when you need to have Service Discovery refresh it's cache after the inital autoload proccess. This can be done by running :php:meth:`Config\\Services::updateServicesCache()`.
This will force the service discovery to re-scan the directories for any new services files.
Updating Services Cache
=======================
.. versionadded:: 4.6.0
When Services is first called fairly early in the framework initialization process,
the Services classes discovered by auto-discovery is cached in a property.
If modules are dynamically loaded later, this cache must be updated.
This can be done by running ``Config\Services::updateServicesCache()``. This will
force the service discovery to re-scan the directories for any new Services files.

:php:meth:Config\\Services::updateServicesCache() is okay if there is the class reference for the method. But in this case, there is no class reference to link.

Loading