Skip to content

fix(database): return the documented failure value for a non-result argument - #143

Merged
mambax7 merged 2 commits into
XOOPS:masterfrom
mambax7:fix/db-result-guards
Jul 28, 2026
Merged

fix(database): return the documented failure value for a non-result argument#143
mambax7 merged 2 commits into
XOOPS:masterfrom
mambax7:fix/db-result-guards

Conversation

@mambax7

@mambax7 mambax7 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Stops a single module's bad call from blanking an entire page, and corrects a factual error in the 2.7.3 release notes.

Motivation

The result-set methods in XoopsMySQLDatabase pass their argument straight to mysqli. Two things reach them in practice:

  • a failed query, which returns false
  • a mis-wired caller passing the SQL string itself

PHP 8 turns both into an uncaught TypeError inside mysqli, so one bad call takes the whole request down. The @ on those calls never helped — it suppresses diagnostics, not thrown errors.

Observed on xoops.org: modules/core/brokenfile.php reached getRowsNum() with a string.

TypeError: mysqli_num_rows(): Argument #1 ($result) must be of type mysqli_result, string given
File: /class/database/mysqldatabase.php  Line: 171
  /modules/core/brokenfile.php:103

Approach

Each method returns the failure value it already documentsfalse for the fetch family, 0 for the counts, '' for field names. That is what these return at end of data or on error anyway, so every caller is already written to handle it.

The check reuses the existing public isResultSet(); only the reporting is new. Misuse goes through addExtra() plus E_USER_WARNING — the same channel this class already uses when a caller misuses query()/exec() — so the module's bug stays visible rather than quietly becoming an empty result.

Also: a correction to the 2.7.3 notes

The notes claimed a site picks up the new logging "only once its mainfile.php is updated from the new dist." That was wronginclude/common.php reads debug.php itself, so file logging needs no mainfile.php change. Updating mainfile.php adds the XOOPS_DEBUG / XOOPS_DB_LEGACY_LOG constants and raises reporting earlier than common.php runs.

A visibility note is added beside it: debugLevel clamps error_reporting to 0 for non-admins, but after authentication, while display_errors is applied much earlier — so bootstrap-time errors are still shown to any visitor.

Testing

All nine guarded methods exercised against both production shapes (false and a SQL string), using the real base classes and the real XoopsLogger: 18/18 return the documented value rather than throwing, with 18 logger entries and 18 warnings raised.

Summary by Sourcery

Guard MySQL result-set helper methods against invalid arguments to avoid PHP 8 type errors and ensure they return their documented failure values, and correct related release notes documentation.

Bug Fixes:

  • Prevent PHP 8 TypeError crashes in MySQL result-set helper methods when they are called with non-result arguments by returning the documented failure values instead.

Enhancements:

  • Add centralized reporting for invalid MySQL result-set arguments via logger extras and user warnings to keep module misuse visible without breaking the page.

Documentation:

  • Correct the 2.7.3 changelog description of when database logging takes effect and clarify the interaction between debug level, error reporting, and display_errors visibility.

Summary by CodeRabbit

  • Bug Fixes

    • Database result operations now handle invalid inputs safely, returning documented fallback values instead of causing PHP errors or blank pages.
    • Invalid database usage now generates a warning and includes additional diagnostic logging when available.
    • Improved handling of early bootstrap errors and file-based debugging settings.
  • Documentation

    • Updated upgrade guidance and clarified debugging, logging, and error-display behavior.

mambax7 added 2 commits July 27, 2026 22:15
…rgument

The result-set methods pass their argument straight to mysqli. A failed
query returns false, and a mis-wired caller can pass the SQL string
itself -- PHP 8 turns both into an uncaught TypeError inside mysqli, so a
single bad call from a single module blanks the whole page. The '@' in
front of those calls never covered it: it suppresses diagnostics, not
thrown errors.

Seen on xoops.org, where modules/core/brokenfile.php reached
getRowsNum() with a string and took the page down.

Each method now returns the failure value it already documents -- false
for the fetch family, 0 for the counts, '' for the field names -- which
is what every caller is written to handle, since that is what these
return at end of data or on error anyway.

The check reuses the existing public isResultSet(); only the reporting is
new. The misuse is reported through addExtra() plus E_USER_WARNING, the
same channel this class already uses when a caller misuses query()/exec(),
so the module bug stays visible instead of quietly becoming an empty
result.
…e guard

The 2.7.3 note claimed an existing site picks up the new logging "only
once its mainfile.php is updated from the new dist". That is wrong:
include/common.php reads debug.php itself, so file logging works with no
change to mainfile.php at all. Updating mainfile.php adds the XOOPS_DEBUG
and XOOPS_DB_LEGACY_LOG constants and raises error reporting earlier than
common.php runs, which matters only for bootstrap failures.

Adds the visibility note that belongs beside it. debugLevel in
xoops_data/configs/xoopsconfig.php clamps error_reporting to 0 for
non-admins, but it does so after authentication, while display_errors is
applied much earlier -- so errors during the database connection, config
loading or preloads are still shown to any visitor.
Copilot AI review requested due to automatic review settings July 28, 2026 02:16
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@sourcery-ai

sourcery-ai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds defensive checks around MySQL result-set helper methods to avoid PHP 8 TypeErrors when callers pass non-result values, reporting misuse via the existing logging channel and returning the already-documented failure values, plus corrects and clarifies a note in the 2.7.3 changelog about debug/logging behavior and error visibility.

Sequence diagram for invalid MySQL result handling in helper methods

sequenceDiagram
    actor ModuleCaller
    participant XoopsMySQLDatabase
    participant XoopsLogger
    participant PHPErrorHandler

    ModuleCaller->>XoopsMySQLDatabase: getRowsNum(result)
    XoopsMySQLDatabase->>XoopsMySQLDatabase: isResultSet(result)
    alt [result is not a result set]
        XoopsMySQLDatabase->>XoopsMySQLDatabase: reportInvalidResult(__FUNCTION__, result)
        XoopsMySQLDatabase->>XoopsLogger: addExtra(DB, message)
        XoopsMySQLDatabase->>PHPErrorHandler: trigger_error(message, E_USER_WARNING)
        XoopsMySQLDatabase-->>ModuleCaller: 0
    else [result is a result set]
        XoopsMySQLDatabase->>XoopsMySQLDatabase: mysqli_num_rows(result)
        XoopsMySQLDatabase-->>ModuleCaller: rows_count
    end
Loading

File-Level Changes

Change Details Files
Harden MySQL result helper methods against non-mysqli_result inputs while preserving documented return semantics.
  • Introduce a protected reportInvalidResult() helper that derives a human-readable type description of the provided value, logs a DB extra message via the existing logger if available, and raises an E_USER_WARNING.
  • For all fetch methods (fetchRow, fetchArray, fetchBoth, fetchObject), guard with isResultSet() and, on failure, reportInvalidResult() and return false instead of calling mysqli_* and triggering a TypeError.
  • For numeric/count-style helpers (getRowsNum, getFieldsNum), guard with isResultSet() and, on failure, reportInvalidResult() and return 0.
  • For metadata helpers (getFieldName, getFieldType), guard with isResultSet() and, on failure, reportInvalidResult() and return an empty string.
  • For freeRecordSet(), guard with isResultSet() and, on failure, reportInvalidResult() and return early without calling mysqli_free_result().
htdocs/class/database/mysqldatabase.php
Update changelog notes to correct the description of debug/logging activation and clarify error visibility behavior.
  • Correct prior statement that enabling new logging required updating mainfile.php, noting that include/common.php reads debug.php directly.
  • Clarify that debugLevel zeroes error_reporting only after authentication while display_errors may expose bootstrap-time errors to unauthenticated visitors.
docs/changelog.270.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 01a3f870-4f39-4af5-bddd-b0ea374e15d7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gitar-bot

gitar-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="docs/changelog.270.txt" line_range="15-16" />
<code_context>
+- note on visibility: display_errors from debug.php is applied at include/common.php, while the debugLevel setting in xoops_data/configs/xoopsconfig.php clamps error_reporting to 0 for non-admins later in the same file, after authentication. Errors raised before that point — the database connection, config loading, preloads — are therefore still displayed to any visitor. Set display_errors to false on a public site and read the log instead; it records the same bootstrap window (mamba) in #139
+
+Database:
+- return the documented failure value instead of fataling when a result-set method is handed something that is not one. A failed query returns false and a mis-wired caller can pass the SQL string itself; PHP 8 turns both into an uncaught TypeError inside mysqli, so one bad call from one module blanked the whole page. Observed on xoops.org from modules/core/brokenfile.php. The misuse is still reported, so the caller's bug stays visible (mamba)

 Security:
</code_context>
<issue_to_address>
**suggestion (typo):** Consider rephrasing 'fataling', which is not standard usage.

The phrase "instead of fataling" uses a non-standard verb and could be confusing. Consider something like "instead of causing a fatal error" or "instead of failing with a fatal error" for clarity.

