diff --git a/.github/workflows/test-phpunit.yml b/.github/workflows/test-phpunit.yml index aaa9b17657d..33878349f70 100644 --- a/.github/workflows/test-phpunit.yml +++ b/.github/workflows/test-phpunit.yml @@ -12,10 +12,28 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.4', '8.3', '8.2', '8.1', '8.0', '7.4'] + php: ['8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.4'] DB: [ 'pdo/mysql', 'pdo/pgsql', 'pdo/sqlite', 'mysqli', 'pgsql', 'sqlite' ] compiler: [ default ] include: + - php: '8.5' + DB: 'pdo/mysql' + compiler: jit + - php: '8.5' + DB: 'pdo/pgsql' + compiler: jit + - php: '8.5' + DB: 'pdo/sqlite' + compiler: jit + - php: '8.5' + DB: 'mysqli' + compiler: jit + - php: '8.5' + DB: 'pgsql' + compiler: jit + - php: '8.5' + DB: 'sqlite' + compiler: jit - php: '8.4' DB: 'pdo/mysql' compiler: jit @@ -143,7 +161,7 @@ jobs: tools: composer, pecl extensions: imagick, sqlite3, pgsql, mysqli, pdo, pdo_mysql, pdo_pgsql, pdo_sqlite, mbstring ini-values: ${{ env.PHP_INI_VALUES }} - coverage: xdebug + coverage: pcov # CHANGED from 'xdebug' to 'pcov' - name: Get composer cache directory id: composer-cache @@ -153,12 +171,35 @@ jobs: with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-composer- + restore-keys: ${{ runner.os }}-composer- + + # --- CRUCIAL NEW STEP WITH CORRECT INDENTATION --- + - name: Conditionally Lock PHPUnit 9 for older PHP versions + if: matrix.php == '7.4' || matrix.php == '8.0' + # This command updates the dependency in composer.json/lock without installing. + run: composer require --dev --no-update "phpunit/phpunit:^9.0" + - name: Install composer dependencies + # This runs the actual install after the lock/update (if any) run: composer install --no-progress --prefer-dist --optimize-autoloader - name: PHPUnit Test run: | - php -d error_reporting=E_ALL -d zend.enable_gc=0 -d date.timezone=UTC vendor/bin/phpunit --coverage-text --configuration tests/travis/${{ matrix.DB }}.phpunit.xml - env: - XDEBUG_MODE: coverage + # 1. Determine the configuration file based on PHP version + # Use the _v9 config file for older PHPUnit versions (PHP 7.4 & 8.0) + if [ "${{ matrix.php }}" = "7.4" ] || [ "${{ matrix.php }}" = "8.0" ]; then + CONFIG_FILE="tests/travis/${{ matrix.DB }}.phpunit_v9.xml" + STRICT_FLAGS="" # V9 files handle strictness via XML attributes + else + # Use the modern config file for PHPUnit 10/11 (PHP 8.1+) + CONFIG_FILE="tests/travis/${{ matrix.DB }}.phpunit.xml" + # Explicitly add flags to force failure on PHP Warnings/Notices, + # as the XML schema doesn't allow the modern equivalent tags. + STRICT_FLAGS="--fail-on-warning --fail-on-notice" + fi + + # 2. Execute PHPUnit with dynamic configuration and flags + php -d error_reporting=E_ALL -d zend.enable_gc=0 -d date.timezone=UTC vendor/bin/phpunit \ + --coverage-text \ + --configuration ${CONFIG_FILE} \ + ${STRICT_FLAGS} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 323f06468b9..eef3f846258 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ user_guide_src/cilexer/pycilexer.egg-info/* *.sublime-project /tests/tests/ /tests/results/ +/tests/.phpunit.result.cache +/tests/travis/.phpunit.result.cache diff --git a/composer.json b/composer.json index 13cb2af56f9..6511504fb44 100644 --- a/composer.json +++ b/composer.json @@ -4,11 +4,6 @@ "type": "project", "homepage": "https://github.com/NielBuys/CodeIgniter", "license": "MIT", - "config": { - "platform": { - "php": "7.4" - } - }, "support": { "forum": "http://forum.codeigniter.com/", "issues": "https://github.com/NielBuys/CodeIgniter/issues", diff --git a/contributing.md b/contributing.md index d8274c27c21..f0d37ed3dea 100644 --- a/contributing.md +++ b/contributing.md @@ -1,6 +1,6 @@ -# Contributing to CodeIgniter +# Contributing to this Fork of CodeIgniter 3 -CodeIgniter is a community driven project and accepts contributions of code and documentation from the community. These contributions are made in the form of Issues or [Pull Requests](http://help.github.com/send-pull-requests/) on the [CodeIgniter repository](https://github.com/bcit-ci/CodeIgniter) on GitHub. +CodeIgniter is a community driven project and accepts contributions of code and documentation from the community. These contributions are made in the form of Issues or Pull Requests on the [Fork of CodeIgniter 3 repository](https://github.com/NielBuys/CodeIgniter) on GitHub. Issues are a quick way to point out a bug. If you find a bug or documentation error in CodeIgniter then please check a few things first: @@ -28,7 +28,7 @@ If you change anything that requires a change to documentation then you will nee ### Compatibility -CodeIgniter recommends PHP 7.2 or newer to be used +This Fork of CodeIgniter 3 recommends PHP 7.4 or newer to be used ### Branching @@ -64,7 +64,7 @@ Easy way GitHub allows in-line editing of files for making simple typo changes a Hard way The best way to contribute is to "clone" your fork of CodeIgniter to your development area. That sounds like some jargon, but "forking" on GitHub means "making a copy of that repo to your account" and "cloning" means "copying that code to your environment so you can work on it". 1. [Set up Git](https://help.github.com/en/articles/set-up-git) (Windows, Mac & Linux) -2. Go to the [CodeIgniter repo](https://github.com/bcit-ci/CodeIgniter) +2. Go to the [Fork of CodeIgniter repo](https://github.com/NielBuys/CodeIgniter) 3. [Fork it](https://help.github.com/en/articles/fork-a-repo) 4. [Clone](https://help.github.com/en/articles/fetching-a-remote#clone) your forked CodeIgniter repo: git@github.com:/CodeIgniter.git. 5. Checkout the "develop" branch. At this point you are ready to start making changes. @@ -79,11 +79,11 @@ Once the Reactor Engineer handling your pull request is happy with it they will ### Keeping your fork up-to-date -Unlike systems like Subversion, Git can have multiple remotes. A remote is the name for a URL of a Git repository. By default your fork will have a remote named "origin" which points to your fork, but you can add another remote named "codeigniter" which points to `git://github.com/bcit-ci/CodeIgniter.git`. This is a read-only remote but you can pull from this develop branch to update your own. +Unlike systems like Subversion, Git can have multiple remotes. A remote is the name for a URL of a Git repository. By default your fork will have a remote named "origin" which points to your fork, but you can add another remote named "codeigniter" which points to `git://github.com/NielBuys/CodeIgniter.git`. This is a read-only remote but you can pull from this develop branch to update your own. If you are using command-line you can do the following: -1. `git remote add codeigniter git://github.com/bcit-ci/CodeIgniter.git` +1. `git remote add codeigniter git://github.com/NielBuys/CodeIgniter.git` 2. `git pull codeigniter develop` 3. `git push origin develop` diff --git a/readme.rst b/readme.rst index abccf5b508b..73782b693ed 100644 --- a/readme.rst +++ b/readme.rst @@ -20,7 +20,7 @@ https://github.com/NielBuys/CodeIgniter/releases Server Requirements ******************* -PHP version 7.4 or newer (PHP 8.4 ready) is recommended. +PHP version 7.4 or newer (PHP 8.5 ready) is recommended. ************ Installation @@ -28,7 +28,7 @@ Installation CodeIgniter is installed in four steps: -1. npm install nielbuys/framework +1. composer require nielbuys/framework 2. Change "codeigniter/framework" to "nielbuys/framework" in the "composer.json" file. 3. Change in main "index.php" the following line from "$system_path = 'vendor/codeigniter/framework/system';" to "$system_path = 'vendor/nielbuys/framework/system';" 4. Then run composer update. @@ -62,14 +62,12 @@ Resources ********* - `User Guide `_ -- `Contributing Guide `_ +- `Contributing Guide `_ - `Language File Translations `_ - `Community Forums `_ - `Community Wiki `_ - `Community Slack Channel `_ -Report security issues to our `Security Panel `_ -or via our `page on HackerOne `_, thank you. *************** Acknowledgement diff --git a/system/core/Lang.php b/system/core/Lang.php index 18299060c09..81508839c97 100644 --- a/system/core/Lang.php +++ b/system/core/Lang.php @@ -190,6 +190,7 @@ public function load($langfile, $idiom = '', $return = FALSE, $add_suffix = TRUE */ public function line($line, $log_errors = TRUE) { + $line = (string) $line; $value = isset($this->language[$line]) ? $this->language[$line] : FALSE; // Because killer robots like unicorns! diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php index 1ad854da154..83d209dd76d 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php @@ -123,6 +123,37 @@ public function __construct($params) */ public function db_connect($persistent = FALSE) { + if (PHP_VERSION_ID >= 80400 && class_exists('\Pdo\Mysql')) { + $CONST_PREFIX = \Pdo\Mysql::class . '::'; + $ATTR_INIT_COMMAND_NAME = $CONST_PREFIX . 'ATTR_INIT_COMMAND'; + $ATTR_COMPRESS_NAME = $CONST_PREFIX . 'ATTR_COMPRESS'; + $ATTR_SSL_KEY_NAME = $CONST_PREFIX . 'ATTR_SSL_KEY'; + $ATTR_SSL_CERT_NAME = $CONST_PREFIX . 'ATTR_SSL_CERT'; + $ATTR_SSL_CA_NAME = $CONST_PREFIX . 'ATTR_SSL_CA'; + $ATTR_SSL_CAPATH_NAME = $CONST_PREFIX . 'ATTR_SSL_CAPATH'; + $ATTR_SSL_CIPHER_NAME = $CONST_PREFIX . 'ATTR_SSL_CIPHER'; + $ATTR_SSL_VERIFY_SERVER_CERT_NAME = $CONST_PREFIX . 'ATTR_SSL_VERIFY_SERVER_CERT'; + } else { + $CONST_PREFIX = \PDO::class . '::'; + $ATTR_INIT_COMMAND_NAME = $CONST_PREFIX . 'MYSQL_ATTR_INIT_COMMAND'; + $ATTR_COMPRESS_NAME = $CONST_PREFIX . 'MYSQL_ATTR_COMPRESS'; + $ATTR_SSL_KEY_NAME = $CONST_PREFIX . 'MYSQL_ATTR_SSL_KEY'; + $ATTR_SSL_CERT_NAME = $CONST_PREFIX . 'MYSQL_ATTR_SSL_CERT'; + $ATTR_SSL_CA_NAME = $CONST_PREFIX . 'MYSQL_ATTR_SSL_CA'; + $ATTR_SSL_CAPATH_NAME = $CONST_PREFIX . 'MYSQL_ATTR_SSL_CAPATH'; + $ATTR_SSL_CIPHER_NAME = $CONST_PREFIX . 'MYSQL_ATTR_SSL_CIPHER'; + $ATTR_SSL_VERIFY_SERVER_CERT_NAME = $CONST_PREFIX . 'MYSQL_ATTR_SSL_VERIFY_SERVER_CERT'; + } + + $ATTR_INIT_COMMAND = constant($ATTR_INIT_COMMAND_NAME); + $ATTR_COMPRESS = constant($ATTR_COMPRESS_NAME); + $ATTR_SSL_KEY = constant($ATTR_SSL_KEY_NAME); + $ATTR_SSL_CERT = constant($ATTR_SSL_CERT_NAME); + $ATTR_SSL_CA = constant($ATTR_SSL_CA_NAME); + $ATTR_SSL_CAPATH = constant($ATTR_SSL_CAPATH_NAME); + $ATTR_SSL_CIPHER = constant($ATTR_SSL_CIPHER_NAME); + $ATTR_SSL_VERIFY_SERVER_CERT = constant($ATTR_SSL_VERIFY_SERVER_CERT_NAME) ?? -1; + if (isset($this->stricton)) { if ($this->stricton) @@ -143,34 +174,35 @@ public function db_connect($persistent = FALSE) if ( ! empty($sql)) { - if (empty($this->options[PDO::MYSQL_ATTR_INIT_COMMAND])) + if (empty($this->options[$ATTR_INIT_COMMAND])) { - $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET SESSION sql_mode = '.$sql; + $this->options[$ATTR_INIT_COMMAND] = 'SET SESSION sql_mode = '.$sql; } else { - $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] .= ', @@session.sql_mode = '.$sql; + $this->options[$ATTR_INIT_COMMAND] .= ', @@session.sql_mode = '.$sql; } } } if ($this->compress === TRUE) { - $this->options[PDO::MYSQL_ATTR_COMPRESS] = TRUE; + $this->options[$ATTR_COMPRESS] = TRUE; } if (is_array($this->encrypt)) { $ssl = array(); - empty($this->encrypt['ssl_key']) OR $ssl[PDO::MYSQL_ATTR_SSL_KEY] = $this->encrypt['ssl_key']; - empty($this->encrypt['ssl_cert']) OR $ssl[PDO::MYSQL_ATTR_SSL_CERT] = $this->encrypt['ssl_cert']; - empty($this->encrypt['ssl_ca']) OR $ssl[PDO::MYSQL_ATTR_SSL_CA] = $this->encrypt['ssl_ca']; - empty($this->encrypt['ssl_capath']) OR $ssl[PDO::MYSQL_ATTR_SSL_CAPATH] = $this->encrypt['ssl_capath']; - empty($this->encrypt['ssl_cipher']) OR $ssl[PDO::MYSQL_ATTR_SSL_CIPHER] = $this->encrypt['ssl_cipher']; + empty($this->encrypt['ssl_key']) OR $ssl[$ATTR_SSL_KEY] = $this->encrypt['ssl_key']; + empty($this->encrypt['ssl_cert']) OR $ssl[$ATTR_SSL_CERT] = $this->encrypt['ssl_cert']; + empty($this->encrypt['ssl_ca']) OR $ssl[$ATTR_SSL_CA] = $this->encrypt['ssl_ca']; + empty($this->encrypt['ssl_capath']) OR $ssl[$ATTR_SSL_CAPATH] = $this->encrypt['ssl_capath']; + empty($this->encrypt['ssl_cipher']) OR $ssl[$ATTR_SSL_CIPHER] = $this->encrypt['ssl_cipher']; + - if (defined('PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT') && isset($this->encrypt['ssl_verify'])) + if ($ATTR_SSL_VERIFY_SERVER_CERT !== -1 && isset($this->encrypt['ssl_verify'])) { - $ssl[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $this->encrypt['ssl_verify']; + $ssl[$ATTR_SSL_VERIFY_SERVER_CERT] = $this->encrypt['ssl_verify']; } // DO NOT use array_merge() here! diff --git a/system/helpers/captcha_helper.php b/system/helpers/captcha_helper.php index c7feeac3418..b7553a80d5f 100644 --- a/system/helpers/captcha_helper.php +++ b/system/helpers/captcha_helper.php @@ -346,7 +346,9 @@ function create_captcha($data = '', $img_path = '', $img_url = '', $font_path = } $img = ' '; - ImageDestroy($im); + if (PHP_VERSION_ID < 80500) { + ImageDestroy($im); + } return array('word' => $word, 'time' => $now, 'image' => $img, 'filename' => $img_filename); } diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php index 4c8994c96e1..e04f5d95849 100644 --- a/system/libraries/Image_lib.php +++ b/system/libraries/Image_lib.php @@ -840,8 +840,10 @@ public function image_process_gd($action = 'resize') } // Kill the file handles - imagedestroy($dst_img); - imagedestroy($src_img); + if (PHP_VERSION_ID < 80500) { + imagedestroy($dst_img); + imagedestroy($src_img); + } if ($this->dynamic_output !== TRUE) { @@ -1049,8 +1051,10 @@ public function image_rotate_gd() } // Kill the file handles - imagedestroy($dst_img); - imagedestroy($src_img); + if (PHP_VERSION_ID < 80500) { + imagedestroy($dst_img); + imagedestroy($src_img); + } chmod($this->full_dst_path, $this->file_permissions); @@ -1128,7 +1132,9 @@ public function image_mirror_gd() } // Kill the file handles - imagedestroy($src_img); + if (PHP_VERSION_ID < 80500) { + imagedestroy($src_img); + } chmod($this->full_dst_path, $this->file_permissions); @@ -1258,8 +1264,10 @@ public function overlay_watermark() return FALSE; } - imagedestroy($src_img); - imagedestroy($wm_img); + if (PHP_VERSION_ID < 80500) { + imagedestroy($src_img); + imagedestroy($wm_img); + } return TRUE; } @@ -1429,7 +1437,9 @@ public function text_watermark() $this->image_save_gd($src_img); } - imagedestroy($src_img); + if (PHP_VERSION_ID < 80500) { + imagedestroy($src_img); + } return TRUE; } diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php index 434b6b13609..1cd4448aa86 100644 --- a/system/libraries/Upload.php +++ b/system/libraries/Upload.php @@ -1231,7 +1231,9 @@ protected function _file_mime_type($file) if ($finfo !== FALSE) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system { $mime = @finfo_file($finfo, $file['tmp_name']); - finfo_close($finfo); + if (PHP_VERSION_ID < 80500) { + finfo_close($finfo); + } /* According to the comments section of the PHP manual page, * it is possible that this function returns an empty string diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php index 9129b7210d0..b6a106542c2 100644 --- a/system/libraries/Xmlrpc.php +++ b/system/libraries/Xmlrpc.php @@ -1187,10 +1187,14 @@ public function parseResponse($fp) xml_get_current_line_number($parser)); $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']); - xml_parser_free($parser); + if (PHP_VERSION_ID < 80500) { + xml_parser_free($parser); + } return $r; } - xml_parser_free($parser); + if (PHP_VERSION_ID < 80500) { + xml_parser_free($parser); + } // Got ourselves some badness, it seems if ($this->xh['isf'] > 1) diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php index ed28721f551..b14eb49d2a0 100644 --- a/system/libraries/Xmlrpcs.php +++ b/system/libraries/Xmlrpcs.php @@ -262,7 +262,9 @@ public function parseRequest($data = '') sprintf('XML error: %s at line %d', xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser))); - xml_parser_free($parser); + if (PHP_VERSION_ID < 80500) { + xml_parser_free($parser); + } } elseif ($parser_object->xh['isf']) { @@ -270,7 +272,9 @@ public function parseRequest($data = '') } else { - xml_parser_free($parser); + if (PHP_VERSION_ID < 80500) { + xml_parser_free($parser); + } $m = new XML_RPC_Message($parser_object->xh['method']); $plist = ''; diff --git a/tests/codeigniter/core/Loader_test.php b/tests/codeigniter/core/Loader_test.php index 94491f396ac..f63085ac24f 100644 --- a/tests/codeigniter/core/Loader_test.php +++ b/tests/codeigniter/core/Loader_test.php @@ -299,7 +299,7 @@ public function test_load_view() $this->assertEquals($content.$value, $out); // Mock output class - $output = $this->getMockBuilder('CI_Output')->setMethods(array('append_output'))->getMock(); + $output = $this->getMockBuilder('CI_Output')->onlyMethods(array('append_output'))->getMock(); $output->expects($this->once())->method('append_output')->with($content.$value); $this->ci_instance_var('output', $output); @@ -451,7 +451,7 @@ public function test_language() { // Mock lang class and test load call $file = 'test'; - $lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load'))->getMock(); + $lang = $this->getMockBuilder('CI_Lang')->onlyMethods(array('load'))->getMock(); $lang->expects($this->once())->method('load')->with($file); $this->ci_instance_var('lang', $lang); $this->assertInstanceOf('CI_Loader', $this->load->language($file)); diff --git a/tests/codeigniter/core/Log_test.php b/tests/codeigniter/core/Log_test.php index 9279843857e..c45f651d531 100644 --- a/tests/codeigniter/core/Log_test.php +++ b/tests/codeigniter/core/Log_test.php @@ -1,50 +1,86 @@ setAccessible(TRUE); - $threshold = new ReflectionProperty('CI_Log', '_threshold'); - $threshold->setAccessible(TRUE); - $date_fmt = new ReflectionProperty('CI_Log', '_date_fmt'); - $date_fmt->setAccessible(TRUE); - $file_ext = new ReflectionProperty('CI_Log', '_file_ext'); - $file_ext->setAccessible(TRUE); - $file_perms = new ReflectionProperty('CI_Log', '_file_permissions'); - $file_perms->setAccessible(TRUE); - $enabled = new ReflectionProperty('CI_Log', '_enabled'); - $enabled->setAccessible(TRUE); - - $this->ci_set_config('log_path', '/root/'); - $this->ci_set_config('log_threshold', 'z'); - $this->ci_set_config('log_date_format', 'd.m.Y'); - $this->ci_set_config('log_file_extension', ''); - $this->ci_set_config('log_file_permissions', ''); - $instance = new CI_Log(); + protected $_test_dir; - $this->assertEquals($path->getValue($instance), '/root/'); - $this->assertEquals($threshold->getValue($instance), 1); - $this->assertEquals($date_fmt->getValue($instance), 'd.m.Y'); - $this->assertEquals($file_ext->getValue($instance), 'php'); - $this->assertEquals($file_perms->getValue($instance), 0644); - $this->assertFalse($enabled->getValue($instance)); + public function set_up() + { + // Initialize VFS Stream to create a virtual, mock file system + $this->_test_dir = vfsStream::setup('root'); + $this->helper('file'); + } - $this->ci_set_config('log_path', ''); - $this->ci_set_config('log_threshold', '0'); - $this->ci_set_config('log_date_format', ''); - $this->ci_set_config('log_file_extension', '.log'); - $this->ci_set_config('log_file_permissions', 0600); - $instance = new CI_Log(); + public function test_configuration() + { + // Setup Reflection Properties (as provided in the original request) + $path = new ReflectionProperty('CI_Log', '_log_path'); + if (PHP_VERSION_ID < 80500) { + $path->setAccessible(TRUE); + } + $threshold = new ReflectionProperty('CI_Log', '_threshold'); + if (PHP_VERSION_ID < 80500) { + $threshold->setAccessible(TRUE); + } + $date_fmt = new ReflectionProperty('CI_Log', '_date_fmt'); + if (PHP_VERSION_ID < 80500) { + $date_fmt->setAccessible(TRUE); + } + $file_ext = new ReflectionProperty('CI_Log', '_file_ext'); + if (PHP_VERSION_ID < 80500) { + $file_ext->setAccessible(TRUE); + } + $file_perms = new ReflectionProperty('CI_Log', '_file_permissions'); + if (PHP_VERSION_ID < 80500) { + $file_perms->setAccessible(TRUE); + } + $enabled = new ReflectionProperty('CI_Log', '_enabled'); + if (PHP_VERSION_ID < 80500) { + $enabled->setAccessible(TRUE); + } - $this->assertEquals($path->getValue($instance), APPPATH.'logs/'); - $this->assertEquals($threshold->getValue($instance), 0); - $this->assertEquals($date_fmt->getValue($instance), 'Y-m-d H:i:s'); - $this->assertEquals($file_ext->getValue($instance), 'log'); - $this->assertEquals($file_perms->getValue($instance), 0600); - $this->assertEquals($enabled->getValue($instance), TRUE); - } + // --- TEST BLOCK 1: Check disabled logging on a guaranteed unwritable VFS path --- + + // 1. Create a VFS directory that is explicitly NOT writable (permissions 0444) + $unwritable_dir = vfsStream::newDirectory('unwritable_logs', 0444)->at($this->_test_dir); + $unwritable_path = vfsStream::url('root/unwritable_logs').'/'; + + // 2. Set the log_path configuration to the unwritable VFS path + $this->ci_set_config('log_path', $unwritable_path); + $this->ci_set_config('log_threshold', 'z'); + $this->ci_set_config('log_date_format', 'd.m.Y'); + $this->ci_set_config('log_file_extension', ''); + $this->ci_set_config('log_file_permissions', ''); + $instance = new CI_Log(); + + $this->assertEquals($path->getValue($instance), $unwritable_path); + $this->assertEquals($threshold->getValue($instance), 1); + $this->assertEquals($date_fmt->getValue($instance), 'd.m.Y'); + $this->assertEquals($file_ext->getValue($instance), 'php'); + $this->assertEquals($file_perms->getValue($instance), 0644); + $this->assertFalse($enabled->getValue($instance)); + + // --- TEST BLOCK 2: Check enabled logging (using original configuration intent) --- + + // To ensure this block is also environment-independent, we will use a Writable VFS path. + $writable_dir = vfsStream::newDirectory('writable_logs', 0755)->at($this->_test_dir); + $writable_path = vfsStream::url('root/writable_logs').'/'; + + $this->ci_set_config('log_path', $writable_path); // Use the guaranteed writable VFS path + $this->ci_set_config('log_threshold', '0'); + $this->ci_set_config('log_date_format', ''); + $this->ci_set_config('log_file_extension', '.log'); + $this->ci_set_config('log_file_permissions', 0600); + $instance = new CI_Log(); + // Note: APPPATH.'logs/' is usually what the path falls back to when set to '', + // but using the VFS path makes this reliable. + $this->assertEquals($path->getValue($instance), $writable_path); + $this->assertEquals($threshold->getValue($instance), 0); + $this->assertEquals($date_fmt->getValue($instance), 'Y-m-d H:i:s'); + $this->assertEquals($file_ext->getValue($instance), 'log'); + $this->assertEquals($file_perms->getValue($instance), 0600); + $this->assertEquals($enabled->getValue($instance), TRUE); + } // -------------------------------------------------------------------- public function test_format_line() @@ -54,7 +90,9 @@ public function test_format_line() $instance = new CI_Log(); $format_line = new ReflectionMethod($instance, '_format_line'); - $format_line->setAccessible(TRUE); + if (PHP_VERSION_ID < 80500) { + $format_line->setAccessible(TRUE); + } $this->assertEquals( $format_line->invoke($instance, 'LEVEL', 'Timestamp', 'Message'), "LEVEL - Timestamp --> Message".PHP_EOL diff --git a/tests/codeigniter/database/query_builder/like_test.php b/tests/codeigniter/database/query_builder/like_test.php index 4d8673d9935..793485b9f5b 100644 --- a/tests/codeigniter/database/query_builder/like_test.php +++ b/tests/codeigniter/database/query_builder/like_test.php @@ -117,7 +117,7 @@ public function test_like_set_side($str, $side, $expected_name) $this->assertEquals($expected_name, $actual[0]['name']); } - public function like_set_side_provider() + public static function like_set_side_provider() { return array( array('Developer', 'none', 'Developer'), diff --git a/tests/codeigniter/helpers/language_helper_test.php b/tests/codeigniter/helpers/language_helper_test.php index 1ddabea3d6b..ccb22bab669 100644 --- a/tests/codeigniter/helpers/language_helper_test.php +++ b/tests/codeigniter/helpers/language_helper_test.php @@ -5,7 +5,7 @@ class Language_helper_test extends CI_TestCase { public function test_lang() { $this->helper('language'); - $lang = $this->getMockBuilder('CI_Lang')->setMethods(array('line'))->getMock(); + $lang = $this->getMockBuilder('CI_Lang')->onlyMethods(array('line'))->getMock(); $lang->expects($this->any())->method('line')->will($this->returnValue(FALSE)); $this->ci_instance_var('lang', $lang); diff --git a/tests/codeigniter/helpers/number_helper_test.php b/tests/codeigniter/helpers/number_helper_test.php index 663e354fef3..7aa27776028 100644 --- a/tests/codeigniter/helpers/number_helper_test.php +++ b/tests/codeigniter/helpers/number_helper_test.php @@ -11,7 +11,7 @@ public function set_up() // Mock away load, too much going on in there, // we'll just check for the expected parameter - $lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load'))->getMock(); + $lang = $this->getMockBuilder('CI_Lang')->onlyMethods(array('load'))->getMock(); $lang->expects($this->once()) ->method('load') ->with($this->equalTo('number')); diff --git a/tests/codeigniter/helpers/security_helper_test.php b/tests/codeigniter/helpers/security_helper_test.php index effd3ec0289..a417d528cad 100644 --- a/tests/codeigniter/helpers/security_helper_test.php +++ b/tests/codeigniter/helpers/security_helper_test.php @@ -1,14 +1,22 @@ helper('security'); - $obj = new stdClass; - $obj->security = new Mock_Core_Security(); - $this->ci_instance($obj); - } + /** + * Sets up the environment before each test. + * Must be public to match CI_TestCase parent signature. + */ + public function setUp() // Reverting signature to match CI_TestCase parent (no : void) + { + parent::setUp(); + + $obj = new stdClass(); + $obj->security = new Mock_Core_Security(); + + $this->ci_instance($obj); + + $this->helper('security'); + } function test_xss_clean() { diff --git a/tests/codeigniter/libraries/Calendar_test.php b/tests/codeigniter/libraries/Calendar_test.php index ad1f45e8c77..afbad19648b 100644 --- a/tests/codeigniter/libraries/Calendar_test.php +++ b/tests/codeigniter/libraries/Calendar_test.php @@ -5,9 +5,9 @@ class Calendar_test extends CI_TestCase { public function set_up() { // Required for get_total_days() - $this->ci_instance_var('load', $this->getMockBuilder('CI_Loader')->setMethods(array('helper'))->getMock()); + $this->ci_instance_var('load', $this->getMockBuilder('CI_Loader')->onlyMethods(array('helper'))->getMock()); - $lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load', 'line'))->getMock(); + $lang = $this->getMockBuilder('CI_Lang')->onlyMethods(array('load', 'line'))->getMock(); $lang->expects($this->any())->method('line')->will($this->returnValue(FALSE)); $this->ci_instance_var('lang', $lang); diff --git a/tests/codeigniter/libraries/Driver_test.php b/tests/codeigniter/libraries/Driver_test.php index 5089bb851e1..8070a252268 100644 --- a/tests/codeigniter/libraries/Driver_test.php +++ b/tests/codeigniter/libraries/Driver_test.php @@ -18,7 +18,7 @@ public function set_up() // Mock Loader->get_package_paths $paths = 'get_package_paths'; - $ldr = $this->getMockBuilder('CI_Loader')->setMethods(array($paths))->getMock(); + $ldr = $this->getMockBuilder('CI_Loader')->onlyMethods(array($paths))->getMock(); $ldr->expects($this->any())->method($paths)->will($this->returnValue(array(APPPATH, BASEPATH))); $this->ci_instance_var('load', $ldr); diff --git a/tests/codeigniter/libraries/Form_validation_test.php b/tests/codeigniter/libraries/Form_validation_test.php index 6872b3abdc3..7d941a93c09 100644 --- a/tests/codeigniter/libraries/Form_validation_test.php +++ b/tests/codeigniter/libraries/Form_validation_test.php @@ -8,10 +8,10 @@ public function set_up() // Create a mock loader since load->helper() looks in the wrong directories for unit tests, // We'll use CI_TestCase->helper() instead - $loader = $this->getMockBuilder('CI_Loader')->setMethods(array('helper'))->getMock(); + $loader = $this->getMockBuilder('CI_Loader')->onlyMethods(array('helper'))->getMock(); // Same applies for lang - $lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load'))->getMock(); + $lang = $this->getMockBuilder('CI_Lang')->onlyMethods(array('load'))->getMock(); $this->ci_set_config('charset', 'UTF-8'); $utf8 = new Mock_Core_Utf8(); diff --git a/tests/codeigniter/libraries/Upload_test.php b/tests/codeigniter/libraries/Upload_test.php index 296ac42fb8d..6e00977856f 100644 --- a/tests/codeigniter/libraries/Upload_test.php +++ b/tests/codeigniter/libraries/Upload_test.php @@ -7,7 +7,7 @@ public function set_up() $ci = $this->ci_instance(); $ci->upload = new CI_Upload(); $ci->security = new Mock_Core_Security(); - $ci->lang = $this->getMockBuilder('CI_Lang')->setMethods(array('load', 'line'))->getMock(); + $ci->lang = $this->getMockBuilder('CI_Lang')->onlyMethods(array('load', 'line'))->getMock(); $ci->lang->expects($this->any())->method('line')->will($this->returnValue(FALSE)); $this->upload = $ci->upload; } @@ -27,7 +27,9 @@ public function test___construct_initialize() $reflection = new ReflectionClass($upload); $reflection = $reflection->getProperty('_file_name_override'); - $reflection->setAccessible(TRUE); + if (PHP_VERSION_ID < 80500) { + $reflection->setAccessible(TRUE); + } $this->assertEquals('foo', $reflection->getValue($upload)); $this->assertTrue($upload->file_ext_tolower); diff --git a/tests/codeigniter/libraries/Xmlrpc_test.php b/tests/codeigniter/libraries/Xmlrpc_test.php index 66b1f8e11da..94d7e74712c 100644 --- a/tests/codeigniter/libraries/Xmlrpc_test.php +++ b/tests/codeigniter/libraries/Xmlrpc_test.php @@ -15,7 +15,9 @@ public function set_up() $this->input = new Mock_Core_Input($security, $utf8); $this->input_lib_raw_stream = new ReflectionProperty($this->input, '_raw_input_stream'); - $this->input_lib_raw_stream->setAccessible(TRUE); + if (PHP_VERSION_ID < 80500) { + $this->input_lib_raw_stream->setAccessible(TRUE); + } $this->ci_instance_var('input', $this->input); $this->ci_instance_var('security', $security); diff --git a/tests/mocks/ci_testcase.php b/tests/mocks/ci_testcase.php index 5d863edf936..a6fd63f74fc 100644 --- a/tests/mocks/ci_testcase.php +++ b/tests/mocks/ci_testcase.php @@ -35,6 +35,12 @@ public function __construct($name = null, array $data = array(), $dataName = '') public function setUp() { + // 1. MANDATORY: Call the parent method first (Required by PHPUnit 10+) + parent::setUp(); + + // 2. LOGIC FROM runBare(): Set the global CI test instance + self::$ci_test_instance = $this; + // Setup VFS with base directories $this->ci_vfs_root = vfsStream::setup(''); $this->ci_app_root = vfsStream::newDirectory('application')->at($this->ci_vfs_root); @@ -337,23 +343,6 @@ public function ci_vfs_path($path, $base = '') // Internals // -------------------------------------------------------------------- - /** - * Overwrite runBare - * - * PHPUnit instantiates the test classes before - * running them individually. So right before a test - * runs we set our instance. Normally this step would - * happen in setUp, but someone is bound to forget to - * call the parent method and debugging this is no fun. - */ - public function runBare() - { - self::$ci_test_instance = $this; - parent::runBare(); - } - - // -------------------------------------------------------------------- - public function helper($name) { require_once(SYSTEM_PATH.'helpers/'.$name.'_helper.php'); diff --git a/tests/travis/mysql.phpunit.xml b/tests/travis/mysql.phpunit.xml index 15063fd5ada..92f98beb159 100644 --- a/tests/travis/mysql.phpunit.xml +++ b/tests/travis/mysql.phpunit.xml @@ -1,26 +1,32 @@ - - + + - - - ../codeigniter - - - - + + + ../codeigniter + + + + + ../../system - - + ../../application + + + + ../../tests + + \ No newline at end of file diff --git a/tests/travis/mysql.phpunit_v9.xml b/tests/travis/mysql.phpunit_v9.xml new file mode 100644 index 00000000000..15063fd5ada --- /dev/null +++ b/tests/travis/mysql.phpunit_v9.xml @@ -0,0 +1,26 @@ + + + + + + + + + ../codeigniter + + + + + ../../system + + + \ No newline at end of file diff --git a/tests/travis/mysqli.phpunit.xml b/tests/travis/mysqli.phpunit.xml index c77aaa303b4..a1ef46cb2f6 100644 --- a/tests/travis/mysqli.phpunit.xml +++ b/tests/travis/mysqli.phpunit.xml @@ -1,26 +1,32 @@ - - + + - - - ../codeigniter - - - - + + + ../codeigniter + + + + + ../../system - - + ../../application + + + + ../../tests + + \ No newline at end of file diff --git a/tests/travis/mysqli.phpunit_v9.xml b/tests/travis/mysqli.phpunit_v9.xml new file mode 100644 index 00000000000..c77aaa303b4 --- /dev/null +++ b/tests/travis/mysqli.phpunit_v9.xml @@ -0,0 +1,26 @@ + + + + + + + + + ../codeigniter + + + + + ../../system + + + \ No newline at end of file diff --git a/tests/travis/pdo/mysql.phpunit.xml b/tests/travis/pdo/mysql.phpunit.xml index 1a9030ddf83..6725c2e76af 100644 --- a/tests/travis/pdo/mysql.phpunit.xml +++ b/tests/travis/pdo/mysql.phpunit.xml @@ -1,26 +1,38 @@ - - + bootstrap="../../Bootstrap.php" + colors="true" + stopOnError="false" + stopOnFailure="false" + stopOnIncomplete="false" + stopOnSkipped="false" + beStrictAboutTestsThatDoNotTestAnything="false" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd"> + + + - - - ../../codeigniter - - - - + + + ../../codeigniter + + + + + + + ../../../system - - - \ No newline at end of file + ../../application + + + + ../../tests + + + diff --git a/tests/travis/pdo/mysql.phpunit_v9.xml b/tests/travis/pdo/mysql.phpunit_v9.xml new file mode 100644 index 00000000000..1a9030ddf83 --- /dev/null +++ b/tests/travis/pdo/mysql.phpunit_v9.xml @@ -0,0 +1,26 @@ + + + + + + + + + ../../codeigniter + + + + + ../../../system + + + \ No newline at end of file diff --git a/tests/travis/pdo/pgsql.phpunit.xml b/tests/travis/pdo/pgsql.phpunit.xml index 22261ee7d10..e1cffff2ed3 100644 --- a/tests/travis/pdo/pgsql.phpunit.xml +++ b/tests/travis/pdo/pgsql.phpunit.xml @@ -1,26 +1,38 @@ - - + bootstrap="../../Bootstrap.php" + colors="true" + stopOnError="false" + stopOnFailure="false" + stopOnIncomplete="false" + stopOnSkipped="false" + beStrictAboutTestsThatDoNotTestAnything="false" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd"> + + - - - ../../codeigniter - - - - + + + ../../codeigniter + + + + + + + + ../../../system - - - \ No newline at end of file + ../../application + + + ../../tests + + + diff --git a/tests/travis/pdo/pgsql.phpunit_v9.xml b/tests/travis/pdo/pgsql.phpunit_v9.xml new file mode 100644 index 00000000000..22261ee7d10 --- /dev/null +++ b/tests/travis/pdo/pgsql.phpunit_v9.xml @@ -0,0 +1,26 @@ + + + + + + + + + ../../codeigniter + + + + + ../../../system + + + \ No newline at end of file diff --git a/tests/travis/pdo/sqlite.phpunit.xml b/tests/travis/pdo/sqlite.phpunit.xml index 4b0ca2fe799..9f2e6497ac4 100644 --- a/tests/travis/pdo/sqlite.phpunit.xml +++ b/tests/travis/pdo/sqlite.phpunit.xml @@ -1,26 +1,38 @@ - - + bootstrap="../../Bootstrap.php" + colors="true" + stopOnError="false" + stopOnFailure="false" + stopOnIncomplete="false" + stopOnSkipped="false" + beStrictAboutTestsThatDoNotTestAnything="false" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd"> + + + - - - ../../codeigniter - - - - + + + ../../codeigniter + + + + + + + ../../../system - - - \ No newline at end of file + ../../application + + + + ../../tests + + + diff --git a/tests/travis/pdo/sqlite.phpunit_v9.xml b/tests/travis/pdo/sqlite.phpunit_v9.xml new file mode 100644 index 00000000000..4b0ca2fe799 --- /dev/null +++ b/tests/travis/pdo/sqlite.phpunit_v9.xml @@ -0,0 +1,26 @@ + + + + + + + + + ../../codeigniter + + + + + ../../../system + + + \ No newline at end of file diff --git a/tests/travis/pgsql.phpunit.xml b/tests/travis/pgsql.phpunit.xml index 8d7979a0f2d..941c5234b51 100644 --- a/tests/travis/pgsql.phpunit.xml +++ b/tests/travis/pgsql.phpunit.xml @@ -1,26 +1,32 @@ - - + + - - - ../codeigniter - - - - + + + ../codeigniter + + + + + ../../system - - + ../../application + + + + ../../tests + + \ No newline at end of file diff --git a/tests/travis/pgsql.phpunit_v9.xml b/tests/travis/pgsql.phpunit_v9.xml new file mode 100644 index 00000000000..8d7979a0f2d --- /dev/null +++ b/tests/travis/pgsql.phpunit_v9.xml @@ -0,0 +1,26 @@ + + + + + + + + + ../codeigniter + + + + + ../../system + + + \ No newline at end of file diff --git a/tests/travis/sqlite.phpunit.xml b/tests/travis/sqlite.phpunit.xml index 6d2409cfd0d..4a577fa0415 100644 --- a/tests/travis/sqlite.phpunit.xml +++ b/tests/travis/sqlite.phpunit.xml @@ -1,27 +1,32 @@ - - - - ../../system - - - - - - - - ../codeigniter - - - + + + + + + + + ../codeigniter + + + + + + ../../system + ../../application + + + + ../../tests + + + \ No newline at end of file diff --git a/tests/travis/sqlite.phpunit_v9.xml b/tests/travis/sqlite.phpunit_v9.xml new file mode 100644 index 00000000000..6d2409cfd0d --- /dev/null +++ b/tests/travis/sqlite.phpunit_v9.xml @@ -0,0 +1,27 @@ + + + + + ../../system + + + + + + + + ../codeigniter + + +