Joomla PHP & MySQL changes: Joomla 3 → 4, 5 and 6

18 issues in this guide

PHP and MySQL sit under every Joomla upgrade. This guide groups 18 catalog entries on runtime and driver breaks that kill a site before namespaced CMS classes matter. Covered calls and symbols include JArrayHelper, JCryptCipherMcrypt, JString, JStringInflector, DS, $this->foo = 1 on an untyped class, each($array), eval($code), extract($_REQUEST), optional, and 8 more. The shared replacement pattern is: stop calling removed PHP functions, declare properties or use AllowDynamicProperties where you still need dynamic state, talk to the database only through Joomla DatabaseInterface / mysqli / PDO, and import Joomla\String\StringHelper or Joomla\Utilities\ArrayHelper instead of J-prefixed globals. Hop mix in this data: 16 at Joomla 3 → 4; 1 at Joomla 4 → 5; 1 at Joomla 5 → 6. Work each entry below for the exact before/after code, error strings, and symptoms from the catalog. Nothing here invents upgrade statistics beyond those entries. Catalog replacements in this guide include JArrayHelper → Joomla\Utilities\ArrayHelper; JCryptCipherMcrypt → CryptCipherCrypto / Sodium / OpenSSL; JString → Joomla\String\StringHelper; JStringInflector → Joomla\String\Inflector / Normalise. Open each h2 anchor for the full before/after, fix list, and error strings shipped in the reference data.

JARRAYHELPER-CLASS-001

JArrayHelper

JArrayHelper

JArrayHelper left core. Treat it as gone on Joomla 4 even if a leftover map appears — ship ArrayHelper.

JArrayHelper → Joomla\Utilities\ArrayHelper

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Removed — fatalJ6: Removed — fatalBC plugin: no — already dead on J4Mechanical renamePHP

What you will see

  • Class "JArrayHelper" not found

Symptoms people search

toInteger fatal · array helper missing

Replace this code

Swap JArrayHelper for Joomla\Utilities\ArrayHelper.

Joomla 3

Remove or stop calling this

JArrayHelper::toInteger($cid);
JArrayHelper::fromObject($obj);

Joomla 4 / 6

Use this instead

use Joomla\Utilities\ArrayHelper;

ArrayHelper::toInteger($cid);

How to fix it

  1. 1Replace JArrayHelper with Joomla\Utilities\ArrayHelper.

Also known as

JArrayHelper · ArrayHelper

JCRYPT-MCRYPT-001

JCryptCipherMcrypt

JCryptCipherMcrypt / 3Des / Blowfish / Simple

Weak and mcrypt-based ciphers were removed. PHP mcrypt is gone too.

JCryptCipherMcrypt → CryptCipherCrypto / Sodium / OpenSSL

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Removed — fatalJ6: Removed — fatalBC plugin: no — already dead on J4Architecture change — no auto-fixPHP

What you will see

  • Class "JCryptCipherMcrypt" not found
  • Class "JCryptCipher3Des" not found
  • Call to undefined function mcrypt_encrypt

Symptoms people search

cannot decrypt config · mcrypt

Replace this code

Swap JCryptCipherMcrypt for CryptCipherCrypto / Sodium / OpenSSL.

Joomla 3

Remove or stop calling this

$crypt = new JCrypt(new JCryptCipherMcrypt());

Joomla 4 / 6

Use this instead

Use Joomla\CMS\Crypt\Cipher\CryptoCipher. Do not use mcrypt.

How to fix it

  1. 1Re-encrypt data with Sodium/OpenSSL ciphers.
  2. 2You cannot decrypt old mcrypt blobs without a one-off migrator.

Also known as

mcrypt · JCryptCipher3Des · JCryptCipherBlowfish · JCryptCipherSimple · hasStrongPasswordSupport

There is no 1:1 swap for this one. The architecture changed. If you cannot rewrite it, Infyways can.

JSTRING-CLASS-001

JString

JString / StringHelper

JString was removed in Joomla 4. There is no reliable class alias. UTF-8 string helpers live in the joomla/string package.

JString → Joomla\String\StringHelper

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Removed — fatalJ6: Removed — fatalBC plugin: no — already dead on J4Mechanical renamePHP

