diff --git a/deptrac.yaml b/deptrac.yaml index c68b66c9adf0..178de9a74fd7 100644 --- a/deptrac.yaml +++ b/deptrac.yaml @@ -256,6 +256,8 @@ parameters: - CodeIgniter\HTTP\URI CodeIgniter\Log\Handlers\ChromeLoggerHandler: - CodeIgniter\HTTP\ResponseInterface + CodeIgniter\Security\CheckPhpIni: + - CodeIgniter\View\Table CodeIgniter\View\Table: - CodeIgniter\Database\BaseResult CodeIgniter\View\Plugins: diff --git a/system/Commands/Utilities/PhpIniCheck.php b/system/Commands/Utilities/PhpIniCheck.php new file mode 100644 index 000000000000..0426f9078b77 --- /dev/null +++ b/system/Commands/Utilities/PhpIniCheck.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Commands\Utilities; + +use CodeIgniter\CLI\BaseCommand; +use CodeIgniter\Security\CheckPhpIni; + +/** + * Check php.ini values. + */ +final class PhpIniCheck extends BaseCommand +{ + /** + * The group the command is lumped under + * when listing commands. + * + * @var string + */ + protected $group = 'CodeIgniter'; + + /** + * The Command's name + * + * @var string + */ + protected $name = 'phpini:check'; + + /** + * The Command's short description + * + * @var string + */ + protected $description = 'Check your php.ini values.'; + + /** + * The Command's usage + * + * @var string + */ + protected $usage = 'phpini:check'; + + /** + * The Command's arguments + * + * @var array + */ + protected $arguments = [ + ]; + + /** + * The Command's options + * + * @var array + */ + protected $options = []; + + /** + * {@inheritDoc} + */ + public function run(array $params) + { + CheckPhpIni::run(); + + return EXIT_SUCCESS; + } +} diff --git a/system/Security/CheckPhpIni.php b/system/Security/CheckPhpIni.php new file mode 100644 index 000000000000..6bbd8619c090 --- /dev/null +++ b/system/Security/CheckPhpIni.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Security; + +use CodeIgniter\CLI\CLI; +use CodeIgniter\View\Table; + +/** + * Checks php.ini settings + * + * @used-by \CodeIgniter\Commands\Utilities\PhpIniCheck + * @see \CodeIgniter\Security\CheckPhpIniTest + */ +class CheckPhpIni +{ + /** + * @param bool $isCli Set false if you run via Web + * + * @return string|void HTML string or void in CLI + */ + public static function run(bool $isCli = true) + { + $output = static::checkIni(); + + $thead = ['Directive', 'Global', 'Current', 'Recommended', 'Remark']; + $tbody = []; + + // CLI + if ($isCli) { + self::outputForCli($output, $thead, $tbody); + + return; + } + + // Web + return self::outputForWeb($output, $thead, $tbody); + } + + private static function outputForCli(array $output, array $thead, array $tbody): void + { + foreach ($output as $directive => $values) { + $current = $values['current']; + $notRecommended = false; + + if ($values['recommended'] !== '') { + if ($values['recommended'] !== $values['current']) { + $notRecommended = true; + } + + $current = $notRecommended + ? CLI::color($values['current'] === '' ? 'n/a' : $values['current'], 'red') + : $values['current']; + } + + $directive = $notRecommended ? CLI::color($directive, 'red') : $directive; + $tbody[] = [ + $directive, $values['global'], $current, $values['recommended'], $values['remark'], + ]; + } + + CLI::table($tbody, $thead); + } + + private static function outputForWeb(array $output, array $thead, array $tbody): string + { + foreach ($output as $directive => $values) { + $current = $values['current']; + $notRecommended = false; + + if ($values['recommended'] !== '') { + if ($values['recommended'] !== $values['current']) { + $notRecommended = true; + } + + if ($values['current'] === '') { + $current = 'n/a'; + } + + $current = $notRecommended + ? '' . $current . '' + : $current; + } + + $directive = $notRecommended + ? '' . $directive . '' + : $directive; + $tbody[] = [ + $directive, $values['global'], $current, $values['recommended'], $values['remark'], + ]; + } + + $table = new Table(); + $template = [ + 'table_open' => '', + ]; + $table->setTemplate($template); + + $table->setHeading($thead); + + return '
' . $table->generate($tbody) . '
'; + } + + /** + * @internal Used for testing purposes only. + * @testTag + */ + public static function checkIni(): array + { + $items = [ + 'error_reporting' => ['recommended' => '5111'], + 'display_errors' => ['recommended' => '0'], + 'display_startup_errors' => ['recommended' => '0'], + 'log_errors' => [], + 'error_log' => [], + 'default_charset' => ['recommended' => 'UTF-8'], + 'memory_limit' => ['remark' => '> post_max_size'], + 'post_max_size' => ['remark' => '> upload_max_filesize'], + 'upload_max_filesize' => ['remark' => '< post_max_size'], + 'request_order' => ['recommended' => 'GP'], + 'variables_order' => ['recommended' => 'GPCS'], + 'date.timezone' => ['recommended' => 'UTC'], + 'mbstring.language' => ['recommended' => 'neutral'], + 'opcache.enable' => ['recommended' => '1'], + 'opcache.enable_cli' => [], + 'opcache.jit' => [], + 'opcache.jit_buffer_size' => [], + ]; + + $output = []; + $ini = ini_get_all(); + + foreach ($items as $key => $values) { + $output[$key] = [ + 'global' => $ini[$key]['global_value'], + 'current' => $ini[$key]['local_value'], + 'recommended' => $values['recommended'] ?? '', + 'remark' => $values['remark'] ?? '', + ]; + } + + // [directive => [current_value, recommended_value]] + return $output; + } +} diff --git a/tests/system/Security/CheckPhpIniTest.php b/tests/system/Security/CheckPhpIniTest.php new file mode 100644 index 000000000000..847f9fd82897 --- /dev/null +++ b/tests/system/Security/CheckPhpIniTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Security; + +use CodeIgniter\CLI\CLI; +use CodeIgniter\Test\CIUnitTestCase; +use CodeIgniter\Test\Mock\MockInputOutput; + +/** + * @internal + * + * @group Others + */ +final class CheckPhpIniTest extends CIUnitTestCase +{ + public function testCheckIni() + { + $output = CheckPhpIni::checkIni(); + + $expected = [ + 'global' => '', + 'current' => '1', + 'recommended' => '0', + 'remark' => '', + ]; + $this->assertSame($expected, $output['display_errors']); + } + + public function testRunCli() + { + // Set MockInputOutput to CLI. + $io = new MockInputOutput(); + CLI::setInputOutput($io); + + CheckPhpIni::run(true); + + // Get the whole output string. + $output = $io->getOutput(); + + $this->assertStringContainsString('display_errors', $output); + + // Remove MockInputOutput. + CLI::resetInputOutput(); + } + + public function testRunWeb() + { + $output = CheckPhpIni::run(false); + + $this->assertStringContainsString('display_errors', $output); + } +} diff --git a/user_guide_src/source/changelogs/v4.5.0.rst b/user_guide_src/source/changelogs/v4.5.0.rst index 18a28dff0662..12c1b77e82fc 100644 --- a/user_guide_src/source/changelogs/v4.5.0.rst +++ b/user_guide_src/source/changelogs/v4.5.0.rst @@ -28,6 +28,8 @@ Commands :ref:`cli-generators-make-test` for the details. - Added ``spark config:check`` command to check Config values. See :ref:`confirming-config-values` for the details. +- Added ``spark phpini:check`` command to check important PHP ini settings. See + :ref:`spark-phpini-check` for the details. - Added ``spark lang:find`` command to update translations keys. See :ref:`generating-translation-files-via-command` for the details. - The ``--dbgroup`` option has been added to the ``spark db:table`` command. See :ref:`Database Commands `. diff --git a/user_guide_src/source/installation/running.rst b/user_guide_src/source/installation/running.rst index 924ccebc2bd3..be7dc4e44264 100644 --- a/user_guide_src/source/installation/running.rst +++ b/user_guide_src/source/installation/running.rst @@ -74,6 +74,47 @@ you will need to modify the permissions for the **writable** folder inside your project, so that it is writable by the user or account used by your web server. +.. _spark-phpini-check: + +Checking PHP ini Settings +========================= + +.. versionadded:: 4.5.0 + +`PHP ini settings`_ change the behaviors of PHP. CodeIgniter provides a command to +check important PHP settings. + +.. _PHP ini settings: https://www.php.net/manual/en/ini.list.php + +.. code-block:: console + + php spark phpini:check + +The *Recommended* column shows the recommended values for production environment. +They may differ in development environments. + +.. note:: + If you cannot use the spark command, you can use ``CheckPhpIni::run(false)`` + in your controller. + + E.g., + + .. code-block:: php + +