Skip to content

Php8 compatibility fixes#411

Open
hupendrad4 wants to merge 11 commits into
TestLinkOpenSourceTRMS:testlink_1_9_20_fixedfrom
hupendrad4:php8-compatibility-fixes
Open

Php8 compatibility fixes#411
hupendrad4 wants to merge 11 commits into
TestLinkOpenSourceTRMS:testlink_1_9_20_fixedfrom
hupendrad4:php8-compatibility-fixes

Conversation

@hupendrad4
Copy link
Copy Markdown


Title:
fix(php8): PHP 8 compatibility fixes — count/sizeof/array_keys null safety, typo corrections, SQL guard


Body:

Summary

This PR fixes a series of PHP 8 compatibility issues that cause fatal errors
or broken behaviour when running TestLink 1.9.20 on PHP 8.0+. All fixes were
validated against a running PHP 8.1 + MySQL 8.0 environment (Docker Compose).


Problem

PHP 8 introduced strict type enforcement that breaks several patterns which
were silently tolerated in PHP 7:

PHP 7 behaviour PHP 8 behaviour
count(null)0 TypeError fatal
sizeof(null)0 TypeError fatal
array_keys(null)[] TypeError fatal
$var{N} string offset Removed — parse error
Undefined constant used as string Error fatal

In addition, two non-PHP-8 bugs were found and fixed:

  • A typo (arrya) cast in xmlrpc.class.php caused HTTP 500 on all XMLRPC
    API calls invoking getPlatforms()
  • An empty $users array in deleteUserRoles() generated IN() which is
    invalid SQL and crashed the query

Changes

1. Curly brace string offsets → bracket syntax

Files: third_party/kint/

PHP 8.0 removed $var{N} syntax for string offset access. Replaced all
occurrences with $var[N].


2. count() / sizeof() null safety — lib/

Files: 149 files across lib/

Wrapped variable and property-chain arguments with (array) cast:

// Before
count($someVariable)

// After
count((array)$someVariable)
Preserves PHP 7 behaviour (returns 0 for null) without changing logic
for non-null values.

---
3. Typo fix: (arrya) → (array) in xmlrpc.class.php

File: lib/api/xmlrpc/v1/xmlrpc.class.php

The cast (arrya) is a typo that PHP 8 treats as an undefined constant,
throwing a fatal Error. This caused HTTP 500 on any XMLRPC API call that
invokes getPlatforms(). Fixed to (array).

---
4. Bare constant lib → quoted string in REST example

File: lib/api/rest/v3/custom/api/RestApiCustomExample.class.php

PHP 8 removed silent treatment of undefined constants as strings. The
expression $ds. lib .$ds used lib as a bare word. Quoted as 'lib'.

---
5. count() null safety — third_party/

Files: 31 files across third_party/ (Zend, XML-RPC IXR, kint, daisydiff, etc.)

Same (array) cast guard applied to the bundled third-party libraries
for consistent PHP 8 compatibility.

---
6. array_keys() null safety — lib/

Files: 75 files across lib/

array_keys(null) throws TypeError in PHP 8. Applied (array) cast
across all variable and property-chain argument patterns in lib/.

---
7. array_keys() on method call returns — testplan.class.php

File: lib/functions/testplan.class.php

getPlatforms() and get_builds() can return null when no data exists.
Guards added specifically for method-call patterns not covered by bulk fix:
// Before
array_keys($this->getPlatforms(...))

// After
array_keys((array)$this->getPlatforms(...))

---
8. array_keys() on method call return — treeMenu.inc.php

File: lib/functions/treeMenu.inc.php

Same guard for $tplan_mgr->get_builds() which returns null when a test
plan has no builds.

---
9. array_keys() targeted fixes — 3 files

Files: requirement_mgr.class.php, cfieldsTprojectAssign.php, planImport.php

Three specific locations with non-standard patterns not covered by the bulk
regex pass:
- array_keys(current($rs)) — $rs may be null
- array_keys($argsObj->$serviceInput) — dynamic property access
- array_keys($linkedVersions[...]) — nested array element access

