XOOPS

XOOPS 2.7.3 Beta 1 Released

Mamba  28 Jul 2026 11:30 AM 35 Reads   No comments 
273beta1

XOOPS 2.7.3 Beta 1 is available

XOOPS 2.7.3 Beta 1 is ready for testing. It pairs a set of stability and security fixes with new diagnostic tooling — a persistent, opt-in log that records what the in-page debug panel cannot.

Download: https://github.com/XOOPS/XoopsCore27/releases/tag/v2.7.3-Beta1


Find problems that leave no trace on screen

The headline addition is a persistent file log. XOOPS has always been able to show notices, warnings, errors and SQL to an administrator in the debug panel — but only on a page that actually renders. When a request dies before output, fails only for a guest, or misbehaves only under real traffic, there was nothing to read afterwards.

The new logger records all of it to xoops_data/logs/debug.log, with a backtrace on every entry:

===========[2026-07-28 03:06:31]  ================ WARNING ================
  errstr = Attempt to read property "value" on null
  errfile= /caches/smarty_compile/12f84838_..._themes_item.tpl.php
  errline= 110
-------
  uri: /modules/themes/item.php?itemid=18
  messages.WARNING req=fc006e1d uid=19563
  errno=2
    /caches/smarty_compile/..._themes_item.tpl.php:110 XoopsErrorHandler_HandleError()
    /class/theme.php:601 fetch()
    /footer.php:51 render()
    /modules/themes/item.php:256 require_once()

Each entry opens with a labelled rule — QUERY ERROR, EXCEPTION, ERROR, WARNING, NOTICE, DEPRECATED — so you can see what you are looking at before reading a word, and what failed and where sits directly under the timestamp.

What this gives module developers

If you maintain a module, this closes most of the gaps where a bug used to be invisible:

  • Failures that never render. A fatal, an uncaught exception, or an error during a redirect produces a blank page and an empty debug panel. Those are now the entries you get a full backtrace for.
  • Bugs that only guests hit. The debug panel is admin-only, so a fault that only appears for anonymous visitors was effectively unreproducible. The log records every request regardless of who made it, with the uid on each entry.
  • AJAX, cron and redirect paths. Anything with no HTML output to attach a panel to is covered.
  • Every query, or only the failures. queries_with_errors_only defaults to true; turn it off and you get every statement with its execution time — enough to find an N+1 without installing anything.
  • A backtrace on every entry, so a notice tells you which of your call paths reached it, not merely which line raised it.
  • A per-request id (req=...) on each line, so interleaved requests under real traffic can be told apart.
  • A dedicated deprecation channel. Useful when modernising for PHP 8.4/9: dynamic property creation, legacy constructors and the rest surface as DEPRECATED entries rather than drowning in general noise.
  • Legacy database call patterns. XOOPS_DB_LEGACY_LOG reports deprecated Criteria forms and queryF() used for writes — a checklist for bringing an older module up to date.
  • A log you can attach to a bug report. Entries are already redacted of server paths and session ids, so they can be pasted into an issue without leaking anything.

Use DebugBar on your own module — several fixes in this release came from it

Some of the fixes listed below were found by installing DebugBar and simply looking at what a page was doing. Its query panel is blunt about things no error log will ever report, because none of them are errors:

  • 48 identical SELECT * FROM groups queries on a single page. Nothing failed. Nothing warned. The query panel just showed the same statement 48 times, which is the whole diagnosis.
  • A comment listing costing 541 ms. Sorting the panel by execution time put it at the top immediately; it now runs in 0.5 ms with the right index.
  • Statements failing quietly inside a module that swallowed the result, visible in the panel as a query error while the page rendered as though nothing happened.

If you maintain a module, this is the most direct way to find that class of problem in your own code — an N+1 in a loop, a query built in a template, a listing with no index behind it. They are invisible until something plots them per request, and then they are obvious.

DebugBar is a separate, optional module: it moved to its own distribution in 2.7.1 and installs from XoopsModules27x/debugbar. The php-debugbar and Monolog libraries stay bundled in xoops_lib, so there is no Composer step on your side.

It also stacks with the new file log rather than competing with it — both register with the same XoopsLogger seat and receive identical events. DebugBar shows you the page you are looking at; the log keeps what you cannot look at, such as the request that died before output. Using both is the intended setup.

Turning it on

Debug settings now live in one place instead of being edited into mainfile.php:

