Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Cli/Command/Upgrade.php
<?php

namespace XF\Cli\Command;

use
Symfony\Component\Console\Command\Command;
use
Symfony\Component\Console\Helper\QuestionHelper;
use
Symfony\Component\Console\Input\ArrayInput;
use
Symfony\Component\Console\Input\InputInterface;
use
Symfony\Component\Console\Input\InputOption;
use
Symfony\Component\Console\Output\OutputInterface;
use
Symfony\Component\Console\Question\ConfirmationQuestion;
use
XF\Install\Upgrader;
use
XF\Mvc\Reply\AbstractReply;

use function
is_array, strlen;

class
Upgrade extends Command implements CustomAppCommandInterface
{
    use
JobRunnerTrait;

    public static function
getCustomAppClass()
    {
        return
'XF\Install\App';
    }

    protected function
configure()
    {
       
$this
           
->setName('xf:upgrade')
            ->
setDescription('Upgrades XenForo')
            ->
addOption(
               
'skip-statistics',
               
null,
               
InputOption::VALUE_NONE,
               
'If set (and not already configured), the question to opt into anonymous server statistics collection will be skipped.'
           
);
    }

    protected function
execute(InputInterface $input, OutputInterface $output)
    {
       
/** @var \XF\Install\App $app */
       
$app = \XF::app();
       
$upgrader = new Upgrader($app);
       
$installHelper = new \XF\Install\Helper($app);

        if (
$app->config('legacyExists'))
        {
           
$output->writeln("<error>Cannot start upgrade without a src/config.php file.</error>");
           
$output->writeln("Please use the web interface to create the correct src/config.php file.");
            return
1;
        }
        if (!
$app->config('exists'))
        {
           
$output->writeln("<error>Cannot start upgrade without a src/config.php file.</error>");
           
$output->writeln("Please create a new src/config.php file.");
            return
1;
        }

       
$upgrader->syncUpgradeLogStructure();

       
$lastUpgradeVersion = $upgrader->getLatestUpgradeVersion();

       
$output->writeln(
           
'<info>Current version: '
           
. $lastUpgradeVersion['version_id']
            . (
$lastUpgradeVersion['last_step'] ? " (step $lastUpgradeVersion[last_step])" : '')
            .
'</info>'
       
);
       
$output->writeln(sprintf('<info>Upgrade target: %s (%s)</info>', \XF::$versionId, \XF::$version));

       
$upgradeComplete = ($lastUpgradeVersion['version_id'] === \XF::$versionId && !$lastUpgradeVersion['last_step']);

       
/** @var QuestionHelper $helper */
       
$helper = $this->getHelper('question');

        if (
$upgradeComplete)
        {
           
$startConfirm = 'You are already running the latest version. Rebuild the master data?';
        }
        else
        {
           
$startConfirm = 'Are you sure you want to continue with the upgrade?';
        }

       
$question = new ConfirmationQuestion("<question>$startConfirm [y/n] </question>");

        if (!
$helper->ask($input, $output, $question))
        {
            return
1;
        }

        if (!
$upgradeComplete)
        {
           
$output->writeln('');

           
$controller = new \XF\Install\Controller\Upgrade($app, $app->request());

           
$runVersionId = null;
           
$step = 1;

            if (
$lastUpgradeVersion['last_step'])
            {
               
$runVersionId = $lastUpgradeVersion['version_id'];
               
$step = $lastUpgradeVersion['last_step'] + 1;
            }

            if (!
$runVersionId)
            {
               
$runVersionId = $upgrader->getNextUpgradeVersionId($lastUpgradeVersion['version_id']);
               
$step = 1;
            }

            do
            {
               
$upgrade = $runVersionId ? $upgrader->getUpgrade($runVersionId) : false;
                if (!
$upgrade)
                {
                    break;
                }

               
$upgrader->assertValidUpgradeSteps($upgrade);

               
$position = 0;
               
$data = [];
               
$versionName = $upgrade->getVersionName();

                while (
method_exists($upgrade, 'step' . $step))
                {
                    if (
$position)
                    {
                       
$this->outputStatus($output, "Running upgrade to $versionName, step $step ($position)...");
                    }
                    else
                    {
                       
$this->outputStatus($output, "Running upgrade to $versionName, step $step...");
                    }

                   
$result = $upgrade->{'step' . $step}($position, $data, $controller);
                    if (
$result instanceof AbstractReply)
                    {
                       
$this->clearStatus($output, '<error>This step must be completed via the web interface.</error>');
                        return
2;
                    }
                    else if (
$result === 'complete')
                    {
                       
$this->clearStatus($output, "Running upgrade to $versionName, step $step... done.");
                        break;
                    }
                    else if (
$result === true || $result === null)
                    {
                       
$upgrader->insertUpgradeLog($runVersionId, $step);

                       
$this->clearStatus($output, "Running upgrade to $versionName, step $step... done.");

                       
$step++;
                       
$position = 0;
                       
$data = [];
                    }
                    else if (
is_array($result))
                    {
                       
// stay on the same step
                       
$position = $result[0];
                       
$data = !empty($result[2]) ? $result[2] : [];
                    }
                    else
                    {
                       
$this->clearStatus($output, '<error>The upgrade step returned an unexpected result.</error>');
                        return
3;
                    }
                }

               
$upgrader->insertUpgradeLog($runVersionId);

               
$runVersionId = $upgrader->getNextUpgradeVersionId($runVersionId);
               
$step = 1;
            }
            while (
true);

           
$output->writeln(['<info>All upgrade steps run up to version ' . \XF::$version . '.</info>', '']);

           
$upgrader->insertUpgradeLog(\XF::$versionId);
        }

       
$originalVersion = $upgrader->getCurrentVersion();

       
$devOutput = $app->developmentOutput();
        if (
$devOutput->isEnabled() && $devOutput->isCoreXfDataAvailable())
        {
           
$command = $this->getApplication()->find('xf-dev:import');
           
$childInput = new ArrayInput([
               
'command' => 'xf-dev:import',
               
'--addon' => 'XF'
           
]);
           
$command->run($childInput, $output);

           
$extraJobs = $upgrader->getExtraUpgradeJobsMap();
           
$installHelper->insertRebuildJob(null, $extraJobs, false, $originalVersion);

           
$this->runJob($installHelper->getDefaultRebuildJobName(), $output);
        }
        else
        {
           
$extraJobs = $upgrader->getExtraUpgradeJobsMap();
           
$installHelper->insertRebuildJob(null, $extraJobs, true, $originalVersion);

           
$this->runJob($installHelper->getDefaultRebuildJobName(), $output);
        }

        if (
$upgrader->isUpgradeComplete())
        {
           
$upgrader->completeUpgrade();

           
$schemaErrors = $upgrader->getDefaultSchemaErrors();
            if (
$schemaErrors)
            {
               
$output->writeln("Upgrade completed but errors were found:");
                foreach (
$schemaErrors AS $error)
                {
                   
$output->writeln("\t* $error");
                }
               
$output->writeln("This is likely caused by an add-on conflict. You may need to restore a backup, remove the offending add-on data from the database, and retry the upgrade. Contact support if you are not sure how to proceed.");
                return
4;
            }
            else if (!
$upgradeComplete)
            {
               
$output->writeln("Upgrade completed successfully.");

               
$outdatedTemplates = $app->repository('XF:Template')->countOutdatedTemplates();
                if (
$outdatedTemplates)
                {
                   
$output->writeln("");
                   
$output->writeln(
                       
"Note: outdated templates have been detected. This is normal after upgrading."
                       
. " This can be resolved by visiting the outdated templates section of the control panel."
                       
. " Incorporating template changes is important to ensure new features work properly and bug fixes take effect."
                   
);
                   
$output->writeln("");
                }
            }
            else
            {
               
$output->writeln("Rebuild completed successfully.");
            }
        }
        else
        {
           
$output->writeln("Upgrade failed to complete!");
            return
5;
        }

        \
XF::triggerRunOnce();

       
$options = \XF::options();
       
$session = \XF::session();

        if (!
$input->getOption('skip-statistics'))
        {
            if (empty(
$options->collectServerStats['configured'])
                ||
$session->repromptStatsCollection
           
)
            {
               
$serverStats = [
                   
'configured' => 1,
                   
'enabled' => 0
               
];

                if (
$session->repromptStatsCollection)
                {
                   
$output->writeln("");
                   
$output->writeln("<info>We are making some changes to the anonymous statistics that we collect.</info>");
                    foreach ((array)
$session->repromptStatsCollectionReasons AS $reason)
                    {
                       
$output->writeln("\t<info>* $reason</info>");
                    }
                }

               
$question = new ConfirmationQuestion("<question>Send anonymous usage statistics (PHP, MySQL, XF usage)? (y/n)</question> ");
                if (
$helper->ask($input, $output, $question))
                {
                   
$serverStats['enabled'] = 1;
                }

               
/** @var \XF\Repository\Option $optionRepo */
               
$optionRepo = \XF::repository('XF:Option');
               
$optionRepo->updateOptions([
                   
'collectServerStats' => $serverStats
               
]);

               
$session->remove('repromptStatsCollection');
               
$session->remove('repromptStatsCollectionReasons');
            }
        }

        return
0;
    }

    protected
$lastStatusLength;

    protected function
outputStatus(OutputInterface $output, $status)
    {
        if (
$this->lastStatusLength && $this->lastStatusLength > strlen($status))
        {
           
$status = str_pad($status, $this->lastStatusLength, " ", STR_PAD_RIGHT);
        }

       
$output->write("\r$status");
       
$this->lastStatusLength = strlen($status);
    }

    protected function
clearStatus(OutputInterface $output, $extraMessage = null)
    {
       
$this->outputStatus($output, '');
       
$output->write("\r");
        if (
$extraMessage !== null)
        {
           
$output->writeln($extraMessage);
        }
    }
}