protected function plan(int $from, int $to)
    {
        $migrations = [];
        
        if ($from === $to)
            return $migrations;

        $is_downgrade = $to < $from;
        $reachable = $this->migrations[$from] ?? [];

        // For downgrades, we want the lowest reachable target that is above or equal to the final target
        // For upgrades, we want the highest reachable target that is below or equal to the final target
        // To always use the first encountered, the array needs to be reversed for upgrades, as it's sorted by key
        if (!$is_downgrade)
            $reachable = array_reverse($reachable, true);

        // Traverse the reachable migrations
        foreach ($reachable as $direct_to => $path)
        {
            if (
                ($direct_to === $to)                || // Bingo
                ($is_downgrade && $direct_to > $to) || // Proper downgrade
                (!$is_downgrade && $direct_to < $to)   // Proper upgrade
            )
            {
                // In the right direction, so add to the path
                $migrations[] = ['from' => $from, 'to' => $direct_to, 'file' => $path];
                break;
            }
        }

        if (count($migrations) === 0)
            throw new MigrationException("No migration path from version $from to $to for module {$this->module}");

        $last = end($migrations);
        if ($last['to'] !== $to)
        {
            $rest = $this->plan($last['to'], $to);
            foreach ($rest as $migration)
                $migrations[] = $migration;
        }

        return $migrations;
    }
protected function plan(int $from, int $to)
    {
        $migrations = [];
        
        if ($from === $to)
            return $migrations;

        $is_downgrade = $to < $from;
        $reachable = $this->migrations[$from] ?? [];

        // For downgrades, we want the lowest reachable target that is above or equal to the final target
        // For upgrades, we want the highest reachable target that is below or equal to the final target
        // To always use the first encountered, the array needs to be reversed for upgrades, as it's sorted by key
        if (!$is_downgrade)
            $reachable = array_reverse($reachable, true);

        // Traverse the reachable migrations
        foreach ($reachable as $direct_to => $path)
        {
            if (
                ($direct_to === $to)                || // Bingo
                ($is_downgrade && $direct_to > $to) || // Proper downgrade
                (!$is_downgrade && $direct_to < $to)   // Proper upgrade
            )
            {
                // In the right direction, so add to the path
                $migrations[] = ['from' => $from, 'to' => $direct_to, 'file' => $path];
                break;
            }
        }

        if (count($migrations) === 0)
            throw new MigrationException("No migration path from version $from to $to for module {$this->module}");

        $last = end($migrations);
        if ($last['to'] !== $to)
        {
            $rest = $this->plan($last['to'], $to);
            foreach ($rest as $migration)
                $migrations[] = $migration;
        }

        return $migrations;
    }
protected function plan ( int $ from , int $ to ) { $ migrations = [ ] ; if ( $ from === $ to ) return $ migrations ; $ is_downgrade = $ to < $ from ; $ reachable = $ this -> migrations [ $ from ] ?? [ ] ; // For downgrades, we want the lowest reachable target that is above or equal to the final target // For upgrades, we want the highest reachable target that is below or equal to the final target // To always use the first encountered, the array needs to be reversed for upgrades, as it's sorted by key if ( ! $ is_downgrade ) $ reachable = array_reverse ( $ reachable , true ) ; // Traverse the reachable migrations foreach ( $ reachable as $ direct_to => $ path ) { if ( ( $ direct_to === $ to ) || // Bingo ( $ is_downgrade && $ direct_to > $ to ) || // Proper downgrade ( ! $ is_downgrade && $ direct_to < $ to ) // Proper upgrade ) { // In the right direction, so add to the path $ migrations [ ] = [ 'from' => $ from , 'to' => $ direct_to , 'file' => $ path ] ; break ; } } if ( count ( $ migrations ) === 0 ) throw new MigrationException ( "No migration path from version $from to $to for module {$this->module}" ) ; $ last = end ( $ migrations ) ; if ( $ last [ 'to' ] !== $ to ) { $ rest = $ this -> plan ( $ last [ 'to' ] , $ to ) ; foreach ( $ rest as $ migration ) $ migrations [ ] = $ migration ; } return $ migrations ; }
Plan a path through available migrations to reach a specified version
from another version. This is always done in one direction,
opportunistically.  This means that when downgrading, no intermediate
upgrades are performed, even if they may result in shorter path.

Whenever a more than one step is available, the larger one is selected.

Potentially, this could result in sinks with no way out. Design your
upgrade trajectories with this in mind.

@param $from The source version to start planning from
@param $to The target version to plan towards
@return array The list of migrations to execute. When $from and $to are equal,
an empty array is returned.
@throws MigrationException When no migration path can be found
Plan a path through available migrations to reach a specified version from another version . This is always done in one direction opportunistically . This means that when downgrading no intermediate upgrades are performed even if they may result in shorter path .