CodeIgniter4/tests/system/Autoloader/AutoloaderTest.php

421 lines
13 KiB
PHP
Raw Permalink Normal View History

2020-09-02 23:33:40 +08:00
<?php
2015-08-30 23:13:47 -05:00
declare(strict_types=1);
2021-07-19 21:32:33 +08:00
/**
* 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.
*/
2020-09-02 23:33:40 +08:00
namespace CodeIgniter\Autoloader;
2022-04-29 00:07:19 +07:00
use App\Controllers\Home;
use Closure;
2022-09-08 10:22:04 +09:00
use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\Exceptions\InvalidArgumentException;
use CodeIgniter\Exceptions\RuntimeException;
2020-09-02 23:33:40 +08:00
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\ReflectionHelper;
2016-03-15 21:27:25 +09:00
use Config\Autoload;
use Config\Modules;
2024-05-06 09:19:46 +09:00
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
2024-07-26 20:51:10 +08:00
use PHPUnit\Framework\Attributes\WithoutErrorHandler;
2021-06-10 23:42:49 +07:00
use UnnamespacedClass;
2015-08-30 23:13:47 -05:00
/**
* @internal
*/
2024-04-23 01:29:05 +08:00
#[Group('Others')]
final class AutoloaderTest extends CIUnitTestCase
2015-08-30 23:13:47 -05:00
{
use ReflectionHelper;
private Autoloader $loader;
2023-08-02 10:30:50 +08:00
/**
* @var Closure(string): (false|string)
2023-08-02 10:30:50 +08:00
*/
private Closure $classLoader;
2021-06-04 22:51:52 +08:00
protected function setUp(): void
{
parent::setUp();
$config = new Autoload();
$modules = new Modules();
$modules->discoverInComposer = false;
$config->classmap = [
'UnnamespacedClass' => SUPPORTPATH . 'Autoloader/UnnamespacedClass.php',
'OtherClass' => APPPATH . 'Controllers/Home.php',
'Name\Spaced\Class' => APPPATH . 'Controllers/Home.php',
];
$config->psr4 = [
2021-06-04 22:51:52 +08:00
'App' => APPPATH,
'CodeIgniter' => SYSTEMPATH,
];
$this->loader = new Autoloader();
$this->loader->initialize($config, $modules)->register();
$this->classLoader = $this->getPrivateMethodInvoker($this->loader, 'loadInNamespace');
2021-06-04 22:51:52 +08:00
}
protected function tearDown(): void
{
parent::tearDown();
$this->loader->unregister();
}
2023-07-29 22:59:04 +08:00
public function testLoadStoredClass(): void
2021-06-04 22:51:52 +08:00
{
$this->assertInstanceOf('UnnamespacedClass', new UnnamespacedClass());
}
2023-07-29 22:59:04 +08:00
public function testInitializeWithInvalidArguments(): void
2021-06-04 22:51:52 +08:00
{
2022-03-21 01:39:09 +07:00
$this->expectException('InvalidArgumentException');
2021-06-04 22:51:52 +08:00
$this->expectExceptionMessage("Config array must contain either the 'psr4' key or the 'classmap' key.");
$config = new Autoload();
$config->classmap = [];
$config->psr4 = [];
$modules = new Modules();
$modules->discoverInComposer = false;
(new Autoloader())->initialize($config, $modules);
}
2023-07-29 22:59:04 +08:00
public function testInitializeTwice(): void
2022-01-19 16:33:11 +09:00
{
$loader = new Autoloader();
$loader->initialize(new Autoload(), new Modules());
$ns = $loader->getNamespace();
$this->assertCount(1, $ns['App']);
$this->assertSame('ROOTPATH/app', clean_path($ns['App'][0]));
$loader->initialize(new Autoload(), new Modules());
$ns = $loader->getNamespace();
$this->assertCount(1, $ns['App']);
$this->assertSame('ROOTPATH/app', clean_path($ns['App'][0]));
}
2023-07-29 22:59:04 +08:00
public function testServiceAutoLoaderFromShareInstances(): void
2021-06-04 22:51:52 +08:00
{
2024-11-01 17:00:17 +03:00
$classLoader = $this->getPrivateMethodInvoker(service('autoloader'), 'loadInNamespace');
2021-06-04 22:51:52 +08:00
// look for Home controller, as that should be in base repo
$actual = $classLoader(Home::class);
2021-06-04 22:51:52 +08:00
$expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php';
$resolvedPath = realpath($actual);
$actual = $resolvedPath !== false ? $resolvedPath : $actual;
$this->assertSame($expected, $actual);
2021-06-04 22:51:52 +08:00
}
2023-07-29 22:59:04 +08:00
public function testServiceAutoLoader(): void
2021-06-04 22:51:52 +08:00
{
2024-11-01 17:00:17 +03:00
$autoloader = service('autoloader', false);
2021-06-04 22:51:52 +08:00
$autoloader->initialize(new Autoload(), new Modules());
$autoloader->register();
$classLoader = $this->getPrivateMethodInvoker($autoloader, 'loadInNamespace');
2021-06-04 22:51:52 +08:00
// look for Home controller, as that should be in base repo
$actual = $classLoader(Home::class);
2021-06-04 22:51:52 +08:00
$expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php';
$resolvedPath = realpath($actual);
$actual = $resolvedPath !== false ? $resolvedPath : $actual;
$this->assertSame($expected, $actual);
$autoloader->unregister();
2021-06-04 22:51:52 +08:00
}
2023-07-29 22:59:04 +08:00
public function testExistingFile(): void
2021-06-04 22:51:52 +08:00
{
$actual = ($this->classLoader)(Home::class);
2021-06-04 22:51:52 +08:00
$expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php';
$this->assertSame($expected, $actual);
$actual = ($this->classLoader)('CodeIgniter\Helpers\array_helper');
2021-06-04 22:51:52 +08:00
$expected = SYSTEMPATH . 'Helpers' . DIRECTORY_SEPARATOR . 'array_helper.php';
$this->assertSame($expected, $actual);
}
2023-07-29 22:59:04 +08:00
public function testMatchesWithPrecedingSlash(): void
2021-06-04 22:51:52 +08:00
{
$actual = ($this->classLoader)(Home::class);
2021-06-04 22:51:52 +08:00
$expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php';
$this->assertSame($expected, $actual);
}
2023-07-29 22:59:04 +08:00
public function testMissingFile(): void
2021-06-04 22:51:52 +08:00
{
$this->assertFalse(($this->classLoader)('\App\Missing\Classname'));
2021-06-04 22:51:52 +08:00
}
2023-07-29 22:59:04 +08:00
public function testAddNamespaceWorks(): void
2021-06-04 22:51:52 +08:00
{
$this->assertFalse(($this->classLoader)('My\App\Class'));
2021-06-04 22:51:52 +08:00
$this->loader->addNamespace('My\App', __DIR__);
$actual = ($this->classLoader)('My\App\AutoloaderTest');
2021-06-04 22:51:52 +08:00
$expected = __FILE__;
$this->assertSame($expected, $actual);
}
2023-07-29 22:59:04 +08:00
public function testAddNamespaceMultiplePathsWorks(): void
2021-06-04 22:51:52 +08:00
{
$this->loader->addNamespace([
'My\App' => [
APPPATH . 'Config',
__DIR__,
],
]);
$actual = ($this->classLoader)('My\App\App');
2021-06-04 22:51:52 +08:00
$expected = APPPATH . 'Config' . DIRECTORY_SEPARATOR . 'App.php';
$this->assertSame($expected, $actual);
$actual = ($this->classLoader)('My\App\AutoloaderTest');
2021-06-04 22:51:52 +08:00
$expected = __FILE__;
$this->assertSame($expected, $actual);
}
2023-07-29 22:59:04 +08:00
public function testAddNamespaceStringToArray(): void
2021-06-04 22:51:52 +08:00
{
$this->loader->addNamespace('App\Controllers', __DIR__);
$this->assertSame(
__FILE__,
($this->classLoader)('App\Controllers\AutoloaderTest'),
2021-06-04 22:51:52 +08:00
);
}
2023-07-29 22:59:04 +08:00
public function testGetNamespaceGivesArray(): void
2021-06-04 22:51:52 +08:00
{
$this->assertSame([
'App' => [APPPATH],
'CodeIgniter' => [SYSTEMPATH],
], $this->loader->getNamespace());
$this->assertSame([SYSTEMPATH], $this->loader->getNamespace('CodeIgniter'));
$this->assertSame([], $this->loader->getNamespace('Foo'));
}
2023-07-29 22:59:04 +08:00
public function testRemoveNamespace(): void
2021-06-04 22:51:52 +08:00
{
$this->loader->addNamespace('My\App', __DIR__);
$this->assertSame(__FILE__, ($this->classLoader)('My\App\AutoloaderTest'));
2021-06-04 22:51:52 +08:00
$this->loader->removeNamespace('My\App');
$this->assertFalse(($this->classLoader)('My\App\AutoloaderTest'));
2021-06-04 22:51:52 +08:00
}
2023-07-29 22:59:04 +08:00
public function testloadClassNonNamespaced(): void
2021-06-04 22:51:52 +08:00
{
$this->assertFalse(($this->classLoader)('Modules'));
2021-06-04 22:51:52 +08:00
}
2023-07-29 22:59:04 +08:00
public function testSanitizationContailsSpecialChars(): void
2021-06-04 22:51:52 +08:00
{
$this->expectException(InvalidArgumentException::class);
2022-06-28 15:01:53 +09:00
$this->expectExceptionMessage(
'The file path contains special characters "${}!#" that are not allowed: "${../path}!#/to/some/file.php_"',
2022-06-28 15:01:53 +09:00
);
2021-06-04 22:51:52 +08:00
$test = '${../path}!#/to/some/file.php_';
$this->loader->sanitizeFilename($test);
2021-06-04 22:51:52 +08:00
}
2023-07-29 22:59:04 +08:00
public function testSanitizationFilenameEdges(): void
2022-06-28 15:01:53 +09:00
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(
'The characters ".-_" are not allowed in filename edges: "/path/to/some/file.php_"',
2022-06-28 15:01:53 +09:00
);
$test = '/path/to/some/file.php_';
$this->loader->sanitizeFilename($test);
}
2023-07-29 22:59:04 +08:00
public function testSanitizationRegexError(): void
2022-07-01 16:00:38 +09:00
{
$this->expectException(RuntimeException::class);
$test = mb_convert_encoding('クラスファイル.php', 'EUC-JP', 'UTF-8');
$this->loader->sanitizeFilename($test);
}
2023-07-29 22:59:04 +08:00
public function testSanitizationAllowUnicodeChars(): void
2021-06-04 22:51:52 +08:00
{
$test = 'Ä/path/to/some/file.php';
2021-06-04 22:51:52 +08:00
$this->assertSame($test, $this->loader->sanitizeFilename($test));
2021-06-04 22:51:52 +08:00
}
2023-07-29 22:59:04 +08:00
public function testSanitizationAllowsWindowsFilepaths(): void
2021-06-04 22:51:52 +08:00
{
$test = 'C:\path\to\some/file.php';
2021-06-25 23:35:25 +08:00
$this->assertSame($test, $this->loader->sanitizeFilename($test));
2021-06-04 22:51:52 +08:00
}
2023-07-29 22:59:04 +08:00
public function testFindsComposerRoutes(): void
2021-06-04 22:51:52 +08:00
{
$config = new Autoload();
$modules = new Modules();
$modules->discoverInComposer = true;
2022-08-20 11:48:09 +09:00
$loader = new Autoloader();
$loader->initialize($config, $modules);
2021-06-04 22:51:52 +08:00
2022-08-20 11:48:09 +09:00
$namespaces = $loader->getNamespace();
2021-06-04 22:51:52 +08:00
$this->assertArrayHasKey('Laminas\\Escaper', $namespaces);
}
2023-07-29 22:59:04 +08:00
public function testComposerNamespaceDoesNotOverwriteConfigAutoloadPsr4(): void
{
$config = new Autoload();
$config->psr4 = [
'Psr\Log' => '/Config/Autoload/Psr/Log/',
];
$modules = new Modules();
$modules->discoverInComposer = true;
2022-08-20 11:48:09 +09:00
$loader = new Autoloader();
$loader->initialize($config, $modules);
2022-08-20 11:48:09 +09:00
$namespaces = $loader->getNamespace();
$this->assertSame('/Config/Autoload/Psr/Log/', $namespaces['Psr\Log'][0]);
$this->assertStringContainsString(VENDORPATH, $namespaces['Psr\Log'][1]);
}
2023-07-29 22:59:04 +08:00
public function testComposerPackagesOnly(): void
{
$config = new Autoload();
$config->psr4 = [];
$modules = new Modules();
$modules->discoverInComposer = true;
$modules->composerPackages = ['only' => ['laminas/laminas-escaper']];
$loader = new Autoloader();
$loader->initialize($config, $modules);
$namespaces = $loader->getNamespace();
$this->assertCount(1, $namespaces);
$this->assertStringContainsString(VENDORPATH, $namespaces['Laminas\Escaper'][0]);
}
2023-07-29 22:59:04 +08:00
public function testComposerPackagesExclude(): void
{
$config = new Autoload();
$config->psr4 = [];
$modules = new Modules();
$modules->discoverInComposer = true;
$modules->composerPackages = [
'exclude' => [
'psr/log',
'laminas/laminas-escaper',
],
];
$loader = new Autoloader();
$loader->initialize($config, $modules);
$namespaces = $loader->getNamespace();
$this->assertArrayNotHasKey('Psr\Log', $namespaces);
$this->assertArrayNotHasKey('Laminas\\Escaper', $namespaces);
}
2023-07-29 22:59:04 +08:00
public function testComposerPackagesOnlyAndExclude(): void
2022-09-08 10:22:04 +09:00
{
$this->expectException(ConfigException::class);
$this->expectExceptionMessage('Cannot use "only" and "exclude" at the same time in "Config\Modules::$composerPackages".');
$config = new Autoload();
$config->psr4 = [];
$modules = new Modules();
$modules->discoverInComposer = true;
$modules->composerPackages = [
'only' => ['laminas/laminas-escaper'],
'exclude' => ['psr/log'],
];
$loader = new Autoloader();
$loader->initialize($config, $modules);
}
2023-07-29 22:59:04 +08:00
public function testFindsComposerRoutesWithComposerPathNotFound(): void
2021-06-04 22:51:52 +08:00
{
$composerPath = COMPOSER_PATH;
$config = new Autoload();
$modules = new Modules();
$modules->discoverInComposer = true;
2022-08-20 11:48:09 +09:00
$loader = new Autoloader();
2021-06-04 22:51:52 +08:00
rename(COMPOSER_PATH, COMPOSER_PATH . '.backup');
2022-08-20 11:48:09 +09:00
$loader->initialize($config, $modules);
2021-06-04 22:51:52 +08:00
rename(COMPOSER_PATH . '.backup', $composerPath);
2022-08-20 11:48:09 +09:00
$namespaces = $loader->getNamespace();
2021-06-04 22:51:52 +08:00
$this->assertArrayNotHasKey('Laminas\\Escaper', $namespaces);
}
public function testAutoloaderLoadsNonClassFiles(): void
{
$config = new Autoload();
2021-06-04 22:51:52 +08:00
$config->files[] = SUPPORTPATH . 'Autoloader/functions.php';
2022-08-20 11:48:09 +09:00
$loader = new Autoloader();
$loader->initialize($config, new Modules());
$loader->register();
2021-06-04 22:51:52 +08:00
$this->assertTrue(function_exists('autoload_foo'));
$this->assertSame('I am autoloaded by Autoloader through $files!', autoload_foo());
$this->assertTrue(defined('AUTOLOAD_CONSTANT'));
$this->assertSame('foo', AUTOLOAD_CONSTANT);
2022-08-20 11:48:09 +09:00
$loader->unregister();
2021-06-04 22:51:52 +08:00
}
2022-08-20 11:36:20 +09:00
2024-04-23 01:29:05 +08:00
#[PreserveGlobalState(false)]
2024-05-08 00:05:51 +08:00
#[RunInSeparateProcess]
2024-07-26 20:51:10 +08:00
#[WithoutErrorHandler]
2022-08-22 11:15:25 +09:00
public function testLoadHelpers(): void
2022-08-20 11:36:20 +09:00
{
$config = new Autoload();
$config->helpers[] = 'form';
2022-08-20 11:48:09 +09:00
$loader = new Autoloader();
$loader->initialize($config, new Modules());
2022-08-20 11:36:20 +09:00
2022-08-20 11:48:09 +09:00
$loader->loadHelpers();
2022-08-20 11:36:20 +09:00
$this->assertTrue(function_exists('form_open'));
2022-08-20 11:48:09 +09:00
$loader->unregister();
2022-08-20 11:36:20 +09:00
}
2015-08-30 23:13:47 -05:00
}