xoops_data/data/debug.dist.php  ->  copy to  xoops_data/data/debug.php

Nothing changes until you create that file. Only the .dist template ships. An absent or malformed debug.php leaves XOOPS in its normal production behaviour, so upgrading cannot switch debugging on by accident.

Two things to know before enabling it on a live site

The log holds real data. SQL, user ids, backtraces and file paths. XOOPS ships .htaccess files denying web access, but those work on Apache only — on nginx, IIS, or Apache with AllowOverride None they do nothing. The logger therefore refuses to run while xoops_data sits inside the document root unless you explicitly accept the risk. The right answer is to move xoops_data outside the web root.

display_errors is not covered by debugLevel. The debugLevel setting in xoops_data/configs/xoopsconfig.php restricts debug output to administrators, but it does so *after* authentication, while display_errors is applied much earlier. Errors raised during bootstrap — the database connection, config loading, preloads — are still shown to any visitor. On a public site set display_errors to false and read the log instead; it captures that same window.


Security

  • The debug panel escaped four of its channels and not the others. A PHP warning quotes the offending request value and a database error quotes the offending column, so crafted input reached the administrator's browser as live markup through the error, query, block and deprecation channels. All are now escaped.
  • A deprecation backtrace could publish the full server path. The deployment root was stripped *after* HTML-encoding, so an installation path containing a character that encoding rewrites stopped matching and survived into the panel.

Fixes

  • The front page could blank entirely. XoopsTextSanitizer::htmlSpecialChars() declared a string parameter while its body cast to string anyway, so any module declaring strict_types and passing an id, count or timestamp got an uncaught TypeError instead of coercion.
  • One broken module took global search down for everyone. Module search calls are now contained and logged by name, so a failing module no longer aborts the whole results page.
  • Every page rendering certain users died. xoops_getrank() fataled when no rank row matched — a deleted rank, a post count falling in a gap between ranks, or no ranks defined.
  • One bad call could blank a page. Database result-set methods passed their argument straight to mysqli; PHP 8 turns a failed query or a mis-wired caller into an uncaught TypeError. They now return the failure value they already document, and report the misuse so the calling module's bug stays visible.
  • The login redirect accumulated escaped ampersands. A URL is not HTML, but redirect_header() used the entity & as a query separator — which names the next parameter amp;xoops_redirect to anything parsing the URL. Chains such as ?start=0&amp%3Bamp%3Bamp%3Bforum=0 grew by a layer per hop.
  • The login form on the default theme warned on every render. $redirect_page was only assigned when the request carried a redirect target, and the shipped xbootstrap5 profile form reads it without a guard, so simply opening the login page produced Undefined array key "redirect_page" and Attempt to read property "value" on null. It is now assigned unconditionally, which holds for custom themes too. A malformed redirect target also no longer discards itself entirely on the way through escaping.
  • Two warnings per comment row for every guest on modules with anonymous posting disabled — anon_canpost was only assigned on one branch. On a busy site this was the single largest source of log noise.
  • Roughly 48 identical SELECT * FROM groups queries per page removed by memoising the group list per request.
  • A comment listing query went from 541 ms to 0.5 ms via a new index, carried on the upgrade path.
  • Who's-online tracking no longer depends on the block being rendered on the page.
  • Comment constants are guarded individually, so a second include no longer emits "already defined" warnings — a hard error in PHP 9.

Full details: docs/changelog.270.txt


Upgrading

This is a Beta. Please install it on a test site, not a production one.

The upgrade path carries the 2.7.3 schema changes, including the new comments index. If you are coming from 2.7.1 or 2.7.2, run the upgrade wizard as usual.

mainfile.php is not replaced by an upgrade. You do not need to touch it for the file logger — include/common.php reads debug.php itself. Updating mainfile.php from the new dist adds the XOOPS_DEBUG and XOOPS_DB_LEGACY_LOG constants and raises error reporting earlier than common.php runs, which only matters for failures during bootstrap.

Please report what you find

That is what a Beta is for. Issues, with the relevant debug.log entry attached if you have one, to:

https://github.com/XOOPS/XoopsCore27/issues

Thanks to everyone testing.

Rating 0/5
Rating: 0/5 (0 votes)
Voting is disabled!


Login

Donat-O-Meter

Stats
Goal: $15.00
Due Date: Jul 31
Gross Amount: $0.00
Net Balance: $0.00
Left to go: $15.00
Make donations with PayPal!

Categories