close();
}
@@ -512,7 +514,7 @@ private function rtf2text($filename)
}
$ansa = str_replace('{', '', $ansa);
$ansa = str_replace('}', '', $ansa);
- return utf8_encode($ansa);
+ return mb_convert_encoding($ansa, 'UTF-8', 'ISO-8859-1');
}
private function getpgraph()
From 7cdd5f16c8bcdfae25b420de4adebbd81f6b9f3b Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Sat, 30 May 2026 16:30:00 +0200
Subject: [PATCH 07/18] Remove legacy PHP compatibility code without session
login regression
---
lib/AJAXInterface.php | 7 +++++--
lib/Session.php | 37 +------------------------------------
2 files changed, 6 insertions(+), 38 deletions(-)
diff --git a/lib/AJAXInterface.php b/lib/AJAXInterface.php
index fc22b077c..4838bf827 100755
--- a/lib/AJAXInterface.php
+++ b/lib/AJAXInterface.php
@@ -203,8 +203,11 @@ public function __construct()
{
/* Give the session a unique name to avoid conflicts and start the
* session. */
- @session_name(CATS_SESSION_NAME);
- session_start();
+ if (session_status() !== PHP_SESSION_ACTIVE)
+ {
+ @session_name(CATS_SESSION_NAME);
+ session_start();
+ }
/* Validate the session. */
if (!$this->isSessionLoggedIn())
diff --git a/lib/Session.php b/lib/Session.php
index 429128868..afeb3cef6 100755
--- a/lib/Session.php
+++ b/lib/Session.php
@@ -873,7 +873,7 @@ public function processLogin($username, $password, $addToHistory = true)
if (strlen($rs['columnPreferences']) > 0 && $this->_isDemo == false)
{
- $this->_ = unserialize($rs['columnPreferences']);
+ $this->_dataGridColumnPreferences = unserialize($rs['columnPreferences']);
}
else
{
@@ -913,36 +913,6 @@ public function processLogin($username, $password, $addToHistory = true)
);
$rs = $db->query($sql);
}
- // Start output buffering to prevent "Headers Already Sent" errors
- ob_start();
-
- $cookieValue = $this->getCookie();
- $expires = time() + 3600; // Example expiration time, adjust as needed
- $path = '/';
- // $domain = 'example.com'; // Adjust as needed
- $secure = true; // Adjust based on your environment
- $httponly = true;
- $samesite = 'Lax';
-
- // Fixed setcookie call - define domain variable and remove invalid path format
- $domain = ''; // Use empty string for current domain
- // TODO: Drop PHP < 7.3 fallback and always use setcookie() options array once legacy PHP is no longer supported.
- if (PHP_VERSION_ID >= 70300)
- {
- setcookie('session_cookie', $cookieValue, array(
- 'expires' => $expires,
- 'path' => $path,
- 'domain' => $domain,
- 'secure' => $secure,
- 'httponly' => $httponly,
- 'samesite' => $samesite
- ));
- }
- else
- {
- setcookie('session_cookie', $cookieValue, $expires, $path, $domain, $secure, $httponly);
- }
-
// Update the user session in the database
$sql = sprintf(
"UPDATE
@@ -956,11 +926,6 @@ public function processLogin($username, $password, $addToHistory = true)
$db->makeQueryString($this->_userID),
$this->_siteID
);
-
- // Flush the output buffer and send the output to the browser
- ob_end_flush();
-
-
$rs = $db->query($sql);
break;
From 13f7c7aa9d12c3a6e448a4261eebd261a81b4a84 Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Sat, 30 May 2026 16:30:00 +0200
Subject: [PATCH 08/18] Update checkPHPVersion to PHP 8.5.0+
---
lib/InstallationTests.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/InstallationTests.php b/lib/InstallationTests.php
index 68a136c5f..91aaae096 100755
--- a/lib/InstallationTests.php
+++ b/lib/InstallationTests.php
@@ -155,7 +155,7 @@ public static function printCATSVersion()
/* Check PHP version. */
public static function checkPHPVersion()
{
- if (!self::DEBUG_FAIL && version_compare(PHP_VERSION, '5.0.0', '>='))
+ if (!self::DEBUG_FAIL && version_compare(PHP_VERSION, '8.5.0', '>='))
{
echo sprintf(
'| PHP version is %s. |
',
@@ -166,7 +166,7 @@ public static function checkPHPVersion()
}
echo sprintf(
- 'PHP 5.0.0 or greater is required to run OpenCATS. '
+ ' |
PHP 8.5.0 or greater is required to run OpenCATS. '
. 'Found version: %s. |
',
PHP_VERSION
);
From 6afa286d286e983c0d982c6f5e94ba0cc28d6cc2 Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Sat, 30 May 2026 18:00:59 +0200
Subject: [PATCH 09/18] Fix BrowserKit client signature for Symfony 8
---
src/OpenCATS/Tests/Behat/BrowserKit/StreamHttpBrowser.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/OpenCATS/Tests/Behat/BrowserKit/StreamHttpBrowser.php b/src/OpenCATS/Tests/Behat/BrowserKit/StreamHttpBrowser.php
index a6a3d12bc..3a80f0998 100644
--- a/src/OpenCATS/Tests/Behat/BrowserKit/StreamHttpBrowser.php
+++ b/src/OpenCATS/Tests/Behat/BrowserKit/StreamHttpBrowser.php
@@ -13,7 +13,7 @@ public function __construct(array $serverParameters = array())
parent::__construct($serverParameters);
}
- protected function doRequest($request)
+ protected function doRequest(object $request): object
{
if (!$request instanceof Request) {
throw new \InvalidArgumentException('Unsupported request object.');
From 36aeebbf63ed0bbd6e06a795cd938e28281d454f Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Mon, 1 Jun 2026 13:55:25 +0200
Subject: [PATCH 10/18] Update database compatibility for PHP 8.5
---
lib/DatabaseConnection.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/DatabaseConnection.php b/lib/DatabaseConnection.php
index 054a3ae4b..5a4b8c25c 100755
--- a/lib/DatabaseConnection.php
+++ b/lib/DatabaseConnection.php
@@ -483,6 +483,7 @@ public function escapeString($string)
// user input. For instance see:
// https://johnroach.info/2011/02/17/why-mysql_real_escape_string-isnt-enough-to-stop-sql-injection-attacks/
// To be replaced with Symfony's stack
+ $string = (string) $string;
return mysqli_real_escape_string($this->_connection, $string);
}
From 93af4dba0952a48e3dd0541e1d7139b40b127f88 Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Mon, 1 Jun 2026 13:55:26 +0200
Subject: [PATCH 11/18] Fix PHP 8.5 deprecations in Behat tests
---
test/features/bootstrap/FeatureContext.php | 1 -
1 file changed, 1 deletion(-)
diff --git a/test/features/bootstrap/FeatureContext.php b/test/features/bootstrap/FeatureContext.php
index 71fa74c62..a9cbba8fb 100644
--- a/test/features/bootstrap/FeatureContext.php
+++ b/test/features/bootstrap/FeatureContext.php
@@ -191,7 +191,6 @@ protected function getWsendUser()
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$wsendUser = curl_exec($curl);
- curl_close($curl);
return $wsendUser;
}
From 6b40c254d2e5c62cb2d72896ce402b55ded32244 Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Mon, 1 Jun 2026 13:13:10 +0200
Subject: [PATCH 12/18] Fix PHP 8.5 deprecation casts in core libs
---
lib/CATSUtility.php | 2 +-
lib/DatabaseConnection.php | 4 ++--
lib/Session.php | 2 +-
lib/Users.php | 8 ++++----
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/lib/CATSUtility.php b/lib/CATSUtility.php
index f4450c411..85fda1936 100755
--- a/lib/CATSUtility.php
+++ b/lib/CATSUtility.php
@@ -127,7 +127,7 @@ public static function getBuild()
$data = substr($data, strpos($data, "dir\n") + 4);
$data = substr($data, 0, strpos($data, "\n"));
- return (integer) $data;
+ return (int) $data;
}
}
diff --git a/lib/DatabaseConnection.php b/lib/DatabaseConnection.php
index 5a4b8c25c..689d003d1 100755
--- a/lib/DatabaseConnection.php
+++ b/lib/DatabaseConnection.php
@@ -533,7 +533,7 @@ public function makeQueryIntegerOrNULL($value)
return 'NULL';
}
- return (integer) $value;
+ return (int) $value;
}
/**
@@ -546,7 +546,7 @@ public function makeQueryIntegerOrNULL($value)
*/
public function makeQueryInteger($value)
{
- return (integer) $value;
+ return (int) $value;
}
/**
diff --git a/lib/Session.php b/lib/Session.php
index afeb3cef6..3b0a89131 100755
--- a/lib/Session.php
+++ b/lib/Session.php
@@ -820,7 +820,7 @@ public function processLogin($username, $password, $addToHistory = true)
$this->_userLicenses = $rs['userLicenses'];
$this->_accessLevel = $rs['accessLevel'];
$this->_realAccessLevel = $rs['accessLevel'];
- $this->_categories = explode(',', $rs['categories']);
+ $this->_categories = explode(',', (string) $rs['categories']);
$this->_isASP = ($rs['companyID'] != 0 ? true : false);
$this->_isHrMode = ($rs['isHrMode'] != 0 ? true : false);
$this->_siteCompanyID = ($rs['companyID'] != 0 ? $rs['companyID'] : -1);
diff --git a/lib/Users.php b/lib/Users.php
index 9e83079c7..1d2fc7946 100644
--- a/lib/Users.php
+++ b/lib/Users.php
@@ -187,7 +187,7 @@ public function update($userID, $lastName, $firstName, $email,
$this->_siteID
);
- return (boolean) $this->_db->query($sql);
+ return (bool) $this->_db->query($sql);
}
/**
@@ -213,7 +213,7 @@ public function updateSelfEmail($userID, $email)
$this->_siteID
);
- return (boolean) $this->_db->query($sql);
+ return (bool) $this->_db->query($sql);
}
/**
@@ -239,7 +239,7 @@ public function updateCategories($userID, $categories)
$this->_siteID
);
- return (boolean) $this->_db->query($sql);
+ return (bool) $this->_db->query($sql);
}
/**
@@ -436,7 +436,7 @@ public function updateForAdministration($userID, $firstName, $lastName, $email,
$aspSiteRule
);
- return (boolean) $this->_db->query($sql);
+ return (bool) $this->_db->query($sql);
}
/**
From ed62f52041faf4424dbda9029d412134eb8d547f Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Mon, 1 Jun 2026 13:55:36 +0200
Subject: [PATCH 13/18] Update legacy runtime compatibility for PHP 8.5
---
lib/MRU.php | 3 ++-
lib/ModuleUtility.php | 2 +-
lib/Template.php | 1 +
modules/settings/SettingsUI.php | 1 +
4 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/lib/MRU.php b/lib/MRU.php
index 0ab989b73..99f68ea60 100755
--- a/lib/MRU.php
+++ b/lib/MRU.php
@@ -157,7 +157,8 @@ public function getFormatted()
}
return implode(
- $HTML, ' | '
+ ' | ',
+ $HTML
);
}
diff --git a/lib/ModuleUtility.php b/lib/ModuleUtility.php
index d35c42c0e..5d1dee215 100755
--- a/lib/ModuleUtility.php
+++ b/lib/ModuleUtility.php
@@ -295,7 +295,7 @@ private static function _refreshModuleList()
$_SESSION['hooks'] = $hooks;
/* Sort the modules. */
- uksort($modules , array('self', '_sortModules'));
+ uksort($modules, array(self::class, '_sortModules'));
/* Verify that core modules are present. */
self::_checkCoreModules($modules);
diff --git a/lib/Template.php b/lib/Template.php
index e30a11036..199a37ef0 100755
--- a/lib/Template.php
+++ b/lib/Template.php
@@ -35,6 +35,7 @@
* @package CATS
* @subpackage Library
*/
+#[AllowDynamicProperties]
class Template
{
private $_templateFile;
diff --git a/modules/settings/SettingsUI.php b/modules/settings/SettingsUI.php
index cdf07fd2c..95b271885 100755
--- a/modules/settings/SettingsUI.php
+++ b/modules/settings/SettingsUI.php
@@ -55,6 +55,7 @@ class SettingsUI extends UserInterface
{
/* Maximum number of login history entries to display on User Details. */
const MAX_RECENT_LOGINS = 15;
+ private $_realAccessLevel = null;
public function __construct()
From c15d194c29d67263d7f0025c9e006f977dc7588b Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Mon, 1 Jun 2026 13:55:36 +0200
Subject: [PATCH 14/18] Stabilize Behat company autocomplete wait
---
test/features/bootstrap/FeatureContext.php | 58 +++++++++++++++++++++-
1 file changed, 57 insertions(+), 1 deletion(-)
diff --git a/test/features/bootstrap/FeatureContext.php b/test/features/bootstrap/FeatureContext.php
index a9cbba8fb..e5a3bdb64 100644
--- a/test/features/bootstrap/FeatureContext.php
+++ b/test/features/bootstrap/FeatureContext.php
@@ -82,6 +82,9 @@ public function iAmSpoofingASessionWithCookie($cookieValue)
public function iWaitFor($element)
{
$this->spins(function() use ($element) {
+ if ($element === '#CompanyResults div#suggest0') {
+ $this->activateCompanySuggestionLookup();
+ }
$field = $this->getSession()->getPage()->find('css', $element);
if (null === $field) {
throw new Exception('form field ' . $element . 'id|name|label|value|placeholder');
@@ -337,7 +340,12 @@ public function iClickOnTheElement($locator)
private function clickOnTheElement($locator, $retries = 15)
{
$element = $this->getSession()->getPage()->find('css', $locator); // runs the actual query and returns the element
- // errors must not pass silently
+ if (null === $element && $locator === '#CompanyResults div#suggest0') {
+ $this->activateCompanySuggestionLookup();
+ sleep(1);
+ $element = $this->getSession()->getPage()->find('css', $locator);
+ }
+
if (null === $element) {
throw new \InvalidArgumentException(sprintf('Could not evaluate CSS selector: "%s"', $locator));
}
@@ -354,6 +362,54 @@ private function clickOnTheElement($locator, $retries = 15)
}
}
}
+
+ private function activateCompanySuggestionLookup()
+ {
+ $script = <<<'JS'
+(function () {
+ var field = document.getElementById('companyName');
+ if (!field) {
+ return false;
+ }
+
+ field.focus();
+
+ if (typeof suggestListActivate === 'function' && typeof sessionCookie !== 'undefined') {
+ suggestListActivate(
+ 'getCompanyNames',
+ 'companyName',
+ 'CompanyResults',
+ 'companyID',
+ 'ajaxTextEntryHover',
+ 0,
+ sessionCookie,
+ 'helpShim'
+ );
+ }
+
+ if (typeof Event === 'function') {
+ field.dispatchEvent(new Event('input', { bubbles: true }));
+ field.dispatchEvent(new Event('keyup', { bubbles: true }));
+ } else {
+ var inputEvent = document.createEvent('Event');
+ inputEvent.initEvent('input', true, true);
+ field.dispatchEvent(inputEvent);
+
+ var keyupEvent = document.createEvent('Event');
+ keyupEvent.initEvent('keyup', true, true);
+ field.dispatchEvent(keyupEvent);
+ }
+
+ if (typeof suggestListPopulate === 'function' && typeof sessionCookie !== 'undefined') {
+ suggestListPopulate(0, sessionCookie, field.value, maxInitialResults, -1);
+ }
+
+ return true;
+}());
+JS;
+
+ $this->getSession()->executeScript($script);
+ }
/**
* @When I select :option in the :selectLocator select
From 9910fd52795a3e65a21b6d2b3c8dcef44edeb498 Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Fri, 3 Jul 2026 13:33:06 +0200
Subject: [PATCH 15/18] Add PHP 8.4.1 to CI matrix
---
.github/workflows/ci.yml | 2 +-
composer.json | 2 +-
composer.lock | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 89de5b513..57705bdba 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- php-version: ['8.5']
+ php-version: ['8.4.1', '8.5']
steps:
- name: Checkout Code
diff --git a/composer.json b/composer.json
index c9a7fc7d8..9b1ad19ae 100644
--- a/composer.json
+++ b/composer.json
@@ -3,7 +3,7 @@
"description": "Free and open source candidate/applicant tracking system for recruiters.",
"type": "project",
"require": {
- "php": "^8.5",
+ "php": "^8.4.1",
"ext-mbstring": "*",
"ckeditor/ckeditor": "^4.25.1",
"neutron/sphinxsearch-api": "^2.0.8",
diff --git a/composer.lock b/composer.lock
index ae6d1a5be..5a93edbfa 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "0a84df1a04c171fb550848f858821e21",
+ "content-hash": "e47601dbabbd4ec22ab12bf1463ba507",
"packages": [
{
"name": "ckeditor/ckeditor",
@@ -4625,7 +4625,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": "^8.5",
+ "php": "^8.4.1",
"ext-mbstring": "*"
},
"platform-dev": {},
From 1992dd74ea06eacc66cf70e7dc6a8b0a647c55db Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Fri, 3 Jul 2026 14:08:26 +0200
Subject: [PATCH 16/18] Fix PHP 8.4 tag deprecation breaking redirects
---
lib/Tags.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/Tags.php b/lib/Tags.php
index dec579f6d..ca200cf7d 100644
--- a/lib/Tags.php
+++ b/lib/Tags.php
@@ -109,7 +109,7 @@ public function delete($tagID)
}
- public function add($parent_tag_id=null, $title, $description)
+ public function add($parent_tag_id, $title, $description)
{
$sql = sprintf(
"INSERT INTO
From c25fe7254b174e6f875cacad2e7c25e030c14ef4 Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Fri, 3 Jul 2026 14:12:52 +0200
Subject: [PATCH 17/18] Align installer PHP requirement with Composer
---
lib/InstallationTests.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/InstallationTests.php b/lib/InstallationTests.php
index 91aaae096..74cb31c86 100755
--- a/lib/InstallationTests.php
+++ b/lib/InstallationTests.php
@@ -155,7 +155,7 @@ public static function printCATSVersion()
/* Check PHP version. */
public static function checkPHPVersion()
{
- if (!self::DEBUG_FAIL && version_compare(PHP_VERSION, '8.5.0', '>='))
+ if (!self::DEBUG_FAIL && version_compare(PHP_VERSION, '8.4.1', '>='))
{
echo sprintf(
'| PHP version is %s. |
',
@@ -166,7 +166,7 @@ public static function checkPHPVersion()
}
echo sprintf(
- 'PHP 8.5.0 or greater is required to run OpenCATS. '
+ ' |
PHP 8.4.1 or greater is required to run OpenCATS. '
. 'Found version: %s. |
',
PHP_VERSION
);
From f5d2dd2468cdf17d70526af2506aad08d33d7914 Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Fri, 3 Jul 2026 14:13:56 +0200
Subject: [PATCH 18/18] Default local CI to minimum PHP version
---
docker/docker-compose-test.yml | 2 +-
docker/php/Dockerfile | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docker/docker-compose-test.yml b/docker/docker-compose-test.yml
index 1cfd981b9..cb967dfa6 100644
--- a/docker/docker-compose-test.yml
+++ b/docker/docker-compose-test.yml
@@ -14,7 +14,7 @@ services:
context: ..
dockerfile: docker/php/Dockerfile
args:
- PHP_VERSION: ${PHP_VERSION:-8.5}
+ PHP_VERSION: ${PHP_VERSION:-8.4.1}
working_dir: /var/www/public
volumes_from:
- opencatsdata
diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile
index b68ec4c21..590f3027f 100644
--- a/docker/php/Dockerfile
+++ b/docker/php/Dockerfile
@@ -1,4 +1,4 @@
-ARG PHP_VERSION=8.5
+ARG PHP_VERSION=8.4.1
FROM php:${PHP_VERSION}-fpm-alpine
# Runtime/system packages required by OpenCATS and helper tools.