---
10. deleteUserRoles — guard against empty IN() SQL

File: lib/functions/testplan.class.php

When $users is an empty array the generated SQL becomes WHERE user_id IN()
which is invalid and causes a database error. Added !empty($users) alongside
the existing !is_null($users) guard so the query is skipped for empty sets:
// Before
if (!is_null($users))

// After
if (!is_null($users) && !empty($users))

---
11. Docker / containerised install: installUtils.php wildcard host

File: install/installUtils.php

When running in Docker or any setup where the application connects via a
bridge/proxy IP, MySQL CREATE USER and GRANT statements using the exact
hostname fail with Access denied. Changed host from '$safeDBHost' /
'localhost' to '%' to allow connections from any host — standard practice
for containerised deployments.

---
Testing

- PHP version: 8.1
- MySQL version: 8.0
- Environment: Docker Compose (php:8.1-apache + mysql:8.0)
- Tested flows: installation, login, test project creation, test case CRUD,
test plan execution, XMLRPC API (getPlatforms, getTestCases),
user management, requirements management, report generation

All previously failing endpoints return HTTP 200. No regressions observed
in tested flows.

---
Related

- Issue #399 — PHP 8.x adjustments (Part 1)
- PR #403 / #407 — (arrya) typo fix (this PR includes the same fix)

---

A few tips before you open the PR:
- Target branch: `testlink_1_9_20_fixed` (not `main`)
- The upstream maintainer (`fmancardi`) is active — last commit Dec 2025, so expect a response
- Fix #11 (Docker host) may get pushback since it weakens MySQL security — be ready to explain the Docker networking
context

hupendrad4 added 11 commits June 1, 2026 21:03
PHP 8.0 removed support for curly brace syntax to access string offsets.
Replace all occurrences of $var{N} with $var[N] across all PHP files.

Fixes fatal errors on PHP 8.0+ for any code using this deprecated syntax.
PHP 8 throws a TypeError when count() or sizeof() is called on null.
PHP 7 silently returned 0. Wrap variable arguments with (array) cast
to preserve the PHP 7 behaviour safely on PHP 8+.

Affected: all PHP files under lib/ using variable or property-chain
arguments to count()/sizeof().
The cast (arrya) is a typo causing a PHP fatal error: undefined constant
'arrya'. This caused HTTP 500 on any XMLRPC API call that invokes
getPlatforms(). Changed to correct (array) cast.
PHP 8 removed silent treatment of undefined constants as strings.
The expression $ds. lib .$ds used the bare word 'lib' as an undefined
constant, which throws an Error in PHP 8. Quoted as 'lib' string.
PHP 8 throws TypeError when count() is called on null. Apply the same
(array) cast guard to the bundled XML-RPC IXR library under third_party/
to prevent fatal errors during XMLRPC API operations.
array_keys(null) throws a TypeError in PHP 8. Wrap variable and
property-chain arguments with (array) cast across all PHP files in lib/
to safely handle cases where the value may be null.
These methods can return null; array_keys(null) throws TypeError in PHP 8.
Cast return values to array before passing to array_keys().
get_builds() can return null when no builds exist for a test plan.
array_keys(null) throws TypeError in PHP 8. Cast to array first.
…, planImport

Three targeted fixes for locations not covered by the bulk pattern:
- requirement_mgr.class.php: array_keys(current($rs)) where $rs may be null
- cfieldsTprojectAssign.php: array_keys on dynamic property $argsObj->$serviceInput
- planImport.php: array_keys on $linkedVersions array element
When $users is an empty array, the generated SQL becomes IN() which is
invalid and causes a database error. Added !empty($users) guard alongside
the existing !is_null() check so the query is skipped entirely for empty sets.
…ents

When running in Docker or any environment where the app connects via a
bridge/proxy IP rather than 'localhost', MySQL user grants using the
exact hostname fail with 'Access denied'. Using @'%' allows connections
from any host and is standard practice for containerised deployments.

Fixes TestLink installation failure in Docker Compose setups.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant