Skip to content

fix(kernel): reload XOBJ_DTYPE_UNICODE_ARRAY values intact - #271

Merged
mambax7 merged 1 commit into
XOOPS:masterfrom
mambax7:fix/unicode-array-roundtrip
Oct 16, 2026
Merged

mambax7 merged 1 commit into
XOOPS:masterfrom
mambax7:fix/unicode-array-roundtrip

Conversation

@mambax7

@mambax7 mambax7 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Two defects remained in the XOBJ_DTYPE_UNICODE_ARRAY round trip after #269 made the array_walk() encode and decode calls act on the array:

  • XoopsObject::assignVar() url-decoded the stored value as a whole string before getVar() unserialized it. Any element containing a %-sequence (café, a+b, 100%) changed length, unserialize() warned and getVar() returned []. The serialized string is now stored as is; getVar() unserializes it and decodes the elements.
  • XoopsModelWrite::cleanVars() still ran the addslashes() loop that predates $db->quote() before url-encoding the elements, so the backslashes became stored data (O'Reilly became O\'Reilly). The loop is removed; the whole value is SQL-quoted once.

Test

tests/unit/htdocs/kernel/XoopsObjectUnicodeArrayTest.php stores an array through both write paths in a subprocess with the real functions.encoding.php, reloads it through assignVar() and asserts the original array.

Check Result
New test on master fails (O%5C%27Reilly stored)
New test on this branch passes, 5 assertions
phpunit --filter 'Object|Write|Encoding' 523 tests, 0 failures, 3 skipped

Changelog line added under the 2.7.4 NOT RELEASED section.

Summary by Sourcery

Fix unicode array serialization and reload handling so stored values round-trip without corruption.

Bug Fixes:

  • Preserve XOBJ_DTYPE_UNICODE_ARRAY values exactly when they are written, stored, and reloaded, including values containing percent sequences, apostrophes, plus signs, backslashes, and non-ASCII characters.

Documentation:

  • Add a changelog entry for the unicode-array round-trip fixes.

Tests:

  • Add an integration-style regression test covering both object and model write paths and reload behavior.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue that could corrupt Unicode arrays containing accented text or special characters when saved and reloaded. These values now retain their content across both supported write paths and when loaded again.

Two defects remained after XOOPS#269 made the array_walk() encode and
decode calls act on the array:

XoopsObject::assignVar() url-decoded the stored value as a whole
string before getVar() unserialized it. Any element containing a
%-sequence (café, a+b, 100%) changed length, unserialize() warned and
getVar() returned []. The serialized string is now stored as is;
getVar() unserializes it and decodes the elements.

XoopsModelWrite::cleanVars() still ran the addslashes() loop that
predates $db->quote() before url-encoding the elements, so the
backslashes became stored data (O'Reilly became O\'Reilly). The loop
is removed; the whole value is SQL-quoted once.

A new test stores an array through both write paths in a subprocess
with the real functions.encoding.php, reloads it through assignVar()
and asserts the original array. It fails on master and passes here.
Copilot AI balanced review requested due to automatic review settings October 15, 2026 23:52
@sourcery-ai

sourcery-ai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Fixes unicode-array round trips by preserving serialized payloads until unserialization, removing pre-encoding addslashes() corruption, and adding an end-to-end regression test for both write paths and problematic string values.

Sequence diagram for Unicode array round trip

sequenceDiagram
    participant Writer as XoopsModelWrite
    participant DB as Database
    participant Object as XoopsObject
    participant Value as getVar()

    Writer->>Writer: array_walk(cleanv, xoops_aw_encode)
    Writer->>DB: quote(serialize(cleanv))
    DB-->>Object: stored serialized payload
    Object->>Object: assignVar(key, value)
    Object->>Value: getVar()
    Value->>Value: unserialize(value)
    Value->>Value: decode array elements
    Value-->>Object: original Unicode array
Loading

Flow diagram for safe Unicode array encoding

flowchart LR
    A[Unicode array values] --> B[array_walk with xoops_aw_encode]
    B --> C[serialize encoded array]
    C --> D[Database quote once]
    D --> E[Stored serialized payload]
    E --> F[assignVar preserves payload]
    F --> G[getVar unserializes and decodes elements]
    G --> H[Original array values]
Loading

File-Level Changes

Change Details Files
Preserve serialized unicode-array payloads during reload so element encoding remains length-safe.
  • Stop URL-decoding the serialized array in assignVar().
  • Keep element-level decoding in getVar() after unserialization.
htdocs/kernel/object.php
Remove the legacy escaping pass before serializing unicode-array values.
  • Eliminate addslashes()-based element mutation.
  • URL-encode elements and SQL-quote the serialized payload once.
htdocs/class/model/write.php
Add an end-to-end regression test covering both persistence paths and reload behavior.
  • Run the test in a subprocess with the real encoding functions.
  • Exercise accents, plus signs, apostrophes, backslashes, and percent signs.
  • Verify both write paths store equivalent values and restore the original array.
tests/unit/htdocs/kernel/XoopsObjectUnicodeArrayTest.php
Document the fix in the project changelog.
  • Add a changelog entry under the unreleased 2.7.4 section.
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.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: XOOPS/XoopsCore27/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Essentials
  • Run ID: 31847a7f-f6ff-4917-84f4-dafe09834dec
📥 Commits

Reviewing files that changed from the base of the PR and between c81afcc and 8d5e548.

📒 Files selected for processing: 4
  • docs/changelog.270.txt
  • htdocs/class/model/write.php
  • htdocs/kernel/object.php
  • tests/unit/htdocs/kernel/XoopsObjectUnicodeArrayTest.php

Included review availability: This review used your included allowance. 4 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

Walkthrough

Unicode-array handling changes in cleanVars() and assignVar(). A PHPUnit test checks that values pass through both write paths and reload as the original array. A changelog entry documents the changes and prior failure behavior.

Changes

Unicode array persistence

Layer / File(s) Summary
Write and reload round trip
htdocs/class/model/write.php, htdocs/kernel/object.php, tests/unit/htdocs/kernel/XoopsObjectUnicodeArrayTest.php, docs/changelog.270.txt
cleanVars() no longer adds slashes to Unicode-array elements before URL-encoding. assignVar() preserves a serialized string instead of URL-decoding it immediately. The test checks both write paths and reloads the original array. The changelog describes the changes and prior failures.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 8d5e5

The supplied evidence identifies no actionable issue that would prevent merging after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 3 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: preserving XOBJ_DTYPE_UNICODE_ARRAY values during reload.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 3 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 PHPStan (2.2.16)

PHP Fatal error: Uncaught Error: Class "Nette\DI\ServiceCreationException" not found in /.cache/phpstan/phpstan/cache/nette.configurator/Container_3bdd9a7f53.php:9316
Stack trace:
#0 phar:///usr/bin/phpstan/vendor/nette/di/src/DI/Container.php(51): Container_3bdd9a7f53->getDynamicParameter()
#1 phar:///usr/bin/phpstan/vendor/nette/di/src/DI/Container.php(245): _PHPStan_08e3736ef\Nette\DI\Container->_PHPStan_08e3736ef\Nette\DI{closure}()
#2 phar:///usr/bin/phpstan/vendor/nette/di/src/DI/Container.php(50): _PHPStan_08e3736ef\Nette\DI\Container->preventDeadLock()
#3 [internal function]: _PHPStan_08e3736ef\Nette\DI\Container->getParameter()
#4 /.cache/phpstan/phpstan/cache/nette.configurator/Container_3bdd9a7f53.php(9329): array_map()
#5 phar:///usr/bin/phpstan/src/DependencyInjection/Nette/NetteContainer.php(94): Container_3bdd9a7f53->getParameters()
#6 phar:///usr/bin/phpstan/src/DependencyInjection/MemoizingContainer.php(57): PHPStan\DependencyInjection\Nette\NetteContainer->getParame

... [truncated 2699 characters] ...

an\Command\AnalyseCommand->execute()
#11 phar:///usr/bin/phpstan/vendor/symfony/console/Application.php(868): _PHPStan_08e3736ef\Symfony\Component\Console\Command\Command->run()
#12 phar:///usr/bin/phpstan/vendor/symfony/console/Application.php(261): _PHPStan_08e3736ef\Symfony\Component\Console\Application->doRunCommand()
#13 phar:///usr/bin/phpstan/vendor/symfony/console/Application.php(157): _PHPStan_08e3736ef\Symfony\Component\Console\Application->doRun()
#14 phar:///usr/bin/phpstan/bin/phpstan(116): _PHPStan_08e3736ef\Symfony\Component\Console\Application->run()
#15 phar:///usr/bin/phpstan/bin/phpstan(117): _PHPStan_08e3736ef{closure}()
#16 /usr/bin/phpstan(7): require('...')
#17 {main}
thrown in /.cache/phpstan/phpstan/cache/nette.configurator/Container_3bdd9a7f53.php on line 9316

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

@gitar-bot

gitar-bot Bot commented Oct 15, 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

@codecov

codecov Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 22.54%. Comparing base (22037c6) to head (8d5e548).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
htdocs/kernel/object.php 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##             master     #271   +/-   ##
=========================================
  Coverage     22.54%   22.54%           
+ Complexity     8672     8671    -1     
=========================================
  Files           685      685           
  Lines         46330    46328    -2     
=========================================
  Hits          10444    10444           
+ Misses        35886    35884    -2     

☔ 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.

@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="htdocs/kernel/object.php" line_range="268" />
<code_context>
                         $value = $temp;
                         $this->vars[$key]['value'] = $value;
                     } else {
-                        $this->vars[$key]['value'] = xoops_convert_decode($value);
</code_context>
<issue_to_address>
**Legacy apostrophes retain backslashes**

When a Unicode-array row written by the previous model path contains an apostrophe, `assignVar()` preserves the legacy serialized payload, and `getVar()` decodes its elements without removing the old writer’s extra backslash. The value reloads as `O\'Reilly` instead of `O'Reilly`, so upgraded installations display it incorrectly and saving it again preserves the slash.

Normalize legacy array elements to remove the old writer’s extra escape before returning them from `getVar()`.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and if the encoding or reload logic is still wrong, saving a Unicode-array field can persist malformed serialized data, and reverting the code will not restore rows already written; those records would need repair.

Blocking findings: htdocs/kernel/object.php:268


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread htdocs/kernel/object.php
@mambax7
mambax7 merged commit f4acb2d into XOOPS:master Oct 16, 2026
19 checks passed
@mambax7
mambax7 deleted the fix/unicode-array-roundtrip branch October 16, 2026 01:36
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