What you will see

  • Class "JString" not found
  • Call to undefined method JString::

Symptoms people search

utf8 strlen broken · JString after Joomla 4

Replace this code

Swap JString for Joomla\String\StringHelper.

Joomla 3

Remove or stop calling this

echo JString::strtoupper($name);
$len = JString::strlen($utf8);

Joomla 4 / 6

Use this instead

use Joomla\String\StringHelper;

echo StringHelper::strtoupper($name);

How to fix it

  1. 1Replace JString with StringHelper.
  2. 2Add use Joomla\String\StringHelper.

Also known as

JString · StringHelper · phputf8

JSTRINGINFLECTOR-001

JStringInflector

JStringInflector / JStringNormalise

These JString satellites were obsolete in Joomla 4. Use the String package classes.

JStringInflector → Joomla\String\Inflector / Normalise

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Removed — fatalJ6: Removed — fatalBC plugin: no — already dead on J4Mechanical renamePHP

What you will see

  • Class "JStringInflector" not found
  • Class "JStringNormalise" not found

Symptoms people search

pluralize helper fatal

Replace this code

Swap JStringInflector for Joomla\String\Inflector / Normalise.

Joomla 3

Remove or stop calling this

JStringInflector::getInstance()->toPlural('item');
JStringNormalise::toDashSeparated($s);

Joomla 4 / 6

Use this instead

use Joomla\String\Inflector;
use Joomla\String\Normalise;

How to fix it

  1. 1Switch Inflector and Normalise to the Joomla\String namespace.

Also known as

JStringInflector · JStringNormalise

PHP-DS-001

DS

DS path constant

DS was a Joomla shortcut for the directory separator. It was removed in Joomla 4. PHP already has DIRECTORY_SEPARATOR. Forward slashes work on Windows in PHP.

DS → DIRECTORY_SEPARATOR or /

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Removed — fatalJ6: Removed — fatalBC plugin: no — already dead on J4Mechanical renamePHP

What you will see

  • Undefined constant DS
  • Use of undefined constant DS

Symptoms people search

helper path broken · failed to open stream after upgrade

Replace this code

Swap DS for DIRECTORY_SEPARATOR or /.

Joomla 3

Remove or stop calling this

$path = JPATH_COMPONENT . DS . 'helpers' . DS . 'foo.php';

Joomla 4 / 6

Use this instead

$path = JPATH_COMPONENT . '/helpers/foo.php';

How to fix it

  1. 1Replace DS with '/' inside Joomla paths.
  2. 2Or use DIRECTORY_SEPARATOR if you concatenate OS paths.

Also known as

DS · directory separator

PHP-DYNAMIC-PROP-001

$this->foo = 1 on an untyped class

Dynamic PHP properties (PHP 8.2+)

Joomla 5/6 run on PHP 8.2–8.4. Creating undeclared properties is deprecated and will be removed. Joomla 3 code did this constantly.

$this->foo = 1 on an untyped class → Declare the property or use AllowDynamicProperties

Dies at Joomla 4 → 5
Dies at Joomla 4 → 5J3: Works nativelyJ4: Changed behaviourJ6: Changed behaviourBC plugin: not applicablePatch possible — review itPHP

What you will see

  • Creation of dynamic property
  • Deprecated: Creation of dynamic property

Symptoms people search

php 8.2 deprecation flood · dynamic property

Replace this code

Swap $this->foo = 1 on an untyped class for Declare the property or use AllowDynamicProperties.

Joomla 3

Remove or stop calling this

class LmsHelper {
    public function set($k, $v) { $this->$k = $v; }
}

Joomla 4 / 6

Use this instead

class LmsHelper {
    public string $name;
}
// or #[\AllowDynamicProperties] as a last resort

How to fix it

  1. 1Declare properties on the class.
  2. 2Avoid #[AllowDynamicProperties] except as a temporary bridge.

Also known as

dynamic property · AllowDynamicProperties

PHP-EACH-001

each($array)

each() / create_function() / ereg / split

These PHP functions were removed long before Joomla 6. Joomla 4 already needs PHP 7.2+; Joomla 5 needs 8.1; Joomla 6 needs 8.3. Old extension code still contains them.

