From a830e4e339aa508ac98d84012983ac110d2bc6d9 Mon Sep 17 00:00:00 2001 From: Hex Date: Wed, 31 Jan 2024 15:51:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=88=B0=204.4.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 10 +- source/changelogs/index.rst | 2 + source/changelogs/v4.4.4.rst | 63 ++++++++ source/changelogs/v4.4.5.rst | 19 +++ source/cli/cli_generators.rst | 2 +- source/cli/cli_library/002.php | 2 + source/cli/cli_library/003.php | 2 + source/cli/cli_library/004.php | 2 + source/cli/cli_library/005.php | 2 + source/cli/cli_library/006.php | 2 + source/cli/cli_library/007.php | 2 + source/cli/cli_library/008.php | 2 + source/cli/cli_library/009.php | 2 + source/cli/cli_library/010.php | 2 + source/cli/cli_library/011.php | 2 + source/cli/cli_library/012.php | 2 + source/cli/cli_library/013.php | 2 + source/cli/cli_library/014.php | 2 + source/cli/cli_library/015.php | 2 + source/cli/cli_library/016.php | 2 + source/cli/cli_library/017.php | 2 + source/cli/cli_library/018.php | 2 + source/cli/cli_library/019.php | 2 + source/cli/cli_library/020.php | 2 + source/cli/cli_library/021.php | 2 + source/cli/cli_library/022.php | 2 + source/cli/cli_library/023.php | 2 + source/concepts/factories/002.php | 2 + source/concepts/factories/004.php | 2 + source/concepts/factories/006.php | 3 + source/concepts/factories/007.php | 2 + source/concepts/factories/010.php | 2 + source/concepts/factories/013.php | 2 + source/concepts/factories/014.php | 2 + source/conf.py | 2 +- source/database/events.rst | 4 +- source/database/events/001.php | 16 +- source/database/query_builder.rst | 93 ++++++++---- source/database/query_builder/077.php | 8 +- source/database/query_builder/087.php | 9 +- source/database/query_builder/089.php | 11 +- source/database/query_builder/111.php | 2 + source/database/query_builder/113.php | 9 +- source/database/query_builder/115.php | 2 + source/database/query_builder/116.php | 2 + source/database/query_builder/121.php | 7 + source/database/query_builder/122.php | 10 ++ source/database/results/003.php | 4 +- source/database/results/007.php | 4 +- source/database/results/013.php | 4 +- source/database/results/014.php | 4 +- source/database/results/015.php | 6 +- source/database/results/016.php | 2 +- source/database/utilities.rst | 92 ++++++++++-- source/database/utilities/001.php | 18 ++- source/database/utilities/002.php | 3 + source/database/utilities/003.php | 3 + source/database/utilities/004.php | 9 ++ source/database/utilities/005.php | 7 + source/database/utilities/006.php | 7 + source/database/utilities/008.php | 9 ++ source/database/utilities/009.php | 8 + source/database/utilities/010.php | 12 ++ source/dbmgmt/forge.rst | 56 +++++-- source/dbmgmt/forge/007.php | 1 + source/dbmgmt/forge/025.php | 2 +- source/dbmgmt/forge/028.php | 2 +- source/dbmgmt/migration.rst | 4 +- source/extending/core_classes.rst | 59 +++++--- source/extending/core_classes/002.php | 2 +- source/extending/events/002.php | 4 +- source/extending/events/003.php | 2 + source/extending/events/006.php | 4 +- source/extending/events/007.php | 2 + source/extending/events/008.php | 2 + source/general/ajax.rst | 2 +- source/general/common_functions.rst | 4 +- source/general/helpers.rst | 54 +++++-- source/general/helpers/008.php | 12 ++ source/general/modules/005.php | 2 +- source/helpers/array_helper/014.php | 2 +- source/helpers/cookie_helper.rst | 6 +- source/helpers/filesystem_helper.rst | 26 ++-- source/helpers/filesystem_helper/004.php | 2 +- source/helpers/filesystem_helper/005.php | 2 +- source/helpers/filesystem_helper/010.php | 2 +- source/helpers/filesystem_helper/011.php | 2 +- source/incoming/controllers.rst | 69 +++++---- source/incoming/controllers/006.php | 2 +- source/incoming/filters.rst | 21 ++- source/incoming/filters/004.php | 2 +- source/incoming/filters/008.php | 2 +- source/incoming/incomingrequest.rst | 142 +++++++++++------- source/incoming/incomingrequest/003.php | 2 + source/incoming/incomingrequest/008.php | 2 +- source/incoming/incomingrequest/010.php | 4 +- source/incoming/incomingrequest/014.php | 2 +- source/incoming/incomingrequest/041.php | 3 + source/incoming/incomingrequest/042.php | 3 + source/incoming/incomingrequest/043.php | 4 + source/incoming/incomingrequest/044.php | 3 + source/incoming/incomingrequest/045.php | 3 + source/incoming/request.rst | 2 + source/incoming/restful.rst | 12 ++ source/incoming/restful/003.php | 5 +- source/incoming/restful/011.php | 5 +- source/incoming/restful/017.php | 5 + source/incoming/restful/018.php | 7 + source/incoming/restful/019.php | 5 + source/incoming/restful/020.php | 7 + source/incoming/routing.rst | 64 ++++++-- source/incoming/routing/011.php | 2 +- source/incoming/routing/020.php | 2 + source/incoming/routing/045.php | 4 + source/incoming/routing/046.php | 2 +- source/incoming/routing/049.php | 5 +- source/incoming/routing/050.php | 5 +- source/incoming/routing/051.php | 6 +- source/incoming/routing/052.php | 12 ++ source/incoming/routing/068.php | 13 ++ source/installation/repositories.rst | 3 + source/installation/upgrade_444.rst | 91 ++++++++++++ source/installation/upgrade_444/001.php | 38 +++++ source/installation/upgrade_445.rst | 28 ++++ source/installation/upgrade_4xx.rst | 17 ++- source/installation/upgrade_controllers.rst | 1 + source/installation/upgrade_emails.rst | 2 + source/installation/upgrade_routing.rst | 12 +- source/installation/upgrade_routing/001.php | 15 +- source/installation/upgrade_sessions.rst | 4 + source/installation/upgrading.rst | 2 + source/libraries/caching/012.php | 2 + source/libraries/cookies/013.php | 2 + source/libraries/curlrequest.rst | 20 ++- source/libraries/email.rst | 66 +++++---- source/libraries/files/011.php | 2 + source/libraries/files/015.php | 2 + source/libraries/images/007.php | 2 +- source/libraries/official_packages.rst | 17 ++- source/libraries/pagination.rst | 50 +++++-- source/libraries/publisher/006.php | 2 + source/libraries/publisher/011.php | 2 + source/libraries/publisher/012.php | 2 + source/libraries/security.rst | 12 +- source/libraries/time/002.php | 2 + source/libraries/time/003.php | 2 + source/libraries/time/004.php | 2 + source/libraries/time/005.php | 2 + source/libraries/time/006.php | 2 + source/libraries/time/007.php | 2 + source/libraries/time/008.php | 2 + source/libraries/time/009.php | 2 + source/libraries/time/010.php | 2 + source/libraries/time/011.php | 2 + source/libraries/time/012.php | 2 + source/libraries/time/013.php | 4 +- source/libraries/time/014.php | 2 + source/libraries/time/015.php | 2 + source/libraries/time/016.php | 2 + source/libraries/time/017.php | 2 + source/libraries/time/018.php | 2 + source/libraries/time/019.php | 2 + source/libraries/time/020.php | 27 ++-- source/libraries/time/021.php | 25 ++-- source/libraries/time/022.php | 2 + source/libraries/time/023.php | 2 + source/libraries/time/024.php | 6 +- source/libraries/time/025.php | 2 + source/libraries/time/026.php | 2 + source/libraries/time/027.php | 2 + source/libraries/time/029.php | 2 + source/libraries/time/030.php | 2 + source/libraries/time/032.php | 2 + source/libraries/time/034.php | 2 + source/libraries/time/035.php | 2 + source/libraries/time/037.php | 2 + source/libraries/time/038.php | 4 +- source/libraries/time/039.php | 2 + source/libraries/time/041.php | 2 + source/libraries/uri/007.php | 2 + source/libraries/validation.rst | 59 +++++--- source/libraries/validation/001.php | 8 +- source/libraries/validation/008.php | 3 +- source/libraries/validation/009.php | 7 +- source/libraries/validation/010.php | 2 +- source/models/entities.rst | 12 +- source/models/entities/018.php | 2 +- source/models/model.rst | 147 +++++++++++-------- source/models/model/001.php | 2 +- source/models/model/005.php | 2 + source/outgoing/csp.rst | 106 ++++++++++++++ source/outgoing/csp/011.php | 12 ++ source/outgoing/csp/012.php | 33 +++++ source/outgoing/csp/013.php | 9 ++ source/outgoing/index.rst | 1 + source/outgoing/response.rst | 153 +++++++------------- source/outgoing/response/022.php | 2 +- source/outgoing/response/031.php | 6 - source/outgoing/response/034.php | 4 + source/outgoing/response/035.php | 4 + source/outgoing/view_cells.rst | 28 +++- source/outgoing/view_cells/001.php | 5 + source/outgoing/view_cells/014.php | 2 +- source/outgoing/view_cells/016.php | 2 +- source/outgoing/view_cells/018.php | 2 +- source/outgoing/view_cells/019.php | 2 +- source/outgoing/view_parser.rst | 4 +- source/testing/fabricator/003.php | 3 + source/testing/fabricator/004.php | 3 + source/testing/fabricator/007.php | 3 + source/testing/fabricator/008.php | 4 +- source/testing/fabricator/020.php | 2 + source/testing/overview.rst | 16 ++ source/testing/overview/006.php | 11 +- source/testing/overview/007.php | 6 +- source/testing/overview/008.php | 2 + source/testing/overview/011.php | 2 + source/testing/overview/012.php | 2 + source/testing/overview/013.php | 2 + source/testing/overview/014.php | 2 + source/testing/overview/015.php | 2 + source/testing/overview/017.php | 3 + source/testing/overview/021.php | 26 ++++ source/testing/overview/022.php | 13 ++ source/tutorial/create_news_items.rst | 13 +- source/tutorial/create_news_items/005.php | 4 +- 226 files changed, 1848 insertions(+), 617 deletions(-) create mode 100644 source/changelogs/v4.4.4.rst create mode 100644 source/changelogs/v4.4.5.rst create mode 100644 source/database/query_builder/121.php create mode 100644 source/database/query_builder/122.php create mode 100644 source/database/utilities/002.php create mode 100644 source/database/utilities/003.php create mode 100644 source/database/utilities/004.php create mode 100644 source/database/utilities/005.php create mode 100644 source/database/utilities/006.php create mode 100644 source/database/utilities/008.php create mode 100644 source/database/utilities/009.php create mode 100644 source/database/utilities/010.php create mode 100644 source/general/helpers/008.php create mode 100644 source/incoming/incomingrequest/041.php create mode 100644 source/incoming/incomingrequest/042.php create mode 100644 source/incoming/incomingrequest/043.php create mode 100644 source/incoming/incomingrequest/044.php create mode 100644 source/incoming/incomingrequest/045.php create mode 100644 source/incoming/restful/017.php create mode 100644 source/incoming/restful/018.php create mode 100644 source/incoming/restful/019.php create mode 100644 source/incoming/restful/020.php create mode 100644 source/incoming/routing/068.php create mode 100644 source/installation/upgrade_444.rst create mode 100644 source/installation/upgrade_444/001.php create mode 100644 source/installation/upgrade_445.rst create mode 100644 source/outgoing/csp.rst create mode 100644 source/outgoing/csp/011.php create mode 100644 source/outgoing/csp/012.php create mode 100644 source/outgoing/csp/013.php create mode 100644 source/outgoing/response/034.php create mode 100644 source/outgoing/response/035.php create mode 100644 source/testing/overview/021.php create mode 100644 source/testing/overview/022.php diff --git a/requirements.txt b/requirements.txt index 82da765a..281ecada 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ -sphinx>=4.5.0,<5 -sphinxcontrib-phpdomain>=0.8.0 -docutils>=0.16 -sphinx-rtd-theme>=0.5.0 -jinja2<3.1 +sphinx>=5.3.0,<6.0.0 +sphinxcontrib-phpdomain>=0.11.0 +docutils>=0.19 +sphinx-rtd-theme>=2.0.0,<3.0.0 +jinja2>=3.1.3,<4.0.0 jieba==0.42.1 diff --git a/source/changelogs/index.rst b/source/changelogs/index.rst index da77a47c..7f5e7d91 100644 --- a/source/changelogs/index.rst +++ b/source/changelogs/index.rst @@ -12,6 +12,8 @@ .. toctree:: :titlesonly: + v4.4.5 + v4.4.4 v4.4.3 v4.4.2 v4.4.1 diff --git a/source/changelogs/v4.4.4.rst b/source/changelogs/v4.4.4.rst new file mode 100644 index 00000000..b76e0004 --- /dev/null +++ b/source/changelogs/v4.4.4.rst @@ -0,0 +1,63 @@ +############# +版本 4.4.4 +############# + +发布日期:2023 年 12 月 28 日 + +**CodeIgniter4 的 4.4.4 版本发布** + +.. contents:: + :local: + :depth: 3 + +******** +重大变化 +******** + +采用 Dot 数组语法进行验证 +========================= + +使用通配符 ``*`` 的验证规则现在只验证符合"Dot 数组语法"的正确维度数据。 +详见 :ref:`升级 ` 获取详情。 + +验证规则匹配和差异 +=================== + +在严格和传统规则中,``matches`` 和 ``differs`` 验证非字符串类型数据的情况已修复。 + +在 CURLRequest 中移除了 `ssl_key` 选项的使用 +============================================ + +由于一个错误,我们在 CURLRequest 中使用了未记录的 `ssl_key` 配置选项来定义 CA bundle。现在已经修复,并且按照文档要求工作。你可以通过 `verify` 选项来定义你的 CA bundle。 + +文件系统助手 +============= + +:php:func:`get_filenames()` 现在会跟踪符号连接文件夹,而之前只是返回而不跟踪。 + +******** +改进 +******** + +- 完全支持 PHP 8.3。 + +*************** +消息变更 +*************** + +- 添加 ``HTTP.invalidJSON`` 错误消息。 +- 添加 ``HTTP.unsupportedJSONFormat`` 错误消息。 + +************ +弃用 +************ + +- **请求:** :php:meth:`CodeIgniter\\HTTP\\Request::getEnv()` 方法已被弃用。此方法自从一开始就没有工作,请使用 :php:func:`env()`。 + +************ +已修复的错误 +************ + +- **CURLRequest:** 修复了即使配置项 'verify' 设置为 *false* 时,也会检查主机名的错误。 + +要查看完整的错误修复列表,请去看仓库的 `CHANGELOG.md `_。 diff --git a/source/changelogs/v4.4.5.rst b/source/changelogs/v4.4.5.rst new file mode 100644 index 00000000..6b7a9e4b --- /dev/null +++ b/source/changelogs/v4.4.5.rst @@ -0,0 +1,19 @@ +############# +版本 4.4.5 +############# + +发布日期:2024 年 1 月 27 日 + +**CodeIgniter4 的 4.4.5 版本发布** + +.. contents:: + :local: + :depth: 3 + +************ +已修复的错误 +************ + +- **QueryBuilder:** 修复了因为 PostgreSQL 上的类型错误,导致 ``updateBatch()`` 方法无法工作的问题。 + +要查看完整的错误修复列表,可以查看仓库的 `CHANGELOG.md `_。 diff --git a/source/cli/cli_generators.rst b/source/cli/cli_generators.rst index 18aa5e2c..a339ce9f 100644 --- a/source/cli/cli_generators.rst +++ b/source/cli/cli_generators.rst @@ -75,7 +75,7 @@ make:command 选项: ======== * ``--command``:在 spark 中运行的命令名称。默认为 ``command:name``。 -* ``--group``:命令的组/命名空间。对于基本命令默认为 ``CodeIgniter``,对于生成器命令默认为 ``Generators``。 +* ``--group``:命令的组/命名空间。对于基本命令默认为 ``App``,对于生成器命令默认为 ``Generators``。 * ``--type``:命令类型,可以是 ``basic`` 基本命令或 ``generator`` 生成器命令。默认为 ``basic``。 * ``--namespace``:设置根命名空间。默认为 ``APP_NAMESPACE`` 的值。 * ``--suffix``:在生成的类名后附加组件后缀。 diff --git a/source/cli/cli_library/002.php b/source/cli/cli_library/002.php index 076bf0c4..1576f82e 100644 --- a/source/cli/cli_library/002.php +++ b/source/cli/cli_library/002.php @@ -1,3 +1,5 @@ 'The red apple', 'orange' => 'The plump orange', diff --git a/source/cli/cli_library/009.php b/source/cli/cli_library/009.php index a5bc3f49..672b67ac 100644 --- a/source/cli/cli_library/009.php +++ b/source/cli/cli_library/009.php @@ -1,3 +1,5 @@ FilterInterface::class, 'prefersApp' => false, diff --git a/source/concepts/factories/007.php b/source/concepts/factories/007.php index 2a7def23..0a07cf35 100644 --- a/source/concepts/factories/007.php +++ b/source/concepts/factories/007.php @@ -1,4 +1,6 @@ true]); // Default; will always be the same instance $other = Factories::models('UserModel', ['getShared' => false]); // Will always create a new instance diff --git a/source/concepts/factories/010.php b/source/concepts/factories/010.php index 90f5f26e..29abf8cd 100644 --- a/source/concepts/factories/010.php +++ b/source/concepts/factories/010.php @@ -1,3 +1,5 @@ false]); diff --git a/source/concepts/factories/013.php b/source/concepts/factories/013.php index ce10c928..8f6ed48f 100644 --- a/source/concepts/factories/013.php +++ b/source/concepts/factories/013.php @@ -1,3 +1,5 @@ ` 实例。你可以使用它在 STDOUT 中显示所有查询,或日志记录到文件,甚至创建工具来执行自动查询分析以帮助你发现可能缺少的索引、慢查询等。示例用法可能是: +无论成功与否,每当有新的查询被执行时,此事件就会被触发。唯一的参数是当前查询的 :doc:`查询 ` 实例。你可以使用这个来在 STDOUT 显示所有的查询,或者记录到文件,甚至创建工具来进行自动查询分析,帮助你发现可能丢失的索引、慢查询等。 + +示例用法可能是: .. literalinclude:: events/001.php diff --git a/source/database/events/001.php b/source/database/events/001.php index 4461b715..ed2bb1fc 100644 --- a/source/database/events/001.php +++ b/source/database/events/001.php @@ -1,4 +1,18 @@ insert() 第一个参数是一个关联数组。 +.. note:: 除 ``RawSql`` 外,所有值都会自动转义,生成更安全的查询。 + +.. warning:: 当你使用 ``RawSql`` 时,必须手动对数据进行转义。否则可能会导致 SQL 注入。 + 这是一个使用对象的示例: .. literalinclude:: query_builder/077.php -第一个参数是一个对象。 - -.. note:: 除 ``RawSql`` 外,所有值都会自动转义,生成更安全的查询。 +.. literalinclude:: query_builder/121.php -.. warning:: 当你使用 ``RawSql`` 时,必须手动对数据进行转义。否则可能会导致 SQL 注入。 +第一个参数是一个对象。 $builder->ignore() ------------------ @@ -775,6 +777,9 @@ insertBatch $builder->insertBatch() ----------------------- +通过数据插入 +^^^^^^^^^^^^^^^^ + 根据你提供的数据生成 insert 字符串,并运行查询。你可以将一个 **数组** 或 **对象** 传递给该方法。下面是一个使用数组的示例: .. literalinclude:: query_builder/081.php @@ -785,6 +790,9 @@ $builder->insertBatch() .. warning:: 当你使用 ``RawSql`` 时,必须手动对数据进行转义。否则可能会导致 SQL 注入。 +通过查询插入 +^^^^^^^^^^^^^^^^^^^ + 你也可以从查询中插入: .. literalinclude:: query_builder/117.php @@ -815,6 +823,8 @@ $builder->upsert() 这是一个使用对象的示例: +.. literalinclude:: query_builder/122.php + .. literalinclude:: query_builder/113.php 第一个参数是一个对象。 @@ -842,7 +852,12 @@ $builder->upsertBatch() .. versionadded:: 4.3.0 -根据你提供的数据生成插入更新字符串,并运行查询。你可以将一个 **数组** 或 **对象** 传递给该方法。默认情况下,约束将按顺序定义。首先选择主键,然后是唯一键。MySQL 将默认使用任何约束。下面是一个使用数组的示例: +通过数据插入更新 +^^^^^^^^^^^^^^^^ + +根据你提供的数据生成插入更新字符串,并运行查询。你可以将一个 **数组** 或 **对象** 传递给该方法。默认情况下,约束将按顺序定义。首先选择主键,然后是唯一键。MySQL 将默认使用任何约束。 + +下面是一个使用数组的示例: .. literalinclude:: query_builder/108.php @@ -850,6 +865,9 @@ $builder->upsertBatch() .. note:: 所有值都会自动转义,生成更安全的查询。 +通过查询插入更新 +^^^^^^^^^^^^^^^^^^^ + 你也可以从查询中插入更新: .. literalinclude:: query_builder/115.php @@ -928,6 +946,8 @@ $builder->set() 或者一个对象: +.. literalinclude:: query_builder/077.php + .. literalinclude:: query_builder/087.php $builder->update() @@ -939,6 +959,8 @@ $builder->update() 或者你可以提供一个对象: +.. literalinclude:: query_builder/077.php + .. literalinclude:: query_builder/089.php .. note:: 除 ``RawSql`` 外,所有值都会自动转义,生成更安全的查询。 @@ -956,6 +978,15 @@ $builder->update() 你也可以在执行更新时使用上面描述的 ``$builder->set()`` 方法。 +$builder->getCompiledUpdate() +----------------------------- + +此方法的工作方式与 ``$builder->getCompiledInsert()`` 完全相同,只是它生成的是 **UPDATE** SQL 字符串,而不是 **INSERT** SQL 字符串。 + +要获取更多信息,请查看 `$builder->getCompiledInsert()`_ 的文档。 + +.. note:: 这个方法不适用于批量更新。 + .. _update-batch: UpdateBatch @@ -966,15 +997,18 @@ $builder->updateBatch() .. note:: 从 v4.3.0 开始, ``updateBatch()`` 的第二个参数 ``$index`` 改为 ``$constraints``。它现在接受数组、字符串或 ``RawSql`` 类型。 +通过数据更新 +^^^^^^^^^^^^^^^^ + 根据你提供的数据生成 update 字符串,并运行查询。你可以将一个 **数组** 或 **对象** 传递给该方法。下面是一个使用数组的示例: .. literalinclude:: query_builder/092.php -.. note:: 从 v4.3.0 开始,生成的 SQL 结构得到了改进。 - 第一个参数是一个关联数组,第二个参数是 where 键。 -从 v4.3.0 开始,你也可以使用 ``setQueryAsData()``、``onConstraint()`` 和 ``updateFields()`` 方法: +.. note:: 从 v4.3.0 开始,生成的 SQL 结构得到了改进。 + +从 v4.3.0 开始,你也可以使用 ``onConstraint()`` 和 ``updateFields()`` 方法: .. literalinclude:: query_builder/120.php @@ -985,23 +1019,15 @@ $builder->updateBatch() .. note:: 由于这项工作的性质,此方法无法为 ``affectedRows()`` 提供适当的结果。 相反, ``updateBatch()`` 返回受影响的行数。 -你也可以从查询中更新: +通过查询更新 +^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: query_builder/116.php +从 v4.3.0 开始,你也可以使用 ``setQueryAsData()`` 方法从查询中进行更新: -.. note:: 可以从 v4.3.0 开始使用 ``setQueryAsData()``、``onConstraint()`` 和 ``updateFields()`` 方法。 +.. literalinclude:: query_builder/116.php .. note:: 必须将选择查询的列别名为目标表的列名。 -$builder->getCompiledUpdate() ------------------------------ - -此方法的工作方式与 ``$builder->getCompiledInsert()`` 完全相同,只是它生成 **UPDATE** SQL 字符串而不是 **INSERT** SQL 字符串。 - -有关更多信息,请查看 ``$builder->getCompiledInsert()`` 的文档。 - -.. note:: 此方法不适用于批量更新。 - ************* 删除数据 ************* @@ -1023,27 +1049,41 @@ $builder->delete() 如果要从表中删除所有数据,可以使用 ``truncate()`` 方法或 ``emptyTable()``。 +$builder->getCompiledDelete() +----------------------------- + +此方法的工作方式与 ``$builder->getCompiledInsert()`` 完全相同,只是它生成 **DELETE** SQL 字符串而不是 **INSERT** SQL 字符串。 + +有关更多信息,请查看 `$builder->getCompiledInsert()`_ 的文档。 + .. _delete-batch: +DeleteBatch +=========== + $builder->deleteBatch() ----------------------- .. versionadded:: 4.3.0 +通过数据删除 +^^^^^^^^^^^^^^^^ + 根据一组数据生成批量 **DELETE** 语句。 .. literalinclude:: query_builder/118.php 当在具有复合主键的表中删除数据时,此方法特别有用。 -.. note:: SQLite 不支持使用 ``where()``。 +.. note:: SQLite3 不支持使用 ``where()``。 + +通过查询删除 +^^^^^^^^^^^^^^^^^^^ 你也可以从查询中删除: .. literalinclude:: query_builder/119.php -.. note:: 可以从 v4.3.0 开始使用 ``deleteBatch()``。 - $builder->emptyTable() ---------------------- @@ -1060,13 +1100,6 @@ $builder->truncate() .. note:: 如果不可用 TRUNCATE 命令, ``truncate()`` 将使用 ``DELETE FROM table``。 -$builder->getCompiledDelete() ------------------------------ - -此方法的工作方式与 ``$builder->getCompiledInsert()`` 完全相同,只是它生成 **DELETE** SQL字符串而不是 **INSERT** SQL字符串。 - -有关更多信息,请查看 ``$builder->getCompiledInsert()`` 的文档。 - ********************** 条件语句 ********************** diff --git a/source/database/query_builder/077.php b/source/database/query_builder/077.php index ba38d99f..15f26e6f 100644 --- a/source/database/query_builder/077.php +++ b/source/database/query_builder/077.php @@ -1,12 +1,10 @@ insert($object); -// Produces: INSERT INTO mytable (title, content, date) VALUES ('My Title', 'My Content', 'My Date') diff --git a/source/database/query_builder/087.php b/source/database/query_builder/087.php index d395bc3b..3ecba8d3 100644 --- a/source/database/query_builder/087.php +++ b/source/database/query_builder/087.php @@ -1,12 +1,7 @@ set($object); $builder->insert(); diff --git a/source/database/query_builder/089.php b/source/database/query_builder/089.php index 445e5371..be7e3b9a 100644 --- a/source/database/query_builder/089.php +++ b/source/database/query_builder/089.php @@ -1,18 +1,13 @@ where('id', $id); $builder->update($object); /* * Produces: * UPDATE `mytable` - * SET `title` = '{$title}', `name` = '{$name}', `date` = '{$date}' + * SET `title` = '{$title}', `content` = '{$content}', `date` = '{$date}' * WHERE id = `$id` */ diff --git a/source/database/query_builder/111.php b/source/database/query_builder/111.php index e02a3d17..707c5fc2 100644 --- a/source/database/query_builder/111.php +++ b/source/database/query_builder/111.php @@ -1,5 +1,7 @@ 2, diff --git a/source/database/query_builder/113.php b/source/database/query_builder/113.php index 862839b5..f57f8410 100644 --- a/source/database/query_builder/113.php +++ b/source/database/query_builder/113.php @@ -1,11 +1,6 @@ upsert($object); diff --git a/source/database/query_builder/115.php b/source/database/query_builder/115.php index 7526474d..803e2be5 100644 --- a/source/database/query_builder/115.php +++ b/source/database/query_builder/115.php @@ -1,5 +1,7 @@ db->table('user2') ->select('user2.name, user2.email, user2.country') ->join('user', 'user.email = user2.email', 'left') diff --git a/source/database/query_builder/116.php b/source/database/query_builder/116.php index 416fe9c9..8ec1f068 100644 --- a/source/database/query_builder/116.php +++ b/source/database/query_builder/116.php @@ -1,5 +1,7 @@ db->table('user2') ->select('user2.name, user2.email, user2.country') ->join('user', 'user.email = user2.email', 'inner') diff --git a/source/database/query_builder/121.php b/source/database/query_builder/121.php new file mode 100644 index 00000000..d230aba5 --- /dev/null +++ b/source/database/query_builder/121.php @@ -0,0 +1,7 @@ +insert($object); +// Produces: INSERT INTO mytable (title, content, date) VALUES ('My Title', 'My Content', 'My Date') diff --git a/source/database/query_builder/122.php b/source/database/query_builder/122.php new file mode 100644 index 00000000..3197fcb0 --- /dev/null +++ b/source/database/query_builder/122.php @@ -0,0 +1,10 @@ +query('SELECT * FROM users;'); -foreach ($query->getResult('User') as $user) { - echo $user->name; // access attributes +foreach ($query->getResult(\App\Entities\User::class) as $user) { + echo $user->name; // access attributes echo $user->reverseName(); // or methods defined on the 'User' class } diff --git a/source/database/results/007.php b/source/database/results/007.php index 6a6c309e..ed155083 100644 --- a/source/database/results/007.php +++ b/source/database/results/007.php @@ -1,7 +1,7 @@ query('SELECT * FROM users LIMIT 1;'); -$row = $query->getRow(0, 'User'); +$row = $query->getRow(0, \App\Entities\User::class); -echo $row->name; // access attributes +echo $row->name; // access attributes echo $row->reverse_name(); // or methods defined on the 'User' class diff --git a/source/database/results/013.php b/source/database/results/013.php index d0b1e445..baef2cd9 100644 --- a/source/database/results/013.php +++ b/source/database/results/013.php @@ -1,12 +1,14 @@ query('YOUR QUERY'); -$rows = $query->getCustomResultObject('User'); +$rows = $query->getCustomResultObject(\App\Entities\User::class); foreach ($rows as $row) { echo $row->id; echo $row->email; - echo $row->last_login('Y-m-d'); + echo $row->lastLogin('Y-m-d'); } diff --git a/source/database/results/015.php b/source/database/results/015.php index 247d5d8b..7cc9b799 100644 --- a/source/database/results/015.php +++ b/source/database/results/015.php @@ -2,9 +2,9 @@ $query = $db->query('YOUR QUERY'); -$row = $query->getCustomRowObject(0, 'User'); +$row = $query->getCustomRowObject(0, \App\Entities\User::class); if (isset($row)) { - echo $row->email; // access attributes - echo $row->last_login('Y-m-d'); // access class methods + echo $row->email; // access attributes + echo $row->lastLogin('Y-m-d'); // access class methods } diff --git a/source/database/results/016.php b/source/database/results/016.php index ed5ac68c..f6d0e232 100644 --- a/source/database/results/016.php +++ b/source/database/results/016.php @@ -1,3 +1,3 @@ getCustomRowObject(0, 'User'); +$row = $query->getCustomRowObject(0, \App\Entities\User::class); diff --git a/source/database/utilities.rst b/source/database/utilities.rst index 40b9221e..8924639b 100644 --- a/source/database/utilities.rst +++ b/source/database/utilities.rst @@ -1,25 +1,95 @@ -######### -实用工具 -######### +###################### +数据库实用工具类 +###################### -数据库实用工具类包含帮助你管理数据库的方法。 +数据库实用工具类包含了帮助你管理数据库的方法。 .. contents:: :local: :depth: 2 -******************* -从结果中获取 XML -******************* +****************************** +初始化实用工具类 +****************************** -getXMLFromResult() +按照以下方式加载实用工具类: + +.. literalinclude:: utilities/002.php + :lines: 2- + +你也可以将另一数据库组传递给 DB Utility 加载器,以防你要管理的数据库不是默认的: + +.. literalinclude:: utilities/003.php + :lines: 2- + +在上述示例中,我们将数据库组名称作为第一参数传递了进去。 + +**************************** +使用数据库实用工具 +**************************** + +检索数据库名称列表 +=============================== + +返回一个包含数据库名称的数组: + +.. literalinclude:: utilities/004.php + :lines: 2- + +判断数据库是否存在 ================== -此方法从数据库结果返回 xml 结果。你可以这样做: +有时候,我们需要知道特定的数据库是否存在。 +返回一个布尔值 ``true``/``false``。使用示例: + +.. literalinclude:: utilities/005.php + :lines: 2- + +.. note:: 将 ``database_name`` 替换为你正在查找的数据库名称。此方法区分大小写。 + +优化数据表 +========== + +允许你使用第一参数中特定的表名来优化一个表。根据成功或失败返回 ``true``/``false``: + +.. literalinclude:: utilities/006.php + :lines: 2- + +.. note:: 并非所有的数据库平台都支持表优化。它主要用于 MySQL。 + +优化数据库 +=========== + +允许你优化 DB 类当前连接的数据库。成功返回包含数据库状态消息的数组,失败返回 ``false``: + +.. literalinclude:: utilities/008.php + :lines: 2- + +.. note:: 并非所有的数据库平台都支持数据库优化。它主要用于 MySQL。 + +将查询结果导出为 CSV 文件 +========================== + +允许你生成一个来自查询结果的 CSV 文件。方法的第一参数必须包含你的查询结果对象。示例: + +.. literalinclude:: utilities/009.php + :lines: 2- + +第二、第三和第四参数分别允许你设置分隔符、换行和封闭字符。默认的分隔符是逗号,``"\n"`` 用作新行,双引号用作封闭符。示例: + +.. literalinclude:: utilities/010.php + :lines: 2- + +.. important:: 这个方法不会为你写入 CSV 文件。它仅创建 CSV 布局。如果你需要写入文件,使用 :php:func:`write_file()` 辅助函数。 + +将查询结果导出为 XML 文档 +========================== + +通过此方法,你可以生成一个来自查询结果的 XML 文件。第一参数需要是一个查询结果对象,第二参数可能包含一个可选的配置参数数组。示例: .. literalinclude:: utilities/001.php -它将获取以下 xml 结果:: +当 ``mytable`` 有 ``id`` 和 ``name`` 列时,将得到以下 xml 结果:: @@ -27,3 +97,5 @@ getXMLFromResult() bar + +.. important:: 这个方法不会为你写入 XML 文件。它仅仅创建 XML 布局。如果你需要写入文件,使用 :php:func:`write_file()` 辅助函数。 diff --git a/source/database/utilities/001.php b/source/database/utilities/001.php index 41582ffc..131222ef 100644 --- a/source/database/utilities/001.php +++ b/source/database/utilities/001.php @@ -1,13 +1,15 @@ query('SELECT * FROM mytable'); -$util = \CodeIgniter\Database\Config::utils(); +$config = [ + 'root' => 'root', + 'element' => 'element', + 'newline' => "\n", + 'tab' => "\t", +]; -echo $util->getXMLFromResult($model->get()); +echo $dbutil->getXMLFromResult($query, $config); diff --git a/source/database/utilities/002.php b/source/database/utilities/002.php new file mode 100644 index 00000000..42bbbd63 --- /dev/null +++ b/source/database/utilities/002.php @@ -0,0 +1,3 @@ +listDatabases(); + +foreach ($dbs as $db) { + echo $db; +} diff --git a/source/database/utilities/005.php b/source/database/utilities/005.php new file mode 100644 index 00000000..92ddaa34 --- /dev/null +++ b/source/database/utilities/005.php @@ -0,0 +1,7 @@ +databaseExists('database_name')) { + // some code... +} diff --git a/source/database/utilities/006.php b/source/database/utilities/006.php new file mode 100644 index 00000000..e85a0793 --- /dev/null +++ b/source/database/utilities/006.php @@ -0,0 +1,7 @@ +optimizeTable('table_name')) { + echo 'Success!'; +} diff --git a/source/database/utilities/008.php b/source/database/utilities/008.php new file mode 100644 index 00000000..726b15a8 --- /dev/null +++ b/source/database/utilities/008.php @@ -0,0 +1,9 @@ +optimizeDatabase(); + +if ($result !== false) { + print_r($result); +} diff --git a/source/database/utilities/009.php b/source/database/utilities/009.php new file mode 100644 index 00000000..f8bf84f6 --- /dev/null +++ b/source/database/utilities/009.php @@ -0,0 +1,8 @@ +query('SELECT * FROM mytable'); + +echo $dbutil->getCSVFromResult($query); diff --git a/source/database/utilities/010.php b/source/database/utilities/010.php new file mode 100644 index 00000000..7e11cc18 --- /dev/null +++ b/source/database/utilities/010.php @@ -0,0 +1,12 @@ +query('SELECT * FROM mytable'); + +$delimiter = ','; +$newline = "\r\n"; +$enclosure = '"'; + +echo $dbutil->getCSVFromResult($query, $delimiter, $newline, $enclosure); diff --git a/source/dbmgmt/forge.rst b/source/dbmgmt/forge.rst index 174d10f9..54ee6ca9 100644 --- a/source/dbmgmt/forge.rst +++ b/source/dbmgmt/forge.rst @@ -82,26 +82,58 @@ CodeIgniter 支持直接从喜欢的终端使用专用的 ``db:create`` 命令 添加字段 ============= -字段通常通过关联数组创建。在数组中,你必须包含与字段的数据类型相关的 ``type`` 键。 例如,INT、VARCHAR、TEXT 等。许多数据类型(例如 VARCHAR)还需要一个 ``constraint`` 键。 +$forge->addField() +------------------ + +字段通常通过关联数组创建。在数组中,你必须包含与字段的数据类型相关的 ``type`` 键。 + +例如, ``INT``、``VARCHAR``、``TEXT`` 等。许多数据类型(例如 ``VARCHAR``)还需要一个 ``constraint`` 键。 .. literalinclude:: forge/006.php 另外,可以使用以下键/值: -- ``unsigned``/true : 在字段定义中生成“UNSIGNED”。 -- ``default``/value : 在字段定义中生成默认值。 -- ``null``/true : 在字段定义中生成“null”。如果不指定,字段将默认为“NOT null”。 -- ``auto_increment``/true : 在字段上生成 auto_increment 标志。请注意,字段类型必须是支持这一点的类型,如整数。 +- ``unsigned``/true : 在字段定义中生成 ``UNSIGNED``。 +- ``default``/value : 在字段定义中生成 ``DEFAULT`` 约束。 +- ``null``/true : 在字段定义中生成 ``null``。如果不指定,字段将默认为 ``NOT null``。 +- ``auto_increment``/true : 在字段上生成 auto_increment 标志。请注意,字段类型必须是支持这一点的类型,如 ``INTEGER``。 - ``unique``/true : 为字段定义生成唯一键。 .. literalinclude:: forge/007.php -在定义了字段后,可以使用 ``$forge->addField($fields)`` 后跟对 ``createTable()`` 方法的调用来添加它们。 +在定义了字段后,可以使用 ``$forge->addField($fields)`` 后跟对 :ref:`createTable() ` 方法的调用来添加它们。 -$forge->addField() ------------------- +关于数据类型的注解 +------------------- + +浮点类型 +^^^^^^^^^^^^^^^^^^^^ -``addField()`` 方法将接受上述数组。 +浮点类型,如 ``FLOAT`` 和 ``DOUBLE``,表示的是近似值。因此,当需要精确值时,不应使用它们。 + +:: + + mysql> CREATE TABLE t (f FLOAT, d DOUBLE); + mysql> INSERT INTO t VALUES(99.9, 99.9); + + mysql> SELECT * FROM t WHERE f=99.9; + Empty set (0.00 sec) + + mysql> SELECT * FROM t WHERE f > 99.89 AND f < 99.91; + +------+------+ + | f | d | + +------+------+ + | 99.9 | 99.9 | + +------+------+ + 1 row in set (0.01 sec) + +当需要保存精确的精度时,例如在处理货币数据,应使用 ``DECIMAL`` 或 ``NUMERIC``。 + +TEXT +^^^^ + +SQLSRV 上不应使用 ``TEXT``,它已被弃用。 +欲知详情,请参见 `ntext, text, 和 image (Transact-SQL) - SQL Server | Microsoft Learn `_。 .. _forge-addfield-default-value-rawsql: @@ -175,6 +207,8 @@ $forge->addUniqueKey() .. note:: SQLite3 不支持命名外键。CodeIgniter 将引用它们的 ``prefix_table_column_foreign``。 +.. _creating-a-table: + 创建表格 ================ @@ -259,9 +293,9 @@ $forge->modifyColumn() .. note:: ``modifyColumn()`` 可能会意外地更改 ``NULL``/``NOT NULL``。因此,建议始终为 ``null`` 键指定值。与创建表不同,如果未指定 ``null``,列将为 ``NULL``,而不是 ``NOT NULL``。 -.. note:: 由于一个错误,在 v4.3.3 之前,即使指定 ``'null' => false``,SQLite3 也可能不设置 ``NOT NULL``。 +.. note:: 由于一个错误,在 v4.3.4 之前,即使指定 ``'null' => false``,SQLite3 也可能不设置 ``NOT NULL``。 -.. note:: 由于一个错误,在 v4.3.3 之前,Postgres 和 SQLSRV 即使指定 ``'null' => false`` 也会设置 ``NOT NULL``。 +.. note:: 由于一个错误,在 v4.3.4 之前,Postgres 和 SQLSRV 即使指定 ``'null' => true`` 也会设置 ``NOT NULL``。 .. _db-forge-adding-keys-to-a-table: diff --git a/source/dbmgmt/forge/007.php b/source/dbmgmt/forge/007.php index 182ea834..1cfa7748 100644 --- a/source/dbmgmt/forge/007.php +++ b/source/dbmgmt/forge/007.php @@ -27,3 +27,4 @@ 'default' => 'pending', ], ]; +$forge->addField($fields); diff --git a/source/dbmgmt/forge/025.php b/source/dbmgmt/forge/025.php index 7e1aa8eb..848625a9 100644 --- a/source/dbmgmt/forge/025.php +++ b/source/dbmgmt/forge/025.php @@ -1,4 +1,4 @@ dropColumn('table_name', 'column_1,column_2'); // by proving comma separated column names +$forge->dropColumn('table_name', 'column_1,column_2'); // by proving comma separated column names $forge->dropColumn('table_name', ['column_1', 'column_2']); // by proving array of column names diff --git a/source/dbmgmt/forge/028.php b/source/dbmgmt/forge/028.php index 776c587b..b0a92357 100644 --- a/source/dbmgmt/forge/028.php +++ b/source/dbmgmt/forge/028.php @@ -1,5 +1,5 @@ dropPrimaryKey('tablename'); diff --git a/source/dbmgmt/migration.rst b/source/dbmgmt/migration.rst index 208f108e..7b1bc3f3 100644 --- a/source/dbmgmt/migration.rst +++ b/source/dbmgmt/migration.rst @@ -46,7 +46,7 @@ 数据库组 =============== -迁移只会针对单个数据库组运行。如果你在 **app/Config/Database.php** 中定义了多个组,那么它将针对那里指定的 ``$defaultGroup`` 运行。 +一个迁移只会对单个数据库组执行。如果你在 **app/Config/Database.php** 中定义了多个组,那么它将按照该配置文件中指定的 ``$defaultGroup`` 运行。 有时你可能需要为不同的数据库组使用不同的模式。也许你有一个数据库用于所有常规站点信息,而另一个数据库用于业务关键的数据。 @@ -54,6 +54,8 @@ .. literalinclude:: migration/003.php +.. note:: 跟踪已经运行过的迁移的 **migrations** 表将始终在默认数据库组中创建。 + 命名空间 ========== diff --git a/source/extending/core_classes.rst b/source/extending/core_classes.rst index faf15cb1..9c7ce85e 100755 --- a/source/extending/core_classes.rst +++ b/source/extending/core_classes.rst @@ -17,28 +17,43 @@ 以下是每次 CodeIgniter 运行时都会调用的核心系统类列表: -* ``CodeIgniter\Autoloader\Autoloader`` -* ``CodeIgniter\CodeIgniter`` -* ``CodeIgniter\Config\DotEnv`` -* ``CodeIgniter\Config\Services`` -* ``CodeIgniter\Controller`` -* ``CodeIgniter\Debug\Exceptions`` -* ``CodeIgniter\Debug\Timer`` -* ``CodeIgniter\Events\Events`` -* ``CodeIgniter\Filters\Filters`` -* ``CodeIgniter\HTTP\ContentSecurityPolicy`` -* ``CodeIgniter\HTTP\CLIRequest`` (如果仅从命令行启动) -* ``CodeIgniter\HTTP\IncomingRequest`` (如果通过 HTTP 启动) -* ``CodeIgniter\HTTP\Request`` -* ``CodeIgniter\HTTP\Response`` -* ``CodeIgniter\HTTP\Message`` -* ``CodeIgniter\HTTP\URI`` -* ``CodeIgniter\Log\Logger`` -* ``CodeIgniter\Log\Handlers\BaseHandler`` -* ``CodeIgniter\Log\Handlers\FileHandler`` -* ``CodeIgniter\Router\RouteCollection`` -* ``CodeIgniter\Router\Router`` -* ``CodeIgniter\View\View`` +* ``CodeIgniter\Autoloader\Autoloader`` +* ``CodeIgniter\Autoloader\FileLocator`` +* ``CodeIgniter\Cache\CacheFactory`` +* ``CodeIgniter\Cache\Handlers\BaseHandler`` +* ``CodeIgniter\Cache\Handlers\FileHandler`` +* ``CodeIgniter\Cache\ResponseCache`` +* ``CodeIgniter\CodeIgniter`` +* ``CodeIgniter\Config\BaseService`` +* ``CodeIgniter\Config\DotEnv`` +* ``CodeIgniter\Config\Factories`` +* ``CodeIgniter\Config\Services`` +* ``CodeIgniter\Controller`` +* ``CodeIgniter\Cookie\Cookie`` +* ``CodeIgniter\Cookie\CookieStore`` +* ``CodeIgniter\Debug\Exceptions`` +* ``CodeIgniter\Debug\Timer`` +* ``CodeIgniter\Events\Events`` +* ``CodeIgniter\Filters\Filters`` +* ``CodeIgniter\HTTP\CLIRequest`` (如果仅从命令行启动) +* ``CodeIgniter\HTTP\ContentSecurityPolicy`` +* ``CodeIgniter\HTTP\Header`` +* ``CodeIgniter\HTTP\IncomingRequest`` (如果通过 HTTP 启动) +* ``CodeIgniter\HTTP\Message`` +* ``CodeIgniter\HTTP\OutgoingRequest`` +* ``CodeIgniter\HTTP\Request`` +* ``CodeIgniter\HTTP\Response`` +* ``CodeIgniter\HTTP\SiteURI`` +* ``CodeIgniter\HTTP\SiteURIFactory`` +* ``CodeIgniter\HTTP\URI`` +* ``CodeIgniter\HTTP\UserAgent`` (如果通过 HTTP 启动) +* ``CodeIgniter\Log\Logger`` +* ``CodeIgniter\Log\Handlers\BaseHandler`` +* ``CodeIgniter\Log\Handlers\FileHandler`` +* ``CodeIgniter\Router\RouteCollection`` +* ``CodeIgniter\Router\Router`` +* ``CodeIgniter\Superglobals`` +* ``CodeIgniter\View\View`` 替换核心类 ====================== diff --git a/source/extending/core_classes/002.php b/source/extending/core_classes/002.php index 68d22ff5..a176459a 100644 --- a/source/extending/core_classes/002.php +++ b/source/extending/core_classes/002.php @@ -12,7 +12,7 @@ public static function routes(bool $getShared = true) return static::getSharedInstance('routes'); } - return new \App\Libraries\RouteCollection(static::locator(), config('Modules')); + return new \App\Libraries\RouteCollection(static::locator(), config(Modules::class), config(Routing::class)); } // ... diff --git a/source/extending/events/002.php b/source/extending/events/002.php index ca9bf007..4e602f66 100644 --- a/source/extending/events/002.php +++ b/source/extending/events/002.php @@ -1,10 +1,12 @@ `。 .. php:function:: csp_style_nonce() :returns: 样式标签的 CSP 随机数属性 :rtype: string - 返回样式标签的随机数属性。例如:``nonce="Eskdikejidojdk978Ad8jf"``。请参阅 :ref:`content-security-policy`。 + 返回样式标签的随机数属性。例如:``nonce="Eskdikejidojdk978Ad8jf"``。请参阅 :ref:`內容安全策略 `。 .. php:function:: csrf_token() diff --git a/source/general/helpers.rst b/source/general/helpers.rst index 568e9ef8..09eb698f 100755 --- a/source/general/helpers.rst +++ b/source/general/helpers.rst @@ -10,7 +10,7 @@ 什么是辅助函数? ***************** -顾名思义,辅助函数可以帮助你完成任务。每个 helper 文件只是某个特定类别的函数集合。有 **URL 辅助函数**,可以帮助创建链接,有 **表单辅助函数** 可以帮助创建表单元素, **文本辅助函数** 执行各种文本格式化, **Cookie 辅助函数** 设置和读取cookie, **文件辅助函数** 帮助处理文件等等。 +顾名思义,辅助函数可以帮助你完成任务。每个 helper 文件只是某个特定类别的函数集合。有 :doc:`URL 辅助函数 <../helpers/url_helper>`,可以帮助创建链接,有 :doc:`表单辅助函数 <../helpers/form_helper>` 可以帮助创建表单元素,:doc:`文本辅助函数 <../helpers/text_helper>` 执行各种文本格式化,:doc:`Cookie 辅助函数 <../helpers/cookie_helper>` 设置和读取 Cookie,:doc:`文件系统辅助函数 <../helpers/filesystem_helper>` 帮助处理文件等等。 与 CodeIgniter 中的大多数其他系统不同,辅助函数不是面向对象的格式。它们是简单的程序性函数。每个辅助函数执行一个特定的任务,不依赖于其他函数。 @@ -22,7 +22,7 @@ CodeIgniter 默认不加载辅助文件,所以使用辅助函数的第一步是 加载辅助函数 **************** -.. note:: URL 辅助函数总是加载的,所以你不需要自己加载它。 +.. note:: :doc:`../helpers/url_helper` 总是加载的,所以你不需要自己加载它。 加载单个辅助函数 ================ @@ -31,15 +31,15 @@ CodeIgniter 默认不加载辅助文件,所以使用辅助函数的第一步是 .. literalinclude:: helpers/001.php -其中 ``name`` 是辅助函数的文件名,不含 “**.php**” 文件扩展名或 “**_helper**” 部分。 +上述代码会加载 **name_helper.php** 文件。 .. important:: CodeIgniter 辅助函数文件名全部小写。因此,在区分大小写的文件系统(如 Linux)上, ``helper('Name')`` 将无法工作。 -例如,要加载名为 **cookie_helper.php** 的 **Cookie 辅助函数** 文件,你会这样做: +例如,要加载名为 **cookie_helper.php** 的 :doc:`../helpers/cookie_helper` 文件,你会这样做: .. literalinclude:: helpers/002.php -.. note:: 上面的辅助函数加载方法不返回值,所以不要试图将其分配给变量。只按显示的方式使用它。 +.. note:: :php:func:`helper()` 函数不返回值,所以不要试图将其分配给变量。只按上面示例的方式使用它。 自动发现和 Composer 包 ------------------------ @@ -54,6 +54,17 @@ CodeIgniter 默认会扫描所有命名空间。 或者,你可以为要加载的辅助函数 :ref:`指定一个命名空间 `。 +加载顺序 +---------- + +:php:func:`helper()` 函数会扫描通过所有定义的命名空间,并加载所有名称匹配的辅助函数。这样可以加载任何模块的辅助函数,以及你为此应用专门创建的任何辅助函数。 + +加载顺序如下: + +1. app/Helpers - 这里的文件总是首先加载。 +2. {namespace}/Helpers - 所有的命名空间都会按照它们定义的顺序依次循环。 +3. system/Helpers - 基础文件最后加载。 + 加载多个辅助函数 ======================== @@ -73,11 +84,11 @@ CodeIgniter 默认会扫描所有命名空间。 .. _helpers-loading-from-specified-namespace: 从指定命名空间加载 -=================================== +================================ -辅助函数可以从 **app/Helpers** 和 **system/Helpers** 之外的目录加载,只要该路径可以在定义的命名空间中找到即可。 +默认情况下,CodeIgniter 会在所有定义的命名空间中搜索辅助函数文件,并加载所有找到的文件。 -你会使用可以找到它们的命名空间前缀辅助函数的名称。在该命名空间目录内,加载器期望它存在于名为 **Helpers** 的子目录中。一个示例将有助于理解这一点。 +如果你只想加载特定命名空间中的一个辅助函数,在辅助函数的名称前加上它所在的命名空间作为前缀。在该命名空间目录中,加载器预期它位于一个名为 **Helpers** 的子目录内。以示例来帮助理解这一点。 对于此示例,假设我们已经将所有与博客相关的代码分组到自己的命名空间 ``Example\Blog`` 中。文件存在于我们的服务器上的 **Modules/Blog/** 中。因此,我们会将博客模块的辅助函数文件放在 **Modules/Blog/Helpers/** 中。**blog_helper** 文件将位于 **Modules/Blog/Helpers/blog_helper.php**。在我们的控制器中,我们可以使用以下命令加载辅助函数: @@ -111,11 +122,26 @@ CodeIgniter 默认会扫描所有命名空间。 其中 “Click Here” 是链接的名称,“blog/comments” 是你想要链接到的控制器/方法的 URI。 -******************* +**************** +创建辅助函数 +**************** + +创建自定义辅助函数 +======================= + +辅助函数文件名是 **辅助函数名** 和 **_helper.php**。 + +例如,要创建 info 辅助函数,你需要创建一个名为 +**app/Helpers/info_helper.php** 的文件,并向文件中添加一个函数: + +.. literalinclude:: helpers/008.php + +你可以在一个辅助函数文件中添加尽可能多的函数。 + “扩展”辅助函数 -******************* +=================== -要“扩展”辅助函数,请在 **app/Helpers/** 文件夹中创建一个与现有辅助函数相同名称的文件。 +要“扩展”辅助函数,请在 **app/Helpers** 文件夹中创建一个与现有辅助函数相同名称的文件。 如果你只需要为现有辅助函数添加一些功能 - 可能添加一个或两个函数,或者更改某个特定辅助函数的工作方式 - 那么用你的版本完全替换整个辅助函数有点过度设计。在这种情况下,最好只是“扩展”辅助函数。 @@ -127,11 +153,7 @@ CodeIgniter 默认会扫描所有命名空间。 .. important:: 不要指定命名空间 ``App\Helpers``。 -:php:func:`helper()` 函数将扫描 **app/Config/Autoload.php** 中定义的所有 PSR-4 命名空间,并加载所有匹配的同名辅助函数。这允许加载任何模块的辅助函数,以及你专门为此应用程序创建的任何辅助函数。加载顺序如下: - -1. app/Helpers - 这里加载的文件总是先加载。 -2. {namespace}/Helpers - 所有命名空间按定义的顺序循环。 -3. system/Helpers - 基础文件最后加载。 +参见 `加载顺序`_ 了解辅助函数文件的加载顺序。 ********* 接下来呢? diff --git a/source/general/helpers/008.php b/source/general/helpers/008.php new file mode 100644 index 00000000..475b4dd8 --- /dev/null +++ b/source/general/helpers/008.php @@ -0,0 +1,12 @@ +aliases['menus'] = MenusFilter::class; +$filters->aliases['menus'] = \App\Filters\MenusFilter::class; diff --git a/source/helpers/array_helper/014.php b/source/helpers/array_helper/014.php index 99089d23..8cd64f7f 100644 --- a/source/helpers/array_helper/014.php +++ b/source/helpers/array_helper/014.php @@ -1,6 +1,6 @@ [ diff --git a/source/helpers/cookie_helper.rst b/source/helpers/cookie_helper.rst index 725a3de1..1d5fab05 100755 --- a/source/helpers/cookie_helper.rst +++ b/source/helpers/cookie_helper.rst @@ -40,6 +40,8 @@ Cookie 辅助函数文件包含了帮助处理 cookie 的函数。 :doc:`Response 库 `,因为此函数是 :php:meth:`CodeIgniter\\HTTP\\Response::setCookie()` 的别名。 + .. note:: 这个辅助函数只设置全局响应实例的浏览器 Cookie(由 ``Services::response()`` 返回)。所以,如果你创建并返回另一个响应实例(例如,如果你调用 :php:func:`redirect()`),这里设置的 Cookie 不会自动发送。 + .. php:function:: get_cookie($index[, $xssClean = false[, $prefix = '']]) :param string $index: Cookie 名称 @@ -68,6 +70,8 @@ Cookie 辅助函数文件包含了帮助处理 cookie 的函数。 此函数与 :php:func:`set_cookie()` 其他方面相同,只是它没有 ``value`` 和 ``expire`` 参数。 + 这也只是为删除全局响应实例(由 ``Services::response()`` 返回)的浏览器 Cookie 设置浏览器 Cookie。 + .. note:: 当你使用 :php:func:`set_cookie()` 时,如果 ``value`` 设置为空字符串且 ``expire`` 设置为 ``0``,则 cookie 将被删除。 如果 ``value`` 设置为非空字符串且 ``expire`` 设置为 ``0``,则 cookie 仅在浏览器打开时有效。 @@ -82,4 +86,4 @@ Cookie 辅助函数文件包含了帮助处理 cookie 的函数。 :param string $prefix: Cookie 前缀 :rtype: bool - 通过名称检查 cookie 是否存在。这是 ``Response::hasCookie()`` 的别名。 + 检查在全局响应实例中(由 ``Services::response()`` 返回)是否存在同名的 Cookie。这是 :php:meth::`CodeIgniter\\HTTP\\Response::hasCookie()` 的别名。 diff --git a/source/helpers/filesystem_helper.rst b/source/helpers/filesystem_helper.rst index 48497d51..8328ce53 100755 --- a/source/helpers/filesystem_helper.rst +++ b/source/helpers/filesystem_helper.rst @@ -2,7 +2,7 @@ 文件系统辅助函数 ################# -文件系统辅助函数文件包含了帮助处理目录的函数。 +文件系统辅助函数文件包含了帮助处理文件和目录的函数。 .. contents:: :local: @@ -23,7 +23,7 @@ .. php:function:: directory_map($sourceDir[, $directoryDepth = 0[, $hidden = false]]) :param string $sourceDir: 源目录路径 - :param int $directoryDepth: 遍历的目录深度(0 = 完全递归,1 = 当前目录,等等) + :param int $directoryDepth: 遍历的目录深度(``0`` = 完全递归, ``1`` = 当前目录,等等) :param bool $hidden: 是否包含隐藏路径 :returns: 文件数组 :rtype: array @@ -32,13 +32,13 @@ .. literalinclude:: filesystem_helper/002.php - .. note:: 路径几乎总是相对于你的 main index.php 文件。 + .. note:: 路径几乎总是相对于你的主 **index.php** 文件。 - 包含在目录中的子文件夹也将被映射。如果你希望控制递归深度,可以使用第二个参数(整数)。深度为 1 只会映射顶级目录: + 包含在目录中的子文件夹也将被映射。如果你希望控制递归深度,可以使用第二个参数(整数)。深度为 ``1`` 只会映射顶级目录: .. literalinclude:: filesystem_helper/003.php - 默认情况下,返回的数组中不包括隐藏文件,跳过隐藏目录。要覆盖此行为,可以将第三个参数设置为 true(布尔值): + 默认情况下,返回的数组中不包括隐藏文件,跳过隐藏目录。要覆盖此行为,可以将第三个参数设置为 ``true`` (布尔值): .. literalinclude:: filesystem_helper/004.php @@ -92,7 +92,7 @@ :param string $path: 文件路径 :param string $data: 要写入文件的数据 :param string $mode: ``fopen()`` 模式 - :returns: 如果写入成功则为 true,如果有错误则为 false + :returns: 如果写入成功则为 ``true``,如果有错误则为 ``false`` :rtype: bool 将数据写入路径中指定的文件。如果文件不存在,则该函数将创建它。 @@ -105,11 +105,11 @@ .. literalinclude:: filesystem_helper/007.php - 默认模式为 'wb'。写入模式选项请参阅 `PHP 用户指南 `_。 + 默认模式为 ``'wb'``。写入模式选项请参阅 PHP 用户指南的 `fopen() `。 .. note:: 为了使此函数能够将数据写入文件,必须设置其权限以使其可写。如果文件不存在,则包含它的目录必须可写。 - .. note:: 该路径是相对于你的主站点 index.php 文件,而不是你的控制器或视图文件。CodeIgniter 使用前端控制器,因此路径始终相对于主站点 index。 + .. note:: 该路径是相对于你的主站点 **index.php** 文件,而不是你的控制器或视图文件。CodeIgniter 使用前端控制器,因此路径始终相对于主站点 index。 .. note:: 此函数在写入文件时对该文件进行排他锁定。 @@ -119,7 +119,7 @@ :param bool $delDir: 是否也删除目录 :param bool $htdocs: 是否跳过删除 .htaccess 和索引页面文件 :param bool $hidden: 是否也删除隐藏文件(以句点开头的文件) - :returns: 成功为 true,错误为 false + :returns: 成功为 ``true``,错误为 ``false`` :rtype: bool 删除提供的路径中包含的所有文件。 @@ -128,7 +128,7 @@ .. literalinclude:: filesystem_helper/008.php - 如果第二个参数设置为 true,则提供的根路径中包含的任何目录也将被删除。 + 如果第二个参数设置为 ``true``,则提供的根路径中包含的任何目录也将被删除。 例子: @@ -147,6 +147,8 @@ 获取一个服务器路径作为输入,返回一个包含其中包含的所有文件名的数组。通过将第二个参数设置为 'relative' 获取相对路径,或任何其他非空值以获取完整文件路径,可以选择将文件路径添加到文件名中。 + .. note:: 在 v4.4.4 之前,由于一个错误,这个函数并未跟随文件夹的符号链接。 + 示例: .. literalinclude:: filesystem_helper/010.php @@ -181,7 +183,7 @@ :returns: 符号权限字符串 :rtype: string - 获取数字权限(例如 ``fileperms()`` 返回的)并返回标准符号表示法的文件权限。 + 获取数字权限(例如 `fileperms() `_ 返回的)并返回标准符号表示法的文件权限。 .. literalinclude:: filesystem_helper/012.php @@ -191,7 +193,7 @@ :returns: 八进制权限字符串 :rtype: string - 获取数字权限(例如 ``fileperms()`` 返回的)并返回三字符八进制表示法的文件权限。 + 获取数字权限(例如 `fileperms() `_ 返回的)并返回三字符八进制表示法的文件权限。 .. literalinclude:: filesystem_helper/013.php diff --git a/source/helpers/filesystem_helper/004.php b/source/helpers/filesystem_helper/004.php index 6b1e065c..418ecd76 100644 --- a/source/helpers/filesystem_helper/004.php +++ b/source/helpers/filesystem_helper/004.php @@ -1,3 +1,3 @@ ` 将一个 URI 与一个控制器关联起来。 +一个控制器简单来说就是一个处理 HTTP 请求的类文件。:doc:`URI 路由 ` 将一个 URI 与一个控制器关联起来。控制器会返回一个视图字符串或 ``Response`` 对象。 你创建的每个控制器都应该扩展 ``BaseController`` 类。 这个类为你所有的控制器提供了几个可用的功能。 @@ -80,13 +80,37 @@ forceHTTPS 验证数据 *************** +.. _controller-validatedata: + +$this->validateData() +===================== + +.. versionadded:: 4.2.0 + +为了简化数据检查,控制器也提供了方便的方法 ``validateData()``。 + +该方法接受 (1) 一个要验证的数据数组,(2) 规则数组, +(3) 如果项目无效,显示自定义错误消息的可选数组, +(4) 使用的可选数据库组。 + +在 :doc:`验证库文档 ` 中有规则和消息数组格式的详细信息,以及可用的规则: + +.. literalinclude:: controllers/006.php + .. _controller-validate: $this->validate() ================= -为了简化数据检查,控制器还提供了方便的 ``validate()`` 方法。 -该方法在第一个参数中接受规则数组,在可选的第二个参数中,接受自定义错误消息的数组,以在项目无效时显示。在内部,这使用控制器的 ``$this->request`` 实例来获取要验证的数据。 +.. important:: 该方法仅存在于向后兼容中。在新项目中不要使用它。即使你已经在使用它,我们也建议你使用 ``validateData()`` 方法。 + +控制器也提供了便利的方法 ``validate()``。 + +.. warning:: 与其使用 ``validate()``,不如使用 ``validateData()`` 仅验证 POST 数据。 ``validate()`` 使用 ``$request->getVar()``,它返回 ``$_GET``,``$_POST`` 或 ``$_COOKIE`` 数据,按照该顺序(取决于 php.ini 的 `request-order `_)。新的值会覆写旧的值。如果它们的名字相同,POST 值可能被 Cookie 覆写。 + +该方法在第一个参数中接受规则数组,在可选的第二个参数中,接受自定义错误消息的数组,以在项目无效时显示。 + +在内部,这使用控制器的 ``$this->request`` 实例来获取要验证的数据。 :doc:`验证库文档 ` 有关于规则和消息数组格式以及可用规则的详细信息: @@ -102,19 +126,6 @@ $this->validate() .. note:: 验证也可以在模型中自动处理,但有时在控制器中更容易。由你决定在哪里。 -.. _controller-validatedata: - -$this->validateData() -===================== - -.. versionadded:: 4.2.0 - -有时你可能想检查控制器方法的参数或其他自定义数据。 -在这种情况下,你可以使用 ``$this->validateData()`` 方法。 -该方法在第一个参数中接受要验证的数据数组: - -.. literalinclude:: controllers/006.php - 保护方法 ****************** @@ -258,22 +269,22 @@ URI 的第二段通常确定控制器中的哪个方法被调用。 让我们用 ``Helloworld`` 控制器试一试。 -要指定默认控制器,请打开 **app/Config/Routes.php** 文件并设置此变量: +要指定默认控制器,请打开 **app/Config/Routing.php** 文件并设置此属性:: -.. literalinclude:: controllers/015.php + public string $defaultController = 'Helloworld'; 其中 ``Helloworld`` 是希望用作默认控制器的控制器类名称。 -在 **Routes.php** 中的“路由定义”部分向下几行,注释掉该行: +并注释掉 **app/Config/Routes.php** 文件中的这一行: .. literalinclude:: controllers/016.php + :lines: 2- 现在如果在不指定任何 URI 段的情况下浏览你的站点,你将看到 "Hello World" 消息。 .. important:: 当你使用自动路由(改进版)时,你必须删除 ``$routes->get('/', 'Home::index');`` 这一行。因为定义的路由优先于自动路由,并且出于安全考虑,自动路由(改进版)拒绝定义路由中的控制器访问。 -有关更多信息,请参阅 :ref:`routes-configuration-options` 部分 -:ref:`URI 路由 ` 文档。 +有关更多信息,请参阅 :ref:`routing-auto-routing-improved-configuration-options` 文档。 .. _controller-default-method-fallback: @@ -334,7 +345,7 @@ URI 的第二段通常确定控制器中的哪个方法被调用。 .. note:: 你不能在 **app/Controllers** 和 **public** 中有相同名称的目录。 这是因为如果存在目录,web 服务器将搜索它,而不会路由到 CodeIgniter。 -你的每个子目录都可以包含一个默认控制器,如果 URL 只包含 *子目录*,则会调用该控制器。只需把一个控制器放在那里,使其与 **app/Config/Routes.php** 文件中指定的默认控制器名称匹配即可。 +你的每个子目录都可以包含一个默认控制器,如果 URL 只包含 *子目录*,则会调用该控制器。只需把一个控制器放在那里,使其与 **app/Config/Routing.php** 文件中指定的默认控制器名称匹配即可。 CodeIgniter 还允许你使用其 :ref:`定义的路由 ` 映射 URI。 @@ -343,6 +354,8 @@ CodeIgniter 还允许你使用其 :ref:`定义的路由 ` 自动路由(传统) ********************* +.. important:: 该功能仅存在于向后兼容中。在新项目中不要使用它。即使你已经在使用它,我们也建议你使用 :ref:`auto-routing-improved` 替代。 + 本节描述自动路由(传统)的功能,这是 CodeIgniter 3 的路由系统。 它会自动路由 HTTP 请求,并执行相应的控制器方法, 而无需路由定义。自动路由默认被禁用。 @@ -449,22 +462,22 @@ BaseController 为加载组件和执行所有控制器需要的函数提供了 让我们以 ``Helloworld`` 控制器为例。 -要指定默认控制器,打开配置文件 **app/Config/Routes.php**,设置如下变量: +要指定默认控制器,打开配置文件 **app/Config/Routing.php**,设置如下属性:: -.. literalinclude:: controllers/015.php + public string $defaultController = 'Helloworld'; 其中 ``Helloworld`` 是希望用作默认控制器的控制器类名称。 -在 **Routes.php** 的“路由定义”部分,注释掉如下行: +并注释掉 **app/Config/Routes.php** 文件中的这一行: .. literalinclude:: controllers/016.php + :lines: 2- 现在如果在不指定任何 URI 段的情况下浏览你的站点,你将看到 "Hello World" 消息。 .. note:: ``$routes->get('/', 'Home::index');`` 这一行是优化,在“真实的”应用中会使用。但是为了演示的目的,我们不想使用这个功能。``$routes->get()`` 在 :doc:`URI 路由 ` 中有解释。 -有关更多信息,请参阅 :ref:`routes-configuration-options` 部分 -:ref:`URI 路由 ` 文档。 +有关更多信息,请参阅 :ref:`routing-auto-routing-legacy-configuration-options` 文档。 将控制器组织到子目录中(传统) ========================================================== @@ -485,7 +498,7 @@ BaseController 为加载组件和执行所有控制器需要的函数提供了 .. note:: 在 **app/Controllers** 和 **public** 中不能有同名的目录。这是因为如果目录存在,web 服务器会进行查找,路由不会转到 CodeIgniter。 -每个子目录下可以包含一个默认控制器,如果 URL 仅包含 **子目录**,则会调用该默认控制器。只需在相应位置放置一个控制器,使其与 **app/Config/Routes.php** 文件中指定的默认控制器名称匹配即可。 +每个子目录下可以包含一个默认控制器,如果 URL 仅包含 **子目录**,则会调用该默认控制器。只需在相应位置放置一个控制器,使其与 **app/Config/Routing.php** 文件中指定的默认控制器名称匹配即可。 CodeIgniter 也支持通过 :ref:`定义路由 ` 来映射 URI。 diff --git a/source/incoming/controllers/006.php b/source/incoming/controllers/006.php index 555a8c98..8bf70454 100644 --- a/source/incoming/controllers/006.php +++ b/source/incoming/controllers/006.php @@ -8,7 +8,7 @@ public function product(int $id) { $data = [ 'id' => $id, - 'name' => $this->request->getVar('name'), + 'name' => $this->request->getPost('name'), ]; $rule = [ diff --git a/source/incoming/filters.rst b/source/incoming/filters.rst index fb6225be..59677a20 100644 --- a/source/incoming/filters.rst +++ b/source/incoming/filters.rst @@ -92,18 +92,26 @@ $aliases $globals ======== -第二部分允许你定义任何应用于框架的每个请求的过滤器。在这里使用太多可能会对性能产生影响,所以要小心。可以通过将别名添加到 before 或 after 数组来指定过滤器: +第二部分允许你定义任何应用于框架的每个有效请求的过滤器。 + +在这里使用太多可能会对性能产生影响,所以要小心。 + +可以通过将别名添加到 ``before`` 或 ``after`` 数组来指定过滤器: .. literalinclude:: filters/005.php 除了少数 URI --------------------- -有时你希望将过滤器应用于几乎所有请求,但有一些应该不受影响。一个常见的示例是,如果你需要从 CSRF 保护过滤器中排除几个 URI,以允许第三方网站的请求访问一个或两个特定的 URI,同时保持其余 URI 受保护。要做到这一点,请在别名旁边添加一个包含 ``except`` 键和要匹配的 URI 路径(相对于 BaseURL)值的数组: +有时你希望将过滤器应用于几乎所有请求,但有一些应该不受影响。一个常见的示例是,如果你需要从 CSRF 保护过滤器中排除几个 URI,以允许第三方网站的请求访问一个或两个特定的 URI,同时保持其余 URI 受保护。 + +要做到这一点,请在别名旁边添加一个包含 ``except`` 键和要匹配的 URI 路径(相对于 BaseURL)值的数组: .. literalinclude:: filters/006.php -在过滤器设置中可以使用 URI 路径(相对于 BaseURL)的任何位置,你都可以使用正则表达式,或者像在这个例子中使用星号 (``*``) 作为通配符,匹配之后的所有字符。在这个例子中,任何以 ``api/`` 开头的 URI 路径都将被免于 CSRF 保护,但网站的表单将全部受保护。如果你需要指定多个 URI,可以使用 URI 路径模式数组: +在过滤器设置中可以使用 URI 路径(相对于 BaseURL)的任何位置,你都可以使用正则表达式,或者像在这个例子中使用星号 (``*``) 作为通配符,匹配之后的所有字符。在这个例子中,任何以 ``api/`` 开头的 URI 路径都将被免于 CSRF 保护,但网站的表单将全部受保护。 + +如果你需要指定多个 URI,可以使用 URI 路径模式数组: .. literalinclude:: filters/007.php @@ -153,7 +161,7 @@ filter:check .. versionadded:: 4.3.0 -使用 **GET** 方法检查路由 ``/`` 的过滤器: +例如,使用 **GET** 方法检查路由 ``/`` 的过滤器: .. code-block:: console @@ -169,8 +177,9 @@ filter:check | GET | / | | toolbar | +--------+-------+----------------+---------------+ -你还可以通过 ``spark routes`` 命令查看路由和过滤器。 -参见 :ref:`URI 路由 `。 +你还可以通过 ``spark routes`` 命令查看路由和过滤器, +但是当你在路由中使用正则表达式时,它可能无法显示准确的过滤器。 +具体详情请查看 :ref:`URI 路由 `。 **************** 提供的过滤器 diff --git a/source/incoming/filters/004.php b/source/incoming/filters/004.php index 304bd21b..fbaa8358 100644 --- a/source/incoming/filters/004.php +++ b/source/incoming/filters/004.php @@ -7,7 +7,7 @@ class Filters extends BaseConfig { public array $aliases = [ - 'apiPrep' => [ + 'api-prep' => [ \App\Filters\Negotiate::class, \App\Filters\ApiAuth::class, ], diff --git a/source/incoming/filters/008.php b/source/incoming/filters/008.php index 945e9498..8d417fd4 100644 --- a/source/incoming/filters/008.php +++ b/source/incoming/filters/008.php @@ -9,7 +9,7 @@ class Filters extends BaseConfig // ... public array $methods = [ - 'post' => ['InvalidChars', 'csrf'], + 'post' => ['invalidchars', 'csrf'], 'get' => ['csrf'], ]; diff --git a/source/incoming/incomingrequest.rst b/source/incoming/incomingrequest.rst index 7f113d71..bdaabe07 100755 --- a/source/incoming/incomingrequest.rst +++ b/source/incoming/incomingrequest.rst @@ -64,7 +64,7 @@ getMethod() 检索输入 ****************** -你可以通过 Request 对象检索来自 ``$_SERVER``、``$_GET``、``$_POST`` 和 ``$_ENV`` 的输入。 +你可以通过 Request 对象检索来自 ``$_GET``、``$_POST``、``$_COOKIE``、``$_SERVER`` 和 ``$_ENV`` 的输入。 数据不会自动过滤,并以请求中传递的原始输入数据形式返回。 .. note:: 使用全局变量是不好的做法。基本上,应该避免使用它,建议使用 Request 对象的方法。 @@ -82,23 +82,67 @@ getMethod() 获取数据 ============ -``getVar()`` 方法将从 ``$_REQUEST`` 中获取数据,因此将返回 ``$_GET``、``$_POST`` 或 ``$_COOKIE`` 中的任何数据(取决于 php.ini `request-order `_ )。 +getGet() +-------- -.. note:: 如果传入请求的 ``Content-Type`` 标头设置为 ``application/json``, - ``getVar()`` 方法会返回 JSON 数据,而不是 ``$_REQUEST`` 数据。 - -虽然这很方便,但你通常需要使用更具体的方法,如: +``getGet()`` 方法将从 ``$_GET`` 中获取。 * ``$request->getGet()`` + +getPost() +--------- + +``getPost()`` 方法将从 ``$_POST`` 中获取。 + * ``$request->getPost()`` + +getCookie() +----------- + +``getCookie()`` 方法将从 ``$_COOKIE`` 中获取。 + * ``$request->getCookie()`` + +getServer() +----------- + +``getServer()`` 方法将从 ``$_SERVER`` 中获取。 + * ``$request->getServer()`` + +getEnv() +-------- + +.. deprecated:: 4.4.4 该方法从一开始就不起作用。请使用 :php:func:`env()` 替代。 + +``getEnv()`` 方法将从 ``$_ENV`` 中获取。 + * ``$request->getEnv()`` -另外,还有一些实用程序方法可以从 ``$_GET`` 或 ``$_POST`` 中检索信息,同时保持控制查找顺序的能力: +getPostGet() +------------ + +此外,还有一些用于从 ``$_GET`` 或 ``$_POST`` 中检索信息的实用方法,同时保持控制查找的顺序: + +* ``$request->getPostGet()`` - 先检查 ``$_POST``,然后是 ``$_GET`` + +getGetPost() +------------ + +* ``$request->getGetPost()`` - 先检查 ``$_GET``,然后是 ``$_POST`` + +getVar() +-------- -* ``$request->getPostGet()`` - 首先检查 ``$_POST``,然后检查 ``$_GET`` -* ``$request->getGetPost()`` - 首先检查 ``$_GET``,然后检查 ``$_POST`` +.. important:: 该方法仅存在于向后兼容中。不要在新项目中使用它。即使你已经在使用,我们也建议你使用另一个更合适的方法。 + +``getVar()`` 方法将从 ``$_REQUEST`` 中获取,因此会返回任何来自 ``$_GET``, ``$POST``, 或者 ``$_COOKIE`` 的数据(取决于 php.ini `request-order `_)。 + +.. warning:: 如果你只想验证 POST 数据,不要使用 ``getVar()``。 + 新值会覆盖旧值。如果 POST 值和 Cookie 有相同的名字,并且你在 `request-order `_ 中先设置 "P" 之后设置 "C",则 POST 的值可能会被 Cookie 覆盖。 + +.. note:: 如果传入的请求的 ``Content-Type`` 头设置为 ``application/json``, + 则 ``getVar()`` 方法会返回 JSON 数据,而不是 ``$_REQUEST`` 数据。 .. _incomingrequest-getting-json-data: @@ -113,18 +157,16 @@ getMethod() 默认情况下,这将返回 JSON 数据中的任何对象作为对象。如果你想要将其转换为关联数组,请在第一个参数中传递 ``true``。 -第二和第三个参数与 `json_decode `_ PHP 函数的 ``depth`` 和 ``options`` 参数对应。 - -如果传入请求的 ``Content-Type`` 标头设置为 ``application/json``,你也可以使用 ``getVar()`` 来获取 JSON 流。以这种方式使用 ``getVar()`` 将始终返回一个对象。 +第二和第三个参数与 `json_decode() `_ PHP 函数的 ``$depth`` 和 ``$flags`` 参数对应。 从 JSON 获取特定数据 =============================== -你可以通过向 ``getVar()`` 传入变量名来从 JSON 流中获取特定的数据片段,用于获取所需的数据,或者可以使用“点”表示法深入到 JSON 中,以获取不在根级别的数据。 +你可以通过向 ``getJsonVar()`` 传入变量名来从 JSON 流中获取特定的数据片段,用于获取所需的数据,或者可以使用“点”表示法深入到 JSON 中,以获取不在根级别的数据。 .. literalinclude:: incomingrequest/010.php -如果要结果是一个关联数组而不是对象,可以使用 ``getJsonVar()`` ,并在第二个参数中传递 true。如果你无法保证传入请求具有正确的 ``Content-Type`` 标头,也可以使用此函数。 +如果你希望结果是关联数组而不是对象,你可以在第二个参数中传入 true: .. literalinclude:: incomingrequest/011.php @@ -279,47 +321,47 @@ getMethod() :param string $index: 要查找的变量/键的名称。 :param int $filter: 要应用的过滤器类型。过滤器列表可在 - `这里 `__ 找到。 + `过滤器类型 `__ 中找到。 :param int $flags: 要应用的标志。标志列表可在 - `这里 `__ 找到。 + `过滤器 flag `__ 中找到。 :returns: 如果没有提供参数,则返回 ``$_REQUEST``,否则如果找到则返回 REQUEST 值,如果没找到则为 null :rtype: array|bool|float|int|object|string|null - 第一个参数将包含要查找的 REQUEST 项的名称: + .. important:: 该方法仅存在于向后兼容中。不要在新项目中使用它。即使你已经在使用,我们也建议你使用另一个更合适的方法。 - .. literalinclude:: incomingrequest/027.php + 这个方法与 ``getGet()`` 相同,只是它获取的是 REQUEST 数据。 - 如果尝试检索的项目不存在,该方法将返回 null。 + .. php:method:: getGet([$index = null[, $filter = null[, $flags = null]]]) - 第二个可选参数允许你通过 PHP 的过滤器运行数据。将所需的过滤器类型作为第二个参数传递: + :param string $index: 要查找的变量/键的名称。 + :param int $filter: 要应用的过滤类型。过滤器类型列表可以在 `过滤器类型 `__ 中找到。 + :param int $flags: 要应用的标记。标记列表可以在 `过滤器 flag `__ 中找到。 + :returns: 如果没有提供参数,则为 ``$_GET``,否则如果找到 GET 值则为 GET 值,如果未找到则为 null + :rtype: array|bool|float|int|object|string|null - .. literalinclude:: incomingrequest/028.php + 第一个参数将包含你正在查找的 GET 项的名称: - 若要返回所有 POST 项,请不带任何参数调用。 + .. literalinclude:: incomingrequest/041.php - 要返回所有 POST 项并通过过滤器传递它们,请将第一个参数设置为 null,同时将第二个参数设置为要使用的过滤器: + 如果尝试检索的项目不存在,该方法将返回 null。 - .. literalinclude:: incomingrequest/029.php + 第二个可选参数允许你通过 PHP 的过滤器运行数据。将所需的过滤器类型作为第二个参数传递: - 要返回多个 POST 参数的数组,请传递所有所需键的数组: + .. literalinclude:: incomingrequest/042.php - .. literalinclude:: incomingrequest/030.php + 若要返回所有 GET 项,请不带任何参数调用。 - 这里也应用了相同的规则,要使用过滤检索参数,请将第二个参数设置为要应用的过滤器类型: + 要返回所有 GET 项并通过过滤器传递它们,请将第一个参数设置为 null,同时将第二个参数设置为要使用的过滤器: - .. literalinclude:: incomingrequest/031.php + .. literalinclude:: incomingrequest/043.php - .. php:method:: getGet([$index = null[, $filter = null[, $flags = null]]]) + 要返回多个 GET 参数的数组,请传递所有所需键的数组: - :param string $index: 要查找的变量/键的名称。 - :param int $filter: 要应用的过滤器类型。过滤器列表可在 - `这里 `__ 找到。 - :param int $flags: 要应用的标志。标志列表可在 - `这里 `__ 找到。 - :returns: 如果没有提供参数,则返回 ``$_GET``,否则如果找到则返回 GET 值,如果没找到则为 null - :rtype: array|bool|float|int|object|string|null + .. literalinclude:: incomingrequest/044.php + + 这里也应用了相同的规则,要使用过滤检索参数,请将第二个参数设置为要应用的过滤器类型: - 此方法与 ``getVar()`` 相同,只是它获取 GET 数据。 + .. literalinclude:: incomingrequest/045.php .. php:method:: getPost([$index = null[, $filter = null[, $flags = null]]]) @@ -331,15 +373,15 @@ getMethod() :returns: 如果没有提供参数,则返回 ``$_POST``,否则如果找到则返回 POST 值,如果没找到则为 null :rtype: array|bool|float|int|object|string|null - 此方法与 ``getVar()`` 相同,只是它获取 POST 数据。 + 此方法与 ``getGet()`` 相同,只是它获取 POST 数据。 .. php:method:: getPostGet([$index = null[, $filter = null[, $flags = null]]]) :param string $index: 要查找的变量/键的名称。 :param int $filter: 要应用的过滤器类型。过滤器列表可在 - `这里 `__ 找到。 + `过滤器类型 `__ 中找到。 :param int $flags: 要应用的标志。标志列表可在 - `这里 `__ 找到。 + `过滤器 flag `__ 中找到。 :returns: 如果没有指定参数,则返回 ``$_POST`` 和 ``$_GET`` 组合(冲突时优先 POST 值), 否则首先查找 POST 值,找不到则查找 GET 值,如果没找到则返回 null :rtype: array|bool|float|int|object|string|null @@ -356,9 +398,9 @@ getMethod() :param string $index: 要查找的变量/键的名称。 :param int $filter: 要应用的过滤器类型。过滤器列表可在 - `这里 `__ 找到。 + `过滤器类型 `__ 中找到。 :param int $flags: 要应用的标志。标志列表可在 - `这里 `__ 找到。 + `过滤器 flag `__ 中找到。 :returns: 如果没有指定参数,则返回 ``$_GET`` 和 ``$_POST`` 组合(冲突时优先 GET 值), 否则首先查找 GET 值,找不到则查找 POST 值,如果没找到则返回 null :rtype: array|bool|float|int|object|string|null @@ -375,9 +417,9 @@ getMethod() :param array|string|null $index: COOKIE 名称 :param int $filter: 要应用的过滤器类型。过滤器列表可在 - `这里 `__ 找到。 + `过滤器类型 `__ 中找到。 :param int $flags: 要应用的标志。标志列表可在 - `这里 `__ 找到。 + `过滤器 flag `__ 中找到。 :returns: 如果没有提供参数,则返回 ``$_COOKIE``,否则如果找到则返回 COOKIE 值,如果没有找到则为 null :rtype: array|bool|float|int|object|string|null @@ -395,13 +437,13 @@ getMethod() :param array|string|null $index: 值名称 :param int $filter: 要应用的过滤器类型。过滤器列表可在 - `这里 `__ 找到。 + `过滤器类型 `__ 中找到。 :param int $flags: 要应用的标志。标志列表可在 - `这里 `__ 找到。 + `过滤器 flag `__ 中找到。 :returns: 如果找到则返回 ``$_SERVER`` 项的值,否则为 null :rtype: array|bool|float|int|object|string|null - 此方法与 ``getPost()``、``getGet()`` 和 ``getCookie()`` 方法相同,只是它获取 getServer 数据(``$_SERVER``): + 此方法与 ``getPost()``、``getGet()`` 和 ``getCookie()`` 方法相同,只是它获取 Server 数据(``$_SERVER``): .. literalinclude:: incomingrequest/036.php @@ -409,14 +451,12 @@ getMethod() .. literalinclude:: incomingrequest/037.php - .. php:method:: getUserAgent([$filter = null]) + .. php:method:: getUserAgent() - :param int $filter: 要应用的过滤器类型。过滤器列表可在 - `这里 `__ 找到。 :returns: 在 SERVER 数据中找到的用户代理字符串,如果没有找到则为 null。 :rtype: CodeIgniter\\HTTP\\UserAgent - 此方法返回来自 SERVER 数据的用户代理字符串: + 此方法返回来自 SERVER 数据的用户代理实例: .. literalinclude:: incomingrequest/038.php diff --git a/source/incoming/incomingrequest/003.php b/source/incoming/incomingrequest/003.php index 5668f74a..78378b43 100644 --- a/source/incoming/incomingrequest/003.php +++ b/source/incoming/incomingrequest/003.php @@ -1,5 +1,7 @@ getVar('foo'); +$something = $request->getPost('foo'); diff --git a/source/incoming/incomingrequest/010.php b/source/incoming/incomingrequest/010.php index a199c91b..cf9824d8 100644 --- a/source/incoming/incomingrequest/010.php +++ b/source/incoming/incomingrequest/010.php @@ -10,8 +10,8 @@ * } */ -$data = $request->getVar('foo'); +$data = $request->getJsonVar('foo'); // $data = "bar" -$data = $request->getVar('fizz.buzz'); +$data = $request->getJsonVar('fizz.buzz'); // $data = "baz" diff --git a/source/incoming/incomingrequest/014.php b/source/incoming/incomingrequest/014.php index 564fc543..8455402e 100644 --- a/source/incoming/incomingrequest/014.php +++ b/source/incoming/incomingrequest/014.php @@ -1,3 +1,3 @@ getVar('email', FILTER_SANITIZE_EMAIL); +$email = $request->getPost('email', FILTER_SANITIZE_EMAIL); diff --git a/source/incoming/incomingrequest/041.php b/source/incoming/incomingrequest/041.php new file mode 100644 index 00000000..b05ea233 --- /dev/null +++ b/source/incoming/incomingrequest/041.php @@ -0,0 +1,3 @@ +getGet('some_data'); diff --git a/source/incoming/incomingrequest/042.php b/source/incoming/incomingrequest/042.php new file mode 100644 index 00000000..567b2645 --- /dev/null +++ b/source/incoming/incomingrequest/042.php @@ -0,0 +1,3 @@ +getGet('some_data', FILTER_SANITIZE_FULL_SPECIAL_CHARS); diff --git a/source/incoming/incomingrequest/043.php b/source/incoming/incomingrequest/043.php new file mode 100644 index 00000000..4bd67d42 --- /dev/null +++ b/source/incoming/incomingrequest/043.php @@ -0,0 +1,4 @@ +getGet(null, FILTER_SANITIZE_FULL_SPECIAL_CHARS); +// returns all GET items with string sanitation diff --git a/source/incoming/incomingrequest/044.php b/source/incoming/incomingrequest/044.php new file mode 100644 index 00000000..3e482e7a --- /dev/null +++ b/source/incoming/incomingrequest/044.php @@ -0,0 +1,3 @@ +getGet(['field1', 'field2']); diff --git a/source/incoming/incomingrequest/045.php b/source/incoming/incomingrequest/045.php new file mode 100644 index 00000000..b0b0de81 --- /dev/null +++ b/source/incoming/incomingrequest/045.php @@ -0,0 +1,3 @@ +getGet(['field1', 'field2'], FILTER_SANITIZE_FULL_SPECIAL_CHARS); diff --git a/source/incoming/request.rst b/source/incoming/request.rst index e6ec1e8f..772b07c3 100755 --- a/source/incoming/request.rst +++ b/source/incoming/request.rst @@ -94,6 +94,8 @@ .. php:method:: getEnv([$index = null[, $filter = null[, $flags = null]]]) + .. deprecated:: 4.4.4 从一开始,这个方法就不起作用。请改用 :php:func:`env()`。 + :param mixed $index: 值名称 :param int $filter: 要应用的过滤类型。过滤器列表可在 `PHP 手册 `__ 中找到。 :param int|array $flags: 要应用的标志。标志列表可在 `PHP 手册 `__ 中找到。 diff --git a/source/incoming/restful.rst b/source/incoming/restful.rst index d84ae138..d6a6fb32 100644 --- a/source/incoming/restful.rst +++ b/source/incoming/restful.rst @@ -35,6 +35,12 @@ CodeIgniter 通过其资源路由和 `ResourceController` 可以轻松创建资 .. literalinclude:: restful/003.php +.. literalinclude:: restful/017.php + +.. literalinclude:: restful/018.php + +也可参考 :ref:`controllers-namespace`。 + 更改使用的占位符 =========================== @@ -98,6 +104,12 @@ Presenter 路由 .. literalinclude:: restful/011.php +.. literalinclude:: restful/019.php + +.. literalinclude:: restful/020.php + +也可参考 :ref:`controllers-namespace`。 + 更改使用的占位符 =========================== diff --git a/source/incoming/restful/003.php b/source/incoming/restful/003.php index 6610555f..6f255282 100644 --- a/source/incoming/restful/003.php +++ b/source/incoming/restful/003.php @@ -1,6 +1,5 @@ resource('photos', ['controller' => 'App\Gallery']); - +$routes->resource('photos', ['controller' => 'Gallery']); // Would create routes like: -$routes->get('photos', 'App\Gallery::index'); +$routes->get('photos', '\App\Controllers\Gallery::index'); diff --git a/source/incoming/restful/011.php b/source/incoming/restful/011.php index cbb6f13b..e08078d4 100644 --- a/source/incoming/restful/011.php +++ b/source/incoming/restful/011.php @@ -1,6 +1,5 @@ presenter('photos', ['controller' => 'App\Gallery']); - +$routes->presenter('photos', ['controller' => 'Gallery']); // Would create routes like: -$routes->get('photos', 'App\Gallery::index'); +$routes->get('photos', '\App\Controllers\Gallery::index'); diff --git a/source/incoming/restful/017.php b/source/incoming/restful/017.php new file mode 100644 index 00000000..853748f8 --- /dev/null +++ b/source/incoming/restful/017.php @@ -0,0 +1,5 @@ +resource('photos', ['controller' => '\App\Gallery']); +// Would create routes like: +$routes->get('photos', '\App\Gallery::index'); diff --git a/source/incoming/restful/018.php b/source/incoming/restful/018.php new file mode 100644 index 00000000..6951d07b --- /dev/null +++ b/source/incoming/restful/018.php @@ -0,0 +1,7 @@ +resource('photos', ['namespace' => '', 'controller' => Gallery::class]); +// Would create routes like: +$routes->get('photos', '\App\Controllers\Gallery::index'); diff --git a/source/incoming/restful/019.php b/source/incoming/restful/019.php new file mode 100644 index 00000000..09885e7f --- /dev/null +++ b/source/incoming/restful/019.php @@ -0,0 +1,5 @@ +presenter('photos', ['controller' => '\App\Gallery']); +// Would create routes like: +$routes->get('photos', '\App\Gallery::index'); diff --git a/source/incoming/restful/020.php b/source/incoming/restful/020.php new file mode 100644 index 00000000..ddd67cc2 --- /dev/null +++ b/source/incoming/restful/020.php @@ -0,0 +1,7 @@ +presenter('photos', ['namespace' => '', 'controller' => Gallery::class]); +// Would create routes like: +$routes->get('photos', '\App\Controllers\Gallery::index'); diff --git a/source/incoming/routing.rst b/source/incoming/routing.rst index 6359cb12..e9632882 100755 --- a/source/incoming/routing.rst +++ b/source/incoming/routing.rst @@ -66,6 +66,8 @@ ID 将被设置为 ``34``: .. literalinclude:: routing/009.php +.. _routing-http-verb-routes: + HTTP 动词路由 ================ @@ -80,6 +82,8 @@ HTTP 动词路由 指定路由处理程序 ========================= +.. _controllers-namespace: + 控制器的命名空间 ---------------------- @@ -176,15 +180,29 @@ HTTP 动词路由 .. note:: ``{locale}`` 不能作为占位符或路由的其他部分使用,因为它保留用于 :doc:`localization `。 -请注意,单个 ``(:any)`` 将在 URL 中匹配多个段(如果存在)。例如,路由: +.. _routing-placeholder-any: + +(:any) 的行为 +^^^^^^^^^^^^^^^^^^^^^^ + +请注意,单个 ``(:any)`` 将在 URL 中匹配多个段(如果存在)。 + +例如,路由: .. literalinclude:: routing/010.php 将匹配 **product/123**、**product/123/456**、**product/123/456/789** 等等。 + +在上面的例子中,如果 ``$1`` 占位符包含一个斜杠(``/``),当传递给 ``Catalog::productLookup()`` 时,它仍然会被分割成多个参数。 + 控制器中的实现应考虑最大参数: .. literalinclude:: routing/011.php +或者你可以使用 `变长参数列表 `_: + +.. literalinclude:: routing/068.php + .. important:: 不要在 ``(:any)`` 后面放任何占位符。因为传递给控制器方法的参数数量可能会改变。 如果匹配多个段不是预期的行为,那么在定义路由时应该使用 ``(:segment)``。对于上面的示例 URL: @@ -222,6 +240,8 @@ HTTP 动词路由 .. literalinclude:: routing/019.php +在上面的例子中,如果 ``$1`` 占位符包含一个斜杠(``/``),当传递给 ``Auth::login()`` 时,它仍然会被分割成多个参数。 + 对于那些不了解正则表达式并希望学习更多知识的人,`regular-expressions.info `_ 可能是一个不错的起点。 .. note:: 你也可以将占位符与正则表达式结合使用。 @@ -268,6 +288,8 @@ HTTP 动词路由 任意 HTTP 动词的路由 ========================== +.. important:: 这个方法只为了向后兼容而存在。在新项目中不要使用它。即使你已经在使用它,我们也建议你使用另一种更适合的方法。 + .. warning:: 尽管 ``add()`` 方法看起来很方便,但建议始终使用基于 HTTP 动词的路由,如上所述,因为它更安全。 如果你使用 :doc:`CSRF 保护 `,它不会保护 **GET** 请求。 如果 ``add()`` 方法中指定的 URI 可以通过 GET 方法访问,CSRF 保护将不起作用。 @@ -282,6 +304,8 @@ HTTP 动词路由 映射多个路由 ======================= +.. important:: 这个方法只为了向后兼容而存在。在新项目中不要使用它。即使你已经在使用它,我们也建议你使用另一种更适合的方法。 + .. warning:: 不推荐使用 ``map()`` 方法,就像 ``add()`` 一样,因为它在内部调用 ``add()``。 尽管 ``add()`` 方法使用简单,但通常更方便的是同时使用多个路由,使用 ``map()`` 方法。 @@ -618,7 +642,7 @@ RoutesCollection 类提供了几个选项,可以影响所有路由,并且可 启用自动路由 =================== -要使用它,你需要在 **app/Config/Routing.php** 中将 ``$autoRoute`` 选项设置为 true:: +要使用它,你需要在 **app/Config/Routing.php** 中将 ``$autoRoute`` 选项设置为 ``true``:: public bool $autoRoute = true; @@ -653,7 +677,7 @@ URI 段 配置选项 ===================== -这些选项在 **app/Config/Routes.php** 的顶部可用。 +这些选项在 **app/Config/Routing.php** 文件中可用。 默认控制器 ------------------ @@ -661,11 +685,11 @@ URI 段 针对网站根 URI ^^^^^^^^^^^^^^^^^ -当用户访问你站点的根目录(即 **example.com**)时,除非为它明确定义了路由,否则使用的控制器由 ``setDefaultController()`` 方法设置的值确定。 +当用户访问你站点的根目录(即 **example.com**)时,除非为它明确定义了路由,否则使用的控制器由 ``$defaultController`` 属性设置的值确定。 -默认值为 ``Home``,它与 **app/Controllers/Home.php** 中的控制器匹配: +默认值为 ``Home``,它与 **app/Controllers/Home.php** 中的控制器匹配:: -.. literalinclude:: routing/047.php + public string $defaultController = 'Home'; 针对目录 URI ^^^^^^^^^^^^^^^^^ @@ -679,9 +703,9 @@ URI 段 这与默认控制器设置类似,但用于在找到与 URI 匹配的控制器但不存在方法段时确定使用的默认方法。默认值为 ``index``。 -在此示例中,如果用户访问 **example.com/products**,且存在 ``Products`` 控制器,将执行 ``Products::listAll()`` 方法: +在此示例中,如果用户访问 **example.com/products**,且存在 ``Products`` 控制器,将执行 ``Products::getListAll()`` 方法:: -.. literalinclude:: routing/048.php + public string $defaultMethod = 'listAll'; .. important:: 你无法使用控制器的默认方法名称访问控制器。在上面的示例中,你可以访问 **example.com/products**,但是如果访问 **example.com/products/listall** 将找不到。 @@ -709,6 +733,8 @@ URI 段 自动路由(传统) ********************* +.. important:: 这个功能只为了向后兼容而存在。在新项目中不要使用它。即使你已经在使用它,我们也推荐你使用 :ref:`auto-routing-improved` 替代。 + 自动路由(传统)是来自 CodeIgniter 3 的路由系统。它可以根据约定自动路由 HTTP 请求,并执行相应的控制器方法。 推荐在 **app/Config/Routes.php** 文件中定义所有路由,或者使用 :ref:`auto-routing-improved`。 @@ -722,9 +748,13 @@ URI 段 自 v4.2.0 起,默认禁用自动路由。 -要使用它,你需要在 **app/Config/Routing.php** 中将 ``$autoRoute`` 选项设置为 true:: +要使用它,你需要在 **app/Config/Routing.php** 中将 ``$autoRoute`` 选项设置为 ``true``:: - $routes->setAutoRoute(true); + public bool $autoRoute = true; + +并且在 **app/Config/Feature.php** 中,将属性 ``$autoRoutesImproved`` 设置为 ``false``:: + + public bool $autoRoutesImproved = false; URI 段(传统) ===================== @@ -738,7 +768,7 @@ URI 段(传统) 配置选项(传统) ============================== -这些选项在 **app/Config/Routes.php** 的顶部可用。 +这些选项在 **app/Config/Routing.php** 文件中可用。 默认控制器(传统) --------------------------- @@ -746,9 +776,11 @@ URI 段(传统) 针对网站根 URI(传统) ^^^^^^^^^^^^^^^^^^^^^^^^^^ -当用户访问你站点的根目录(即 example.com)时,除非为它明确定义了路由,否则使用的控制器由 ``setDefaultController()`` 方法设置的值确定。默认值为 ``Home``,它与 **app/Controllers/Home.php** 中的控制器匹配: +当用户访问你网站的根(例如,**example.com**)时,除非存在明确的路由,否则将根据 ``$defaultController`` 属性设定的值来确定要使用的控制器。 -.. literalinclude:: routing/047.php +对于这个属性,默认值是 ``Home``,它匹配在 **app/Controllers/Home.php** 的控制器:: + + public string $defaultController = 'Home'; 针对目录 URI(传统) ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -764,7 +796,7 @@ URI 段(传统) 在此示例中,如果用户访问 **example.com/products**,且存在 ``Products`` 控制器,将执行 ``Products::listAll()`` 方法: -.. literalinclude:: routing/048.php + public string $defaultMethod = 'listAll'; 确认路由 ***************** @@ -799,7 +831,9 @@ spark 路由 自 v4.3.0 起, *Name* 列显示路由名称。``»`` 表示名称与路由路径相同。 -.. important:: 该系统并非完美。如果使用自定义占位符, *Filters* 可能不正确。如果要检查路由的过滤器,可以使用 :ref:`spark filter:check ` 命令。 +.. important:: 系统并非完美。对于包含如 ``([^/]+)`` 或 ``{locale}`` 的正则表达式模式的路由,显示的 *Filters* 可能不正确(如果你在 **app/Config/Filters.php** 中为过滤器设置了复杂的 URI 模式),或者它显示为 ````。 + + :ref:`spark filter:check ` 命令可以用来检查 100% 准确的过滤器。 自动路由(改进版) ----------------------- diff --git a/source/incoming/routing/011.php b/source/incoming/routing/011.php index 2b5433da..1aa779af 100644 --- a/source/incoming/routing/011.php +++ b/source/incoming/routing/011.php @@ -2,7 +2,7 @@ namespace App\Controllers; -class ProductController extends BaseController +class Catalog extends BaseController { public function productLookup($seg1 = false, $seg2 = false, $seg3 = false) { diff --git a/source/incoming/routing/020.php b/source/incoming/routing/020.php index e4fc5bde..450d3dd4 100644 --- a/source/incoming/routing/020.php +++ b/source/incoming/routing/020.php @@ -1,5 +1,7 @@ get('feed', static function () { $rss = new RSSFeeder(); diff --git a/source/incoming/routing/045.php b/source/incoming/routing/045.php index e9534931..d3d5e66d 100644 --- a/source/incoming/routing/045.php +++ b/source/incoming/routing/045.php @@ -1,6 +1,9 @@ get('users', 'Users::index'); diff --git a/source/incoming/routing/046.php b/source/incoming/routing/046.php index 8998fe70..78768c76 100644 --- a/source/incoming/routing/046.php +++ b/source/incoming/routing/046.php @@ -1,6 +1,6 @@ setDefaultNamespace('App'); // Controller is \App\Users diff --git a/source/incoming/routing/049.php b/source/incoming/routing/049.php index 9f53bee8..303fff95 100644 --- a/source/incoming/routing/049.php +++ b/source/incoming/routing/049.php @@ -1,6 +1,9 @@ setTranslateURIDashes(true); diff --git a/source/incoming/routing/050.php b/source/incoming/routing/050.php index 6f5446f5..f11fabf8 100644 --- a/source/incoming/routing/050.php +++ b/source/incoming/routing/050.php @@ -1,6 +1,9 @@ setAutoRoute(false); diff --git a/source/incoming/routing/051.php b/source/incoming/routing/051.php index bced5d53..8c598c40 100644 --- a/source/incoming/routing/051.php +++ b/source/incoming/routing/051.php @@ -1,13 +1,17 @@ set404Override('App\Errors::show404'); diff --git a/source/incoming/routing/052.php b/source/incoming/routing/052.php index e3dd9f60..cf40693c 100644 --- a/source/incoming/routing/052.php +++ b/source/incoming/routing/052.php @@ -1,5 +1,17 @@ setPrioritize(); diff --git a/source/incoming/routing/068.php b/source/incoming/routing/068.php new file mode 100644 index 00000000..50b0891c --- /dev/null +++ b/source/incoming/routing/068.php @@ -0,0 +1,13 @@ +` +- :ref:`Composer 安装在已有项目中添加 CodeIgniter4 升级 ` +- :ref:`手动安装升级 ` + +.. contents:: + :local: + :depth: 2 + +********************** +强制性文件更改 +********************** + +错误文件 +=========== + +更新以下文件以显示正确的错误信息: + +- app/Views/errors/cli/error_exception.php +- app/Views/errors/html/error_exception.php + +**************** +重大更改 +**************** + +.. _upgrade-444-validation-with-dot-array-syntax: + +使用 Dot 数组语法验证 +================================ + +如果你在验证规则中使用 :ref:`Dot 数组语法 `,已修复了一个 ``*`` 在错误下标验证数据的错误。 + +在以前的版本中,规则 key ``contacts.*.name`` 错误地捕获了任何级别的数据,如 ``contacts.*.name``,``contacts.*.*.name``,``contacts.*.*.*.name`` 等。 + +以下代码解释了详细信息: + +.. literalinclude:: upgrade_444/001.php + :lines: 2- + +如果你有依赖于这个错误的代码,修复规则 key。 + +验证规则匹配和差异 +==================================== + +由于在严格和传统规则中使用 ``matches`` 和 ``differs`` 验证非字符串类型数据的情况下已经修复了错误,如果你正在使用这些规则并验证非字符串数据,验证结果可能会被更改(修复)。 + +注意,传统规则不应该用于验证非字符串的数据。 + +在 CURLRequest 中使用 `ssl_key` 选项已被移除 +========================================================== + +CURLRequest 选项 `ssl_key` 不再被识别。 +如果在使用,选项 `ssl_key` 必须被选项 `verify` 替代,以定义 CURLRequest 的 CA 包路径。 + +CURLRequest 选项 `verify` 也可以像往常一样接受 *布尔值*。 + +************* +项目文件 +************* + +**项目空间**(root,app,public,writable)中的一些文件已经更新。由于这些文件在 **system** 范围之外,没有你的干预不会发生改变。 + +有一些第三方 CodeIgniter 模块可用于帮助合并对项目空间的更改:`在 Packagist 上探索 `_。 + +所有更改 +=========== + +这是 **项目空间** 中所有更改的文件列表; +许多文件只是简单的注释或格式更改,对运行时没有影响: + +- app/Config/App.php +- app/Config/Autoload.php +- app/Config/Boot/development.php +- app/Config/Boot/testing.php +- app/Config/Cache.php +- app/Config/Email.php +- app/Config/Filters.php +- app/Config/Kint.php +- app/Config/Modules.php +- app/Config/Publisher.php +- app/Config/Session.php +- app/Views/errors/cli/error_exception.php +- app/Views/errors/html/error_exception.php +- composer.json +- env +- spark diff --git a/source/installation/upgrade_444/001.php b/source/installation/upgrade_444/001.php new file mode 100644 index 00000000..9bcc6412 --- /dev/null +++ b/source/installation/upgrade_444/001.php @@ -0,0 +1,38 @@ + [ + 'name' => 'Joe Smith', + 'just' => [ + 'friends' => [ + ['name' => 'SATO Taro'], + ['name' => 'Li Ming'], + ['name' => 'Heinz Müller'], + ], + ], + ], +]; + +$validation->setRules( + ['contacts.*.name' => 'required|max_length[8]'] +); + +$validation->run($data); // false + +d($validation->getErrors()); +/* + Before: Captured `contacts.*.*.*.name` incorrectly. + [ + contacts.just.friends.0.name => "The contacts.*.name field cannot exceed 8 characters in length.", + contacts.just.friends.2.name => "The contacts.*.name field cannot exceed 8 characters in length.", + ] + + After: Captures no data for `contacts.*.name`. + [ + contacts.*.name => string (38) "The contacts.*.name field is required.", + ] +*/ diff --git a/source/installation/upgrade_445.rst b/source/installation/upgrade_445.rst new file mode 100644 index 00000000..28a3d646 --- /dev/null +++ b/source/installation/upgrade_445.rst @@ -0,0 +1,28 @@ +############################# +从 4.4.4 升级到 4.4.5 +############################# + +请参考与你的安装方法相对应的升级指南。 + +- :ref:`Composer 安装 App Starter 升级 ` +- :ref:`Composer 安装将 CodeIgniter4 添加到现有项目的升级 ` +- :ref:`手动安装升级 ` + +.. contents:: + :local: + :depth: 2 + +************* +项目文件 +************* + +**项目空间**(根目录,app,public,writable)中的一些文件接收到更新。由于这些文件在 **system** 范围之外,它们不会在没有你的干预的情况下被更改。 + +有一些第三方 CodeIgniter 模块可用于帮助合并对项目空间的更改:`在 Packagist 上探索 `_。 + +所有更改 +=========== + +这是在 **项目空间** 中接收到更改的所有文件的列表;许多文件只是简单的注释或格式更改,对运行时没有影响: + +- composer.json diff --git a/source/installation/upgrade_4xx.rst b/source/installation/upgrade_4xx.rst index 88c7afc3..2e5718e0 100755 --- a/source/installation/upgrade_4xx.rst +++ b/source/installation/upgrade_4xx.rst @@ -104,7 +104,8 @@ CodeIgniter 4 是框架的重写,并且不向后兼容。将你的应用程序 - 在 CI4 中, ``redirect()`` 与 CI3 中的完全不同。 - `redirect() 文档 CodeIgniter 3.X `_ - `redirect() 文档 CodeIgniter 4.X <../general/common_functions.html#redirect>`_ - - 在 CI4 中, ``redirect()`` 返回一个 ``RedirectResponse`` 实例,而不是重定向和终止脚本执行。你必须返回它。 + - 在 CI4 中,:php:func:`redirect()` 返回一个 ``RedirectResponse`` 实例,而不是重定向并终止脚本执行。你必须从控制器或控制器过滤器中返回它。 + - 在调用 ``redirect()`` 之前设置的 Cookie 和 Header 不会自动携带到 ``RedirectResponse``。如果你想发送它们,你需要手动调用 ``withCookies()`` 或 ``withHeaders()``。 - 你需要将 CI3 的 ``redirect('login/form')`` 改为 ``return redirect()->to('login/form')``。 钩子 @@ -118,6 +119,20 @@ CodeIgniter 4 是框架的重写,并且不向后兼容。将你的应用程序 - 挂钩点 ``display_override`` 和 ``cache_override`` 已被移除。因为基础方法已被移除。 - 挂钩点 ``post_system`` 已经移动到在发送最终渲染页面之前。 +错误处理 +============== + +- CI4 中的行为已经稍有更改。 + + - 在 CI3 中,行为在 **index.php** 文件中设置: + + - 错误级别由 ``error_reporting()`` 设置的错误将被记录(但根据 ``log_threshold`` 设置,它们可能不会被写入日志文件)。 + - 错误级别为 ``E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR`` 的错误将停止框架处理,无论在 ``error_reporting()`` 中设置的错误级别如何。 + - 在 CI4 中,行为在 **app/Config/Boot/{environment}.php** 文件中设置: + + - 错误级别由 ``error_reporting()`` 设置的错误将被记录(但根据 ``Config\Logger::$threshold`` 设置,它们可能不会被写入日志文件)。 + - 所有不被 ``error_reporting()`` 忽略的错误都将停止框架处理。 + 扩展框架 ======================= diff --git a/source/installation/upgrade_controllers.rst b/source/installation/upgrade_controllers.rst index ee33ed6f..7d9d1e81 100644 --- a/source/installation/upgrade_controllers.rst +++ b/source/installation/upgrade_controllers.rst @@ -20,6 +20,7 @@ - CI4 为你提供了 :doc:`Request ` 和 :doc:`Responses ` 对象来使用 - 比 CI3 的方式更强大。 - 如果你需要一个基类控制器(CI3 中的 ``MY_Controller``),请使用 **app/Controllers/BaseController.php**。 +- 在控制器中调用 ``echo``,如同在 CI3 中一样,仍然被支持,但建议从控制器返回字符串或 Response 对象。 升级指南 ============= diff --git a/source/installation/upgrade_emails.rst b/source/installation/upgrade_emails.rst index 5970263a..ad1318af 100644 --- a/source/installation/upgrade_emails.rst +++ b/source/installation/upgrade_emails.rst @@ -13,7 +13,9 @@ 变更点 ===================== + - 只是一些小变化,如方法名称和库的加载。 +- 使用 SMTP 协议时的行为已经稍有更改。如果你使用 CI3 的设置,可能无法与你的 SMTP 服务器正确通信。请参见 :ref:`email-ssl-tls-for-smtp` 和 :ref:`email-preferences`。 升级指南 ============= diff --git a/source/installation/upgrade_routing.rst b/source/installation/upgrade_routing.rst index fd5ed4b2..82229ad0 100644 --- a/source/installation/upgrade_routing.rst +++ b/source/installation/upgrade_routing.rst @@ -17,18 +17,21 @@ - 在 CI4 中,默认关闭自动路由。 - 在 CI4 中引入了新的更安全的 :ref:`auto-routing-improved`。 - 在 CI4 中,路由配置不再通过设置路由数组来完成。 +- CI3 中的通配符 ``(:any)`` 在 CI4 中将会是占位符 ``(:segment)``。在 CI4 中的 ``(:any)`` 匹配多个段。请参见 :ref:`URI 路由 `。 升级指南 ============= 1. 如果你以与 CI3 相同的方式使用自动路由,则需要启用 :ref:`auto-routing-legacy`。 -2. CI3 中的占位符 ``(:any)`` 在 CI4 中将是 ``(:segment)``。 -3. 你必须更改每个路由行的语法,并将其附加到 **app/Config/Routes.php** 中。例如: +2. 你必须更改每个路由行的语法,并将其附加到 **app/Config/Routes.php** 中。例如: - ``$route['journals'] = 'blogs';`` 改为 ``$routes->add('journals', 'Blogs::index');``。这将映射到 ``Blogs`` 控制器中的 ``index()`` 方法。 - - ``$route['product/(:any)'] = 'catalog/product_lookup';`` 改为 ``$routes->add('product/(:segment)', 'Catalog::productLookup');`` + - ``$route['product/(:any)'] = 'catalog/product_lookup';`` 改为 ``$routes->add('product/(:segment)', 'Catalog::productLookup');``。别忘了将 ``(:any)`` 替换为 ``(:segment)``。 - ``$route['login/(.+)'] = 'auth/login/$1';`` 改为 ``$routes->add('login/(.+)', 'Auth::login/$1');`` + .. note:: 为了向后兼容,这里使用了 ``$routes->add()``。但我们强烈建议使用 :ref:`routing-http-verb-routes` 中的 + ``$routes->get()`` 替代 ``$routes->add()``,以增强安全性。 + 代码示例 ============ @@ -43,3 +46,6 @@ CodeIgniter 4.x 版本 路径:**app/Config/Routes.php**: .. literalinclude:: upgrade_routing/001.php + +.. note:: 为了向后兼容,这里使用了 ``$routes->add()``。但我们强烈建议使用 :ref:`routing-http-verb-routes` 中的 + ``$routes->get()`` 替代 ``$routes->add()``,以增强安全性。 diff --git a/source/installation/upgrade_routing/001.php b/source/installation/upgrade_routing/001.php index 1378517e..b2cb2ee5 100644 --- a/source/installation/upgrade_routing/001.php +++ b/source/installation/upgrade_routing/001.php @@ -1,16 +1,11 @@ get('/', 'Home::index'); $routes->add('posts/index', 'Posts::index'); $routes->add('teams/create', 'Teams::create'); @@ -21,5 +16,3 @@ $routes->add('drivers/create', 'Drivers::create'); $routes->add('drivers/update', 'Drivers::update'); $routes->add('posts/(:segment)', 'Posts::view/$1'); - -// ... diff --git a/source/installation/upgrade_sessions.rst b/source/installation/upgrade_sessions.rst index 9eb78e02..dbe4d307 100644 --- a/source/installation/upgrade_sessions.rst +++ b/source/installation/upgrade_sessions.rst @@ -13,10 +13,13 @@ 变更点 ===================== + - 只是一些小变化,如方法名称和库的加载。 +- 在数据库驱动中,Session 表的定义已经发生了变化。 升级指南 ============= + 1. 在使用 Session 库的任何地方,用 ``$session = session();`` 替换 ``$this->load->library('session');``。 2. 从那时起,必须用 ``$session`` 后跟新方法名替换以 ``$this->session`` 开头的每一行。 @@ -24,6 +27,7 @@ - 要设置数据,请使用 ``$session->set($array);`` 代替 ``$this->session->set_userdata($array);``。 - 要删除数据,请使用 ``unset($_SESSION['some_name']);`` 或 ``$session->remove('some_name');`` 代替 ``$this->session->unset_userdata('some_name');``。 - 要将 Session 数据标记为只在下一个请求中可用的闪存数据,请使用 ``$session->markAsFlashdata('item');`` 代替 ``$this->session->mark_as_flash('item');``` +3. 如果你使用数据库驱动,你需要重新创建 Session 表。参见 :ref:`sessions-databasehandler-driver`。 代码示例 ============ diff --git a/source/installation/upgrading.rst b/source/installation/upgrading.rst index 3d488872..baba066a 100755 --- a/source/installation/upgrading.rst +++ b/source/installation/upgrading.rst @@ -14,6 +14,8 @@ backward_compatibility_notes + upgrade_445 + upgrade_444 upgrade_443 upgrade_442 upgrade_441 diff --git a/source/libraries/caching/012.php b/source/libraries/caching/012.php index 90395ed7..b6a56120 100644 --- a/source/libraries/caching/012.php +++ b/source/libraries/caching/012.php @@ -1,3 +1,5 @@ get(); // array of Cookie objects // alternatively, you can use the display method diff --git a/source/libraries/curlrequest.rst b/source/libraries/curlrequest.rst index 12d6fdc5..c1b00e16 100755 --- a/source/libraries/curlrequest.rst +++ b/source/libraries/curlrequest.rst @@ -21,7 +21,9 @@ CURLRequest 配置 共享选项 =============== -.. note:: 自 v4.4.0 起,默认值已更改为 ``false``。此设置仅用于向后兼容。新用户无需更改此设置。 +.. important:: 这个设置只是为了向后兼容而存在。在新项目中不要使用它。即使你已经在使用它,我们也建议你禁用它。 + +.. note:: 自 v4.4.0 起,其默认值已被改为 ``false``。 如果你想在多个请求之间共享所有选项,请在 **app/Config/CURLRequest.php** 中将 ``$shareOptions`` 设置为 ``true``: @@ -64,6 +66,10 @@ CURLRequest 配置 .. literalinclude:: curlrequest/005.php +.. important:: 默认情况下,如果返回的 HTTP + 代码大于或等于 400,CURLRequest 将抛出 ``HTTPException``。如果你想获取响应, + 请查看 `http_errors`_ 选项。 + .. note:: 当 ``$shareOptions`` 为 false 时,传入方法的选项将用于该请求。发送请求后,选项将被清除。如果你想对所有请求使用选项,请在构造函数中传入选项。 由于响应是一个 ``CodeIgniter\HTTP\Response`` 实例,你可以使用所有正常的信息: @@ -87,12 +93,12 @@ CURLRequest 配置 ===================== ================ ======================== baseURI URI 结果 ===================== ================ ======================== - `http://foo.com` /bar `http://foo.com/bar` - `http://foo.com/foo` /bar `http://foo.com/bar` - `http://foo.com/foo` bar `http://foo.com/bar` - `http://foo.com/foo/` bar `http://foo.com/foo/bar` - `http://foo.com` `http://baz.com` `http://baz.com` - `http://foo.com/?bar` bar `http://foo.com/bar` + \http://foo.com /bar \http://foo.com/bar + \http://foo.com/foo /bar \http://foo.com/bar + \http://foo.com/foo bar \http://foo.com/bar + \http://foo.com/foo/ bar \http://foo.com/foo/bar + \http://foo.com \http://baz.com \http://baz.com + \http://foo.com/?bar bar \http://foo.com/bar ===================== ================ ======================== 使用响应 diff --git a/source/libraries/email.rst b/source/libraries/email.rst index 3233bf59..51860900 100644 --- a/source/libraries/email.rst +++ b/source/libraries/email.rst @@ -37,7 +37,7 @@ CodeIgniter 强大的 Email 类支持以下功能: 设置电子邮件首选项 ========================= -有 21 个不同的首选项可用于定制电子邮件消息的发送方式。你可以像这里描述的那样手动设置它们,也可以通过存储在配置文件中的首选项自动设置,如下所述: +有 21 个不同的首选项可用于定制电子邮件消息的发送方式。你可以像这里描述的那样手动设置它们,也可以通过存储在配置文件中的首选项自动设置,如 `电子邮件首选项`_ 中所述: 通过传递数组设置电子邮件首选项 --------------------------------------------- @@ -75,41 +75,45 @@ SMTP 协议的 SSL 与 TLS 成功发送的最后使用的设置可以从实例属性 ``$archive`` 获取。这对于测试和调试很有帮助,以确定在 ``send()`` 调用时的实际值。 +.. _email-preferences: + 电子邮件首选项 ================= 以下是可以在发送电子邮件时设置的所有首选项列表。 -=================== ====================== ============================ ======================================================================= -首选项 默认值 选项 描述 -=================== ====================== ============================ ======================================================================= -**userAgent** CodeIgniter 无 “用户代理”。 -**protocol** mail mail、sendmail 或 smtp 邮件发送协议。 -**mailPath** /usr/sbin/sendmail 无 到 Sendmail 的服务器路径。 -**SMTPHost** 无默认值 无 SMTP 服务器地址。 -**SMTPUser** 无默认值 无 SMTP 用户名。 -**SMTPPass** 无默认值 无 SMTP 密码。 -**SMTPPort** 25 无 SMTP 端口。(如果设置为 ``465``,不管 ``SMTPCrypto`` 设置如何, - 都将使用 TLS 建立连接) -**SMTPTimeout** 5 无 SMTP 超时(秒)。 -**SMTPKeepAlive** false true 或 false(布尔值) 启用持久 SMTP 连接。 -**SMTPCrypto** tls tls, ssl 或空字符串 SMTP 加密。将其设置为 ``ssl`` 将使用 SSL 创建与服务器的安全通道, - 而 ``tls`` 将向服务器发出 ``STARTTLS`` 命令。 - 在端口 465 上的连接应将其设置为空字符串(``''``)。 - 另请参阅 :ref:`email-ssl-tls-for-smtp`。 -**wordWrap** true true 或 false(布尔值) 启用自动换行。 -**wrapChars** 76 换行处的字符数。 -**mailType** text text 或 html 邮件类型。如果发送 HTML 电子邮件,你必须将其作为完整的网页发送。 - 确保你没有任何相对链接或相对图像路径,否则它们将无法工作。 -**charset** utf-8 字符集(utf-8、iso-8859-1 等)。 -**validate** true true 或 false(布尔值) 是否验证电子邮件地址。 -**priority** 3 1、2、3、4、5 电子邮件优先级。1 最高。5 最低。3 为正常。 -**CRLF** \\n "\\r\\n" 或 "\\n" 或 "\\r" 换行符。(遵循 RFC 822 使用 "\\r\\n")。 -**newline** \\n "\\r\\n" 或 "\\n" 或 "\\r" 换行符。(遵循 RFC 822 使用 "\\r\\n")。 -**BCCBatchMode** false true 或 false(布尔值) 启用 BCC 批量模式。 -**BCCBatchSize** 200 无 每个 BCC 批次中的电子邮件数量。 -**DSN** false true 或 false(布尔值) 启用来自服务器的通知消息 -=================== ====================== ============================ ======================================================================= +=================== =================== ============================ ======================================================================= +首选项 默认值 选项 描述 +=================== =================== ============================ ======================================================================= +**userAgent** CodeIgniter 无 "用户代理"。 +**protocol** mail ``mail``, ``sendmail``, 邮件发送协议。 + 或 ``smtp`` +**mailPath** /usr/sbin/sendmail 无 到 Sendmail 的服务器路径。 +**SMTPHost** 无默认值 无 SMTP 服务器主机名。 +**SMTPUser** 无默认值 无 SMTP 用户名。 +**SMTPPass** 无默认值 无 SMTP 密码。 +**SMTPPort** 25 无 SMTP 端口。(如果设置为 ``465``,无论 ``SMTPCrypto`` 设置如何, + 都将使用 TLS 连接。) +**SMTPTimeout** 5 无 SMTP 超时时间(以秒为单位)。 +**SMTPKeepAlive** false ``true``/``false`` (布尔值) 启用持久化的 SMTP 连接。 +**SMTPCrypto** tls ``tls``, ``ssl``, 或 SMTP 加密。将此设置为 ``ssl`` 将使用 SSL 创建一个安全 + 空字符串 (``''``) 通道到服务器,``tls`` 将向服务器发出 + ``STARTTLS`` 命令。在端口 ``465`` 的连接应 + 将此设置为空字符串 (``''``)。请参见 + :ref:`email-ssl-tls-for-smtp`。 +**wordWrap** true ``true``/``false`` (布尔值) 启用自动换行。 +**wrapChars** 76 换行字符数。 +**mailType** text ``text`` 或 ``html`` 邮件类型。如果你发送 HTML 邮件,你必须将其作为一个完整的网页发送。 + 确保你没有任何相对链接或相对图像路径,否则它们将无法工作。 +**charset** utf-8 字符集(``utf-8``, ``iso-8859-1``, 等等)。 +**validate** true ``true``/``false`` (布尔值) 是否验证电子邮件地址。 +**priority** 3 1, 2, 3, 4, 5 邮件优先级。``1`` = 最高。``5`` = 最低。``3`` = 正常。 +**CRLF** \\n ``\r\n`` 或 ``\n`` 或 ``\r`` 换行字符。(使用 ``\r\n`` 以符合 RFC 822)。 +**newline** \\n ``\r\n`` 或 ``\n`` 或 ``\r`` 换行字符。(使用 ``\r\n`` 以符合 RFC 822)。 +**BCCBatchMode** false ``true``/``false`` (布尔值) 启用 BCC 批处理模式。 +**BCCBatchSize** 200 无 每个 BCC 批次的电子邮件数量。 +**DSN** false ``true``/``false`` (布尔值) 启用来自服务器的通知消息。 +=================== =================== ============================ ======================================================================= 覆盖文字换行 ======================== diff --git a/source/libraries/files/011.php b/source/libraries/files/011.php index fb15a5ca..db63f20a 100644 --- a/source/libraries/files/011.php +++ b/source/libraries/files/011.php @@ -1,5 +1,7 @@ add(APPPATH . 'Config', true); // Adds all Config files and directories diff --git a/source/libraries/images/007.php b/source/libraries/images/007.php index 51521268..5faaf426 100644 --- a/source/libraries/images/007.php +++ b/source/libraries/images/007.php @@ -6,6 +6,6 @@ $image->withFile('/path/to/image/mypic.jpg') ->fit(100, 100, 'center') ->save('/path/to/image/mypic_thumb.jpg'); -} catch (CodeIgniter\Images\Exceptions\ImageException $e) { +} catch (\CodeIgniter\Images\Exceptions\ImageException $e) { echo $e->getMessage(); } diff --git a/source/libraries/official_packages.rst b/source/libraries/official_packages.rst index 0294bb94..7aea614b 100644 --- a/source/libraries/official_packages.rst +++ b/source/libraries/official_packages.rst @@ -14,7 +14,7 @@ CodeIgniter 框架无法解决开发者将遇到的所有问题。许多用户 Shield ****** -`CodeIgniter Shield `_ 是 CodeIgniter 4 的身份验证和授权框架。它旨在安全、灵活且易于扩展以满足许多不同类型网站的需求。其中许多功能包括: +`CodeIgniter Shield `_ 是 CodeIgniter 4 的身份验证和授权框架。它旨在安全、灵活且易于扩展以满足许多不同类型网站的需求。其中许多功能包括: * 基于会话的身份验证 * 个人访问令牌认证 @@ -27,8 +27,21 @@ Shield Settings ******** -`CodeIgniter Settings `_ 是配置文件包装器,允许任何配置设置保存到数据库中,同时在没有自定义值存储时默认为配置文件。这允许应用程序与默认配置值一起发布,但随着项目的发展或服务器的迁移而不必接触代码而适应。 +`CodeIgniter Settings `_ 是配置文件包装器,允许任何配置设置保存到数据库中,同时在没有自定义值存储时默认为配置文件。这允许应用程序与默认配置值一起发布,但随着项目的发展或服务器的迁移而不必接触代码而适应。 +************ +任务 (BETA) +************ + +`CodeIgniter 任务 `_ 是 CodeIgniter 4 的一个简单任务调度器。 +它允许你安排任务在特定时间运行,或者定期运行。它的设计目标是简单易用,但足够灵活以处理大多数使用场景。 + +************ +队列 (BETA) +************ + +`CodeIgniter 队列 `_ 是 CodeIgniter 4 的一个简单队列系统。 +它允许你将任务排队,以便稍后运行。 ***** Cache diff --git a/source/libraries/pagination.rst b/source/libraries/pagination.rst index 545432de..01c3c126 100755 --- a/source/libraries/pagination.rst +++ b/source/libraries/pagination.rst @@ -186,28 +186,37 @@ setSurroundCount() 在第一行中, ``setSurroundCount()`` 方法指定我们希望在当前页面链接的两侧显示两个链接。它只接受显示链接数的参数。 +.. note:: 你必须首先调用此方法来生成正确的分页链接。 + hasPrevious() & hasNext() ------------------------- -这些方法返回一个布尔值,如果根据传给 ``setSurroundCount()`` 的值,在当前页面的任一侧有更多可以显示的链接,则返回 true。例如,假设我们有 20 页数据。当前页面是第 3 页。如果周围计数为 2,那么将在列表中显示以下链接:1、2、3、4 和 5。由于显示的第一个链接是第 1 页,所以 ``hasPrevious()`` 将返回 **false**,因为没有 0 页。但是, ``hasNext()`` 将返回 **true**,因为在第 5 页之后还有 15 页结果。 +这些方法会返回一个布尔值 ``true``,如果在当前页面的两侧可以显示更多的链接,这取决于传递给 `setSurroundCount()`_ 的值。 + +例如,假设我们有 20 页的数据。当前的页面是第 3 页。如果周围的数量是 2,那么以下链接会显示如下:: + + 1 | 2 | 3 | 4 | 5 + +由于显示的第一个链接是第一页,``hasPrevious()`` 将返回 ``false``,因为没有第零页。然而, +``hasNext()`` 将返回 ``true``,因为在第五页之后还有 15 页的结果。 getPrevious() & getNext() ------------------------- -这些方法返回在编号链接任一侧当前页面之前和之后的结果页面的 URL。 +这些方法返回数字链接两侧的上一页或下一页结果的 **URL**。 -例如,你将当前页面设置为 5,并希望它前后(surroundCount)的链接分别为 2 个,这将给你这样的结果:: +例如,你将当前页面设置为 5,你希望在之前和之后的链接(surroundCount)各为 2,那么你会得到如下内容:: 3 | 4 | 5 | 6 | 7 ``getPrevious()`` 返回第 2 页的 URL。``getNext()`` 返回第 8 页的 URL。 -如果你想要第 4 页和第 6 页,请改用 ``getPreviousPage()`` 和 ``getNextPage()``。 +如果你想要第 4 页和第 6 页,请改用 `getPreviousPage() & getNextPage()`_。 getFirst() & getLast() ---------------------- -与 ``getPrevious()`` 和 ``getNext()`` 类似,这些方法返回结果集中的第一页和最后一页的链接。 +与 `getPrevious() & getNext()`_ 类似,这些方法返回结果集中的第一页和最后一页的 **URL**。 links() ------- @@ -216,9 +225,9 @@ links() .. literalinclude:: pagination/013.php -在为标准分页结构提供的代码中,使用 ``getPrevious()`` 和 ``getNext()`` 方法分别获取前一个和下一个分页组的链接。 +在为标准分页结构提供的代码中,使用 `getPrevious() & getNext()`_ 方法分别获取前一个和下一个分页组的链接。 -如果你要使用上一页和下一页将链接到当前页面基于当前页面的上一页和下一页的分页结构,只需分别用 ``getPreviousPage()`` 和 ``getNextPage()`` 替换 ``getPrevious()`` 和 ``getNext()``,以及分别用 ``hasPreviousPage()`` 和 ``hasNextPage()`` 替换 ``hasPrevious()`` 和 ``hasNext()``。 +如果你要使用上一页和下一页将链接到当前页面基于当前页面的上一页和下一页的分页结构,只需分别用 `getPreviousPage() & getNextPage()`_ 替换 `getPrevious() & getNext()`_,以及分别用 `hasPreviousPage() & hasNextPage()`_ 替换 `hasPrevious() & hasNext()`_。 请参阅以下示例及其更改: @@ -227,22 +236,31 @@ links() hasPreviousPage() & hasNextPage() --------------------------------- -该方法分别返回一个布尔值,指示当前显示页面之前和之后是否存在链接。 +这个方法返回一个布尔值 ``true``,如果在当前显示的页面前后各有一个页面的链接。 + +例如,假设我们有 20 页的数据。当前的页面是第 3 页。如果周围的数量是 2,那么以下链接会显示如下:: + + 1 | 2 | 3 | 4 | 5 -它们与 ``hasPrevious()`` 和 ``hasNext()`` 的区别在于,它们基于当前显示的页面,而 ``hasPrevious()`` 和 ``hasNext()`` 基于传入 ``setSurroundCount()`` 的值设置在当前页面之前和之后的链接集。 +``hasPreviousPage()`` 将返回 ``true``,因为有第 2 页。并且, +``hasNextPage()`` 将返回 ``true``,因为有第 4 页。 + +.. note:: 与 `hasPrevious() & hasNext()`_ 的区别在于,它们是基于当前页面的,而 `hasPrevious() & hasNext()`_ 是基于在当前页面前后显示的链接集,这取决于传递给 `setSurroundCount()`_ 的值。 getPreviousPage() & getNextPage() --------------------------------- -这些方法返回当前显示页面之前和之后的页面的 URL,与 ``getPrevious()`` 和 ``getNext()`` 不同,后两者返回编号链接任一侧之前和之后的结果页面的 URL。请参阅前一段落的完整说明。 +这些方法返回当前显示页面的前一页和后一页的 **URL**。 -例如,你将当前页面设置为 5,并希望它前后(surroundCount)的链接分别为 2 个,这将给你这样的结果:: +例如,你将当前页面设置为 5,你希望在之前和之后的链接(surroundCount)各为 2,那么你会得到如下内容:: 3 | 4 | 5 | 6 | 7 ``getPreviousPage()`` 返回第 4 页的 URL。``getNextPage()`` 返回第 6 页的 URL。 -如果你想要页面数而不是 URL,可以使用以下方法: +.. note:: `getPrevious() & getNext()`_ 返回数字链接两侧的上一页或下一页结果的 URL。 + +如果你希望得到的是页面数字而不是 URL,你可以使用以下方法: getPreviousPageNumber() & getNextPageNumber() --------------------------------------------- @@ -252,7 +270,13 @@ getPreviousPageNumber() & getNextPageNumber() getFirstPageNumber() & getLastPageNumber() ------------------------------------------ -这些方法分别返回结果集中的第一页和最后一页的页码。 +这些方法返回要显示的链接集中第一页和最后一页的页码。例如,如果要显示的链接集如下所示:: + + 3 | 4 | 5 | 6 | 7 + +``getFirstPageNumber()`` 将返回 3,而 ``getLastPageNumber()`` 将返回 7。 + +.. note:: 要获取整个结果集中第一页和最后一页的页码,你可以使用以下方法:第一页的页码总是 1,可以使用 `getPageCount()`_ 来获取最后一页的页码。 getCurrentPageNumber() ---------------------- diff --git a/source/libraries/publisher/006.php b/source/libraries/publisher/006.php index 9ec20cc4..5a1eb24c 100644 --- a/source/libraries/publisher/006.php +++ b/source/libraries/publisher/006.php @@ -1,3 +1,5 @@ addPaths([ 'pencil/lead.png', diff --git a/source/libraries/publisher/012.php b/source/libraries/publisher/012.php index 32ec11f1..7d978744 100644 --- a/source/libraries/publisher/012.php +++ b/source/libraries/publisher/012.php @@ -1,5 +1,7 @@ addPaths([ 'pencil/lead.png', diff --git a/source/libraries/security.rst b/source/libraries/security.rst index 7420da1e..5d20e301 100755 --- a/source/libraries/security.rst +++ b/source/libraries/security.rst @@ -67,6 +67,11 @@ CSRF 配置 CSRF 保护方法 ----------------------- +.. warning:: 如果你使用 :doc:`Session <./sessions>`,一定要使用基于 Session 的 + CSRF 保护。基于 Cookie 的 CSRF 保护无法防止同站攻击。 + 详情请参见 + `GHSA-5hm8-vh6r-2cjq `_ 。 + 默认情况下,使用基于 Cookie 的 CSRF 保护。它是 OWASP 跨站请求伪造预防备忘单上的 `双重提交 Cookie `_。 @@ -93,10 +98,15 @@ CSRF 保护方法 令牌再生成 ------------------ -令牌可以在每次提交时重新生成(默认),或者在 CSRF cookie 的整个生命周期内保持不变。令牌的默认再生成提供了更严格的安全性,但可能导致可用性问题,因为其他令牌变得无效(后退/前进导航、多个选项卡/窗口、异步操作等)。你可以通过编辑以下配置参数的值在 **app/Config/Security.php** 来更改此行为: +令牌可以在每次提交时重新生成(默认),或者在 Session 或 CSRF Cookie 的整个生命周期内保持不变。 + +令牌的默认再生成提供了更严格的安全性,但可能导致可用性问题,因为其他令牌变得无效(后退/前进导航、多个选项卡/窗口、异步操作等)。你可以通过编辑以下配置参数的值在 **app/Config/Security.php** 来更改此行为: .. literalinclude:: security/004.php +.. warning:: 如果你使用基于 Cookie 的 CSRF 保护,并在提交后使用 :php:func:`redirect()`, + 你必须调用 ``withCookie()`` 来发送重新生成的 CSRF Cookie。详情请参见 :ref:`response-redirect`。 + .. note:: 自 v4.2.3 起,你可以用 ``Security::generateHash()`` 方法手动重新生成 CSRF 令牌。 .. _csrf-redirection-on-failure: diff --git a/source/libraries/time/002.php b/source/libraries/time/002.php index 46b84050..ebd5895d 100644 --- a/source/libraries/time/002.php +++ b/source/libraries/time/002.php @@ -1,3 +1,5 @@ toLocalizedString('MMM d, yyyy'); // March 9, 2016 diff --git a/source/libraries/time/016.php b/source/libraries/time/016.php index 0645fd9b..fe67508a 100644 --- a/source/libraries/time/016.php +++ b/source/libraries/time/016.php @@ -1,5 +1,7 @@ toDateTimeString(); // 2016-03-09 12:00:00 diff --git a/source/libraries/time/017.php b/source/libraries/time/017.php index c0283d9d..6047929b 100644 --- a/source/libraries/time/017.php +++ b/source/libraries/time/017.php @@ -1,5 +1,7 @@ toDateString(); // 2016-03-09 diff --git a/source/libraries/time/018.php b/source/libraries/time/018.php index d3001a77..b53dd969 100644 --- a/source/libraries/time/018.php +++ b/source/libraries/time/018.php @@ -1,5 +1,7 @@ toTimeString(); // 12:00:00 diff --git a/source/libraries/time/019.php b/source/libraries/time/019.php index 50088537..cc355921 100644 --- a/source/libraries/time/019.php +++ b/source/libraries/time/019.php @@ -1,5 +1,7 @@ getYear(); // 2016 -echo $time->getMonth(); // 8 -echo $time->getDay(); // 12 -echo $time->getHour(); // 16 -echo $time->getMinute(); // 15 -echo $time->getSecond(); // 23 +// The output may vary based on locale. +echo $time->getYear(); // '2016' +echo $time->getMonth(); // '8' +echo $time->getDay(); // '12' +echo $time->getHour(); // '16' +echo $time->getMinute(); // '15' +echo $time->getSecond(); // '23' -echo $time->year; // 2016 -echo $time->month; // 8 -echo $time->day; // 12 -echo $time->hour; // 16 -echo $time->minute; // 15 -echo $time->second; // 23 +echo $time->year; // '2016' +echo $time->month; // '8' +echo $time->day; // '12' +echo $time->hour; // '16' +echo $time->minute; // '15' +echo $time->second; // '23' diff --git a/source/libraries/time/021.php b/source/libraries/time/021.php index a3e0f128..fb384942 100644 --- a/source/libraries/time/021.php +++ b/source/libraries/time/021.php @@ -1,17 +1,20 @@ getDayOfWeek(); // 6 - but may vary based on locale's starting day of the week -echo $time->getDayOfYear(); // 225 -echo $time->getWeekOfMonth(); // 2 -echo $time->getWeekOfYear(); // 33 -echo $time->getTimestamp(); // 1471018523 - UNIX timestamp -echo $time->getQuarter(); // 3 +// The output may vary based on locale. +echo $time->getDayOfWeek(); // '6' +echo $time->getDayOfYear(); // '225' +echo $time->getWeekOfMonth(); // '2' +echo $time->getWeekOfYear(); // '33' +echo $time->getTimestamp(); // 1471018523 - UNIX timestamp (locale independent) +echo $time->getQuarter(); // '3' -echo $time->dayOfWeek; // 6 -echo $time->dayOfYear; // 225 -echo $time->weekOfMonth; // 2 -echo $time->weekOfYear; // 33 +echo $time->dayOfWeek; // '6' +echo $time->dayOfYear; // '225' +echo $time->weekOfMonth; // '2' +echo $time->weekOfYear; // '33' echo $time->timestamp; // 1471018523 -echo $time->quarter; // 3 +echo $time->quarter; // '3' diff --git a/source/libraries/time/022.php b/source/libraries/time/022.php index facfdc5a..deda59e0 100644 --- a/source/libraries/time/022.php +++ b/source/libraries/time/022.php @@ -1,5 +1,7 @@ getAge(); // 5 diff --git a/source/libraries/time/023.php b/source/libraries/time/023.php index 970aaad4..9f0b0803 100644 --- a/source/libraries/time/023.php +++ b/source/libraries/time/023.php @@ -1,4 +1,6 @@ getDst(); // false echo Time::createFromDate(2012, 9, 1)->dst; // true diff --git a/source/libraries/time/024.php b/source/libraries/time/024.php index 1919e1d0..ab73eb62 100644 --- a/source/libraries/time/024.php +++ b/source/libraries/time/024.php @@ -1,4 +1,6 @@ getLocal(); // true -echo Time::now('Europe/London'); // false +use CodeIgniter\I18n\Time; + +echo Time::now()->getLocal(); // true +echo Time::now('Europe/London')->local; // false diff --git a/source/libraries/time/025.php b/source/libraries/time/025.php index b25d5de4..50b14fdc 100644 --- a/source/libraries/time/025.php +++ b/source/libraries/time/025.php @@ -1,4 +1,6 @@ getUtc(); // false echo Time::now('UTC')->utc; // true diff --git a/source/libraries/time/026.php b/source/libraries/time/026.php index 030102f7..2aff6825 100644 --- a/source/libraries/time/026.php +++ b/source/libraries/time/026.php @@ -1,5 +1,7 @@ getTimezone(); $tz = Time::now()->timezone; diff --git a/source/libraries/time/027.php b/source/libraries/time/027.php index dde3a39b..666bbd27 100644 --- a/source/libraries/time/027.php +++ b/source/libraries/time/027.php @@ -1,4 +1,6 @@ getTimezoneName(); // America/Chicago echo Time::now('Europe/London')->timezoneName; // Europe/London diff --git a/source/libraries/time/029.php b/source/libraries/time/029.php index d65f408f..fbff5ab5 100644 --- a/source/libraries/time/029.php +++ b/source/libraries/time/029.php @@ -1,5 +1,7 @@ setTimezone('Europe/London'); // Returns new instance converted to new timezone diff --git a/source/libraries/time/030.php b/source/libraries/time/030.php index 243416cb..1e22876e 100644 --- a/source/libraries/time/030.php +++ b/source/libraries/time/030.php @@ -1,5 +1,7 @@ setTimestamp(strtotime('April 1, 2017')); diff --git a/source/libraries/time/032.php b/source/libraries/time/032.php index 9207945c..ce6490e3 100644 --- a/source/libraries/time/032.php +++ b/source/libraries/time/032.php @@ -1,5 +1,7 @@ difference(Time::now()); -$diff = $time->difference(new DateTime('July 4, 1975', 'America/Chicago')); +$diff = $time->difference(new \DateTime('July 4, 1975', 'America/Chicago')); $diff = $time->difference('July 4, 1975 13:32:05', 'America/Chicago'); diff --git a/source/libraries/time/039.php b/source/libraries/time/039.php index 12cc3d63..9c31042b 100644 --- a/source/libraries/time/039.php +++ b/source/libraries/time/039.php @@ -1,5 +1,7 @@ validate()`` 中设置任何验证规则。 +如果提交表单,你应该只是简单地看到表单重新加载。这是因为你还没有在 :ref:`controller-validatedata` 中设置任何验证规则。 -``validate()`` 方法是控制器中的一个方法。它使用内部的 **验证类**。参见 :ref:`controllers-validating-data`。 +``validateData()`` 方法是控制器中的一个方法。它使用内部的 **验证类**。参见 :ref:`controller-validatedata`。 -.. note:: 由于你还没有告诉 ``validate()`` 方法要验证任何内容,它 **默认返回 false** (布尔值 false)。只有在成功应用你的规则且没有失败时, ``validate()`` 方法才会返回 true。 +.. note:: 由于你还没有告诉 ``validateData()`` 方法要验证任何内容,它 **默认返回 false** (布尔值 false)。只有在成功应用你的规则且没有失败时, ``validateData()`` 方法才会返回 true。 解释 =========== @@ -155,7 +155,7 @@ Form.php 控制器(**Form.php**)有一个属性:``$helpers``。 它加载了视图文件使用的表单辅助函数。 -控制器有一个方法:``index()``。这个方法在收到非 POST 请求时返回 **signup** 视图以显示表单。否则,它使用控制器提供的 ``validate()`` 方法。它还运行验证例程。 +控制器有一个方法:``index()``。这个方法在收到非 POST 请求时返回 **signup** 视图以显示表单。否则,它使用控制器提供的 :ref:`controller-validatedata` 方法。它还运行验证例程。 根据验证是否成功,它要么显示表单,要么显示成功页面。 添加验证规则 @@ -186,6 +186,8 @@ CodeIgniter 4 有两种验证规则类。 传统规则 ----------------- +.. important:: 传统规则只存在于向后兼容性。在新项目中不要使用它们。即使你已经在使用它们,我们也建议切换到严格规则。 + .. warning:: 在验证包含非字符串值的数据时,比如 JSON 数据,推荐使用 **严格规则**。 **传统规则** 隐式地假设正在验证字符串值,输入值可能会隐式转换为字符串值。 @@ -265,10 +267,10 @@ setRules() .. literalinclude:: validation/007.php :lines: 2- -.. _validation-withrequest: - .. note:: ``setRules()`` 会覆盖先前设置的任何规则。要向现有规则集添加多个规则,请多次使用 ``setRule()``。 +.. _validation-dot-array-syntax: + 为数组数据设置规则 ============================ @@ -282,15 +284,28 @@ setRules() .. literalinclude:: validation/010.php :lines: 2- +.. note:: 在 v4.4.4 之前,由于一个错误,通配符 ``*`` 在错误的下标上验证了数据。详情请参见 :ref:`升级 `。 + 当你有单维数组数据时,"点数组语法"也很有用。 例如,下拉多选返回的数据: .. literalinclude:: validation/011.php :lines: 2- +.. _validation-withrequest: + withRequest() ============= +.. important:: 该方法只存在于向后兼容性。在新项目中不要使用它。即使你已经在使用它,我们也建议你使用另一个更适合的方法。 + +.. warning:: 如果你只想验证 POST 数据,不要使用 ``withRequest()``。 + 此方法使用 :ref:`$request->getVar() ` + 它按照该顺序返回 ``$_GET``, ``$_POST`` 或 ``$_COOKIE`` 数据 + (取决于 php.ini `request-order `_)。 + 新的值会覆盖旧的值。如果它们具有相同的名称,POST 值可能会被 + Cookie 覆盖。 + 当验证从 HTTP 请求输入的数据时,你会最常使用验证库。如果需要的话,你可以传入当前请求对象的实例,它会获取所有输入数据并将其设置为要验证的数据: .. literalinclude:: validation/008.php @@ -424,7 +439,7 @@ withRequest() .. literalinclude:: validation/020.php :lines: 2- -.. note:: 从 v4.3.5 开始,你必须为占位符字段(``id``)设置验证规则。 +.. note:: 从 v4.3.5 开始,出于安全考虑,你必须为占位符字段(上面示例代码中的 ``id`` 字段)设置验证规则。 在这组规则中,它说明电子邮件地址在数据库中应该是唯一的,除了具有与占位符的值匹配的 id 的行。假设表单 POST 数据如下: @@ -448,7 +463,8 @@ withRequest() 验证库提供了几种方法来帮助你设置错误消息、提供自定义错误消息以及检索一个或多个错误以显示。 -默认情况下,错误消息来自 **system/Language/en/Validation.php** 中的语言字符串,其中每个规则都有一个条目。 +默认情况下,错误消息来源于 **system/Language/en/Validation.php** 中的语言字符串,其中每个规则都有一个条目。如果你想要更改消息默认值,创建一个文件 **app/Language/en/Validation.php** (使用本地化对应的文件夹,替代 ``en``) +并在其中放置那些不同于默认值的错误消息的 Key 和值。 .. _validation-custom-errors: @@ -501,7 +517,7 @@ withRequest() 如果没有错误,将返回一个空数组。 -当使用通配符时,错误将指向特定的字段,用适当的键替换通配符:: +当使用通配符 (``*``) 时,错误将指向特定的字段,用适当的键替换通配符:: // 对于数据 'contacts' => [ @@ -516,10 +532,10 @@ withRequest() ] // 规则 - 'contacts.*.name' => 'required' + 'contacts.friends.*.name' => 'required' // 错误将是 - 'contacts.friends.1.name' => 'contacts.*.name 字段是必需的。' + 'contacts.friends.1.name' => 'contacts.friends.*.name 字段是必需的。' 获取单个错误 ====================== @@ -631,7 +647,7 @@ PHP 请求之间不共享任何内容。所以在验证失败时重定向,重定 .. literalinclude:: validation/034.php -默认情况下,系统将在 **system/Language/en/Validation.php** 中查找错误消息中使用的语言字符串。在自定义规则中,你可以通过第二个参数引用 ``&$error`` 来提供错误消息: +默认情况下,系统会在 **system/Language/en/Validation.php** 中查找错误中使用的语言字符串。为了为你的自定义规则提供默认的错误消息,你可以将它们放在 **app/Language/en/Validation.php** 中(使用本地化对应的文件夹,替代 ``en``)。另外,如果你想使用其他语言字符串文件替代默认的 **Validation.php**,你可以通过接受一个 ``&$error`` 变量(作为第二个参数,或者,如果你的规则需要处理参数,如下所述 - 第四个参数)来提供错误消息: .. literalinclude:: validation/035.php @@ -700,11 +716,12 @@ PHP 请求之间不共享任何内容。所以在验证失败时重定向,重定 ======================= ========== ================================================================= =================================================== 规则 参数 描述 示例 ======================= ========== ================================================================= =================================================== -alpha 无 如果字段包含除字母字符之外的任何内容,则失败。 -alpha_space 无 如果字段包含空格和字母字符之外的任何内容,则失败。 -alpha_dash 无 如果字段包含除字母数字字符、下划线或破折号之外的任何内容,则失败。 -alpha_numeric 无 如果字段包含除字母数字字符之外的任何内容,则失败。 -alpha_numeric_space 无 如果字段包含除字母数字和空格字符之外的任何内容,则失败。 +alpha 无 如果字段包含除 ASCII 字母字符之外的任何内容,则失败。 +alpha_space 无 如果字段包含 ASCII 空格和字母字符之外的任何内容,则失败。 +alpha_dash 无 如果字段包含除 ASCII 字母数字字符、下划线或破折号之外的任何内容, + 则失败。 +alpha_numeric 无 如果字段包含除 ASCII 字母数字字符之外的任何内容,则失败。 +alpha_numeric_space 无 如果字段包含除 ASCII 字母数字和空格字符之外的任何内容,则失败。 alpha_numeric_punct 无 如果字段包含除字母数字、空格和这组有限标点之外的任何内容, 则失败:``~`` (波浪号)、 ``!`` (感叹号)、``#`` (数字)、 @@ -792,7 +809,10 @@ valid_cc_number 是 验证信用卡号是否与指定提供程 文件上传规则 ====================== -这些验证规则使你可以执行验证上传的文件以满足业务需求的基本检查。 +当你验证上传的文件时,必须使用专门为文件验证创建的规则。 + +.. important:: 只有下表中列出的规则可以用于验证文件。因此,如果在文件验证规则数组或字符串中添加任何通用规则,如 ``permit_empty``,文件验证将无法正确工作。 + 由于文件上传 HTML 字段的值不存在,而是存储在 ``$_FILES`` 全局变量中,所以需要两次使用输入字段的名称。一次是像其他规则一样指定字段名称,另一次是作为所有与文件上传相关规则的第一个参数:: // 在 HTML 中 @@ -820,6 +840,3 @@ is_image 是 如果根据 mime 类型无法确定文件 ======================= ========== ============================================================ =================================================== 文件验证规则适用于单个和多个文件上传。 - -.. note:: 仅可以使用专门为文件验证创建的规则(如上表中列出的规则)来验证文件。 - 因此,将任何通用规则(如 ``permit_empty``)添加到文件验证规则数组或字符串中,文件验证将无法正常工作。 diff --git a/source/libraries/validation/001.php b/source/libraries/validation/001.php index e797b21a..e3aad548 100644 --- a/source/libraries/validation/001.php +++ b/source/libraries/validation/001.php @@ -12,9 +12,13 @@ public function index() return view('signup'); } - $rules = []; + $rules = [ + // @TODO + ]; - if (! $this->validate($rules)) { + $data = $this->request->getPost(array_keys($rules)); + + if (! $this->validateData($data, $rules)) { return view('signup'); } diff --git a/source/libraries/validation/008.php b/source/libraries/validation/008.php index 38656dda..41e37f06 100644 --- a/source/libraries/validation/008.php +++ b/source/libraries/validation/008.php @@ -4,7 +4,8 @@ $request = \Config\Services::request(); if ($validation->withRequest($request)->run()) { - // If you want to get the validated data. + // If you use the input data, you should get it from the getValidated() method. + // Otherwise you may create a vulnerability. $validData = $validation->getValidated(); // ... diff --git a/source/libraries/validation/009.php b/source/libraries/validation/009.php index 7e321e96..85be52b7 100644 --- a/source/libraries/validation/009.php +++ b/source/libraries/validation/009.php @@ -4,7 +4,7 @@ * The data to test: * [ * 'contacts' => [ - * 'name' => 'Joe Smith', + * 'name' => 'Joe Smith', * 'friends' => [ * [ * 'name' => 'Fred Flinstone', @@ -21,8 +21,3 @@ $validation->setRules([ 'contacts.name' => 'required|max_length[60]', ]); - -// Fred Flintsone & Wilma -$validation->setRules([ - 'contacts.friends.name' => 'required|max_length[60]', -]); diff --git a/source/libraries/validation/010.php b/source/libraries/validation/010.php index 0951f582..2646fcc3 100644 --- a/source/libraries/validation/010.php +++ b/source/libraries/validation/010.php @@ -2,5 +2,5 @@ // Fred Flintsone & Wilma $validation->setRules([ - 'contacts.*.name' => 'required|max_length[60]', + 'contacts.friends.*.name' => 'required|max_length[60]', ]); diff --git a/source/models/entities.rst b/source/models/entities.rst index 87059827..fc141a79 100644 --- a/source/models/entities.rst +++ b/source/models/entities.rst @@ -2,7 +2,7 @@ 使用实体类 ##################### -CodeIgniter 在其数据库层全面支持实体类,同时保持它们的完全可选。它们通常用作存储库模式的一部分,但如果更符合你的需求,也可以直接与 :doc:`Model ` 一起使用。 +CodeIgniter 全面支持实体类,同时保持它们的完全可选。它们通常用作存储库模式的一部分,但如果更符合你的需求,也可以直接与 :doc:`Model ` 一起使用。 .. contents:: :local: @@ -12,7 +12,13 @@ CodeIgniter 在其数据库层全面支持实体类,同时保持它们的完全 实体用法 ************ -核心上,实体类仅仅是一个代表单个数据库行的类。它具有表示数据库列的类属性,并提供任何其他方法来实现该行的业务逻辑。但是,关键是它不知道如何持久化自己。这是模型或存储库类的责任。这样,如果你需要保存对象的方式发生了任何更改,你不需要更改整个应用程序中该对象的使用方式。这使得在快速原型阶段使用 JSON 或 XML 文件存储对象成为可能,然后在概念证明有效时轻松切换到数据库。 +本质上,实体类仅仅是一个代表单个数据库行的类。它具有表示数据库列的类属性,并提供任何其他方法来实现该行的业务逻辑。 + +.. note:: 为了便于理解,这里的解释是基于使用数据库的情况。然而,实体也可以用于不来自数据库的数据。 + +然而,关键是它不知道如何持久化自己。这是模型或存储库类的责任。这样,如果你需要保存对象的方式发生了任何更改,你不需要更改整个应用程序中该对象的使用方式。 + +这使得在快速原型阶段使用 JSON 或 XML 文件存储对象成为可能,然后在概念证明有效时轻松切换到数据库。 让我们来看一个非常简单的用户实体示例,并介绍如何使用它以使事情变得清楚。 @@ -53,7 +59,7 @@ CodeIgniter 在其数据库层全面支持实体类,同时保持它们的完全 你可能已经注意到, ``User`` 类还没有为列设置任何属性,但你仍然可以像它们是公共属性一样访问它们。基类 ``CodeIgniter\Entity\Entity`` 会替你处理这些,以及提供使用 ``isset()`` 检查属性,或 ``unset()`` 属性的能力,并跟踪对象创建或从数据库中提取后哪些列发生了更改。 -.. note:: 实体类在属性 ``$attributes`` 中存储数据。 +.. note:: 实体类在内部的类属性 ``$attributes`` 中存储数据。 当 User 传递给模型的 ``save()`` 方法时,它会自动读取属性并保存 ``$allowedFields`` 属性中列出的任何更改。它还知道是创建新行还是更新现有行。 diff --git a/source/models/entities/018.php b/source/models/entities/018.php index dc793bb8..2bb8750a 100644 --- a/source/models/entities/018.php +++ b/source/models/entities/018.php @@ -13,7 +13,7 @@ class MyEntity extends Entity // Bind the type to the handler protected $castHandlers = [ - 'base64' => \App\Entities\Cast\CastBase64::class, + 'base64' => Cast\CastBase64::class, ]; } diff --git a/source/models/model.rst b/source/models/model.rst index eb5b83a0..d9e31d23 100644 --- a/source/models/model.rst +++ b/source/models/model.rst @@ -90,9 +90,9 @@ $primaryKey $useAutoIncrement ----------------- -指定表是否使用 ``$primaryKey`` 的自动增量功能。如果设置为 ``false``,则你有责任为表中的每条记录提供主键值。当我们想实现 1:1 关系或在模型中使用 UUID 时,此功能可能很方便。默认值为 ``true``。 +指定表是否使用 `$primaryKey`_ 的自增功能。如果设置为 ``false``,则你有责任为表中的每条记录提供主键值。当我们想实现 1:1 关系或在模型中使用 UUID 时,此功能可能很方便。默认值为 ``true``。 -.. note:: 如果你将 ``$useAutoIncrement`` 设置为 ``false``,请确保在数据库中将主键设置为 ``unique``。这样可以确保模型的所有功能与以前一样工作。 +.. note:: 如果你将 `$useAutoIncrement`_ 设置为 ``false``,请确保在数据库中将主键设置为 ``unique``。这样可以确保模型的所有功能与以前一样工作。 $returnType ----------- @@ -106,7 +106,7 @@ $useSoftDeletes 如果为 true,那么任何 ``delete()`` 方法调用都会在数据库中设置 ``deleted_at``,而不是真正删除行。这可以在数据可能在其他地方被引用时保留数据,或者可以维护一个“回收站”,其中的对象可以恢复,或者即使只是保留它作为安全轨迹的一部分。如果为 true,则 **find*()** 方法只返回非已删除行,除非在调用 **find*()** 方法之前调用 ``withDeleted()`` 方法。 -这需要数据库中具有与模型的 ``$dateFormat`` 设置相应的数据类型的 DATETIME 或 INTEGER 字段。默认字段名称为 ``deleted_at``,但是可以通过使用 ``$deletedField`` 属性将其配置为你选择的任何名称。 +这需要数据库中具有与模型的 `$dateFormat`_ 设置相应的数据类型的 DATETIME 或 INTEGER 字段。默认字段名称为 ``deleted_at``,但是可以通过使用 `$deletedField`_ 属性将其配置为你选择的任何名称。 .. important:: ``deleted_at`` 字段必须可为空。 @@ -115,7 +115,16 @@ $allowedFields 当通过 ``save()``、``insert()`` 或 ``update()`` 方法设置时,此数组应更新可以设置的字段名称。这些字段名之外的任何字段都会被丢弃。这有助于防止只从表单获取输入并将其全部抛给模型,从而导致潜在的大规模分配漏洞。 -.. note:: ``$primaryKey`` 字段永远不应该是允许的字段。 +.. note:: `$primaryKey`_ 字段永远不应该是允许的字段。 + +$allowEmptyInserts +------------------ + +.. versionadded:: 4.3.0 + +是否允许插入空数据。默认值是 ``false``,意味着如果你试图插入空数据,将会抛出 "There is no data to insert." 的异常。 + +你也可以通过 :ref:`model-allow-empty-inserts` 方法来改变这个设置。 日期 ----- @@ -123,22 +132,22 @@ $allowedFields $useTimestamps ^^^^^^^^^^^^^^ -此布尔值确定是否会向所有插入和更新自动添加当前日期。如果为 true,将以 ``$dateFormat`` 中指定的格式设置当前时间。这需要表中具有数据类型适当的 **created_at**、**updated_at** 和 **deleted_at** 列。 +此布尔值确定是否会向所有插入和更新自动添加当前日期。如果为 ``true``,将以 `$dateFormat`_ 中指定的格式设置当前时间。这需要表中具有数据类型适当的 **created_at**、**updated_at** 和 **deleted_at** 列。也可参考 `$createdField`_, `$updatedField`_ 和 `$deletedField`_。 $dateFormat ^^^^^^^^^^^ -此值与 ``$useTimestamps`` 和 ``$useSoftDeletes`` 一起使用,以确保插入到数据库中的是正确类型的日期值。默认情况下,这会创建 DATETIME 值,但有效选项有: ``'datetime'``、 ``'date'`` 或 ``'int'`` (PHP 时间戳)。在缺少或无效的 **dateFormat** 情况下使用 **useSoftDeletes** 或 **useTimestamps** 会引发异常。 +此值与 `$useTimestamps`_ 和 `$useSoftDeletes`_ 一起使用,以确保插入到数据库中的是正确类型的日期值。默认情况下,这会创建 DATETIME 值,但有效选项有: ``'datetime'``、 ``'date'`` 或 ``'int'`` (PHP 时间戳)。在缺少或无效的 `$dateFormat`_ 情况下使用 `$useSoftDeletes`_ 或 `$useTimestamps`_ 会引发异常。 $createdField ^^^^^^^^^^^^^ -指定用于数据记录创建时间戳的数据库字段。如果留空则不更新它(即使启用了 ``$useTimestamps``)。 +指定用于数据记录创建时间戳的数据库字段。如果设置为空字符串 (``''``) 则不更新它(即使启用了 `$useTimestamps`_)。 $updatedField ^^^^^^^^^^^^^ -指定应该用于保持数据记录更新时间戳的数据库字段。如果留空则不更新它(即使启用了 ``$useTimestamps``)。 +指定应该用于保持数据记录更新时间戳的数据库字段。如果设置为空字符串 (``''``) 则不更新它(即使启用了 `$useTimestamps`_)。 $deletedField ^^^^^^^^^^^^^ @@ -182,24 +191,16 @@ $cleanValidationRules $allowCallbacks ^^^^^^^^^^^^^^^ -是否应使用下面定义的回调。 +是否应使用下面定义的回调。参考 :ref:`model-events`。 $beforeInsert ^^^^^^^^^^^^^ $afterInsert -^^^^^^^^^^^^^ -$beforeInsertBatch -^^^^^^^^^^^^^^^^^^ -$afterInsertBatch -^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^ $beforeUpdate ^^^^^^^^^^^^^ $afterUpdate ^^^^^^^^^^^^^ -$beforeUpdateBatch -^^^^^^^^^^^^^^^^^^ -$afterUpdateBatch -^^^^^^^^^^^^^^^^^ $beforeFind ^^^^^^^^^^^ $afterFind @@ -208,8 +209,16 @@ $beforeDelete ^^^^^^^^^^^^^ $afterDelete ^^^^^^^^^^^^ +$beforeInsertBatch +^^^^^^^^^^^^^^^^^^ +$afterInsertBatch +^^^^^^^^^^^^^^^^^ +$beforeUpdateBatch +^^^^^^^^^^^^^^^^^^ +$afterUpdateBatch +^^^^^^^^^^^^^^^^^ -这些数组允许你指定在属性名称中指定的时间回调方法。 +这些数组允许你指定在属性名称中指定的时间回调方法。参考 :ref:`model-events`。 使用数据 ***************** @@ -226,7 +235,7 @@ find() .. literalinclude:: model/006.php -该值以 ``$returnType`` 中指定的格式返回。 +该值以 `$returnType`_ 中指定的格式返回。 你可以通过传递主键值数组而不是一个来指定要返回多行。 @@ -269,7 +278,7 @@ first() withDeleted() ------------- -如果 ``$useSoftDeletes`` 为 true,则 **find*()** 方法不会返回任何 ``deleted_at IS NOT NULL`` 的行。 +如果 `$useSoftDeletes`_ 为 true,则 **find*()** 方法不会返回任何 ``deleted_at IS NOT NULL`` 的行。 要暂时覆盖此设置,可以在调用 **find*()** 方法之前使用 ``withDeleted()`` 方法。 .. literalinclude:: model/013.php @@ -290,7 +299,7 @@ insert() 第一个参数是一个关联数组,用于在数据库中创建新行数据。 如果传递对象而不是数组,它将尝试将其转换为数组。 -数组的键必须与 ``$table`` 中的列名匹配,而数组的值是要为该键保存的值。 +数组的键必须与 `$table`_ 中的列名匹配,而数组的值是要为该键保存的值。 可选的第二个参数为布尔类型,如果设置为 false,该方法将返回一个布尔值,表示查询的成功或失败。 @@ -309,17 +318,19 @@ allowEmptyInserts() .. literalinclude:: model/056.php +你也可以通过 `$allowEmptyInserts`_ 属性来改变这个设置。 + 你可以通过调用 ``allowEmptyInserts(false)`` 再次启用检查。 update() -------- -更新数据库中的现有记录。第一个参数是要更新的记录的 ``$primaryKey``。关联数组作为第二个参数传递给此方法。数组的键必须与 ``$table`` 中的列名匹配,而数组的值是要为该键保存的值: +更新数据库中的现有记录。第一个参数是要更新的记录的 `$primaryKey`_。关联数组作为第二个参数传递给此方法。数组的键必须与 `$table`_ 中的列名匹配,而数组的值是要为该键保存的值: .. literalinclude:: model/016.php .. important:: 从 v4.3.0 开始,如果它生成没有 WHERE 子句的 SQL 语句,此方法会抛出 ``DatabaseException``。 - 在以前的版本中,如果在没有指定 ``$primaryKey`` 的情况下调用它并生成没有 WHERE 子句的 SQL 语句,查询仍会执行,表中的所有记录都会被更新。 + 在以前的版本中,如果在没有指定 `$primaryKey`_ 的情况下调用它并生成没有 WHERE 子句的 SQL 语句,查询仍会执行,表中的所有记录都会被更新。 可以通过作为第一个参数传递主键数组来一次更新多条记录: @@ -364,7 +375,7 @@ delete() .. literalinclude:: model/023.php -如果模型的 ``$useSoftDeletes`` 值为 true,这将更新行以将 ``deleted_at`` 设置为当前日期和时间。你可以通过将第二个参数设置为 true 来强制永久删除。 +如果模型的 `$useSoftDeletes`_ 值为 true,这将更新行以将 ``deleted_at`` 设置为当前日期和时间。你可以通过将第二个参数设置为 true 来强制永久删除。 可以作为第一个参数传递主键数组,以一次删除多条记录: @@ -400,11 +411,11 @@ purgeDeleted() 设置验证规则 ------------------------ -第一步是用将应用的字段和规则填充 ``$validationRules`` 类属性。如果你有要使用的自定义错误消息,请将其放入 ``$validationMessages`` 数组中: +第一步是用将应用的字段和规则填充 `$validationRules`_ 类属性。如果你有要使用的自定义错误消息,请将其放入 `$validationMessages`_ 数组中: .. literalinclude:: model/027.php -如果你更喜欢在验证配置文件中组织你的规则和错误消息,你可以这样做,并将 ``$validationRules`` 设置为你创建的验证规则组的名称: +如果你更喜欢在验证配置文件中组织你的规则和错误消息,你可以这样做,并将 `$validationRules`_ 设置为你创建的验证规则组的名称: .. literalinclude:: model/034.php @@ -517,7 +528,7 @@ purgeDeleted() 保护字段 ================= -为了帮助防止大规模分配攻击,模型类 **要求** 你列出可以在插入和更新期间更改的所有字段名称在 ``$allowedFields`` 类属性中。除这些字段外提供的任何数据在插入数据库之前都将被删除。这对确保时间戳或主键不被更改非常有用。 +为了帮助防止大规模分配攻击,模型类 **要求** 你列出可以在插入和更新期间更改的所有字段名称在 `$allowedFields`_ 类属性中。除这些字段外提供的任何数据在插入数据库之前都将被删除。这对确保时间戳或主键不被更改非常有用。 .. literalinclude:: model/041.php @@ -528,7 +539,7 @@ purgeDeleted() 运行时返回类型更改 =========================== -你可以将 ``find*()`` 方法返回的数据格式指定为类属性 ``$returnType``。但是,有时你可能希望以不同的格式返回数据。模型提供了允许你做到这一点的方法。 +你可以将 ``find*()`` 方法返回的数据格式指定为类属性 `$returnType`_。但是,有时你可能希望以不同的格式返回数据。模型提供了允许你做到这一点的方法。 .. note:: 这些方法仅更改下一个 **find*()** 方法调用的返回类型。之后,它将重置为默认值。 @@ -568,7 +579,7 @@ CodeIgniter 模型对该模型的数据库连接有一个查询构建器实例 .. literalinclude:: model/043.php -此构建器已经使用模型的 ``$table`` 设置。 +此构建器已经使用模型的 `$table`_ 设置。 .. note:: 一旦你获取查询构建器实例,你就可以调用查询构建器的方法。 但是,由于查询构建器不是模型,你无法调用模型的方法。 @@ -603,29 +614,46 @@ CodeIgniter 模型对该模型的数据库连接有一个查询构建器实例 .. literalinclude:: model/046.php +.. _model-events: + 模型事件 ************ -在模型执行的几个点上,你可以指定多个回调方法来运行。这些方法可以用于规范化数据、散列密码、保存相关实体等等。可以影响以下模型执行点,每个都通过一个类属性:``$beforeInsert``、``$afterInsert``、``$beforeUpdate``、``$afterUpdate``、``$afterFind`` 和 ``$afterDelete``。 +在模型执行的几个点上,你可以指定多个回调方法来运行。这些方法可以用于规范化数据、散列密码、保存相关实体等等。 + +可以影响以下模型执行点,每个都通过一个类属性: + +- `$beforeInsert`_, `$afterInsert`_ +- `$beforeUpdate`_, `$afterUpdate`_ +- `$beforeFind`_, `$afterFind`_ +- `$beforeDelete`_, `$afterDelete`_ +- `$beforeInsertBatch`_, `$afterInsertBatch`_ +- `$beforeUpdateBatch`_, `$afterUpdateBatch`_ .. note:: ``$beforeInsertBatch``、``$afterInsertBatch``、``$beforeUpdateBatch`` 和 ``$afterUpdateBatch`` 可以从 v4.3.0 开始使用。 -定义回调 +定义回调函数 ================== -你通过在模型中首先创建要使用的新类方法来指定回调。此类将始终以 ``$data`` 数组作为其唯一参数接收。 ``$data`` 数组的确切内容将因事件而异,但将始终包含一个名为 **data** 的键,其中包含传递给原始方法的主要数据。对于 insert* 或 update* 方法,这将是要插入数据库的键/值对。主数组还将包含传递给方法的其他值,并在后面详细描述。回调方法必须返回原始的 $data 数组,以便其他回调具有完整的信息。 +你可以通过在模型中创建一个新的类方法来指定回调函数。 + +这个类方法总是会接收一个 ``$data`` 数组作为唯一的参数。 + +``$data`` 数组的具体内容会因事件而异,但总是会包含一个名为 ``data`` 的键,该键包含传递给原始方法的主要数据。在 **insert*()** 或 **update*()** 方法的情况下,这将是正在插入到数据库中的键/值对。主要的 ``$data`` 数组也会包含传递给方法的其他值,并在 `事件参数`_ 中详细说明。 + +回调方法必须返回原始的 ``$data`` 数组,以便其他回调函数获取完整信息。 .. literalinclude:: model/050.php 指定要运行的回调 =========================== -你可以通过将方法名称添加到适当的类属性(``$beforeInsert``、``$afterUpdate`` 等)来指定运行回调时机。可以向单个事件添加多个回调,并且它们将一个接一个地处理。你可以在多个事件中使用相同的回调: +你可以通过将方法名称添加到适当的类属性(`$beforeInsert`_, `$afterUpdate`_ 等)来指定运行回调时机。可以向单个事件添加多个回调,并且它们将一个接一个地处理。你可以在多个事件中使用相同的回调: .. literalinclude:: model/051.php -此外,每个模型可以通过设置其 ``$allowCallbacks`` 属性在类级别允许(默认)或拒绝回调。 +此外,每个模型可以通过设置其 `$allowCallbacks`_ 属性在类级别允许(默认)或拒绝回调。 .. literalinclude:: model/052.php @@ -641,34 +669,37 @@ CodeIgniter 模型对该模型的数据库连接有一个查询构建器实例 ================= ========================================================================================================= 事件 $data 内容 ================= ========================================================================================================= -beforeInsert **data** = 正在插入的键/值对。如果向 insert 方法传递对象或实体类,则首先转换为数组。 -afterInsert **id** = 新行的主键,如果失败则为 0。 +beforeInsert **data** = 正在插入的键/值对。如果将对象或 Entity 类传递给 + ``insert()`` 方法,它首先会被转换为数组。 +afterInsert **id** = 新行的主键,如果失败则为 0。 **data** = 正在插入的键/值对。 - **result** = 通过查询构建器使用的 insert() 方法的结果。 -beforeInsertBatch **data** = 正在插入的值的关联数组。如果向 insertBatch 方法传递对象或实体类,则首先转换为数组。 -afterInsertBatch **data** = 正在插入的值的关联数组。 - **result** = 通过查询构建器使用的 insertBatch() 方法的结果。 + **result** = 通过查询构建器使用的 ``insert()`` 方法的结果。 beforeUpdate **id** = 正在更新的行的主键数组。 - **data** = 正在更新的键/值对。如果向 update 方法传递对象或实体类,则首先转换为数组。 + **data** = 正在更新的键/值对。如果将对象或 Entity 类传递给 + ``update()`` 方法,它首先会被转换为数组。 afterUpdate **id** = 正在更新的行的主键数组。 **data** = 正在更新的键/值对。 - **result** = 通过查询构建器使用的 update() 方法的结果。 -beforeUpdateBatch **data** = 正在更新的值的关联数组。如果向 updateBatch 方法传递对象或实体类,则首先转换为数组。 -afterUpdateBatch **data** = 正在更新的键/值对。 - **result** = 通过查询构建器使用的 updateBatch() 方法的结果。 -beforeFind 调用的 **方法** 名称,是否请求了 **单例**,以及这些附加字段: -- first() 无附加字段 -- find() **id** = 正在搜索的行的主键。 -- findAll() **limit** = 要找到的行数。 - **offset** = 在搜索期间要跳过的行数。 -afterFind 与 **beforeFind** 相同,但包括结果行数据,如果未找到结果则为 null。 -beforeDelete 根据 delete* 方法而变化。参见以下内容: -- delete() **id** = 正在删除的行的主键。 - **purge** = 是否应该硬删除软删除的行的布尔值。 -afterDelete **id** = 正在删除的行的主键。 - **purge** = 是否应该硬删除软删除的行的布尔值。 - **result** = 在查询构建器上的 delete() 调用的结果。 + **result** = 通过查询构建器使用的 ``update()`` 方法的结果。 +beforeFind 调用 **method** 的名称,是否请求了 **singleton**,以及以下附加字段: +- ``first()`` 没有附加字段 +- ``find()`` **id** = 正在搜索的行的主键。 +- ``findAll()`` **limit** = 要查找的行数。 + **offset** = 在搜索过程中要跳过的行数。 +afterFind 与 **beforeFind** 相同,但包括找到的行的数据结果,如果没有找到结果则为 null。 +beforeDelete **id** = 被传递给 ``delete()`` 方法的行的主键。 + **purge** = 是否应硬删除软删除的行的布尔值。 +afterDelete **id** = 被传递给 ``delete()`` 方法的行的主键。 + **purge** = 是否应硬删除软删除的行的布尔值。 + **result** = 在查询构建器上调用 ``delete()`` 的结果。 **data** = 未使用。 +beforeInsertBatch **data** = 正在插入的值的关联数组。如果将对象或 Entity 类传递给 + ``insertBatch()`` 方法,它首先会被转换为数组。 +afterInsertBatch **data** = 正在插入的值的关联数组。 + **result** = 通过查询构建器使用的 ``insertBatch()`` 方法的结果。 +beforeUpdateBatch **data** = 正在更新的值的关联数组。如果将对象或 Entity 类传递给 + ``updateBatch()`` 方法,它首先会被转换为数组。 +afterUpdateBatch **data** = 正在更新的键/值对。 + **result** = 通过查询构建器使用的 ``updateBatch()`` 方法的结果。 ================= ========================================================================================================= 修改 Find* 数据 diff --git a/source/models/model/001.php b/source/models/model/001.php index 90cdb17d..a02d4689 100644 --- a/source/models/model/001.php +++ b/source/models/model/001.php @@ -8,7 +8,7 @@ // or $userModel = model('App\Models\UserModel'); // or -$userModel = model(App\Models\UserModel::class); +$userModel = model(\App\Models\UserModel::class); // Create a new class with the model() function. $userModel = model('UserModel', false); diff --git a/source/models/model/005.php b/source/models/model/005.php index c54aa69c..956870af 100644 --- a/source/models/model/005.php +++ b/source/models/model/005.php @@ -16,6 +16,8 @@ class UserModel extends Model protected $allowedFields = ['name', 'email']; + protected bool $allowEmptyInserts = false; + // Dates protected $useTimestamps = false; protected $dateFormat = 'datetime'; diff --git a/source/outgoing/csp.rst b/source/outgoing/csp.rst new file mode 100644 index 00000000..5765842e --- /dev/null +++ b/source/outgoing/csp.rst @@ -0,0 +1,106 @@ +.. _content-security-policy: + +####################### +内容安全策略 +####################### + +.. contents:: + :local: + :depth: 2 + +******************************** +什么是内容安全策略? +******************************** + +你可以采取的最好的防范 XSS 攻击的措施之一就是在网站上实施内容安全策略(CSP)。这要求你指定并授权在你的网站 HTML 中包含的每一个内容源,包括图片、样式表、JavaScript 文件等等。浏览器会拒绝来自未明确授权的源的内容。这个授权在响应的 ``Content-Security-Policy`` 头部中定义,并提供了各种配置选项。 + +这听起来很复杂,在某些网站上,确实可以是一项挑战。但是,对于许多简单的网站,其中所有的内容都由同一个域名(比如,**http://example.com**)提供,集成起来非常简单。 + +由于这是一个复杂的主题,这个用户指南不会详细介绍所有的细节。更多信息,你应该访问以下网站: + +* `内容安全策略主站 `_ +* `W3C 规范 `_ +* `HTML5Rocks 的介绍 `_ +* `SitePoint 的文章 `_ + +************** +开启 CSP +************** + +.. important:: :ref:`Debug Toolbar ` 可能会使用 Kint,它会输出内联脚本。因此,当 CSP 开启时,CSP nonce 会自动为 Debug Toolbar 输出。然而,如果你没有使用 CSP nonce,这将会改变你并未打算的 CSP 头部,它的行为将会与生产环境中的不同;如果你想验证 CSP 的行为,关闭 Debug Toolbar。 + +默认情况下,这项支持是关闭的。要在你的应用程序中启用支持,编辑 **app/Config/App.php** 中的 ``CSPEnabled`` 值: + +.. literalinclude:: csp/011.php + +当启用时,响应对象会包含一个 ``CodeIgniter\HTTP\ContentSecurityPolicy`` 的实例。**app/Config/ContentSecurityPolicy.php** 中设定的值会被应用到这个实例,如果在运行时不需要任何改变,那么正确格式化的头部就会被发送,你就完成了所有的工作。 + +在启用 CSP 后,两行头部行会被添加到 HTTP 响应:一个 **Content-Security-Policy** 头部,它的策略明确允许不同上下文的内容类型或源,和一个 **Content-Security-Policy-Report-Only** 头部,它识别将被允许但也会被报告给你选择的目标的内容类型或源。 + +我们的实现提供了一个默认的处理方法,可以通过 ``reportOnly()`` 方法进行更改。当一个额外的条目被添加到 CSP 指令,如下所示,它将被添加到适当的 CSP 头部以进行阻止或预防。这可以在每次调用的基础上被覆盖,通过向添加方法调用提供一个可选的第二个参数。 + +********************* +运行时配置 +********************* + +如果你的应用程序需要在运行时进行更改,你可以在你的控制器中通过 ``$this->response->getCSP()`` 访问实例。这个类包含了一些方法,这些方法与你需要设置的适当的头部值相当明显的映射。下面展示了一些例子,它们有不同的参数组合,尽管所有的都接受一个指令名或者它们的数组: + +.. literalinclude:: csp/012.php + +每个 "add" 方法的第一个参数是一个适当的字符串值,或者是它们的数组。 + +``reportOnly()`` 方法允许你为后续的源指定默认的报告处理,除非被覆盖。例如,你可以指定 youtube.com 是被允许的,然后提供几个被允许但被报告的源: + +.. literalinclude:: csp/013.php + +************** +内联内容 +************** + +有可能设置一个网站不保护其自身页面上的内联脚本和样式,因为这可能是用户生成内容的结果。为了防止这种情况,CSP 允许你在 `` + +.. warning:: 如果攻击者注入像 `` + + // 变为 + + + // 或者 + diff --git a/source/outgoing/csp/011.php b/source/outgoing/csp/011.php new file mode 100644 index 00000000..19310559 --- /dev/null +++ b/source/outgoing/csp/011.php @@ -0,0 +1,12 @@ +response->getCSP(); + +// specify the default directive treatment +$csp->reportOnly(false); + +// specify the origin to use if none provided for a directive +$csp->setDefaultSrc('cdn.example.com'); + +// specify the URL that "report-only" reports get sent to +$csp->setReportURI('http://example.com/csp/reports'); + +// specify that HTTP requests be upgraded to HTTPS +$csp->upgradeInsecureRequests(true); + +// add types or origins to CSP directives +// assuming that the default treatment is to block rather than just report +$csp->addBaseURI('example.com', true); // report only +$csp->addChildSrc('https://youtube.com'); // blocked +$csp->addConnectSrc('https://*.facebook.com', false); // blocked +$csp->addFontSrc('fonts.example.com'); +$csp->addFormAction('self'); +$csp->addFrameAncestor('none', true); // report this one +$csp->addImageSrc('cdn.example.com'); +$csp->addMediaSrc('cdn.example.com'); +$csp->addManifestSrc('cdn.example.com'); +$csp->addObjectSrc('cdn.example.com', false); // reject from here +$csp->addPluginType('application/pdf', false); // reject this media type +$csp->addScriptSrc('scripts.example.com', true); // allow but report requests from here +$csp->addStyleSrc('css.example.com'); +$csp->addSandbox(['allow-forms', 'allow-scripts']); diff --git a/source/outgoing/csp/013.php b/source/outgoing/csp/013.php new file mode 100644 index 00000000..a34b3460 --- /dev/null +++ b/source/outgoing/csp/013.php @@ -0,0 +1,9 @@ +response->getCSP(); + +$csp->addChildSrc('https://youtube.com'); // allowed +$csp->reportOnly(true); +$csp->addChildSrc('https://metube.com'); // allowed but reported +$csp->addChildSrc('https://ourtube.com', false); // allowed diff --git a/source/outgoing/index.rst b/source/outgoing/index.rst index 6a94d344..87c51d7a 100644 --- a/source/outgoing/index.rst +++ b/source/outgoing/index.rst @@ -16,5 +16,6 @@ table response api_responses + csp localization alternative_php diff --git a/source/outgoing/response.rst b/source/outgoing/response.rst index a1f4ac36..6fe8b188 100644 --- a/source/outgoing/response.rst +++ b/source/outgoing/response.rst @@ -8,10 +8,12 @@ Response 类通过只适合服务器对调用它的客户端做出响应的方 :local: :depth: 2 -使用响应 +处理响应 ========================= -一个 Response 类实例会为你实例化并传入控制器中。它可以通过 ``$this->response`` 访问。许多时候你不需要直接接触该类,因为 CodeIgniter 会为你发送 header 和 body。如果页面成功地创建了它被要求的内容,情况就是这样。当事情出错时,或者你需要发送非常具体的状态码回应,或者利用 HTTP 缓存的强大功能,它就为你提供了这些。 +一个响应类已经为你实例化并传入你的控制器。它可以通过 ``$this->response`` 访问。它和 ``Services::response()`` 返回的实例是同一个。我们称之为全局响应实例。 + +许多时候你不需要直接接触该类,因为 CodeIgniter 会为你发送 header 和 body。如果页面成功地创建了它被要求的内容,情况就是这样。当事情出错时,或者你需要发送非常具体的状态码回应,或者利用 HTTP 缓存的强大功能,它就为你提供了这些。 设置输出 ------------------ @@ -28,18 +30,33 @@ Response 类通过只适合服务器对调用它的客户端做出响应的方 .. literalinclude:: response/003.php -设置标题 +设置 Header --------------- -你经常需要为响应设置标题。Response 类使得这非常简单,通过 ``setHeader()`` 方法。第一个参数是标题的名称。第二个参数是值,可以是字符串或在发送到客户端时将正确组合的字符串数组。与使用原生 PHP 函数相比,使用这些函数可以确保标题不会过早发送,从而造成错误,并使测试成为可能。 +setHeader() +^^^^^^^^^^^ + +你经常需要为响应设置 Header。Response 类使得这非常简单,通过 ``setHeader()`` 方法。 + +第一个参数是 Header 的名称。第二个参数是值,可以是字符串或在发送到客户端时将正确组合的字符串数组。 .. literalinclude:: response/004.php -如果标题已经存在且可以有多个值,则可以使用 ``appendHeader()`` 和 ``prependHeader()`` 方法将值添加到值列表的末尾或开头。第一个参数是标题名称,第二个参数是要追加或前置的价值。 +与使用原生 PHP 函数相比,使用这些函数可以确保 Header 不会过早发送,从而造成错误,并使测试成为可能。 + +.. note:: 这个方法只是将头部设置到响应实例。所以,如果你创建并返回另一个响应实例(例如,如果你调用 :php:func:`redirect()`),这里设置的头部不会自动发送。 + +appendHeader() +^^^^^^^^^^^^^^ + +如果 Header 已经存在且可以有多个值,则可以使用 ``appendHeader()`` 和 ``prependHeader()`` 方法将值添加到值列表的末尾或开头。第一个参数是 Header 名称,第二个参数是要追加或前置的价值。 .. literalinclude:: response/005.php -可以使用 ``removeHeader()`` 方法从响应中删除标题,该方法仅将标题名称作为唯一参数。这不区分大小写。 +removeHeader() +^^^^^^^^^^^^^^ + +可以使用 ``removeHeader()`` 方法从响应中删除 Header,该方法仅将 Header 名称作为唯一参数。这不区分大小写。 .. literalinclude:: response/006.php @@ -48,8 +65,11 @@ Response 类通过只适合服务器对调用它的客户端做出响应的方 重定向 ======== -如果你想要创建一个重定向,请使用 :php:func:`redirect()` 函数。 -它将返回一个 ``RedirectResponse`` 实例。 +如果你想创建一个重定向,使用 :php:func:`redirect()` 函数。 + +它返回一个 ``RedirectResponse`` 实例。它是一个与 ``Services::response()`` 返回的全局响应实例不同的实例。 + +.. warning:: 如果你在调用 ``redirect()`` 之前设置了 Cookie 或响应头部,它们会被设置到全局响应实例,它们并不会自动复制到 ``RedirectResponse`` 实例。要发送它们,你需要手动调用 ``withCookies()`` 或 ``withHeaders()`` 方法。 .. important:: 如果你想要重定向,必须在 :doc:`Controller <../incoming/controllers>` 或 :doc:`Controller Filter <../incoming/filters>` 的方法中返回 ``RedirectResponse`` 实例。 @@ -94,6 +114,26 @@ Response 类通过只适合服务器对调用它的客户端做出响应的方 当 Session 可用时,它会将访问者带到“在 Session 期间查看的最后一页”。 如果没有加载 Session,或者 Session 不可用,那么将使用 HTTP_REFERER 的安全版本。 +带 Cookie 的重定向 +--------------------- + +如果你在调用 ``redirect()`` 之前设置了 Cookie,它们会被设置到全局响应实例,它们并不会自动复制到 ``RedirectResponse`` 实例。 + +要发送 Cookie,你需要手动调用 ``withCookies()`` 方法。 + +.. literalinclude:: ./response/034.php + :lines: 2- + +带 Header 的重定向 +--------------------- + +如果你在调用 ``redirect()`` 之前设置了响应头部,它们会被设置到全局响应实例,它们并不会自动复制到 ``RedirectResponse`` 实例。 + +要发送 Header,你需要手动调用 ``withHeaders()`` 方法。 + +.. literalinclude:: ./response/035.php + :lines: 2- + .. _response-redirect-status-code: 重定向状态码 @@ -165,97 +205,6 @@ HTTP 缓存 ``$options`` 数组简单地以 key/value 对的形式获取通常分配给 ``Cache-Control`` 头的数组。你可以自由地根据具体情况完全设置所需的所有选项。虽然大多数选项应用于 ``Cache-Control`` 头,但它也智能地处理 ``etag`` 和 ``last-modified`` 选项到适当的头。 -.. _content-security-policy: - -内容安全策略 -======================= - -防止站点遭受 XSS 攻击的最佳保护之一是在站点上实现内容安全策略。这会强制你列出站点 HTML 中拉入的每一个内容源,包括图像、样式表、javascript 文件等。浏览器将拒绝不符合白名单的内容源。这个白名单在响应的 ``Content-Security-Policy`` 头中创建,可以用多种不同的方式进行配置。 - -这听起来很复杂,在一些网站上,确实可能具有挑战性。但是,对于许多简单的网站来说,其中所有内容都由同一域服务(http://example.com), integrating 它非常简单。 - -由于这是一个复杂的主题,本用户指南不会详细介绍所有细节。欲了解更多信息,你应访问以下网站: - -* `Content Security Policy 主站点 `_ -* `W3C 规范 `_ -* `HTML5Rocks 入门 `_ -* `SitePoint 的文章 `_ - -打开 CSP --------------- - -.. important:: :ref:`Debug 工具栏 ` 可能使用 Kint,它 - 输出内联脚本。因此,打开 CSP 时,Debug 工具栏的 CSP nonce 将自动输出。 - 但是,如果你不使用 CSP nonce,这将改变 CSP 头以实现你不打算的方式, - 它的行为与生产环境不同;如果你想验证 CSP 的行为,请关闭 Debug 工具栏。 - -默认情况下,不支持此功能。要在应用程序中启用支持,请编辑 **app/Config/App.php** 中的 ``CSPEnabled`` 值: - -.. literalinclude:: response/011.php - -启用后,响应对象将包含 ``CodeIgniter\HTTP\ContentSecurityPolicy`` 的一个实例。 -**app/Config/ContentSecurityPolicy.php** 中设置的值将应用于该实例,如果运行时不需要更改,那么格式正确的头将被发送,你就完成了。 - -启用 CSP 后,会向 HTTP 响应添加两行头:一个是 **Content-Security-Policy** 头,其中包含策略以标识在不同上下文中明确允许的内容类型或来源;另一个是 **Content-Security-Policy-Report-Only** 头,它标识将被允许但也将报告给你选择的目标的内容类型或来源。 - -我们的实现提供了对默认处理的支持,可以通过 ``reportOnly()`` 方法更改。 -当向 CSP 指令添加额外条目时,如下所示,它将添加到适当的用于阻止或防止的 CSP 头中。这可以在每次调用的基础上通过提供可选的第二个参数来覆盖。 - -运行时配置 ---------------------- - -如果你的应用程序需要在运行时进行更改,你可以在控制器中通过 ``$this->response->getCSP()`` 访问实例。 -该类包含许多与适当的头值映射非常清楚的方法。示例如下,使用不同的组合参数,尽管所有这些“add”方法都接受指令名称或指令名称数组: - -.. literalinclude:: response/012.php - -每个“add”方法的第一个参数是一个适当的字符串值或值数组。 - -``reportOnly()`` 方法允许你为后续源指定默认报告处理,除非被覆盖。例如,你可以指定 youtube.com 被允许,然后提供几个允许但报告的源: - -.. literalinclude:: response/013.php - -内联内容 --------------- - -可以将网站设置为不保护自己页面上的内联脚本和样式,因为这可能是用户生成内容的结果。为了防止这种情况,CSP 允许你在 `` - -.. warning:: 如果攻击者注入类似 `` - - // 变为 - - - // 或者 - - 类参考 =============== @@ -402,7 +351,9 @@ HTTP 缓存 .. note:: 在 v4.2.7 之前版本,由于一个错误, ``$secure`` 和 ``$httponly`` 的默认值为 ``false``, 从未使用来自 **app/Config/Cookie.php** 的这些值。 - 使用你指定的值设置 cookie。有两种传递信息的方式以便可以设置 cookie:数组方法和离散参数: + 将包含你指定值的 Cookie 设置到响应实例。 + + 有两种传递信息的方式以便可以设置 Cookie: 数组方法和离散参数: **数组方法** @@ -445,6 +396,8 @@ HTTP 缓存 删除现有的 cookie。 + .. note:: 这也只是设置浏览器 cookie 以删除 cookie。 + 仅 ``name`` 是必需的。 仅当需要避免与服务器上其他同名 cookie 的名称冲突时才需要 ``prefix``。 diff --git a/source/outgoing/response/022.php b/source/outgoing/response/022.php index 0fd2fa47..85f2b3d6 100644 --- a/source/outgoing/response/022.php +++ b/source/outgoing/response/022.php @@ -1,4 +1,4 @@ setLastModified(date('D, d M Y H:i:s')); -$response->setLastModified(DateTime::createFromFormat('!U', $timestamp)); +$response->setLastModified(\DateTime::createFromFormat('!U', $timestamp)); diff --git a/source/outgoing/response/031.php b/source/outgoing/response/031.php index 28b73d6c..bd24e151 100644 --- a/source/outgoing/response/031.php +++ b/source/outgoing/response/031.php @@ -8,9 +8,3 @@ // Set a flash message. return redirect()->back()->with('foo', 'message'); - -// Copies all cookies from global response instance. -return redirect()->back()->withCookies(); - -// Copies all headers from the global response instance. -return redirect()->back()->withHeaders(); diff --git a/source/outgoing/response/034.php b/source/outgoing/response/034.php new file mode 100644 index 00000000..32c56afe --- /dev/null +++ b/source/outgoing/response/034.php @@ -0,0 +1,4 @@ +back()->withCookies(); diff --git a/source/outgoing/response/035.php b/source/outgoing/response/035.php new file mode 100644 index 00000000..3ac3578d --- /dev/null +++ b/source/outgoing/response/035.php @@ -0,0 +1,4 @@ +back()->withHeaders(); diff --git a/source/outgoing/view_cells.rst b/source/outgoing/view_cells.rst index 8a0935cf..78eaac1e 100644 --- a/source/outgoing/view_cells.rst +++ b/source/outgoing/view_cells.rst @@ -4,12 +4,20 @@ 许多应用程序都有一些小的视图片段,可以在页面之间重复使用,或者在页面的不同位置使用。这些通常是帮助框、导航控件、广告、登录表单等。CodeIgniter 允许你将这些呈现块的逻辑封装在视图单元中。它们基本上是可以包含在其他视图中的小视图。它们可以内置逻辑来处理任何特定于单元的显示逻辑。它们可以通过将每个单元的逻辑分离到自己的类中,使你的视图更易读和可维护。 -CodeIgniter 支持两种类型的视图单元:简单单元和受控单元。简单单元可以从你选择的任何类和方法生成,并且不必遵循任何规则,只需返回一个字符串即可。受控单元必须从扩展 ``Codeigniter\View\Cells\Cell`` 类的类生成,该类提供了额外的功能,使你的视图单元更加灵活和快速。 - .. contents:: :local: :depth: 2 +*************************** +简单和受控制的单元 +*************************** + +CodeIgniter 支持两种类型的视图单元:简单的和受控制的。 + +**简单的视图单元** 可以从你选择的任何类和方法生成,不必遵循任何规则,只需返回一个字符串。 + +**受控制的视图单元** 必须从扩展了 ``Codeigniter\View\Cells\Cell`` 类的类生成,这提供了额外的功能,使你的视图单元更加灵活和快速使用。 + .. _app-cells: ******************* @@ -18,11 +26,11 @@ CodeIgniter 支持两种类型的视图单元:简单单元和受控单元。 无论你使用哪种类型的视图单元,都可以使用 ``view_cell()`` 辅助函数从任何视图中调用它。 -第一个参数是要调用的类和方法的名称,第二个参数是要传递给方法的参数数组: +第一个参数是(1)*类和方法的名称*(简单单元)或(2)*类的名称和可选方法*(受控制的单元),第二个参数是要传递给该方法的参数数组或字符串: .. literalinclude:: view_cells/001.php -单元方法必须返回一个字符串,该字符串将插入到调用 ``view_cell()`` 函数的视图中。 +单元返回的字符串将被插入到调用 ``view_cell()`` 函数的视图中。 省略命名空间 ================== @@ -67,14 +75,18 @@ CodeIgniter 支持两种类型的视图单元:简单单元和受控单元。 .. versionadded:: 4.3.0 -受控单元有两个主要目标:尽可能快地构建单元,并为视图提供额外的逻辑和灵活性(如果需要)。该类必须扩展 ``CodeIgniter\View\Cells\Cell``。它们应该在同一文件夹中有一个视图文件。按照惯例,类名应为 PascalCase,后缀为 ``Cell``,视图应为类名的 snake_case 版本,不包括后缀。例如,如果你有一个 ``MyCell`` 类,视图文件应为 ``my.php``。 +受控单元有两个主要目标:(1) 尽可能快地构建单元,(2) 并为视图提供额外的逻辑和灵活性(如果需要)。 + +该类必须扩展 ``CodeIgniter\View\Cells\Cell``。它们应该在同一文件夹中有一个视图文件。按照惯例,类名应为 PascalCase,后缀为 ``Cell``,视图应为类名的 snake_case 版本,不包括后缀。例如,如果你有一个 ``MyCell`` 类,视图文件应为 ``my.php``。 .. note:: 在 v4.3.5 之前,生成的视图文件以 ``_cell.php`` 结尾。尽管 v4.3.5 及更高版本将生成不带 ``_cell`` 后缀的视图文件,但现有的视图文件仍将被定位和加载。 创建受控单元 ========================== -在类中实现的最基本的级别上,你只需要实现公共属性。这些属性将自动提供给视图文件。将上面的 AlertMessage 实现为受控单元将如下所示: +在类中实现的最基本的级别上,你只需要实现公共属性。这些属性将自动提供给视图文件。 + +将上面的 AlertMessage 实现为受控单元将如下所示: .. literalinclude:: view_cells/008.php @@ -103,7 +115,9 @@ CodeIgniter 支持两种类型的视图单元:简单单元和受控单元。 自定义渲染 ======================= -如果你需要更多控制HTML的渲染过程,可以实现一个 ``render()`` 方法。该方法允许你执行其他逻辑并向视图传递额外的数据(如果需要)。``render()`` 方法必须返回一个字符串。为了充分利用受控单元的全部功能,你应该使用 ``$this->view()`` 而不是普通的 ``view()`` 辅助函数: +如果你需要更多控制HTML的渲染过程,可以实现一个 ``render()`` 方法。该方法允许你执行其他逻辑并向视图传递额外的数据(如果需要)。``render()`` 方法必须返回一个字符串。 + +为了充分利用受控单元的全部功能,你应该使用 ``$this->view()`` 而不是普通的 ``view()`` 辅助函数: .. literalinclude:: view_cells/012.php diff --git a/source/outgoing/view_cells/001.php b/source/outgoing/view_cells/001.php index 2a9e4361..57a4fdc4 100644 --- a/source/outgoing/view_cells/001.php +++ b/source/outgoing/view_cells/001.php @@ -1,2 +1,7 @@ // In a View. + +// Simple Cell 'value1', 'param2' => 'value2']) ?> + +// Controlled Cell + 'value1', 'param2' => 'value2']) ?> diff --git a/source/outgoing/view_cells/014.php b/source/outgoing/view_cells/014.php index 29378e11..7aa1ad4c 100644 --- a/source/outgoing/view_cells/014.php +++ b/source/outgoing/view_cells/014.php @@ -12,7 +12,7 @@ class AlertMessageCell extends Cell protected $message; private $computed; - public function mount() + public function mount(): void { $this->computed = sprintf('%s - %s', $this->type, $this->message); } diff --git a/source/outgoing/view_cells/016.php b/source/outgoing/view_cells/016.php index 4b2c10ef..54d9d7d9 100644 --- a/source/outgoing/view_cells/016.php +++ b/source/outgoing/view_cells/016.php @@ -10,7 +10,7 @@ class RecentPostsCell extends Cell { protected $posts; - public function linkPost($post) + public function linkPost($post): string { return anchor('posts/' . $post->id, $post->title); } diff --git a/source/outgoing/view_cells/018.php b/source/outgoing/view_cells/018.php index 3f64e684..8767af1b 100644 --- a/source/outgoing/view_cells/018.php +++ b/source/outgoing/view_cells/018.php @@ -8,7 +8,7 @@ class RecentPostsCell extends Cell { protected $posts; - public function mount() + public function mount(): void { $this->posts = model('PostModel')->orderBy('created_at', 'DESC')->findAll(10); } diff --git a/source/outgoing/view_cells/019.php b/source/outgoing/view_cells/019.php index df78ab0a..db1b12b5 100644 --- a/source/outgoing/view_cells/019.php +++ b/source/outgoing/view_cells/019.php @@ -10,7 +10,7 @@ class RecentPostsCell extends Cell { protected $posts; - public function mount(?int $categoryId) + public function mount(?int $categoryId): void { $this->posts = model('PostModel') ->when( diff --git a/source/outgoing/view_parser.rst b/source/outgoing/view_parser.rst index afb89984..4d42b420 100644 --- a/source/outgoing/view_parser.rst +++ b/source/outgoing/view_parser.rst @@ -311,7 +311,7 @@ date 格式(Y-m-d) 与 PHP **date** 兼容的格式化字符 date_modify 要添加/减去的值 与 **strtotime** 兼容的字符串,用于修改日期, { v|date_modify(+1 day) } 如 ``+5 day`` 或 ``-1 week``。 -default 默认值 如果变量为空或未定义,显示默认值。 { v|default(just in case) } +default 默认值 如果变量为 `empty()`_,显示默认值。 { v|default(just in case) } esc html、attr、 指定转义数据的上下文。 { v|esc(attr) } css、js @@ -351,6 +351,8 @@ title 以“标题大小写”显示字符串,所 upper 将字符串显示为全部大写。 { v|upper } ================ ================= =========================================================== ====================================== +.. _empty(): https://www.php.net/manual/en/function.empty.php + 有关与“local_number”过滤器相关的详细信息,请参阅 `PHP 的 NumberFormatter `_。 自定义过滤器 diff --git a/source/testing/fabricator/003.php b/source/testing/fabricator/003.php index 5432bba5..6f03358f 100644 --- a/source/testing/fabricator/003.php +++ b/source/testing/fabricator/003.php @@ -1,5 +1,8 @@ 'firstName', 'email' => 'email', diff --git a/source/testing/fabricator/007.php b/source/testing/fabricator/007.php index 906a210e..ce0acdc4 100644 --- a/source/testing/fabricator/007.php +++ b/source/testing/fabricator/007.php @@ -1,3 +1,6 @@ make(); print_r($testUser); diff --git a/source/testing/fabricator/020.php b/source/testing/fabricator/020.php index 385646b2..784afa93 100644 --- a/source/testing/fabricator/020.php +++ b/source/testing/fabricator/020.php @@ -1,5 +1,7 @@ setOverrides(['name' => 'Gerry']); $user = $fabricator->create(); diff --git a/source/testing/overview.rst b/source/testing/overview.rst index 90a1a718..0ef08d20 100644 --- a/source/testing/overview.rst +++ b/source/testing/overview.rst @@ -103,6 +103,8 @@ Traits .. literalinclude:: overview/006.php +.. literalinclude:: overview/022.php + 其他断言 --------------------- @@ -228,6 +230,20 @@ Services::resetSingle(string $name) .. note:: 所有组件工厂在每个测试之间默认重置。如果需要实例持久化,请修改测试用例的 ``$setUpMethods``。 +测试和时间 +================ + +测试依赖于时间的代码可能会很有挑战性。然而,当使用 :doc:`Time <../libraries/time>` 类时,可以在测试期间随意固定或更改当前时间。 + +下面是一个固定当前时间的样本测试代码: + +.. literalinclude:: overview/021.php + +你可以使用 ``Time::setTestNow()`` 方法来固定当前时间。 +你还可以选择性地将地区设置为第二个参数。 + +不要忘记在测试后通过调用它而不带参数来重置当前时间。 + .. _testing-cli-output: 测试 CLI 输出 diff --git a/source/testing/overview/006.php b/source/testing/overview/006.php index 6193eb88..98a31481 100644 --- a/source/testing/overview/006.php +++ b/source/testing/overview/006.php @@ -1,5 +1,7 @@ log('error', "That's no moon"); $this->assertLogged('error', "That's no moon"); // check that a portion of the message is found in the logs -$exception = new RuntimeException('Hello world.'); +$exception = new \RuntimeException('Hello world.'); $logger->log('error', $exception->getTraceAsString()); $this->assertLogContains('error', '{main}'); diff --git a/source/testing/overview/008.php b/source/testing/overview/008.php index 5d69c01c..65995334 100644 --- a/source/testing/overview/008.php +++ b/source/testing/overview/008.php @@ -1,5 +1,7 @@ start('longjohn', strtotime('-11 minutes')); $this->assertCloseEnough(11 * 60, $timer->getElapsedTime('longjohn')); diff --git a/source/testing/overview/012.php b/source/testing/overview/012.php index 1c037338..af7f506d 100644 --- a/source/testing/overview/012.php +++ b/source/testing/overview/012.php @@ -1,5 +1,7 @@ start('longjohn', strtotime('-11 minutes')); $this->assertCloseEnoughString(11 * 60, $timer->getElapsedTime('longjohn')); diff --git a/source/testing/overview/013.php b/source/testing/overview/013.php index f228dcf2..bca1a851 100644 --- a/source/testing/overview/013.php +++ b/source/testing/overview/013.php @@ -1,5 +1,7 @@ assertSame('2023-11-25 12:00:00', (string) Time::now()); + } +} diff --git a/source/testing/overview/022.php b/source/testing/overview/022.php new file mode 100644 index 00000000..8d53304e --- /dev/null +++ b/source/testing/overview/022.php @@ -0,0 +1,13 @@ +` 对象 ``$this->request``。 + +我们从用户的 **POST** 数据中获取必要的项目,并将它们设置在 ``$data`` 变量中。 + 验证数据 ^^^^^^^^^^^^^^^^^ -你将使用控制器提供的辅助函数 :ref:`validate() ` 来验证提交的数据。 -在这种情况下,标题和正文字段是必填的,并且有特定的长度要求。 -CodeIgniter 提供了一个强大的验证库,如上所示。你可以阅读更多关于 :doc:`验证库 <../libraries/validation>` 的信息。 +接下来,你将使用由 Controller 提供的辅助函数 :ref:`validateData() ` 来验证提交的数据。在这种情况下,标题和正文字段是必需的,并且具有特定的长度。 + +如上所示,CodeIgniter 拥有一个强大的验证库。你可以阅读更多关于 :doc:`验证库 <../libraries/validation>` 的信息。 如果验证失败,我们调用刚刚创建的 ``new()`` 方法并返回 HTML 表单。 diff --git a/source/tutorial/create_news_items/005.php b/source/tutorial/create_news_items/005.php index 6d565789..bf3a3e2b 100644 --- a/source/tutorial/create_news_items/005.php +++ b/source/tutorial/create_news_items/005.php @@ -13,8 +13,10 @@ public function create() { helper('form'); + $data = $this->request->getPost(['title', 'body']); + // Checks whether the submitted data passed the validation rules. - if (! $this->validate([ + if (! $this->validateData($data, [ 'title' => 'required|max_length[255]|min_length[3]', 'body' => 'required|max_length[5000]|min_length[10]', ])) {