mirror of
https://github.com/codeigniter4/CodeIgniter4.git
synced 2025-02-20 11:44:28 +08:00
Dynamically fill arguments for single service
This commit is contained in:
parent
d304522912
commit
fd25af189c
@ -1036,7 +1036,6 @@ if (! function_exists('service'))
|
||||
if (! function_exists('single_service'))
|
||||
{
|
||||
/**
|
||||
* Allow cleaner access to a Service.
|
||||
* Always returns a new instance of the class.
|
||||
*
|
||||
* @param string $name
|
||||
@ -1046,10 +1045,36 @@ if (! function_exists('single_service'))
|
||||
*/
|
||||
function single_service(string $name, ...$params)
|
||||
{
|
||||
// Ensure it's NOT a shared instance
|
||||
array_push($params, false);
|
||||
$service = Services::serviceExists($name);
|
||||
|
||||
return Services::$name(...$params);
|
||||
if ($service === null)
|
||||
{
|
||||
// The service is not defined anywhere so just return.
|
||||
return null;
|
||||
}
|
||||
|
||||
$method = new ReflectionMethod($service, $name);
|
||||
$count = $method->getNumberOfParameters();
|
||||
$mParam = $method->getParameters();
|
||||
$params = $params ?? [];
|
||||
|
||||
if ($count === 1)
|
||||
{
|
||||
// This service needs only one argument, which is the shared
|
||||
// instance flag, so let's wrap up and pass false here.
|
||||
return $service::$name(false);
|
||||
}
|
||||
|
||||
// Fill in the params with the defaults, but stop before the last
|
||||
for ($startIndex = count($params); $startIndex <= $count - 2; $startIndex++)
|
||||
{
|
||||
$params[$startIndex] = $mParam[$startIndex]->getDefaultValue();
|
||||
}
|
||||
|
||||
// Ensure the last argument will not create a shared instance
|
||||
$params[$count - 1] = false;
|
||||
|
||||
return $service::$name(...$params);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,12 @@ class BaseService
|
||||
*/
|
||||
protected static $services = [];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* A cache of the names of services classes found.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
private static $serviceNames = [];
|
||||
|
||||
/**
|
||||
* Returns a shared instance of any of the class' services.
|
||||
@ -98,8 +103,6 @@ class BaseService
|
||||
return static::$instances[$key];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Autoloader class is the central class that handles our
|
||||
* spl_autoload_register method, and helper methods.
|
||||
@ -123,8 +126,6 @@ class BaseService
|
||||
return new Autoloader();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The file locator provides utility methods for looking for non-classes
|
||||
* within namespaced folders, as well as convenience methods for
|
||||
@ -149,8 +150,6 @@ class BaseService
|
||||
return new FileLocator(static::autoloader());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Provides the ability to perform case-insensitive calling of service
|
||||
* names.
|
||||
@ -162,17 +161,40 @@ class BaseService
|
||||
*/
|
||||
public static function __callStatic(string $name, array $arguments)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$service = static::serviceExists($name);
|
||||
|
||||
if (method_exists(Services::class, $name))
|
||||
if ($service === null)
|
||||
{
|
||||
return Services::$name(...$arguments);
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::discoverServices($name, $arguments);
|
||||
return $service::$name(...$arguments);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/**
|
||||
* Check if the requested service is defined and return the declaring
|
||||
* class. Return null if not found.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function serviceExists(string $name): ?string
|
||||
{
|
||||
static::buildServicesCache();
|
||||
$services = array_merge([Services::class], self::$serviceNames);
|
||||
$name = strtolower($name);
|
||||
|
||||
foreach ($services as $service)
|
||||
{
|
||||
if (method_exists($service, $name))
|
||||
{
|
||||
return $service;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset shared instances and mocks for testing.
|
||||
@ -181,8 +203,7 @@ class BaseService
|
||||
*/
|
||||
public static function reset(bool $initAutoloader = false)
|
||||
{
|
||||
static::$mocks = [];
|
||||
|
||||
static::$mocks = [];
|
||||
static::$instances = [];
|
||||
|
||||
if ($initAutoloader)
|
||||
@ -191,8 +212,6 @@ class BaseService
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Inject mock object for testing.
|
||||
*
|
||||
@ -204,8 +223,6 @@ class BaseService
|
||||
static::$mocks[strtolower($name)] = $mock;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will scan all psr4 namespaces registered with system to look
|
||||
* for new Config\Services files. Caches a copy of each one, then
|
||||
@ -216,6 +233,10 @@ class BaseService
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
protected static function discoverServices(string $name, array $arguments)
|
||||
{
|
||||
@ -266,4 +287,32 @@ class BaseService
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static function buildServicesCache(): void
|
||||
{
|
||||
if (! static::$discovered)
|
||||
{
|
||||
$config = config('Modules');
|
||||
|
||||
if ($config->shouldDiscover('services'))
|
||||
{
|
||||
$locator = static::locator();
|
||||
$files = $locator->search('Config/Services');
|
||||
|
||||
// Get instances of all service classes and cache them locally.
|
||||
foreach ($files as $file)
|
||||
{
|
||||
$classname = $locator->getClassname($file);
|
||||
|
||||
if ($classname !== 'CodeIgniter\\Config\\Services')
|
||||
{
|
||||
self::$serviceNames[] = $classname;
|
||||
static::$services[] = new $classname();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static::$discovered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use CodeIgniter\HTTP\URI;
|
||||
use CodeIgniter\HTTP\UserAgent;
|
||||
use CodeIgniter\Router\RouteCollection;
|
||||
use CodeIgniter\Session\Handlers\FileHandler;
|
||||
use CodeIgniter\Test\CIUnitTestCase;
|
||||
use CodeIgniter\Test\Mock\MockIncomingRequest;
|
||||
use CodeIgniter\Test\Mock\MockSession;
|
||||
use CodeIgniter\Test\TestLogger;
|
||||
@ -18,11 +19,8 @@ use Tests\Support\Models\JobModel;
|
||||
/**
|
||||
* @backupGlobals enabled
|
||||
*/
|
||||
class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
class CommonFunctionsTest extends CIUnitTestCase
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
@ -31,8 +29,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
unset($_ENV['foo'], $_SERVER['foo']);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testStringifyAttributes()
|
||||
{
|
||||
$this->assertEquals(' class="foo" id="bar"', stringify_attributes(['class' => 'foo', 'id' => 'bar']));
|
||||
@ -50,8 +46,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertEquals('', stringify_attributes([]));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testStringifyJsAttributes()
|
||||
{
|
||||
$this->assertEquals('width=800,height=600', stringify_attributes(['width' => '800', 'height' => '600'], true));
|
||||
@ -62,8 +56,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertEquals('width=800,height=600', stringify_attributes($atts, true));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testEnvReturnsDefault()
|
||||
{
|
||||
$this->assertEquals('baz', env('foo', 'baz'));
|
||||
@ -96,8 +88,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertNull(env('p4'));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testRedirectReturnsRedirectResponse()
|
||||
{
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
@ -122,8 +112,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertInstanceOf(\CodeIgniter\HTTP\RedirectResponse::class, redirect());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testView()
|
||||
{
|
||||
$data = [
|
||||
@ -145,16 +133,12 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertStringContainsString($expected, view('\Tests\Support\View\Views\simple'));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testViewCell()
|
||||
{
|
||||
$expected = 'Hello';
|
||||
$this->assertEquals($expected, view_cell('\Tests\Support\View\SampleClass::hello'));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testEscapeWithDifferentEncodings()
|
||||
{
|
||||
$this->assertEquals('<x', esc('<x', 'html', 'utf-8'));
|
||||
@ -162,16 +146,12 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertEquals('<x', esc('<x', 'html', 'windows-1251'));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testEscapeBadContext()
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
esc(['width' => '800', 'height' => '600'], 'bogus');
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
@ -208,17 +188,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertEquals(null, session('notbogus'));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testSingleService()
|
||||
{
|
||||
$timer1 = single_service('timer');
|
||||
$timer2 = single_service('timer');
|
||||
$this->assertFalse($timer1 === $timer2);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testRouteTo()
|
||||
{
|
||||
// prime the pump
|
||||
@ -228,8 +197,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertEquals('/path/string/to/13', route_to('myController::goto', 'string', 13));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testInvisible()
|
||||
{
|
||||
$this->assertEquals('Javascript', remove_invisible_characters("Java\0script"));
|
||||
@ -240,15 +207,11 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertEquals('Javascript', remove_invisible_characters('Java%0cscript', true));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testAppTimezone()
|
||||
{
|
||||
$this->assertEquals('America/Chicago', app_timezone());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testCSRFToken()
|
||||
{
|
||||
$this->assertEquals('csrf_test_name', csrf_token());
|
||||
@ -274,8 +237,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertStringContainsString('<meta name="X-CSRF-TOKEN" ', csrf_meta());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testModelNotExists()
|
||||
{
|
||||
$this->assertNull(model(UnexsistenceClass::class));
|
||||
@ -296,8 +257,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertInstanceOf(JobModel::class, model('\Tests\Support\Models\JobModel'));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
@ -370,16 +329,12 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertEquals($locations, old('location'));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testReallyWritable()
|
||||
{
|
||||
// cannot test fully on *nix
|
||||
$this->assertTrue(is_really_writable(WRITEPATH));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public function testSlashItem()
|
||||
{
|
||||
$this->assertEquals('/', slash_item('cookiePath')); // slash already there
|
||||
@ -415,7 +370,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
\CodeIgniter\Config\BaseService::injectMock('session', $session);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Make sure cookies are set by RedirectResponse this way
|
||||
// See https://github.com/codeigniter4/CodeIgniter4/issues/1393
|
||||
public function testRedirectResponseCookies1()
|
||||
@ -435,8 +389,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertTrue($answer1->hasCookie('login_time'));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testTrace()
|
||||
{
|
||||
ob_start();
|
||||
@ -456,8 +408,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertStringContainsString('<h1>is_not</h1>', view('\Tests\Support\View\Views\simples'));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
@ -471,8 +421,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
$this->assertEquals('https://example.com/', Services::response()->getHeader('Location')->getValue());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @dataProvider dirtyPathsProvider
|
||||
*/
|
||||
@ -509,8 +457,6 @@ class CommonFunctionsTest extends \CodeIgniter\Test\CIUnitTestCase
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testHelperWithFatalLocatorThrowsException()
|
||||
{
|
||||
// Replace the locator with one that will fail if it is called
|
||||
|
96
tests/system/CommonSingleServiceTest.php
Normal file
96
tests/system/CommonSingleServiceTest.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
use CodeIgniter\Config\Services;
|
||||
use CodeIgniter\Test\CIUnitTestCase;
|
||||
|
||||
final class CommonSingleServiceTest extends CIUnitTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider serviceNamesProvider
|
||||
*
|
||||
* @param string $service
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSingleServiceWithNoParamsSupplied(string $service): void
|
||||
{
|
||||
$service1 = single_service($service);
|
||||
$service2 = single_service($service);
|
||||
|
||||
$this->assertSame(get_class($service1), get_class($service2));
|
||||
$this->assertNotSame($service1, $service2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider serviceNamesProvider
|
||||
*
|
||||
* @param string $service
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSingleServiceWithAtLeastOneParamSupplied(string $service): void
|
||||
{
|
||||
$params = [];
|
||||
$method = new ReflectionMethod(Services::class, $service);
|
||||
|
||||
if ($method->getNumberOfParameters() === 1)
|
||||
{
|
||||
$params[] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$params[] = $method->getParameters()[0]->getDefaultValue();
|
||||
}
|
||||
|
||||
$service1 = single_service($service, ...$params);
|
||||
$service2 = single_service($service, ...$params);
|
||||
|
||||
$this->assertSame(get_class($service1), get_class($service2));
|
||||
$this->assertNotSame($service1, $service2);
|
||||
}
|
||||
|
||||
public function testSingleServiceWithAllParamsSupplied(): void
|
||||
{
|
||||
$cache1 = single_service('cache', null, true);
|
||||
$cache2 = single_service('cache', null, true);
|
||||
|
||||
// Assert that even passing true as last param this will
|
||||
// not create a shared instance.
|
||||
$this->assertSame(get_class($cache1), get_class($cache2));
|
||||
$this->assertNotSame($cache1, $cache2);
|
||||
}
|
||||
|
||||
public function testSingleServiceWithGibberishGiven(): void
|
||||
{
|
||||
$this->assertNull(single_service('foo'));
|
||||
$this->assertNull(single_service('bar'));
|
||||
$this->assertNull(single_service('baz'));
|
||||
$this->assertNull(single_service('caches'));
|
||||
$this->assertNull(single_service('timers'));
|
||||
}
|
||||
|
||||
public static function serviceNamesProvider(): iterable
|
||||
{
|
||||
$methods = (new ReflectionClass(Services::class))->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
|
||||
foreach ($methods as $method)
|
||||
{
|
||||
$name = $method->getName();
|
||||
$excl = [
|
||||
'__callStatic',
|
||||
'serviceExists',
|
||||
'reset',
|
||||
'injectMock',
|
||||
'encrypter', // Encrypter needs a starter key
|
||||
'session', // Headers already sent
|
||||
];
|
||||
|
||||
if (in_array($name, $excl, true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield [$name];
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,6 @@ use CodeIgniter\Test\Mock\MockResponse;
|
||||
|
||||
class ServicesTest extends CIUnitTestCase
|
||||
{
|
||||
|
||||
protected $config;
|
||||
protected $original;
|
||||
|
||||
@ -17,15 +16,13 @@ class ServicesTest extends CIUnitTestCase
|
||||
parent::setUp();
|
||||
|
||||
$this->original = $_SERVER;
|
||||
// $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'es; q=1.0, en; q=0.5';
|
||||
$this->config = new App();
|
||||
// $this->config->negotiateLocale = true;
|
||||
// $this->config->supportedLocales = ['en', 'es'];
|
||||
$this->config = new App();
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
$_SERVER = $this->original;
|
||||
Services::reset();
|
||||
}
|
||||
|
||||
public function testNewAutoloader()
|
||||
|
Loading…
x
Reference in New Issue
Block a user