Skip to content

Fix PHP 8 fatal/warning on first opt-in without paid plans - #888

Open
Tobias-Conrad wants to merge 1 commit into
Freemius:masterfrom
Tobias-Conrad:fix/php8-fresh-optin-plans-guards
Open

Tobias-Conrad wants to merge 1 commit into
Freemius:masterfrom
Tobias-Conrad:fix/php8-fresh-optin-plans-guards

Conversation

@Tobias-Conrad

Copy link
Copy Markdown

What this fixes

On a fresh first opt-in of a product/store, the SDK can raise two PHP 8 diagnostics before the account carries any plan list (see #879, reproduced on SDK 2.13.1/2.13.4 and current develop):

  1. Fatal error — _store_plans() calls count( $this->_plans ) while $this->_plans is still its default false:

    Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, bool given
    in .../includes/class-freemius.php
    
  2. Warning — FS_Site::__construct() reads $site->plan_id, which the install payload can omit:

    Warning: Undefined property: stdClass::$plan_id in .../includes/entities/class-fs-site.php on line 123
    

Root cause

$this->_plans defaults to false (declaration private $_plans = false;). _set_account() only assigns it when a plan list is passed:

if ( false !== $plans ) {
    $this->_plans = $plans;
}

On a first opt-in without paid plans, $plans stays false, and _store_plans() is still reached via _store_account().

The change (minimal, two spots)

  • _store_plans(): encrypt an empty list when _plans is not an array, instead of calling count() on false.
  • FS_Site::__construct(): read plan_id via isset() and default to null, matching how the property is handled elsewhere.

Both use the exact convention already present in the class (! is_array( $this->_plans ), see _get_plan_by_id() / get_plan_by_name()), so this is consistent with the existing code style rather than a new pattern.

Verification

  • php -l clean on both changed files.
  • Change is a no-op for the normal (array) case — behaviour is unchanged whenever _plans is a real array.
  • Reproduced the fatal on a clean WordPress + PHP 8 first opt-in before the fix; the warning/fatal no longer occur after it.

No other code paths were touched: is_plan() / is_trial_plan() etc. are only reached once is_registered() is true, at which point _plans is already an array — so they are not on the reported crash path.

Fixes #879.

On a fresh first opt-in, _plans defaults to false and _set_account() skips
the assignment when no plan list is passed. Two spots then touched it as if
it were an array/object:

- _store_plans(): count( $this->_plans ) on false -> fatal TypeError (PHP 8).
- FS_Site::__construct(): read $site->plan_id which the install payload can
  omit -> 'Undefined property: stdClass::$plan_id' warning (PHP 8).

Both are now guarded, matching the existing '! is_array( $this->_plans )'
convention used elsewhere in the class.

Refs Freemius#879
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.

Fatal error on first opt-in when plugin has no paid plans (count() on bool _plans)

1 participant