```suggestion
Database:
- return the documented failure value instead of causing a fatal error when a result-set method is handed something that is not one. A failed query returns false and a mis-wired caller can pass the SQL string itself; PHP 8 turns both into an uncaught TypeError inside mysqli, so one bad call from one module blanked the whole page. Observed on xoops.org from modules/core/brokenfile.php. The misuse is still reported, so the caller's bug stays visible (mamba)
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread docs/changelog.270.txt
Comment on lines +15 to +16
Database:
- return the documented failure value instead of fataling when a result-set method is handed something that is not one. A failed query returns false and a mis-wired caller can pass the SQL string itself; PHP 8 turns both into an uncaught TypeError inside mysqli, so one bad call from one module blanked the whole page. Observed on xoops.org from modules/core/brokenfile.php. The misuse is still reported, so the caller's bug stays visible (mamba)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Consider rephrasing 'fataling', which is not standard usage.

The phrase "instead of fataling" uses a non-standard verb and could be confusing. Consider something like "instead of causing a fatal error" or "instead of failing with a fatal error" for clarity.

Suggested change
Database:
- return the documented failure value instead of fataling when a result-set method is handed something that is not one. A failed query returns false and a mis-wired caller can pass the SQL string itself; PHP 8 turns both into an uncaught TypeError inside mysqli, so one bad call from one module blanked the whole page. Observed on xoops.org from modules/core/brokenfile.php. The misuse is still reported, so the caller's bug stays visible (mamba)
Database:
- return the documented failure value instead of causing a fatal error when a result-set method is handed something that is not one. A failed query returns false and a mis-wired caller can pass the SQL string itself; PHP 8 turns both into an uncaught TypeError inside mysqli, so one bad call from one module blanked the whole page. Observed on xoops.org from modules/core/brokenfile.php. The misuse is still reported, so the caller's bug stays visible (mamba)

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 19.18%. Comparing base (df231c2) to head (4980119).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
htdocs/class/database/mysqldatabase.php 0.00% 33 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master     #143      +/-   ##
============================================
+ Coverage     18.96%   19.18%   +0.21%     
- Complexity     8091     8198     +107     
============================================
  Files           670      672       +2     
  Lines         43925    44202     +277     
============================================
+ Hits           8332     8481     +149     
- Misses        35593    35721     +128     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/changelog.270.txt`:
- Around line 15-16: Update the Database changelog entry to state that
result-set methods return the documented failure value (`false`, `0`, or `''`),
covering fetch, count, and field metadata methods while preserving the existing
description of the TypeError scenario and misuse reporting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d5688173-e9c1-4091-afcc-9e78a3367150

📥 Commits

Reviewing files that changed from the base of the PR and between 20bd0b5 and 4980119.

📒 Files selected for processing (2)
  • docs/changelog.270.txt
  • htdocs/class/database/mysqldatabase.php

Comment thread docs/changelog.270.txt
Comment on lines +15 to +16
Database:
- return the documented failure value instead of fataling when a result-set method is handed something that is not one. A failed query returns false and a mis-wired caller can pass the SQL string itself; PHP 8 turns both into an uncaught TypeError inside mysqli, so one bad call from one module blanked the whole page. Observed on xoops.org from modules/core/brokenfile.php. The misuse is still reported, so the caller's bug stays visible (mamba)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Document all fallback values, not only false.

The implementation returns false for fetch methods, 0 for count methods, and '' for field metadata methods. Update this entry to say “the documented failure value (false, 0, or '')” so the release note accurately reflects the API contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/changelog.270.txt` around lines 15 - 16, Update the Database changelog
entry to state that result-set methods return the documented failure value
(`false`, `0`, or `''`), covering fetch, count, and field metadata methods while
preserving the existing description of the TypeError scenario and misuse
reporting.

@mambax7
mambax7 merged commit 52069dc into XOOPS:master Jul 28, 2026
14 checks passed
mambax7 added a commit that referenced this pull request Jul 28, 2026
…rect fix

The 2.7.3 block carried two "Database:" headings, because #143's entry was
added at the top of the section rather than beside the existing one from
#130. They are one section now, and #143 gained the PR reference it was
missing.

#145 had no curated entry at all -- it landed after the section was
written -- so the login redirect fix appears under a new "Redirects:"
heading, with the detail the generated one-liner cannot carry: why a URL
carrying an HTML entity hid the fault for exactly one hop, and what made
it start accumulating.
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.

2 participants