# 代码模块

CodeIgniter 支持一种代码模块化形式，帮助你创建可重用的代码。模块通常围绕特定主题展开，可以看作是大型应用程序中的小型应用程序。

支持框架内的任何标准文件类型，如控制器、模型、视图、配置文件、辅助函数、语言文件等。模块可以包含你想要的任意数量或任意少的这些文件。

如果要创建 Composer 包形式的模块，请参阅 创建 Composer 包。

## 命名空间

模块功能的核心元素来自 CodeIgniter 使用的 PSR-4 兼容的自动加载。 虽然任何代码都可以使用 PSR-4 自动加载和命名空间，但要充分利用模块，主要方法是为代码添加命名空间， 并将其添加到 app/Config/Autoload.php 的 `$psr4` 属性中。

例如，假设我们想要创建一个可以在应用程序之间重用的简单博客模块。我们可以创建一个以公司名称 Acme 命名的文件夹来存储所有模块。 我们将其直接放在主项目根目录的 app 目录旁边:

    acme/        // 新模块目录
    app/
    public/
    system/
    tests/
    writable/

打开 app/Config/Autoload.php，将 `Acme\Blog` 命名空间添加到 `$psr4` 数组属性中：

    <?php

    namespace Config;

    use CodeIgniter\Config\AutoloadConfig;

    class Autoload extends AutoloadConfig
    {
        // ...
        public $psr4 = [
            APP_NAMESPACE => APPPATH,
            'Acme\Blog'   => ROOTPATH . 'acme/Blog',
        ];

        // ...
    }

现在设置完成，可以通过 `Acme\Blog` 命名空间访问 acme/Blog 文件夹内的任何文件。仅此一项就满足了模块正常工作所需的 80% 功能， 因此你需要熟悉命名空间并习惯使用它们。多种文件类型将通过所有已定义的命名空间自动扫描 - 这是使用模块的关键要素。

模块内的常见目录结构将模仿主应用程序文件夹:

    acme/
        Blog/
            Config/
            Controllers/
            Database/
                Migrations/
                Seeds/
            Helpers/
            Language/
                en/
            Libraries/
            Models/
            Views/

当然，并没有强制要求你必须完全照搬这种结构，你应该以最适合模块的方式组织它， 比如省略不需要的目录，为实体（Entities）、接口（Interfaces）或仓库（Repositories）等创建新目录。

## 自动加载非类文件

你的模块通常不仅包含 PHP 类，还会包含过程式函数、引导文件、模块常量文件等，这些文件通常不像类那样被（自动）加载。处理这种情况的一种方法是，在需要使用这些文件的文件开头，直接使用 `require` 引入它们。

CodeIgniter 提供的另一种方法是自动加载这些 非类 文件，就像自动加载类一样。我们只需要提供这些文件的路径列表，并将它们包含在 app/Config/Autoload.php 文件的 `$files` 属性中即可。

    <?php

    namespace Config;

    use CodeIgniter\Config\AutoloadConfig;

    class Autoload extends AutoloadConfig
    {
        // ...

        public $files = [
            'path/to/my/functions.php',
            'path/to/my/constants.php',
            'path/to/my/bootstrap.php',
        ];

        // ...
    }

## 自动发现

很多时候，你需要为要包含的文件指定完整的命名空间，但 CodeIgniter 可以配置为通过自动发现多种不同的文件类型， 使模块集成到应用程序中更简单，包括：

- 事件

- 过滤器

- 注册器

- 路由文件

- 服务

这是在 app/Config/Modules.php 文件中配置的。

自动发现系统通过扫描在 Config/Autoload.php 和 Composer 包中定义的 psr4 命名空间中的特定目录和文件来工作。

发现过程将在该路径上查找可发现的项目，例如，应该找到位于 acme/Blog/Config/Routes.php 的路由文件。

### 启用/禁用发现

可以使用 `$enabled` 类变量开启或关闭系统中的所有自动发现。False 将禁用所有发现， 优化性能，但会失去模块和 Composer 包的特殊功能。

### 指定发现项目

使用 `$aliases` 选项，可以指定哪些项目被自动发现。如果项目不存在， 则不会对该项目进行自动发现，但数组中的其他项目仍将被发现。

### 发现与 Composer

通过 Composer 使用 PSR-4 命名空间安装的包默认也会被发现。 PSR-0 命名空间的包将不会被检测到。

#### 指定 Composer 包

Added in version 4.3.0.

为避免浪费时间扫描不相关的 Composer 包，可以通过编辑 app/Config/Modules.php 中的 `$composerPackages` 变量手动指定要发现的包：

    <?php

    namespace Config;

    use CodeIgniter\Modules\Modules as BaseModules;

    class Modules extends BaseModules
    {
        // ...

        public $composerPackages = [
            'only' => [
                // List up all packages to auto-discover
                'codeigniter4/shield',
            ],
        ];

        // ...
    }

或者，可以指定要从发现中排除的包。

    <?php

    namespace Config;

    use CodeIgniter\Modules\Modules as BaseModules;

    class Modules extends BaseModules
    {
        // ...

        public $composerPackages = [
            'exclude' => [
                // List up packages to exclude.
                'pestphp/pest',
            ],
        ];

        // ...
    }

