moved code to class, cleaned up, removed requirement of composer.phar in scripts, added ajaxified status updates, redirection to install path and back

pull/31/head
Daniel Klabbers 2016-01-21 15:25:05 +01:00
parent 743e1884ba
commit 4a63d51338
5 changed files with 329 additions and 97 deletions

View File

@ -8,98 +8,13 @@
* file that was distributed with this source code.
*/
// prevent all those questions concerning ::class and possible composer installation errors (5.3+)
if (PHP_VERSION_ID < 50509) {
throw new Exception('At least PHP 5.5.9 is required to make Flarum work. See the system requirements: http://flarum.org/docs/installation/#system-requirements');
} elseif (file_exists('vendor/autoload.php')) {
if (file_exists('vendor/autoload.php')) {
require_once 'vendor/autoload.php';
/**
* Start up the Flarum installation wizard or the Forums.
*/
require 'vendor/autoload.php';
(new Flarum\Forum\Server(__DIR__))->listen();
// delete /_install_tmp using composer functionality
if (is_dir('_install_tmp') && file_exists('_install_tmp/composer/vendor/autoload.php')) {
if (array_key_exists('install-done', $_GET)) {
echo 1;
exit;
}
// include the extracted composer libraries
require_once '_install_tmp/composer/vendor/autoload.php';
$fs = new Composer\Util\Filesystem();
$fs->removeDirectory('_install_tmp');
}
$server = new Flarum\Forum\Server(__DIR__);
$server->listen();
} elseif ((ini_get('allow_url_fopen') && !ini_get('phar.readonly')) || file_exists('scripts/composer.phar')) {
if (array_key_exists('install-done', $_GET)) {
echo 0;
exit;
}
// prevent time out of page, drawback of running in browser
@set_time_limit(0);
// prevent extracting composer anew if already exists
if (!is_dir('_install_tmp/composer')) {
// attempt to download the latest composer file
if (!file_exists('scripts/composer.phar') && ini_get('allow_url_fopen')) {
file_put_contents('https://getcomposer.org/installer', 'scripts/composer.phar');
}
// use Phar to extract the composer package
$composer = new Phar('scripts/composer.phar');
// create a temporary directory for saving the package
mkdir('_install_tmp');
// extract composer
$composer->extractTo('_install_tmp/composer');
}
// if extraction succeeded, let's run the update command.
if (is_dir('_install_tmp/composer') && file_exists('_install_tmp/composer/vendor/autoload.php')) {
// force memory to at least 1GB (default for composer) otherwise composer will run out of memory
if (function_exists('ini_set')) {
ini_set('memory_limit', '1G');
}
// show an installation html
include "storage/views/composer-installation.html";
// force the template to the user
ob_flush();
// sets a home directory for storing information
putenv('COMPOSER_HOME=' . getcwd() . '/_install_tmp/home');
// prevents any interaction composer might require
putenv('COMPOSER_NO_INTERACTION=true');
require_once '_install_tmp/composer/vendor/autoload.php';
// run the composer installation command
$application = new Composer\Console\Application();
// disable auto exit
$application->setAutoExit(false);
// first set the github token to prevent installation errors
$input = new \Symfony\Component\Console\Input\ArrayInput([
'command' => 'config',
'github-oauth.github.com' => 'ec785da935d5535e151f7b3386190265f00e8fe2'
]);
$application->run($input);
// set the input for the composer install command
$input = new Symfony\Component\Console\Input\ArrayInput([
'command' => 'install',
'--no-dev' => true,
'--prefer-dist' => true,
'--optimize-autoloader' => true,
'-q' => true
]);
$application->run($input);
// application installation is now done, redirect to Flarum installer
// we cannot send a header or redirect because we're already flushed the buffer
}
} else {
// todo provide proper error message
throw new Exception('This method of installation is currently unsupported.');
} elseif (file_exists('install/index.php')) {
$url = 'http://' . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/') . '/install/index.php';
header("Location:{$url}", true, 301);
exit;
}

13
install/index.php Normal file
View File

@ -0,0 +1,13 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require_once 'src/Installer.php';
(new Flarum\Installer\Installer(__DIR__ . '/../'))->listen();

297
install/src/Installer.php Normal file
View File

@ -0,0 +1,297 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Installer;
use Composer\Console\Application;
use Phar;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Filesystem\Filesystem;
class Installer
{
const GITHUB_TOKEN = 'ec785da935d5535e151f7b3386190265f00e8fe2';
/**
* Absolute path to Flarum installation.
*
* @var string
*/
protected $basePath;
/**
* Absolute path to directory for storing temporary installation files.
*
* @var string
*/
protected $tmpPath;
/**
* The installation path, temporary files needed for running the Installer.
*
* @var string
*/
protected $installPath;
public function __construct($basePath)
{
$this->setBasePath($basePath);
$this->setTmpPath($this->getBasePath('/storage/tmp'));
$this->setInstallPath();
$this->setupEnvironment();
}
/**
* Sets up environment specifics
*/
public function setupEnvironment()
{
// prevent time out of page, drawback of running in browser
@set_time_limit(0);
error_reporting(E_ALL);
// sets a home directory for storing information
putenv('COMPOSER_HOME=' . $this->getTmpPath('/composer-home'));
// prevents any interaction composer might require
putenv('COMPOSER_NO_INTERACTION=true');
// force memory to at least 1GB (default for composer) otherwise composer will run out of memory
if (function_exists('ini_set')) {
ini_set('memory_limit', '1G');
}
}
/**
* @param string $basePath
* @return Installer
*/
public function setBasePath($basePath)
{
$this->basePath = realpath($basePath);
return $this;
}
/**
* @param string $path
* @return string
*/
public function getBasePath($path = '')
{
return $this->basePath . $path;
}
/**
* @return Installer
*/
public function setInstallPath()
{
$this->installPath = realpath(__DIR__ . '/../');
return $this;
}
/**
* @param string $path
* @return string
*/
public function getInstallPath($path = '')
{
return $this->installPath . $path;
}
/**
* @param string $tmpPath
* @return Installer
*/
public function setTmpPath($tmpPath)
{
$this->tmpPath = realpath($tmpPath);
return $this;
}
/**
* @param string $path
* @return string
*/
public function getTmpPath($path = '')
{
return $this->tmpPath . $path;
}
/**
* Reports about specific stages during installation.
*
* @param null $state
* @return bool|string
*/
protected function progressReport($state = null)
{
switch ($state) {
case 'phar-downloaded':
return file_exists($this->getTmpPath('/composer.phar'));
case 'phar-extracted':
return file_exists($this->getTmpPath('/composer-phar-extracted/vendor/autoload.php'));
case 'dependencies-loaded':
return file_exists($this->getBasePath('/vendor/autoload.php'));
case 'cleaned-up':
return $this->progressReport('dependencies-loaded') && !$this->progressReport('phar-extracted');
}
}
/**
* Generates a json payload identifying the current state of installing.
*
* @return json
*/
public function getCurrentState()
{
$state = null;
if ($this->progressReport('cleaned-up')) {
$state = true;
} elseif ($this->progressReport('dependencies-loaded')) {
$state = 'Dependencies downloaded. Cleaning up ..';
} elseif ($this->progressReport('phar-extracted')) {
$state = 'Composer extracted. Loading dependencies, this will take the longest ..';
} elseif ($this->progressReport('phar-downloaded')) {
$state = 'Composer downloaded. Extracting ..';
}
return json_encode($state);
}
/**
* Listens for HTTP connections.
*/
public function listen()
{
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
return $this->listenAjax();
}
// show temporary installation page
ob_end_clean();
ob_start();
include $this->getInstallPath('/views/composer-installation.html');
ob_end_flush();
$this->runInstall();
}
/**
* Listens for XHR calls.
*/
public function listenAjax()
{
echo $this->getCurrentState();
}
/**
* Runs the actual installation steps.
*/
protected function runInstall()
{
if (!$this->progressReport('phar-extracted') && !$this->progressReport('phar-downloaded')) {
$this->downloadComposerPhar();
}
if (!$this->progressReport('phar-extracted') && $this->progressReport('phar-downloaded')) {
$this->extractComposerPhar();
}
if ($this->progressReport('phar-extracted')) {
$this->installDependencies();
}
if ($this->progressReport('dependencies-loaded')) {
$this->cleanUp();
}
}
/**
* Downloads the composer.phar.
*/
protected function downloadComposerPhar()
{
$c = curl_init('https://getcomposer.org/composer.phar');
curl_setopt_array($c, [
CURLOPT_RETURNTRANSFER => true
]);
$phar = curl_exec($c);
curl_close($c);
file_put_contents($this->getTmpPath('/composer.phar'), $phar);
unset($phar);
}
/**
* Extracts the composer.phar.
*/
protected function extractComposerPhar()
{
$composer = new Phar($this->getTmpPath('/composer.phar'));
$composer->extractTo($this->getTmpPath('/composer-phar-extracted'), null, true);
unset($composer);
}
/**
* Installs dependencies using composer phar packages.
*/
protected function installDependencies()
{
require_once $this->getTmpPath('/composer-phar-extracted/vendor/autoload.php');
chdir($this->getBasePath());
// run the composer installation command
$application = new Application();
// disable auto exit
$application->setAutoExit(false);
// first set the github token to prevent installation errors
$input = new ArrayInput([
'command' => 'config',
'github-oauth.github.com' => self::GITHUB_TOKEN
]);
$application->run($input);
// set the input for the composer install command
$input = new ArrayInput([
'command' => 'install',
'--no-dev' => true,
'--prefer-dist' => true,
'--optimize-autoloader' => true,
'-q' => true
]);
$application->run($input);
unset($input, $application);
chdir($this->getInstallPath());
}
/**
* Cleans up all tmp files.
*/
protected function cleanUp()
{
$fs = new Filesystem();
$fs->remove([
$this->getTmpPath('/composer.phar'),
$this->getTmpPath('/composer-home'),
$this->getTmpPath('/composer-phar-extracted'),
]);
}
}

View File

@ -42,17 +42,24 @@
You are installing Flarum without composer. We need to download some missing packages. This will take a few
minutes to complete.
</p>
<p class="status">
</p>
</div>
</header>
</div>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
setInterval(function() {
$.ajax('?install-done', {
success: function(done) {
console.log(done);
if(done == 1) {
location.reload();
var statusElement = $('p.status');
$.ajax('#', {
method: 'post',
success: function(state) {
console.log(state);
if(state == 'true') {
location.replace(location.pathname.replace('/install/index.php', ''));
} else if(statusElement.text() != state) {
statusElement.text(state);
}
}
});

Binary file not shown.