diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index 3ed4ee70f8ca..52e5e18635ae 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -23,43 +23,21 @@ BREAKING Behavior Changes ================ -- The static ``Boot::initializeConsole()`` method no longer handles the display of the console header. This is now handled within ``Console::run()``. - If you have overridden ``Boot::initializeConsole()``, you should remove any code related to displaying the console header, as this is now the responsibility of the ``Console`` class. -- **CLI:** Uncaught exceptions in ``spark``, and errors shown with ``BaseCommand::showError()`` or ``AbstractCommand::renderThrowable()`` (as ``migrate`` and - ``db:seed`` do), are now printed to STDERR instead of STDOUT. The exit code is unchanged. - See :ref:`upgrade-480-cli-error-output`. -- **CLI:** ``CLI::beep()`` now writes through the ``InputOutput`` transport instead of ``echo``, so it is discarded by ``callSilently()``, captured - by ``MockInputOutput``, and no longer seen by PHP output buffering on the command line. -- **CLI:** ``CLI::print()`` of text ending in a line feed, and a progress bar drawn with ``CLI::showProgress()``, no longer cause a blank line - before the next ``CLI::write()``, since the cursor already sits at the start of a line. ``CLI::init()`` now also resets that cursor state, - the progress bar, and the cached terminal dimensions. +- The static ``Boot::initializeConsole()`` method no longer handles the display of the console header. This is now handled within ``Console::run()``. If you have overridden ``Boot::initializeConsole()``, you should remove any code related to displaying the console header, as this is now the responsibility of the ``Console`` class. +- **CLI:** Uncaught exceptions in ``spark``, and errors shown with ``BaseCommand::showError()`` or ``AbstractCommand::renderThrowable()`` (as ``migrate`` and ``db:seed`` do), are now printed to STDERR instead of STDOUT. The exit code is unchanged. See :ref:`upgrade-480-cli-error-output`. +- **CLI:** ``CLI::beep()`` now writes through the ``InputOutput`` transport instead of ``echo``, so it is discarded by ``callSilently()``, captured by ``MockInputOutput``, and no longer seen by PHP output buffering on the command line. +- **CLI:** ``CLI::print()`` of text ending in a line feed, and a progress bar drawn with ``CLI::showProgress()``, no longer cause a blank line before the next ``CLI::write()``, since the cursor already sits at the start of a line. ``CLI::init()`` now also resets that cursor state, the progress bar, and the cached terminal dimensions. - **CLI:** ``CLI::showProgress()`` now draws a full bar at 100% for a step beyond the total, instead of throwing a ``ValueError``. -- **CLI:** ``CLI::error()`` no longer leaves colour support disabled for STDOUT when it is given an invalid colour name, and - ``CLI::write()``, ``CLI::print()``, and ``CLI::color()`` now accept an empty foreground with a background instead of throwing. -- **CLI:** An empty ``NO_COLOR`` no longer turns colour off, following https://no-color.org/. Colour detection now turns colour off when ``TERM`` is ``dumb``, and on Windows when the stream is not a terminal (outside MSYS2's - ``MINGW32`` and ``MINGW64``). On Windows without VT100 support, any ``TERM`` starting with ``xterm``, ``screen``, ``vt100``, ``vt220``, ``putty``, - ``rxvt``, ``ansi``, ``cygwin`` or ``linux``, or ending in ``-256color``, now enables colour, where previously only ``xterm`` did. - Colouring text that already contains a coloured segment now keeps that segment whatever its colour, where previously only the - dim colours (``\033[0;...m``) were kept and a bright or background-only segment lost the colour of the text after it. -- **CLI:** The output state moved out of the static ``CLI`` properties. A subclass of ``CLI`` that overrides ``fwrite()`` or ``color()``, or reads - ``$lastWrite``, ``$isColored``, ``$io``, ``$height``, ``$width``, or the colour tables, no longer affects or observes the output. - See :ref:`upgrade-480-cli-output-classes`. -- **CLI:** The prompt methods moved to ``Prompt``. A subclass of ``CLI`` that overrides ``validate()`` no longer affects the prompts, and - an override of ``prompt()`` no longer affects ``promptByKey()`` or ``promptByMultipleKeys()``. Passing ``prompt()`` options or rules, or a - ``promptByKey()`` text, of another type than ``string``, ``array``, or ``null`` now throws a ``TypeError`` instead of an ``InvalidArgumentException``. - See :ref:`upgrade-480-cli-prompt`. -- **Commands:** The ``-h`` option to ``routes`` command which was mapped previously to the ``--sort-by-handler`` option is now removed. - Use ``--sort-by-handler`` instead to sort the routes by handler when running the ``routes`` command. +- **CLI:** ``CLI::error()`` no longer leaves color support disabled for STDOUT when it is given an invalid color name, and ``CLI::write()``, ``CLI::print()``, and ``CLI::color()`` now accept an empty foreground with a background instead of throwing. +- **CLI:** An empty ``NO_COLOR`` no longer turns color off, following https://no-color.org/. Color detection now turns color off when ``TERM`` is ``dumb``, and on Windows when the stream is not a terminal (outside MSYS2's ``MINGW32`` and ``MINGW64``). On Windows without VT100 support, any ``TERM`` starting with ``xterm``, ``screen``, ``vt100``, ``vt220``, ``putty``, ``rxvt``, ``ansi``, ``cygwin`` or ``linux``, or ending in ``-256color``, now enables color, where previously only ``xterm`` did. Coloring text that already contains a colored segment now keeps that segment whatever its color, where previously only the dim colors (``\033[0;...m``) were kept and a bright or background-only segment lost the color of the text after it. +- **CLI:** The output state moved out of the static ``CLI`` properties. A subclass of ``CLI`` that overrides ``fwrite()`` or ``color()``, or reads ``$lastWrite``, ``$isColored``, ``$io``, ``$height``, ``$width``, or the color tables, no longer affects or observes the output. See :ref:`upgrade-480-cli-output-classes`. +- **CLI:** The prompt methods moved to ``Prompt``. A subclass of ``CLI`` that overrides ``validate()`` no longer affects the prompts, and an override of ``prompt()`` no longer affects ``promptByKey()`` or ``promptByMultipleKeys()``. Passing ``prompt()`` options or rules, or a ``promptByKey()`` text, of another type than ``string``, ``array``, or ``null`` now throws a ``TypeError`` instead of an ``InvalidArgumentException``. See :ref:`upgrade-480-cli-prompt`. +- **Commands:** The ``-h`` option of the ``routes`` command, which was previously mapped to ``--sort-by-handler``, is now removed. Use ``--sort-by-handler`` instead to sort the routes by handler when running the ``routes`` command. - **Commands:** The ``filter:check`` command now requires the HTTP method argument to be uppercase (e.g., ``spark filter:check GET /`` instead of ``spark filter:check get /``). -- **Commands:** Several built-in commands have been migrated from ``BaseCommand`` to the modern ``AbstractCommand`` style. Applications that extend a built-in command to override - behaviour may need to re-implement against the modern API (``configure()`` + ``execute()`` and the ``#[Command]`` attribute) once the class it extends is migrated, or, preferably, compose instead of extending. Invocations on the command line are unaffected. -- **Commands:** Generator commands migrated to ``AbstractGeneratorCommand`` now return ``EXIT_ERROR`` (previously ``EXIT_SUCCESS``) when the target file exists - without ``--force``, when the namespace is not defined, or when the name does not form a valid class name. The ``CodeIgniter`` namespace confirmation is no - longer prompted on non-interactive runs, and ``make:cell`` and ``make:model --return entity`` now ask it only once. -- **Commands:** The ``make:command`` command now generates modern commands: a ``basic`` command extends ``AbstractCommand`` with the ``#[Command]`` attribute, - and a ``generator`` command extends ``AbstractGeneratorCommand`` with the ``#[GeneratorCommand]`` attribute. -- **Commands:** The ``make:migration`` command now rejects ``--force`` as an unknown option. It was previously accepted but ignored, since the - timestamped file name only collides with an existing file when the same name is generated twice within one second. +- **Commands:** Several built-in commands have been migrated from ``BaseCommand`` to the modern ``AbstractCommand`` style. Applications that extend a built-in command to override behavior may need to re-implement against the modern API (``configure()`` + ``execute()`` and the ``#[Command]`` attribute) once the class it extends is migrated, or, preferably, compose instead of extending. Invocations on the command line are unaffected. +- **Commands:** Generator commands migrated to ``AbstractGeneratorCommand`` now return ``EXIT_ERROR`` (previously ``EXIT_SUCCESS``) when the target file exists without ``--force``, when the namespace is not defined, or when the name does not form a valid class name. The ``CodeIgniter`` namespace confirmation is no longer prompted on non-interactive runs, and ``make:cell`` and ``make:model --return entity`` now ask it only once. +- **Commands:** The ``make:command`` command now generates modern commands: a ``basic`` command extends ``AbstractCommand`` with the ``#[Command]`` attribute, and a ``generator`` command extends ``AbstractGeneratorCommand`` with the ``#[GeneratorCommand]`` attribute. +- **Commands:** The ``make:migration`` command now rejects ``--force`` as an unknown option. It was previously accepted but ignored, since the timestamped file name only collides with an existing file when the same name is generated twice within one second. - **Commands:** The success and error messages from ``debugbar:clear``, ``cache:clear``, and ``cache:info`` now include the affected path or cache driver/handler so the user can see which resource was acted on (or rejected). Scripts asserting on the prior literal text will need to be updated. - **Commands:** Declining the ``key:generate`` overwrite prompt interactively now returns ``EXIT_SUCCESS`` instead of ``EXIT_ERROR``. Output messages were also reworded; CI/automation that branches on the exit code or greps the previous wording will need updating. - **Commands:** The ``migrate:rollback`` command no longer accepts the undocumented ``-g`` (database group) option. It never had any effect, since ``MigrationRunner::regress()`` ignores the group, and the modern command pipeline now rejects unknown options. Remove ``-g`` from any ``migrate:rollback`` invocation. @@ -70,20 +48,15 @@ Behavior Changes - **Commands:** The ``phpini:check`` command's invalid-argument error now prints only ``You must specify a correct argument.``. The usage example and the argument listing moved to ``phpini:check --help``, where the argument is now named ``section``. Scripts that grep the previous output will need updating. - **Commands:** The prompts that pick from fixed options (``db:table``, ``db:create``, ``key:generate``, ``key:rotate``, ``make:command``, ``make:controller``, and ``make:model``) now use ``Prompt::select()``, so where the arrow-key menu is unavailable, as on Windows, the answer is the option's number instead of its value. Option values such as ``--prefix`` still take the value. - **Config:** ``CodeIgniter\Config\BaseService`` is now ``abstract``. It is intended to be extended (as ``Config\Services`` does) and accessed statically; code that instantiated it directly with ``new BaseService()`` will now throw an ``Error``. -- **Config:** ``BaseConfig`` now scans the environment once instead of checking every property against it, which makes building config classes - several times faster without config caching and in Worker Mode. As a result, a bare ``putenv()`` call (adding, changing, or removing a variable) made - after the first config class was built may not be seen until ``BaseConfig::reset()`` runs, including in later requests in Worker Mode. See :ref:`upgrade-480-config-env-lookups`. +- **Config:** ``BaseConfig`` now scans the environment once instead of checking every property against it, which makes building config classes several times faster without config caching and in Worker Mode. As a result, a bare ``putenv()`` call (adding, changing, or removing a variable) made after the first config class was built may not be seen until ``BaseConfig::reset()`` runs, including in later requests in Worker Mode. See :ref:`upgrade-480-config-env-lookups`. - **Database:** ``BaseBuilder::getCompiledSelect()``, ``getCompiledInsert()``, ``getCompiledUpdate()``, ``getCompiledDelete()``, ``delete()`` and ``countAllResults()`` now clear bindings and binding counters after compilation or execution when their reset parameter is enabled (the default). This prevents accumulation when a builder is reused, but ``getBinds()`` now returns an empty array after these calls. This also aligns ``getCompiledSelect()`` with its documented reset behavior, which should match ``get()``. Pass ``false`` for the reset parameter to retain the query and its bindings. Test mode retains bindings needed by its returned raw SQL. See :ref:`upgrade-480-query-builder-bindings`. - **Database:** The Postgre driver's ``$db->error()['code']`` previously always returned ``''``. It now returns the 5-character SQLSTATE string for query and transaction failures (e.g., ``'42P01'``), or ``'08006'`` for connection-level failures. Code that relied on ``$db->error()['code'] === ''`` will need updating. - **Debug:** ``CodeIgniter\Debug\Toolbar\Collectors\BaseCollector`` is now ``abstract``. Custom toolbar collectors must extend it; code that instantiated it directly with ``new BaseCollector()`` will now throw an ``Error``. -- **Filters:** HTTP method matching for method-based filters is now case-sensitive. The keys in ``Config\Filters::$methods`` must exactly match the request method - (e.g., ``GET``, ``POST``). Lowercase method names (e.g., ``post``) will no longer match. +- **Filters:** HTTP method matching for method-based filters is now case-sensitive. The keys in ``Config\Filters::$methods`` must exactly match the request method (e.g., ``GET``, ``POST``). Lowercase method names (e.g., ``post``) will no longer match. - **HTTP:** Routes defined with ``$routes->add()`` now also match HTTP ``QUERY`` requests. If the route has CSRF protection, remember that CSRF verification does not protect safe methods such as ``GET`` and ``QUERY``. - **HTTP:** ``CodeIgniter\HTTP\Exceptions\RedirectException`` no longer redeclares the inherited ``$code`` property. The status applied to a ``Response`` passed to the constructor whose status is outside the 301-308 range now comes from the new ``protected int $defaultStatusCode = 302`` property. Subclasses that overrode ``$code`` to change that status must override ``$defaultStatusCode`` instead. - **Router:** ``RouteCollection`` no longer applies ``esc(strip_tags())`` to namespace, controller, or method values, including controller options passed to ``resource()`` and ``presenter()``. Applications relying on this implicit transformation must preprocess these values before passing them to the router. -- **Testing:** Tests using the ``FeatureTestTrait`` must now use uppercase HTTP method names when performing a request when using the ``call()`` method directly - (e.g., ``$this->call('GET', '/path')`` instead of ``$this->call('get', '/path')``). Additionally, setting method-based routes using ``withRoutes()`` must - also use uppercase method names (e.g., ``$this->withRoutes([['GET', 'home', 'Home::index']])``). +- **Testing:** Tests using the ``FeatureTestTrait`` must now use uppercase HTTP method names when they call the ``call()`` method directly (e.g., ``$this->call('GET', '/path')`` instead of ``$this->call('get', '/path')``). Additionally, method-based routes set with ``withRoutes()`` must also use uppercase method names (e.g., ``$this->withRoutes([['GET', 'home', 'Home::index']])``). Interface Changes ================= @@ -107,10 +80,9 @@ Method Signature Changes - **CodeIgniter:** The deprecated parameters in methods have been removed: - ``CodeIgniter\CodeIgniter::handleRequest()`` no longer accepts the deprecated ``$cacheConfig`` and ``$returnResponse`` parameters. - ``$cacheConfig`` is no longer used and is now hard deprecated. A deprecation notice will be triggered if this is passed to the method. - - ``$returnResponse`` is now removed since already deprecated and unused. + - ``$returnResponse`` has been removed, since it was already deprecated and unused. - The updated method signature is now ``handleRequest(?RouteCollectionInterface $routes, ?Cache $cacheConfig = null)``. - - ``CodeIgniter\CodeIgniter::gatherOutput()`` no longer accepts the deprecated ``$cacheConfig`` parameter. - As this is the first parameter, custom uses of this method will need to be updated to remove the parameter. + - ``CodeIgniter\CodeIgniter::gatherOutput()`` no longer accepts the deprecated ``$cacheConfig`` parameter. As this is the first parameter, custom uses of this method will need to be updated to remove the parameter. - **Config:** ``CodeIgniter\Config\Services::request()`` no longer accepts any parameter. - **Database:** The following methods have had their signatures updated to remove deprecated parameters: - ``CodeIgniter\Database\Forge::_createTable()`` no longer accepts the deprecated ``$ifNotExists`` parameter. The method signature is now ``_createTable(string $table, array $attributes)``. @@ -146,13 +118,13 @@ Removed Deprecated Items - ``CodeIgniter\Cache\Handlers\FileHandler::deleteFile()`` (deprecated since v4.6.0) - ``CodeIgniter\Cache\Handlers\FileHandler::getDirFileInfo()`` (deprecated since v4.6.0) - ``CodeIgniter\Cache\Handlers\FileHandler::getFileInfo()`` (deprecated since v4.6.0) -- **CLI:** Removed the following properties and methods deprecated: +- **CLI:** Removed the following deprecated properties and methods: - ``CodeIgniter\CLI\BaseCommand::getPad()`` - ``CodeIgniter\CLI\CLI::$readline_support`` - ``CodeIgniter\CLI\CLI::$wait_msg`` - ``CodeIgniter\CLI\CLI::isWindows()`` - ``CodeIgniter\CLI\GeneratorTrait::execute()`` -- **CodeIgniter:** Removed the following properties and methods deprecated: +- **CodeIgniter:** Removed the following deprecated properties and methods: - ``CodeIgniter\CodeIgniter::$cacheTTL`` - ``CodeIgniter\CodeIgniter::$returnResponse`` - ``CodeIgniter\CodeIgniter::initializeKint()`` @@ -167,9 +139,9 @@ Removed Deprecated Items - ``CodeIgniter\CodeIgniter::determinePath()`` - ``CodeIgniter\CodeIgniter::callExit()`` - ``CodeIgniter\Test\MockCodeIgniter::callExit()`` -- **Config:** Removed the following property deprecated: +- **Config:** Removed the following deprecated property: - ``CodeIgniter\Config\BaseService::$services`` (deprecated since v4.5.0) -- **Database:** Removed the following properties and methods deprecated: +- **Database:** Removed the following deprecated properties and methods: - ``CodeIgniter\Database\BaseBuilder::setInsertBatch()`` - ``CodeIgniter\Database\BaseBuilder::setUpdateBatch()`` - ``CodeIgniter\Database\BaseBuilder::cleanClone()`` @@ -199,11 +171,11 @@ Removed Deprecated Items - ``CodeIgniter\HTTP\Exceptions\HTTPException::forInvalidSameSiteSetting()`` - ``CodeIgniter\Security\Exceptions\SecurityException::forInvalidSameSite()`` - ``CodeIgniter\Session\Exceptions\SessionException::forInvalidSameSiteSetting()`` -- **Filters:** Removed the following properties and methods deprecated: +- **Filters:** Removed the following deprecated properties and methods: - ``CodeIgniter\Filters\Filters::$arguments`` (deprecated since v4.6.0) - ``CodeIgniter\Filters\Filters::$argumentsClass`` (deprecated since v4.6.0) - ``CodeIgniter\Filters\Filters::getArguments()`` (deprecated since v4.6.0) -- **HTTP:** Removed the following properties and methods deprecated: +- **HTTP:** Removed the following deprecated properties and methods: - ``CodeIgniter\HTTP\Message::getHeaders()`` (deprecated since v4.0.5) - ``CodeIgniter\HTTP\Message::getHeader()`` (deprecated since v4.0.5) - ``CodeIgniter\HTTP\RequestTrait::getEnv()`` (deprecated since v4.4.4) @@ -215,7 +187,7 @@ Removed Deprecated Items - ``CodeIgniter\HTTP\SiteURI::$segments`` (deprecated since v4.4.0) - ``CodeIgniter\HTTP\SiteURI::setBaseURL()`` (deprecated since v4.4.0) - ``CodeIgniter\HTTP\SiteURI::setURI()`` (deprecated since v4.4.0) -- **Security:** Removed the following properties and methods deprecated: +- **Security:** Removed the following deprecated properties and methods: - ``CodeIgniter\Security\SecurityInterface::sanitizeFilename()`` (deprecated since v4.6.2) - ``CodeIgniter\Security\Security::sanitizeFilename()`` (deprecated since v4.6.2) - ``CodeIgniter\Security\Security::$csrfProtection`` (deprecated since v4.4.0) @@ -234,87 +206,45 @@ Enhancements API === -- Added per-type sparse fieldsets to API transformers. A transformer may declare a ``$type`` so clients - can request ``?fields[]=...`` to filter the fields of that resource type independently, including - for nested/related resources, while the flat ``?fields=...`` continues to apply to the root resource only. - See :ref:`api_transformers`. +- Added per-type sparse fieldsets to API transformers. A transformer may declare a ``$type`` so clients can request ``?fields[]=...`` to filter the fields of that resource type independently, including for nested/related resources, while the flat ``?fields=...`` continues to apply to the root resource only. See :ref:`api_transformers`. Commands ======== -- Added a new attribute-based command style built on :php:class:`AbstractCommand ` and the ``#[Command]`` attribute, - with ``configure()`` / ``isAvailable()`` / ``initialize()`` / ``interact()`` / ``execute()`` hooks and typed ``Argument`` / ``Option`` definitions. - The legacy ``BaseCommand`` style continues to work. See :doc:`../cli/cli_modern_commands`. -- Modern commands can now declare command aliases through an ``aliases`` list on the ``#[Command]`` attribute. Aliases resolve to the command - at dispatch (``php spark `` and ``help ``), are listed as their own rows in ``spark list``, and appear in an ``Aliases:`` section - of ``help ``. An alias that collides with an existing command name or another alias is rejected at discovery. See :doc:`../cli/cli_modern_commands`. -- Modern commands can now be hidden with ``hidden: true`` on the ``#[Command]`` attribute. A hidden command and its aliases are left out of - ``spark list`` and of the suggestions for a mistyped command name, but still run by exact name or alias and still have a ``help`` page. - ``AbstractCommand::isHidden()`` and ``Commands::isHiddenCommand()`` report the flag. See :ref:`hidden-commands`. -- Modern commands can now opt out of the spark header with ``headerless: true`` on the ``#[Command]`` attribute, with the same effect as always - passing ``--no-header``. ``help `` and `` --help`` still print the header. ``AbstractCommand::isHeaderless()`` and - ``Commands::isHeaderlessCommand()`` report the flag. See :ref:`commands-without-the-header`. -- Every modern command now ships with a ``--no-interaction`` / ``-N`` flag that skips the ``interact()`` hook, plus public - ``isInteractive()`` / ``setInteractive()`` methods on ``AbstractCommand``. ``isInteractive()`` also auto-detects piped or - CI environments by probing STDIN for a TTY, and the state cascades to sub-commands invoked via ``$this->call(...)``. - See :ref:`non-interactive-mode`. -- ``spark help `` now shows the default value of each option that accepts a value (e.g., ``[default: "file"]``), - the same way argument defaults are already displayed. Boolean flags and negatable toggles, which have no meaningful default to show, are unaffected. +- Added a new attribute-based command style built on :php:class:`AbstractCommand ` and the ``#[Command]`` attribute, with ``configure()`` / ``isAvailable()`` / ``initialize()`` / ``interact()`` / ``execute()`` hooks and typed ``Argument`` / ``Option`` definitions. The legacy ``BaseCommand`` style continues to work. See :doc:`../cli/cli_modern_commands`. +- Modern commands can now declare command aliases through an ``aliases`` list on the ``#[Command]`` attribute. Aliases resolve to the command at dispatch (``php spark `` and ``help ``), are listed as their own rows in ``spark list``, and appear in an ``Aliases:`` section of ``help ``. An alias that collides with an existing command name or another alias is rejected at discovery. See :doc:`../cli/cli_modern_commands`. +- Modern commands can now be hidden with ``hidden: true`` on the ``#[Command]`` attribute. A hidden command and its aliases are left out of ``spark list`` and of the suggestions for a mistyped command name, but still run by exact name or alias and still have a ``help`` page. ``AbstractCommand::isHidden()`` and ``Commands::isHiddenCommand()`` report the flag. See :ref:`hidden-commands`. +- Modern commands can now opt out of the spark header with ``headerless: true`` on the ``#[Command]`` attribute, with the same effect as always passing ``--no-header``. ``help `` and `` --help`` still print the header. ``AbstractCommand::isHeaderless()`` and ``Commands::isHeaderlessCommand()`` report the flag. See :ref:`commands-without-the-header`. +- Every modern command now ships with a ``--no-interaction`` / ``-N`` flag that skips the ``interact()`` hook, plus public ``isInteractive()`` / ``setInteractive()`` methods on ``AbstractCommand``. ``isInteractive()`` also auto-detects piped or CI environments by probing STDIN for a TTY, and the state cascades to sub-commands invoked via ``$this->call(...)``. See :ref:`non-interactive-mode`. +- ``spark help `` now shows the default value of each option that accepts a value (e.g., ``[default: "file"]``), the same way argument defaults are already displayed. Boolean flags and negatable toggles, which have no meaningful default to show, are unaffected. - You can now retrieve the last executed command in the console using the new ``Console::getCommand()`` method. This is useful for logging, debugging, or any situation where you need to know which command was run. -- ``CLI`` now supports the ``--`` separator to mean that what follows are arguments, not options. This allows you to have arguments that start with ``-`` without them being treated as options. - For example: ``spark my:command -- --myarg`` will pass ``--myarg`` as an argument instead of an option. -- ``CLI`` now supports options with values specified using an equals sign (e.g., ``--option=value``) in addition to the existing space-separated syntax (e.g., ``--option value``). - This provides more flexibility in how you can pass options to commands. -- ``CLI`` now supports parsing array options written multiple times (e.g., ``--option=value1 --option=value2``) into an array of values. This allows you to easily pass multiple values for the same option without needing to use a comma-separated string. - When used with ``CLI::getOption()``, an array option will return its last value (for example, in this case, ``value2``). To retrieve all values for an array option, use ``CLI::getRawOption()``. +- ``CLI`` now supports the ``--`` separator to mean that what follows are arguments, not options. This allows you to have arguments that start with ``-`` without them being treated as options. For example: ``spark my:command -- --myarg`` will pass ``--myarg`` as an argument instead of an option. +- ``CLI`` now supports options with values specified using an equals sign (e.g., ``--option=value``) in addition to the existing space-separated syntax (e.g., ``--option value``). This provides more flexibility in how you can pass options to commands. +- ``CLI`` now supports parsing array options written multiple times (e.g., ``--option=value1 --option=value2``) into an array of values. This allows you to easily pass multiple values for the same option without needing to use a comma-separated string. When used with ``CLI::getOption()``, an array option will return its last value (for example, in this case, ``value2``). To retrieve all values for an array option, use ``CLI::getRawOption()``. - Likewise, the ``command()`` function now also supports the above enhancements for command-line option parsing when using the function to run commands from code. - Added ``make:request`` generator command to scaffold :ref:`Form Request ` classes. - Added ``key:rotate`` command to demote the current ``encryption.key`` to ``encryption.previousKeys`` in **.env** and generate a new key. See :ref:`spark-key-rotate`. - Added ``AbstractCommand::callSilently()`` to invoke another command with its output discarded, restoring the prior output afterwards. See :ref:`modern-commands-call-silently`. -- The output side of the ``CLI`` class now lives in instance classes, with ``CLI`` delegating to them: :php:class:`Output ` - (``print()``, ``write()``, ``error()``, ``newLine()``, ``deferNewLine()``, ``beep()``, ``clearScreen()``, ``color()``, ``wrap()``), - :php:class:`Terminal ` (dimensions and colour support), :php:class:`ProgressBar `, - :php:class:`Table `, and :php:class:`Color ` (ANSI codes and ``Color::width()``). - ``CLI::getOutput()`` and ``CLI::getTerminal()`` return the shared instances. See :ref:`cli-library-classes`. -- Modern commands can write through ``$this->getOutput()``, the same ``Output`` used by the static ``CLI`` methods. ``AbstractCommand::run()`` - and ``Commands::runCommand()`` accept an ``Output`` to run against, which ``Console`` passes down and sub-commands inherit. - See :ref:`modern-commands-writing-output`. -- The prompt methods of ``CLI`` now delegate to :php:class:`Prompt ` (``ask()``, ``askByKey()``, - ``askByMultipleKeys()``), which reads through an ``Output`` and accepts the validation that checks the answers. - ``CLI::getPrompt()`` and ``AbstractCommand::getPrompt()`` return one on the shared output and on the output of the run. - See :ref:`cli-library-classes`. -- Added ``Prompt::select()`` and ``Prompt::multiselect()``, which pick options with the arrow keys and the space bar, and ask for - numbers where keys cannot be read. See :ref:`cli-library-select`. -- Added ``--silent``, ``--quiet`` / ``-q``, and ``--verbose`` / ``-v``, ``-vv``, ``-vvv`` to every command, with ``SHELL_VERBOSITY`` as the fallback. Modern - commands can write at a :php:class:`Verbosity ` through ``$this->getOutput()``. ``lang:find`` uses the global ``--verbose`` in place of its own. See :ref:`spark-verbosity` and :ref:`modern-commands-verbosity`. -- Colour detection now honours ``FORCE_COLOR`` (see https://force-color.org/), which turns colour on even when the output is piped, and ``COLORTERM``. - ``NO_COLOR`` still wins over both. The colour variables can also be set in the **.env** file. -- Added :php:class:`AbstractGeneratorCommand ` and the ``#[GeneratorCommand]`` attribute, the modern - counterpart of ``GeneratorTrait`` for commands that generate files from templates. Generator commands built on it accept the ``-n``, ``-s``, - and ``-f`` shortcuts for ``--namespace``, ``--suffix``, and ``--force``. See :doc:`../cli/cli_modern_generators`. -- Legacy ``BaseCommand::call()`` can now invoke modern commands: integer-keyed params are passed as arguments and string-keyed params as - options, which the target command validates like any other input. -- The ``make:controller`` command now accepts ``-b`` and ``-r`` as shortcuts for ``--bare`` and ``--restful``, and rejects an invalid - ``--restful`` value on non-interactive runs instead of prompting. -- The ``make:model`` command now accepts ``-t``, ``-g``, and ``-r`` as shortcuts for ``--table``, ``--dbgroup``, and ``--return``, and rejects an - invalid ``--return`` value on non-interactive runs instead of prompting. -- The ``make:command`` command now accepts ``-c``, ``-t``, and ``-g`` as shortcuts for ``--command``, ``--type``, and ``--group``, and rejects an - invalid ``--type`` value on non-interactive runs instead of prompting. +- The output side of the ``CLI`` class now lives in instance classes, with ``CLI`` delegating to them: :php:class:`Output ` (``print()``, ``write()``, ``error()``, ``newLine()``, ``deferNewLine()``, ``beep()``, ``clearScreen()``, ``color()``, ``wrap()``), :php:class:`Terminal ` (dimensions and color support), :php:class:`ProgressBar `, :php:class:`Table `, and :php:class:`Color ` (ANSI codes and ``Color::width()``). ``CLI::getOutput()`` and ``CLI::getTerminal()`` return the shared instances. See :ref:`cli-library-classes`. +- Modern commands can write through ``$this->getOutput()``, the same ``Output`` used by the static ``CLI`` methods. ``AbstractCommand::run()`` and ``Commands::runCommand()`` accept an ``Output`` to run against, which ``Console`` passes down and sub-commands inherit. See :ref:`modern-commands-writing-output`. +- The prompt methods of ``CLI`` now delegate to :php:class:`Prompt ` (``ask()``, ``askByKey()``, ``askByMultipleKeys()``), which reads through an ``Output`` and accepts the validation that checks the answers. ``CLI::getPrompt()`` and ``AbstractCommand::getPrompt()`` return one on the shared output and on the output of the run. See :ref:`cli-library-classes`. +- Added ``Prompt::select()`` and ``Prompt::multiselect()``, which pick options with the arrow keys and the space bar, and ask for numbers where keys cannot be read. See :ref:`cli-library-select`. +- Added ``--silent``, ``--quiet`` / ``-q``, and ``--verbose`` / ``-v``, ``-vv``, ``-vvv`` to every command, with ``SHELL_VERBOSITY`` as the fallback. Modern commands can write at a :php:class:`Verbosity ` through ``$this->getOutput()``. ``lang:find`` uses the global ``--verbose`` in place of its own. See :ref:`spark-verbosity` and :ref:`modern-commands-verbosity`. +- Color detection now honors ``FORCE_COLOR`` (see https://force-color.org/), which turns color on even when the output is piped, and ``COLORTERM``. ``NO_COLOR`` still wins over both. The color variables can also be set in the **.env** file. +- Added :php:class:`AbstractGeneratorCommand ` and the ``#[GeneratorCommand]`` attribute, the modern counterpart of ``GeneratorTrait`` for commands that generate files from templates. Generator commands built on it accept the ``-n``, ``-s``, and ``-f`` shortcuts for ``--namespace``, ``--suffix``, and ``--force``. See :doc:`../cli/cli_modern_generators`. +- Legacy ``BaseCommand::call()`` can now invoke modern commands: integer-keyed params are passed as arguments and string-keyed params as options, which the target command validates like any other input. +- The ``make:controller`` command now accepts ``-b`` and ``-r`` as shortcuts for ``--bare`` and ``--restful``, and rejects an invalid ``--restful`` value on non-interactive runs instead of prompting. +- The ``make:model`` command now accepts ``-t``, ``-g``, and ``-r`` as shortcuts for ``--table``, ``--dbgroup``, and ``--return``, and rejects an invalid ``--return`` value on non-interactive runs instead of prompting. +- The ``make:command`` command now accepts ``-c``, ``-t``, and ``-g`` as shortcuts for ``--command``, ``--type``, and ``--group``, and rejects an invalid ``--type`` value on non-interactive runs instead of prompting. - The ``make:migration`` command now accepts ``-t`` and ``-g`` as shortcuts for ``--table`` and ``--dbgroup``. -- The ``make:scaffold`` command now accepts ``-b``, ``-t``, and ``-g`` as shortcuts for ``--bare``, ``--table``, and ``--dbgroup``, and asks for a - missing class name once instead of once per generated file. -- **spark** now offers to run the closest matching command when a command name is mistyped on an interactive run. - See :ref:`correcting-a-mistyped-command`. -- Modern commands can now opt in to prompting for missing required arguments on interactive runs by implementing the new - ``PromptsForMissingInputInterface`` marker interface. Prompt labels can be customized via ``getArgumentPromptLabels()``, and an - ``afterPrompting()`` hook runs when prompting occurred. Non-interactive runs keep failing fast with the missing-arguments error. - See :ref:`prompts-for-missing-input`. +- The ``make:scaffold`` command now accepts ``-b``, ``-t``, and ``-g`` as shortcuts for ``--bare``, ``--table``, and ``--dbgroup``, and asks for a missing class name once instead of once per generated file. +- **spark** now offers to run the closest matching command when a command name is mistyped on an interactive run. See :ref:`correcting-a-mistyped-command`. +- Modern commands can now opt in to prompting for missing required arguments on interactive runs by implementing the new ``PromptsForMissingInputInterface`` marker interface. Prompt labels can be customized via ``getArgumentPromptLabels()``, and an ``afterPrompting()`` hook runs when prompting occurred. Non-interactive runs keep failing fast with the missing-arguments error. See :ref:`prompts-for-missing-input`. - The ``migrate``, ``migrate:rollback``, ``migrate:refresh``, and ``migrate:status`` commands now accept long option names (``--namespace``, ``--group``, ``--batch``, ``--force``) alongside their existing short forms (``-n``, ``-g``, ``-b``, ``-f``). - The ``namespaces`` command now accepts long option names (``--config-only``, ``--raw``, ``--max-length``) alongside its existing short forms (``-c``, ``-r``, ``-m``). - The ``worker:install`` and ``worker:uninstall`` commands now accept the ``-f`` short option alongside ``--force``. - Added :php:class:`NullInputOutput `, an :php:class:`InputOutput ` sink that discards all writes and returns an empty string from ``input()``. -- The ``spark routes`` command now resolves the Before/After Filters columns for routes that use custom placeholders registered via ``$routes->addPlaceholder()``. - Sample URIs for custom placeholders are derived from the placeholder regex when possible, and can be overridden through the new ``$placeholderSamples`` property in ``Config\Routing``. See :ref:`placeholder-samples-for-spark-routes`. +- The ``spark routes`` command now resolves the Before/After Filters columns for routes that use custom placeholders registered via ``$routes->addPlaceholder()``. Sample URIs for custom placeholders are derived from the placeholder regex when possible, and can be overridden through the new ``$placeholderSamples`` property in ``Config\Routing``. See :ref:`placeholder-samples-for-spark-routes`. Testing ======= @@ -379,8 +309,8 @@ Libraries ========= - **Cache:** Added support for TTL callables in the ``remember()`` method of cache handlers. This allows you to specify a callable that returns a TTL value, which can be useful for dynamic TTL. -- **Context**: This new feature allows you to easily set and retrieve normal or hidden contextual data for the current request. See :ref:`Context ` for details. -- **Images:**: Added support for the AVIF file format. +- **Context:** This new feature allows you to easily set and retrieve normal or hidden contextual data for the current request. See :ref:`Context ` for details. +- **Images:** Added support for the AVIF file format. - **Locks:** Added :doc:`Atomic Locks ` for owner-aware, cross-process mutual exclusion backed by supported cache handlers: **File**, **Redis**, **Predis**, and **Memcached**. Memcached support has driver-specific release limitations because Memcached has no atomic compare-and-delete command. - **Logging:** Log handlers now receive the full context array as a third argument to ``handle()``. When ``$logGlobalContext`` is enabled, the CI global context is available under the ``HandlerInterface::GLOBAL_CONTEXT_KEY`` key. Built-in handlers append it to the log output; custom handlers can use it for structured logging. - **Logging:** Added :ref:`per-call context logging ` with three new ``Config\Logger`` options (``$logContext``, ``$logContextTrace``, ``$logContextUsedKeys``). Per PSR-3, a ``Throwable`` in the ``exception`` context key is automatically normalized to a meaningful array. All options default to ``false``. @@ -390,16 +320,8 @@ Libraries Helpers and Functions ===================== -- :doc:`Array Helper ` gained five new dot-path functions: - :php:func:`dot_array_has()`, :php:func:`dot_array_set()`, :php:func:`dot_array_unset()`, - :php:func:`dot_array_only()`, and :php:func:`dot_array_except()`. -- :doc:`Array Helper ` dot-path read operations now support - object rows (including ``Entity``) in :php:func:`dot_array_search()`, - :php:func:`dot_array_has()`, :php:func:`dot_array_only()`, - :php:func:`dot_array_except()`, and :php:func:`array_group_by()`. - :php:func:`dot_array_only()` and :php:func:`dot_array_except()` still return arrays. If the source itself is an object, - it is read as an array-like value. Object values inside the source are kept unchanged when selected as a whole or left untouched. - Partial object paths are returned as arrays. +- :doc:`Array Helper ` gained five new dot-path functions: :php:func:`dot_array_has()`, :php:func:`dot_array_set()`, :php:func:`dot_array_unset()`, :php:func:`dot_array_only()`, and :php:func:`dot_array_except()`. +- :doc:`Array Helper ` dot-path read operations now support object rows (including ``Entity``) in :php:func:`dot_array_search()`, :php:func:`dot_array_has()`, :php:func:`dot_array_only()`, :php:func:`dot_array_except()`, and :php:func:`array_group_by()`. :php:func:`dot_array_only()` and :php:func:`dot_array_except()` still return arrays. If the source itself is an object, it is read as an array-like value. Object values inside the source are kept unchanged when selected as a whole or left untouched. Partial object paths are returned as arrays. HTTP ==== @@ -408,29 +330,17 @@ HTTP - Added the ``retry`` option to ``CURLRequest`` for retrying failed responses with configurable delays, retryable status codes, optional transient cURL error retries, and ``Retry-After`` support. See :ref:`curlrequest-request-options-retry`. - Added :ref:`Form Requests ` - a new ``FormRequest`` base class that encapsulates validation rules, custom error messages, and authorization logic for a single HTTP request. - Added ``IncomingRequest::input()`` to read GET, POST, JSON, and raw request data through ``InputData``. -- Added support for streaming responses. The ``StreamResponse`` class streams a body that is generated - while it is being sent, such as large dataset exports or AI/LLM token streams, and the ``SSEResponse`` - class extends it to stream Server-Sent Events (SSE) over HTTP. Both can be created via the new - ``$this->response->stream()`` and ``$this->response->eventStream()`` factory methods. - See :ref:`streaming-responses` and :ref:`server-sent-events`. -- ``Response`` and its child classes no longer require ``Config\App`` passed to their constructors. - Consequently, ``CURLRequest``'s ``$config`` parameter is unused and will be removed in a future release. -- ``Response`` now lazily loads the ``ContentSecurityPolicy``, ``Cookie``, and ``CookieStore`` classes only when used, - instead of instantiating them on every request in the constructor. Requests that do not touch CSP or cookies no longer - incur the cost of loading these classes. -- ``CLIRequest`` now supports the ``--`` separator to mean that what follows are arguments, not options. This allows you to have arguments that start with ``-`` without them being treated as options. - For example: ``php index.php command -- --myarg`` will pass ``--myarg`` as an argument instead of an option. -- ``CLIRequest`` now supports options with values specified using an equals sign (e.g., ``--option=value``) in addition to the existing space-separated syntax (e.g., ``--option value``). - This provides more flexibility in how you can pass options to CLI requests. +- Added support for streaming responses. The ``StreamResponse`` class streams a body that is generated while it is being sent, such as large dataset exports or AI/LLM token streams, and the ``SSEResponse`` class extends it to stream Server-Sent Events (SSE) over HTTP. Both can be created via the new ``$this->response->stream()`` and ``$this->response->eventStream()`` factory methods. See :ref:`streaming-responses` and :ref:`server-sent-events`. +- ``Response`` and its child classes no longer require ``Config\App`` to be passed to their constructors. Consequently, ``CURLRequest``'s ``$config`` parameter is unused and will be removed in a future release. +- ``Response`` now lazily loads the ``ContentSecurityPolicy``, ``Cookie``, and ``CookieStore`` classes only when used, instead of instantiating them on every request in the constructor. Requests that do not touch CSP or cookies no longer incur the cost of loading these classes. +- ``CLIRequest`` now supports the ``--`` separator to mean that what follows are arguments, not options. This allows you to have arguments that start with ``-`` without them being treated as options. For example: ``php index.php command -- --myarg`` will pass ``--myarg`` as an argument instead of an option. +- ``CLIRequest`` now supports options with values specified using an equals sign (e.g., ``--option=value``) in addition to the existing space-separated syntax (e.g., ``--option value``). This provides more flexibility in how you can pass options to CLI requests. - Added ``enable()`` and ``disable()`` methods to ``ContentSecurityPolicy`` to enable or disable the CSP header for the current request. See :ref:`csp-enable-disable`. -- Added ``$enableStyleNonce`` and ``$enableScriptNonce`` options to ``Config\App`` to automatically add nonces to control whether to add nonces to style-* and script-* directives in the Content Security Policy (CSP) header when CSP is enabled. See :ref:`csp-control-nonce-generation` for details. +- Added ``$enableStyleNonce`` and ``$enableScriptNonce`` options to ``Config\App`` to control whether nonces are automatically added to the ``style-*`` and ``script-*`` directives in the Content Security Policy (CSP) header when CSP is enabled. See :ref:`csp-control-nonce-generation` for details. - Added ``URI::withQuery()``, ``URI::withQueryArray()``, ``URI::withoutQueryVars()``, and ``URI::withOnlyQueryVars()`` to return cloned URIs with replaced or filtered query variables. - Added ``URI::withQueryVar()`` and ``URI::withQueryVars()`` to return a cloned URI with query variables added or replaced. -- ``URI`` now accepts an optional boolean second parameter in the constructor, defaulting to ``false``, to control how the query string is parsed in instantiation. - This is the behavior of ``->useRawQueryString()`` brought into the constructor for convenience. Previously, you need to call ``$uri->useRawQueryString(true)->setURI($uri)`` to get this behavior. - Now you can simply do ``new URI($uri, true)``. -- ``CLIRequest`` now supports parsing array options written multiple times (e.g., ``--option=value1 --option=value2``) into an array of values. This allows you to easily pass multiple values for the same option without needing to use a comma-separated string. - When used with ``CLIRequest::getOption()``, an array option will return its last value (for example, in this case, ``value2``). To retrieve all values for an array option, use ``CLIRequest::getRawOption()``. +- ``URI`` now accepts an optional boolean second parameter in the constructor, defaulting to ``false``, to control how the query string is parsed on instantiation. This is the behavior of ``->useRawQueryString()`` brought into the constructor for convenience. Previously, you had to call ``$uri->useRawQueryString(true)->setURI($uri)`` to get this behavior. Now you can simply do ``new URI($uri, true)``. +- ``CLIRequest`` now supports parsing array options written multiple times (e.g., ``--option=value1 --option=value2``) into an array of values. This allows you to easily pass multiple values for the same option without needing to use a comma-separated string. When used with ``CLIRequest::getOption()``, an array option will return its last value (for example, in this case, ``value2``). To retrieve all values for an array option, use ``CLIRequest::getRawOption()``. Validation ========== @@ -444,10 +354,9 @@ Others - **Config:** Added optional ``CodeIgniter\Config\Merge`` directives for :ref:`Registrars ` to control replacing, deep merging, and ordered list additions. Existing registrar merge behavior is unchanged. See :ref:`registrar-merge-directives`. - **Float and Double Casting:** Added support for precision and rounding mode when casting to float or double in entities. - Added ``CodeIgniter\Input\InputData``, ``ValidatedInput``, and ``InputDataFactory`` for reusable typed input data objects. -- Float and Double casting now throws ``CastException::forInvalidFloatRoundingMode()`` if an rounding mode other than up, down, even or odd is provided. +- Float and Double casting now throws ``CastException::forInvalidFloatRoundingMode()`` if a rounding mode other than up, down, even, or odd is provided. - **Filters:** Added ``RequestId`` filter for request tracing and correlation logging. The filter stores the request ID in the request context and automatically adds the ``X-Request-ID`` response header. Incoming ``X-Request-ID`` headers are used when valid. See :ref:`requestid` for details. -- **Environment:** Added ``CodeIgniter\EnvironmentDetector`` class and corresponding ``environment`` service as a mockable wrapper around the ``ENVIRONMENT`` constant. - Framework internals that previously compared ``ENVIRONMENT`` directly now go through this service, making environment-specific branches reachable in tests via ``Services::injectMock()``. See :ref:`environment-detector-service`. +- **Environment:** Added ``CodeIgniter\EnvironmentDetector`` class and corresponding ``environment`` service as a mockable wrapper around the ``ENVIRONMENT`` constant. Framework internals that previously compared ``ENVIRONMENT`` directly now go through this service, making environment-specific branches reachable in tests via ``Services::injectMock()``. See :ref:`environment-detector-service`. - **Time:** Added ``Time::between()``, ``Time::min()``, and ``Time::max()`` comparison helpers. See :ref:`between `, :ref:`min `, and :ref:`max `. *************** @@ -488,13 +397,10 @@ Deprecations - **CLI:** ``BaseCommand`` is now deprecated in favor of ``AbstractCommand``. Running a command that extends it triggers a deprecation notice. See :doc:`../cli/cli_modern_commands`. - **CLI:** ``GeneratorTrait`` is now deprecated in favor of ``AbstractGeneratorCommand``. See :doc:`../cli/cli_modern_generators`. - **CLI:** The ``CLI.generator.viewName.cell`` language string is deprecated. It was never displayed, since ``make:cell`` only prompts for the class name. -- **CLI:** The ``CLI::$foreground_colors``, ``CLI::$background_colors``, ``CLI::$lastWrite``, ``CLI::$height``, ``CLI::$width``, ``CLI::$isColored``, - ``CLI::$io``, and ``CLI::$stdoutToStderr`` properties are deprecated and no longer used. Their state lives in ``Color``, ``Output``, and - ``Terminal``. See :ref:`upgrade-480-cli-output-classes`. -- **CLI:** ``CLI::validate()`` is deprecated and no longer used by the prompts. Calling it triggers a deprecation notice. Override ``Prompt::validate()`` instead. - See :ref:`upgrade-480-cli-prompt`. +- **CLI:** The ``CLI::$foreground_colors``, ``CLI::$background_colors``, ``CLI::$lastWrite``, ``CLI::$height``, ``CLI::$width``, ``CLI::$isColored``, ``CLI::$io``, and ``CLI::$stdoutToStderr`` properties are deprecated and no longer used. Their state lives in ``Color``, ``Output``, and ``Terminal``. See :ref:`upgrade-480-cli-output-classes`. +- **CLI:** ``CLI::validate()`` is deprecated and no longer used by the prompts. Calling it triggers a deprecation notice. Override ``Prompt::validate()`` instead. See :ref:`upgrade-480-cli-prompt`. - **HTTP:** The ``CLIRequest::parseCommand()`` method is now deprecated and will be removed in a future release. The ``CLIRequest`` class now uses the new ``CommandLineParser`` class to handle command-line argument parsing. -- **HTTP:** ``URI::setSilent()`` is now hard deprecated. This method was only previously marked as deprecated. It will now trigger a deprecation notice when used. +- **HTTP:** ``URI::setSilent()`` is now hard deprecated. This method was previously only marked as deprecated. It will now trigger a deprecation notice when used. ********** Bugs Fixed diff --git a/user_guide_src/source/cli/cli_commands.rst b/user_guide_src/source/cli/cli_commands.rst index 8c954dbcd43d..1e887d6d5bd1 100644 --- a/user_guide_src/source/cli/cli_commands.rst +++ b/user_guide_src/source/cli/cli_commands.rst @@ -10,9 +10,8 @@ building out custom code generators for your company. .. deprecated:: 4.8.0 - ``BaseCommand`` is deprecated, and running a command that extends it triggers a - deprecation notice. Write new commands against ``AbstractCommand`` instead. - See :doc:`cli_modern_commands`. + ``BaseCommand`` is deprecated, and running a command that extends it triggers a deprecation notice. Write new + commands against ``AbstractCommand`` instead. See :doc:`cli_modern_commands`. .. contents:: :local: diff --git a/user_guide_src/source/cli/cli_generators.rst b/user_guide_src/source/cli/cli_generators.rst index 34d6ace45da9..a59090ff38c5 100644 --- a/user_guide_src/source/cli/cli_generators.rst +++ b/user_guide_src/source/cli/cli_generators.rst @@ -85,14 +85,17 @@ Argument: Options: ======== * ``--command`` (``-c``): The command name to run in spark. Defaults to ``command:name``. -* ``--type`` (``-t``): The type of command, whether a ``basic`` command or a ``generator`` command. Defaults to ``basic``. -* ``--group`` (``-g``): The group of the command. Defaults to ``App`` for basic commands, and ``Generators`` for generator commands. +* ``--type`` (``-t``): The type of command, whether a ``basic`` command or a ``generator`` command. Defaults to + ``basic``. +* ``--group`` (``-g``): The group of the command. Defaults to ``App`` for basic commands, and ``Generators`` for + generator commands. * ``--namespace`` (``-n``): Set the root namespace. Defaults to value of ``APP_NAMESPACE``. * ``--suffix`` (``-s``): Append the component suffix to the generated class name. * ``--force`` (``-f``): Set this flag to overwrite existing files on destination. -.. note:: Since v4.8.0, the generated class is a :doc:`modern command `: a ``basic`` command extends - ``AbstractCommand``, and a ``generator`` command extends :doc:`AbstractGeneratorCommand `. +.. note:: Since v4.8.0, the generated class is a :doc:`modern command `: a ``basic`` command + extends ``AbstractCommand``, and a ``generator`` command extends + :doc:`AbstractGeneratorCommand `. make:config ----------- @@ -133,7 +136,8 @@ Argument: Options: ======== * ``--bare`` (``-b``): Extends from ``CodeIgniter\Controller`` instead of ``BaseController``. -* ``--restful`` (``-r``): Extends from a RESTful resource. Choices are ``controller`` and ``presenter``. Defaults to ``controller``. +* ``--restful`` (``-r``): Extends from a RESTful resource. Choices are ``controller`` and ``presenter``. Defaults to + ``controller``. * ``--namespace`` (``-n``): Set the root namespace. Defaults to value of ``APP_NAMESPACE``. * ``--suffix`` (``-s``): Append the component suffix to the generated class name. * ``--force`` (``-f``): Set this flag to overwrite existing files on destination. @@ -377,8 +381,8 @@ To include an ``Entity`` class in the scaffolded files, just include the ``--ret and it will be passed to the model generator. .. note:: Since v4.8.0, ``make:scaffold`` accepts the ``-b``, ``-t``, ``-g``, ``-n``, ``-s``, and ``-f`` shortcuts. - ``--restful`` and ``--return`` have no shortcut here, since both use ``-r`` in their own commands. - When the class name is omitted on an interactive run, it is asked for once and reused for every generated file. + ``--restful`` and ``--return`` have no shortcut here, since both use ``-r`` in their own commands. When the class + name is omitted on an interactive run, it is asked for once and reused for every generated file. ************** GeneratorTrait @@ -386,9 +390,8 @@ GeneratorTrait .. deprecated:: 4.8.0 - ``GeneratorTrait`` is the legacy way of building generator commands, tied to the deprecated - ``BaseCommand``. New generator commands should extend ``AbstractGeneratorCommand`` instead. - See :doc:`cli_modern_generators`. + ``GeneratorTrait`` is the legacy way of building generator commands, tied to the deprecated ``BaseCommand``. New + generator commands should extend ``AbstractGeneratorCommand`` instead. See :doc:`cli_modern_generators`. Legacy generator commands use the ``GeneratorTrait`` to fully utilize its methods that are used in code generation. diff --git a/user_guide_src/source/cli/cli_library.rst b/user_guide_src/source/cli/cli_library.rst index 13d5aa2e70a0..2f3dd52b1342 100644 --- a/user_guide_src/source/cli/cli_library.rst +++ b/user_guide_src/source/cli/cli_library.rst @@ -24,8 +24,8 @@ need to ensure your controller can locate it via a ``use`` statement above your The class is automatically initialized when the file is loaded the first time. -Since v4.8.0 the static methods delegate to instance classes, which you can also use directly. -See :ref:`cli-library-classes`. +Since v4.8.0 the static methods delegate to instance classes, which you can also use directly. See +:ref:`cli-library-classes`. Getting Input from the User *************************** @@ -95,16 +95,15 @@ select() and multiselect() .. versionadded:: 4.8.0 -``select()`` and ``multiselect()`` on :php:class:`Prompt` let the user pick with the arrow keys -(or ``j`` and ``k``), tick with the space bar, tick everything or nothing with ``a``, and confirm -with enter. ``select()`` returns the chosen key, and ``multiselect()`` returns the ticked options, -keyed as in ``$options``. Both take any keys, and long lists scroll: +``select()`` and ``multiselect()`` on :php:class:`Prompt` let the user pick with the arrow keys (or ``j`` and ``k``), +tick with the space bar, tick everything or nothing with ``a``, and confirm with enter. ``select()`` returns the chosen +key, and ``multiselect()`` returns the ticked options, keyed as in ``$options``. Both take any keys, and long lists +scroll: .. literalinclude:: cli_library/026.php -Where keys cannot be read one by one, such as on Windows or when the input is piped, both prompts -list the options by number and ask for the number, or numbers separated by commas. The filters -question above then reads: +Where keys cannot be read one by one, such as on Windows or when the input is piped, both prompts list the options by +number and ask for the number, or numbers separated by commas. The filters question above then reads: .. code-block:: console @@ -116,9 +115,9 @@ question above then reads: Under WSL, PHP runs as on Linux and reads the keys. -On a quiet output (see :ref:`spark-verbosity`) both prompts return the defaults without asking, -and a required ``multiselect()`` without a default throws a ``CLIException``. They also throw it -when piped input ends before a valid answer. +On a quiet output (see :ref:`spark-verbosity`) both prompts return the defaults without asking, and a required +``multiselect()`` without a default throws a ``CLIException``. They also throw it when piped input ends before a valid +answer. Pressing Ctrl+C restores the terminal and then interrupts the process as usual. @@ -309,19 +308,17 @@ The Classes Behind the Facade .. versionadded:: 4.8.0 -The static output methods delegate to instance classes, each with one job. -``CLI::getOutput()`` and ``CLI::getTerminal()`` return the shared instances, and -modern commands reach the output through ``$this->getOutput()`` +The static output methods delegate to instance classes, each with one job. ``CLI::getOutput()`` and +``CLI::getTerminal()`` return the shared instances, and modern commands reach the output through ``$this->getOutput()`` (see :ref:`modern-commands-writing-output`): .. literalinclude:: cli_library/024.php -Mixing the static methods with the instance is safe, because both write to the same -``Output`` and share its line breaks. +Mixing the static methods with the instance is safe, because both write to the same ``Output`` and share its line +breaks. -The prompt methods delegate to :php:class:`Prompt`, which reads through an ``Output``. -``CLI::getPrompt()`` returns one on the shared output, and modern commands reach one on the -output of their run through ``$this->getPrompt()``: +The prompt methods delegate to :php:class:`Prompt`, which reads through an ``Output``. ``CLI::getPrompt()`` returns one +on the shared output, and modern commands reach one on the output of their run through ``$this->getPrompt()``: .. literalinclude:: cli_library/025.php @@ -329,32 +326,32 @@ output of their run through ``$this->getPrompt()``: .. php:class:: Output - The writer. It targets STDOUT, sends ``error()`` to STDERR, and remembers whether the - cursor sits mid-line so that ``write()`` starts on a fresh line. + The writer. It targets STDOUT, sends ``error()`` to STDERR, and remembers whether the cursor sits mid-line so that + ``write()`` starts on a fresh line. .. php:method:: __construct([InputOutput $io = new InputOutput()[, Terminal $terminal = new Terminal()]]) :param InputOutput $io: The transport the text is written through. - :param Terminal $terminal: The terminal consulted for colour support. + :param Terminal $terminal: The terminal consulted for color support. .. php:method:: print(string $text = ''[, string $foreground = ''[, string $background = ''[, Verbosity $verbosity = Verbosity::Normal]]]): void .. php:method:: write(string $text = ''[, string $foreground = ''[, string $background = ''[, Verbosity $verbosity = Verbosity::Normal]]]): void .. php:method:: newLine([int $num = 1[, Verbosity $verbosity = Verbosity::Normal]]): void - The same as the static methods of the same names, writing nothing when the - level of the output is below ``$verbosity``. + The same as the static methods of the same names, writing nothing when the level of the output is below + ``$verbosity``. .. php:method:: deferNewLine(): void - Writes a blank line before whatever is written next, and none if nothing follows. - The prompts end with it, so a prompt that ends the run leaves no blank line behind. + Writes a blank line before whatever is written next, and none if nothing follows. The prompts end with it, so a + prompt that ends the run leaves no blank line behind. .. php:method:: error(string $text[, string $foreground = 'light_red'[, string $background = '']]): void .. php:method:: beep(int $num = 1): void .. php:method:: clearScreen(): void - The same as the static methods of the same names. ``error()`` writes at every - level but ``Verbosity::Silent``, and the other two only from ``Verbosity::Normal`` on. + The same as the static methods of the same names. ``error()`` writes at every level but ``Verbosity::Silent``, + and the other two only from ``Verbosity::Normal`` on. .. php:method:: getVerbosity(): Verbosity .. php:method:: setVerbosity(Verbosity $verbosity): void @@ -363,14 +360,14 @@ output of their run through ``$this->getPrompt()``: .. php:method:: isVeryVerbose(): bool .. php:method:: isDebug(): bool - ``isQuiet()`` is true for the quiet and silent levels, and the others for their - level or above. See :ref:`modern-commands-verbosity`. + ``isQuiet()`` is true for the quiet and silent levels, and the others for their level or above. See + :ref:`modern-commands-verbosity`. .. php:method:: color(string $text, string $foreground[, string $background = ''[, string $format = '']]): string - Colours the text when the target stream supports colours, and returns it unchanged otherwise. - ``print()``, ``write()``, and ``error()`` colour through this method, so a subclass overriding - it changes the colouring of every write. + Colors the text when the target stream supports colors, and returns it unchanged otherwise. ``print()``, + ``write()``, and ``error()`` color through this method, so a subclass overriding it changes the coloring of + every write. .. php:method:: wrap(string $text[, int $max = 0[, int $padLeft = 0]]): string @@ -378,12 +375,12 @@ output of their run through ``$this->getPrompt()``: .. php:method:: isDecorated(): bool - Whether the target stream supports colours. + Whether the target stream supports colors. .. php:method:: forStderr(): static - Returns a copy whose STDOUT writes target STDERR, which is what the framework swaps in - while rendering an exception. + Returns a copy whose STDOUT writes target STDERR, which is what the framework swaps in while rendering an + exception. .. php:method:: getTerminal(): Terminal @@ -418,9 +415,8 @@ output of their run through ``$this->getPrompt()``: .. php:class:: Verbosity - The level of the output, as an ``int``-backed enum valued as ``SHELL_VERBOSITY``: - ``Silent`` (-2), ``Quiet`` (-1), ``Normal`` (0), ``Verbose`` (1), ``VeryVerbose`` (2), - and ``Debug`` (3). + The level of the output, as an ``int``-backed enum valued as ``SHELL_VERBOSITY``: ``Silent`` (-2), ``Quiet`` (-1), + ``Normal`` (0), ``Verbose`` (1), ``VeryVerbose`` (2), and ``Debug`` (3). .. php:class:: Terminal @@ -442,14 +438,13 @@ output of their run through ``$this->getPrompt()``: .. php:method:: advanceTo(float|int $step[, int $totalSteps = 10]): void - Draws the bar for the step, capped at 100%, over the previous bar unless the step - went backwards. A negative step or a total below one throws ``InvalidArgumentException``, - where ``CLI::showProgress()`` keeps tolerating them. + Draws the bar for the step, capped at 100%, over the previous bar unless the step went backwards. A negative + step or a total below one throws ``InvalidArgumentException``, where ``CLI::showProgress()`` keeps tolerating + them. .. php:method:: clear(): void - Rings the bell and starts a new bar on the next ``advanceTo()``. This is what - ``CLI::showProgress(false)`` does. + Rings the bell and starts a new bar on the next ``advanceTo()``. This is what ``CLI::showProgress(false)`` does. .. php:class:: Table @@ -464,12 +459,12 @@ output of their run through ``$this->getPrompt()``: .. php:class:: Color - The ANSI colour codes, independent of any stream. + The ANSI color codes, independent of any stream. .. php:staticmethod:: apply(string $text, string $foreground[, string $background = ''[, string $format = '']]): string - Wraps the text in the colour codes, always. Throws ``CLIException`` for an unknown colour. + Wraps the text in the color codes, always. Throws ``CLIException`` for an unknown color. .. php:staticmethod:: width(string $string): int - Returns the display width of the text without its colour codes, as ``CLI::strlen()`` does. + Returns the display width of the text without its color codes, as ``CLI::strlen()`` does. diff --git a/user_guide_src/source/cli/cli_modern_commands.rst b/user_guide_src/source/cli/cli_modern_commands.rst index 5477dffb02f7..62db0089f79a 100644 --- a/user_guide_src/source/cli/cli_modern_commands.rst +++ b/user_guide_src/source/cli/cli_modern_commands.rst @@ -4,25 +4,22 @@ Modern Spark Commands .. versionadded:: 4.8.0 -Modern commands are a newer style of :doc:`spark commands `. -Instead of declaring metadata through class properties, modern commands describe -themselves through a ``#[Command]`` attribute and build their argument/option -surface inside a ``configure()`` method. The framework then parses the command -line, applies the declared defaults, validates what was passed, and finally -calls ``execute()`` with typed, validated values. - -Modern and legacy commands can coexist: existing ``BaseCommand`` classes -continue to work, and the framework routes invocations to whichever command -matches the requested name, regardless of style. ``BaseCommand`` itself is -deprecated, though. See `Migrating From BaseCommand`_. +Modern commands are a newer style of :doc:`spark commands `. Instead of declaring metadata through class +properties, modern commands describe themselves through a ``#[Command]`` attribute and build their argument/option +surface inside a ``configure()`` method. The framework then parses the command line, applies the declared defaults, +validates what was passed, and finally calls ``execute()`` with typed, validated values. + +Modern and legacy commands can coexist: existing ``BaseCommand`` classes continue to work, and the framework routes +invocations to whichever command matches the requested name, regardless of style. ``BaseCommand`` itself is deprecated, +though. See `Migrating From BaseCommand`_. .. contents:: :local: :depth: 2 -******************* +****************** Creating a Command -******************* +****************** A modern command is a class that: @@ -37,36 +34,32 @@ A minimal example: File Location ============= -Same rule as the legacy style — commands must live under a directory named -**Commands** that is reachable through PSR-4 autoloading, for instance -**app/Commands/**. The framework auto-discovers them the first time the -command runner is used. +The rule is the same as for the legacy style: commands must live under a directory named **Commands** that is reachable +through PSR-4 autoloading, for instance **app/Commands/**. The framework auto-discovers them the first time the command +runner is used. The ``#[Command]`` Attribute ============================ The attribute holds the command's identity: -- ``name`` is the token users type after ``php spark``. It must not be empty, must not contain - whitespace, and may use a colon to namespace related commands (``cache:clear``, ``make:migration``). - Leading, trailing, or consecutive colons are rejected. +- ``name`` is the token users type after ``php spark``. It must not be empty, must not contain whitespace, and may use a + colon to namespace related commands (``cache:clear``, ``make:migration``). Leading, trailing, or consecutive colons + are rejected. - ``description`` is shown in the ``list`` output and at the top of ``help ``. -- ``group`` controls how the command is grouped in the ``list`` output. A command with an empty - ``group`` is skipped by discovery. -- ``aliases`` is an optional list of alternative names the command can also be invoked by. Each alias - follows the same naming rules as ``name`` and must differ from it. Aliases resolve to the command at - dispatch (``php spark `` and ``help `` both work), are listed as their own rows in the - ``list`` output, and are shown in an ``Aliases:`` section of ``help ``. -- ``hidden`` is an optional flag, ``false`` by default, that keeps the command out of listings. - See `Hidden Commands`_. -- ``headerless`` is an optional flag, ``false`` by default, that stops spark from printing its header - before the command runs. See `Commands Without the Header`_. - -The attribute itself validates these constraints at construction time. If you -misspell ``name``, you will see the error at discovery rather than at run time. -An alias that collides with an existing command name or with another command's -alias is a hard error at discovery, since the runner could not tell which command -you meant. +- ``group`` controls how the command is grouped in the ``list`` output. A command with an empty ``group`` is skipped by + discovery. +- ``aliases`` is an optional list of alternative names the command can also be invoked by. Each alias follows the same + naming rules as ``name`` and must differ from it. Aliases resolve to the command at dispatch (``php spark `` + and ``help `` both work), are listed as their own rows in the ``list`` output, and are shown in an ``Aliases:`` + section of ``help ``. +- ``hidden`` is an optional flag, ``false`` by default, that keeps the command out of listings. See `Hidden Commands`_. +- ``headerless`` is an optional flag, ``false`` by default, that stops spark from printing its header before the command + runs. See `Commands Without the Header`_. + +The attribute itself validates these constraints at construction time. If you misspell ``name``, you will see the error +at discovery rather than at run time. An alias that collides with an existing command name or with another command's +alias is a hard error at discovery, since the runner could not tell which command you meant. .. literalinclude:: cli_modern_commands/014.php @@ -75,10 +68,9 @@ you meant. Hidden Commands =============== -A hidden command is an ordinary command that spark does not advertise. It suits commands -meant for scripts, cron jobs, or deployment tooling, which should stay runnable without -cluttering the list people browse. Hide a command by setting ``hidden: true`` on its -``#[Command]`` attribute: +A hidden command is an ordinary command that spark does not advertise. It suits commands meant for scripts, cron jobs, +or deployment tooling, which should stay runnable without cluttering the list people browse. Hide a command by setting +``hidden: true`` on its ``#[Command]`` attribute: .. literalinclude:: cli_modern_commands/016.php @@ -93,58 +85,52 @@ Anything that names the command exactly still works: - ``command('app:reindex')``, and ``call('app:reindex')`` from another command; - ``php spark help app:reindex``, or ``php spark app:reindex --help``. -To check the flag in code, use ``isHidden()`` on a command instance, or -``Commands::isHiddenCommand()`` with a command name or alias. +To check the flag in code, use ``isHidden()`` on a command instance, or ``Commands::isHiddenCommand()`` with a command +name or alias. -.. note:: Hiding a command is not the same as leaving its ``group`` empty. A command with an - empty group is never discovered, so it cannot run at all. +.. note:: Hiding a command is not the same as leaving its ``group`` empty. A command with an empty group is never + discovered, so it cannot run at all. .. _commands-without-the-header: Commands Without the Header =========================== -Spark prints a header with the framework version and the server time before running a command. -A command whose output is read by another program, such as JSON piped to ``jq``, can drop the -header by setting ``headerless: true`` on its ``#[Command]`` attribute: +Spark prints a header with the framework version and the server time before running a command. A command whose output is +read by another program, such as JSON piped to ``jq``, can drop the header by setting ``headerless: true`` on its +``#[Command]`` attribute: .. literalinclude:: cli_modern_commands/017.php -This has the same effect as always passing ``--no-header``, and it covers the command's aliases -too. ``php spark help app:status`` and ``php spark app:status --help`` still print the header, -since help output is meant for people. +This has the same effect as always passing ``--no-header``, and it covers the command's aliases too. +``php spark help app:status`` and ``php spark app:status --help`` still print the header, since help output is meant for +people. -To check the flag in code, use ``isHeaderless()`` on a command instance, or -``Commands::isHeaderlessCommand()`` with a command name or alias. +To check the flag in code, use ``isHeaderless()`` on a command instance, or ``Commands::isHeaderlessCommand()`` with a +command name or alias. ***************** Command Lifecycle ***************** -When the runner invokes a modern command, it walks through several phases in -this order: - -1. **Construction.** The ``#[Command]`` attribute is read, then your - ``configure(): void`` hook runs so you can register arguments, options, and - extra usage examples. A default ``--help``/ ``-h`` flag, ``--no-header`` - flag, ``--no-interaction``/ ``-N`` flag, ``--silent`` flag, ``--quiet``/ ``-q`` - flag, and ``--verbose``/ ``-v`` flag are added automatically afterwards. -2. ``isAvailable(): bool`` is called to check whether the command should execute - (see :ref:`restricting-execution`). -3. ``initialize(array &$arguments, array &$options): void`` receives the raw - arguments and options by reference. Useful when your command needs to - massage input — for instance, to unfold an alias argument into the canonical - form before anything else runs. -4. ``interact(array &$arguments, array &$options): void`` also receives the - raw arguments and options by reference. This is where you prompt the user - for missing input, set values conditionally, or abort early. This hook is - skipped when the command is non-interactive (see :ref:`non-interactive-mode`). -5. **Bind & validate.** The framework maps the raw input to the definitions - you declared in ``configure()``, applies defaults, and rejects input that - violates the definitions (missing required argument, unknown option, array - option passed without a value, and so on). -6. ``execute(array $arguments, array $options): int`` receives the bound and - validated arguments and options, and returns an exit code. +When the runner invokes a modern command, it walks through several phases in this order: + +1. **Construction.** The ``#[Command]`` attribute is read, then your ``configure(): void`` hook runs so you can register + arguments, options, and extra usage examples. A default ``--help`` / ``-h`` flag, ``--no-header`` flag, + ``--no-interaction`` / ``-N`` flag, ``--silent`` flag, ``--quiet`` / ``-q`` flag, and ``--verbose`` / ``-v`` flag are + added automatically afterwards. +2. ``isAvailable(): bool`` is called to check whether the command should execute (see :ref:`restricting-execution`). +3. ``initialize(array &$arguments, array &$options): void`` receives the raw arguments and options by reference. Useful + when your command needs to massage input — for instance, to unfold an alias argument into the canonical form before + anything else runs. +4. ``interact(array &$arguments, array &$options): void`` also receives the raw arguments and options by reference. This + is where you prompt the user for missing input, set values conditionally, or abort early. This hook is skipped when + the command is non-interactive (see :ref:`non-interactive-mode`). +5. **Bind & validate.** The framework maps the raw input to the definitions you declared in ``configure()``, applies + defaults, and rejects input that violates the definitions (missing required argument, unknown option, array option + passed without a value, and so on). +6. ``execute(array $arguments, array $options): int`` receives the bound and validated arguments and options, and + returns an exit code. You only have to implement ``execute()``; the other hooks are optional. @@ -152,20 +138,19 @@ You only have to implement ``execute()``; the other hooks are optional. Arguments ********* -Arguments are positional — the first token after the command name is bound to -the first declared argument, the second token to the second declared argument, -and so on. They are declared inside ``configure()`` using the +Arguments are positional — the first token after the command name is bound to the first declared argument, the second +token to the second declared argument, and so on. They are declared inside ``configure()`` using the ``CodeIgniter\CLI\Input\Argument`` value object: .. literalinclude:: cli_modern_commands/002.php -The following rules are enforced at configuration time. Violating any of them -raises an ``InvalidArgumentDefinitionException``: +The following rules are enforced at configuration time. Violating any of them raises an +``InvalidArgumentDefinitionException``: - A required argument **must not** have a default value. - An optional argument **must** have a default value. -- An array argument collects every remaining positional token. - Only one array argument may be declared, and it must come last. +- An array argument collects every remaining positional token. Only one array argument may be declared, and it must come + last. - An array argument cannot be required (but it can have a non-empty default). - Required arguments must all come before optional arguments. - Argument names must match ``[A-Za-z0-9_-]+`` and the name ``extra_arguments`` is reserved. @@ -178,27 +163,26 @@ Options are name-based. They are declared with ``CodeIgniter\CLI\Input\Option``: .. literalinclude:: cli_modern_commands/003.php -Options support the following modes (they can be combined where it makes -sense): +Options support the following modes (they can be combined where it makes sense): -- **Flag** — the default. The option takes no value. Presence makes the bound - value ``true``; absence leaves it ``false``. +- **Flag** — the default. The option takes no value. Presence makes the bound value ``true``; absence leaves it + ``false``. - ``requiresValue: true`` — the option must be followed by a value when passed. - ``acceptsValue: true`` — the option may be followed by a value, but the value is optional. - ``isArray: true`` — the option may be passed multiple times; each value is appended to the bound array. -- ``negatable: true`` — a second long form ``--no-`` is registered automatically. - Passing ``--name`` sets the option to ``true``; passing ``--no-name`` sets it to ``false``. +- ``negatable: true`` — a second long form ``--no-`` is registered automatically. Passing ``--name`` sets the + option to ``true``; passing ``--no-name`` sets it to ``false``. -Every option may also declare a single-character ``shortcut`` (e.g., ``-f`` for ``--force``). -Shortcuts must be a single alphanumeric character and unique within the command. +Every option may also declare a single-character ``shortcut`` (e.g., ``-f`` for ``--force``). Shortcuts must be a single +alphanumeric character and unique within the command. A few quirks are worth knowing: - ``requiresValue: true`` and ``isArray: true`` both imply ``acceptsValue: true``. -- An option that requires a value must be given a **string** default. The default is used only when the - option is not passed at all; passing the option without a value throws at validation. -- An array option must require a value. Its default must be ``null`` or a non-empty array - (``null`` is normalised to an empty array internally). +- An option that requires a value must be given a **string** default. The default is used only when the option is not + passed at all; passing the option without a value throws at validation. +- An array option must require a value. Its default must be ``null`` or a non-empty array (``null`` is normalized to an + empty array internally). - A negatable option cannot accept a value or be an array. Its default must be a boolean. - A negatable option's auto-generated ``--no-`` form will clash if another option is already named ``no-``. - Option names must match ``[A-Za-z0-9_-]+`` and the name ``extra_options`` is reserved. @@ -215,13 +199,11 @@ Configuration-time violations raise ``InvalidOptionDefinitionException``. .. note:: - The command-line parser does **not** understand bundled shortcuts or - shortcuts with a glued value: + The command-line parser does **not** understand bundled shortcuts or shortcuts with a glued value: - - ``-abc`` is read as one option named ``abc``, *not* as ``-a -b -c``. The only - exception is a run of ``v``, such as ``-vvv``, which sets the verbosity. - - ``-fvalue`` is read as one option named ``fvalue``; it is not split into - shortcut ``-f`` with value ``value``. + - ``-abc`` is read as one option named ``abc``, *not* as ``-a -b -c``. The only exception is a run of ``v``, such as + ``-vvv``, which sets the verbosity. + - ``-fvalue`` is read as one option named ``fvalue``; it is not split into shortcut ``-f`` with value ``value``. Pass shortcut values with ``-f=value`` or ``-f value`` instead. @@ -229,95 +211,81 @@ Configuration-time violations raise ``InvalidOptionDefinitionException``. Interacting With the User ************************* -``interact()`` is designed for commands that need to prompt, confirm, or fill -in missing input before validation runs. Its ``$arguments`` and ``$options`` -parameters are **raw** — they are the tokens the framework parsed from the -command line, *before* the values are mapped to your declared definitions. +``interact()`` is designed for commands that need to prompt, confirm, or fill in missing input before validation runs. +Its ``$arguments`` and ``$options`` parameters are **raw** — they are the tokens the framework parsed from the command +line, *before* the values are mapped to your declared definitions. -Because the raw input may be keyed by the long name, the shortcut, or the -negation form, two helpers make lookups alias-aware: +Because the raw input may be keyed by the long name, the shortcut, or the negation form, two helpers make lookups +alias-aware: - ``hasUnboundOption(string $name, ?array $options = null): bool`` - ``getUnboundOption(string $name, ?array $options = null): array|string|null`` -Inside ``initialize()`` and ``interact()`` pass ``$options`` explicitly — the -instance snapshot is not populated yet. From ``execute()`` (or any helper -reached from it) you can omit ``$options`` and the helpers will read from the -snapshot taken right after ``interact()`` returns and before bind and validate. +Inside ``initialize()`` and ``interact()`` pass ``$options`` explicitly — the instance snapshot is not populated yet. +From ``execute()`` (or any helper reached from it) you can omit ``$options`` and the helpers will read from the snapshot +taken right after ``interact()`` returns and before bind and validate. .. literalinclude:: cli_modern_commands/004.php -Any change you make to ``$arguments`` or ``$options`` inside ``interact()`` -carries through to bind, validate, and ``execute()``. +Any change you make to ``$arguments`` or ``$options`` inside ``interact()`` carries through to bind, validate, and +``execute()``. .. _prompts-for-missing-input: Prompting for Missing Arguments =============================== -A command can let the framework prompt for its missing required arguments -instead of hand-rolling that logic in ``interact()``. Opt in by implementing -the ``CodeIgniter\CLI\PromptsForMissingInputInterface`` marker interface: +A command can let the framework prompt for its missing required arguments instead of hand-rolling that logic in +``interact()``. Opt in by implementing the ``CodeIgniter\CLI\PromptsForMissingInputInterface`` marker interface: .. literalinclude:: cli_modern_commands/015.php -On an interactive run, right before ``interact()`` is called, the framework -compares the provided positional tokens against the declared required arguments -and prompts (with the ``required`` validation rule) for each one still missing, +On an interactive run, right before ``interact()`` is called, the framework compares the provided positional tokens +against the declared required arguments and prompts (with the ``required`` validation rule) for each one still missing, in declaration order. Two hooks refine the behavior: -- ``getArgumentPromptLabels(): array`` returns a map of argument name to - prompt label. Arguments without an entry fall back to a generic label naming - the argument. -- ``afterPrompting(array &$arguments, array &$options): void`` - runs after at least one prompt occurred. Useful to derive further input from - the answers. +- ``getArgumentPromptLabels(): array`` returns a map of argument name to prompt label. Arguments without an entry fall + back to a generic label naming the argument. +- ``afterPrompting(array &$arguments, array &$options): void`` runs after at least one prompt occurred. Useful to derive + further input from the answers. -On a non-interactive run nothing is prompted, and a missing required argument -fails validation with the usual missing-arguments error. Since the prompted -values are appended before the raw input is snapshotted, they are also visible -to ``interact()``, the unbound accessors, and anything forwarded through -``$this->call(...)``. +On a non-interactive run nothing is prompted, and a missing required argument fails validation with the usual +missing-arguments error. Since the prompted values are appended before the raw input is snapshotted, they are also +visible to ``interact()``, the unbound accessors, and anything forwarded through ``$this->call(...)``. .. _non-interactive-mode: Non-Interactive Mode ==================== -Every modern command accepts ``--no-interaction`` / ``-N`` out of the box. -When the flag is present, or when the command is otherwise non-interactive, -the ``interact()`` hook is skipped entirely and the command proceeds straight -to bind, validate, and ``execute()``. +Every modern command accepts ``--no-interaction`` / ``-N`` out of the box. When the flag is present, or when the command +is otherwise non-interactive, the ``interact()`` hook is skipped entirely and the command proceeds straight to bind, +validate, and ``execute()``. -Programmatically, the state is exposed through two public methods on -``AbstractCommand``: +Programmatically, the state is exposed through two public methods on ``AbstractCommand``: - ``isInteractive(): bool``: reports the current state. -- ``setInteractive(bool $interactive): static``: pins the state, overriding - both the CLI flag and TTY detection. Returns ``$this`` for chaining. +- ``setInteractive(bool $interactive): static``: pins the state, overriding both the CLI flag and TTY detection. Returns + ``$this`` for chaining. The resolved state follows this precedence: -1. An explicit ``setInteractive(bool)`` call wins. Useful when a command - must force a specific mode for safety. +1. An explicit ``setInteractive(bool)`` call wins. Useful when a command must force a specific mode for safety. 2. Otherwise, the CLI flag ``--no-interaction`` / ``-N`` forces non-interactive state. -3. Otherwise, **STDIN** is probed: if it is not a TTY (piped input, cron, - CI, ``nohup``), the command is non-interactive. +3. Otherwise, **STDIN** is probed: if it is not a TTY (piped input, cron, CI, ``nohup``), the command is + non-interactive. 4. Otherwise, the command is interactive. -When the current command invokes another via ``$this->call(...)``, the -parent's non-interactive state is propagated to the sub-command -automatically. A caller that passes ``no-interaction`` (or ``N``) in the -sub-command's ``$options`` wins over that propagation. +When the current command invokes another via ``$this->call(...)``, the parent's non-interactive state is propagated to +the sub-command automatically. A caller that passes ``no-interaction`` (or ``N``) in the sub-command's ``$options`` wins +over that propagation. -The propagation can be overridden with the ``$noInteractionOverride`` -parameter of ``call()``: +The propagation can be overridden with the ``$noInteractionOverride`` parameter of ``call()``: - ``null`` (default): propagate the parent's state. - ``true``: force the sub-command non-interactive regardless of the parent. -- ``false``: remove any forwarded ``--no-interaction`` / ``-N`` from the - child ``$options`` so the sub-command resolves its own state. Note: TTY - detection can still downgrade the sub-command if STDIN is not a TTY. +- ``false``: remove any forwarded ``--no-interaction`` / ``-N`` from the child ``$options`` so the sub-command resolves + its own state. Note: TTY detection can still downgrade the sub-command if STDIN is not a TTY. .. _restricting-execution: @@ -325,34 +293,31 @@ parameter of ``call()``: Restricting Command Execution ***************************** -Sometimes a command should not run in a specific runtime context. For example, -development-only commands may need to be blocked in the ``production`` environment. +Sometimes a command should not run in a specific runtime context. For example, development-only commands may need to be +blocked in the ``production`` environment. -You can override ``isAvailable()`` to decide at runtime whether the command may execute. -By default, this method returns ``true``. +You can override ``isAvailable()`` to decide at runtime whether the command may execute. By default, this method returns +``true``. .. literalinclude:: cli_modern_commands/013.php -The availability check runs before ``initialize()``, ``interact()``, argument and -option binding, validation, and ``execute()``. If the command is not available, -a ``CommandNotAvailableException`` is thrown immediately. +The availability check runs before ``initialize()``, ``interact()``, argument and option binding, validation, and +``execute()``. If the command is not available, a ``CommandNotAvailableException`` is thrown immediately. .. note:: This method prevents execution of the command, but does not remove it from help output or command discovery. -****************** +**************** Inside execute() -****************** +**************** ``execute()`` receives two arrays that mirror your declared definitions: - ``$arguments`` contains every declared argument, bound to the provided value or the declared default. -- ``$options`` contains every declared option plus the framework defaults - (``help``, ``no-header``, ``no-interaction``, ``silent``, ``quiet``, ``verbose``), bound to the provided value - or the declared default. +- ``$options`` contains every declared option plus the framework defaults (``help``, ``no-header``, ``no-interaction``, + ``silent``, ``quiet``, ``verbose``), bound to the provided value or the declared default. -Within ``execute()`` itself, reaching into ``$arguments`` / ``$options`` directly -is the simplest thing to do. The same data is also available through helpers -so you can reach it from sub-methods without having to thread the two arrays +Within ``execute()`` itself, reaching into ``$arguments`` / ``$options`` directly is the simplest thing to do. The same +data is also available through helpers so you can reach it from sub-methods without having to thread the two arrays through every signature: - ``getValidatedArgument(string $name)`` / ``getValidatedArguments()`` @@ -360,10 +325,9 @@ through every signature: - ``getUnboundArgument(int $index)`` / ``getUnboundArguments()`` - ``getUnboundOption(string $name, ?array $options = null)`` / ``getUnboundOptions()`` -The *validated* variants expose the bound values (what your definition says). -The *unbound* variants expose the raw input snapshot — useful when forwarding -the command to another command, or when your logic needs to know whether a -flag was actually passed rather than whether it resolved to a default value. +The *validated* variants expose the bound values (what your definition says). The *unbound* variants expose the raw +input snapshot — useful when forwarding the command to another command, or when your logic needs to know whether a flag +was actually passed rather than whether it resolved to a default value. .. literalinclude:: cli_modern_commands/005.php @@ -375,55 +339,50 @@ Accessors that take a name throw ``LogicException`` when the name is not declare Writing Output ************** -``$this->getOutput()`` returns the :php:class:`Output ` of the -current run. It is the same instance written to by the static ``CLI`` methods, so the two -can be mixed and the line breaks stay consistent. ``Output`` carries the writing -methods: ``write()``, ``print()``, ``error()``, ``newLine()``, ``beep()``, +``$this->getOutput()`` returns the :php:class:`Output ` of the current run. It is the same +instance written to by the static ``CLI`` methods, so the two can be mixed and the line breaks stay consistent. +``Output`` carries the writing methods: ``write()``, ``print()``, ``error()``, ``newLine()``, ``beep()``, ``clearScreen()``, and ``color()``. Tables and progress bars are rendered by :php:class:`Table ` and :php:class:`ProgressBar `: .. literalinclude:: cli_modern_commands/018.php -By default the run writes to the output shared through ``CLI::getOutput()``. Pass an -``Output`` as the third argument of ``run()`` to write somewhere else, for instance -to a ``MockInputOutput`` in a test. See :doc:`../cli/cli_library` for the classes and -:ref:`testing-cli-output` for testing. +By default the run writes to the output shared through ``CLI::getOutput()``. Pass an ``Output`` as the third argument of +``run()`` to write somewhere else, for instance to a ``MockInputOutput`` in a test. See :doc:`../cli/cli_library` for +the classes and :ref:`testing-cli-output` for testing. .. _modern-commands-verbosity: Writing at a Verbosity ====================== -**spark** sets the :php:class:`Verbosity ` of the output -from ``--silent``, ``--quiet`` / ``-q``, and ``--verbose`` / ``-v``, ``-vv``, ``-vvv`` (see :ref:`spark-verbosity`). -``print()``, ``write()``, and ``newLine()`` take the lowest level that shows the text as their last -parameter, and ``isVerbose()``, ``isVeryVerbose()``, ``isDebug()``, and ``isQuiet()`` guard larger blocks: +**spark** sets the :php:class:`Verbosity ` of the output from ``--silent``, ``--quiet`` / +``-q``, and ``--verbose`` / ``-v``, ``-vv``, ``-vvv`` (see :ref:`spark-verbosity`). ``print()``, ``write()``, and +``newLine()`` take the lowest level that shows the text as their last parameter, and ``isVerbose()``, +``isVeryVerbose()``, ``isDebug()``, and ``isQuiet()`` guard larger blocks: .. literalinclude:: cli_modern_commands/019.php -``--quiet`` drops every write at the normal level, so only errors and text written at -``Verbosity::Quiet`` remain, and it makes the run non-interactive. +``--quiet`` drops every write at the normal level, so only errors and text written at ``Verbosity::Quiet`` remain, and +it makes the run non-interactive. -A command run through ``call()`` writes at the level of the calling command. Use -``callSilently()`` to hide its output. +A command run through ``call()`` writes at the level of the calling command. Use ``callSilently()`` to hide its output. *********************** Calling Another Command *********************** -Inside ``execute()``, a modern command can invoke another modern command through -``$this->call()``. ``call()`` must not be used from ``configure()``, ``initialize()``, -or ``interact()`` — the current command has not been bound and validated yet at -those points, and its unbound state has not been snapshotted. +Inside ``execute()``, a modern command can invoke another modern command through ``$this->call()``. ``call()`` must not +be used from ``configure()``, ``initialize()``, or ``interact()`` — the current command has not been bound and validated +yet at those points, and its unbound state has not been snapshotted. .. literalinclude:: cli_modern_commands/006.php -The ``$arguments`` and ``$options`` you pass are interpreted as raw input — -they go through bind and validate on the target command, just like a user -invocation. +The ``$arguments`` and ``$options`` you pass are interpreted as raw input — they go through bind and validate on the +target command, just like a user invocation. -To forward the caller's own input through to the target command, pass -``$this->getUnboundArguments()`` and ``$this->getUnboundOptions()`` to ``call()``: +To forward the caller's own input through to the target command, pass ``$this->getUnboundArguments()`` and +``$this->getUnboundOptions()`` to ``call()``: .. literalinclude:: cli_modern_commands/008.php @@ -432,50 +391,44 @@ To forward the caller's own input through to the target command, pass Calling Silently ================ -When a command delegates a step to another command but wants to emit its own -consolidated message instead of letting the sub-command's output leak through, -use ``$this->callSilently()``: +When a command delegates a step to another command but wants to emit its own consolidated message instead of letting the +sub-command's output leak through, use ``$this->callSilently()``: .. literalinclude:: cli_modern_commands/012.php -The sub-command's output is suppressed and ``$noInteractionOverride`` defaults -to ``true``, since a silenced sub-command cannot meaningfully prompt. Pass an -explicit value to override. +The sub-command's output is suppressed and ``$noInteractionOverride`` defaults to ``true``, since a silenced sub-command +cannot meaningfully prompt. Pass an explicit value to override. -While the sub-command runs, ``CLI::getOutput()`` and ``$this->getOutput()`` return an -output backed by ``NullInputOutput``, so everything written through either, including -the sub-command's own ``callSilently()`` or ``call()`` sub-commands, is discarded. The -previous output is restored afterwards. +While the sub-command runs, ``CLI::getOutput()`` and ``$this->getOutput()`` return an output backed by +``NullInputOutput``, so everything written through either, including the sub-command's own ``callSilently()`` or +``call()`` sub-commands, is discarded. The previous output is restored afterwards. ************** Usage Examples ************** -The default usage line is built automatically from the command name and the -declared argument list. You can append additional example lines by calling -``addUsage()`` inside ``configure()``: +The default usage line is built automatically from the command name and the declared argument list. You can append +additional example lines by calling ``addUsage()`` inside ``configure()``: .. literalinclude:: cli_modern_commands/007.php -In the ``help `` or `` --help`` output the default usage line is shown first, -followed by each ``addUsage()`` entry in the order it was added. +In the ``help `` or `` --help`` output the default usage line is shown first, followed by each +``addUsage()`` entry in the order it was added. ********************** Rendering an Exception ********************** -If your command catches a ``Throwable`` and wants to produce the same -formatted output the framework uses for uncaught exceptions, call -``$this->renderThrowable($exception)``. The helper is safe to call from any -command, and it will not disturb the currently shared request. +If your command catches a ``Throwable`` and wants to produce the same formatted output the framework uses for uncaught +exceptions, call ``$this->renderThrowable($exception)``. The helper is safe to call from any command, and it will not +disturb the currently shared request. -********************************** +****************************** Migrating From ``BaseCommand`` -********************************** +****************************** -The modern command system is a superset of the legacy ``BaseCommand`` API — the -same capabilities are there, just expressed through an attribute and explicit -definitions rather than class properties and ad-hoc lookups. +The modern command system is a superset of the legacy ``BaseCommand`` API — the same capabilities are there, just +expressed through an attribute and explicit definitions rather than class properties and ad-hoc lookups. **Identity** @@ -523,8 +476,8 @@ definitions rather than class properties and ad-hoc lookups. ``showHelp()`` override Gone. The built-in ``help`` command builds the help output itself from the declared arguments, options, and usages. -Prompting the user goes through ``$this->getPrompt()``, and the idiomatic spot moves from ``run()`` -to ``interact()`` so validation can see whatever the user provides interactively. +Prompting the user goes through ``$this->getPrompt()``, and the idiomatic spot moves from ``run()`` to ``interact()`` so +validation can see whatever the user provides interactively. A typical ``BaseCommand`` implementation: @@ -534,55 +487,48 @@ A typical ``BaseCommand`` implementation: .. literalinclude:: cli_modern_commands/010.php -Two behavioural changes are worth calling out explicitly: +Two behavioral changes are worth calling out explicitly: -- **Validated, not raw.** Arguments and options are parsed, defaulted, and validated before ``execute()`` runs. - If a required argument is missing or a ``requiresValue`` option was passed without a value, the framework - raises a typed exception and your command is never entered. -- **Exit codes are mandatory.** Legacy ``run()`` could return ``null``. The modern ``execute()`` must return an - integer; the framework emits a deprecation notice for any legacy command that still returns ``null``. +- **Validated, not raw.** Arguments and options are parsed, defaulted, and validated before ``execute()`` runs. If a + required argument is missing or a ``requiresValue`` option was passed without a value, the framework raises a typed + exception and your command is never entered. +- **Exit codes are mandatory.** Legacy ``run()`` could return ``null``. The modern ``execute()`` must return an integer; + the framework emits a deprecation notice for any legacy command that still returns ``null``. ******************************** Coexistence With Legacy Commands ******************************** -Legacy ``BaseCommand`` classes are still supported, and they are discovered -alongside modern commands. If the same name is claimed by both a legacy and a -modern command, the legacy one is invoked and a warning is printed to STDERR once -at discovery time so you can rename or retire one of the two. Any aliases declared -by the shadowed modern command are dropped at discovery, so they are neither -listed nor runnable. Resolve the collision and the modern command, along with +Legacy ``BaseCommand`` classes are still supported, and they are discovered alongside modern commands. If the same name +is claimed by both a legacy and a modern command, the legacy one is invoked and a warning is printed to STDERR once at +discovery time so you can rename or retire one of the two. Any aliases declared by the shadowed modern command are +dropped at discovery, so they are neither listed nor runnable. Resolve the collision and the modern command, along with its aliases, becomes reachable again. -To detect the collision programmatically — for example, in a migration script -that verifies the legacy copy was removed — the ``Commands`` runner exposes two -read-only checks: +To detect the collision programmatically — for example, in a migration script that verifies the legacy copy was removed +— the ``Commands`` runner exposes two read-only checks: .. literalinclude:: cli_modern_commands/011.php -The ``help`` command understands both styles — it delegates to the legacy -``showHelp()`` method for legacy commands and renders a structured view for -modern ones. +The ``help`` command understands both styles — it delegates to the legacy ``showHelp()`` method for legacy commands and +renders a structured view for modern ones. -A legacy command can call a modern one through ``$this->call()``: integer-keyed -params become positional arguments and string-keyed params become options, and -the modern command validates them like any other input. Unknown options or extra +A legacy command can call a modern one through ``$this->call()``: integer-keyed params become positional arguments and +string-keyed params become options, and the modern command validates them like any other input. Unknown options or extra arguments that a legacy target would have ignored are rejected. .. deprecated:: 4.8.0 - ``BaseCommand`` is deprecated, and running a legacy command triggers a - deprecation notice (see :ref:`logging_deprecation_warnings`). New commands - should be written against ``AbstractCommand`` from the start. + ``BaseCommand`` is deprecated, and running a legacy command triggers a deprecation notice (see + :ref:`logging_deprecation_warnings`). New commands should be written against ``AbstractCommand`` from the start. *************** AbstractCommand *************** -The ``AbstractCommand`` class that all modern commands must extend exposes a -number of utility methods you call from within your own command. Hooks like -``configure()``, ``initialize()``, ``interact()``, and ``execute()`` are -covered in the sections above and are not listed here. +The ``AbstractCommand`` class that all modern commands must extend exposes a number of utility methods you call from +within your own command. Hooks like ``configure()``, ``initialize()``, ``interact()``, and ``execute()`` are covered in +the sections above and are not listed here. .. php:namespace:: CodeIgniter\CLI @@ -590,9 +536,8 @@ covered in the sections above and are not listed here. .. php:method:: getCommandRunner(): Commands - Returns the ``Commands`` runner the command was constructed with. - Useful when you need to introspect other discovered commands (for - instance, building a custom ``list``-style command). + Returns the ``Commands`` runner the command was constructed with. Useful when you need to introspect other + discovered commands (for instance, building a custom ``list``-style command). .. php:method:: getName(): string @@ -600,8 +545,7 @@ covered in the sections above and are not listed here. .. php:method:: getDescription(): string - Returns the command description declared on the ``#[Command]`` - attribute. + Returns the command description declared on the ``#[Command]`` attribute. .. php:method:: getGroup(): string @@ -609,47 +553,42 @@ covered in the sections above and are not listed here. .. php:method:: isHidden(): bool - Returns whether the ``#[Command]`` attribute marks the command as hidden. - See `Hidden Commands`_. + Returns whether the ``#[Command]`` attribute marks the command as hidden. See `Hidden Commands`_. .. php:method:: isHeaderless(): bool - Returns whether the ``#[Command]`` attribute opts the command out of the header. - See `Commands Without the Header`_. + Returns whether the ``#[Command]`` attribute opts the command out of the header. See + `Commands Without the Header`_. .. php:method:: getUsages(): array - Returns every usage line registered for the command — the default - line built from the argument list, followed by each ``addUsage()`` - entry in declaration order. + Returns every usage line registered for the command — the default line built from the argument list, followed by + each ``addUsage()`` entry in declaration order. .. php:method:: getArgumentsDefinition(): array - Returns the ``Argument`` value objects registered on this command, - keyed by argument name and ordered by declaration. + Returns the ``Argument`` value objects registered on this command, keyed by argument name and ordered by + declaration. .. php:method:: getOptionsDefinition(): array - Returns the ``Option`` value objects registered on this command, - keyed by option name. + Returns the ``Option`` value objects registered on this command, keyed by option name. .. php:method:: getShortcuts(): array - Returns the shortcut-to-option-name map (for example - ``['f' => 'force']``). Empty when no shortcut is declared. + Returns the shortcut-to-option-name map (for example ``['f' => 'force']``). Empty when no shortcut is declared. .. php:method:: getNegations(): array - Returns the negation-to-option-name map (for example - ``['no-force' => 'force']``). Empty when no negatable option is - declared. + Returns the negation-to-option-name map (for example ``['no-force' => 'force']``). Empty when no negatable + option is declared. .. php:method:: addUsage(string $usage): static :param string $usage: An extra usage example line. - Adds a usage example to the ``help `` output. The default - usage line derived from the argument list is always shown first. + Adds a usage example to the ``help `` output. The default usage line derived from the argument list is + always shown first. .. php:method:: addArgument(Argument $argument): static @@ -667,50 +606,44 @@ covered in the sections above and are not listed here. :param Throwable $e: The throwable to render. - Produces the same formatted output on STDERR that the framework uses - for uncaught exceptions. Safe to call from any command. + Produces the same formatted output on STDERR that the framework uses for uncaught exceptions. Safe to call from + any command. .. php:method:: hasArgument(string $name): bool :param string $name: The argument name to look up. - Returns ``true`` if an argument with that name is declared on the - command. + Returns ``true`` if an argument with that name is declared on the command. .. php:method:: hasOption(string $name): bool :param string $name: The option name to look up. - Returns ``true`` if an option with that name is declared on the - command. + Returns ``true`` if an option with that name is declared on the command. .. php:method:: hasShortcut(string $shortcut): bool :param string $shortcut: The shortcut character to look up. - Returns ``true`` if the shortcut is claimed by one of the declared - options. + Returns ``true`` if the shortcut is claimed by one of the declared options. .. php:method:: hasNegation(string $name): bool :param string $name: The negation name (for example ``no-force``) to look up. - Returns ``true`` if the negation is registered by one of the - declared options. + Returns ``true`` if the negation is registered by one of the declared options. .. php:method:: isInteractive(): bool - Reports whether the command will prompt the user. See - :ref:`non-interactive-mode` for the resolution order. + Reports whether the command will prompt the user. See :ref:`non-interactive-mode` for the resolution order. .. php:method:: setInteractive(bool $interactive): static :param bool $interactive: The state to pin. :returns: The current command instance for chaining. - Overrides both the ``--no-interaction`` / ``-N`` flag and TTY - detection for this command instance. Typically called from - ``initialize()`` or by an outer caller. + Overrides both the ``--no-interaction`` / ``-N`` flag and TTY detection for this command instance. Typically + called from ``initialize()`` or by an outer caller. .. php:method:: run(array $arguments, array $options[, ?Output $output = null]): int @@ -719,28 +652,24 @@ covered in the sections above and are not listed here. :param Output|null $output: The output to run against, or ``null`` for ``CLI::getOutput()``. :returns: The exit code returned by ``execute()``. - **Final.** Walks the command through ``initialize()``, ``interact()``, - bind, validate, and finally ``execute()``. The framework calls this - on your behalf — you rarely invoke it directly, but you can when - driving a command manually (for instance, from a test). + **Final.** Walks the command through ``initialize()``, ``interact()``, bind, validate, and finally + ``execute()``. The framework calls this on your behalf — you rarely invoke it directly, but you can when driving + a command manually (for instance, from a test). - A passed ``$output`` replaces ``CLI::getOutput()`` for the duration of the - run, so sub-commands called with :php:meth:`call` inherit it. The previous - output is restored afterwards, also when the run throws, so an uncaught - exception is rendered on the previous output. + A passed ``$output`` replaces ``CLI::getOutput()`` for the duration of the run, so sub-commands called with + :php:meth:`call` inherit it. The previous output is restored afterwards, also when the run throws, so an + uncaught exception is rendered on the previous output. .. php:method:: getOutput(): Output - Returns the :php:class:`Output ` of the current run, - which is the same instance written to by the static ``CLI`` methods. See - :ref:`modern-commands-writing-output`. + Returns the :php:class:`Output ` of the current run, which is the same instance + written to by the static ``CLI`` methods. See :ref:`modern-commands-writing-output`. .. php:method:: getPrompt(): Prompt - Returns a :php:class:`Prompt ` reading through the - output of the current run. See :ref:`cli-library-classes`. Override it to return a - ``Prompt`` subclass, which then also asks for missing arguments (see - :ref:`prompts-for-missing-input`) and the generators' confirmation. + Returns a :php:class:`Prompt ` reading through the output of the current run. See + :ref:`cli-library-classes`. Override it to return a ``Prompt`` subclass, which then also asks for missing + arguments (see :ref:`prompts-for-missing-input`) and the generators' confirmation. .. php:method:: call(string $command[, array $arguments = [], array $options = [], ?bool $noInteractionOverride = null]): int @@ -753,8 +682,8 @@ covered in the sections above and are not listed here. any forwarded ``--no-interaction`` from ``$options``. :returns: The exit code returned by the called command. - Invokes another modern command. The arguments and options go through - bind and validate on the target command, just like a user invocation. + Invokes another modern command. The arguments and options go through bind and validate on the target command, + just like a user invocation. .. php:method:: callSilently(string $command[, array $arguments = [], array $options = [], ?bool $noInteractionOverride = true]): int @@ -768,41 +697,35 @@ covered in the sections above and are not listed here. .. php:method:: getUnboundArguments(): array - Returns the raw, parsed positional arguments as passed to the - command. + Returns the raw, parsed positional arguments as passed to the command. .. php:method:: getUnboundArgument(int $index): string :param int $index: The zero-based index of the argument to read. - Returns a single raw positional argument. Throws - ``LogicException`` when the index does not exist. + Returns a single raw positional argument. Throws ``LogicException`` when the index does not exist. .. php:method:: getUnboundOptions(): array - Returns the raw, parsed option map, keyed by long name, shortcut, - or negation. + Returns the raw, parsed option map, keyed by long name, shortcut, or negation. .. php:method:: getUnboundOption(string $name[, array|null $options = null]): array|string|null :param string $name: The declared option name to look up. :param array|null $options: Raw option map to read from. Required inside ``initialize()`` and ``interact()``, optional from ``execute()`` onwards. - Returns the raw value the option was given, resolving its shortcut - and negation. Returns ``null`` when the option was not provided — - callers can use the ``??`` operator to supply a fallback, or - :php:meth:`hasUnboundOption` to disambiguate presence from a ``null`` - value. Throws ``LogicException`` when the option is not declared on - this command. + Returns the raw value the option was given, resolving its shortcut and negation. Returns ``null`` when the + option was not provided — callers can use the ``??`` operator to supply a fallback, or + :php:meth:`hasUnboundOption` to disambiguate presence from a ``null`` value. Throws ``LogicException`` when the + option is not declared on this command. .. php:method:: hasUnboundOption(string $name[, array|null $options = null]): bool :param string $name: The declared option name to look up. :param array|null $options: Raw option map to read from. Required inside ``initialize()`` and ``interact()``, optional from ``execute()`` onwards. - Returns ``true`` if the option was provided under its long name, - shortcut, or negation. Throws ``LogicException`` when the option is - not declared on this command. + Returns ``true`` if the option was provided under its long name, shortcut, or negation. Throws + ``LogicException`` when the option is not declared on this command. .. php:method:: getValidatedArguments(): array @@ -812,8 +735,8 @@ covered in the sections above and are not listed here. :param string $name: The declared argument name to read. - Returns the bound and validated value for a single argument. Throws - ``LogicException`` when the argument is not declared on this command. + Returns the bound and validated value for a single argument. Throws ``LogicException`` when the argument is not + declared on this command. .. php:method:: getValidatedOptions(): array @@ -823,5 +746,5 @@ covered in the sections above and are not listed here. :param string $name: The declared option name to read. - Returns the bound and validated value for a single option. Throws - ``LogicException`` when the option is not declared on this command. + Returns the bound and validated value for a single option. Throws ``LogicException`` when the option is not + declared on this command. diff --git a/user_guide_src/source/cli/cli_modern_generators.rst b/user_guide_src/source/cli/cli_modern_generators.rst index 10c5472a2566..b08ecb54983b 100644 --- a/user_guide_src/source/cli/cli_modern_generators.rst +++ b/user_guide_src/source/cli/cli_modern_generators.rst @@ -4,18 +4,16 @@ Modern Generator Commands .. versionadded:: 4.8.0 -Generator commands are spark commands that create files from templates. The -built-in ``make:*`` commands documented in :doc:`cli_generators` are the -canonical examples. The modern counterpart of the legacy ``GeneratorTrait`` is -the ``AbstractGeneratorCommand`` base class paired with a ``#[GeneratorCommand]`` -attribute: the attribute describes *what* to generate, and the base class carries -the whole generation pipeline so that a trivial generator needs no method body +Generator commands are spark commands that create files from templates. The built-in ``make:*`` commands documented in +:doc:`cli_generators` are the canonical examples. The modern counterpart of the legacy ``GeneratorTrait`` is the +``AbstractGeneratorCommand`` base class paired with a ``#[GeneratorCommand]`` attribute: the attribute describes *what* +to generate, and the base class carries the whole generation pipeline so that a trivial generator needs no method body at all. .. note:: - ``GeneratorTrait`` is deprecated, but it continues to work for legacy - ``BaseCommand`` generators. See `Migrating From GeneratorTrait`_. + ``GeneratorTrait`` is deprecated, but it continues to work for legacy ``BaseCommand`` generators. See + `Migrating From GeneratorTrait`_. .. contents:: :local: @@ -36,36 +34,33 @@ A minimal generator needs nothing else: Out of the box this command: -- declares a required ``name`` argument, and prompts for it when omitted on an - interactive run (see :ref:`prompts-for-missing-input`); -- declares the ``--namespace`` / ``-n``, ``--suffix`` / ``-s``, and - ``--force`` / ``-f`` options on top of the framework defaults; -- ships a default ``execute()`` that renders the template and writes the class - file, returning ``EXIT_SUCCESS`` or ``EXIT_ERROR``. +- declares a required ``name`` argument, and prompts for it when omitted on an interactive run (see + :ref:`prompts-for-missing-input`); +- declares the ``--namespace`` / ``-n``, ``--suffix`` / ``-s``, and ``--force`` / ``-f`` options on top of the framework + defaults; +- ships a default ``execute()`` that renders the template and writes the class file, returning ``EXIT_SUCCESS`` or + ``EXIT_ERROR``. The ``#[GeneratorCommand]`` Attribute ===================================== The attribute holds the generation configuration: -- ``component`` is the component noun (``Controller``, ``Model``, ...). It drives suffix handling: - an input whose trailing component is spelled with the wrong case is normalized - (``make:widget foowidget`` generates ``FooWidget``), and passing ``--suffix`` appends the - component to inputs that do not already end with it. -- ``template`` is the basename of the fallback view under ``CodeIgniter\Commands\Generators\Views`` - (see :ref:`generator-command-templates` for the lookup order). -- ``directory`` is the optional sub-namespace (and thus subdirectory) the class is generated into, - e.g. ``Widgets`` places classes under ``App\Widgets``. -- ``classNameLang`` is the language string key used as the prompt label when the ``name`` - argument must be asked for interactively. Defaults to the generic - ``CLI.generator.className.default`` label. +- ``component`` is the component noun (``Controller``, ``Model``, ...). It drives suffix handling: an input whose + trailing component is spelled with the wrong case is normalized (``make:widget foowidget`` generates ``FooWidget``), + and passing ``--suffix`` appends the component to inputs that do not already end with it. +- ``template`` is the basename of the fallback view under ``CodeIgniter\Commands\Generators\Views`` (see + :ref:`generator-command-templates` for the lookup order). +- ``directory`` is the optional sub-namespace (and thus subdirectory) the class is generated into, e.g. ``Widgets`` + places classes under ``App\Widgets``. +- ``classNameLang`` is the language string key used as the prompt label when the ``name`` argument must be asked for + interactively. Defaults to the generic ``CLI.generator.className.default`` label. - ``namespace`` optionally pins the root namespace, ignoring the ``--namespace`` option. -- ``sortImports`` (default ``true``) controls whether the first contiguous ``use`` block of the - generated content is sorted alphabetically. +- ``sortImports`` (default ``true``) controls whether the first contiguous ``use`` block of the generated content is + sorted alphabetically. -Like ``#[Command]``, the attribute validates its inputs at construction time: -``component`` must be a class-name fragment (a letter followed by letters, digits, -or underscores), ``template`` must not be empty, and ``directory`` and +Like ``#[Command]``, the attribute validates its inputs at construction time: ``component`` must be a class-name +fragment (a letter followed by letters, digits, or underscores), ``template`` must not be empty, and ``directory`` and ``namespace``, when given, must be valid backslash-separated namespace fragments. .. _generator-command-templates: @@ -74,9 +69,8 @@ or underscores), ``template`` must not be empty, and ``directory`` and Templates ********* -Templates are regular view files. Because they are rendered through ``view()``, -a template cannot open with a literal PHP tag, so the pipeline recognizes a few -pseudo-placeholders that are substituted after rendering: +Templates are regular view files. Because they are rendered through ``view()``, a template cannot open with a literal +PHP tag, so the pipeline recognizes a few pseudo-placeholders that are substituted after rendering: - ``<@php`` becomes ``generateClass()``, which walks -through these steps: - -1. The ``name`` argument is normalized: segments are converted to PascalCase, a - miscased trailing component is fixed, and the component suffix is appended - when applicable. The result is qualified against the root namespace and the - attribute's ``directory``. A result that is not a valid class name (for example, - one containing ``..`` segments) aborts with ``EXIT_ERROR``. -2. The target path is derived from the autoloader's mapping for the root - namespace. An unknown namespace aborts with ``EXIT_ERROR`` and an error - message. -3. The template is rendered with the view data, placeholders are replaced, and - the imports are sorted when ``sortImports`` is enabled. -4. Safety checks run before writing: generating into the ``CodeIgniter`` - namespace asks for confirmation on interactive runs (and proceeds with a - warning on non-interactive ones), and an existing target file aborts with - ``EXIT_ERROR`` unless ``--force`` is passed. - -Generators that produce more than one artifact can call ``generateView()`` for -non-class files, and may reassign the mutable ``$template`` / ``$templatePath`` -properties between generation calls. +The default ``execute()`` simply returns ``$this->generateClass()``, which walks through these steps: + +1. The ``name`` argument is normalized: segments are converted to PascalCase, a miscased trailing component is fixed, + and the component suffix is appended when applicable. The result is qualified against the root namespace and the + attribute's ``directory``. A result that is not a valid class name (for example, one containing ``..`` segments) + aborts with ``EXIT_ERROR``. +2. The target path is derived from the autoloader's mapping for the root namespace. An unknown namespace aborts with + ``EXIT_ERROR`` and an error message. +3. The template is rendered with the view data, placeholders are replaced, and the imports are sorted when + ``sortImports`` is enabled. +4. Safety checks run before writing: generating into the ``CodeIgniter`` namespace asks for confirmation on interactive + runs (and proceeds with a warning on non-interactive ones), and an existing target file aborts with ``EXIT_ERROR`` + unless ``--force`` is passed. + +Generators that produce more than one artifact can call ``generateView()`` for non-class files, and may reassign the +mutable ``$template`` / ``$templatePath`` properties between generation calls. ******************* Customization Hooks @@ -137,61 +126,56 @@ Customization Hooks Placeholders and View Data ========================== -Most real generators need more than ``{namespace}`` and ``{class}``. Override -``getReplacements()`` to add placeholder substitutions and ``getTemplateData()`` -to pass variables into the view: +Most real generators need more than ``{namespace}`` and ``{class}``. Override ``getReplacements()`` to add placeholder +substitutions and ``getTemplateData()`` to pass variables into the view: .. literalinclude:: cli_modern_generators/003.php -Entries returned by ``getReplacements()`` take precedence over the core pairs, -so a generator can even override how ``{namespace}`` or ``{class}`` is derived. +Entries returned by ``getReplacements()`` take precedence over the core pairs, so a generator can even override how +``{namespace}`` or ``{class}`` is derived. Trimming Options and Forcing the Suffix ======================================= -The common generator options are registered through ``provideGeneratorOptions()``. -Override it to drop options that make no sense for your generator, and override -``shouldAppendSuffix()`` when suffixing is not driven by the ``--suffix`` flag: +The common generator options are registered through ``provideGeneratorOptions()``. Override it to drop options that make +no sense for your generator, and override ``shouldAppendSuffix()`` when suffixing is not driven by the ``--suffix`` +flag: .. literalinclude:: cli_modern_generators/004.php -The base class guards every read of ``--suffix`` and ``--force``, so omitting -them is safe. +The base class guards every read of ``--suffix`` and ``--force``, so omitting them is safe. Other Hooks =========== -- ``basename(string $filename): string`` changes the file basename before saving. - Useful for components whose file name carries a date, like migrations. -- ``getNamespace(): string`` resolves the root namespace. The default returns the attribute's - ``namespace`` or the ``--namespace`` option. -- ``buildPath(string $class): string`` maps the qualified class to a file path through the - autoloader. Override for components with special file locations, like tests. -- ``getBasePath(string $namespace): ?string`` picks the directory registered for the namespace. - The default takes the first autoloader entry. Override when the namespace maps to several - directories and a specific one is wanted, like the ``tests/`` copy of ``CodeIgniter``. +- ``basename(string $filename): string`` changes the file basename before saving. Useful for components whose file name + carries a date, like migrations. +- ``getNamespace(): string`` resolves the root namespace. The default returns the attribute's ``namespace`` or the + ``--namespace`` option. +- ``buildPath(string $class): string`` maps the qualified class to a file path through the autoloader. Override for + components with special file locations, like tests. +- ``getBasePath(string $namespace): ?string`` picks the directory registered for the namespace. The default takes the + first autoloader entry. Override when the namespace maps to several directories and a specific one is wanted, like the + ``tests/`` copy of ``CodeIgniter``. - ``renderTemplate(array $data = []): string`` renders the resolved view. **************************** Prompting for the Class Name **************************** -``AbstractGeneratorCommand`` implements ``PromptsForMissingInputInterface``, so -running a generator interactively without a class name prompts for it instead of -failing validation. The prompt label comes from the attribute's ``classNameLang`` -language key. Non-interactive runs (piped input, ``--no-interaction``) fail fast -with the usual missing-arguments error. +``AbstractGeneratorCommand`` implements ``PromptsForMissingInputInterface``, so running a generator interactively +without a class name prompts for it instead of failing validation. The prompt label comes from the attribute's +``classNameLang`` language key. Non-interactive runs (piped input, ``--no-interaction``) fail fast with the usual +missing-arguments error. -The mechanism is available to every modern command, not just generators. See -:ref:`prompts-for-missing-input`. +The mechanism is available to every modern command, not just generators. See :ref:`prompts-for-missing-input`. ********************************* Migrating From ``GeneratorTrait`` ********************************* -The base class covers everything the trait did, but the configuration moves -from mutable properties and the ``$params`` array to the attribute and the -validated input accessors. +The base class covers everything the trait did, but the configuration moves from mutable properties and the ``$params`` +array to the attribute and the validated input accessors. **Configuration properties** @@ -256,22 +240,21 @@ A typical ``GeneratorTrait`` generator: .. literalinclude:: cli_modern_generators/001.php -Behavioural changes we need to be aware of when migrating: - -- **Failures produce failing exit codes.** The trait's ``generateClass()`` returned ``void``, so a - generator reported ``EXIT_SUCCESS`` even when the target file already existed. The base class - returns ``EXIT_ERROR`` for an existing file without ``--force``, a write failure, and an undefined - namespace. Declining the ``CodeIgniter`` namespace confirmation still exits with ``EXIT_SUCCESS``. -- **The** ``CodeIgniter`` **namespace confirmation is asked once, and only on interactive runs.** The - trait asked for every file. The base class asks once per ``generateClass()`` and reuses the answer for - the views generated after it. Non-interactive runs print the warning and proceed instead of blocking - on input that will never arrive. -- **The** ``CodeIgniter`` **namespace confirmation keys off the target path.** The trait compared - the raw ``--namespace`` option, so an attribute-pinned ``CodeIgniter`` namespace or a spelling like - ``--namespace CodeIgniter/`` did not warn. The base class warns exactly when the file would be written - under ``SYSTEMPATH``, so a ``CodeIgniter\Foo`` test class headed for ``tests/system`` is left alone. -- **Placeholder replacement is single-pass.** Replacements are applied with ``strtr()``, so a - replacement value that happens to contain another placeholder is no longer substituted again. +Behavioral changes to be aware of when migrating: + +- **Failures produce failing exit codes.** The trait's ``generateClass()`` returned ``void``, so a generator reported + ``EXIT_SUCCESS`` even when the target file already existed. The base class returns ``EXIT_ERROR`` for an existing file + without ``--force``, a write failure, and an undefined namespace. Declining the ``CodeIgniter`` namespace confirmation + still exits with ``EXIT_SUCCESS``. +- **The** ``CodeIgniter`` **namespace confirmation is asked once, and only on interactive runs.** The trait asked for + every file. The base class asks once per ``generateClass()`` and reuses the answer for the views generated after it. + Non-interactive runs print the warning and proceed instead of blocking on input that will never arrive. +- **The** ``CodeIgniter`` **namespace confirmation keys off the target path.** The trait compared the raw + ``--namespace`` option, so an attribute-pinned ``CodeIgniter`` namespace or a spelling like + ``--namespace CodeIgniter/`` did not warn. The base class warns exactly when the file would be written under + ``SYSTEMPATH``, so a ``CodeIgniter\Foo`` test class headed for ``tests/system`` is left alone. +- **Placeholder replacement is single-pass.** Replacements are applied with ``strtr()``, so a replacement value that + happens to contain another placeholder is no longer substituted again. ************************ AbstractGeneratorCommand @@ -281,34 +264,33 @@ AbstractGeneratorCommand .. php:class:: AbstractGeneratorCommand - All hooks below are ``protected``: they are called or overridden from within - your own generator, never from the outside. + All hooks below are ``protected``: they are called or overridden from within your own generator, never from the + outside. .. php:method:: generateClass(): int - Runs the full pipeline for the qualified class name and returns an - ``EXIT_*`` status. This is what the default ``execute()`` calls. + Runs the full pipeline for the qualified class name and returns an ``EXIT_*`` status. This is what the default + ``execute()`` calls. .. php:method:: generateView(string $view): int :param string $view: Namespaced view name to generate. - Like :php:meth:`generateClass`, but for non-class artifacts. The name is - used as-is, without qualification or suffix handling. + Like :php:meth:`generateClass`, but for non-class artifacts. The name is used as-is, without qualification or + suffix handling. .. php:method:: isCodeIgniterNamespaceDeclined(): bool - Whether the ``CodeIgniter`` namespace confirmation was declined since the - last :php:meth:`generateClass` call. A declined run still returns - ``EXIT_SUCCESS``, so check this before generating anything that depends - on the declined file. + Whether the ``CodeIgniter`` namespace confirmation was declined since the last :php:meth:`generateClass` call. A + declined run still returns ``EXIT_SUCCESS``, so check this before generating anything that depends on the + declined file. .. php:method:: getReplacements(string $class): array :param string $class: The namespaced class (or view) being generated. - Returns extra placeholder replacements. Entries take precedence over the - core ``{namespace}`` / ``{class}`` pairs. Defaults to ``[]``. + Returns extra placeholder replacements. Entries take precedence over the core ``{namespace}`` / ``{class}`` + pairs. Defaults to ``[]``. .. php:method:: getTemplateData(string $class): array @@ -318,48 +300,43 @@ AbstractGeneratorCommand .. php:method:: shouldAppendSuffix(): bool - Whether the component suffix should be appended to the class name. The - default reads the ``--suffix`` flag when it is declared. + Whether the component suffix should be appended to the class name. The default reads the ``--suffix`` flag when + it is declared. .. php:method:: basename(string $filename): string :param string $filename: The computed target file path. - Returns the file basename to save under. Override to decorate it, for - example with a timestamp. + Returns the file basename to save under. Override to decorate it, for example with a timestamp. .. php:method:: getNamespace(): string - Resolves the root namespace from the attribute override or the - ``--namespace`` option. + Resolves the root namespace from the attribute override or the ``--namespace`` option. .. php:method:: buildPath(string $class): string :param string $class: The namespaced class (or view) being generated. - Maps the class to its target file path through the autoloader. Returns - an empty string (after printing an error) when the namespace is not - registered. + Maps the class to its target file path through the autoloader. Returns an empty string (after printing an error) + when the namespace is not registered. .. php:method:: getBasePath(string $namespace): ?string :param string $namespace: The resolved root namespace. - Returns the directory registered for the namespace, or ``null`` when it - is not registered. The default takes the first autoloader entry. + Returns the directory registered for the namespace, or ``null`` when it is not registered. The default takes the + first autoloader entry. .. php:method:: renderTemplate(array $data = []): string :param array $data: View data for the template. - Renders the resolved generator view (see - :ref:`generator-command-templates` for the resolution order). + Renders the resolved generator view (see :ref:`generator-command-templates` for the resolution order). .. php:method:: provideGeneratorOptions(): void - Registers the common generator options. The default registers all three - of ``--namespace``, ``--suffix``, and ``--force`` through the ``final`` - helpers :php:meth:`addNamespaceOption`, :php:meth:`addSuffixOption`, and + Registers the common generator options. The default registers all three of ``--namespace``, ``--suffix``, and + ``--force`` through the ``final`` helpers :php:meth:`addNamespaceOption`, :php:meth:`addSuffixOption`, and :php:meth:`addForceOption`. Override to register a subset. .. php:method:: addNamespaceOption(string $default = APP_NAMESPACE): static diff --git a/user_guide_src/source/cli/cli_request.rst b/user_guide_src/source/cli/cli_request.rst index de19120da6fe..0fb31c8c2ce8 100644 --- a/user_guide_src/source/cli/cli_request.rst +++ b/user_guide_src/source/cli/cli_request.rst @@ -38,8 +38,8 @@ Returns the value of a specific command line argument deemed to be an option: .. literalinclude:: cli_request/004.php -.. note:: Starting in v4.8.0, if the option you are trying to access is an array, this method will return - the last value in the array. Use ``getRawOption()`` to get the full array of values for that option. +.. note:: Starting in v4.8.0, if the option you are trying to access is an array, this method will return the last value + in the array. Use ``getRawOption()`` to get the full array of values for that option. getRawOption($key) ------------------ diff --git a/user_guide_src/source/cli/spark_commands.rst b/user_guide_src/source/cli/spark_commands.rst index 6e0ddd6b626d..8e9b7e05cdeb 100644 --- a/user_guide_src/source/cli/spark_commands.rst +++ b/user_guide_src/source/cli/spark_commands.rst @@ -45,8 +45,7 @@ sorted alphabetically: php spark list --simple -.. note:: :ref:`Hidden commands ` are left out of both lists, but still run - when called by name. +.. note:: :ref:`Hidden commands ` are left out of both lists, but still run when called by name. Showing Help ------------ @@ -109,8 +108,7 @@ You may always pass ``--no-header`` to suppress the header output, helpful for p Your environment is currently set as development. -.. note:: A modern command can also opt out of the header for every run. - See :ref:`commands-without-the-header`. +.. note:: A modern command can also opt out of the header for every run. See :ref:`commands-without-the-header`. .. _spark-verbosity: @@ -119,24 +117,21 @@ Verbosity .. versionadded:: 4.8.0 -``--quiet`` / ``-q`` hides everything but errors, including the header, and makes a -modern command non-interactive. ``--silent`` hides the errors too. ``--verbose`` / ``-v`` -shows more detail, ``-vv`` even more, and ``-vvv`` everything, including debug messages. -A command shows the extra detail only when it writes some, and an uncaught exception -shows its backtrace from ``-v`` on. +``--quiet`` / ``-q`` hides everything but errors, including the header, and makes a modern command non-interactive. +``--silent`` hides the errors too. ``--verbose`` / ``-v`` shows more detail, ``-vv`` even more, and ``-vvv`` everything, +including debug messages. A command shows the extra detail only when it writes some, and an uncaught exception shows its +backtrace from ``-v`` on. -The flags can be combined, and the quietest one wins: ``--silent`` over ``-q``, and ``-q`` -over any ``-v``. +The flags can be combined, and the quietest one wins: ``--silent`` over ``-q``, and ``-q`` over any ``-v``. .. code-block:: console php spark lang:find -v php spark migrate -q -Without these flags, the ``SHELL_VERBOSITY`` environment variable sets the level: -``-2`` for no output at all, ``-1`` for quiet, ``0`` for normal, and ``1`` to ``3`` -for the three verbose levels. It can also be set in the **.env** file. Processes started -by the command inherit the level. +Without these flags, the ``SHELL_VERBOSITY`` environment variable sets the level: ``-2`` for no output at all, ``-1`` +for quiet, ``0`` for normal, and ``1`` to ``3`` for the three verbose levels. It can also be set in the **.env** file. +Processes started by the command inherit the level. .. code-block:: console @@ -151,12 +146,11 @@ Correcting a Mistyped Command .. versionadded:: 4.8.0 -When the command name is not found, **spark** lists the closest matches. On an interactive -run it also offers to run one of them: a single match asks for a ``y``/``n`` confirmation, and -several matches present a menu with a "none of these" entry, picked with the arrow keys (see -:ref:`cli-library-select`). Pressing Enter picks the highlighted entry. Non-interactive runs -(``--no-interaction`` / ``-N``, or piped input) only print the suggestions and exit with -an error, as before. +When the command name is not found, **spark** lists the closest matches. On an interactive run it also offers to run one +of them: a single match asks for a ``y``/``n`` confirmation, and several matches present a menu with a "none of these" +entry, picked with the arrow keys (see :ref:`cli-library-select`). Pressing Enter picks the highlighted entry. +Non-interactive runs (``--no-interaction`` / ``-N``, or piped input) only print the suggestions and exit with an error, +as before. .. code-block:: console @@ -183,6 +177,6 @@ All output from the command that is ran is captured when not run from the comman so that you can choose to display it or not. .. note:: The ``command()`` function does not catch exceptions thrown while the command runs. - :doc:`Modern commands ` in particular throw when their input fails - validation, such as a missing required argument or an unknown option. Wrap the call in a - ``try``/``catch`` block if you need to handle these cases. + :doc:`Modern commands ` in particular throw when their input fails validation, such as a + missing required argument or an unknown option. Wrap the call in a ``try``/``catch`` block if you need to handle + these cases. diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst index 460717d8435a..b1d31eaf4e3d 100644 --- a/user_guide_src/source/database/configuration.rst +++ b/user_guide_src/source/database/configuration.rst @@ -175,7 +175,7 @@ Description of Values **foreignKeys** (``SQLite3`` only) true/false (boolean) - Whether to enable Foreign Key constraint. **busyTimeout** (``SQLite3`` only) milliseconds (int) - Sleeps for a specified amount of time when a table is locked. **synchronous** (``SQLite3`` only) flag (int) - How strict SQLite will be at flushing to disk during transactions. - Use `null` to stay with the default setting. This can be used since v4.6.0. + Use ``null`` to stay with the default setting. This can be used since v4.6.0. **numberNative** (``MySQLi`` only) true/false (boolean) - Whether to enable MYSQLI_OPT_INT_AND_FLOAT_NATIVE. **foundRows** (``MySQLi`` only) true/false (boolean) - Whether to enable MYSQLI_CLIENT_FOUND_ROWS. **dateFormat** The default date/time formats as PHP's `DateTime format`_, with the keys listed below the table. @@ -206,12 +206,12 @@ but :ref:`Model ` uses them. The **timezone** setting accepts the following values: -* ``false`` - Don't set session timezone (default, backward compatible) -* ``true`` - Automatically sync with ``App::$appTimezone`` -* ``string`` - Specific timezone offset (e.g., ``'+05:30'``) or named timezone (e.g., ``'America/New_York'``) +* ``false`` - Don't set session timezone (default, backward compatible) +* ``true`` - Automatically sync with ``App::$appTimezone`` +* ``string`` - Specific timezone offset (e.g., ``'+05:30'``) or named timezone (e.g., ``'America/New_York'``) -Named timezones are automatically converted to offsets for database compatibility. -See :ref:`database-config-timezone` for details. +Named timezones are automatically converted to offsets for database compatibility. See :ref:`database-config-timezone` +for details. .. note:: Depending on what database driver you are using (``MySQLi``, ``Postgre``, etc.) not all values will be needed. For example, when using ``SQLite3`` you @@ -255,13 +255,12 @@ timezone .. versionadded:: 4.8.0 -Synchronizes the database session timezone with your application timezone to ensure consistent -timestamps between model operations and database functions like ``NOW()``. +Synchronizes the database session timezone with your application timezone to ensure consistent timestamps between model +operations and database functions like ``NOW()``. -.. note:: Modern database environments usually have UTC set by default, so this option may not be needed - in most cases. +.. note:: Modern database environments usually have UTC set by default, so this option may not be needed in most cases. -Accepts ``false`` (default, don't set), ``true`` (auto-sync with ``App::$appTimezone``), -or a timezone string (e.g., ``'+05:30'`` or ``'America/New_York'``). +Accepts ``false`` (default, don't set), ``true`` (auto-sync with ``App::$appTimezone``), or a timezone string (e.g., +``'+05:30'`` or ``'America/New_York'``). Named timezones are automatically converted to offsets for compatibility. diff --git a/user_guide_src/source/database/queries.rst b/user_guide_src/source/database/queries.rst index b34eb37a5428..e64e35684d69 100644 --- a/user_guide_src/source/database/queries.rst +++ b/user_guide_src/source/database/queries.rst @@ -139,8 +139,7 @@ don't have to: .. literalinclude:: queries/009.php .. versionadded:: 4.8.0 - ``$db->escape()`` accepts PHP ``BackedEnum`` values and escapes their backing - values. + ``$db->escape()`` accepts PHP ``BackedEnum`` values and escapes their backing values. 2. $db->escapeString() ====================== @@ -193,9 +192,8 @@ automatically escaped producing safer queries. You don't have to remember to manually escape data - the engine does it automatically for you. .. versionadded:: 4.8.0 - Query bindings, prepared query values, and Query Builder bound values accept - PHP ``BackedEnum`` values. CodeIgniter uses the enum backing value before - escaping or passing the value to the database driver. + Query bindings, prepared query values, and Query Builder bound values accept PHP ``BackedEnum`` values. CodeIgniter + uses the enum backing value before escaping or passing the value to the database driver. .. literalinclude:: queries/032.php @@ -220,17 +218,15 @@ placeholders in the query: Handling Errors *************** -.. note:: It is strongly recommended to keep ``DBDebug`` set to ``true`` - (the default). This ensures all query failures surface immediately as - exceptions, preventing silent data corruption. Setting it to ``false`` - is considered legacy behaviour and may be deprecated in a future version. +.. note:: It is strongly recommended to keep ``DBDebug`` set to ``true`` (the default). This ensures all query failures + surface immediately as exceptions, preventing silent data corruption. Setting it to ``false`` is considered legacy + behavior and may be deprecated in a future version. DBDebug Enabled (Recommended) ============================= -When :ref:`DBDebug ` is ``true`` -(the default), any query failure throws a ``DatabaseException`` or one of -its subclasses, which you can catch and handle: +When :ref:`DBDebug ` is ``true`` (the default), any query failure throws a +``DatabaseException`` or one of its subclasses, which you can catch and handle: .. literalinclude:: queries/030.php @@ -242,36 +238,32 @@ Constraint Violation Exceptions .. versionadded:: 4.8.0 -Database constraint violations are reported as ``ConstraintViolationException`` -or one of its subclasses when CodeIgniter can identify the native driver error: +Database constraint violations are reported as ``ConstraintViolationException`` or one of its subclasses when +CodeIgniter can identify the native driver error: - ``UniqueConstraintViolationException`` - ``ForeignKeyConstraintViolationException`` - ``NotNullConstraintViolationException`` - ``CheckConstraintViolationException`` -These exceptions allow you to handle common integrity errors without inspecting -raw driver-specific error codes. If a driver reports a constraint violation that -CodeIgniter cannot safely classify more precisely, it throws the parent +These exceptions allow you to handle common integrity errors without inspecting raw driver-specific error codes. If a +driver reports a constraint violation that CodeIgniter cannot safely classify more precisely, it throws the parent ``ConstraintViolationException``. -Some database engines report certain constraint failures with a generic -integrity-constraint code. In those cases CodeIgniter uses the parent -``ConstraintViolationException`` rather than guessing the more specific subtype. +Some database engines report certain constraint failures with a generic integrity-constraint code. In those cases +CodeIgniter uses the parent ``ConstraintViolationException`` rather than guessing the more specific subtype. DBDebug Disabled ================ -When ``DBDebug`` is ``false``, query and prepared query execution failures -return ``false`` instead of throwing. Two methods are available to inspect what -went wrong. +When ``DBDebug`` is ``false``, query and prepared query execution failures return ``false`` instead of throwing. Two +methods are available to inspect what went wrong. $db->error() ------------ -The ``error()`` method returns an array with ``code`` and ``message`` keys -describing the last error. Error codes are driver-specific (an **int** for -MySQLi, SQLite3, and OCI8; a SQLSTATE **string** for Postgre and SQLSRV): +The ``error()`` method returns an array with ``code`` and ``message`` keys describing the last error. Error codes are +driver-specific (an **int** for MySQLi, SQLite3, and OCI8; a SQLSTATE **string** for Postgre and SQLSRV): .. literalinclude:: queries/015.php @@ -282,16 +274,14 @@ $db->getLastException() .. versionadded:: 4.8.0 -``getLastException()`` returns the typed exception that would have been thrown -had ``DBDebug`` been ``true``. This is the recommended way to distinguish -between failure types (e.g., a constraint violation vs. another database error) +``getLastException()`` returns the typed exception that would have been thrown had ``DBDebug`` been ``true``. This is +the recommended way to distinguish between failure types (e.g., a constraint violation vs. another database error) without enabling ``DBDebug``: .. literalinclude:: queries/031.php -.. note:: ``getLastException()`` is reset to ``null`` at the start of every - query or prepared query execution. Inspect it immediately after the failed - operation. +.. note:: ``getLastException()`` is reset to ``null`` at the start of every query or prepared query execution. Inspect + it immediately after the failed operation. **************** Prepared Queries @@ -339,8 +329,7 @@ query: .. literalinclude:: queries/019.php .. versionadded:: 4.8.0 - Prepared query values accept PHP ``BackedEnum`` values and pass their - backing values to the database driver. + Prepared query values accept PHP ``BackedEnum`` values and pass their backing values to the database driver. For queries of type "write" it returns true or false, indicating the success or failure of the query. For queries of type "read" it returns a standard :doc:`result set `. diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index 903670506513..cc0de9d3b7cc 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -113,9 +113,10 @@ utilize ``limit(10, 20)`` but the generated SQL query has ``LIMIT 20, 10``. The reason for this outcome is because the parameter in the first query is set to ``false``, ``limit(10, 20)`` remained in the second query. .. note:: - **Changed in version 4.8.0.** With ``$reset = true`` (the default), ``getCompiledSelect()``, ``getCompiledInsert()``, ``getCompiledUpdate()`` and ``getCompiledDelete()`` - also clear their bindings and binding counters after compilation. ``getBinds()`` therefore returns an empty array after these calls. - Pass ``false`` to retain the query clauses and bindings for reuse. + **Changed in version 4.8.0.** With ``$reset = true`` (the default), ``getCompiledSelect()``, + ``getCompiledInsert()``, ``getCompiledUpdate()`` and ``getCompiledDelete()`` also clear their bindings and binding + counters after compilation. ``getBinds()`` therefore returns an empty array after these calls. Pass ``false`` to + retain the query clauses and bindings for reuse. .. _query-builder-explain: @@ -128,19 +129,16 @@ Runs an execution-plan query for the current Query Builder ``SELECT`` query: .. literalinclude:: query_builder/129.php -This method returns a database result object. The result columns are driver-specific -because each database reports execution plans in its own format. -You can read the result with ``getResultArray()`` or ``getResultObject()``, the -same as any other query result. -If test mode is enabled, it returns the compiled execution-plan SQL string. -If the query fails and ``DBDebug`` is ``false``, it returns ``false``. +This method returns a database result object. The result columns are driver-specific because each database reports +execution plans in its own format. You can read the result with ``getResultArray()`` or ``getResultObject()``, the same +as any other query result. If test mode is enabled, it returns the compiled execution-plan SQL string. If the query +fails and ``DBDebug`` is ``false``, it returns ``false``. -The method resets the current Query Builder state by default. If you need to keep -the current Query Builder state, you can pass ``false`` as the first parameter. +The method resets the current Query Builder state by default. If you need to keep the current Query Builder state, you +can pass ``false`` as the first parameter. -.. note:: This method is currently supported by MySQLi, Postgre, and SQLite3. - SQLite3 uses ``EXPLAIN QUERY PLAN``. SQLSRV and OCI8 are not supported by - this method. +.. note:: This method is currently supported by MySQLi, Postgre, and SQLite3. SQLite3 uses ``EXPLAIN QUERY PLAN``. + SQLSRV and OCI8 are not supported by this method. $builder->getWhere() -------------------- @@ -308,8 +306,7 @@ Since v4.2.0, ``$builder->join()`` accepts a ``CodeIgniter\Database\RawSql`` ins .. literalinclude:: query_builder/102.php -Since v4.8.0, the table can also be a ``RawSql`` instance. For example, you -can join a subquery: +Since v4.8.0, the table can also be a ``RawSql`` instance. For example, you can join a subquery: .. literalinclude:: query_builder/134.php @@ -335,11 +332,9 @@ methods: ``false``, CodeIgniter will not try to protect your field or table names. .. versionadded:: 4.8.0 - Query Builder methods that bind and escape field values or value lists, - such as ``where()``, ``whereIn()``, ``having()``, ``set()``, ``insert()``, - and ``update()``, accept PHP ``BackedEnum`` instances and use their backing - values. Raw SQL values, such as values passed with escaping disabled, are - not converted. + Query Builder methods that bind and escape field values or value lists, such as ``where()``, ``whereIn()``, + ``having()``, ``set()``, ``insert()``, and ``update()``, accept PHP ``BackedEnum`` instances and use their backing + values. Raw SQL values, such as values passed with escaping disabled, are not converted. 1. Simple key/value method ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -416,28 +411,22 @@ $builder->whereColumn() .. versionadded:: 4.8.0 -Compares one column to another column. If the first parameter does not end with -a supported operator, ``=`` is used: +Compares one column to another column. If the first parameter does not end with a supported operator, ``=`` is used: .. literalinclude:: query_builder/123.php -You can include a supported operator at the end of the first parameter in order -to control the comparison. Supported operators are ``=``, ``!=``, ``<>``, ``<``, -``>``, ``<=``, and ``>=``. If none of these operators is detected at the end of the first -parameter, ``=`` is used. Empty -column names throw an ``InvalidArgumentException``. +You can include a supported operator at the end of the first parameter to control the comparison. Supported operators +are ``=``, ``!=``, ``<>``, ``<``, ``>``, ``<=``, and ``>=``. Empty column names throw an ``InvalidArgumentException``. -Column names are protected by default, unless the ``$escape`` parameter is -``false``. +Column names are protected by default, unless the ``$escape`` parameter is ``false``. -.. warning:: Do not pass user-supplied data as column names. Values should use - ``where()`` or another value-binding method instead. +.. warning:: Do not pass user-supplied data as column names. Values should use ``where()`` or another value-binding + method instead. $builder->orWhereColumn() ------------------------- -This method is identical to ``whereColumn()``, except that multiple instances -are joined by **OR**. +This method is identical to ``whereColumn()``, except that multiple instances are joined by **OR**. .. _query-builder-where-between: @@ -446,36 +435,30 @@ $builder->whereBetween() .. versionadded:: 4.8.0 -Generates a **WHERE** field ``BETWEEN`` minimum and maximum value SQL query. -``BETWEEN`` includes both values: +Generates a **WHERE** field ``BETWEEN`` minimum and maximum value SQL query. ``BETWEEN`` includes both values: .. literalinclude:: query_builder/126.php -The range array must contain exactly two values: the lower and upper bounds. -These values are bound and escaped automatically. The ``$escape`` parameter -controls value escaping and identifier protection. +The range array must contain exactly two values: the lower and upper bounds. These values are bound and escaped +automatically. The ``$escape`` parameter controls value escaping and identifier protection. -.. warning:: Do not pass user-supplied data as field names. If you need a more - complex SQL expression, use ``where()`` with :ref:`RawSql ` - and escape values manually. +.. warning:: Do not pass user-supplied data as field names. If you need a more complex SQL expression, use ``where()`` + with :ref:`RawSql ` and escape values manually. $builder->orWhereBetween() -------------------------- -This method is identical to ``whereBetween()``, except that multiple instances -are joined by **OR**. +This method is identical to ``whereBetween()``, except that multiple instances are joined by **OR**. $builder->whereNotBetween() --------------------------- -This method is identical to ``whereBetween()``, except that it generates -``NOT BETWEEN``. +This method is identical to ``whereBetween()``, except that it generates ``NOT BETWEEN``. $builder->orWhereNotBetween() ----------------------------- -This method is identical to ``whereNotBetween()``, except that multiple -instances are joined by **OR**. +This method is identical to ``whereNotBetween()``, except that multiple instances are joined by **OR**. .. _query-builder-where-exists: @@ -484,32 +467,27 @@ $builder->whereExists() .. versionadded:: 4.8.0 -Generates a ``WHERE EXISTS`` subquery. This method accepts either a Closure or -a ``BaseBuilder`` instance: +Generates a ``WHERE EXISTS`` subquery. This method accepts either a Closure or a ``BaseBuilder`` instance: .. literalinclude:: query_builder/125.php -.. warning:: Raw SQL strings are not accepted. If you need to write the - ``EXISTS`` clause yourself, use ``where()`` with a manually escaped - condition. See :ref:`query-builder-where-rawsql`. +.. warning:: Raw SQL strings are not accepted. If you need to write the ``EXISTS`` clause yourself, use ``where()`` with + a manually escaped condition. See :ref:`query-builder-where-rawsql`. $builder->orWhereExists() ------------------------- -This method is identical to ``whereExists()``, except that multiple instances -are joined by **OR**. +This method is identical to ``whereExists()``, except that multiple instances are joined by **OR**. $builder->whereNotExists() -------------------------- -This method is identical to ``whereExists()``, except that it generates a -``WHERE NOT EXISTS`` subquery. +This method is identical to ``whereExists()``, except that it generates a ``WHERE NOT EXISTS`` subquery. $builder->orWhereNotExists() ---------------------------- -This method is identical to ``whereNotExists()``, except that multiple -instances are joined by **OR**. +This method is identical to ``whereNotExists()``, except that multiple instances are joined by **OR**. $builder->whereIn() ------------------- @@ -620,15 +598,14 @@ $builder->likeAny() .. versionadded:: 4.8.0 -This method generates a grouped set of **LIKE** clauses joined by **OR**. -Use it when you want to search for one value across multiple fields: +This method generates a grouped set of **LIKE** clauses joined by **OR**. Use it when you want to search for one value +across multiple fields: .. literalinclude:: query_builder/130.php -Unlike the associative array form of ``like()``, the field list must be a -non-empty list of field names. The same match value is used for every field. -The field list may also contain ``RawSql`` instances; see :ref:`query-builder-like-rawsql`. -Use ``orLikeAny()`` when the grouped search should be separated from previous +Unlike the associative array form of ``like()``, the field list must be a non-empty list of field names. The same match +value is used for every field. The field list may also contain ``RawSql`` instances; see +:ref:`query-builder-like-rawsql`. Use ``orLikeAny()`` when the grouped search should be separated from previous conditions with **OR**. $builder->orLike() @@ -703,36 +680,30 @@ $builder->havingBetween() .. versionadded:: 4.8.0 -Generates a **HAVING** field ``BETWEEN`` minimum and maximum value SQL query. -``BETWEEN`` includes both values: +Generates a **HAVING** field ``BETWEEN`` minimum and maximum value SQL query. ``BETWEEN`` includes both values: .. literalinclude:: query_builder/127.php -The range array must contain exactly two values: the lower and upper bounds. -These values are bound and escaped automatically. The ``$escape`` parameter -controls value escaping and identifier protection. +The range array must contain exactly two values: the lower and upper bounds. These values are bound and escaped +automatically. The ``$escape`` parameter controls value escaping and identifier protection. -.. warning:: Do not pass user-supplied data as field names. If you need a more - complex SQL expression, use ``having()`` with :ref:`RawSql ` - and escape values manually. +.. warning:: Do not pass user-supplied data as field names. If you need a more complex SQL expression, use ``having()`` + with :ref:`RawSql ` and escape values manually. $builder->orHavingBetween() --------------------------- -This method is identical to ``havingBetween()``, except that multiple instances -are joined by **OR**. +This method is identical to ``havingBetween()``, except that multiple instances are joined by **OR**. $builder->havingNotBetween() ---------------------------- -This method is identical to ``havingBetween()``, except that it generates -``NOT BETWEEN``. +This method is identical to ``havingBetween()``, except that it generates ``NOT BETWEEN``. $builder->orHavingNotBetween() ------------------------------ -This method is identical to ``havingNotBetween()``, except that multiple -instances are joined by **OR**. +This method is identical to ``havingNotBetween()``, except that multiple instances are joined by **OR**. $builder->havingIn() -------------------- @@ -914,10 +885,9 @@ first parameter. .. literalinclude:: query_builder/073.php .. note:: - **Changed in version 4.8.0.** With ``$reset = true`` (the default), bindings and - binding counters are cleared after execution, so ``getBinds()`` returns an empty - array. Pass ``false`` to retain the query conditions and bindings. Test mode - retains bindings for the placeholders in its returned SQL. + **Changed in version 4.8.0.** With ``$reset = true`` (the default), bindings and binding counters are cleared after + execution, so ``getBinds()`` returns an empty array. Pass ``false`` to retain the query conditions and bindings. + Test mode retains bindings for the placeholders in its returned SQL. .. _query-builder-exists: @@ -926,30 +896,24 @@ $builder->exists() .. versionadded:: 4.8.0 -Permits you to determine whether the current Query Builder query would return -at least one row: +Permits you to determine whether the current Query Builder query would return at least one row: .. literalinclude:: query_builder/128.php -This method returns ``true`` when the query would return at least one row and -``false`` when it would not. It respects any existing ``limit()`` and -``offset()`` clauses, and resets the current Query Builder state by default. If -you need to keep the current Query Builder state, you can pass ``false`` as the -first parameter. -If the existence query fails and ``DBDebug`` is ``false``, both methods return -``false``. +This method returns ``true`` when the query would return at least one row and ``false`` when it would not. It respects +any existing ``limit()`` and ``offset()`` clauses, and resets the current Query Builder state by default. If you need to +keep the current Query Builder state, you can pass ``false`` as the first parameter. If the existence query fails and +``DBDebug`` is ``false``, both methods return ``false``. $builder->doesntExist() ----------------------- .. versionadded:: 4.8.0 -This method is identical to ``exists()``, except that it returns ``true`` when -the query would not return any rows. +This method is identical to ``exists()``, except that it returns ``true`` when the query would not return any rows. -.. note:: These methods execute the current Query Builder query to check for - rows. To add an SQL ``EXISTS`` predicate to a query, use - :ref:`query-builder-where-exists`. +.. note:: These methods execute the current Query Builder query to check for rows. To add an SQL ``EXISTS`` predicate to + a query, use :ref:`query-builder-where-exists`. $builder->countAll() -------------------- @@ -963,17 +927,16 @@ As is in ``countAllResult()`` method, this method resets any field values that y to ``select()`` as well. If you need to keep them, you can pass ``false`` as the first parameter. -******************** +******************* Pessimistic Locking -******************** +******************* .. _query-builder-pessimistic-locking-driver-support: Driver Support ============== -The following table summarizes Query Builder support for pessimistic locks and -lock-wait modifiers: +The following table summarizes Query Builder support for pessimistic locks and lock-wait modifiers: .. list-table:: :header-rows: 1 @@ -1034,9 +997,8 @@ lock-wait modifiers: ``READCOMMITTEDLOCK`` and ``READPAST`` table hints. It can still wait on locks that ``READPAST`` does not skip, such as page-level locks. -.. note:: ``countAllResults()``, ``exists()``, and ``doesntExist()`` do not apply - pessimistic locks or lock-wait modifiers. Consequently, they report matching - rows rather than rows currently available for locking. +.. note:: ``countAllResults()``, ``exists()``, and ``doesntExist()`` do not apply pessimistic locks or lock-wait + modifiers. Consequently, they report matching rows rather than rows currently available for locking. .. _query-builder-shared-lock: @@ -1048,36 +1010,29 @@ $builder->sharedLock() .. versionadded:: 4.8.0 -Adds a pessimistic read lock to a ``SELECT`` query. This is useful when rows -must be read consistently while other transactions are prevented from modifying -them until the current transaction ends. +Adds a pessimistic read lock to a ``SELECT`` query. This is useful when rows must be read consistently while other +transactions are prevented from modifying them until the current transaction ends. .. literalinclude:: query_builder/131.php -Use this method inside a database transaction. Without an explicit transaction, -the lock is typically released when the ``SELECT`` statement finishes. If the -same transaction will update the selected rows, use ``lockForUpdate()`` instead. +Use this method inside a database transaction. Without an explicit transaction, the lock is typically released when the +``SELECT`` statement finishes. If the same transaction will update the selected rows, use ``lockForUpdate()`` instead. -See :ref:`query-builder-pessimistic-locking-driver-support` for supported -drivers and lock combinations. Unsupported drivers throw a ``DatabaseException``. -``sharedLock()`` is not supported with ``union()`` or ``unionAll()``. Some -databases restrict which query shapes can be used with row locking. When -CodeIgniter can detect an unsupported combination, it throws a -``DatabaseException``. See the following warnings for driver-specific behavior. +See :ref:`query-builder-pessimistic-locking-driver-support` for supported drivers and lock combinations. Unsupported +drivers throw a ``DatabaseException``. ``sharedLock()`` is not supported with ``union()`` or ``unionAll()``. Some +databases restrict which query shapes can be used with row locking. When CodeIgniter can detect an unsupported +combination, it throws a ``DatabaseException``. See the following warnings for driver-specific behavior. -.. warning:: MySQLi does not support ``sharedLock()`` with ``fromSubquery()`` - because an outer locking read on a derived table does not lock the underlying - rows as users may expect. +.. warning:: MySQLi does not support ``sharedLock()`` with ``fromSubquery()`` because an outer locking read on a derived + table does not lock the underlying rows as users may expect. -.. warning:: Postgre does not support ``sharedLock()`` with ``distinct()``, - ``groupBy()``, ``having()``, or aggregate helper selections such as - ``selectCount()``. +.. warning:: Postgre does not support ``sharedLock()`` with ``distinct()``, ``groupBy()``, ``having()``, or aggregate + helper selections such as ``selectCount()``. -.. warning:: SQLSRV uses SQL Server table hints instead of a trailing ``FOR SHARE`` - clause. The hint is applied to table references in the ``FROM`` clause; - joined tables are not hinted. Its exact lock granularity depends on SQL - Server's execution plan and transaction isolation level. SQLSRV does not - support ``sharedLock()`` without a ``FROM`` table or on subqueries. +.. warning:: SQLSRV uses SQL Server table hints instead of a trailing ``FOR SHARE`` clause. The hint is applied to table + references in the ``FROM`` clause; joined tables are not hinted. Its exact lock granularity depends on SQL Server's + execution plan and transaction isolation level. SQLSRV does not support ``sharedLock()`` without a ``FROM`` table or + on subqueries. .. _query-builder-lock-for-update: @@ -1089,41 +1044,33 @@ $builder->lockForUpdate() .. versionadded:: 4.8.0 -Adds a pessimistic write lock to a ``SELECT`` query. This is useful when a row -must be read and then updated safely while other transactions are prevented -from modifying it first. +Adds a pessimistic write lock to a ``SELECT`` query. This is useful when a row must be read and then updated safely +while other transactions are prevented from modifying it first. .. literalinclude:: query_builder/124.php -Use this method inside a database transaction. Without an explicit transaction, -the lock is typically released when the ``SELECT`` statement finishes. The exact -locking behavior is determined by the database server and transaction isolation +Use this method inside a database transaction. Without an explicit transaction, the lock is typically released when the +``SELECT`` statement finishes. The exact locking behavior is determined by the database server and transaction isolation level. -See :ref:`query-builder-pessimistic-locking-driver-support` for supported -drivers and lock combinations. Unsupported drivers throw a ``DatabaseException``. -``lockForUpdate()`` is not supported with ``union()`` or ``unionAll()``. Some -databases restrict which query shapes can be used with row locking. When -CodeIgniter can detect an unsupported combination, it throws a -``DatabaseException``. See the following warnings for driver-specific behavior. +See :ref:`query-builder-pessimistic-locking-driver-support` for supported drivers and lock combinations. Unsupported +drivers throw a ``DatabaseException``. ``lockForUpdate()`` is not supported with ``union()`` or ``unionAll()``. Some +databases restrict which query shapes can be used with row locking. When CodeIgniter can detect an unsupported +combination, it throws a ``DatabaseException``. See the following warnings for driver-specific behavior. -.. warning:: MySQLi does not support ``lockForUpdate()`` with ``fromSubquery()`` - because an outer locking read on a derived table does not lock the underlying - rows as users may expect. +.. warning:: MySQLi does not support ``lockForUpdate()`` with ``fromSubquery()`` because an outer locking read on a + derived table does not lock the underlying rows as users may expect. -.. warning:: Postgre does not support ``lockForUpdate()`` with ``distinct()``, - ``groupBy()``, ``having()``, or aggregate helper selections such as - ``selectCount()``. +.. warning:: Postgre does not support ``lockForUpdate()`` with ``distinct()``, ``groupBy()``, ``having()``, or aggregate + helper selections such as ``selectCount()``. -.. warning:: SQLSRV uses SQL Server table hints instead of a trailing ``FOR UPDATE`` - clause. The hint is applied to table references in the ``FROM`` clause; - joined tables are not hinted. Its exact lock granularity depends on SQL - Server's execution plan and transaction isolation level. SQLSRV does not - support ``lockForUpdate()`` without a ``FROM`` table or on subqueries. +.. warning:: SQLSRV uses SQL Server table hints instead of a trailing ``FOR UPDATE`` clause. The hint is applied to + table references in the ``FROM`` clause; joined tables are not hinted. Its exact lock granularity depends on SQL + Server's execution plan and transaction isolation level. SQLSRV does not support ``lockForUpdate()`` without a + ``FROM`` table or on subqueries. -.. warning:: OCI8 does not support ``lockForUpdate()`` together with - ``limit()``, ``offset()``, ``distinct()``, ``groupBy()``, ``having()``, or - aggregate helper selections such as ``selectCount()``. +.. warning:: OCI8 does not support ``lockForUpdate()`` together with ``limit()``, ``offset()``, ``distinct()``, + ``groupBy()``, ``having()``, or aggregate helper selections such as ``selectCount()``. .. _query-builder-skip-locked: @@ -1135,50 +1082,44 @@ $builder->skipLocked() .. versionadded:: 4.8.0 -Adds a ``SKIP LOCKED`` style modifier to a pessimistic ``SELECT`` lock. This is -useful when multiple workers claim rows from the same queue-like table and each -worker should skip rows already locked by another transaction. +Adds a ``SKIP LOCKED`` style modifier to a pessimistic ``SELECT`` lock. This is useful when multiple workers claim rows +from the same queue-like table and each worker should skip rows already locked by another transaction. .. literalinclude:: query_builder/132.php -Use this method with ``lockForUpdate()`` or ``sharedLock()``. Calling -``skipLocked()`` without a pessimistic lock throws a ``DatabaseException``. +Use this method with ``lockForUpdate()`` or ``sharedLock()``. Calling ``skipLocked()`` without a pessimistic lock throws +a ``DatabaseException``. -``skipLocked()`` can return an incomplete view of matching rows because locked -rows are skipped instead of waited on. This is usually what queue workers want, -but it is not suitable for general-purpose reads that must see every matching +``skipLocked()`` can return an incomplete view of matching rows because locked rows are skipped instead of waited on. +This is usually what queue workers want, but it is not suitable for general-purpose reads that must see every matching row. -See :ref:`query-builder-pessimistic-locking-driver-support` for supported -drivers and lock combinations. Unsupported combinations throw a -``DatabaseException``. +See :ref:`query-builder-pessimistic-locking-driver-support` for supported drivers and lock combinations. Unsupported +combinations throw a ``DatabaseException``. .. _query-builder-nowait: No Wait -======== +======= $builder->nowait() ------------------ .. versionadded:: 4.8.0 -Adds a ``NOWAIT`` style modifier to a pessimistic ``SELECT`` lock. This is -useful when the query should fail immediately instead of waiting for locked rows -to become available. +Adds a ``NOWAIT`` style modifier to a pessimistic ``SELECT`` lock. This is useful when the query should fail immediately +instead of waiting for locked rows to become available. .. literalinclude:: query_builder/133.php -Use this method with ``lockForUpdate()`` or ``sharedLock()``. Calling -``nowait()`` without a pessimistic lock throws a ``DatabaseException``. +Use this method with ``lockForUpdate()`` or ``sharedLock()``. Calling ``nowait()`` without a pessimistic lock throws a +``DatabaseException``. -When a row cannot be locked immediately, the database server returns an error -and CodeIgniter throws a database exception according to the current database -debug and transaction settings. +When a row cannot be locked immediately, the database server returns an error and CodeIgniter throws a database +exception according to the current database debug and transaction settings. -See :ref:`query-builder-pessimistic-locking-driver-support` for supported -drivers and lock combinations. Unsupported combinations throw a -``DatabaseException``. +See :ref:`query-builder-pessimistic-locking-driver-support` for supported drivers and lock combinations. Unsupported +combinations throw a ``DatabaseException``. .. _query-builder-union: @@ -1661,10 +1602,9 @@ If you want to delete all data from a table, you can use the ``truncate()`` method, or ``emptyTable()``. .. note:: - **Changed in version 4.8.0.** With ``$resetData = true`` (the default), bindings - and binding counters are cleared after execution, so ``getBinds()`` returns an - empty array. Pass ``false`` as the third argument to retain the query conditions - and bindings. Test mode retains bindings for the placeholders in its returned SQL. + **Changed in version 4.8.0.** With ``$resetData = true`` (the default), bindings and binding counters are cleared + after execution, so ``getBinds()`` returns an empty array. Pass ``false`` as the third argument to retain the query + conditions and bindings. Test mode retains bindings for the placeholders in its returned SQL. $builder->getCompiledDelete() ----------------------------- @@ -2059,9 +1999,9 @@ Class Reference :returns: ``BaseBuilder`` instance (method chaining) :rtype: ``BaseBuilder`` - Generates a ``WHERE`` clause that compares two columns. Separates multiple calls with ``AND``. - If ``$first`` does not end with a supported operator, ``=`` is used as the comparison operator. - Supported operators are ``=``, ``!=``, ``<>``, ``<``, ``>``, ``<=``, and ``>=``. + Generates a ``WHERE`` clause that compares two columns. Separates multiple calls with ``AND``. If ``$first`` + does not end with a supported operator, ``=`` is used as the comparison operator. Supported operators are ``=``, + ``!=``, ``<>``, ``<``, ``>``, ``<=``, and ``>=``. .. php:method:: orWhereColumn($first, $second[, $escape = null]) @@ -2071,9 +2011,9 @@ Class Reference :returns: ``BaseBuilder`` instance (method chaining) :rtype: ``BaseBuilder`` - Generates a ``WHERE`` clause that compares two columns. Separates multiple calls with ``OR``. - If ``$first`` does not end with a supported operator, ``=`` is used as the comparison operator. - Supported operators are ``=``, ``!=``, ``<>``, ``<``, ``>``, ``<=``, and ``>=``. + Generates a ``WHERE`` clause that compares two columns. Separates multiple calls with ``OR``. If ``$first`` does + not end with a supported operator, ``=`` is used as the comparison operator. Supported operators are ``=``, + ``!=``, ``<>``, ``<``, ``>``, ``<=``, and ``>=``. .. php:method:: whereBetween(?string $key = null, ?array $values = null, ?bool $escape = null) @@ -2103,7 +2043,8 @@ Class Reference :returns: ``BaseBuilder`` instance (method chaining) :rtype: ``BaseBuilder`` - Generates a ``WHERE`` field ``NOT BETWEEN`` minimum and maximum value SQL query, joined with ``AND`` if appropriate. + Generates a ``WHERE`` field ``NOT BETWEEN`` minimum and maximum value SQL query, joined with ``AND`` if + appropriate. .. php:method:: orWhereNotBetween(?string $key = null, ?array $values = null, ?bool $escape = null) @@ -2113,7 +2054,8 @@ Class Reference :returns: ``BaseBuilder`` instance (method chaining) :rtype: ``BaseBuilder`` - Generates a ``WHERE`` field ``NOT BETWEEN`` minimum and maximum value SQL query, joined with ``OR`` if appropriate. + Generates a ``WHERE`` field ``NOT BETWEEN`` minimum and maximum value SQL query, joined with ``OR`` if + appropriate. .. php:method:: whereExists($subquery) @@ -2322,7 +2264,8 @@ Class Reference :returns: ``BaseBuilder`` instance (method chaining) :rtype: ``BaseBuilder`` - Generates a ``HAVING`` field ``BETWEEN`` minimum and maximum value SQL query, joined with ``AND`` if appropriate. + Generates a ``HAVING`` field ``BETWEEN`` minimum and maximum value SQL query, joined with ``AND`` if + appropriate. .. php:method:: orHavingBetween(?string $key = null, ?array $values = null, ?bool $escape = null) @@ -2342,7 +2285,8 @@ Class Reference :returns: ``BaseBuilder`` instance (method chaining) :rtype: ``BaseBuilder`` - Generates a ``HAVING`` field ``NOT BETWEEN`` minimum and maximum value SQL query, joined with ``AND`` if appropriate. + Generates a ``HAVING`` field ``NOT BETWEEN`` minimum and maximum value SQL query, joined with ``AND`` if + appropriate. .. php:method:: orHavingNotBetween(?string $key = null, ?array $values = null, ?bool $escape = null) @@ -2352,7 +2296,8 @@ Class Reference :returns: ``BaseBuilder`` instance (method chaining) :rtype: ``BaseBuilder`` - Generates a ``HAVING`` field ``NOT BETWEEN`` minimum and maximum value SQL query, joined with ``OR`` if appropriate. + Generates a ``HAVING`` field ``NOT BETWEEN`` minimum and maximum value SQL query, joined with ``OR`` if + appropriate. .. php:method:: orHavingIn([$key = null[, $values = null[, $escape = null]]]) @@ -2722,13 +2667,12 @@ Class Reference .. versionadded:: 4.8.0 :param array $columns: A list of columns or array of column => value pairs to increment. - :param int $value: The amount to increment in the columns, if $columns is a list of columns. + :param int $value: The amount to increment in the columns, if ``$columns`` is a list of columns. :returns: ``true`` on success, ``false`` on failure :rtype: bool - Increments the value of multiple fields by the specified amounts. If a field - is not a numeric field, like a ``VARCHAR``, it will likely be replaced - with the amount specified for that field. + Increments the value of multiple fields by the specified amounts. If a field is not a numeric field, like a + ``VARCHAR``, it will likely be replaced with the amount specified for that field. .. php:method:: decrement($column[, $value = 1]) @@ -2744,13 +2688,12 @@ Class Reference .. versionadded:: 4.8.0 :param array $columns: A list of columns or array of column => value pairs to decrement. - :param int $value: The amount to decrement in the columns, if $columns is a list of columns. + :param int $value: The amount to decrement in the columns, if ``$columns`` is a list of columns. :returns: ``true`` on success, ``false`` on failure :rtype: bool - Decrements the value of multiple fields by the specified amounts. If a field - is not a numeric field, like a ``VARCHAR``, it will likely be replaced - with the amount specified for that field. + Decrements the value of multiple fields by the specified amounts. If a field is not a numeric field, like a + ``VARCHAR``, it will likely be replaced with the amount specified for that field. .. php:method:: truncate() diff --git a/user_guide_src/source/database/transactions.rst b/user_guide_src/source/database/transactions.rst index 1b6a2ae705e7..b05885c99d11 100644 --- a/user_guide_src/source/database/transactions.rst +++ b/user_guide_src/source/database/transactions.rst @@ -56,98 +56,80 @@ Running Transactions with a Closure .. versionadded:: 4.8.0 -You may also run a transaction with the ``transaction()`` method. It starts a -transaction, runs the callback, commits when the callback completes -successfully, and rolls back if the callback throws an exception: +You may also run a transaction with the ``transaction()`` method. It starts a transaction, runs the callback, commits +when the callback completes successfully, and rolls back if the callback throws an exception: .. literalinclude:: transactions/012.php -The callback receives the current database connection as its only argument. -If the transaction commits successfully, ``transaction()`` returns the callback -return value. If the transaction cannot begin, or if a query failure marks the -transaction as failed without throwing an exception, ``transaction()`` rolls -back and returns ``false``. +The callback receives the current database connection as its only argument. If the transaction commits successfully, +``transaction()`` returns the callback return value. If the transaction cannot begin, or if a query failure marks the +transaction as failed without throwing an exception, ``transaction()`` rolls back and returns ``false``. -If transactions are disabled, ``transaction()`` does not start a transaction. -It runs the callback and returns the callback result. +If transactions are disabled, ``transaction()`` does not start a transaction. It runs the callback and returns the +callback result. -If the callback throws an exception, ``transaction()`` rolls back and rethrows -the original exception unless retry attempts remain, as described below. +If the callback throws an exception, ``transaction()`` rolls back and rethrows the original exception unless retry +attempts remain, as described below. -If an ``afterRollback()`` callback throws while ``transaction()`` is rolling -back, that callback exception bubbles to the caller instead of the normal -``false`` return value or the original callback exception. +If an ``afterRollback()`` callback throws while ``transaction()`` is rolling back, that callback exception bubbles to +the caller instead of the normal ``false`` return value or the original callback exception. -Callbacks registered with ``afterCommit()`` or ``afterRollback()`` inside the -transaction callback follow the same rules as other transaction callbacks: they -run only after the outermost transaction commits or rolls back. +Callbacks registered with ``afterCommit()`` or ``afterRollback()`` inside the transaction callback follow the same rules +as other transaction callbacks: they run only after the outermost transaction commits or rolls back. Scoped Transaction Options -------------------------- -The ``transaction()`` method can temporarily override existing transaction -options for the duration of the helper call: +The ``transaction()`` method can temporarily override existing transaction options for the duration of the helper call: .. literalinclude:: transactions/017.php -Set ``transException`` to temporarily enable or disable the same transaction -exception mode configured by ``transException()``. The previous mode is restored -after ``transaction()`` finishes, even if the callback throws an exception. -If ``transaction()`` is called inside an active transaction, the temporary mode -applies while the nested callback runs, then the previous mode is restored for -the outer transaction. When ``transException`` is set to ``true`` in a nested -transaction and a query fails, CodeIgniter's existing transaction exception -handling rolls back the outer transaction as well. +Set ``transException`` to temporarily enable or disable the same transaction exception mode configured by +``transException()``. The previous mode is restored after ``transaction()`` finishes, even if the callback throws an +exception. If ``transaction()`` is called inside an active transaction, the temporary mode applies while the nested +callback runs, then the previous mode is restored for the outer transaction. When ``transException`` is set to ``true`` +in a nested transaction and a query fails, CodeIgniter's existing transaction exception handling rolls back the outer +transaction as well. -Set ``resetTransStatus`` to reset the transaction status before the helper starts -an outermost transaction. This is equivalent to calling ``resetTransStatus()`` -before the transaction, and is useful when strict mode has marked the connection -as failed from an earlier transaction. +Set ``resetTransStatus`` to reset the transaction status before the helper starts an outermost transaction. This is +equivalent to calling ``resetTransStatus()`` before the transaction, and is useful when strict mode has marked the +connection as failed from an earlier transaction. -``resetTransStatus`` only applies when ``transaction()`` starts the outermost -transaction. If ``transaction()`` is called inside an active transaction, it does -not reset the outer transaction status. +``resetTransStatus`` only applies when ``transaction()`` starts the outermost transaction. If ``transaction()`` is +called inside an active transaction, it does not reset the outer transaction status. -The ``transException`` option follows CodeIgniter's existing transaction -exception behavior, including the current ``DBDebug`` setting. +The ``transException`` option follows CodeIgniter's existing transaction exception behavior, including the current +``DBDebug`` setting. Retrying Transactions --------------------- -You may pass ``attempts`` when you want CodeIgniter to retry the whole -transaction after retryable database concurrency failures, such as deadlocks or -serialization failures. Retries happen when a ``RetryableTransactionException`` -occurs while the callback is running, including from query or prepared query -execution: +You may pass ``attempts`` when you want CodeIgniter to retry the whole transaction after retryable database concurrency +failures, such as deadlocks or serialization failures. Retries happen when a ``RetryableTransactionException`` occurs +while the callback is running, including from query or prepared query execution: .. literalinclude:: transactions/016.php -``attempts`` is the total number of times to try the transaction, including the -first run, and must be greater than or equal to ``1``. +``attempts`` is the total number of times to try the transaction, including the first run, and must be greater than or +equal to ``1``. Keep these rules in mind when using retry attempts: -- The callback may run more than once, and retries run immediately without delay - or backoff. -- Retries only apply when ``transaction()`` starts the outermost transaction. If - ``transaction()`` is called inside an active transaction, the callback runs - once using the existing nested transaction behavior. -- Only ``RetryableTransactionException`` failures are retried. Other exceptions - are rolled back and rethrown without another attempt. -- Retry attempts do not retry failures that are reported only while the - transaction is committing. -- If an ``afterRollback()`` callback throws while a failed attempt is rolling - back, that callback exception bubbles to the caller and no further attempts are - made. - -Avoid non-transactional side effects inside callbacks that may be retried. For -side effects such as queued jobs, emails, cache invalidation, events, or external -API calls, register them with ``afterCommit()`` so they run only after the final +- The callback may run more than once, and retries run immediately without delay or backoff. +- Retries only apply when ``transaction()`` starts the outermost transaction. If ``transaction()`` is called inside an + active transaction, the callback runs once using the existing nested transaction behavior. +- Only ``RetryableTransactionException`` failures are retried. Other exceptions are rolled back and rethrown without + another attempt. +- Retry attempts do not retry failures that are reported only while the transaction is committing. +- If an ``afterRollback()`` callback throws while a failed attempt is rolling back, that callback exception bubbles to + the caller and no further attempts are made. + +Avoid non-transactional side effects inside callbacks that may be retried. For side effects such as queued jobs, emails, +cache invalidation, events, or external API calls, register them with ``afterCommit()`` so they run only after the final successful commit. -``afterRollback()`` callbacks may run for failed retry attempts even when a later -attempt commits successfully, so use them only for cleanup that is safe to run -per rolled-back attempt. +``afterRollback()`` callbacks may run for failed retry attempts even when a later attempt commits successfully, so use +them only for cleanup that is safe to run per rolled-back attempt. .. _transactions-retryable-exceptions: @@ -156,26 +138,21 @@ Classifying Retryable Transaction Failures .. versionadded:: 4.8.0 -Some database engines report transaction failures that may succeed when the -entire transaction is attempted again, such as deadlocks or serialization -failures. When a driver classifies a query or prepared query execution failure as -one of these retryable transaction failures, CodeIgniter throws -``RetryableTransactionException`` so you can decide how your application should -respond: +Some database engines report transaction failures that may succeed when the entire transaction is attempted again, such +as deadlocks or serialization failures. When a driver classifies a query or prepared query execution failure as one of +these retryable transaction failures, CodeIgniter throws ``RetryableTransactionException`` so you can decide how your +application should respond: .. literalinclude:: transactions/015.php -``RetryableTransactionException`` is the classifier used by ``transaction()`` -retry attempts. If you are not using ``transaction()`` attempts, catch this -exception and retry the whole transaction according to your application's policy. -Avoid non-transactional side effects inside transaction bodies that may be -retried. For side effects such as queued jobs, emails, cache invalidation, or -external API calls, register them with ``afterCommit()`` so they run only after -the transaction commits. - -When ``DBDebug`` is ``false`` and a failed query or prepared query returns -``false`` instead of throwing, inspect ``getLastException()`` immediately after -the failed operation. It will contain the ``RetryableTransactionException`` +``RetryableTransactionException`` is the classifier used by ``transaction()`` retry attempts. If you are not using +``transaction()`` attempts, catch this exception and retry the whole transaction according to your application's policy. +Avoid non-transactional side effects inside transaction bodies that may be retried. For side effects such as queued +jobs, emails, cache invalidation, or external API calls, register them with ``afterCommit()`` so they run only after the +transaction commits. + +When ``DBDebug`` is ``false`` and a failed query or prepared query returns ``false`` instead of throwing, inspect +``getLastException()`` immediately after the failed operation. It will contain the ``RetryableTransactionException`` instance when the driver classifies the failure as retryable. Strict Mode @@ -247,71 +224,57 @@ Running Code after Commit or Rollback .. versionadded:: 4.8.0 -You may register callbacks to run only after the outermost transaction has -successfully committed by using the ``afterCommit()`` method, or after the -outermost transaction has rolled back by using the ``afterRollback()`` method: +You may register callbacks to run only after the outermost transaction has successfully committed by using the +``afterCommit()`` method, or after the outermost transaction has rolled back by using the ``afterRollback()`` method: .. literalinclude:: transactions/010.php -Callbacks registered during an active transaction are delayed until the -outermost transaction commits or rolls back. +Callbacks registered during an active transaction are delayed until the outermost transaction commits or rolls back. -If the transaction commits, ``afterCommit()`` callbacks run and -``afterRollback()`` callbacks are discarded. If the transaction rolls back, -``afterRollback()`` callbacks run and ``afterCommit()`` callbacks are discarded. -If no transaction is active, ``afterCommit()`` callbacks run immediately, while -``afterRollback()`` callbacks are not run. +If the transaction commits, ``afterCommit()`` callbacks run and ``afterRollback()`` callbacks are discarded. If the +transaction rolls back, ``afterRollback()`` callbacks run and ``afterCommit()`` callbacks are discarded. If no +transaction is active, ``afterCommit()`` callbacks run immediately, while ``afterRollback()`` callbacks are not run. For example: .. literalinclude:: transactions/011.php -.. note:: When ``afterCommit()`` is called outside an active transaction, it runs - immediately. This includes calls from Model ``beforeInsert`` or - ``beforeUpdate`` events when the calling code has not already started a - transaction, so the callback may run before the Model's insert or update - query is executed. +.. note:: When ``afterCommit()`` is called outside an active transaction, it runs immediately. This includes calls from + Model ``beforeInsert`` or ``beforeUpdate`` events when the calling code has not already started a transaction, so + the callback may run before the Model's insert or update query is executed. -This is useful for side effects that should only happen after committed data is -visible, such as dispatching a queued job or sending a notification, and for -cleanup that should only happen after a real rollback. +This is useful for side effects that should only happen after committed data is visible, such as dispatching a queued +job or sending a notification, and for cleanup that should only happen after a real rollback. Deferring Side Effects ---------------------- -Code that changes external state should usually not run in the middle of a -transaction. If the transaction rolls back after the side effect has already -run, the application may send a notification for data that was never saved, -invalidate a cache for a write that did not persist, or start background work -that cannot find the committed row it expects. +Code that changes external state should usually not run in the middle of a transaction. If the transaction rolls back +after the side effect has already run, the application may send a notification for data that was never saved, invalidate +a cache for a write that did not persist, or start background work that cannot find the committed row it expects. Register those side effects with ``afterCommit()`` instead: .. literalinclude:: transactions/013.php -Use ``afterRollback()`` for cleanup that should happen only when the transaction -does not commit, such as removing a temporary file created before the database -write: +Use ``afterRollback()`` for cleanup that should happen only when the transaction does not commit, such as removing a +temporary file created before the database write: .. literalinclude:: transactions/014.php -Model callbacks such as ``afterInsert`` and ``afterUpdate`` run after the Model -query has executed, but not necessarily after the surrounding transaction has -committed. If a Model callback needs to run a side effect only after commit, it -should register that work with the database connection's ``afterCommit()`` -method while an active transaction is already open. +Model callbacks such as ``afterInsert`` and ``afterUpdate`` run after the Model query has executed, but not necessarily +after the surrounding transaction has committed. If a Model callback needs to run a side effect only after commit, it +should register that work with the database connection's ``afterCommit()`` method while an active transaction is already +open. -Callbacks run after the database transaction has already committed or rolled -back. If a callback throws an exception, that exception bubbles to the caller, -but the transaction outcome is not changed. +Callbacks run after the database transaction has already committed or rolled back. If a callback throws an exception, +that exception bubbles to the caller, but the transaction outcome is not changed. -.. warning:: When multiple callbacks are registered for the same transaction - outcome, they run in registration order. If one callback throws an exception, - the subsequent callbacks are not run. +.. warning:: When multiple callbacks are registered for the same transaction outcome, they run in registration order. If + one callback throws an exception, the subsequent callbacks are not run. -Rollback callbacks also run when CodeIgniter automatically rolls back an active -transaction while handling a transaction failure or cleaning up an unfinished -transaction. +Rollback callbacks also run when CodeIgniter automatically rolls back an active transaction while handling a transaction +failure or cleaning up an unfinished transaction. .. _transactions-checking-transaction-state: @@ -320,19 +283,16 @@ Checking Transaction State .. versionadded:: 4.8.0 -You may use ``inTransaction()`` to check whether the connection is currently -inside an active CodeIgniter-managed transaction. +You may use ``inTransaction()`` to check whether the connection is currently inside an active CodeIgniter-managed +transaction. -It returns ``false`` when no CodeIgniter-managed transaction is active, -including when transactions are disabled. +It returns ``false`` when no CodeIgniter-managed transaction is active, including when transactions are disabled. -This is useful for services or libraries that need to adapt their behavior when -they are called from inside an existing transaction. +This is useful for services or libraries that need to adapt their behavior when they are called from inside an existing +transaction. -.. note:: ``inTransaction()`` reflects transactions started through - CodeIgniter's transaction methods. If you start or end transactions through - raw SQL or driver-specific APIs, CodeIgniter will not be aware of those - transactions. +.. note:: ``inTransaction()`` reflects transactions started through CodeIgniter's transaction methods. If you start or + end transactions through raw SQL or driver-specific APIs, CodeIgniter will not be aware of those transactions. Disabling Transactions ====================== diff --git a/user_guide_src/source/dbmgmt/migration.rst b/user_guide_src/source/dbmgmt/migration.rst index dfa6021382bc..562f376f9d6e 100644 --- a/user_guide_src/source/dbmgmt/migration.rst +++ b/user_guide_src/source/dbmgmt/migration.rst @@ -132,7 +132,8 @@ Migrates a database group with all available migrations: You can use ``migrate`` with the following options: -- ``--group`` (``-g``) - to specify database group. If specified, only migrations for the specified database group will be run. If not specified, all migrations will be run. +- ``--group`` (``-g``) - to specify database group. If specified, only migrations for the specified database group will + be run. If not specified, all migrations will be run. - ``--namespace`` (``-n``) - to choose namespace, otherwise ``App`` namespace will be used. - ``--all`` - to migrate all namespaces to the latest migration. @@ -166,7 +167,7 @@ Rolls back all migrations to a blank slate, effectively migration 0: You can use ``migrate:rollback`` with the following options: - ``--batch`` (``-b``) - to choose a batch: natural numbers specify the batch. -- ``--force`` (``-f``) - to force a bypass confirmation question, it is only asked in a production environment. +- ``--force`` (``-f``) - to bypass the confirmation question, which is only asked in a production environment. migrate:refresh =============== @@ -179,10 +180,11 @@ Refreshes the database state by first rolling back all migrations, and then migr You can use ``migrate:refresh`` with the following options: -- ``--group`` (``-g``) - to specify database group. If specified, only migrations for the specified database group will be run. If not specified, all migrations will be run. +- ``--group`` (``-g``) - to specify database group. If specified, only migrations for the specified database group will + be run. If not specified, all migrations will be run. - ``--namespace`` (``-n``) - to choose namespace, otherwise ``App`` namespace will be used. - ``--all`` - to refresh all namespaces. -- ``--force`` (``-f``) - to force a bypass confirmation question, it is only asked in a production environment. +- ``--force`` (``-f``) - to bypass the confirmation question, which is only asked in a production environment. migrate:status ============== @@ -205,7 +207,8 @@ Displays a list of all migrations and the date and time they ran, or '--' if the You can use ``migrate:status`` with the following options: -- ``--group`` (``-g``) - to specify database group. If specified, only migrations for the specified database group will be checked. If not specified, all migrations will be checked. +- ``--group`` (``-g``) - to specify database group. If specified, only migrations for the specified database group will + be checked. If not specified, all migrations will be checked. make:migration ============== @@ -221,13 +224,13 @@ creates is the Pascal case version of the filename. You can use ``make:migration`` with the following options: - ``--namespace`` (``-n``) - Set root namespace. Default: ``APP_NAMESPACE``. -- ``--suffix`` (``-s``) - Append the component title to the class name. +- ``--suffix`` (``-s``) - Append the component title to the class name. The following options are also available to generate the migration file for database sessions: -- ``--session`` - Generates the migration file for database sessions. -- ``--table`` (``-t``) - Table name to use for database sessions. Default: ``ci_sessions``. +- ``--session`` - Generates the migration file for database sessions. +- ``--table`` (``-t``) - Table name to use for database sessions. Default: ``ci_sessions``. - ``--dbgroup`` (``-g``) - Database group to use for database sessions. Default: ``default``. ********************* diff --git a/user_guide_src/source/general/caching.rst b/user_guide_src/source/general/caching.rst index b7abd7697f6c..b9b187b77495 100644 --- a/user_guide_src/source/general/caching.rst +++ b/user_guide_src/source/general/caching.rst @@ -96,8 +96,8 @@ Valid options are: caching errors that should be temporary. For example, a cached 404 page would remain cached even after the resource is created, until the cache expires. -.. note:: Regardless of this setting, ``DownloadResponse``, ``RedirectResponse``, - and HTTP ``QUERY`` responses are never cached by the ``PageCache`` filter. +.. note:: Regardless of this setting, ``DownloadResponse``, ``RedirectResponse``, and HTTP ``QUERY`` responses are never + cached by the ``PageCache`` filter. Enabling Caching ================ diff --git a/user_guide_src/source/general/configuration.rst b/user_guide_src/source/general/configuration.rst index fd970bab0da2..01a8b5ab4afc 100644 --- a/user_guide_src/source/general/configuration.rst +++ b/user_guide_src/source/general/configuration.rst @@ -342,10 +342,9 @@ Registrar methods must always return an array, with keys corresponding to the pr of the target config file. Existing values are merged, and Registrar properties have overwrite priority. -By default this merge is **shallow** (top-level only). Arrays are combined with -``array_merge()``, so a nested array under a top-level key *replaces* the existing -nested array rather than merging into it. In the following example the ``key2`` -subtree from the config is replaced entirely, dropping ``val2`` and ``val3``: +By default this merge is **shallow** (top-level only). Arrays are combined with ``array_merge()``, so a nested array +under a top-level key *replaces* the existing nested array rather than merging into it. In the following example the +``key2`` subtree from the config is replaced entirely, dropping ``val2`` and ``val3``: .. literalinclude:: configuration/012.php @@ -356,84 +355,66 @@ Controlling how values are merged .. versionadded:: 4.8.0 -When a registrar needs finer control than the shallow default, it can return a -``CodeIgniter\Config\Merge`` directive as the value of a property. Merge -directives are explicit instructions to either keep merging into a value, replace -it, or add items to a list. +When a registrar needs finer control than the shallow default, it can return a ``CodeIgniter\Config\Merge`` directive as +the value of a property. Merge directives are explicit instructions to either keep merging into a value, replace it, or +add items to a list. The two directives that control whole values are: -- ``Merge::replace($value)`` - discard the existing value and use ``$value``. - Accepts **any type** (scalar, ``null``, or array - e.g. ``['a', 'b']`` becomes - ``['c']``, or ``Merge::replace('redis')``). -- ``Merge::byKey($value)`` - deep-merge by key: **string keys recurse, integer - keys append, and scalar leaves are replaced**. The name is deliberately not - ``recursive`` to avoid confusion with PHP's ``array_merge_recursive()``, which - collects scalar values into arrays instead of replacing them. +- ``Merge::replace($value)`` - discard the existing value and use ``$value``. Accepts **any type** (scalar, ``null``, or + array - e.g. ``['a', 'b']`` becomes ``['c']``, or ``Merge::replace('redis')``). +- ``Merge::byKey($value)`` - deep-merge by key: + **string keys recurse, integer keys append, and scalar leaves are replaced**. The name is deliberately not + ``recursive`` to avoid confusion with PHP's ``array_merge_recursive()``, which collects scalar values into arrays + instead of replacing them. -Use ``Merge::byKey()`` when you want to navigate into nested configuration and -preserve sibling keys: +Use ``Merge::byKey()`` when you want to navigate into nested configuration and preserve sibling keys: .. literalinclude:: configuration/013.php -.. important:: Inside ``Merge::byKey()``, plain arrays are still merged by key. - Use ``Merge::replace()`` when you want to stop merging at that key and - overwrite the value. For example, ``'after' => []`` leaves an existing - ``after`` list unchanged, while ``'after' => Merge::replace([])`` clears it. +.. important:: Inside ``Merge::byKey()``, plain arrays are still merged by key. Use ``Merge::replace()`` when you want + to stop merging at that key and overwrite the value. For example, ``'after' => []`` leaves an existing ``after`` + list unchanged, while ``'after' => Merge::replace([])`` clears it. -The following registrar adds a filter to ``globals['before']`` while hard-resetting -``globals['after']``, leaving any other ``globals`` keys untouched: +The following registrar adds a filter to ``globals['before']`` while hard-resetting ``globals['after']``, leaving any +other ``globals`` keys untouched: .. literalinclude:: configuration/014.php -The *list* strategies add items to a list and control where they land. They are -useful where order matters, such as the filter lists in ``Config\Filters``: - -- ``Merge::append($value)`` - add items to the **end** of the list - (e.g. ``['a', 'b']`` becomes ``['a', 'b', 'c']``). -- ``Merge::prepend($value)`` - add items to the **front** of the list - (e.g. ``['a', 'b']`` becomes ``['c', 'a', 'b']``). -- ``Merge::before($anchor, $value)`` - insert items immediately **before** the - first element equal to ``$anchor``. -- ``Merge::after($anchor, $value)`` - insert items immediately **after** the - first element equal to ``$anchor``. - -All four de-duplicate, so the directives never *introduce* a duplicate value: a -value already in the list is not added again, and duplicate payload values are -collapsed (e.g. ``Merge::append(['x', 'x'])`` adds ``x`` once). Duplicates that -already exist in the current list are left as-is. They differ only in how they -treat a value that is *already present*: - -- ``append()`` / ``prepend()`` leave an already-present value **where it is** - (they only add values that are missing). -- ``before()`` / ``after()`` **move** an already-present value to the anchor - position - but only when the anchor is in the list. If the anchor is missing - they fall back to ``append()`` / ``prepend()`` respectively, and do **not** +The *list* strategies add items to a list and control where they land. They are useful where order matters, such as the +filter lists in ``Config\Filters``: + +- ``Merge::append($value)`` - add items to the **end** of the list (e.g. ``['a', 'b']`` becomes ``['a', 'b', 'c']``). +- ``Merge::prepend($value)`` - add items to the **front** of the list (e.g. ``['a', 'b']`` becomes ``['c', 'a', 'b']``). +- ``Merge::before($anchor, $value)`` - insert items immediately **before** the first element equal to ``$anchor``. +- ``Merge::after($anchor, $value)`` - insert items immediately **after** the first element equal to ``$anchor``. + +All four de-duplicate, so the directives never *introduce* a duplicate value: a value already in the list is not added +again, and duplicate payload values are collapsed (e.g. ``Merge::append(['x', 'x'])`` adds ``x`` once). Duplicates that +already exist in the current list are left as-is. They differ only in how they treat a value that is *already present*: + +- ``append()`` / ``prepend()`` leave an already-present value **where it is** (they only add values that are missing). +- ``before()`` / ``after()`` **move** an already-present value to the anchor position - but only when the anchor is in + the list. If the anchor is missing they fall back to ``append()`` / ``prepend()`` respectively, and do **not** relocate a value that is already present. -The anchor is matched strictly against the **direct elements** of the list; the -list strategies act on a single list level and never recurse. To reach a list -that is nested under other keys (such as ``globals['before']``), navigate to it +The anchor is matched strictly against the **direct elements** of the list; the list strategies act on a single list +level and never recurse. To reach a list that is nested under other keys (such as ``globals['before']``), navigate to it with ``Merge::byKey()`` and place the list directive at that key: .. literalinclude:: configuration/015.php -.. important:: Merge directives are interpreted only when used as the **value of - a config property** returned by a registrar, and recursively **inside** - ``Merge::byKey()``. The payloads of the ``replace()``, ``append()``, - ``prepend()``, ``before()``, and ``after()`` strategies are taken literally and - are **not** scanned for nested directives - for nested control, wrap the - property in ``Merge::byKey()`` and place the directives at its keys. - -.. note:: Merge directives sharpen a *single* registrar's intent; they do not add - an explicit priority mechanism between registrars. Registrars are still applied - in discovery order, and an item's final position follows from that order *plus* - the strategy: with ``append()`` an earlier registrar's items sit ahead of a - later one's, while with ``prepend()``, ``before()``, and ``after()`` a later - registrar's items land **nearer the front or the anchor** than an earlier one's. - For example, ``after('csrf', ['a'])`` followed by ``after('csrf', ['b'])`` from a - second registrar yields ``['csrf', 'b', 'a']``. As always, **.env** values take - priority over registrars. +.. important:: Merge directives are interpreted only when used as the **value of a config property** returned by a + registrar, and recursively **inside** ``Merge::byKey()``. The payloads of the ``replace()``, ``append()``, + ``prepend()``, ``before()``, and ``after()`` strategies are taken literally and are **not** scanned for nested + directives - for nested control, wrap the property in ``Merge::byKey()`` and place the directives at its keys. + +.. note:: Merge directives sharpen a *single* registrar's intent; they do not add an explicit priority mechanism between + registrars. Registrars are still applied in discovery order, and an item's final position follows from that order + *plus* the strategy: with ``append()`` an earlier registrar's items sit ahead of a later one's, while with + ``prepend()``, ``before()``, and ``after()`` a later registrar's items land **nearer the front or the anchor** than + an earlier one's. For example, ``after('csrf', ['a'])`` followed by ``after('csrf', ['b'])`` from a second registrar + yields ``['csrf', 'b', 'a']``. As always, **.env** values take priority over registrars. Explicit Registrars =================== diff --git a/user_guide_src/source/general/context.rst b/user_guide_src/source/general/context.rst index 5c9dac35b16b..d7798b7263a1 100644 --- a/user_guide_src/source/general/context.rst +++ b/user_guide_src/source/general/context.rst @@ -1,8 +1,8 @@ .. _context: -################### +####### Context -################### +####### .. versionadded:: 4.8.0 @@ -14,7 +14,9 @@ Context What is it? *********** -The Context class provides a simple, convenient way to store and retrieve user-defined data throughout a single request. It functions as a key-value store that can hold any data you need to access across different parts of your application during the request lifecycle. +The Context class provides a simple, convenient way to store and retrieve user-defined data throughout a single request. +It functions as a key-value store that can hold any data you need to access across different parts of your application +during the request lifecycle. The Context class is particularly useful for: @@ -27,13 +29,14 @@ The Context class is particularly useful for: Accessing Context Class *********************** -You can access the Context service anywhere in your application using the ``service()`` function or ``context()`` helper: +You can access the Context service anywhere in your application using the ``service()`` function or the ``context()`` +helper: .. literalinclude:: context/001.php -********************* +******************** Setting Context Data -********************* +******************** Setting a Single Value ====================== @@ -53,12 +56,12 @@ The ``set()`` method returns the Context instance, allowing you to chain multipl .. literalinclude:: context/004.php -********************* +******************** Getting Context Data -********************* +******************** Retrieving a Single Value -========================== +========================= Use the ``get()`` method to retrieve a value by its key: @@ -76,7 +79,7 @@ To get all stored context data: .. literalinclude:: context/007.php Retrieving Specific Keys -========================= +======================== You can retrieve only specific keys using ``getOnly()``: @@ -86,9 +89,9 @@ If you need all data except specific keys, use ``getExcept()``: .. literalinclude:: context/009.php -********************** +***************** Checking for Data -********************** +***************** You can check if a key exists in the context: @@ -99,14 +102,14 @@ Removing Context Data ********************* Removing a Single Value -======================== +======================= You can remove data from the context using the ``remove()`` method: .. literalinclude:: context/011.php Removing Multiple Values -========================= +======================== To remove multiple keys at once, pass an array: @@ -119,13 +122,13 @@ To remove all context data: .. literalinclude:: context/013.php -********************* +******************* Hidden Context Data -********************* +******************* -The Context class provides a separate storage area for sensitive data that should not be included in logs. -This is useful for storing API keys, passwords, tokens, or other sensitive information that you need to access -during the request but don't want to expose in log files. +The Context class provides a separate storage area for sensitive data that should not be included in logs. This is +useful for storing API keys, passwords, tokens, or other sensitive information that you need to access during the +request but don't want to expose in log files. Setting Hidden Data =================== @@ -174,17 +177,18 @@ To clear both regular and hidden data: .. literalinclude:: context/021.php -.. important:: Regular data and hidden data are stored separately. A key can exist in both regular and hidden storage with different values. Use ``get()`` for regular data and ``getHidden()`` for hidden data. +.. important:: Regular data and hidden data are stored separately. A key can exist in both regular and hidden storage + with different values. Use ``get()`` for regular data and ``getHidden()`` for hidden data. -*********************************** +************************ Integration with Logging -*********************************** +************************ The Context class integrates seamlessly with CodeIgniter's logging system. When enabled, context data is automatically appended to log messages, providing additional information for debugging and monitoring. Enabling Global Context Logging -================================ +=============================== To enable automatic logging of context data, set the ``$logGlobalContext`` property to ``true`` in your **app/Config/Logger.php** file: @@ -201,7 +205,8 @@ This would produce a log entry like: ERROR - 2026-02-18 --> Payment processing failed {"user_id":123,"transaction_id":"txn_12345"} -.. note:: Hidden data set with ``setHidden()`` is **never** included in logs, even when ``$logGlobalContext`` is enabled. This ensures sensitive information like API keys or tokens remain secure. +.. note:: Hidden data set with ``setHidden()`` is **never** included in logs, even when ``$logGlobalContext`` is + enabled. This ensures sensitive information like API keys or tokens remain secure. *************** Important Notes @@ -225,7 +230,7 @@ Class Reference .. php:method:: set($key[, $value = null]) :param array|string $key: The key or an array of key-value pairs - :param mixed $value: The value to store (ignored if $key is an array) + :param mixed $value: The value to store (ignored if ``$key`` is an array) :returns: Context instance for method chaining :rtype: Context @@ -234,7 +239,7 @@ Class Reference .. php:method:: setHidden($key[, $value = null]) :param array|string $key: The key or an array of key-value pairs - :param mixed $value: The value to store (ignored if $key is an array) + :param mixed $value: The value to store (ignored if ``$key`` is an array) :returns: Context instance for method chaining :rtype: Context diff --git a/user_guide_src/source/general/environments.rst b/user_guide_src/source/general/environments.rst index baad733e3cb5..d94c96c45f65 100644 --- a/user_guide_src/source/general/environments.rst +++ b/user_guide_src/source/general/environments.rst @@ -152,25 +152,21 @@ The ``environment`` service .. versionadded:: 4.8.0 -As an alternative to reading the ``ENVIRONMENT`` constant directly, CodeIgniter -provides the ``environment`` service, backed by the -``CodeIgniter\EnvironmentDetector`` class. Because it is a shared service, it -can be mocked in tests (via ``Services::injectMock()``) to exercise -environment-specific branches without having to redefine the ``ENVIRONMENT`` +As an alternative to reading the ``ENVIRONMENT`` constant directly, CodeIgniter provides the ``environment`` service, +backed by the ``CodeIgniter\EnvironmentDetector`` class. Because it is a shared service, it can be mocked in tests (via +``Services::injectMock()``) to exercise environment-specific branches without having to redefine the ``ENVIRONMENT`` constant. .. literalinclude:: environments/001.php .. note:: - The ``environment`` service is primarily intended for testing - environment-specific code paths. Mocking it only affects code that resolves - and uses the service itself. It does not modify the ``ENVIRONMENT`` constant. - Code that still reads ``ENVIRONMENT`` directly keeps its current behavior. + The ``environment`` service is primarily intended for testing environment-specific code paths. Mocking it only + affects code that resolves and uses the service itself. It does not modify the ``ENVIRONMENT`` constant. Code that + still reads ``ENVIRONMENT`` directly keeps its current behavior. -Passing a value to the constructor overrides the detected environment; passing -``null`` (the default) falls back to the ``ENVIRONMENT`` constant. An empty or -whitespace-only string throws ``CodeIgniter\Exceptions\InvalidArgumentException``. +Passing a value to the constructor overrides the detected environment; passing ``null`` (the default) falls back to the +``ENVIRONMENT`` constant. An empty or whitespace-only string throws ``CodeIgniter\Exceptions\InvalidArgumentException``. ************************************* Effects on Default Framework Behavior diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index 46bc3773f531..6dcbae502d7f 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -209,8 +209,8 @@ UniqueConstraintViolationException .. versionadded:: 4.8.0 -``UniqueConstraintViolationException`` extends ``DatabaseException`` and is thrown when a query -fails due to a duplicate key or unique constraint violation. It is supported by all database drivers. +``UniqueConstraintViolationException`` extends ``DatabaseException`` and is thrown when a query fails due to a duplicate +key or unique constraint violation. It is supported by all database drivers. .. literalinclude:: errors/019.php diff --git a/user_guide_src/source/general/logging.rst b/user_guide_src/source/general/logging.rst index 7c2df5dfb63d..3b9e9705cf5c 100644 --- a/user_guide_src/source/general/logging.rst +++ b/user_guide_src/source/general/logging.rst @@ -88,23 +88,23 @@ Stopping the Handler Chain on Failure .. versionadded:: 4.8.0 -Handlers are executed in the order they are defined in the ``$handlers`` property. When a handler's -``handle()`` method returns ``false``, the handlers defined after it are not executed. +Handlers are executed in the order they are defined in the ``$handlers`` property. When a handler's ``handle()`` method +returns ``false``, the handlers defined after it are not executed. -By default, the **File Handler** and the **Errorlog Handler** return ``false`` when they fail to write the -log, for example when the log directory is not writable. This means that a failing handler prevents the -handlers after it from logging the message. +By default, the **File Handler** and the **Errorlog Handler** return ``false`` when they fail to write the log, for +example when the log directory is not writable. This means that a failing handler prevents the handlers after it from +logging the message. -If you want the remaining handlers to run even when a handler fails to write the log, set its -``stopChainOnFailure`` option to ``false``: +If you want the remaining handlers to run even when a handler fails to write the log, set its ``stopChainOnFailure`` +option to ``false``: .. literalinclude:: logging/010.php -In the example above, the **Errorlog Handler** still logs the message when the **File Handler** cannot write -to the log file. +In the example above, the **Errorlog Handler** still logs the message when the **File Handler** cannot write to the log +file. -.. note:: This option only affects failed writes in the **File Handler** and the **Errorlog Handler**. - A custom handler can still return ``false`` from ``handle()`` to stop the remaining handlers. +.. note:: This option only affects failed writes in the **File Handler** and the **Errorlog Handler**. A custom handler + can still return ``false`` from ``handle()`` to stop the remaining handlers. Modifying the Message with Context ================================== @@ -150,13 +150,13 @@ Global Context Logging .. versionadded:: 4.8.0 -You can automatically append context data to all log messages by enabling the ``$logGlobalContext`` -property in **app/Config/Logger.php**: +You can automatically append context data to all log messages by enabling the ``$logGlobalContext`` property in +**app/Config/Logger.php**: .. literalinclude:: context/023.php -When enabled, all regular context data (set via the :ref:`Context class `) is automatically -appended to every log message as a JSON string: +When enabled, all regular context data (set via the :ref:`Context class `) is automatically appended to every +log message as a JSON string: .. literalinclude:: context/023.php @@ -166,9 +166,8 @@ This would produce a log entry like: ERROR - 2026-02-18 --> Payment processing failed {"user_id":123,"transaction_id":"txn_12345"} -.. note:: Hidden data set with ``setHidden()`` are **never** included in log output, even when - ``$logGlobalContext`` is enabled. This protects sensitive information such as API keys - and tokens from appearing in log files. +.. note:: Hidden data set with ``setHidden()`` is **never** included in log output, even when ``$logGlobalContext`` is + enabled. This protects sensitive information such as API keys and tokens from appearing in log files. See :ref:`context` for full documentation on storing and managing context data. @@ -179,33 +178,29 @@ Per-Call Context Logging .. versionadded:: 4.8.0 -By default, context values passed to ``log_message()`` are only used for placeholder -interpolation and are not stored anywhere. You can enable structured context logging -by setting ``$logContext = true`` in **app/Config/Logger.php**: +By default, context values passed to ``log_message()`` are only used for placeholder interpolation and are not stored +anywhere. You can enable structured context logging by setting ``$logContext = true`` in **app/Config/Logger.php**: .. literalinclude:: logging/009.php -When enabled, any context key that is **not** referenced as a ``{placeholder}`` in the -message is passed to handlers as structured data. Keys that were interpolated into the -message are stripped by default (since their values are already present in the message -text), but you can keep them by setting ``$logContextUsedKeys = true``. +When enabled, any context key that is **not** referenced as a ``{placeholder}`` in the message is passed to handlers as +structured data. Keys that were interpolated into the message are stripped by default (since their values are already +present in the message text), but you can keep them by setting ``$logContextUsedKeys = true``. .. literalinclude:: logging/007.php **Throwable normalization** -Per PSR-3, a ``Throwable`` instance must be passed under the ``exception`` key to be -handled specially. When found there, it is automatically normalized into a meaningful -array instead of being serialized as an empty object: +Per PSR-3, a ``Throwable`` instance must be passed under the ``exception`` key to be handled specially. When found +there, it is automatically normalized into a meaningful array instead of being serialized as an empty object: .. literalinclude:: logging/008.php -The normalized array contains ``class``, ``message``, ``code``, ``file``, and ``line``. -To also include the full stack trace, set ``$logContextTrace = true``. +The normalized array contains ``class``, ``message``, ``code``, ``file``, and ``line``. To also include the full stack +trace, set ``$logContextTrace = true``. -.. note:: ``$logContext`` and ``$logGlobalContext`` are independent. You can enable either - or both. When both are enabled, per-call context and global context are merged before - being passed to handlers. +.. note:: ``$logContext`` and ``$logGlobalContext`` are independent. You can enable either or both. When both are + enabled, per-call context and global context are merged before being passed to handlers. Using Third-Party Loggers ========================= diff --git a/user_guide_src/source/helpers/array_helper.rst b/user_guide_src/source/helpers/array_helper.rst index 8d859de077a1..3d6408e24267 100644 --- a/user_guide_src/source/helpers/array_helper.rst +++ b/user_guide_src/source/helpers/array_helper.rst @@ -22,16 +22,12 @@ Available Functions The following functions are available: -.. note:: Since v4.8.0, the dot-path helpers can read values from arrays or - objects, including ``Entity`` objects. This applies to - :php:func:`dot_array_search()`, :php:func:`dot_array_has()`, - :php:func:`dot_array_only()`, :php:func:`dot_array_except()`, and - :php:func:`array_group_by()`. - - :php:func:`dot_array_set()` and :php:func:`dot_array_unset()` still modify - arrays only. :php:func:`dot_array_only()` and - :php:func:`dot_array_except()` always return arrays. See their descriptions - for how object values are handled. +.. note:: Since v4.8.0, the dot-path helpers can read values from arrays or objects, including ``Entity`` objects. This + applies to :php:func:`dot_array_search()`, :php:func:`dot_array_has()`, :php:func:`dot_array_only()`, + :php:func:`dot_array_except()`, and :php:func:`array_group_by()`. + + :php:func:`dot_array_set()` and :php:func:`dot_array_unset()` still modify arrays only. :php:func:`dot_array_only()` + and :php:func:`dot_array_except()` always return arrays. See their descriptions for how object values are handled. .. php:function:: dot_array_search(string $search, array|object $values) diff --git a/user_guide_src/source/incoming/filters.rst b/user_guide_src/source/incoming/filters.rst index d15df025a858..fa960dd4ff19 100644 --- a/user_guide_src/source/incoming/filters.rst +++ b/user_guide_src/source/incoming/filters.rst @@ -386,18 +386,20 @@ Request ID .. versionadded:: 4.8.0 -This filter adds a request ID to each request in context of the application. This can be useful for -debugging and logging purposes, as it allows you to trace a specific request through the application. +This filter adds a request ID to each request in the context of the application. This can be useful for debugging and +logging purposes, as it allows you to trace a specific request through the application. -Framework-generated IDs are 32-character hexadecimal strings and are unique for practical purposes, however valid incoming IDs are reused. -It is added to the request's context and can be accessed via the ``request_id`` key. +Framework-generated IDs are 32-character hexadecimal strings and are unique for practical purposes. However, valid +incoming IDs are reused. It is added to the request's context and can be accessed via the ``request_id`` key. -To enable this filter, simply add/uncomment the ``requestid`` alias in the ``$required['before']`` and ``$required['after']`` array in **app/Config/Filters.php**: +To enable this filter, simply add/uncomment the ``requestid`` alias in the ``$required['before']`` and +``$required['after']`` array in **app/Config/Filters.php**: .. literalinclude:: filters/014.php -.. note:: If the incoming request has a header named ``X-Request-ID``, the value of that header - will be used as the request ID instead of generating a new one or checking for uniqueness. - The framework does basic validation to ensure that the incoming request ID is a non-empty string, - has at most 64 characters, and contains only valid characters (alphanumeric, dot, underscore, colon, and hyphen). - If the validation fails, a new request ID will be generated as normal. This allows you to pass in your own request ID if you have one available, such as from a client or a load balancer. +.. note:: If the incoming request has a header named ``X-Request-ID``, the value of that header will be used as the + request ID instead of generating a new one or checking for uniqueness. The framework does basic validation to ensure + that the incoming request ID is a non-empty string, has at most 64 characters, and contains only valid characters + (alphanumeric, dot, underscore, colon, and hyphen). If the validation fails, a new request ID will be generated as + normal. This allows you to pass in your own request ID if you have one available, such as from a client or a load + balancer. diff --git a/user_guide_src/source/incoming/form_requests.rst b/user_guide_src/source/incoming/form_requests.rst index 6528ca33a8a0..b64a95643631 100644 --- a/user_guide_src/source/incoming/form_requests.rst +++ b/user_guide_src/source/incoming/form_requests.rst @@ -6,12 +6,10 @@ Form Requests .. versionadded:: 4.8.0 -A **FormRequest** is a dedicated class that encapsulates the validation rules, -custom error messages, and authorization logic for a single HTTP request. -Instead of writing validation code inside every controller method, you define -it once in a FormRequest class and type-hint it in the controller method -signature - the framework resolves, authorizes, and validates the request -automatically before your method body runs. +A **FormRequest** is a dedicated class that encapsulates the validation rules, custom error messages, and authorization +logic for a single HTTP request. Instead of writing validation code inside every controller method, you define it once +in a FormRequest class and type-hint it in the controller method signature - the framework resolves, authorizes, and +validates the request automatically before your method body runs. .. contents:: :local: @@ -25,9 +23,8 @@ Use the ``make:request`` Spark command to generate a new FormRequest class:: php spark make:request StorePostRequest -This creates **app/Requests/StorePostRequest.php**. Fill in the ``rules()`` -method with the same field/rule pairs you would normally pass to -:ref:`validation-running`: +This creates **app/Requests/StorePostRequest.php**. Fill in the ``rules()`` method with the same field/rule pairs you +would normally pass to :ref:`validation-running`: .. literalinclude:: form_requests/001.php :lines: 2- @@ -36,12 +33,10 @@ method with the same field/rule pairs you would normally pass to Using a Form Request in a Controller ************************************ -Type-hint the FormRequest class as a parameter of your controller method. The -framework instantiates it, runs authorization and validation, and passes the -resolved object to your method. If validation fails, the default behavior -redirects back with errors for web requests or returns a 422 JSON response for -JSON request bodies or requests that prefer ``application/json`` - the method -body is never reached. +Type-hint the FormRequest class as a parameter of your controller method. The framework instantiates it, runs +authorization and validation, and passes the resolved object to your method. If validation fails, the default behavior +redirects back with errors for web requests or returns a 422 JSON response for JSON request bodies or requests that +prefer ``application/json`` - the method body is never reached. .. literalinclude:: form_requests/002.php :lines: 2- @@ -50,16 +45,14 @@ body is never reached. Accessing Validated Data ************************ -``getValidated()`` returns an array containing only the fields that were declared -in ``rules()``. Fields submitted by the client that are not covered by a rule -are silently discarded, protecting against mass-assignment. +``getValidated()`` returns an array containing only the fields that were declared in ``rules()``. Fields submitted by +the client that are not covered by a rule are silently discarded, protecting against mass-assignment. .. literalinclude:: form_requests/009.php :lines: 2- -Use ``getValidatedInput()`` when you want to read a single validated field or -check whether a validated key exists, including keys whose value is ``null``. -The input object supports dot-array syntax for nested validated data: +Use ``getValidatedInput()`` when you want to read a single validated field or check whether a validated key exists, +including keys whose value is ``null``. The input object supports dot-array syntax for nested validated data: .. literalinclude:: form_requests/014.php :lines: 2- @@ -67,40 +60,35 @@ The input object supports dot-array syntax for nested validated data: Typed Validated Input ===================== -``getValidatedInput()`` returns the same validated data as a typed input object. -This complements ``getValidated()`` by making common controller values easier -to read after validation has succeeded. +``getValidatedInput()`` returns the same validated data as a typed input object. This complements ``getValidated()`` by +making common controller values easier to read after validation has succeeded. -After the FormRequest has been validated, read the successful values in the -controller: +After the FormRequest has been validated, read the successful values in the controller: .. literalinclude:: form_requests/015.php :lines: 2- -These typed methods do not replace validation rules. They only make accepted -values easier to consume in the controller. See :ref:`validation-validated-input` -for the full behavior of the typed input methods. +These typed methods do not replace validation rules. They only make accepted values easier to consume in the controller. +See :ref:`validation-validated-input` for the full behavior of the typed input methods. Accessing Other Request Data ============================ -For anything not covered by ``getValidated()`` - uploaded files, request headers, -the client IP address, raw input, and so on - use ``$this->request`` as usual. -It is the same :doc:`IncomingRequest ` instance that +For anything not covered by ``getValidated()`` - uploaded files, request headers, the client IP address, raw input, and +so on - use ``$this->request`` as usual. It is the same :doc:`IncomingRequest ` instance that the FormRequest uses internally: .. literalinclude:: form_requests/010.php :lines: 2- -******************************************* +***************************************** Route Parameters Alongside a Form Request -******************************************* +***************************************** -Required scalar route parameters come first, then the FormRequest, then any optional -scalar parameters. The framework matches URI segments to scalar parameters positionally -and injects the FormRequest wherever the type hint appears. Parameters declared with -PHP's ``...`` syntax captures all remaining URI segments. Optional scalar parameters -after the FormRequest get their default values when no URI segment is left to fill them. +Required scalar route parameters come first, then the FormRequest, then any optional scalar parameters. The framework +matches URI segments to scalar parameters positionally and injects the FormRequest wherever the type hint appears. +Parameters declared with PHP's ``...`` syntax capture all remaining URI segments. Optional scalar parameters after the +FormRequest get their default values when no URI segment is left to fill them. .. literalinclude:: form_requests/003.php :lines: 2- @@ -109,9 +97,8 @@ after the FormRequest get their default values when no URI segment is left to fi Closure Routes ************** -FormRequest injection works identically in closure routes and follows the same -signature shape: route parameters first, then the FormRequest, then optional -scalar parameters. The framework resolves it the same way it does for controller +FormRequest injection works identically in closure routes and follows the same signature shape: route parameters first, +then the FormRequest, then optional scalar parameters. The framework resolves it the same way it does for controller methods. .. literalinclude:: form_requests/011.php @@ -121,14 +108,12 @@ methods. ``_remap()`` Methods ******************** -Automatic FormRequest injection is **not** supported for controller methods that -use ``_remap()``. Because ``_remap()`` has a fixed signature -``($method, ...$params)``, there is no typed position for the framework to -inject a FormRequest into. +Automatic FormRequest injection is **not** supported for controller methods that use ``_remap()``. Because ``_remap()`` +has a fixed signature ``($method, ...$params)``, there is no typed position for the framework to inject a FormRequest +into. -Instantiate the FormRequest manually inside ``_remap()`` and call -``resolveRequest()`` yourself. The method returns ``null`` on success or a -``ResponseInterface`` when authorization or validation fails: +Instantiate the FormRequest manually inside ``_remap()`` and call ``resolveRequest()`` yourself. The method returns +``null`` on success or a ``ResponseInterface`` when authorization or validation fails: .. literalinclude:: form_requests/012.php :lines: 2- @@ -137,8 +122,8 @@ Instantiate the FormRequest manually inside ``_remap()`` and call Custom Error Messages ********************* -Override ``messages()`` to return field-specific error messages. The format is -identical to the ``$errors`` argument of :ref:`saving-validation-rules-to-config-file`: +Override ``messages()`` to return field-specific error messages. The format is identical to the ``$errors`` argument of +:ref:`saving-validation-rules-to-config-file`: .. literalinclude:: form_requests/004.php :lines: 2- @@ -147,53 +132,46 @@ identical to the ``$errors`` argument of :ref:`saving-validation-rules-to-config Authorization ************* -Override ``isAuthorized()`` to control whether the current user is allowed to make -this request. Return ``false`` to reject the request with a 403 Forbidden -response before validation even runs. +Override ``isAuthorized()`` to control whether the current user is allowed to make this request. Return ``false`` to +reject the request with a 403 Forbidden response before validation even runs. .. literalinclude:: form_requests/005.php :lines: 2- -.. note:: The ``isAuthorized()`` check runs before ``prepareForValidation()`` and - before validation itself. An unauthorized request never reaches the - validation stage. +.. note:: The ``isAuthorized()`` check runs before ``prepareForValidation()`` and before validation itself. An + unauthorized request never reaches the validation stage. -********************************* +******************************** Preparing Data Before Validation -********************************* +******************************** -Override ``prepareForValidation(array $data): array`` to normalize or derive -input values before the validation rules are applied. The method receives the -same data array that will be passed to the validator and must return the -(possibly modified) array. This is useful for computed fields such as slugs, -normalized phone numbers, or trimmed strings. +Override ``prepareForValidation(array $data): array`` to normalize or derive input values before the validation rules +are applied. The method receives the same data array that will be passed to the validator and must return the (possibly +modified) array. This is useful for computed fields such as slugs, normalized phone numbers, or trimmed strings. .. literalinclude:: form_requests/006.php :lines: 2- -.. note:: When validation fails and the default redirect response is used, - ``old()`` returns the prepared validation data. Use ``getValidated()`` to - access the processed data after a successful request. +.. note:: When validation fails and the default redirect response is used, ``old()`` returns the prepared validation + data. Use ``getValidated()`` to access the processed data after a successful request. .. _form-request-validation-data: -**************************** +*************************** Customizing the Data Source -**************************** +*************************** -By default ``validationData()`` selects the appropriate data source -automatically based on the HTTP method and ``Content-Type`` header: +By default ``validationData()`` selects the appropriate data source automatically based on the HTTP method and +``Content-Type`` header: * **JSON request** (``Content-Type: application/json``) -> decoded JSON body * **PUT / PATCH / DELETE / QUERY** (non-multipart) -> raw body via ``getRawInput()`` * **GET / HEAD** -> query-string parameters via ``getGet()`` * **Everything else** (POST, multipart) -> POST body via ``getPost()`` -This avoids the pitfalls of :ref:`validation-withrequest`, which mixes GET and -POST data via ``getVar()``. +This avoids the pitfalls of :ref:`validation-withrequest`, which mixes GET and POST data via ``getVar()``. -Override ``validationData()`` when you need a different data source - for -example, to merge GET and POST parameters: +Override ``validationData()`` when you need a different data source - for example, to merge GET and POST parameters: .. literalinclude:: form_requests/007.php :lines: 2- @@ -202,20 +180,17 @@ example, to merge GET and POST parameters: Customizing Failure Behavior **************************** -Override ``failedValidation()`` and ``failedAuthorization()`` to take full -control of what happens when a request is rejected. Both methods return a -``ResponseInterface`` that the framework sends to the client: +Override ``failedValidation()`` and ``failedAuthorization()`` to take full control of what happens when a request is +rejected. Both methods return a ``ResponseInterface`` that the framework sends to the client: .. literalinclude:: form_requests/008.php :lines: 2- -The default ``failedValidation()`` returns a 422 JSON response for JSON request -bodies or requests that prefer ``application/json`` through the ``Accept`` -header. Otherwise, it redirects back with input and validation errors. +The default ``failedValidation()`` returns a 422 JSON response for JSON request bodies or requests that prefer +``application/json`` through the ``Accept`` header. Otherwise, it redirects back with input and validation errors. -.. note:: The ``X-Requested-With: XMLHttpRequest`` header alone does not select - a JSON response. If an AJAX client expects JSON validation errors, send an - ``Accept: application/json`` header. If your application needs HTML +.. note:: The ``X-Requested-With: XMLHttpRequest`` header alone does not select a JSON response. If an AJAX client + expects JSON validation errors, send an ``Accept: application/json`` header. If your application needs HTML fragments for AJAX form failures, override ``failedValidation()``. .. _form-request-flash-normalized: @@ -223,45 +198,37 @@ header. Otherwise, it redirects back with input and validation errors. Flashing Normalized Input ========================= -If your ``prepareForValidation()`` transforms visible form fields (for example, -trimming strings or canonicalizing values), the default redirect response flashes -the prepared validation data as old input. +If your ``prepareForValidation()`` transforms visible form fields (for example, trimming strings or canonicalizing +values), the default redirect response flashes the prepared validation data as old input. -If you override ``failedValidation()`` and still need to flash normalized input, -use the second ``$preparedData`` argument. It contains the same data that was -passed to validation: +If you override ``failedValidation()`` and still need to flash normalized input, use the second ``$preparedData`` +argument. It contains the same data that was passed to validation: .. literalinclude:: form_requests/013.php :lines: 2- -The prepared data has not passed validation. After successful validation, use -``getValidated()`` or ``getValidatedInput()`` for trusted values. +The prepared data has not passed validation. After successful validation, use ``getValidated()`` or +``getValidatedInput()`` for trusted values. -***************************************** +**************************************** How the Framework Resolves Form Requests -***************************************** +**************************************** -When the router dispatches a controller method or closure route, the framework -inspects the callable's parameter list using reflection. For each parameter -whose type extends ``FormRequest``: +When the router dispatches a controller method or closure route, the framework inspects the callable's parameter list +using reflection. For each parameter whose type extends ``FormRequest``: -#. A new instance is created with the current request injected via the - constructor. -#. ``isAuthorized()`` is called. If it returns ``false``, ``failedAuthorization()`` - is called, and its response is returned to the client. +#. A new instance is created with the current request injected via the constructor. +#. ``isAuthorized()`` is called. If it returns ``false``, ``failedAuthorization()`` is called, and its response is + returned to the client. #. ``validationData()`` collects the data to validate. -#. ``prepareForValidation()`` receives that data and may modify it before the - rules are applied. -#. ``run()`` executes the validation rules. If it fails, ``failedValidation()`` - is called, and its response is returned to the client. -#. The validated data is stored internally and available via ``getValidated()`` - and ``getValidatedInput()``. -#. The resolved FormRequest object is injected into the controller method or - closure. - -The callable is never invoked if authorization or validation fails. Non-FormRequest -parameters consume URI route segments in declaration order; variadic parameters -receive all remaining segments. - -.. note:: Automatic injection does not apply to ``_remap()`` methods. See - `_remap() Methods`_ above for the manual workaround. +#. ``prepareForValidation()`` receives that data and may modify it before the rules are applied. +#. ``run()`` executes the validation rules. If it fails, ``failedValidation()`` is called, and its response is returned + to the client. +#. The validated data is stored internally and available via ``getValidated()`` and ``getValidatedInput()``. +#. The resolved FormRequest object is injected into the controller method or closure. + +The callable is never invoked if authorization or validation fails. Non-FormRequest parameters consume URI route +segments in declaration order; variadic parameters receive all remaining segments. + +.. note:: Automatic injection does not apply to ``_remap()`` methods. See `_remap() Methods`_ above for the manual + workaround. diff --git a/user_guide_src/source/incoming/incomingrequest.rst b/user_guide_src/source/incoming/incomingrequest.rst index 1ba3aa7a1104..a06790e3b15f 100644 --- a/user_guide_src/source/incoming/incomingrequest.rst +++ b/user_guide_src/source/incoming/incomingrequest.rst @@ -179,22 +179,19 @@ Typed Request Input .. versionadded:: 4.8.0 -``input()`` returns a ``CodeIgniter\HTTP\RequestInput`` object. Use it to read -values from a specific part of the request with typed fallback helpers: +``input()`` returns a ``CodeIgniter\HTTP\RequestInput`` object. Use it to read values from a specific part of the +request with typed fallback helpers: .. literalinclude:: incomingrequest/046.php :lines: 2- -``input()->get()`` reads query-string parameters. ``input()->post()`` reads -POST body parameters. ``input()->json()`` reads JSON request body parameters. -``input()->raw()`` reads raw input parameters, like ``getRawInput()``. +``input()->get()`` reads query-string parameters. ``input()->post()`` reads POST body parameters. ``input()->json()`` +reads JSON request body parameters. ``input()->raw()`` reads raw input parameters, like ``getRawInput()``. -These methods keep GET, POST, JSON, and raw data separate. They do not combine -multiple request sources for you. +These methods keep GET, POST, JSON, and raw data separate. They do not combine multiple request sources for you. -These methods do not validate input. They are fallback-friendly helpers for -reading raw request data. Use Validation or :ref:`form-requests` when input -must satisfy application rules before it is consumed. +These methods do not validate input. They are fallback-friendly helpers for reading raw request data. Use Validation or +:ref:`form-requests` when input must satisfy application rules before it is consumed. .. _incomingrequest-getting-json-data: diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 827cbc1fa5f8..8bcb9f8a9cee 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -78,18 +78,15 @@ You can use any standard HTTP verb (GET, POST, PUT, DELETE, OPTIONS, etc): .. literalinclude:: routing/003.php -``QUERY`` routes are for the HTTP QUERY method defined by RFC 10008. -``QUERY`` requests are safe and idempotent like GET, but send query content in the -request body. Read that body explicitly with request input methods such as -``$this->request->input()->json()`` or ``$this->request->input()->raw()``. Your -controller should reject missing or unsupported ``Content-Type`` values for the -query content it expects. Do not use ``QUERY`` for operations that create, +``QUERY`` routes are for the HTTP QUERY method defined by RFC 10008. ``QUERY`` requests are safe and idempotent like +GET, but send query content in the request body. Read that body explicitly with request input methods such as +``$this->request->input()->json()`` or ``$this->request->input()->raw()``. Your controller should reject missing or +unsupported ``Content-Type`` values for the query content it expects. Do not use ``QUERY`` for operations that create, update, delete, or otherwise change server state. -.. note:: Your web server, proxy, or CORS configuration may need to allow the - ``QUERY`` method before these requests can reach CodeIgniter. For - browser-based cross-origin requests, ensure ``QUERY`` is included in your - allowed CORS methods so preflight requests can succeed. +.. note:: Your web server, proxy, or CORS configuration may need to allow the ``QUERY`` method before these requests can + reach CodeIgniter. For browser-based cross-origin requests, ensure ``QUERY`` is included in your allowed CORS + methods so preflight requests can succeed. You can supply multiple verbs that a route should match by passing them in as an array to the ``match()`` method: @@ -257,27 +254,25 @@ Sample Values for ``spark routes`` .. versionadded:: 4.8.0 -The :doc:`spark routes ` command needs a concrete URI that matches each route in order -to resolve the filters that would run for it. For the built-in placeholders (``num``, ``segment``, -``alpha``, and so on) it uses a hardcoded sample value. For custom placeholders it now attempts to generate -a value from the placeholder's regular expression, so common patterns like ``[A-Z]{3}[0-9]+`` or the UUID -example above work without any extra configuration. +The :doc:`spark routes ` command needs a concrete URI that matches each route in order to resolve +the filters that would run for it. For the built-in placeholders (``num``, ``segment``, ``alpha``, and so on) it uses a +hardcoded sample value. For custom placeholders it now attempts to generate a value from the placeholder's regular +expression, so common patterns like ``[A-Z]{3}[0-9]+`` or the UUID example above work without any extra configuration. -If the regex uses features the generator cannot reverse (lookarounds, backreferences, complex alternations, -etc.), the Before/After Filters columns fall back to showing ````. You can fix that by providing an -explicit sample in ``app/Config/Routing.php``: +If the regex uses features the generator cannot reverse (lookarounds, backreferences, complex alternations, etc.), the +Before/After Filters columns fall back to showing ````. You can fix that by providing an explicit sample in +``app/Config/Routing.php``: .. literalinclude:: routing/075.php -Each value must match its placeholder's regular expression. A value that does not match is ignored. Entries -override the samples for the built-in placeholders too, not only custom ones. +Each value must match its placeholder's regular expression. A value that does not match is ignored. Entries override the +samples for the built-in placeholders too, not only custom ones. -The resolution order is: a matching value declared in ``$placeholderSamples``, then the built-in sample for -standard placeholders, then an auto-generated value, then ```` as a last resort. +The resolution order is: a matching value declared in ``$placeholderSamples``, then the built-in sample for standard +placeholders, then an auto-generated value, then ```` as a last resort. -.. note:: This configuration only affects the ``spark routes`` display. - Filter execution at request time already honors custom placeholders - without any setup. +.. note:: This configuration only affects the ``spark routes`` display. Filter execution at request time already honors + custom placeholders without any setup. Regular Expressions ------------------- diff --git a/user_guide_src/source/installation/upgrade_480.rst b/user_guide_src/source/installation/upgrade_480.rst index 35435a3308a9..e687a70a2dc3 100644 --- a/user_guide_src/source/installation/upgrade_480.rst +++ b/user_guide_src/source/installation/upgrade_480.rst @@ -25,52 +25,44 @@ Breaking Changes Query Builder Bindings ====================== -``getCompiledSelect()``, ``getCompiledInsert()``, ``getCompiledUpdate()``, -``getCompiledDelete()``, ``delete()`` and ``countAllResults()`` now clear their -bindings and binding counters after compilation or execution when their reset -parameter is enabled (the default). This prevents accumulation when a builder -is reused, but ``getBinds()`` now returns an empty array after these calls. -Bindings are also cleared when the database query returns ``false``. - -The :ref:`getCompiledSelect() documentation ` -already states that the default reset should work just like ``get()``. -Clearing the bindings corrects that inconsistency while changing the observable -result of ``getBinds()``. - -To retain the query and its bindings, pass ``false`` as the first argument to -any of the compiled methods or ``countAllResults()``, or as the third argument -to ``delete()``: ``$builder->delete('', null, false)``. This also retains query -clauses such as WHERE and LIMIT. - -If you only need a snapshot of the bindings, read them before compilation or -execution instead. When passing conditions directly to ``delete()``, move them -into ``where()`` first so their bindings are available before execution. - -Test mode retains bindings needed by the placeholders in its returned raw SQL. -In particular, ``getCompiledDelete()`` needs these bindings to finish compilation -and clears them afterwards according to its own reset parameter. +``getCompiledSelect()``, ``getCompiledInsert()``, ``getCompiledUpdate()``, ``getCompiledDelete()``, ``delete()`` and +``countAllResults()`` now clear their bindings and binding counters after compilation or execution when their reset +parameter is enabled (the default). This prevents accumulation when a builder is reused, but ``getBinds()`` now returns +an empty array after these calls. Bindings are also cleared when the database query returns ``false``. + +The :ref:`getCompiledSelect() documentation ` already states that the default reset +should work just like ``get()``. Clearing the bindings corrects that inconsistency while changing the observable result +of ``getBinds()``. + +To retain the query and its bindings, pass ``false`` as the first argument to any of the compiled methods or +``countAllResults()``, or as the third argument to ``delete()``: ``$builder->delete('', null, false)``. This also +retains query clauses such as WHERE and LIMIT. + +If you only need a snapshot of the bindings, read them before compilation or execution instead. When passing conditions +directly to ``delete()``, move them into ``where()`` first so their bindings are available before execution. + +Test mode retains bindings needed by the placeholders in its returned raw SQL. In particular, ``getCompiledDelete()`` +needs these bindings to finish compilation and clears them afterwards according to its own reset parameter. Query Builder JOIN Overrides ============================ -``BaseBuilder::join()`` and ``BaseBuilder::compileJoinTable()`` now accept -``RawSql|string`` for the table argument. If a custom Query Builder subclass -overrides either method, change its ``$table`` parameter type from ``string`` -to ``RawSql|string``. The method body must also handle ``RawSql`` without -escaping or prefixing it. +``BaseBuilder::join()`` and ``BaseBuilder::compileJoinTable()`` now accept ``RawSql|string`` for the table argument. If +a custom Query Builder subclass overrides either method, change its ``$table`` parameter type from ``string`` to +``RawSql|string``. The method body must also handle ``RawSql`` without escaping or prefixing it. Console Exit Codes ================== -Previously, returning a non-integer value from a command run through ``spark`` would be treated as a successful execution (exit code ``0``). -Starting with v4.8.0, this behavior is still supported but will trigger a deprecation notice. Commands should now return an integer exit code -to ensure proper behavior across all platforms. +Previously, returning a non-integer value from a command run through ``spark`` would be treated as a successful +execution (exit code ``0``). Starting with v4.8.0, this behavior is still supported but will trigger a deprecation +notice. Commands should now return an integer exit code to ensure proper behavior across all platforms. Command Alternatives Visibility =============================== -``CodeIgniter\CLI\Commands::getCommandAlternatives()`` is now ``public``. If you extend ``Commands`` and override this method, -change the override's visibility from ``protected`` to ``public``. +``CodeIgniter\CLI\Commands::getCommandAlternatives()`` is now ``public``. If you extend ``Commands`` and override this +method, change the override's visibility from ``protected`` to ``public``. .. _upgrade-480-cli-error-output: @@ -78,67 +70,59 @@ CLI Error Output ================ Uncaught exceptions in ``spark``, and errors shown with ``BaseCommand::showError()`` or -``AbstractCommand::renderThrowable()``, are now printed to STDERR instead of STDOUT, so they no -longer mix with output read by another program. -The exit code is unchanged. +``AbstractCommand::renderThrowable()``, are now printed to STDERR instead of STDOUT, so they no longer mix with output +read by another program. The exit code is unchanged. -If you redirect only STDOUT to a log file (``php spark my:command > my.log``), errors no longer -reach that file. Redirect STDERR as well to keep them (``php spark my:command > my.log 2>&1``). +If you redirect only STDOUT to a log file (``php spark my:command > my.log``), errors no longer reach that file. +Redirect STDERR as well to keep them (``php spark my:command > my.log 2>&1``). -A custom CLI error view that writes with ``echo`` instead of the ``CLI`` class still prints to -STDOUT. +A custom CLI error view that writes with ``echo`` instead of the ``CLI`` class still prints to STDOUT. .. _upgrade-480-cli-output-classes: CLI Output Classes ================== -The static ``CLI`` class now delegates its output methods to instance classes: -``Output``, ``Terminal``, ``ProgressBar``, ``Table``, and ``Color``. Calling the static -methods keeps working, and the output is byte-identical, except that ``CLI::beep()`` now -goes through the ``InputOutput`` transport and that ``CLI::print()`` of text ending in -a line feed, or a progress bar drawn with ``CLI::showProgress()``, no longer adds a blank -line before the next ``CLI::write()``. - -If you extend ``CLI`` and override ``color()``, or read the ``$lastWrite``, ``$isColored``, -``$io``, ``$height``, ``$width``, ``$foreground_colors``, or ``$background_colors`` -properties, those overrides and reads no longer take effect. The properties are still -declared but unused. An override of ``write()`` is still reached by ``newLine()``, -``table()``, and ``clearScreen()``, and an override of ``fwrite()`` by ``wait()`` and -``clearScreen()``, but no longer by ``print()``, ``error()``, ``beep()``, or -``showProgress()``. Replace a transport override with ``CLI::setInputOutput()``, and a -colour or terminal override with an ``Output`` or ``Terminal`` subclass passed to -``CLI::setOutput()``. Overriding ``Output::color()`` covers ``print()``, ``write()``, and -``error()`` as well, since they colour through it. - -Tests that reached those properties through reflection should use the instances -returned by ``CLI::getOutput()`` and ``CLI::getTerminal()`` instead. +The static ``CLI`` class now delegates its output methods to instance classes: ``Output``, ``Terminal``, +``ProgressBar``, ``Table``, and ``Color``. Calling the static methods keeps working, and the output is byte-identical, +except that ``CLI::beep()`` now goes through the ``InputOutput`` transport and that ``CLI::print()`` of text ending in a +line feed, or a progress bar drawn with ``CLI::showProgress()``, no longer adds a blank line before the next +``CLI::write()``. + +If you extend ``CLI`` and override ``color()``, or read the ``$lastWrite``, ``$isColored``, ``$io``, ``$height``, +``$width``, ``$foreground_colors``, or ``$background_colors`` properties, those overrides and reads no longer take +effect. The properties are still declared but unused. An override of ``write()`` is still reached by ``newLine()``, +``table()``, and ``clearScreen()``, and an override of ``fwrite()`` by ``wait()`` and ``clearScreen()``, but no longer +by ``print()``, ``error()``, ``beep()``, or ``showProgress()``. Replace a transport override with +``CLI::setInputOutput()``, and a color or terminal override with an ``Output`` or ``Terminal`` subclass passed to +``CLI::setOutput()``. Overriding ``Output::color()`` covers ``print()``, ``write()``, and ``error()`` as well, since +they color through it. + +Tests that reached those properties through reflection should use the instances returned by ``CLI::getOutput()`` and +``CLI::getTerminal()`` instead. .. _upgrade-480-cli-prompt: CLI Prompt ========== -``CLI::prompt()``, ``CLI::promptByKey()``, and ``CLI::promptByMultipleKeys()`` now delegate to -the ``Prompt`` class, and ask and answer exactly as before. +``CLI::prompt()``, ``CLI::promptByKey()``, and ``CLI::promptByMultipleKeys()`` now delegate to the ``Prompt`` class, and +ask and answer exactly as before. -If you extend ``CLI`` and override ``validate()``, the prompts no longer call it. Extend ``Prompt`` -and override its ``validate()`` instead, then return your subclass from ``getPrompt()``: the static -``CLI::getPrompt()`` in your ``CLI`` subclass, or ``AbstractCommand::getPrompt()`` in a modern command. -To change only the validation, pass a validation instance of your own to the ``Prompt`` constructor. -An override of ``CLI::prompt()`` is still reached by calls to ``CLI::prompt()``, but no longer by -``promptByKey()`` or ``promptByMultipleKeys()``. +If you extend ``CLI`` and override ``validate()``, the prompts no longer call it. Extend ``Prompt`` and override its +``validate()`` instead, then return your subclass from ``getPrompt()``: the static ``CLI::getPrompt()`` in your ``CLI`` +subclass, or ``AbstractCommand::getPrompt()`` in a modern command. To change only the validation, pass a validation +instance of your own to the ``Prompt`` constructor. An override of ``CLI::prompt()`` is still reached by calls to +``CLI::prompt()``, but no longer by ``promptByKey()`` or ``promptByMultipleKeys()``. Uploaded File Move Return Type ============================== -``CodeIgniter\HTTP\Files\UploadedFileInterface::move()`` now returns ``static`` -instead of ``bool``, matching ``CodeIgniter\Files\File::move()`` which -``UploadedFile`` extends. +``CodeIgniter\HTTP\Files\UploadedFileInterface::move()`` now returns ``static`` instead of ``bool``, matching +``CodeIgniter\Files\File::move()`` which ``UploadedFile`` extends. -If you have a custom implementation of ``UploadedFileInterface``, or a class -extending ``UploadedFile`` that overrides ``move()``, return the instance -instead of ``true``: +If you have a custom implementation of ``UploadedFileInterface``, or a class extending ``UploadedFile`` that overrides +``move()``, return the instance instead of ``true``: .. code-block:: php @@ -158,20 +142,17 @@ instead of ``true``: return $this; } -Calling code that only tests the result, such as ``if ($file->move($path))``, -needs no change because the returned instance is truthy. Code comparing the -result strictly against ``true`` must be updated. +Calling code that only tests the result, such as ``if ($file->move($path))``, needs no change because the returned +instance is truthy. Code comparing the result strictly against ``true`` must be updated. Outgoing Request Constructor ============================ -``CodeIgniter\HTTP\OutgoingRequest::__construct()`` now requires the ``$uri`` -parameter, which was previously ``?URI $uri = null``. Consequently -``OutgoingRequest::getUri()`` now returns ``URI`` instead of ``URI|null``. +``CodeIgniter\HTTP\OutgoingRequest::__construct()`` now requires the ``$uri`` parameter, which was previously +``?URI $uri = null``. Consequently ``OutgoingRequest::getUri()`` now returns ``URI`` instead of ``URI|null``. -Passing ``null`` only worked when a ``Host`` header was supplied in the same -call, because the constructor's host check short-circuits before dereferencing -the URI. Such calls must now pass a ``URI``: +Passing ``null`` only worked when a ``Host`` header was supplied in the same call, because the constructor's host check +short-circuits before dereferencing the URI. Such calls must now pass a ``URI``: .. code-block:: php @@ -185,11 +166,11 @@ Any other call that omitted ``$uri`` or passed ``null`` already failed with ``Call to a member function getHost() on null``, so it needs no migration. RedirectException Default Status Code -====================================== +===================================== -``CodeIgniter\HTTP\Exceptions\RedirectException`` no longer redeclares the inherited ``$code`` property to store the status applied to a -``Response`` whose status is outside the 301-308 range. If you have a subclass that overrode ``$code`` for this purpose, override the new -``$defaultStatusCode`` property instead: +``CodeIgniter\HTTP\Exceptions\RedirectException`` no longer redeclares the inherited ``$code`` property to store the +status applied to a ``Response`` whose status is outside the 301-308 range. If you have a subclass that overrode +``$code`` for this purpose, override the new ``$defaultStatusCode`` property instead: .. code-block:: php @@ -210,30 +191,23 @@ RedirectException Default Status Code Config Environment Lookups ========================== -``BaseConfig`` now reads the environment once and reuses the result, instead of -looking up every property of every config class. The matching rules and their -order are unchanged. One edge case behaves differently: a ``putenv()`` call -that does not also write ``$_ENV`` or ``$_SERVER``, made after the first config -class was built, may not be seen by config classes built later until -``BaseConfig::reset()`` runs. This applies whether the variable is added, -changed, or removed. In Worker Mode, this cache persists across requests, so -such changes may not be seen in later requests either. To make the change -visible, also set the value in ``$_ENV`` (or unset it there), or call -``BaseConfig::reset()``. Values loaded from the **.env** file are not -affected, since they are written to ``$_ENV`` and ``$_SERVER`` as well. - -Config classes that override ``getEnvValue()`` or ``initEnvValue()`` keep the -previous behavior. +``BaseConfig`` now reads the environment once and reuses the result, instead of looking up every property of every +config class. The matching rules and their order are unchanged. One edge case behaves differently: a ``putenv()`` call +that does not also write ``$_ENV`` or ``$_SERVER``, made after the first config class was built, may not be seen by +config classes built later until ``BaseConfig::reset()`` runs. This applies whether the variable is added, changed, or +removed. In Worker Mode, this cache persists across requests, so such changes may not be seen in later requests either. +To make the change visible, also set the value in ``$_ENV`` (or unset it there), or call ``BaseConfig::reset()``. Values +loaded from the **.env** file are not affected, since they are written to ``$_ENV`` and ``$_SERVER`` as well. + +Config classes that override ``getEnvValue()`` or ``initEnvValue()`` keep the previous behavior. RouteCollection Identifier Transformation ========================================= -``RouteCollection`` no longer applies ``esc(strip_tags())`` to namespace, -controller, and method values, including custom controllers passed to -``resource()`` and ``presenter()``. Applications relying on HTML tags being -stripped or special characters being HTML-encoded must preprocess these values -before passing them to the router. Values originating from untrusted sources -should be restricted to an explicit allowlist of permitted handlers. +``RouteCollection`` no longer applies ``esc(strip_tags())`` to namespace, controller, and method values, including +custom controllers passed to ``resource()`` and ``presenter()``. Applications relying on HTML tags being stripped or +special characters being HTML-encoded must preprocess these values before passing them to the router. Values originating +from untrusted sources should be restricted to an explicit allowlist of permitted handlers. ********************* Breaking Enhancements @@ -242,12 +216,10 @@ Breaking Enhancements Log Handler Interface ===================== -``CodeIgniter\Log\Handlers\HandlerInterface::handle()`` now accepts a third -parameter ``array $context = []``. +``CodeIgniter\Log\Handlers\HandlerInterface::handle()`` now accepts a third parameter ``array $context = []``. -If you have a custom log handler that overrides the ``handle()`` method -(whether implementing ``HandlerInterface`` directly or extending a built-in -handler class), you must update your ``handle()`` method signature: +If you have a custom log handler that overrides the ``handle()`` method (whether implementing ``HandlerInterface`` +directly or extending a built-in handler class), you must update your ``handle()`` method signature: .. code-block:: php @@ -257,61 +229,59 @@ handler class), you must update your ``handle()`` method signature: // After public function handle($level, $message, array $context = []): bool -The context array may contain the CI global context data under the -``HandlerInterface::GLOBAL_CONTEXT_KEY`` (``'_ci_context'``) key when -``$logGlobalContext`` is enabled in ``Config\Logger``. +The context array may contain the CI global context data under the ``HandlerInterface::GLOBAL_CONTEXT_KEY`` +(``'_ci_context'``) key when ``$logGlobalContext`` is enabled in ``Config\Logger``. ************* Project Files ************* -Some files in the **project space** (root, app, public, writable) received updates. Due to -these files being outside of the **system** scope they will not be changed without your intervention. +Some files in the **project space** (root, app, public, writable) received updates. Due to these files being outside of +the **system** scope they will not be changed without your intervention. -.. note:: There are some third-party CodeIgniter modules available to assist - with merging changes to the project space: +.. note:: There are some third-party CodeIgniter modules available to assist with merging changes to the project space: `Explore on Packagist `_. Content Changes =============== -The following files received significant changes (including deprecations or visual adjustments) -and it is recommended that you merge the updated versions with your application: +The following files received significant changes (including deprecations or visual adjustments) and it is recommended +that you merge the updated versions with your application: Config ------ -- app/Config/Filters.php +- **app/Config/Filters.php** - Added a new filter named ``requestid`` that adds a unique request ID to each request in the application's context. -- app/Config/Generators.php +- **app/Config/Generators.php** - ``Config\Generators::$views`` added entries for ``make:request``, ``make:test``, and ``make:transformer``, and dropped the stale ``session:migration`` entry. -- app/Config/Logger.php +- **app/Config/Logger.php** - ``Config\Logger::$handlers`` added a new key ``stopChainOnFailure`` to the ``FileHandler`` and ``ErrorlogHandler`` settings. -- app/Config/Mimes.php +- **app/Config/Mimes.php** - ``Config\Mimes::$mimes`` added a new key ``md`` for Markdown files. -- app/Config/Routing.php +- **app/Config/Routing.php** - ``Config\Routing::$placeholderSamples`` was added to provide sample values for custom route placeholders so the ``spark routes`` command can resolve their filters. -- app/Config/Security.php +- **app/Config/Security.php** - ``Config\Security::$csrfFetchMetadata`` and ``Config\Security::$csrfFetchMetadataRejectSameSite`` were added for Fetch Metadata based CSRF protection. Error Views ----------- -- app/Views/errors/cli/error_exception.php +- **app/Views/errors/cli/error_exception.php** - Shows the backtrace also when the command runs with ``-v``. -- app/Views/errors/html/debug.css +- **app/Views/errors/html/debug.css** - Added styles for the **Copy Details** button. -- app/Views/errors/html/debug.js +- **app/Views/errors/html/debug.js** - Added clipboard handling for the **Copy Details** button. -- app/Views/errors/html/error_exception.php +- **app/Views/errors/html/error_exception.php** - Added a **Copy Details** button to detailed HTML exception pages. -- app/Views/errors/html/error_report.php +- **app/Views/errors/html/error_report.php** - Added a Markdown error report partial used by the **Copy Details** button. All Changes =========== -This is a list of all files in the **project space** that received changes; -many will be simple comments or formatting that have no effect on the runtime: +This is a list of all files in the **project space** that received changes; many will be simple comments or formatting +that have no effect on the runtime: - @TODO diff --git a/user_guide_src/source/libraries/caching.rst b/user_guide_src/source/libraries/caching.rst index 1d658cdd4e51..0d269e87bced 100644 --- a/user_guide_src/source/libraries/caching.rst +++ b/user_guide_src/source/libraries/caching.rst @@ -139,25 +139,22 @@ Class Reference Gets an item from the cache. If ``null`` was returned, this will invoke the callback and save the result. Either way, this will return the value. - The ``$ttl`` parameter may also be a callable, allowing the TTL to be - determined dynamically at runtime. This is especially useful when the - expiration time depends on the computed value or requires an expensive - calculation. + The ``$ttl`` parameter may also be a callable, allowing the TTL to be determined dynamically at runtime. This is + especially useful when the expiration time depends on the computed value or requires an expensive calculation. - When a callable is provided, it will only be executed on a cache miss, - after the callback has been invoked. The callable always receives the - computed value as its first argument: + When a callable is provided, it is executed only on a cache miss, after the callback has been invoked. The + callable always receives the computed value as its first argument: .. literalinclude:: caching/015.php - This ensures that TTL computation is deferred until necessary and avoids - unnecessary overhead when the cache item already exists. + This ensures that TTL computation is deferred until necessary and avoids unnecessary overhead when the cache + item already exists. - .. note:: Prior to v4.8.0, the second parameter only accepted an integer TTL value. The ability to pass a callable was added in v4.8.0. + .. note:: Prior to v4.8.0, the second parameter only accepted an integer TTL value. The ability to pass a + callable was added in v4.8.0. - .. note:: When using the APCu cache handler, providing a callable TTL disables - the use of ``apcu_entry()`` and falls back to a manual cache retrieval - and storage process. As a result, the operation is no longer atomic + .. note:: When using the APCu cache handler, providing a callable TTL disables the use of ``apcu_entry()`` and + falls back to a manual cache retrieval and storage process. As a result, the operation is no longer atomic and may be subject to race conditions under high concurrency. If atomic behavior is required, use an integer TTL value. diff --git a/user_guide_src/source/libraries/curlrequest.rst b/user_guide_src/source/libraries/curlrequest.rst index 6fbcef726e18..f6aa02acf290 100644 --- a/user_guide_src/source/libraries/curlrequest.rst +++ b/user_guide_src/source/libraries/curlrequest.rst @@ -121,9 +121,8 @@ each take the URL as the first parameter and an array of options as the second: .. literalinclude:: curlrequest/007.php -The ``query()`` shortcut sends an HTTP QUERY request. QUERY requests usually -include query content, so use options such as ``json``, ``form_params``, or -``body`` to send the expected body. This is different from the ``query`` request +The ``query()`` shortcut sends an HTTP QUERY request. QUERY requests usually include query content, so use options such +as ``json``, ``form_params``, or ``body`` to send the expected body. This is different from the ``query`` request option, which adds URL query variables to the request URI. Base URI @@ -391,8 +390,8 @@ retry .. versionadded:: 4.8.0 -The ``retry`` option retries failed requests before returning the final response or throwing an exception. -For simple cases, set ``retry`` to the number of retry attempts: +The ``retry`` option retries failed requests before returning the final response or throwing an exception. For simple +cases, set ``retry`` to the number of retry attempts: .. code-block:: php @@ -407,36 +406,34 @@ For more control, pass an array: The available retry settings are: - ``max_retries``: Number of retries after the initial request. The default is ``3``. -- ``delay``: Delay in milliseconds before retrying. This may be an integer or a list of integers - for simple backoff. If the list is shorter than the retry count, the last value is reused. - The default is ``1000``. -- ``max_delay``: Maximum delay in milliseconds. This caps both the configured ``delay`` and a - valid ``Retry-After`` header. Set to ``0`` for no maximum delay. The default is ``30000``. +- ``delay``: Delay in milliseconds before retrying. This may be an integer or a list of integers for simple backoff. If + the list is shorter than the retry count, the last value is reused. The default is ``1000``. +- ``max_delay``: Maximum delay in milliseconds. This caps both the configured ``delay`` and a valid ``Retry-After`` + header. Set to ``0`` for no maximum delay. The default is ``30000``. - ``status_codes``: HTTP status codes that should be retried. The default is ``[429, 503, 504]``. - ``curl_errors``: Whether to retry transient cURL errors. The default is ``false``. -- ``respect_retry_after``: Whether to use a valid ``Retry-After`` header instead of the configured - ``delay``. The default is ``true``. +- ``respect_retry_after``: Whether to use a valid ``Retry-After`` header instead of the configured ``delay``. The + default is ``true``. -When ``respect_retry_after`` is enabled, a valid ``Retry-After`` header takes priority over the -configured ``delay``. The header may be either a number of seconds or an HTTP date. If the header -is invalid, the configured ``delay`` is used instead. +When ``respect_retry_after`` is enabled, a valid ``Retry-After`` header takes priority over the configured ``delay``. +The header may be either a number of seconds or an HTTP date. If the header is invalid, the configured ``delay`` is used +instead. -Because ``Retry-After`` is supplied by the remote server, it is capped by ``max_delay`` by default -to avoid unexpectedly long waits in the current PHP process. +Because ``Retry-After`` is supplied by the remote server, it is capped by ``max_delay`` by default to avoid unexpectedly +long waits in the current PHP process. -When ``curl_errors`` is enabled, only DNS resolution failures, connection failures, timeouts, -and send or receive failures are retried. +When ``curl_errors`` is enabled, only DNS resolution failures, connection failures, timeouts, and send or receive +failures are retried. -When `http_errors`_ is enabled, an HTTP error response is retried first if its status code is -configured in ``status_codes``. If all retry attempts are exhausted, the final HTTP error response -throws the same as a request without retries. +When `http_errors`_ is enabled, an HTTP error response is retried first if its status code is configured in +``status_codes``. If all retry attempts are exhausted, the final HTTP error response throws the same exception as a +request without retries. -.. note:: Retry delays block the current PHP process. The total request time may exceed the - configured `timeout`_ because each retry attempt has its own cURL timeout and retry delays - are added between attempts. +.. note:: Retry delays block the current PHP process. The total request time may exceed the configured `timeout`_ + because each retry attempt has its own cURL timeout and retry delays are added between attempts. -.. warning:: Be careful when retrying non-idempotent requests such as ``POST`` or ``PATCH``. - The remote server may receive the request more than once. +.. warning:: Be careful when retrying non-idempotent requests such as ``POST`` or ``PATCH``. The remote server may + receive the request more than once. timeout ======= diff --git a/user_guide_src/source/libraries/encryption.rst b/user_guide_src/source/libraries/encryption.rst index 3d9da139dce1..233444081ed4 100644 --- a/user_guide_src/source/libraries/encryption.rst +++ b/user_guide_src/source/libraries/encryption.rst @@ -233,28 +233,24 @@ Rotating with the ``key:rotate`` Command .. versionadded:: 4.8.0 -Step 2 above (demoting the current ``key`` and generating a new one) can be performed with the -``key:rotate`` spark command, which edits the **.env** file in place:: +Step 2 above (demoting the current ``key`` and generating a new one) can be performed with the ``key:rotate`` spark +command, which edits the **.env** file in place:: php spark key:rotate -The command reads ``encryption.key`` from your environment, prepends it to -``encryption.previousKeys`` (newest first, deduplicated), and writes a fresh ``encryption.key``. -Useful options: +The command reads ``encryption.key`` from your environment, prepends it to ``encryption.previousKeys`` (newest first, +deduplicated), and writes a fresh ``encryption.key``. Useful options: -- ``--prefix`` (``hex2bin`` or ``base64``, default ``hex2bin``) and ``--length`` (positive - integer, default ``32``) control how the new key is generated, mirroring ``key:generate``. -- ``--keep=N`` caps the retained ``previousKeys`` list to the ``N`` most recent entries. ``N`` must - be a non-negative integer; ``0`` (the default) keeps every previous key. -- ``--force`` / ``-f`` skips the interactive confirmation. Required when running with - ``--no-interaction``. +- ``--prefix`` (``hex2bin`` or ``base64``, default ``hex2bin``) and ``--length`` (positive integer, default ``32``) + control how the new key is generated, mirroring ``key:generate``. +- ``--keep=N`` caps the retained ``previousKeys`` list to the ``N`` most recent entries. ``N`` must be a non-negative + integer; ``0`` (the default) keeps every previous key. +- ``--force`` / ``-f`` skips the interactive confirmation. Required when running with ``--no-interaction``. -All three options are validated up-front, so an invalid value cannot leave the **.env** file -half-rotated. +All three options are validated up-front, so an invalid value cannot leave the **.env** file half-rotated. -.. warning:: ``key:rotate`` is not safe under concurrent execution. The command edits the - **.env** file without taking a file lock, so two operators (or two automation runs) - rotating at the same time can lose rotated-out keys. +.. warning:: ``key:rotate`` is not safe under concurrent execution. The command edits the **.env** file without taking a + file lock, so two operators (or two automation runs) rotating at the same time can lose rotated-out keys. Padding ======= diff --git a/user_guide_src/source/libraries/images.rst b/user_guide_src/source/libraries/images.rst index 3675f8e58d7c..99de8bd5d707 100644 --- a/user_guide_src/source/libraries/images.rst +++ b/user_guide_src/source/libraries/images.rst @@ -95,9 +95,10 @@ Image Quality ``save()`` can take an additional parameter ``$quality`` to alter the resulting image quality. Values range from 0 to 100 with 90 being the framework default. This parameter -only applies to JPEG, WebP and AVIF images, will be ignored otherwise: +only applies to JPEG, WebP, and AVIF images and is ignored otherwise: -.. note:: For AVIF images, it is suggested to set the ``$quality`` parameter to 52. See also https://www.php.net/manual/en/function.imageavif.php +.. note:: For AVIF images, it is suggested to set the ``$quality`` parameter to 52. See also + https://www.php.net/manual/en/function.imageavif.php .. note:: The parameter ``$quality`` for WebP can be used since v4.4.0. diff --git a/user_guide_src/source/libraries/locks.rst b/user_guide_src/source/libraries/locks.rst index 0af1b9c7e73b..8a29205abb93 100644 --- a/user_guide_src/source/libraries/locks.rst +++ b/user_guide_src/source/libraries/locks.rst @@ -8,87 +8,71 @@ Atomic Locks :local: :depth: 2 -Atomic locks provide a simple way to prevent the same task from running -concurrently across requests, CLI commands, or workers that share the same -cache storage. +Atomic locks provide a simple way to prevent the same task from running concurrently across requests, CLI commands, or +workers that share the same cache storage. -Locks are advisory. Your code must acquire the lock before entering the -critical section, and release it when the work is finished. +Locks are advisory. Your code must acquire the lock before entering the critical section, and release it when the work +is finished. ************* Configuration ************* -The Locks library uses the Cache service. The cache handler must support -owner-aware lock operations. The built-in **File**, **Redis**, and **Predis** -cache handlers support atomic lock operations. The **Memcached** cache handler -also supports locks, with Memcached-specific limitations described below. - -.. note:: Locks are most useful when all competing processes share the same cache - storage. The File handler is suitable for a single server. For multiple - application servers, use a shared handler such as Redis. - -.. important:: Locks are stored in the configured cache handler. Clearing or - flushing that cache storage, for example with ``cache()->clean()`` or a - Redis ``FLUSHDB``, may remove active locks. Avoid clearing shared lock - storage while lock-protected work is running, or use a dedicated cache - store for locks when that separation is important. - -.. note:: Memcached lock support requires the ``memcached`` PHP extension. - The older ``memcache`` extension does not provide the CAS operations used - for owner-aware refresh and best-effort release. Memcached does not provide - an atomic compare-and-delete command, so releasing a Memcached lock cannot - provide the same owner-checked atomic release guarantee as Redis or Predis. - A delayed release may delete a lock that expired and was acquired by another - owner. Memcached locks may also be lost if the Memcached server restarts, - evicts keys, or flushes its cache. - -.. note:: File-backed locks clear released and expired lock contents, but may - leave empty lock files in the cache directory. These files do not represent - active locks and may be removed by normal cache cleanup when no +The Locks library uses the Cache service. The cache handler must support owner-aware lock operations. The built-in +**File**, **Redis**, and **Predis** cache handlers support atomic lock operations. The **Memcached** cache handler also +supports locks, with Memcached-specific limitations described below. + +.. note:: Locks are most useful when all competing processes share the same cache storage. The File handler is suitable + for a single server. For multiple application servers, use a shared handler such as Redis. + +.. important:: Locks are stored in the configured cache handler. Clearing or flushing that cache storage, for example + with ``cache()->clean()`` or a Redis ``FLUSHDB``, may remove active locks. Avoid clearing shared lock storage while + lock-protected work is running, or use a dedicated cache store for locks when that separation is important. + +.. note:: Memcached lock support requires the ``memcached`` PHP extension. The older ``memcache`` extension does not + provide the CAS operations used for owner-aware refresh and best-effort release. Memcached does not provide an + atomic compare-and-delete command, so releasing a Memcached lock cannot provide the same owner-checked atomic + release guarantee as Redis or Predis. A delayed release may delete a lock that expired and was acquired by another + owner. Memcached locks may also be lost if the Memcached server restarts, evicts keys, or flushes its cache. + +.. note:: File-backed locks clear released and expired lock contents, but may leave empty lock files in the cache + directory. These files do not represent active locks and may be removed by normal cache cleanup when no lock-protected work is running. ************* Example Usage ************* -You can create a lock through the ``locks`` service. The second argument is the -lock TTL, in seconds. The TTL prevents abandoned locks from being held forever -if a process exits unexpectedly. +You can create a lock through the ``locks`` service. The second argument is the lock TTL, in seconds. The TTL prevents +abandoned locks from being held forever if a process exits unexpectedly. .. literalinclude:: locks/001.php -.. warning:: If the work takes longer than the lock TTL, another process may - acquire the same lock while the first process is still running. For - long-running work, choose a TTL that comfortably covers the operation, call - ``refresh()`` while the lock is held, or check ``isAcquired()`` before - performing irreversible side effects. +.. warning:: If the work takes longer than the lock TTL, another process may acquire the same lock while the first + process is still running. For long-running work, choose a TTL that comfortably covers the operation, call + ``refresh()`` while the lock is held, or check ``isAcquired()`` before performing irreversible side effects. Running a Callback ================== -The ``run()`` method acquires the lock, runs the callback, and releases the lock -in a ``finally`` block. +The ``run()`` method acquires the lock, runs the callback, and releases the lock in a ``finally`` block. .. literalinclude:: locks/002.php -If the lock cannot be acquired, ``run()`` returns ``false`` and the callback is -not called. +If the lock cannot be acquired, ``run()`` returns ``false`` and the callback is not called. Blocking ======== -The ``block()`` method waits up to the given number of seconds for the lock to -become available: +The ``block()`` method waits up to the given number of seconds for the lock to become available: .. literalinclude:: locks/003.php Restoring a Lock by Owner ========================= -Each acquired lock has an owner token. You may pass this token to another -process and restore the lock there, for example to release a lock from a queued -worker that continues work started by the current request. +Each acquired lock has an owner token. You may pass this token to another process and restore the lock there, for +example to release a lock from a queued worker that continues work started by the current request. .. literalinclude:: locks/004.php @@ -96,25 +80,19 @@ worker that continues work started by the current request. Locks and Cache Handlers ************************ -The default File cache handler supports locks, so locks work without additional -configuration in a standard application. - -If the configured cache handler does not support locks, resolving the ``locks`` -service or constructing a lock manager throws a -``CodeIgniter\Lock\Exceptions\LockException``. - -Custom cache handlers can support locks by implementing -``CodeIgniter\Cache\LockStoreProviderInterface`` and returning a -``CodeIgniter\Cache\LockStoreInterface`` instance. This keeps lock support -opt-in and does not require all cache handlers to implement lock operations. - -Custom lock stores must implement owner-aware acquisition, release, refresh, -force release, and owner lookup methods. ``acquireLock()`` should atomically -claim the lock for an owner token and TTL. ``releaseLock()`` and -``refreshLock()`` must only affect the lock when the supplied owner token still -matches the current owner. ``forceReleaseLock()`` intentionally ignores -ownership, and ``getLockOwner()`` should return ``null`` when the lock is absent -or expired. +The default File cache handler supports locks, so locks work without additional configuration in a standard application. + +If the configured cache handler does not support locks, resolving the ``locks`` service or constructing a lock manager +throws a ``CodeIgniter\Lock\Exceptions\LockException``. + +Custom cache handlers can support locks by implementing ``CodeIgniter\Cache\LockStoreProviderInterface`` and returning a +``CodeIgniter\Cache\LockStoreInterface`` instance. This keeps lock support opt-in and does not require all cache +handlers to implement lock operations. + +Custom lock stores must implement owner-aware acquisition, release, refresh, force release, and owner lookup methods. +``acquireLock()`` should atomically claim the lock for an owner token and TTL. ``releaseLock()`` and ``refreshLock()`` +must only affect the lock when the supplied owner token still matches the current owner. ``forceReleaseLock()`` +intentionally ignores ownership, and ``getLockOwner()`` should return ``null`` when the lock is absent or expired. *************** Class Reference diff --git a/user_guide_src/source/libraries/security.rst b/user_guide_src/source/libraries/security.rst index fecc1cd0a0fd..deafa82d303a 100644 --- a/user_guide_src/source/libraries/security.rst +++ b/user_guide_src/source/libraries/security.rst @@ -96,40 +96,38 @@ Fetch Metadata CSRF Protection .. versionadded:: 4.8.0 -CodeIgniter can use Fetch Metadata request headers as a first-line CSRF check for unsafe browser requests. -Fetch Metadata is a browser-supplied signal that helps the framework allow same-origin requests and reject -cross-site requests before checking the CSRF token. +CodeIgniter can use Fetch Metadata request headers as a first-line CSRF check for unsafe browser requests. Fetch +Metadata is a browser-supplied signal that helps the framework allow same-origin requests and reject cross-site requests +before checking the CSRF token. -When CSRF protection is enabled, new applications use Fetch Metadata first by default. You can configure -this behavior in **app/Config/Security.php**: +When CSRF protection is enabled, new applications use Fetch Metadata first by default. You can configure this behavior +in **app/Config/Security.php**: .. literalinclude:: security/011.php -When it is enabled, requests using unsafe HTTP methods with ``Sec-Fetch-Site: same-origin`` are allowed -without a token. +When it is enabled, requests using unsafe HTTP methods with ``Sec-Fetch-Site: same-origin`` are allowed without a token. Requests with ``Sec-Fetch-Site: cross-site`` are rejected. Requests with a missing ``Sec-Fetch-Site`` header, -``Sec-Fetch-Site: none``, or an unknown value fall back to token verification. This keeps protection working -for browsers or clients that do not send Fetch Metadata headers. Requests with ``Sec-Fetch-Site: same-site`` -also fall back to token verification by default. +``Sec-Fetch-Site: none``, or an unknown value fall back to token verification. This keeps protection working for +browsers or clients that do not send Fetch Metadata headers. Requests with ``Sec-Fetch-Site: same-site`` also fall back +to token verification by default. -Upgraded applications without this config value continue to use token verification. You may also disable -Fetch Metadata protection by setting ``$csrfFetchMetadata`` to ``false``. +Upgraded applications without this config value continue to use token verification. You may also disable Fetch Metadata +protection by setting ``$csrfFetchMetadata`` to ``false``. -When an unsafe request passes with Fetch Metadata, the CSRF token is not regenerated. Token regeneration only -runs when token verification is used. +When an unsafe request passes with Fetch Metadata, the CSRF token is not regenerated. Token regeneration only runs when +token verification is used. -.. warning:: Fetch Metadata protects browser-based requests. Browsers only send these headers for - potentially trustworthy URLs, and non-browser clients can send their own headers. It is not API - authentication. +.. warning:: Fetch Metadata protects browser-based requests. Browsers only send these headers for potentially + trustworthy URLs, and non-browser clients can send their own headers. It is not API authentication. -.. warning:: Same-site is not the same as same-origin, because sibling subdomains are considered same-site. - Same-site requests fall back to token verification by default. If sibling subdomains are not trusted, - you may reject same-site requests before token verification in **app/Config/Security.php**: +.. warning:: Same-site is not the same as same-origin, because sibling subdomains are considered same-site. Same-site + requests fall back to token verification by default. If sibling subdomains are not trusted, you may reject same-site + requests before token verification in **app/Config/Security.php**: .. literalinclude:: security/012.php -If your application receives legitimate cross-origin POST requests, such as webhooks, callbacks, or -OAuth/SAML responses, use :ref:`CSRF route exclusions ` and verify those routes another way. +If your application receives legitimate cross-origin POST requests, such as webhooks, callbacks, or OAuth/SAML +responses, use :ref:`CSRF route exclusions ` and verify those routes another way. Token Randomization ------------------- diff --git a/user_guide_src/source/libraries/time.rst b/user_guide_src/source/libraries/time.rst index 0293f0921085..925671975e62 100644 --- a/user_guide_src/source/libraries/time.rst +++ b/user_guide_src/source/libraries/time.rst @@ -400,8 +400,8 @@ between() .. versionadded:: 4.8.0 -Determines if the current instance is between two other times. By default, the start and end bounds are included. -Pass ``false`` as the third ``$inclusive`` argument to exclude both bounds: +Determines if the current instance is between two other times. By default, the start and end bounds are included. Pass +``false`` as the third ``$inclusive`` argument to exclude both bounds: .. literalinclude:: time/045.php @@ -420,9 +420,9 @@ Returns the earlier of the current instance and the provided time: .. literalinclude:: time/046.php -If ``null`` is provided, it compares against ``Time::now()`` in the current instance's timezone. String values may use -a timezone string as the second parameter. Otherwise, they are parsed using the current instance's timezone. If both -times are equal, the current instance is returned. +If ``null`` is provided, it compares against ``Time::now()`` in the current instance's timezone. String values may use a +timezone string as the second parameter. Otherwise, they are parsed using the current instance's timezone. If both times +are equal, the current instance is returned. .. _time-comparing-two-times-max: @@ -435,9 +435,9 @@ Returns the later of the current instance and the provided time: .. literalinclude:: time/047.php -If ``null`` is provided, it compares against ``Time::now()`` in the current instance's timezone. String values may use -a timezone string as the second parameter. Otherwise, they are parsed using the current instance's timezone. If both -times are equal, the current instance is returned. +If ``null`` is provided, it compares against ``Time::now()`` in the current instance's timezone. String values may use a +timezone string as the second parameter. Otherwise, they are parsed using the current instance's timezone. If both times +are equal, the current instance is returned. .. _time-comparing-two-times-isPast: diff --git a/user_guide_src/source/libraries/uri.rst b/user_guide_src/source/libraries/uri.rst index a8ff61694cad..9496ab8b93dd 100644 --- a/user_guide_src/source/libraries/uri.rst +++ b/user_guide_src/source/libraries/uri.rst @@ -209,22 +209,21 @@ The immutable query methods provide alternatives to the older mutable methods: | ``keepQuery()`` | ``withOnlyQueryVars()`` | +-----------------------+--------------------------------------------+ -The ``with*()`` methods return a cloned URI instance and do not modify the original URI. -The older mutable methods change the current URI instance. +The ``with*()`` methods return a cloned URI instance and do not modify the original URI. The older mutable methods +change the current URI instance. -You can return a new URI instance with its query variables replaced by using the -``withQuery()`` and ``withQueryArray()`` methods: +You can return a new URI instance with its query variables replaced by using the ``withQuery()`` and +``withQueryArray()`` methods: .. literalinclude:: uri/029.php -You can return a new URI instance with one or more query variables added or replaced by using the -``withQueryVar()`` and ``withQueryVars()`` methods. Existing query variables are preserved unless they -are replaced: +You can return a new URI instance with one or more query variables added or replaced by using the ``withQueryVar()`` and +``withQueryVars()`` methods. Existing query variables are preserved unless they are replaced: .. literalinclude:: uri/028.php -You can return a new URI instance with query variables removed or filtered by using the -``withoutQueryVars()`` and ``withOnlyQueryVars()`` methods: +You can return a new URI instance with query variables removed or filtered by using the ``withoutQueryVars()`` and +``withOnlyQueryVars()`` methods: .. literalinclude:: uri/030.php @@ -296,7 +295,7 @@ By default, some methods of this class may throw an exception. If you want to di that will prevent throwing exceptions. .. deprecated:: 4.4.0 - This method is deprecated and will be removed in a future version. - It is recommended to handle exceptions properly instead of disabling them. + This method is deprecated and will be removed in a future version. It is recommended to handle exceptions properly + instead of disabling them. .. literalinclude:: uri/027.php diff --git a/user_guide_src/source/libraries/validation.rst b/user_guide_src/source/libraries/validation.rst index 276395562561..cf1ec742eb62 100644 --- a/user_guide_src/source/libraries/validation.rst +++ b/user_guide_src/source/libraries/validation.rst @@ -483,69 +483,58 @@ the validation rules. Typed Validated Input --------------------- -``getValidatedInput()`` returns the same validated data as a -``CodeIgniter\Input\ValidatedInput`` object. Use it after validation -succeeds when you want to read common controller values as strings, integers, -floats, booleans, arrays, dates, or enums: +``getValidatedInput()`` returns the same validated data as a ``CodeIgniter\Input\ValidatedInput`` object. Use it after +validation succeeds when you want to read common controller values as strings, integers, floats, booleans, arrays, +dates, or enums: .. versionadded:: 4.8.0 .. literalinclude:: validation/048.php :lines: 2- -Validation decides whether input is acceptable. The typed input object only -helps you consume values that already passed validation. If a present value -cannot be read as the requested type, an ``InvalidArgumentException`` is thrown. -This usually means the validation rules and the expected type do not match. +Validation decides whether input is acceptable. The typed input object only helps you consume values that already passed +validation. If a present value cannot be read as the requested type, an ``InvalidArgumentException`` is thrown. This +usually means the validation rules and the expected type do not match. -``ValidatedInput`` extends ``CodeIgniter\Input\InputData``. This keeps generic -typed input access reusable while adding validation-specific readers for dates -and enums. ``InputData`` is fallback-friendly for raw input; ``ValidatedInput`` -is strict because it represents data that has already passed validation. +``ValidatedInput`` extends ``CodeIgniter\Input\InputData``. This keeps generic typed input access reusable while adding +validation-specific readers for dates and enums. ``InputData`` is fallback-friendly for raw input; ``ValidatedInput`` is +strict because it represents data that has already passed validation. The typed input object has the following methods: All methods support dot-array syntax for nested validated data. -* ``get($key, $default = null)`` returns the raw validated value. If the field - is missing, it returns the default value. -* ``has($key)`` returns whether the field exists in the validated data, even if - its value is ``null``. -* ``string($key, $default = null)`` returns ``string|null``. If the field is - missing, it returns the default value or ``null``. -* ``integer($key, $default = null)`` returns ``int|null``. If the field is - missing, it returns the default value or ``null``. -* ``float($key, $default = null)`` returns ``float|null``. If the field is - missing, it returns the default value or ``null``. -* ``boolean($key, $default = null)`` returns ``bool|null``. If the field is - missing, it returns the default value or ``null``. -* ``array($key, $default = null)`` returns ``array|null``. If the field is - missing, it returns the default value or ``null``. -* ``date($key, $format = null, $timezone = null, $default = null)`` returns - :php:class:`CodeIgniter\\I18n\\Time` or ``null``. If the field is missing, it - returns the default value or ``null``. Pass a format when the value should be - parsed with a specific date format. The default value must be ``null`` or a - :php:class:`CodeIgniter\\I18n\\Time` instance. -* ``enum($key, $enumClass, $default = null)`` returns an enum instance or - ``null``. The default value must be ``null`` or an instance of the requested - enum class. - -Fields that are present with a ``null`` value return ``null``. This lets you -distinguish a missing optional field from a field that was validated as -``null``. - -Use validation rules such as ``integer``, ``decimal``, ``valid_date``, -``in_list``, or a custom rule to ensure the value matches the type you plan to read. The -``date()`` method only parses the value; validation rules should enforce -acceptable date formats and ranges. For strict calendar validation, add a rule -such as ``valid_date[Y-m-d]``. - -The ``enum()`` method accepts PHP enum class names. Backed enums are matched by -their backing value, while unit enums are matched by case name. - -The ``boolean()`` method uses PHP's boolean validation behavior, so common form -values like ``"1"``, ``"0"``, ``"true"``, ``"false"``, ``"yes"``, ``"no"``, -``"on"``, and ``"off"`` are accepted. +* ``get($key, $default = null)`` returns the raw validated value. If the field is missing, it returns the default value. +* ``has($key)`` returns whether the field exists in the validated data, even if its value is ``null``. +* ``string($key, $default = null)`` returns ``string|null``. If the field is missing, it returns the default value or + ``null``. +* ``integer($key, $default = null)`` returns ``int|null``. If the field is missing, it returns the default value or + ``null``. +* ``float($key, $default = null)`` returns ``float|null``. If the field is missing, it returns the default value or + ``null``. +* ``boolean($key, $default = null)`` returns ``bool|null``. If the field is missing, it returns the default value or + ``null``. +* ``array($key, $default = null)`` returns ``array|null``. If the field is missing, it returns the default value or + ``null``. +* ``date($key, $format = null, $timezone = null, $default = null)`` returns :php:class:`CodeIgniter\\I18n\\Time` or + ``null``. If the field is missing, it returns the default value or ``null``. Pass a format when the value should be + parsed with a specific date format. The default value must be ``null`` or a :php:class:`CodeIgniter\\I18n\\Time` + instance. +* ``enum($key, $enumClass, $default = null)`` returns an enum instance or ``null``. The default value must be ``null`` + or an instance of the requested enum class. + +Fields that are present with a ``null`` value return ``null``. This lets you distinguish a missing optional field from a +field that was validated as ``null``. + +Use validation rules such as ``integer``, ``decimal``, ``valid_date``, ``in_list``, or a custom rule to ensure the value +matches the type you plan to read. The ``date()`` method only parses the value; validation rules should enforce +acceptable date formats and ranges. For strict calendar validation, add a rule such as ``valid_date[Y-m-d]``. + +The ``enum()`` method accepts PHP enum class names. Backed enums are matched by their backing value, while unit enums +are matched by case name. + +The ``boolean()`` method uses PHP's boolean validation behavior, so common form values like ``"1"``, ``"0"``, +``"true"``, ``"false"``, ``"yes"``, ``"no"``, ``"on"``, and ``"off"`` are accepted. .. _saving-validation-rules-to-config-file: @@ -839,9 +828,8 @@ Form Requests .. versionadded:: 4.8.0 -For a higher-level approach that encapsulates validation rules, custom error -messages, and authorization logic in a dedicated class, see -:doc:`Form Requests `. +For a higher-level approach that encapsulates validation rules, custom error messages, and authorization logic in a +dedicated class, see :doc:`Form Requests `. ********************* Creating Custom Rules @@ -883,9 +871,9 @@ fourth) parameter: .. literalinclude:: validation/035.php -.. note:: Since v4.8.0, the ``{field}``, ``{param}``, and ``{value}`` placeholders are supported in ``$error`` - messages and will be replaced with the field's human-readable label (or field name if no label is set), - the rule parameter, and the submitted value respectively. +.. note:: Since v4.8.0, the ``{field}``, ``{param}``, and ``{value}`` placeholders are supported in ``$error`` messages + and will be replaced with the field's human-readable label (or field name if no label is set), the rule parameter, + and the submitted value respectively. Using a Custom Rule ------------------- @@ -939,9 +927,9 @@ Or you can use the following parameters: .. literalinclude:: validation/041.php :lines: 2- -.. note:: Since v4.8.0, the ``{field}``, ``{param}``, and ``{value}`` placeholders are supported in ``$error`` - messages and will be replaced with the field's human-readable label (or field name if no label is set), - the rule parameter, and the submitted value respectively. +.. note:: Since v4.8.0, the ``{field}``, ``{param}``, and ``{value}`` placeholders are supported in ``$error`` messages + and will be replaced with the field's human-readable label (or field name if no label is set), the rule parameter, + and the submitted value respectively. .. _validation-using-callable-rule: @@ -966,9 +954,9 @@ Or you can use the following parameters: .. literalinclude:: validation/047.php :lines: 2- -.. note:: Since v4.8.0, the ``{field}``, ``{param}``, and ``{value}`` placeholders are supported in ``$error`` - messages and will be replaced with the field's human-readable label (or field name if no label is set), - the rule parameter, and the submitted value respectively. +.. note:: Since v4.8.0, the ``{field}``, ``{param}``, and ``{value}`` placeholders are supported in ``$error`` messages + and will be replaced with the field's human-readable label (or field name if no label is set), the rule parameter, + and the submitted value respectively. .. _validation-available-rules: diff --git a/user_guide_src/source/models/entities.rst b/user_guide_src/source/models/entities.rst index 138b21ed6e1a..5c978880dc9a 100644 --- a/user_guide_src/source/models/entities.rst +++ b/user_guide_src/source/models/entities.rst @@ -262,7 +262,8 @@ Add a question mark at the beginning of type to mark property as nullable, i.e., .. note:: **int-bool** can be used since v4.3.0. .. note:: **enum** can be used since v4.7.0. .. note:: **encrypted**, **encrypted-json** and **encrypted-json-array** can be used since v4.8.0. -.. note:: Since v4.8.0, you can also pass parameters to **float** and **double** types to specify the number of decimal places and rounding mode, i.e., **float[2,even]**. +.. note:: Since v4.8.0, you can also pass parameters to **float** and **double** types to specify the number of decimal + places and rounding mode, e.g., **float[2,even]**. For example, if you had a User entity with an ``is_banned`` property, you can cast it as a boolean: @@ -344,45 +345,39 @@ Encrypted Casting .. versionadded:: 4.8.0 -Encrypted casting encrypts string values when they are set and decrypts them -when they are read. It uses the :doc:`Encryption ` -service, so you must configure an encryption key before using it. The -configured key is required for both writing new values and reading stored -values back. The ``encrypted`` type accepts string values. Use ``?encrypted`` -for nullable values. +Encrypted casting encrypts string values when they are set and decrypts them when they are read. It uses the +:doc:`Encryption ` service, so you must configure an encryption key before using it. The +configured key is required for both writing new values and reading stored values back. The ``encrypted`` type accepts +string values. Use ``?encrypted`` for nullable values. .. literalinclude:: entities/029.php -The ``encrypted-json`` and ``encrypted-json-array`` types JSON encode values -before encryption and JSON decode them after decryption. The ``encrypted-json`` -type returns ``stdClass`` values, while ``encrypted-json-array`` returns arrays. -Use ``?encrypted-json`` and ``?encrypted-json-array`` for nullable values. These -values are still stored as encrypted text, not as queryable database JSON. +The ``encrypted-json`` and ``encrypted-json-array`` types encode values as JSON before encryption and decode them after +decryption. The ``encrypted-json`` type returns ``stdClass`` values, while ``encrypted-json-array`` returns arrays. Use +``?encrypted-json`` and ``?encrypted-json-array`` for nullable values. These values are still stored as encrypted text, +not as queryable database JSON. -Encrypted values are stored as Base64-encoded ciphertext. Use a ``TEXT`` column -or a sufficiently large string column because the stored value is longer than -the plain text value. Avoid narrow columns like ``VARCHAR(255)`` unless you have +Encrypted values are stored as Base64-encoded ciphertext. Use a ``TEXT`` column or a sufficiently large string column +because the stored value is longer than the plain text value. Avoid narrow columns like ``VARCHAR(255)`` unless you have verified the maximum encrypted length for the values you will store. -.. warning:: Encrypted values cannot be searched, sorted, filtered, or checked - for uniqueness by their plain text value in the database. +.. warning:: Encrypted values cannot be searched, sorted, filtered, or checked for uniqueness by their plain text value + in the database. -.. warning:: Do not use encrypted casting for passwords. Passwords should be - hashed with PHP's password hashing functions. +.. warning:: Do not use encrypted casting for passwords. Passwords should be hashed with PHP's password hashing + functions. -.. note:: ``toArray()`` and JSON serialization return decrypted values. Use - ``toRawArray()`` when you need the encrypted value that will be stored. +.. note:: ``toArray()`` and JSON serialization return decrypted values. Use ``toRawArray()`` when you need the encrypted + value that will be stored. -.. note:: If the stored value cannot be decrypted, an ``EncryptionException`` is - thrown. +.. note:: If the stored value cannot be decrypted, an ``EncryptionException`` is thrown. -.. note:: Encrypted casts follow the Encryption service's key rotation behavior. - Values encrypted with a previous key can be decrypted when that key is - configured in ``previousKeys``. Save the value again to re-encrypt it with - the current key. See :ref:`spark-key-rotate` for rotating keys. +.. note:: Encrypted casts follow the Encryption service's key rotation behavior. Values encrypted with a previous key + can be decrypted when that key is configured in ``previousKeys``. Save the value again to re-encrypt it with the + current key. See :ref:`spark-key-rotate` for rotating keys. -.. note:: Encryption is non-deterministic. Setting the same plain text value can - produce a different encrypted value and mark the Entity attribute as changed. +.. note:: Encryption is non-deterministic. Setting the same plain text value can produce a different encrypted value and + mark the Entity attribute as changed. Custom Casting -------------- diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index e7e8086eafcb..246ae112439a 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -170,26 +170,21 @@ $insertOnlyFields .. versionadded:: 4.8.0 -This array may contain fields that can be set during ``insert()`` and -``insertBatch()`` calls, but should be removed from ``update()``, -``updateBatch()``, and update-side ``save()`` calls. +This array may contain fields that can be set during ``insert()`` and ``insertBatch()`` calls, but should be removed +from ``update()``, ``updateBatch()``, and update-side ``save()`` calls. -This is useful for values that should be created once and then left unchanged -through normal Model writes, such as public IDs, external references, or -generated slugs. +This is useful for values that should be created once and then left unchanged through normal Model writes, such as +public IDs, external references, or generated slugs. .. literalinclude:: model/069.php -Fields listed here must also be listed in `$allowedFields`_ when field -protection is enabled. This is Model-level protection only. It does not create a -database constraint, does not inspect previous database values, and does not -intercept ``replace()`` or direct Query Builder writes. Calling -``protect(false)`` disables this protection. +Fields listed here must also be listed in `$allowedFields`_ when field protection is enabled. This is Model-level +protection only. It does not create a database constraint, does not inspect previous database values, and does not +intercept ``replace()`` or direct Query Builder writes. Calling ``protect(false)`` disables this protection. -By default, insert-only fields are discarded from update data, the same way -fields outside `$allowedFields`_ are discarded. When `$throwOnDisallowedFields`_ -is enabled, submitting an insert-only field during an update operation throws a -``DataException``. +By default, insert-only fields are discarded from update data, the same way fields outside `$allowedFields`_ are +discarded. When `$throwOnDisallowedFields`_ is enabled, submitting an insert-only field during an update operation +throws a ``DataException``. You may also change this setting with the ``setInsertOnlyFields()`` method. @@ -200,13 +195,11 @@ $throwOnDisallowedFields .. versionadded:: 4.8.0 -When ``true``, the model throws a ``DataException`` instead of discarding fields -that are not listed in `$allowedFields`_ during ``insert()``, ``insertBatch()``, -``update()``, ``updateBatch()``, and ``save()`` calls. +When ``true``, the model throws a ``DataException`` instead of discarding fields that are not listed in +`$allowedFields`_ during ``insert()``, ``insertBatch()``, ``update()``, ``updateBatch()``, and ``save()`` calls. -This is useful when you want to catch typos, stale form fields, or unexpected -write payloads during development or in strict application code. It does not -replace validation and does not inspect the database schema. +This is useful when you want to catch typos, stale form fields, or unexpected write payloads during development or in +strict application code. It does not replace validation and does not inspect the database schema. You may also change this setting with the ``throwOnDisallowedFields()`` method. @@ -436,16 +429,16 @@ of type to mark the field as nullable, i.e., ``?int``, ``?datetime``. float ----- -Casting as ``float`` will convert the value to a float type in PHP. -This is best used with database columns that are of a float or numeric type. +Casting as ``float`` will convert the value to a float type in PHP. This is best used with database columns that are of +a float or numeric type. -You can also pass arguments to the ``float`` type to specify the number -of decimal places to round to as well as the rounding mode (up, down, even or odd). +You can also pass arguments to the ``float`` type to specify the number of decimal places to round to as well as the +rounding mode (up, down, even, or odd). .. literalinclude:: model/067.php -.. note:: Prior to v4.8.0 the ``float`` type did not support any parameters. - It simply converted the value to a float type in PHP without rounding. +.. note:: Prior to v4.8.0 the ``float`` type did not support any parameters. It simply converted the value to a float + type in PHP without rounding. csv --- @@ -502,44 +495,37 @@ encrypted .. versionadded:: 4.8.0 -Casting as ``encrypted`` encrypts string values before they are stored and -decrypts them when they are retrieved. It uses the -:doc:`Encryption ` service, so you must configure an -encryption key before using it. The configured key is required for both writing -new values and reading stored values back. The ``encrypted`` type accepts string -values. Use ``?encrypted`` for nullable values. +Casting as ``encrypted`` encrypts string values before they are stored and decrypts them when they are retrieved. It +uses the :doc:`Encryption ` service, so you must configure an encryption key before using it. The +configured key is required for both writing new values and reading stored values back. The ``encrypted`` type accepts +string values. Use ``?encrypted`` for nullable values. .. literalinclude:: model/070.php -The ``encrypted-json`` and ``encrypted-json-array`` types JSON encode values -before encryption and JSON decode them after decryption. The ``encrypted-json`` -type returns ``stdClass`` values, while ``encrypted-json-array`` returns arrays. -Use ``?encrypted-json`` and ``?encrypted-json-array`` for nullable values. These -values are still stored as encrypted text, not as queryable database JSON. +The ``encrypted-json`` and ``encrypted-json-array`` types encode values as JSON before encryption and decode them after +decryption. The ``encrypted-json`` type returns ``stdClass`` values, while ``encrypted-json-array`` returns arrays. Use +``?encrypted-json`` and ``?encrypted-json-array`` for nullable values. These values are still stored as encrypted text, +not as queryable database JSON. -Encrypted values are stored as Base64-encoded ciphertext. Use a ``TEXT`` column -or a sufficiently large string column because the stored value is longer than -the plain text value. Avoid narrow columns like ``VARCHAR(255)`` unless you have +Encrypted values are stored as Base64-encoded ciphertext. Use a ``TEXT`` column or a sufficiently large string column +because the stored value is longer than the plain text value. Avoid narrow columns like ``VARCHAR(255)`` unless you have verified the maximum encrypted length for the values you will store. -.. warning:: Encrypted values cannot be searched, sorted, filtered, or checked - for uniqueness by their plain text value in the database. +.. warning:: Encrypted values cannot be searched, sorted, filtered, or checked for uniqueness by their plain text value + in the database. -.. warning:: Do not use encrypted casting for passwords. Passwords should be - hashed with PHP's password hashing functions. +.. warning:: Do not use encrypted casting for passwords. Passwords should be hashed with PHP's password hashing + functions. -.. note:: Model validation and write callbacks receive the encrypted value - because Model Field Casting converts values before they are stored. Validate - the plain text value before passing it to the Model when validation must - inspect the plain text. +.. note:: Model validation and write callbacks receive the encrypted value because Model Field Casting converts values + before they are stored. Validate the plain text value before passing it to the Model when validation must inspect + the plain text. -.. note:: If the stored value cannot be decrypted, an ``EncryptionException`` is - thrown. +.. note:: If the stored value cannot be decrypted, an ``EncryptionException`` is thrown. -.. note:: If you rotate encryption keys, values encrypted with a previous key can - be decrypted when that key is configured in ``previousKeys``. Save the value - again to re-encrypt it with the current key. See :ref:`spark-key-rotate` for - rotating keys. +.. note:: If you rotate encryption keys, values encrypted with a previous key can be decrypted when that key is + configured in ``previousKeys``. Save the value again to re-encrypt it with the current key. See + :ref:`spark-key-rotate` for rotating keys. Custom Casting ============== @@ -789,29 +775,24 @@ firstOrInsert() .. versionadded:: 4.8.0 -Finds the first row matching the given ``$attributes``, or inserts a new row -combining ``$attributes`` and ``$values`` when no match is found. +Finds the first row matching the given ``$attributes``, or inserts a new row combining ``$attributes`` and ``$values`` +when no match is found. -Both parameters accept an array, a ``stdClass`` object, or an -:doc:`Entity `: +Both parameters accept an array, a ``stdClass`` object, or an :doc:`Entity `: .. literalinclude:: model/065.php -``$attributes`` is used as the WHERE condition for the lookup. If no record is -found, a new row is inserted using the merged result of ``$attributes`` and -``$values``. The ``$values`` data is only applied during insertion and is -ignored when a matching record already exists. +``$attributes`` is used as the WHERE condition for the lookup. If no record is found, a new row is inserted using the +merged result of ``$attributes`` and ``$values``. The ``$values`` data is only applied during insertion and is ignored +when a matching record already exists. .. literalinclude:: model/066.php -The method returns the found or newly inserted row in the format defined by -`$returnType`_, or ``false`` on failure (e.g., validation error or database -error when ``DBDebug`` is ``false``). +The method returns the found or newly inserted row in the format defined by `$returnType`_, or ``false`` on failure +(e.g., validation error or database error when ``DBDebug`` is ``false``). -.. note:: A database **unique constraint** on the lookup column(s) is required - for the method to be race-safe. Without it, two concurrent requests could - both pass the initial lookup and attempt to insert, resulting in duplicate - rows. +.. note:: A database **unique constraint** on the lookup column(s) is required for the method to be race-safe. Without + it, two concurrent requests could both pass the initial lookup and attempt to insert, resulting in duplicate rows. When a unique constraint is present, a concurrent insert is detected via :php:class:`UniqueConstraintViolationException ` @@ -824,34 +805,28 @@ withLockedRow() .. versionadded:: 4.8.0 -Reloads one row by primary key inside a transaction using -:ref:`query-builder-lock-for-update`, then runs the callback with the locked row -and the current Model instance: +Reloads one row by primary key inside a transaction using :ref:`query-builder-lock-for-update`, then runs the callback +with the locked row and the current Model instance: .. literalinclude:: model/073.php -The method returns the callback return value. If the row is not found, the -callback is not run and ``null`` is returned. If the transaction cannot begin or -the transaction status fails without an exception, ``false`` is returned. +The method returns the callback return value. If the row is not found, the callback is not run and ``null`` is returned. +If the transaction cannot begin or the transaction status fails without an exception, ``false`` is returned. -Existing Model query constraints are applied to the locked lookup, so methods -like ``where()`` and ``withDeleted()`` can be called before ``withLockedRow()``. +Existing Model query constraints are applied to the locked lookup, so methods like ``where()`` and ``withDeleted()`` can +be called before ``withLockedRow()``. -Returning ``false`` from the callback does not trigger a rollback. To explicitly -roll back from the callback, throw an exception. Any exception thrown by the -callback rolls back the transaction and is rethrown. Unsupported lock +Returning ``false`` from the callback does not trigger a rollback. To explicitly roll back from the callback, throw an +exception. Any exception thrown by the callback rolls back the transaction and is rethrown. Unsupported lock combinations throw the same database exceptions as ``lockForUpdate()``. -.. important:: The locked row is reloaded directly from the database, so Model - find callbacks are not run for that lookup. Model methods called inside the - callback, such as ``save()`` or ``update()``, still follow the Model's +.. important:: The locked row is reloaded directly from the database, so Model find callbacks are not run for that + lookup. Model methods called inside the callback, such as ``save()`` or ``update()``, still follow the Model's callback setting. -.. note:: Row locks are only useful when the database supports transactions and - pessimistic locks. See :ref:`query-builder-lock-for-update` for driver - support and limitations. If database transactions are disabled, this method - follows the behavior of ``transaction()`` and runs the callback without - starting a transaction. +.. note:: Row locks are only useful when the database supports transactions and pessimistic locks. See + :ref:`query-builder-lock-for-update` for driver support and limitations. If database transactions are disabled, this + method follows the behavior of ``transaction()`` and runs the callback without starting a transaction. .. _model-saving-dates: @@ -1091,17 +1066,15 @@ For ``replace()``, a supplied `$primaryKey`_ is retained to identify the record .. literalinclude:: model/041.php -If you prefer disallowed fields to raise an exception instead of being silently -removed, enable throwing on disallowed fields: +If you prefer disallowed fields to raise an exception instead of being silently removed, enable throwing on disallowed +fields: .. literalinclude:: model/068.php -When throwing on disallowed fields is enabled, operation fields such as the -primary key passed to ``update()`` or the index passed to ``updateBatch()`` may -still be used to locate rows. +When throwing on disallowed fields is enabled, operation fields such as the primary key passed to ``update()`` or the +index passed to ``updateBatch()`` may still be used to locate rows. -If some allowed fields should only be set during insert operations, list them -in `$insertOnlyFields`_: +If some allowed fields should only be set during insert operations, list them in `$insertOnlyFields`_: .. literalinclude:: model/069.php @@ -1141,7 +1114,8 @@ Sometimes, you need to process large amounts of data and would run the risk of r This is best used during cronjobs, data exports, or other large tasks. To make this simpler, you can process the data in smaller, manageable pieces using the methods below. -.. note:: The ``chunk()``, ``chunkRows()``, ``chunkById()``, and ``chunkRowsById()`` methods do not trigger Model callbacks. +.. note:: The ``chunk()``, ``chunkRows()``, ``chunkById()``, and ``chunkRowsById()`` methods do not trigger Model + callbacks. chunk() ------- @@ -1157,7 +1131,8 @@ chunkRows() .. versionadded:: 4.8.0 -On the other hand, if you want the entire chunk to be passed to the Closure at once, you can use the ``chunkRows()`` method. +On the other hand, if you want the entire chunk to be passed to the Closure at once, you can use the ``chunkRows()`` +method. .. literalinclude:: model/064.php @@ -1166,9 +1141,9 @@ chunkById() .. versionadded:: 4.8.0 -If you need to update or delete rows while processing them, use ``chunkById()``. -This method retrieves each chunk ordered by the model's primary key instead of using an offset, -so deleting or updating rows already processed will not cause later rows to be skipped. +If you need to update or delete rows while processing them, use ``chunkById()``. This method retrieves each chunk +ordered by the model's primary key instead of using an offset, so deleting or updating rows already processed will not +cause later rows to be skipped. .. literalinclude:: model/071.php @@ -1181,15 +1156,13 @@ If you want the entire ID-based chunk to be passed to the Closure at once, use ` .. literalinclude:: model/072.php -.. warning:: ID-based chunking needs the model to define a primary key, and that primary key must be selected. - If you customize the selected columns, make sure the primary key is included. - When using joins, the model primary key must remain available under its normal field name, - and each primary key value must appear only once in the result set. Otherwise, rows may be skipped. - These methods manage their own primary key ordering and cannot be used with - ``orderBy()``, ``groupBy()``, ``limit()``, ``offset()`` or ``union()``. - You should also avoid changing primary key values while processing chunks. - Since these methods add their own ``WHERE`` condition to each chunk query, - group any ``orWhere()`` conditions that should be evaluated together. +.. warning:: ID-based chunking needs the model to define a primary key, and that primary key must be selected. If you + customize the selected columns, make sure the primary key is included. When using joins, the model primary key must + remain available under its normal field name, and each primary key value must appear only once in the result set. + Otherwise, rows may be skipped. These methods manage their own primary key ordering and cannot be used with + ``orderBy()``, ``groupBy()``, ``limit()``, ``offset()`` or ``union()``. You should also avoid changing primary key + values while processing chunks. Since these methods add their own ``WHERE`` condition to each chunk query, group any + ``orWhere()`` conditions that should be evaluated together. .. _model-events-callbacks: diff --git a/user_guide_src/source/outgoing/api_transformers.rst b/user_guide_src/source/outgoing/api_transformers.rst index 79f0f1c6ea50..ffe7613c8b57 100644 --- a/user_guide_src/source/outgoing/api_transformers.rst +++ b/user_guide_src/source/outgoing/api_transformers.rst @@ -135,18 +135,18 @@ Sparse Fieldsets (Per-Type Filtering) .. versionadded:: 4.8.0 The flat ``fields`` parameter filters the root resource. When a response also embeds -:ref:`related resources `, you can filter each related type -independently using the bracketed *sparse fieldset* form ``fields[]=...``. +:ref:`related resources `, you can filter each related type independently using the bracketed +*sparse fieldset* form ``fields[]=...``. To opt a transformer into this, declare its resource type via the ``$resourceType`` property: .. literalinclude:: api_transformers/024.php -A matching ``fields[]`` fieldset applies whether the transformer is used as the root resource or -as an included resource. The flat ``fields=...`` form still applies to the root transformer only. +A matching ``fields[]`` fieldset applies whether the transformer is used as the root resource or as an included +resource. The flat ``fields=...`` form still applies to the root transformer only. -Each transformer reads the fieldset that matches its own ``$resourceType``. Given the ``UserTransformer`` -from :ref:`Including Related Resources ` (which includes posts), a request: +Each transformer reads the fieldset that matches its own ``$resourceType``. Given the ``UserTransformer`` from +:ref:`Including Related Resources ` (which includes posts), a request: .. code-block:: text @@ -315,10 +315,10 @@ Class Reference .. versionadded:: 4.8.0 - The resource type this transformer represents. Used to resolve per-type sparse fieldsets from - the request (e.g. a type of ``'posts'`` reads ``?fields[posts]=...``). Defaults to ``null``, - in which case only the flat ``?fields=...`` parameter applies, and only to the root transformer. - See `Sparse Fieldsets (Per-Type Filtering)`_. + The resource type this transformer represents. Used to resolve per-type sparse fieldsets from the request (e.g. + a type of ``'posts'`` reads ``?fields[posts]=...``). Defaults to ``null``, in which case only the flat + ``?fields=...`` parameter applies, and only to the root transformer. See + `Sparse Fieldsets (Per-Type Filtering)`_. .. php:method:: __construct(?IncomingRequest $request = null) diff --git a/user_guide_src/source/outgoing/csp.rst b/user_guide_src/source/outgoing/csp.rst index 64d6f9c151e7..b9d10a668032 100644 --- a/user_guide_src/source/outgoing/csp.rst +++ b/user_guide_src/source/outgoing/csp.rst @@ -85,22 +85,20 @@ Enable or Disable CSP at Runtime .. versionadded:: 4.8.0 -You can enable or disable CSP for the current request by using the -``enable()`` and ``disable()`` methods on the CSP instance. +You can enable or disable CSP for the current request by using the ``enable()`` and ``disable()`` methods on the CSP +instance. For example, to enable/disable CSP for a specific response: .. literalinclude:: csp/017.php -The runtime value takes precedence over the ``CSPEnabled`` configuration value -for the current request. +The runtime value takes precedence over the ``CSPEnabled`` configuration value for the current request. -These methods only affect the current request. They do not modify the -application configuration. On subsequent requests, the ``CSPEnabled`` value in -**app/Config/App.php** is used again. +These methods only affect the current request. They do not modify the application configuration. On subsequent requests, +the ``CSPEnabled`` value in **app/Config/App.php** is used again. -This allows applications to keep CSP enabled globally while temporarily -disabling it for specific requests, or enable it only where required. +This allows applications to keep CSP enabled globally while temporarily disabling it for specific requests, or enable it +only where required. Report Only =========== @@ -203,10 +201,11 @@ Control Nonce Generation .. versionadded:: 4.8.0 -By default, both the script and style nonces are generated automatically. If you want to only generate one of them, -you can set ``$enableStyleNonce`` or ``$enableScriptNonce`` to false in **app/Config/ContentSecurityPolicy.php**: +By default, both the script and style nonces are generated automatically. If you want to only generate one of them, you +can set ``$enableStyleNonce`` or ``$enableScriptNonce`` to false in **app/Config/ContentSecurityPolicy.php**: .. literalinclude:: csp/016.php -By setting one of these to false, the corresponding nonce will not be generated, and the placeholder will be replaced with an empty string. -This gives you the flexibility to use nonces for only one type of content if you choose, without affecting the other. +By setting one of these to false, the corresponding nonce will not be generated, and the placeholder will be replaced +with an empty string. This gives you the flexibility to use nonces for only one type of content if you choose, without +affecting the other. diff --git a/user_guide_src/source/outgoing/response.rst b/user_guide_src/source/outgoing/response.rst index f68091a30021..7f2d701054c4 100644 --- a/user_guide_src/source/outgoing/response.rst +++ b/user_guide_src/source/outgoing/response.rst @@ -238,83 +238,69 @@ Streaming Responses .. versionadded:: 4.8.0 -CodeIgniter provides a ``StreamResponse`` class that streams its body to the -client as it is generated, instead of buffering the complete body first. This -is useful for large dataset exports, AI/LLM token streaming, proxying upstream -streams, or any content that is produced while it is being sent. Streaming -keeps memory usage low and improves time-to-first-byte for large responses. +CodeIgniter provides a ``StreamResponse`` class that streams its body to the client as it is generated, instead of +buffering the complete body first. This is useful for large dataset exports, AI/LLM token streaming, proxying upstream +streams, or any content that is produced while it is being sent. Streaming keeps memory usage low and improves +time-to-first-byte for large responses. Use the ``$this->response->stream()`` method with a callback: .. literalinclude:: response/039.php -The callback receives the ``StreamResponse`` instance. ``StreamResponse::write()`` -returns ``false`` once the client has disconnected, so you can stop producing -output early. By default, every ``write()`` flushes output to the client -immediately. When writing many small chunks, pass ``false`` as the second -argument and call ``StreamResponse::flush()`` at intervals instead - the -example above flushes once per batch. +The callback receives the ``StreamResponse`` instance. ``StreamResponse::write()`` returns ``false`` once the client has +disconnected, so you can stop producing output early. By default, every ``write()`` flushes output to the client +immediately. When writing many small chunks, pass ``false`` as the second argument and call ``StreamResponse::flush()`` +at intervals instead - the example above flushes once per batch. -The callback is not limited to ``write()``: you may also write directly to -``php://output`` (for example, with ``fputcsv()`` for CSV exports), calling -``flush()`` and ``StreamResponse::isClientConnected()`` yourself. +The callback is not limited to ``write()``: you may also write directly to ``php://output`` (for example, with +``fputcsv()`` for CSV exports), calling ``flush()`` and ``StreamResponse::isClientConnected()`` yourself. -Alternatively, you may pass an iterable of string chunks (such as a generator), -and each chunk is written and flushed in order: +Alternatively, you may pass an iterable of string chunks (such as a generator), and each chunk is written and flushed in +order: .. literalinclude:: response/040.php Headers and Status Code ----------------------- -Set the content type, status code, and any custom headers **before** returning -the response - anything set inside the callback will be too late. Unless you -have already set it, ``StreamResponse`` applies ``X-Accel-Buffering: no`` to -discourage intermediaries from buffering the stream. PHP's zlib output -compression is turned off automatically, but if your web server or CDN -compresses responses (e.g., nginx ``gzip`` or Apache ``mod_deflate``), configure -it to skip your streaming endpoints - compression can delay chunk delivery. - -.. note:: ``stream()`` and ``eventStream()`` create a new response instance. - Headers, cookies, and status codes already set on the main response are not - copied. Set them on the returned ``StreamResponse`` or ``SSEResponse`` - instance before returning it. - -The response is streamed: output buffering is disabled, the PHP time limit is -removed, and the session is closed to avoid blocking other requests. After -filters still run and may set headers, but they must not rely on the response -body. View rendering and decorators are not applied - stream your output in -the callback. - -If :doc:`Content Security Policy ` is enabled, the -``Content-Security-Policy`` header is sent as usual. However, nonce -placeholders cannot be replaced in a streamed body. When streaming HTML that -needs a nonce, call ``$this->response->getCSP()->getScriptNonce()`` (or -``getStyleNonce()``) **before** returning the response and embed the returned -value in your output yourself. - -``StreamResponse`` does not set a ``Content-Length`` header by default, since -the body length is typically unknown in advance. Without it, clients cannot -display download progress or resume interrupted transfers. If you do know the -total size (for example, when proxying a stream of known length), you may set -``Content-Length`` yourself before returning the response. - -.. note:: To serve an existing file, prefer :ref:`DownloadResponse `, - which sends proper ``Content-Length`` and ``Content-Disposition`` headers. - Use ``StreamResponse`` when the content is generated while it is being sent. +Set the content type, status code, and any custom headers **before** returning the response - anything set inside the +callback will be too late. Unless you have already set it, ``StreamResponse`` applies ``X-Accel-Buffering: no`` to +discourage intermediaries from buffering the stream. PHP's zlib output compression is turned off automatically, but if +your web server or CDN compresses responses (e.g., nginx ``gzip`` or Apache ``mod_deflate``), configure it to skip your +streaming endpoints - compression can delay chunk delivery. + +.. note:: ``stream()`` and ``eventStream()`` create a new response instance. Headers, cookies, and status codes already + set on the main response are not copied. Set them on the returned ``StreamResponse`` or ``SSEResponse`` instance + before returning it. + +The response is streamed: output buffering is disabled, the PHP time limit is removed, and the session is closed to +avoid blocking other requests. After filters still run and may set headers, but they must not rely on the response body. +View rendering and decorators are not applied - stream your output in the callback. + +If :doc:`Content Security Policy ` is enabled, the ``Content-Security-Policy`` header is sent as usual. +However, nonce placeholders cannot be replaced in a streamed body. When streaming HTML that needs a nonce, call +``$this->response->getCSP()->getScriptNonce()`` (or ``getStyleNonce()``) **before** returning the response and embed the +returned value in your output yourself. + +``StreamResponse`` does not set a ``Content-Length`` header by default, since the body length is typically unknown in +advance. Without it, clients cannot display download progress or resume interrupted transfers. If you do know the total +size (for example, when proxying a stream of known length), you may set ``Content-Length`` yourself before returning the +response. + +.. note:: To serve an existing file, prefer :ref:`DownloadResponse `, which sends proper + ``Content-Length`` and ``Content-Disposition`` headers. Use ``StreamResponse`` when the content is generated while + it is being sent. Example: Proxying an Upstream Stream ------------------------------------ -``StreamResponse`` can forward a stream from another source - such as object -storage or an internal API - to the client without buffering it in memory or -writing it to disk: +``StreamResponse`` can forward a stream from another source - such as object storage or an internal API - to the client +without buffering it in memory or writing it to disk: .. literalinclude:: response/041.php -The :ref:`development server ` and -:ref:`production ` considerations described for SSE below apply -to all streaming responses. +The :ref:`development server ` and :ref:`production ` considerations described +for SSE below apply to all streaming responses. .. _server-sent-events: @@ -325,33 +311,28 @@ Server-Sent Events (SSE) CodeIgniter provides an ``SSEResponse`` for streaming `Server-Sent Events `_ -over HTTP. This is useful for long-lived connections where the server pushes -events to the client. ``SSEResponse`` is a specialized -:ref:`StreamResponse ` that formats output according to -the SSE protocol and forces the appropriate headers. Create it via the -``$this->response->eventStream()`` factory method, or instantiate +over HTTP. This is useful for long-lived connections where the server pushes events to the client. ``SSEResponse`` is a +specialized :ref:`StreamResponse ` that formats output according to the SSE protocol and forces the +appropriate headers. Create it via the ``$this->response->eventStream()`` factory method, or instantiate ``new SSEResponse(...)`` directly. .. literalinclude:: response/036.php -The callback receives the ``SSEResponse`` instance. Use ``event()``, -``comment()``, or ``retry()`` to send SSE fields. If you pass an array to -``event()``, it will be JSON-encoded. If encoding fails or the client -disconnects, ``event()`` returns ``false``. +The callback receives the ``SSEResponse`` instance. Use ``event()``, ``comment()``, or ``retry()`` to send SSE fields. +If you pass an array to ``event()``, it will be JSON-encoded. If encoding fails or the client disconnects, ``event()`` +returns ``false``. -The response is streamed: output buffering is disabled, and the session is closed -to avoid blocking other requests. Headers must be set **before** returning the -response because anything set inside the callback will be too late. +The response is streamed: output buffering is disabled, and the session is closed to avoid blocking other requests. +Headers must be set **before** returning the response because anything set inside the callback will be too late. -After filters still run. Any headers or cookies they set will be sent, but they -must not rely on the response body. View rendering, and decorators are not applied -because the response body is not built - stream your output in the callback. +After filters still run. Any headers or cookies they set will be sent, but they must not rely on the response body. View +rendering and decorators are not applied because the response body is not built - stream your output in the callback. Custom Headers and Keep-Alive ----------------------------- -If you need custom headers, set them before returning the response. You can also -use comments for keep-alive and configure the client retry interval: +If you need custom headers, set them before returning the response. You can also use comments for keep-alive and +configure the client retry interval: .. literalinclude:: response/037.php @@ -360,13 +341,11 @@ use comments for keep-alive and configure the client retry interval: Development Server Limitations ------------------------------ -When testing SSE locally, keep in mind that PHP's built-in development server -(used by ``php spark serve``) is not designed to handle long-lived streaming -connections effectively. +When testing SSE locally, keep in mind that PHP's built-in development server (used by ``php spark serve``) is not +designed to handle long-lived streaming connections effectively. -Because an SSE connection remains open for an extended period, the built-in -server may spend most of its capacity serving the stream. As a result, other -requests may appear slow, blocked, or delayed while the SSE connection is active. +Because an SSE connection remains open for an extended period, the built-in server may spend most of its capacity +serving the stream. As a result, other requests may appear slow, blocked, or delayed while the SSE connection is active. Possible symptoms include: @@ -374,29 +353,25 @@ Possible symptoms include: - API requests responding slowly - frontend requests remaining in a pending state -For more realistic testing, prefer a server stack that handles concurrent -requests better, such as Apache, nginx with PHP-FPM, or FrankenPHP. +For more realistic testing, prefer a server stack that handles concurrent requests better, such as Apache, nginx with +PHP-FPM, or FrankenPHP. -This behavior is a limitation of the development server environment, not of -``SSEResponse`` itself. +This behavior is a limitation of the development server environment, not of ``SSEResponse`` itself. .. _sse-production: Production Considerations ------------------------- -Some server stacks and CDNs buffer or compress responses (e.g., Apache with -``mod_deflate``), which can break real-time SSE delivery. -``SSEResponse`` disables PHP output buffering, turns off zlib output -compression, and sets ``X-Accel-Buffering: no``. However, intermediaries may -still buffer or compress, so configure your web server or CDN to disable -buffering/compression for SSE endpoints. +Some server stacks and CDNs buffer or compress responses (e.g., Apache with ``mod_deflate``), which can break real-time +SSE delivery. ``SSEResponse`` disables PHP output buffering, turns off zlib output compression, and sets +``X-Accel-Buffering: no``. However, intermediaries may still buffer or compress, so configure your web server or CDN to +disable buffering/compression for SSE endpoints. Example: Product-Oriented Use Case ---------------------------------- -The following example simulates a small notification stream to illustrate a more -product-focused use case: +The following example simulates a small notification stream to illustrate a more product-focused use case: .. literalinclude:: response/038.php diff --git a/user_guide_src/source/testing/cli.rst b/user_guide_src/source/testing/cli.rst index 835ff4411191..c2c1dd0c5769 100644 --- a/user_guide_src/source/testing/cli.rst +++ b/user_guide_src/source/testing/cli.rst @@ -63,8 +63,8 @@ Running a Modern Command Against an Output .. versionadded:: 4.8.0 -A modern command can also be run directly against an ``Output`` built on the mock, -which leaves the ``CLI`` class untouched once ``run()`` returns: +A modern command can also be run directly against an ``Output`` built on the mock, which leaves the ``CLI`` class +untouched once ``run()`` returns: .. literalinclude:: cli/002.php diff --git a/user_guide_src/source/testing/feature.rst b/user_guide_src/source/testing/feature.rst index 8748039a7dc1..f07fe31b485d 100644 --- a/user_guide_src/source/testing/feature.rst +++ b/user_guide_src/source/testing/feature.rst @@ -60,7 +60,7 @@ override any existing routes in the system: .. literalinclude:: feature/004.php :lines: 2- -Each of the "routes" is a 3 element array containing the uppercase HTTP verb (or "add" for all), +Each of the "routes" is a 3-element array containing the uppercase HTTP verb (or "add" for all), the URI to match, and the routing destination. .. _feature-setting-session-values: @@ -97,9 +97,10 @@ Use ``withFiles()`` to attach uploaded files to the next feature request. Create $file = new MockUploadedFile($path, 'photo.jpg', 'image/jpeg'); $result = $this->withFiles(['photo' => $file])->post('photos', ['title' => 'Portrait']); -The route can access the file through ``$request->getFile('photo')`` and use upload validation, ``move()``, or ``store()``. -``withFiles()`` sets the request's ``Content-Type`` to ``multipart/form-data`` and clears the attached files after the request. -Moving or storing the file moves the local source file, so create a new file object for each upload request. +The route can access the file through ``$request->getFile('photo')`` and use upload validation, ``move()``, or +``store()``. ``withFiles()`` sets the request's ``Content-Type`` to ``multipart/form-data`` and clears the attached +files after the request. Moving or storing the file moves the local source file, so create a new file object for each +upload request. Bypassing Events ---------------- @@ -122,8 +123,7 @@ This is useful when testing JSON or XML APIs so that you can set the request in This will take the parameters passed into ``call()``, ``post()``, ``get()``... and assign them to the body of the request in the given format. -For example, use ``withBodyFormat('json')->query()`` to test a QUERY request -with a JSON body. +For example, use ``withBodyFormat('json')->query()`` to test a QUERY request with a JSON body. This will also set the `Content-Type` header for your request accordingly.