#### 禁用 Composer 包发现

如果在定位文件时不希望扫描 Composer 的所有已知目录，可以通过编辑 app/Config/Modules.php 中的 `$discoverInComposer` 变量将其关闭：

    <?php

    namespace Config;

    use CodeIgniter\Modules\Modules as BaseModules;

    class Modules extends BaseModules
    {
        public $discoverInComposer = false;

        // ...
    }

## 文件操作

本节将介绍各种文件类型（如控制器、视图、语言文件等）及其在模块中的使用方法。尽管其中部分内容在用户指南的相关章节中有更详细的说明，但我们仍在此处进行了重述，以便你能更轻松地理解所有组件是如何协同工作的。

### 路由

默认情况下，模块内的 路由 会被自动扫描。可以在上述 Modules 配置文件中关闭此功能。

备注

由于文件被包含到当前作用域中，`$routes` 实例已经为你定义。 如果尝试重新定义该类，将导致错误。

使用模块时，如果应用程序中的路由包含通配符可能会出现问题。 在这种情况下，请参阅 路由优先级。

### 过滤器

自 4.4.2 版本弃用.

备注

此功能已弃用。请改用 注册器，如下所示：

    <?php

    namespace CodeIgniter\Shield\Config;

    use CodeIgniter\Shield\Filters\SessionAuth;
    use CodeIgniter\Shield\Filters\TokenAuth;

    class Registrar
    {
        /**
         * Registers the Shield filters.
         */
        public static function Filters(): array
        {
            return [
                'aliases' => [
                    'session' => SessionAuth::class,
                    'tokens'  => TokenAuth::class,
                ],
            ];
        }
    }

默认情况下，模块内的 过滤器 会被自动扫描。 可以在上述 Modules 配置文件中关闭此功能。

备注

由于文件被包含到当前作用域中，`$filters` 实例已经为你定义。 如果尝试重新定义该类，将导致错误。

在模块的 Config/Filters.php 文件中，需要定义所使用过滤器的别名：

    <?php

    $filters->aliases['menus'] = \App\Filters\MenusFilter::class;

### 控制器

主 app/Controllers 目录外的控制器无法通过 URI 检测自动路由， 但必须在 Routes 文件本身中指定：

    <?php

    // Routes.php
    $routes->get('blog', '\Acme\Blog\Controllers\Blog::index');

为减少此处所需的输入量，group 路由功能很有帮助：

    <?php

    $routes->group('blog', ['namespace' => 'Acme\Blog\Controllers'], static function ($routes) {
        $routes->get('/', 'Blog::index');
    });

### 配置文件

使用配置文件时不需要特殊更改。这些仍然是带命名空间的类，使用 `new` 命令加载：

    <?php

    $config = new \Acme\Blog\Config\Blog();

在使用始终可用的 `config()` 函数并向其传递短类名时，配置文件会被自动发现。

备注

我们不建议你在模块中使用相同的短类名。 需要覆盖或添加到 app/Config/ 中已知配置的模块应使用 注册器。

备注

在 v4.4.0 之前，当存在相同短名称的类时， 即使指定像 `config(\Acme\Blog\Config\Blog::class)` 这样的完全限定类名， `config()` 也会在 app/Config/ 中查找文件。 此行为已在 v4.4.0 中修复，并返回指定的实例。

### 迁移

迁移文件将在定义的命名空间内自动发现。每次运行时都会执行在所有命名空间中找到的所有迁移。

### 数据填充

只要提供完整的命名空间，填充文件就可以从 CLI 使用，也可以从其他填充文件中调用。 如果在 CLI 上调用，需要提供双反斜杠：

对于 Unix：

    php spark db:seed Acme\\Blog\\Database\\Seeds\\TestPostSeeder

对于 Windows：

    php spark db:seed Acme\\Blog\\Database\\Seeds\\TestPostSeeder

### 辅助函数

使用 `helper()` 函数时，只要位于命名空间的 Helpers 目录内， 辅助函数将在定义的命名空间内自动发现：

    <?php

    helper('blog');

可以指定命名空间。详情请参阅 从指定命名空间加载。

### 语言文件

使用 `lang()` 函数时，只要文件遵循与主应用程序目录相同的目录结构， 语言文件就会从定义的命名空间中自动定位。

### 库

库总是通过其完全限定的类名实例化，因此不提供特殊访问：

    <?php

    $lib = new \Acme\Blog\Libraries\BlogLib();

### 模型

如果通过完全限定的类名使用 `new` 关键字实例化模型，不提供特殊访问：

    <?php

    $model = new \Acme\Blog\Models\PostModel();

使用始终可用的 `model()` 函数时，模型文件会被自动发现。

备注

我们不建议你在模块中使用相同的短类名。

备注

在 v4.4.0 之前，当存在相同短名称的类时， 即使指定像 `model(\Acme\Blog\Model\PostModel::class)` 这样的完全限定类名， `model()` 也会在 app/Models/ 中查找文件。 有关更多信息，请参阅 传递完全限定类名 中的说明。

### 视图

可以使用类命名空间加载视图，如 命名空间视图 文档中所述：

    <?php

    echo view('Acme\Blog\Views\index');