callable, or null for the built-in steps(). * * @var array|null */ private $steps; /** * The plugin version the code is at. * * @var string */ private $current; /** * Constructor. * * @param array|null $steps Upgrade steps; null for the built-in ones. * @param string|null $current The code's version; null for YANGRA_CONDITIONAL_VERSION. */ public function __construct( ?array $steps = null, ?string $current = null ) { $this->steps = $steps; $this->current = null !== $current ? $current : YANGRA_CONDITIONAL_VERSION; } /** * Registers hooks. Runs early on `init`, before other code reads settings. * * @return void */ public function register(): void { add_action( 'init', array( $this, 'maybe_upgrade' ), 1 ); } /** * Runs the steps for every version above the saved one, then saves the * current version. Does nothing when the saved version is current or newer * (e.g. after a downgrade). * * A missing option means the version was never recorded, so every step * runs. Steps must be safe to run more than once. * * @return void */ public function maybe_upgrade(): void { $saved = (string) get_option( self::VERSION_OPTION, '' ); if ( '' !== $saved && version_compare( $saved, $this->current, '>=' ) ) { return; } $steps = $this->steps(); uksort( $steps, 'version_compare' ); foreach ( $steps as $version => $step ) { if ( version_compare( (string) $version, $this->current, '>' ) ) { break; } if ( '' === $saved || version_compare( $saved, (string) $version, '<' ) ) { call_user_func( $step ); } } update_option( self::VERSION_OPTION, $this->current, false ); } /** * Upgrade steps, as the version that needs them => callable. Add a step * whenever a release changes stored settings, options or cron events. * * @return array */ public function steps(): array { if ( null !== $this->steps ) { return $this->steps; } return array(); } }