Skip to content

[BUGFIX] Remove shared static state from getLastPagevisit() - #78

Open
t3-vfm wants to merge 2 commits into
in2code-de:developfrom
t3-vfm:feature/fix-static-last-pagevisit
Open

t3-vfm wants to merge 2 commits into
in2code-de:developfrom
t3-vfm:feature/fix-static-last-pagevisit

Conversation

@t3-vfm

@t3-vfm t3-vfm commented Sep 17, 2026

Copy link
Copy Markdown

What

Visitor::getLastPagevisit() caches its result in a static variable. A static variable inside an
instance method is bound to the method, not to the object, so as soon as the first visitor yields a
non-null value, every subsequent visitor in the same PHP process receives that same Pagevisit.

getPagevisitLast() already performs exactly the same lookup without the static, so the method now
delegates to it. This removes the bug and the duplicated logic at once.

Why it matters

Scoring is silently corrupted. ScoringService::getNumberOfDaysSinceLastVisit() uses the getter,
and LuxServiceRecalculateScoringCommand iterates all visitors in a single process:

$visitors = $visitorRepository->findAll();
foreach ($visitors as $visitor) {
    $scoringService->calculateAndSetScoring($visitor);
}

After the first visitor that has any page visit, every remaining visitor is scored with that
visitor's lastVisitDaysAgo, which the default formula subtracts directly. A wrong value is written
to the database for the whole visitor table.

The lead summary mail shows one timestamp for all leads, because getDateOfLastVisit() delegates
to the same getter.

Evidence

Three different visitors, each with their own page visits, on 44.3.0:

visitor  | getPagevisitLast()     | getLastPagevisit()
---------+------------------------+-----------------------
112      | pagevisit 803          | pagevisit 803
122      | pagevisit 480490       | pagevisit 803     <-- wrong
140      | pagevisit 15092        | pagevisit 15092 (after fix)

Before the change getLastPagevisit() returns 1 distinct value for the three visitors, after it
returns 3 — verified against 44.3.0 in a TYPO3 13.4 / PHP 8.2 installation.

A summary mail generated on the same installation showed 11 distinct "last visit" pages across 35
leads but only 1 distinct timestamp.

Notes

If the caching was intentional, a plain instance property would achieve it correctly — happy to
change the implementation if you prefer that.

A static variable inside an instance method is bound to the method, not
to the object. As soon as the first visitor yielded a non-null value,
every subsequent visitor in the same PHP process returned that same
Pagevisit. The intended per-object caching never happened.

This silently corrupted scoring: ScoringService uses the getter in
getNumberOfDaysSinceLastVisit(), and LuxServiceRecalculateScoringCommand
iterates all visitors in a single process, so lastVisitDaysAgo was wrong
for every visitor but the first. It also made the lead summary mail show
one single timestamp for all leads.

getPagevisitLast() already performs exactly the same lookup without the
static, so the method now delegates to it. This removes the bug and the
duplicated logic at once.
Before `static` was used inside the getter. Unfortunately `static`
is bound to the method and not to the individual object.
When calling `getLastPagevisit()` on object a and calling it later again
on object b the cached value of object a was returned.

To fix this, set a dynamic property for the model so the value is cached
individually.
Additionally, tests were added to check if the value is cached and if
the value leaks between visitor objects.

fixes: in2code-de#81
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