BOGO Select for WooCommerce 2.3.3
Install: download bogo-select-2.3.3.zip below, then WordPress → Plugins → Add New → Upload Plugin.
[2.3.3] — 2026-08-02
Three more levels of the analyser, and the one thing they found.
Levels 6 and 7 changed documentation only. Level 8 changed runtime code, which
is why this is a release rather than three annotation commits: null and
false were both being used to mean "no product", and one of them has stopped.
Behaviour is identical — every caller was testing truthiness — so this is a
PATCH.
Changed
-
Static analysis raised to level 8, which checks what becomes of a null.
Eighteen findings, and this level touched runtime code where 6 and 7 had not.wc_get_product()has two ways of saying "there is no product":falseand
null. Three functions passed both straight through while declaring only
WC_Product|false, and every one of their callers was doing a truthiness
test anyway — so the distinction had never meant anything to anyone. They now
fold it at the boundary and return one falsy answer. Behaviour is unchanged,
sincenullandfalseare both falsy and no caller ever compared strictly;
what changes is that a caller now has one absent case to handle instead of
two.The rest were places where the analyser could not see that execution stops.
BOGO_Select_Ajax::fail()andBOGO_Select_Blocks::error()end the request —
one sends a JSON error, the other throws — and both were documented as
returningvoid, so everything after a call to them looked reachable and the
guard above it looked pointless. Both arenevernow, which resolved
thirteen findings between them. -
Static analysis raised to level 7. Level 7 checks that a union type is
narrowed before it is used, and it found seven places where
wc_get_product()returningWC_Product|falsehad gone unnoticed. All seven
were the same story with two different endings.Three are functions that deliberately answer for the
false— a deleted
product is "no longer available", and claims no stock — while their signatures
claimed to require aWC_Product. Each one's very first line is the guard
that handles it. The documentation was understating the code, so the
documentation changed and nothing else did.The other four all trace back to
is_offerable_variation(), whosetrue
answer is proof there is a product, since it starts with aninstanceof.
Nothing said so, and callers went on to use the result without asking again —
correct, but resting on an invariant no tool could see and nothing recorded.
A@phpstan-assert-if-truestates it once, in the one place that establishes
it. Removing that line brings all four findings straight back, which is how it
was checked rather than assumed. -
Static analysis raised to level 6, which is the step 2.3.2 named as next.
Everyarrayin a docblock now says what it holds and every method declares a
return type: 34voiddeclarations and 46 array types, still with no baseline
and nothing suppressed. No defect was found — that was the prediction, and it
held.Three of the array types are shapes rather than
array<string,mixed>, and
those are the ones worth more than documentation.get_choice_page()and the
two methods behind it declare
array{ids: int[], page: int, pages: int, total: int}, which the analyser
checks against what they actually return, so a key added, renamed, or dropped
is an error rather than something the chooser discovers later. The other 43
are honestly open-ended: a settings row and a WooCommerce cart item have no
fixed shape, and inventing one would document a guess.