each($array) → foreach / closures / preg_*

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Removed — fatalJ6: Removed — fatalBC plugin: no — already dead on J4Patch possible — review itPHP

What you will see

  • Call to undefined function each()
  • Call to undefined function create_function()
  • Call to undefined function ereg()
  • Call to undefined function split()
  • Call to undefined function get_magic_quotes_gpc()

Symptoms people search

php 8 fatal in a helper file

Replace this code

Swap each($array) for foreach / closures / preg_*.

Joomla 3

Remove or stop calling this

while (list($k, $v) = each($rows)) { }
$fn = create_function('$a', 'return $a;');

Joomla 4 / 6

Use this instead

foreach ($rows as $k => $v) { }
$fn = static fn ($a) => $a;

How to fix it

  1. 1each() → foreach.
  2. 2create_function() → a closure.
  3. 3ereg/split → preg_match / explode / preg_split.
  4. 4get_magic_quotes_gpc() → delete; filter input instead.

Also known as

each · create_function · ereg · split · magic_quotes

PHP-EVAL-001

eval($code)

eval() in extensions

eval still runs. It is an RCE footgun. Joomla 6 migrations should remove it, not preserve it.

eval($code) → Never — forbidden for Infyways work

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Works nativelyJ6: Works nativelyBC plugin: not applicableArchitecture change — no auto-fixPHP

Symptoms people search

eval in plugin · custom php param

Replace this code

Swap eval($code) for Never — forbidden for Infyways work.

Joomla 3

Remove or stop calling this

eval($params->get('customphp'));

Joomla 4 / 6

Use this instead

// Delete eval. If you need configurable logic, use a real PHP class, not stored strings.

How to fix it

  1. 1Remove eval.
  2. 2Rewrite as proper code.

Also known as

