esedic
4/11/2017 - 9:12 AM

A php script to download and install selected Joomla release

A php script to download and install selected Joomla release

<?php

/*
** Download index.php file from https://raw.githubusercontent.com/bestproject/getjoomla.php/master/index.php
** Place it with FTP client to the root directory of your domain.
** Open your domain in browser.
** Select release (latest version is selected by default)
** Click on Install
** Script downloads selected release, unpacks, creates .htaccess file, removes itself and redirects you to installation process.
*/

// Enable Error reporting
ini_set('display_errors', -1);
error_reporting(E_ALL);

// Disable caching
header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() - 3600));

// Get script base url
$baseURL = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$baseURL = explode(basename(__FILE__), $baseURL);
$baseURL = current($baseURL);

// Disable execution time limit
set_time_limit(0);

class Installer
{
	/**
	 * CURL connection handle.
	 * 
	 * @var   Resource
	 */
	protected $connection = null;

	/**
	 * Gitbhub data cache
	 *
	 * @var   Array
	 */
	protected $cache = array(
		'versions' => array(),
		'latest' => ''
	);

	/**
	 * Load cache
	 */
	public function __construct()
	{

		// Load cache if exists and is fresh (not older then hour)
		$path = __DIR__.'/getjoomla.cache';
		if (
			file_exists($path) AND
			filemtime($path) > (time() - (60 * 15)) AND
			$buffer = json_decode(file_get_contents($path))
		) {
			$this->cache = $this->objectToArray($buffer);
		}
	}

	/**
	 * Converts object to multidimensional array
	 *
	 * @param   Object   $object   Object to conver
	 * 
	 * @return   Array
	 */
	protected function objectToArray($object)
	{
		$buffer = array();
		foreach ($object AS $variable => $data) {
			if (is_object($data)) {
				$buffer[$variable] = $this->objectToArray($data);
			} else {
				$buffer[$variable] = $data;
			}
		}
		return $buffer;
	}

	/**
	 * Get contents of a file via URL (http)
	 *
	 * @param   String   $url   URL of a file.
	 *
	 * @return   String
	 */
	protected function getURLContents($url){
		if( function_exists('curl_init') ) {
			
			// Prepre CURL connection
			$this->prepareConnection($url);

			// Return response
			$buffer = curl_exec ($this->connection);

		} else {
			$options = $file_get_contents_options = array(
				'ssl' => array(
					"verify_peer" => false,
					"verify_peer_name" => false
				),
				'http' => array(
					'user_agent' => $_SERVER['HTTP_USER_AGENT']
				)
			);
			
			$buffer = file_get_contents(
				$url, false,
				stream_context_create($options)
			);
		}

		return $buffer;
	}

