Skip to content

fix(mort): PHP Fatal errors and compatibility issues with DokuWiki Mort#83

Open
eduardomozart wants to merge 12 commits into
dwp-forge:masterfrom
eduardomozart:fix/bibtex-mort
Open

fix(mort): PHP Fatal errors and compatibility issues with DokuWiki Mort#83
eduardomozart wants to merge 12 commits into
dwp-forge:masterfrom
eduardomozart:fix/bibtex-mort

Conversation

@eduardomozart

@eduardomozart eduardomozart commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Description

This PR resolves multiple compatibility issues with recent versions of DokuWiki ("Mort" and newer) that cause the wiki to crash with PHP Fatal errors or fail to parse references (Closes #82).

Recent DokuWiki updates significantly modified the parser architecture and removed deprecated core files, which impacted both the custom BibTeX parser and the reference rendering logic.

1. BibTeX Parser Abstract Method and Lexer Offset Errors

Recent DokuWiki updates modified the parser architecture in several ways:

  • Added abstract public function handle(...) to the \dokuwiki\Parsing\ParserMode\AbstractMode base class. Since refnotes_bibtex_mode extends this base class but delegates handling via mapped handlers ($this->Lexer->mapHandler), the method was missing, violating the class contract.
  • Introduced a dependency injection system in Parser::addMode() which strictly expects the parser to use the native DokuWiki ModeRegistry and Handler. Because refnotes uses a custom handler, this resulted in an uninitialized typed property access error.
  • Changed the Lexer::reduce() method signature to explicitly require a string byte $offset argument instead of relying on string slicing (substr).
  • Modified how regular expressions are evaluated in the Lexer. Because the new Lexer passes $offset to preg_match with PREG_OFFSET_CAPTURE instead of slicing strings, patterns starting with ^ (caret) incorrectly anchored to the beginning of the entire document rather than the current offset, causing the BibTeX parser to silently fail to match fields (such as Title, Author, and namespace).
  • Replaced the legacy Lexer mode tracking ($_mode) with a dedicated \dokuwiki\Parsing\Lexer\StateStack object. Because the refnotes Lexer implementation reuses the same Lexer instance across multiple pages, the failure to reset this new mode stack resulted in catastrophic state leakage. If a previous page contained a typo like an unclosed quote (title = "unfinished string) or an unclosed bracket (author = {missing bracket), the Lexer would reach the end of that page while still stuck inside string_value_quoted or string_value_braced mode. When it parsed the next page, it would start in that broken mode and immediately crash with a Call to a member function addToken() on null error as soon as it encountered nested braces (e.g., author = "Till {Brehm}").

Changes Made:

  • Added an empty handle() method to refnotes_bibtex_mode in bibtex.php returning false.
  • Typed the handler parameter as Doku_Handler to leverage DokuWiki's native backward-compatibility alias layer (inc/legacy.php). This ensures the plugin remains compatible with both older DokuWiki releases and modern ones utilizing the new dokuwiki\Parsing\Handler namespace.
  • Overrode the addMode() method in refnotes_bibtex_parser to bypass the core DokuWiki dependency injection entirely, successfully decoupling the custom BibTeX parser from DokuWiki's internal plugin routing mechanisms.
  • Updated the parse() loop in refnotes_bibtex_lexer to correctly track and pass the $offset parameter to Lexer::reduce() and Lexer::dispatchTokens(), aligning with the newer parsing performance optimizations without infinite looping.
  • Removed the ^ anchors from the $this->entryPattern strings inside refnotes_bibtex_entry_mode, refnotes_bibtex_field_mode, and refnotes_bibtex_string_value_mode to ensure compatibility with the updated Lexer's non-slicing offset mechanism.
  • Replaced the ^ anchor with the PCRE \G anchor in $this->specialPattern inside refnotes_bibtex_integer_value_mode to properly parse unquoted integers (e.g. year = 2026) at the Lexer's current offset.
  • Completely fixed the state leakage crashes (which started occurring in DokuWiki Mort due to its new StateStack architecture) by explicitly re-initializing $this->modeStack to 'base' at the beginning of refnotes_bibtex_lexer::parse().

2. Missing inc/indexer.php Error

DokuWiki Mort completely removed the inc/indexer.php file in favor of the new object-oriented search classes (tracked in dokuwiki/dokuwiki#4646). When running the plugin on a recent installation, it crashes with the following error during the SearchIndex manager's Rebuild index process:

Error: Failed opening required '/home/example/web/wiki.example.com/public_html/inc/indexer.php' (include_path='.:/usr/share/php')
/home/example/web/wiki.example.com/public_html/lib/plugins/refnotes/database.php(109)

Changes Made:

  • Modified database.php to conditionally use the new \dokuwiki\Search\Indexer class to retrieve the index of pages if available on newer DokuWiki versions.
  • Added a file_exists check before requiring inc/indexer.php and retained the usage of idx_getIndex as a fallback, ensuring compatibility with older DokuWiki versions where the legacy system is still used.

3. "Attempt to modify property 'calls' on null" and Context Imbalance Errors

DokuWiki Mort changed how parsers are initialized for nested snippets, introducing a ModeRegistry with pooled sub-parsers. Because refnotes caches the Lexer during initialization but triggers the snippet parse later, the Lexer would attempt to write instruction calls to an unset object reference. Additionally, attempting to use sub-parsers directly caused an imbalance in the PARSER_WIKITEXT_PREPROCESS and PARSER_HANDLER_DONE hooks, leading to an empty parsing context stack and crashing the plugin entirely (Call to a member function canHandle() on false).

Error: Attempt to modify property "calls" on null
/home/example/web/wiki.example.com/public_html/inc/Parsing/Handler/CallWriter.php(23)

Error: Call to a member function canHandle() on false
/home/example/web/wiki.example.com/public_html/lib/plugins/refnotes/core.php(120)

Changes Made:

  • Modified refnotes_parser_core::getInstructions() in core.php to detect newer versions of DokuWiki by checking for getModeRegistry().
  • Replaced the custom call-hijacking mechanism with DokuWiki's native p_get_instructions() API when available. This spawns a clean, isolated parser for footnote text and perfectly balances the PARSER_WIKITEXT_PREPROCESS and PARSER_HANDLER_DONE events, restoring compatibility while remaining backward-compatible with older DokuWiki versions (which still use the legacy fallback).

Note for users upgrading:
After applying this update on an existing installation, you may need to:

  1. Delete the plugin's cached database index at data/cache/refnotes_database.index (so the BibTeX fields can be re-parsed).
  2. Rebuild the wiki search index through the SearchIndex Manager.
  3. If using the SearchIndex plugin, you may also need to apply the patch for splitbrain/dokuwiki-plugin-searchindex#61 to ensure the new DokuWiki Indexer is fully supported.

@eduardomozart eduardomozart changed the title fix(mort): PHP Fatal error by implementing abstract handle() method fix(mort): PHP Fatal errors and compatibility issues with DokuWiki Mort Jul 21, 2026
Reinitialize the BibTeX lexer mode stack to the `base` state at the start of parsing. This prevents stale parser state from previous runs in DokuWiki's Mort lexer and ensures each parse begins with a clean, consistent lexer state.
Reinitialize the BibTeX lexer mode stack to the `base` state at the start of parsing. This prevents stale parser state from previous runs in DokuWiki's Mort lexer and ensures each parse begins with a clean, consistent lexer state.
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.

Error coming up as of late

1 participant