Skip to content

BOGO Select for WooCommerce 2.3.3

Choose a tag to compare

@johnjanney johnjanney released this 02 Aug 02:14
· 49 commits to main since this release

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": false and
    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,
    since null and false are 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() and BOGO_Select_Blocks::error() end the request —
    one sends a JSON error, the other throws — and both were documented as
    returning void, so everything after a call to them looked reachable and the
    guard above it looked pointless. Both are never now, 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() returning WC_Product|false had 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 a WC_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(), whose true
    answer is proof there is a product, since it starts with an instanceof.
    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-true states 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.
    Every array in a docblock now says what it holds and every method declares a
    return type: 34 void declarations 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.