	/**
	 * Prepare CURL connection.
	 *
	 * @param   String   $url    URL to be used in connection.
	 * @param   String   $handle File handle to be used in connection.
	 */
	protected function prepareConnection($url = null, $handle = null){

		// Connection needs to be created
		if( !is_resource($this->connection) ) {

			// Initialise connection
			$this->connection = curl_init();

			// Configure CURL
			curl_setopt($this->connection, CURLOPT_RETURNTRANSFER, true);
			curl_setopt($this->connection, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt($this->connection, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
		}

		// Set URL
		if( !is_null($url) ) {
			curl_setopt($this->connection, CURLOPT_URL, $url);
		}

		// Set File Handle
		if( !is_null($handle) ) {
			curl_setopt($this->connection, CURLOPT_TIMEOUT, 100);
			curl_setopt($this->connection, CURLOPT_FILE, $handle);
			curl_setopt($this->connection, CURLOPT_FOLLOWLOCATION, true);
		}
	}

	/**
	 * Download a file to local filesystem.
	 *
	 * @param type $url
	 * @param type $path
	 *
	 * @throws Exception
	 */
	protected function downloadFile($url, $path) {

		if( function_exists('curl_init') ) {
			// Create file handle
			$handle = fopen ($path, 'w+');

			// Prepare CURL connection
			$this->prepareConnection($url, $handle);

			// Run CURL
			curl_exec($this->connection);
			$error = curl_error($this->connection);
			if( !empty($error) ) {
				throw new Exception('(Curl) '.$error, 502);
			}

			// Close file handle
			fclose($handle);

			// Close CURL connection
			curl_close($this->connection);

		} else {

			$options = $file_get_contents_options = array(
				'ssl' => array(
					"verify_peer" => false,
					"verify_peer_name" => false
				),
				'http' => array(
					'user_agent' => $_SERVER['HTTP_USER_AGENT']
				)
			);

			file_put_contents(
				$path,
				file_get_contents(
					$url, false,
					stream_context_create($options)
				)
			);
		}
	}

	/**
	 * Store cache
	 */
	public function __destruct()
	{
		if (!empty($this->cache['versions'])) {
			file_put_contents(__DIR__.'/getjoomla.cache', json_encode($this->cache));
		}
	}

	/**
	 * Get list of available Joomla! Releases
	 *
	 * @return Array
	 */
	public function getVersions()
	{

		if (empty($this->cache['versions'])) {
			$buffer  = $this->getURLContents('https://api.github.com/repos/joomla/joomla-cms/releases');
			$list	 = json_decode($buffer);
			
			// Search for full installation asset
			foreach ($list AS $version) {
				foreach( $version->assets AS $asset ) {
					if( stripos($asset->name, 'Full_Package.zip') ) {
						$url = $asset->browser_download_url;
					}
				}
				$this->cache['versions'][$version->name] = $url;
			}
		}

		return $this->cache['versions'];
	}

	/**
	 * Get informations about latest Joomla! release
	 *
	 * @return   String
	 */
	public function getLatestVersion()
	{
		if (empty($this->cache['latest'])) {
			$buffer					 = $this->getURLContents('https://api.github.com/repos/joomla/joomla-cms/releases/latest');
			$tmp					 = json_decode($buffer);
			$this->cache['latest']	 = $tmp->name;
		}

		return $this->cache['latest'];
	}

	/**
	 * Download package, unpack it, install and redirect to
	 * Joomla! installation page.
	 *
	 * @global   String   $baseURL   Base URL for script.
	 * @param    String   $url_zip   URL to Joomla! installation package.
	 *
	 * @throws Exception
	 */
	public function prepare($url_zip){
		global $baseURL;

		// Download zip
		$this->downloadFile($url_zip, __DIR__.'/joomla.zip');

		// Remove this script
		unlink(__FILE__);
		unlink(__DIR__.'/getjoomla.cache');
		
		// Unpack
		$package = new ZipArchive;
		if ($package->open(__DIR__.'/joomla.zip') === TRUE) {
			$package->extractTo(__DIR__);
			$package->close();

			// Remove package
			unlink(__DIR__.'/joomla.zip');
			$this->cache = null;
		} else {
			throw new Exception('Cannot extract joomla.zip', 502);
		}

		// Clone htaccess
		copy(__DIR__.'/htaccess.txt', __DIR__.'/.htaccess');


		// Redierct to installation
		header('Location: '.$baseURL.'installation/index.php');
	}

	/**
	 * Check Class requirements
	 *
	 * @throws Exception
	 */
	public function checkRequirements(){
		
		// Check if PHP can get remote contect
		if( !ini_get('allow_url_fopen') OR !function_exists('curl_init') ) {
			throw new Exception('This class require <b>CURL</b> or <b>allow_url_fopen</b> have to be enabled in PHP configuration.', 502);
		}

		// Check if server allow to extract zip files
		if( !class_exists('ZipArchive')) {
			throw new Exception('Class <b>ZipArchive</b> is not available in current PHP configuration.', 502);
		}

	}
}

// Create new Installer instance
$installer = new Installer;

// Run tasks
try {

	// First check if server meets requirements
	$installer->checkRequirements();

	// Get versions data
	$versions	 = $installer->getVersions();
	$latest		 = $installer->getLatestVersion();

	// If this is install task
	if (isset($_GET['install'])) {
		$installer->prepare($_GET['install']);
	}
	
} catch (Exception $e) {
	die('ERROR: '.$e->getMessage());
}

?>
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
		<base href="<?php echo $baseURL ?>" />
        <meta name="description" content="">
        <meta name="author" content="">
        <title>GetJoomla</title>

        <!-- Bootstrap core CSS -->
        <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">

		<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
		<script type="text/javascript" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>

        <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
        <!--[if lt IE 9]>
          <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
          <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
        <![endif]-->

		<style>
			.container {padding-top:30px;padding-bottom:30px;}
			.windows8{position:relative;width:56px;height:56px;margin:auto;top:45%}.windows8 .wBall{position:absolute;width:53px;height:53px;opacity:0;transform:rotate(225deg);-o-transform:rotate(225deg);-ms-transform:rotate(225deg);-webkit-transform:rotate(225deg);-moz-transform:rotate(225deg);animation:orbit 5.4425s infinite;-o-animation:orbit 5.4425s infinite;-ms-animation:orbit 5.4425s infinite;-webkit-animation:orbit 5.4425s infinite;-moz-animation:orbit 5.4425s infinite}.windows8 .wBall .wInnerBall{position:absolute;width:7px;height:7px;background:#fff;left:0;top:0;border-radius:7px}.windows8 #wBall_1{animation-delay:1.186s;-o-animation-delay:1.186s;-ms-animation-delay:1.186s;-webkit-animation-delay:1.186s;-moz-animation-delay:1.186s}.windows8 #wBall_2{animation-delay:.233s;-o-animation-delay:.233s;-ms-animation-delay:.233s;-webkit-animation-delay:.233s;-moz-animation-delay:.233s}.windows8 #wBall_3{animation-delay:.4765s;-o-animation-delay:.4765s;-ms-animation-delay:.4765s;-webkit-animation-delay:.4765s;-moz-animation-delay:.4765s}.windows8 #wBall_4{animation-delay:.7095s;-o-animation-delay:.7095s;-ms-animation-delay:.7095s;-webkit-animation-delay:.7095s;-moz-animation-delay:.7095s}.windows8 #wBall_5{animation-delay:.953s;-o-animation-delay:.953s;-ms-animation-delay:.953s;-webkit-animation-delay:.953s;-moz-animation-delay:.953s}@keyframes orbit{0%{opacity:1;z-index:99;transform:rotate(180deg);animation-timing-function:ease-out}7%{opacity:1;transform:rotate(300deg);animation-timing-function:linear;origin:0}30%{opacity:1;transform:rotate(410deg);animation-timing-function:ease-in-out;origin:7%}39%{opacity:1;transform:rotate(645deg);animation-timing-function:linear;origin:30%}70%{opacity:1;transform:rotate(770deg);animation-timing-function:ease-out;origin:39%}75%{opacity:1;transform:rotate(900deg);animation-timing-function:ease-out;origin:70%}76%{opacity:0;transform:rotate(900deg)}100%{opacity:0;transform:rotate(900deg)}}@-o-keyframes orbit{0%{opacity:1;z-index:99;-o-transform:rotate(180deg);-o-animation-timing-function:ease-out}7%{opacity:1;-o-transform:rotate(300deg);-o-animation-timing-function:linear;-o-origin:0}30%{opacity:1;-o-transform:rotate(410deg);-o-animation-timing-function:ease-in-out;-o-origin:7%}39%{opacity:1;-o-transform:rotate(645deg);-o-animation-timing-function:linear;-o-origin:30%}70%{opacity:1;-o-transform:rotate(770deg);-o-animation-timing-function:ease-out;-o-origin:39%}75%{opacity:1;-o-transform:rotate(900deg);-o-animation-timing-function:ease-out;-o-origin:70%}76%{opacity:0;-o-transform:rotate(900deg)}100%{opacity:0;-o-transform:rotate(900deg)}}@-ms-keyframes orbit{0%{opacity:1;z-index:99;-ms-transform:rotate(180deg);-ms-animation-timing-function:ease-out}7%{opacity:1;-ms-transform:rotate(300deg);-ms-animation-timing-function:linear;-ms-origin:0}30%{opacity:1;-ms-transform:rotate(410deg);-ms-animation-timing-function:ease-in-out;-ms-origin:7%}39%{opacity:1;-ms-transform:rotate(645deg);-ms-animation-timing-function:linear;-ms-origin:30%}70%{opacity:1;-ms-transform:rotate(770deg);-ms-animation-timing-function:ease-out;-ms-origin:39%}75%{opacity:1;-ms-transform:rotate(900deg);-ms-animation-timing-function:ease-out;-ms-origin:70%}76%{opacity:0;-ms-transform:rotate(900deg)}100%{opacity:0;-ms-transform:rotate(900deg)}}@-webkit-keyframes orbit{0%{opacity:1;z-index:99;-webkit-transform:rotate(180deg);-webkit-animation-timing-function:ease-out}7%{opacity:1;-webkit-transform:rotate(300deg);-webkit-animation-timing-function:linear;-webkit-origin:0}30%{opacity:1;-webkit-transform:rotate(410deg);-webkit-animation-timing-function:ease-in-out;-webkit-origin:7%}39%{opacity:1;-webkit-transform:rotate(645deg);-webkit-animation-timing-function:linear;-webkit-origin:30%}70%{opacity:1;-webkit-transform:rotate(770deg);-webkit-animation-timing-function:ease-out;-webkit-origin:39%}75%{opacity:1;-webkit-transform:rotate(900deg);-webkit-animation-timing-function:ease-out;-webkit-origin:70%}76%{opacity:0;-webkit-transform:rotate(900deg)}100%{opacity:0;-webkit-transform:rotate(900deg)}}@-moz-keyframes orbit{0%{opacity:1;z-index:99;-moz-transform:rotate(180deg);-moz-animation-timing-function:ease-out}7%{opacity:1;-moz-transform:rotate(300deg);-moz-animation-timing-function:linear;-moz-origin:0}30%{opacity:1;-moz-transform:rotate(410deg);-moz-animation-timing-function:ease-in-out;-moz-origin:7%}39%{opacity:1;-moz-transform:rotate(645deg);-moz-animation-timing-function:linear;-moz-origin:30%}70%{opacity:1;-moz-transform:rotate(770deg);-moz-animation-timing-function:ease-out;-moz-origin:39%}75%{opacity:1;-moz-transform:rotate(900deg);-moz-animation-timing-function:ease-out;-moz-origin:70%}76%{opacity:0;-moz-transform:rotate(900deg)}100%{opacity:0;-moz-transform:rotate(900deg)}}
			.loader {
				background:#21417A;position:absolute;top:0;left:0;width:0;height:0;overflow:hidden;opacity:0;transition:opacity 0.5s linear;
				z-index:9999;
			}
			.loader.enabled {
				width:100%;height:100%;opacity:1
			}
		</style>
		<script>
			$(document).ready(function(){
				$('form').submit(function(){
					$('.loader').addClass('enabled');
				});
			});
		</script>
    </head>

    <body>

        <div class="container">
            <div class="jumbotron text-center">
                <h1>getJoomla <small>v1.0.0</small></h1>
                <p class="lead">Just a crazy fast script to download and prepare Joomla! installation.</p>
				<form action="<?php echo $baseURL.basename(__FILE__) ?>" method="get">
					<div class="input-group">
						<select class="form-control" name="install">
							<optgroup label="Latest version">
								<?php
								foreach ($versions AS $version => $url):
									if ($version === $latest):
										?>
										<option value="<?php echo $url ?>" <?php
											echo ($version === $latest ? 'selected="true" style="font-weight:700"'
													: '')
										?>>
										<?php echo $version ?>
										</option>
										<?php
									endif;
								endforeach
								?>
							</optgroup>
							<optgroup label="Other versions">
									<?php
									foreach ($versions AS $version => $url):
										if ($version !== $latest):
											?>
										<option value="<?php echo $url ?>" <?php echo ($version === $latest ? 'selected'
										: '') ?>>
										<?php echo $version ?>
										</option>
											<?php
										endif;
									endforeach
									?>
							</optgroup>
						</select>
						<span class="input-group-btn">
							<input type="submit" class="btn btn-primary" value="Install"/>
						</span>
					</div><!-- /input-group -->
				</form>
            </div>

            <div class="row marketing">
                <div class="col-lg-6">
                    <h4>How it works</h4>
                    <p>This script downloads selected release from <a href="https://github.com/joomla/joomla-cms/releases">Joomla! Github repository</a>, unpacks it, creates <code>.htaccess</code>, removes itself and then redirects you to Joomla! installation. In short: just select version and click install to run Joomla! installer.</p>
                </div>

                <div class="col-lg-6">
                    <h4>Warning</h4>
                    <p>This script is free for everyone. Im not responsible for any damage it can make. Remember to always have a copy of files you have on this server.</p>

                    <h4>License</h4>
                    <p>This script is released under <a href="http://www.gnu.org/licenses/gpl-3.0.txt">GNU/GPL 3.0 license</a>. Free for commercial and non-commercial usage.</p>
                </div>
            </div>

            <footer class="footer">
                <p>&copy; 2016 BestProject</p>
            </footer>

        </div>
		<div class="loader">
			<div class="windows8">
				<div class="wBall" id="wBall_1">
					<div class="wInnerBall"></div>
				</div>
				<div class="wBall" id="wBall_2">
					<div class="wInnerBall"></div>
				</div>
				<div class="wBall" id="wBall_3">
					<div class="wInnerBall"></div>
				</div>
				<div class="wBall" id="wBall_4">
					<div class="wInnerBall"></div>
				</div>
				<div class="wBall" id="wBall_5">
					<div class="wInnerBall"></div>
				</div>
			</div>
		</div>
    </body>
</html>