eval(

There is no 1:1 swap for this one. The architecture changed. If you cannot rewrite it, Infyways can.

PHP-EXTRACT-001

extract($_REQUEST)

extract($_GET) / extract($_POST)

extract on superglobals overwrites local variables. It still runs on Joomla 6 and is still wrong.

extract($_REQUEST) → Input API, named variables

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Works nativelyJ6: Works nativelyBC plugin: not applicableArchitecture change — no auto-fixPHP

Symptoms people search

extract post · variable overwrite

Replace this code

Swap extract($_REQUEST) for Input API, named variables.

Joomla 3

Remove or stop calling this

extract($_POST);
echo $title;

Joomla 4 / 6

Use this instead

$title = $app->getInput()->getString('title');

How to fix it

  1. 1Delete extract($_GET/$_POST/$_REQUEST).
  2. 2Read keys through Input.

Also known as

extract($_POST) · extract($_GET) · extract($_REQUEST)

There is no 1:1 swap for this one. The architecture changed. If you cannot rewrite it, Infyways can.

PHP-GMP-001

optional

PHP gmp extension (WebAuthn)

Joomla 4+ WebAuthn/passkeys need GMP. Upgrades fail or MFA breaks without it.

optional → Enable ext-gmp on PHP 8.1+

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Changed behaviourJ6: Changed behaviourBC plugin: not applicableArchitecture change — no auto-fixPHP

What you will see

  • Class "GMP" not found
  • gmp_init()

Symptoms people search

passkey error · webauthn · gmp

Replace this code

Swap optional for Enable ext-gmp on PHP 8.1+.

Joomla 3

Remove or stop calling this

// Joomla 3 sites often omitted gmp

Joomla 4 / 6

Use this instead

php.ini: extension=gmp  (required for passkeys / WebAuthn on many J4+ hosts)

How to fix it

  1. 1Enable gmp (and sodium/openssl).
  2. 2Confirm with phpinfo on staging.

Also known as

gmp · WebAuthn · passkeys

PHP-JEXEC-001

defined('_JEXEC') or die;

defined('_JEXEC') or die;

This is not deprecated. Direct-hit PHP files without it are a security hole on every Joomla version. Do not delete it while “cleaning J3”.

defined('_JEXEC') or die; → Keep it on every entry PHP file

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Works nativelyJ6: Works nativelyBC plugin: not applicableArchitecture change — no auto-fixPHP

Symptoms people search

restricted access · _JEXEC

Replace this code

Swap defined('_JEXEC') or die; for Keep it on every entry PHP file.

Joomla 3

Remove or stop calling this

defined('_JEXEC') or die('Restricted access');

Joomla 4 / 6

Use this instead

defined('_JEXEC') or die;

How to fix it

  1. 1Keep _JEXEC on every web-reachable PHP file except the real entry points.

Also known as

_JEXEC · defined('_JEXEC') · Restricted access

PHP-JPATH-PLATFORM-001

JPATH_PLATFORM

JPATH_PLATFORM

JPATH_PLATFORM pointed at the old libraries tree. That layout is gone.

JPATH_PLATFORM → Do not use — autoload instead

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Removed — fatalJ6: Removed — fatalBC plugin: no — already dead on J4You rewrite thisPHP

What you will see

  • Undefined constant JPATH_PLATFORM

Symptoms people search

library path fatal

Replace this code

Swap JPATH_PLATFORM for Do not use — autoload instead.

Joomla 3

Remove or stop calling this

require JPATH_PLATFORM . '/joomla/foo.php';

Joomla 4 / 6

Use this instead

use the namespaced class. Do not require files out of libraries/src.

How to fix it

  1. 1Remove requires that use JPATH_PLATFORM.
  2. 2Use a use statement for the CMS class.

Also known as

JPATH_PLATFORM

PHP-MYSQL-001

mysql_query()

mysql_* PHP functions

mysql_* was removed from PHP 7. Joomla 4 will not run on PHP that still has it. Direct mysql_query also bypasses Joomla's prefix and ACL.

mysql_query() → Joomla DatabaseInterface / mysqli / PDO

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Removed — fatalJ6: Removed — fatalBC plugin: no — already dead on J4Architecture change — no auto-fixPHP

What you will see

  • Call to undefined function mysql_query()
  • Call to undefined function mysql_connect()

Symptoms people search

undefined function mysql_query

Replace this code

Swap mysql_query() for Joomla DatabaseInterface / mysqli / PDO.

Joomla 3

Remove or stop calling this

$res = mysql_query('SELECT * FROM jos_users');

Joomla 4 / 6

Use this instead

$db = $this->getDatabase();
$query = $db->getQuery(true)->select('*')->from($db->quoteName('#__users'));
$db->setQuery($query);
$rows = $db->loadObjectList();

How to fix it

  1. 1Replace mysql_* with the Joomla database API and bound parameters.

Also known as

mysql_query · mysql_connect · mysql_fetch

There is no 1:1 swap for this one. The architecture changed. If you cannot rewrite it, Infyways can.

PHP-TYPEHINT-OVERRIDE-001

public function display($tpl = null)

Child class missing Joomla 4 typehints

Joomla 4 added scalar/return types on MVC parents. A J3 subclass that omits them fatals: Declaration must be compatible.

public function display($tpl = null) → Match parent signatures (including types and return)

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Changed behaviourJ6: Changed behaviourBC plugin: not applicablePatch possible — review itPHP

What you will see

  • Declaration of ... must be compatible with
  • Fatal error: Declaration

Symptoms people search

incompatible declaration · typehint override · PHP fatal on view display

Replace this code

Swap public function display($tpl = null) for Match parent signatures (including types and return).

Joomla 3

Remove or stop calling this

public function display($tpl = null) { parent::display($tpl); }

Joomla 4 / 6

Use this instead

public function display($tpl = null): void { parent::display($tpl); }

How to fix it

  1. 1Copy the parent method signature from HtmlView / BaseController / ListModel.
  2. 2Do not guess — open the Joomla 6 class.

Also known as

must be compatible · Return type · typed properties

PHP-UTF8-LIB-001

jimport('phputf8.utils.unicode')

libraries/phputf8 copy

Joomla 4 removed the bundled phputf8 tree. String work goes through the String package.

jimport('phputf8.utils.unicode') → Joomla\String\StringHelper

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Removed — fatalJ6: Removed — fatalBC plugin: no — already dead on J4Patch possible — review itPHP

What you will see

  • Failed opening required .../phputf8
  • Call to undefined function utf8_strtolower

Symptoms people search

phputf8 missing · utf8_strtolower

Replace this code

Swap jimport('phputf8.utils.unicode') for Joomla\String\StringHelper.

Joomla 3

Remove or stop calling this

jimport('phputf8.utils.ascii');
utf8_strtolower($s);

Joomla 4 / 6

Use this instead

use Joomla\String\StringHelper;
StringHelper::strtolower($s);

How to fix it

  1. 1Replace utf8_* helpers with StringHelper.
  2. 2Do not jimport phputf8.

Also known as

phputf8 · utf8_strtolower · utf8_strlen

PHP-VERSION-FLOOR-001

PHP 5.3–7.x on Joomla 3

PHP version floors (J4 / J5 / J6)

People blame Joomla for fatals that are PHP. each()/mysql_* die on the PHP J4 already required.

PHP 5.3–7.x on Joomla 3 → J4 ≥ 7.2.5 · J5 ≥ 8.1 · J6 ≥ 8.3 (8.4 recommended)

Dies at Joomla 5 → 6
Dies at Joomla 5 → 6J3: Works nativelyJ4: Changed behaviourJ6: Changed behaviourBC plugin: not applicableArchitecture change — no auto-fixPHP

What you will see

  • Your host needs to use PHP 8.3
  • JOOMLA_MINIMUM_PHP

Symptoms people search

joomla 6 php requirement · php 8.3

Replace this code

Swap PHP 5.3–7.x on Joomla 3 for J4 ≥ 7.2.5 · J5 ≥ 8.1 · J6 ≥ 8.3 (8.4 recommended).

Joomla 3

Remove or stop calling this

// Hosting on PHP 7.4 with Joomla 3

Joomla 4 / 6

Use this instead

Joomla 6 will not install below PHP 8.3.0. Upgrade PHP before the CMS.

How to fix it

  1. 1Check phpversion() on staging.
  2. 2Move to 8.3+ before a Joomla 6 update.

Also known as

PHP 8.3 · PHP 8.1 · minimum php

SEC-ECHO-GET-001

echo $_GET['name']

echo $_GET / unescaped output

XSS is not a Joomla-version feature. Joomla 6 still executes it. Migrations should close it.

echo $_GET['name'] → Input + htmlspecialchars / Text

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Works nativelyJ6: Works nativelyBC plugin: not applicableArchitecture change — no auto-fixPHP

Symptoms people search

xss · echo GET

Replace this code

Swap echo $_GET['name'] for Input + htmlspecialchars / Text.

Joomla 3

Remove or stop calling this

echo $_GET['q'];

Joomla 4 / 6

Use this instead

echo htmlspecialchars($app->getInput()->getString('q'), ENT_QUOTES, 'UTF-8');

How to fix it

  1. 1Never echo superglobals.
  2. 2Escape at the edge.

Also known as

$_GET · $_POST · XSS

There is no 1:1 swap for this one. The architecture changed. If you cannot rewrite it, Infyways can.

SEC-UNSERIALIZE-001

unserialize($_POST['data'])

unserialize($_POST) / untrusted input

unserialize on user input is RCE. It “works” on Joomla 6 and is still wrong.

unserialize($_POST['data']) → JSON + validation

Dies at Joomla 3 → 4
Dies at Joomla 3 → 4J3: Works nativelyJ4: Works nativelyJ6: Works nativelyBC plugin: not applicableArchitecture change — no auto-fixPHP

Symptoms people search

unserialize post · object injection

Replace this code

Swap unserialize($_POST['data']) for JSON + validation.

Joomla 3

Remove or stop calling this

$data = unserialize($_POST['payload']);

Joomla 4 / 6

Use this instead

$data = json_decode($app->getInput()->get('payload', '{}', 'RAW'), true);

How to fix it

  1. 1Use JSON.
  2. 2Never unserialize request data.

Also known as

unserialize

There is no 1:1 swap for this one. The architecture changed. If you cannot rewrite it, Infyways can.

Start a project

Have something
to build?

Tell us what you are building. You will hear back from an engineer within one business day.

hello@infyways.com · Remote, working across time zones

Tell us about your project

Fill out the quick form and we'll reach out.