Error Handling and Legacy APIs
|
This content was generated with the assistance of AI and should be verified against the official Joomla documentation before being relied on in production. This sub-section’s Joomla bibliography lists the reference material consulted while preparing these pages. |
Joomla grew up on PHP 4, which had no exceptions, so the CMS invented its own error protocol: a method records a
message with setError(), returns false, and the caller is expected to ask getError() what went wrong. Twenty years
later that protocol is still wired into models, tables, users and the installer, together with a layer of old class
names, constants and helper packages that the modern code base no longer needs. This page explains how the protocol
works, how to move an extension off it, and which legacy APIs disappeared in Joomla 6.0 or are scheduled to disappear in
7.0. The example extension is the fictitious Tallpine Hiking Club package pkg_tallpine with the component
com_tallpine (namespace Tallpine\Component\Tallpine).
It was written against Joomla 6.2.x on PHP 8.3+ and checked on 2026-10-30. Joomla 6.2 is the current release, 5.x is in its security-only period (5.4.10 was the last bugfix release; security fixes continue until 2027-10-12) and 4.x is end of life. The chapters of the official Manual that this page is built on are the 5.4 to 6.0 update notes Error handling, Removed and Backward Incompatibility, Compatibility Plugins and New deprecations. For the bigger picture see Deprecations and Preparing for Joomla 7 and Upgrading Joomla 5 to 6. PHP’s own exception model is not repeated here; see Errors and Exceptions.
Two error protocols in one code base
Joomla 6.2 supports two ways to report a failure, and a class may use either:
| Legacy protocol | Exceptions | |
|---|---|---|
Reporting |
|
|
Reading |
|
|
Cost of forgetting |
A silent failure: the caller ignores the |
A visible failure: the exception reaches Joomla’s error page or the log |
Status in 6.2 |
Deprecated; core still contains it and still supports it |
Preferred everywhere; the only protocol that will remain after 7.0 |
The legacy protocol lives in one place, the trait \Joomla\CMS\Object\LegacyErrorHandlingTrait. It was introduced in
Joomla 4.3 when the error methods were extracted from CMSObject, so that classes which only needed error handling
could stop extending CMSObject. In the Joomla 6.2 source it is used by BaseModel, AbstractView (and through them
every model and every view), Table, User, Installer, InstallerExtension and Update, and by CMSObject itself.
The trait holds a protected $_errors array and the methods getError(), getErrors() and setError() (all marked
deprecated since 3.1.4, to be removed in 7.0 without replacement), plus two methods added in Joomla 5.4:
shouldUseExceptions() and setUseExceptions(bool $value).
The switch added in Joomla 5.4
Because core has to support extensions that read getError() as well as extensions that expect exceptions, the 5.4
release added a per-object flag. The behaviour is deliberately small:
-
setUseExceptions(true)turns the flag on for that object.shouldUseExceptions()reads it. The default isfalse, so nothing changes for code that does not opt in. -
With the flag on,
setError()with a string argument throws a generic\Exceptionwith code 500 instead of appending to$_errors. An\Exceptionobject passed tosetError()is still just recorded. -
Core code that was converted checks the flag first and throws a specific exception, and only otherwise falls back to
setError()andreturn false. -
BaseDatabaseModel::getTable()passes the flag on to the table it creates, so a model with exceptions enabled produces tables with exceptions enabled.
The list and form views of core components call $model->setUseExceptions(true) themselves and wrap the data loading
in try/catch, converting any exception into a GenericDataException. This is the shape that a view of
com_tallpine should have too:
<?php
namespace Tallpine\Component\Tallpine\Administrator\View\Trails;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
\defined('_JEXEC') or die;
class HtmlView extends BaseHtmlView
{
protected $items;
protected $pagination;
protected $state;
public function display($tpl = null)
{
$model = $this->getModel();
$model->setUseExceptions(true);
try {
$this->items = $model->getItems();
$this->pagination = $model->getPagination();
$this->state = $model->getState();
} catch (\Exception $e) {
// Keep the original exception as "previous" so that the log has the real cause.
throw new GenericDataException($e->getMessage(), 500, $e);
}
parent::display($tpl);
}
}
Before 5.4 the same view ended with the pattern that older books and tutorials still show:
if (count($errors = $this->get('Errors'))) { throw new GenericDataException(implode("\n", $errors), 500); }. That code
runs, but it only reports errors that the model remembered; an exception thrown inside the model, or an error that a
careless model forgot to record, passes it by.
Timeline of the removal
The official plan is spread over three majors, so that extensions can migrate whenever it suits them:
In Joomla 7.0 the core removes its own calls to setError() and its checks of shouldUseExceptions(). Whether
shouldUseExceptions() should then default to true is described by the Manual as still under discussion. The trait is
planned to be dropped in 8.0. The @deprecated tags in the 6.2 source say the same: the error methods are removed in
7.0, and the useExceptions property and its two accessors are marked as deprecated for 7.0.
In Joomla 6.0 two classes that carried the trait without using it were cleaned up: CategoryNode and Changelog no
longer include LegacyErrorHandlingTrait. An extension that subclassed them and called setError() on the subclass
has to add the trait itself or, better, stop calling it.
What a third-party developer should do
The core examples look elaborate because core has to serve both camps at once. The Manual’s advice for extensions is
simpler: switch to exceptions completely and force the flag on in the objects you extend. A table class of
com_tallpine shows it:
<?php
namespace Tallpine\Component\Tallpine\Administrator\Table;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseInterface;
\defined('_JEXEC') or die;
class TrailTable extends Table
{
public function __construct(DatabaseInterface $db)
{
parent::__construct('#__tallpine_trails', 'id', $db);
// Core classes that we extend must throw as well. Remove this line when you drop support
// for Joomla versions that still have the legacy protocol (the trait goes away in 8.0).
$this->setUseExceptions(true);
}
public function check()
{
if (trim((string) $this->title) === '') {
// Legacy style: $this->setError(...); return false;
throw new \UnexpectedValueException('COM_TALLPINE_ERROR_TRAIL_TITLE_REQUIRED');
}
if ($this->distance_km !== null && (float) $this->distance_km < 0) {
throw new \RangeException('COM_TALLPINE_ERROR_TRAIL_DISTANCE_NEGATIVE');
}
return parent::check();
}
}
Rules of thumb that follow from the Manual and from how the flag is propagated:
-
Call
setUseExceptions(true)in the constructor of the object, not at some later point, because internal calls made by the constructor or by the parent class would otherwise still use the legacy path. -
If your object creates other objects that use the trait (a model creating tables, a controller creating models), pass the current
shouldUseExceptions()value to them.getTable()does it for you; a model created withMVCFactory::createModel()does not inherit anything. -
Throw specific exceptions (
\InvalidArgumentException,\RuntimeException, your own subclasses) instead of the generic\ExceptionthatsetError()produces, and pass the original as the third argument when wrapping. -
Do not mix: a method that sometimes returns
falseand sometimes throws is harder to use than either extreme. Document the exceptions with@throws. -
Code that must still run on Joomla 4.x or on 5.x before 5.4 cannot call
setUseExceptions(): on those versions the method does not exist. Guard it withmethod_exists($object, 'setUseExceptions'), or raise the minimum version of the extension.
Using exceptions from controllers, helpers and plugins
Exceptions need a catcher. A controller task catches them, reports through the message queue and redirects; a helper lets them propagate; a plugin event handler must be careful, because an uncaught exception in one plugin aborts the dispatch for everyone:
<?php
namespace Tallpine\Component\Tallpine\Administrator\Controller;
use Joomla\CMS\MVC\Controller\FormController;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
\defined('_JEXEC') or die;
class TrailController extends FormController
{
public function publishWithConditions(): bool
{
$this->checkToken();
$id = $this->input->getInt('id');
$model = $this->getModel('Trail');
$model->setUseExceptions(true);
$ok = false;
try {
$model->publishWithConditions($id);
$this->setMessage(Text::_('COM_TALLPINE_TRAIL_PUBLISHED'));
$ok = true;
} catch (\Throwable $e) {
// Show a translated, generic text to the user; the details go to the log.
\Joomla\CMS\Log\Log::add($e->getMessage(), \Joomla\CMS\Log\Log::ERROR, 'com_tallpine');
$this->setMessage(Text::_('COM_TALLPINE_ERROR_PUBLISH_FAILED'), 'error');
}
$this->setRedirect(Route::_('index.php?option=com_tallpine&view=trails', false));
return $ok;
}
}
The same code shows two security points. First, the exception message is not echoed to the user: it may contain SQL
fragments, file paths or table names. Second, checkToken() runs before any state change. Joomla’s own error pages
follow the Global Configuration setting Error Reporting (System Default, None, Simple or Maximum); see
Error, Offline and Component Pages and
Debugging for what each level reveals, and
Security in Extensions for the broader checklist.
Pitfalls
-
Checking the return value only. A method that used to return
falsemay now throw, and the caller’sif (!$table->store())never runs the error branch. Wrap the call, or catch at a higher level. -
Catching
\Exceptionand swallowing it. Joomla’s PHP code runs under\Throwablesemantics: PHP 8 raises\Errorsubclasses (TypeError,ValueError) thatcatch (\Exception $e)does not catch. Catch\Throwableonly at the outer boundary where you log and recover. -
setError()with an exception object. With the flag on, only strings throw. Passing an\Exceptionobject records it silently. -
Calling
getError()after enabling exceptions. There is nothing to read: the array stays empty. -
Relying on
getErrors()in a layout. Views in 6.x have exceptions turned on by core; error output moves to the exception handler. -
Putting your own plugin in the
behaviourgroup to guarantee early execution. The Manual warns that the group may be removed; use thesystemgroup with an explicit priority.
The Behaviour - Backward Compatibility 6 plugin
Most legacy APIs removed in 6.0 are not simply gone: the plugin Behaviour - Backward Compatibility 6 (folder
plugins/behaviour/compat6, abbreviated BC6) ships the old classes and aliases, so that an extension written for 5.x
keeps working until it is updated. The plugin is a final class that extends CMSPlugin and implements
SubscriberInterface; it is in the behaviour group so that it loads before any other plugin.
Its behaviour differs by how a site got to 6.x:
-
On a site updated from 5.4, the plugin has already been installed and enabled in 5.4 (where it does nothing), and the updater requires it to be enabled. The old Behaviour - Backward Compatibility plugin of Joomla 5 (the one that bridged 4 to 5) must be disabled before the update; if the site breaks without it in 5.4, enable it again and fix the cause while still on 5.4.
-
On a new Joomla 6 installation the plugin is installed but disabled.
Its parameters, as defined in compat6.xml, are three switches:
| Parameter | Default | What it does |
|---|---|---|
Classes Aliases ( |
No |
Loads |
Include Deprecated Classes ( |
Yes |
Registers the folder |
Removed Assets ( |
Yes |
Registers |
The plugin constructor also defines JPATH_PLATFORM (with a deprecation note) so that old class files, which begin
with defined('JPATH_PLATFORM') or die, can still be loaded. Section JPATH_PLATFORM and _JEXEC explains why that matters.
Checking an extension against the plugin
The practical question is "does pkg_tallpine work with BC6 disabled?", because BC6 is itself transitional: its
classes are announced for removal in 7.0. A reliable procedure on a development site:
-
Install the extension on a fresh Joomla 6.2 site where BC6 is disabled (the default for new installs).
-
Open every administrator view and every site view, save an item, run the installer, and read the PHP error log with Error Reporting set to Maximum.
-
A
Class "JFactory" not foundmessage, or a similar one forJoomla\CMS\Input\Input, means the code still depends on an alias or a removed class. Fix the code rather than enabling the plugin. -
Enable BC6 only temporarily to find out which of its three switches the extension needs, and write that down as a to-do list.
The plugin is a rescue tool, not a platform. A site that turns on Classes Aliases permanently has simply postponed the work to 7.0, and an extension that requires BC6 should say so in its description and compatibility notes.
Legacy J* class aliases
Joomla 1.5 to 3.x named every class with a J prefix and a flat name: JFactory, JText, JHtml, JRoute, JUri,
JTable, JModelLegacy, JControllerLegacy, JPlugin. Joomla 4 moved everything to namespaces under \Joomla\CMS
and kept the old names as aliases registered with JLoader::registerAlias($alias, $original, $version); the third
argument is the version in which the alias is to be removed, and using an alias logs a deprecation.
In Joomla 6.0 the aliases are no longer registered by core. They are available only when BC6 is enabled and its Classes
Aliases switch is on, and that switch is off by default. The replacement is always the namespaced class, with an
use statement. A before-and-after for a helper of com_tallpine:
<?php
// Before (Joomla 3 and 4 style; fails on a default Joomla 6 site)
defined('_JEXEC') or die;
JLoader::register('TallpineHelper', JPATH_COMPONENT . '/helpers/tallpine.php');
class TallpineHelper
{
public static function nextHikeTitle(): string
{
$app = JFactory::getApplication();
$db = JFactory::getDbo();
return JText::_('COM_TALLPINE_NEXT_HIKE') . ' ' . $app->input->get('trail');
}
}
<?php
// After (Joomla 6)
namespace Tallpine\Component\Tallpine\Site\Helper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\Database\DatabaseAwareInterface;
use Joomla\Database\DatabaseAwareTrait;
\defined('_JEXEC') or die;
class TallpineHelper implements DatabaseAwareInterface
{
use DatabaseAwareTrait;
public function nextHikeTitle(): string
{
$app = Factory::getApplication();
return Text::_('COM_TALLPINE_NEXT_HIKE') . ' ' . $app->getInput()->getString('trail');
}
}
Notes on this migration:
-
JFactory::getDbo()and the staticFactory::getDbo()are not the target. Inject the database throughDatabaseAwareTraitor take it from the dependency-injection container; see Database API and Namespaces, Autoloading and Dependency Injection. -
Factory::getApplication()itself is still available in 6.2, but inside classes created by Joomla’s factories prefergetApplication()fromCMSApplicationAwareTrait. Plugins and the application classes exposegetApplication()as well; the plugin properties$appand$dbthat older code reads are auto-detected today and that detection is removed in 7.0. -
$app->inputis deprecated too: since the framework application dropped the property, the CMS keeps a magic accessor that logs a deprecation. Use$app->getInput(). -
Table access changed in 6.0: core uses
new \Joomla\CMS\Table\Content($db)instead ofTable::getInstance('content'), andTable::addIncludePath()can no longer be used to substitute a core table class. An extension of your own should construct its table class directly or use the MVC factory. -
The non-namespaced indexer files under
administrator/components/com_finder/helpers/indexerare empty;FinderIndexerHelper::getFinderPluginId()becomesJoomla\Component\Finder\Administrator\Helper\FinderHelper::getFinderPluginId().
A quick way to find what is left in your own repository is a search for the two-letter prefix; this is a shell one-liner, not a Joomla tool:
grep -rnE "\bJ(Factory|Text|Html|Route|Uri|Table|Plugin|Loader|Log|Input|Filter[A-Za-z]*|Model[A-Za-z]*|Controller[A-Za-z]*|View[A-Za-z]*)::?|new J[A-Z]|extends J[A-Z]" \
--include='*.php' src/ pkg_tallpine/
JPATH_PLATFORM and _JEXEC
Every PHP file that Joomla can load should refuse direct execution, so that requesting a file such as
/administrator/components/com_tallpine/src/Table/TrailTable.php over HTTP does not run it outside the CMS. Joomla uses
a constant defined by the entry point. Two such constants existed:
-
_JEXECis defined by the entry points (index.phpof the site, administrator and API, and the command line). It means "this request goes through Joomla". The extension guard is\defined('_JEXEC') or die;. -
JPATH_PLATFORMwas defined by the old platform bootstrap and was the guard for the library classes. It was deprecated in 4.4 and, in Joomla 6.0, is no longer defined by any bootstrap file. It exists only while BC6 is enabled, because the plugin defines it for the sake of old class files.
Consequence: a file that starts with defined('JPATH_PLATFORM') or die; stops with a blank page on a default Joomla
6.x site, because the constant is undefined and die runs. The fix is mechanical:
<?php
// Old:
\defined('JPATH_PLATFORM') or die;
// New:
\defined('_JEXEC') or die;
Details worth knowing:
-
Use the fully qualified
\defined(leading backslash) inside a namespace, otherwise PHP first looks for a functiondefinedin the namespace; core files use\defined('_JEXEC') or die;on its own line, between thenamespaceline and the code. -
The guard is a defence-in-depth measure and not authentication. Files with only declarations (classes, interfaces, traits) are harmless when requested directly, but the guard prevents notices that would reveal paths and avoids running procedural code by accident. Procedural files, layouts and
services/provider.phpmust always have it. -
Do not replace
diewithdie('message')that prints environment details. -
The coding standard and PHPCS ruleset of the project already require this form. See Coding Standards and Static Analysis.
\Joomla\CMS\Input becomes \Joomla\Input
Request data is read from an input object: $input->get('id'), $input->getInt(), $input->post, $input->files,
$input->cookie, $input->json. In Joomla 3 and 4 the class was \Joomla\CMS\Input\Input (with the subclasses Cookie,
Files and Json), an extension of the Joomla Framework class. It was deprecated in 4.3 and, in Joomla 6.0, the CMS
namespace was removed from the core and moved into the BC6 plugin. All MVC classes (controllers, models, views, the
dispatcher) now hand out the Framework class \Joomla\Input\Input from the package joomla/input, which is described
by the Manual as "very much a drop-in replacement".
For an extension this affects imports and type declarations, not call sites:
<?php
namespace Tallpine\Component\Tallpine\Site\Helper;
// Old, removed from core in 6.0 (available only with BC6):
// use Joomla\CMS\Input\Input;
use Joomla\Input\Input;
use Joomla\CMS\Factory;
\defined('_JEXEC') or die;
class TrailFilter
{
public static function fromRequest(?Input $input = null): array
{
$input ??= Factory::getApplication()->getInput();
return [
'difficulty' => $input->getCmd('difficulty', ''),
'max_km' => $input->getFloat('max_km', 0.0),
'family' => $input->getBool('family', false),
];
}
}
A type declaration against the old class is the common failure: a method declared with \Joomla\CMS\Input\Input $input
gets a \Joomla\Input\Input from the core and throws a TypeError. An extension that must run on both 5.x and 6.x
can drop the type or type against \Joomla\Input\Input, which exists in both (the old class extended it).
Security-wise nothing changed in the filtering rules: always read through a typed getter (getInt, getCmd,
getBool, getString, getFloat, getWord), never $_GET or $_POST, and use Joomla\CMS\Filter\InputFilter for
the cases where filtering must differ. Uploaded files come from $input->files and, in Joomla 6, are handled with the
Framework filesystem package described next.
\Joomla\CMS\Filesystem becomes joomla/filesystem
The CMS filesystem helpers (File, Folder, Path, Stream, FilesystemHelper, Patcher) were deprecated since 4.4
in favour of the Joomla Framework package joomla/filesystem with the namespace \Joomla\Filesystem. In Joomla 6.0
they were moved to the BC6 plugin, and they are scheduled to disappear completely in 7.0.
The Framework package ships with the core, so no Composer step is needed for an extension that runs inside Joomla. A standalone library (for tests or a CLI tool) declares the dependency:
{
"name": "tallpine/pkg-tallpine",
"require": {
"php": ">=8.3",
"joomla/filesystem": "^4.2.1",
"joomla/input": "^4.0"
}
}
Joomla 6.2.1 itself requires joomla/filesystem 4.2.1 and joomla/input 4.0. The Manual says the two APIs "can be
used nearly interchangeably", with one exception that causes the most surprises: it states that File::exists() and
Folder::exists() are not available in the Framework package and recommends is_file() and is_dir(). The 4.2.1
Framework source does contain both methods as thin wrappers, but older Framework releases do not, so the built-ins are
the portable choice:
<?php
namespace Tallpine\Component\Tallpine\Administrator\Helper;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
use Joomla\Filesystem\Path;
\defined('_JEXEC') or die;
class GpxStorage
{
public static function store(string $tmpName, string $fileName): string
{
$dir = JPATH_ROOT . '/images/tallpine/gpx';
// Old: if (!Folder::exists($dir)) { Folder::create($dir); }
if (!is_dir($dir)) {
Folder::create($dir);
}
// Never trust the original name: keep only a safe file name and check the final path.
$safe = File::makeSafe($fileName);
$target = Path::clean($dir . '/' . $safe);
Path::check($target); // throws if the path points outside the Joomla root
// Old: if (File::exists($target)) { ... }
if (is_file($target)) {
throw new \RuntimeException('COM_TALLPINE_ERROR_GPX_EXISTS');
}
File::upload($tmpName, $target);
return $target;
}
}
Two behavioural points matter for the error-handling topic of this page:
-
The Framework classes throw exceptions (
\Joomla\Filesystem\Exception\FilesystemException; for example fromFile::upload(),File::copy(),Folder::create()andPath::check()) instead of returningfalseand queueing a warning, which was the habit of the CMS classes. Code that wroteif (!File::upload($src, $dest)) { ... }must switch totry/catch.File::upload()also refuses unsafe files (isSafeFile()) unless$allowUnsafeis set; never set it for user uploads. -
Path::check()andFile::makeSafe()are security tools. Path traversal (../) in an uploaded file name is one of the classic weaknesses of extensions; see Security in Extensions.
The sibling classes follow the same pattern. The CMS \Joomla\CMS\Application\BaseApplication and CliApplication
and the CLI input and output classes are replaced by the namespace \Joomla\Application and by console plugins
(see Console Command Plugins); \Joomla\CMS\Adapter\Adapter and
AdapterInstance were removed without replacement, and $app->isCli() is replaced by an instanceof test against
\Joomla\CMS\Application\ConsoleApplication. In the 5.4 to 6.0 notes, \Joomla\CMS\Http is additionally deprecated
(removal in 8.0) in favour of the Framework \Joomla\Http, whose responses must be read through the PSR-7 methods
rather than magic attributes.
CMSObject removal
\Joomla\CMS\Object\CMSObject (formerly JObject, Joomla 1.5/1.7) was a base class offering get(), set(),
setProperties(), getProperties(), def(), getPublicProperties(), error handling and toString(). It also allowed
code to read and write properties regardless of their visibility, which is the reason the maintainers wanted it gone.
It has been deprecated since 4.0 and is removed in 7.0. Its two jobs now live in LegacyErrorHandlingTrait and
LegacyPropertyManagementTrait, which are also deprecated.
Joomla 6.0 removed every use of it from the core code, which created these changes that are not backward compatible:
-
AdminModel::getItem()returns a\stdClass, not aCMSObject. The method calls$item->get('title')and$item->set('title', 'x')no longer exist; read and write the property directly. -
The models listed by the Manual now return
\stdClass: those of the Installer (update sites), Languages, Mails (template), Menu, Menus (menu types), Messages (config), Modules, Plugins, Scheduler (task and tasks), Templates (style), Users (group), Workflow (transition), theFormModelof Contact and Content, and the TagsTagModel. -
Smart Search plugins keep their state in
\stdClassobjects,Rules::getAllowed()returns astdClass,UserHelper::getProfile()returns astdClass, the model state ofApiControlleris aRegistry, and the events fired when a media file is uploaded or a folder is created now carry astdClass. -
Views lost the
$this->dbproperty. A Joomla 3 era override that compares$this->item->createdto$this->db->getNullDate()should compare tonull; since Joomla 4 the database stores realNULLvalues instead of0000-00-00 00:00:00.
<?php
// Before (CMSObject API on a model item)
$trail = $this->getModel('Trail')->getItem($id);
$km = $trail->get('distance_km', 0);
if ($trail->created !== $this->db->getNullDate()) {
echo $trail->created;
}
// After (Joomla 6)
$trail = $this->getModel('Trail')->getItem($id);
$km = $trail->distance_km ?? 0;
if ($trail->created !== null) {
echo $trail->created;
}
For your own data classes, replace a CMSObject base with a plain class with typed, visibility-controlled
properties, or with \stdClass or a Joomla\Registry\Registry where a bag of values is really needed. Do not use
CMSObject as the return type of a new method: the class does not exist in 7.0.
UTC instead of GMT
Joomla stores all dates in the database in UTC and converts them to the user’s or site’s time zone for display. For
historical reasons some code used the string GMT for "no offset". Joomla 6.0 unified this: all places use or fall back
to UTC, and the Date class forces PHP’s default time zone to UTC while it works. Two related removals belong to the
same change: \Joomla\CMS\Date\Date::$gmt and Date::$stz and the code around them are gone, so a class extending
Date must not depend on them.
<?php
use Joomla\CMS\Date\Date;
use Joomla\CMS\Factory;
\defined('_JEXEC') or die;
$tz = Factory::getApplication()->getIdentity()->getTimezone();
// Old assumption: "if ($tz->getName() === 'GMT')" to detect "no offset configured".
// New: compare with UTC, or better, do not compare names at all.
$isUtc = $tz->getName() === 'UTC';
$departure = new Date($trail->departure_at, 'UTC'); // value as stored in the database
echo $departure->setTimezone($tz)->format('Y-m-d H:i'); // value shown to the visitor
Pitfalls: a template override or plugin that tests for the literal string GMT, a database default written as GMT
in a SQL script, or a unit test that asserts GMT in a formatted date will all fail after the move. Fix the
comparison; do not set the server to GMT to compensate. See
Global Configuration for the Website Time Zone setting.
Other 6.0 removals met in extension code
The Manual’s list is long; these items regularly appear in extensions and are best handled together with the ones above:
| Removed or changed in 6.0 | What to use |
|---|---|
|
Construct the table class directly: |
|
|
|
|
|
The |
|
|
|
Call |
|
Removed without replacement; copy the logic from the 5.3 branch if you really need it. |
|
Removed from the core. Use |
|
Drop the |
A few of the entries in the Manual’s chapter are still marked as needing more detail (for example the replacement for the
extension compatibility fetch and the extended #__ucm_history data); check the chapter again before relying on a
particular replacement.
Where older books and tutorials are outdated
-
Developing Extensions for Joomla! 5 (Packt, 2023) was written for Joomla 5.0. Code in the book that reads
$model->getError()after afalsereturn, throwsGenericDataExceptionfromgetErrors(), importsJoomla\CMS\Input\Input, or usesJoomla\CMS\Filesystem\Fileruns on 5.x, but on Joomla 6 it needs BC6 or an update. The book predatessetUseExceptions()(5.4) and the exception-first guidance. -
Joomla! 4 Masterclass (Packt, 2023) targets Joomla 4.x, which is end of life, and treats
JFactory-style code and$this->inputas normal; none of that is current on 6.2. -
Tutorials on docs.joomla.org (the legacy wiki) still show
JError::raiseWarning(),JRequest::getVar()anddefined('JPATH_PLATFORM') or die. Those calls belong to the Joomla 1.5 to 3.x era and are long gone; the others are covered above. Joomla’s documentation moved to the Developer Manual (manual.joomla.org) and the User Guide (guide.joomla.org).
Migration checklist for pkg_tallpine
-
On Joomla 5.4.x, set Error Reporting to Maximum on a development copy, so deprecation notices show up, and run the extension through all views.
-
Replace
setError()/getError()with exceptions; addsetUseExceptions(true)in every class that extends a core model, table or view base class (guarded withmethod_existsif you still support 5.3 or older). -
Replace
defined('JPATH_PLATFORM')with\defined('_JEXEC'). -
Replace
J*aliases with namespaced classes andusestatements; replace$app->inputwith$app->getInput(). -
Replace
\Joomla\CMS\Inputand\Joomla\CMS\Filesystemimports with\Joomla\Inputand\Joomla\Filesystem, and replaceFile::exists()andFolder::exists()withis_file()andis_dir(). -
Replace
CMSObjectuse with plain objects and property access; remove$this->dbfrom view layouts. -
Replace comparisons with
GMTbyUTC. -
Install the extension on a fresh 6.2 site with BC6 disabled and repeat the test.
-
State the supported Joomla versions in the manifest
<targetplatform>and in the update server file, see Extension Types, Manifests and Packaging.