| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 | 
							- <?php
 
- namespace Elgg;
 
- /**
 
-  * Upgrade service for Elgg
 
-  *
 
-  * This is a straight port of the procedural code used for upgrading before
 
-  * Elgg 1.9.
 
-  *
 
-  * @access private
 
-  *
 
-  * @package    Elgg.Core
 
-  * @subpackage Upgrade
 
-  */
 
- class UpgradeService {
 
- 	/**
 
- 	 * Global Elgg configuration
 
- 	 * 
 
- 	 * @var \stdClass
 
- 	 */
 
- 	private $CONFIG;
 
- 	/**
 
- 	 * Constructor
 
- 	 */
 
- 	public function __construct() {
 
- 		global $CONFIG;
 
- 		$this->CONFIG = $CONFIG;
 
- 	}
 
- 	/**
 
- 	 * Run the upgrade process
 
- 	 *
 
- 	 * @return array
 
- 	 */
 
- 	public function run() {
 
- 		$result = array(
 
- 			'failure' => false,
 
- 			'reason' => '',
 
- 		);
 
- 		// prevent someone from running the upgrade script in parallel (see #4643)
 
- 		if (!$this->getUpgradeMutex()) {
 
- 			$result['failure'] = true;
 
- 			$result['reason'] = _elgg_services()->translator->translate('upgrade:locked');
 
- 			return $result;
 
- 		}
 
- 		// disable the system log for upgrades to avoid exceptions when the schema changes.
 
- 		_elgg_services()->events->unregisterHandler('log', 'systemlog', 'system_log_default_logger');
 
- 		_elgg_services()->events->unregisterHandler('all', 'all', 'system_log_listener');
 
- 		// turn off time limit
 
- 		set_time_limit(0);
 
- 		if ($this->getUnprocessedUpgrades()) {
 
- 			$this->processUpgrades();
 
- 		}
 
- 		_elgg_services()->events->trigger('upgrade', 'system', null);
 
- 		elgg_flush_caches();
 
- 		$this->releaseUpgradeMutex();
 
- 		return $result;
 
- 	}
 
- 	/**
 
- 	 * Run any php upgrade scripts which are required
 
- 	 *
 
- 	 * @param int  $version Version upgrading from.
 
- 	 * @param bool $quiet   Suppress errors.  Don't use this.
 
- 	 *
 
- 	 * @return bool
 
- 	 */
 
- 	protected function upgradeCode($version, $quiet = false) {
 
- 		$version = (int) $version;
 
- 		$upgrade_path = _elgg_services()->config->get('path') . 'engine/lib/upgrades/';
 
- 		$processed_upgrades = $this->getProcessedUpgrades();
 
- 		// upgrading from 1.7 to 1.8. Need to bootstrap.
 
- 		if (!$processed_upgrades) {
 
- 			$this->bootstrap17to18();
 
- 			// grab accurate processed upgrades
 
- 			$processed_upgrades = $this->getProcessedUpgrades();
 
- 		}
 
- 		$upgrade_files = $this->getUpgradeFiles($upgrade_path);
 
- 		if ($upgrade_files === false) {
 
- 			return false;
 
- 		}
 
- 		$upgrades = $this->getUnprocessedUpgrades($upgrade_files, $processed_upgrades);
 
- 		// Sort and execute
 
- 		sort($upgrades);
 
- 		foreach ($upgrades as $upgrade) {
 
- 			$upgrade_version = $this->getUpgradeFileVersion($upgrade);
 
- 			$success = true;
 
- 			if ($upgrade_version <= $version) {
 
- 				// skip upgrade files from before the installation version of Elgg
 
- 				// because the upgrade files from before the installation version aren't
 
- 				// added to the database.
 
- 				continue;
 
- 			}
 
- 			
 
- 			// hide all errors.
 
- 			if ($quiet) {
 
- 				// hide include errors as well as any exceptions that might happen
 
- 				try {
 
- 					if (!@self::includeCode("$upgrade_path/$upgrade")) {
 
- 						$success = false;
 
- 						error_log("Could not include $upgrade_path/$upgrade");
 
- 					}
 
- 				} catch (\Exception $e) {
 
- 					$success = false;
 
- 					error_log($e->getMessage());
 
- 				}
 
- 			} else {
 
- 				if (!self::includeCode("$upgrade_path/$upgrade")) {
 
- 					$success = false;
 
- 					error_log("Could not include $upgrade_path/$upgrade");
 
- 				}
 
- 			}
 
- 			if ($success) {
 
- 				// don't set the version to a lower number in instances where an upgrade
 
- 				// has been merged from a lower version of Elgg
 
- 				if ($upgrade_version > $version) {
 
- 					_elgg_services()->datalist->set('version', $upgrade_version);
 
- 				}
 
- 				// incrementally set upgrade so we know where to start if something fails.
 
- 				$this->setProcessedUpgrade($upgrade);
 
- 			} else {
 
- 				return false;
 
- 			}
 
- 		}
 
- 		return true;
 
- 	}
 
- 	/**
 
- 	 * PHP include a file with a very limited scope
 
- 	 *
 
- 	 * @param string $file File path to include
 
- 	 * @return mixed
 
- 	 */
 
- 	protected static function includeCode($file) {
 
- 		// do not remove - some upgrade scripts depend on this
 
- 		global $CONFIG;
 
- 		return include $file;
 
- 	}
 
- 	/**
 
- 	 * Saves a processed upgrade to a dataset.
 
- 	 *
 
- 	 * @param string $upgrade Filename of the processed upgrade
 
- 	 *                        (not the path, just the file)
 
- 	 * @return bool
 
- 	 */
 
- 	protected function setProcessedUpgrade($upgrade) {
 
- 		$processed_upgrades = $this->getProcessedUpgrades();
 
- 		$processed_upgrades[] = $upgrade;
 
- 		$processed_upgrades = array_unique($processed_upgrades);
 
- 		return _elgg_services()->datalist->set('processed_upgrades', serialize($processed_upgrades));
 
- 	}
 
- 	/**
 
- 	 * Gets a list of processes upgrades
 
- 	 *
 
- 	 * @return mixed Array of processed upgrade filenames or false
 
- 	 */
 
- 	protected function getProcessedUpgrades() {
 
- 		$upgrades = _elgg_services()->datalist->get('processed_upgrades');
 
- 		$unserialized = unserialize($upgrades);
 
- 		return $unserialized;
 
- 	}
 
- 	/**
 
- 	 * Returns the version of the upgrade filename.
 
- 	 *
 
- 	 * @param string $filename The upgrade filename. No full path.
 
- 	 * @return int|false
 
- 	 * @since 1.8.0
 
- 	 */
 
- 	protected function getUpgradeFileVersion($filename) {
 
- 		preg_match('/^([0-9]{10})([\.a-z0-9-_]+)?\.(php)$/i', $filename, $matches);
 
- 		if (isset($matches[1])) {
 
- 			return (int) $matches[1];
 
- 		}
 
- 		return false;
 
- 	}
 
- 	/**
 
- 	 * Returns a list of upgrade files relative to the $upgrade_path dir.
 
- 	 *
 
- 	 * @param string $upgrade_path The up
 
- 	 * @return array|false
 
- 	 */
 
- 	protected function getUpgradeFiles($upgrade_path = null) {
 
- 		if (!$upgrade_path) {
 
- 			$upgrade_path = _elgg_services()->config->get('path') . 'engine/lib/upgrades/';
 
- 		}
 
- 		$upgrade_path = sanitise_filepath($upgrade_path);
 
- 		$handle = opendir($upgrade_path);
 
- 		if (!$handle) {
 
- 			return false;
 
- 		}
 
- 		$upgrade_files = array();
 
- 		while ($upgrade_file = readdir($handle)) {
 
- 			// make sure this is a wellformed upgrade.
 
- 			if (is_dir($upgrade_path . '$upgrade_file')) {
 
- 				continue;
 
- 			}
 
- 			$upgrade_version = $this->getUpgradeFileVersion($upgrade_file);
 
- 			if (!$upgrade_version) {
 
- 				continue;
 
- 			}
 
- 			$upgrade_files[] = $upgrade_file;
 
- 		}
 
- 		sort($upgrade_files);
 
- 		return $upgrade_files;
 
- 	}
 
- 	/**
 
- 	 * Checks if any upgrades need to be run.
 
- 	 *
 
- 	 * @param null|array $upgrade_files      Optional upgrade files
 
- 	 * @param null|array $processed_upgrades Optional processed upgrades
 
- 	 *
 
- 	 * @return array
 
- 	 */
 
- 	protected function getUnprocessedUpgrades($upgrade_files = null, $processed_upgrades = null) {
 
- 		if ($upgrade_files === null) {
 
- 			$upgrade_files = $this->getUpgradeFiles();
 
- 		}
 
- 		if ($processed_upgrades === null) {
 
- 			$processed_upgrades = unserialize(_elgg_services()->datalist->get('processed_upgrades'));
 
- 			if (!is_array($processed_upgrades)) {
 
- 				$processed_upgrades = array();
 
- 			}
 
- 		}
 
- 		$unprocessed = array_diff($upgrade_files, $processed_upgrades);
 
- 		return $unprocessed;
 
- 	}
 
- 	/**
 
- 	 * Upgrades Elgg Database and code
 
- 	 *
 
- 	 * @return bool
 
- 	 */
 
- 	protected function processUpgrades() {
 
- 		$dbversion = (int) _elgg_services()->datalist->get('version');
 
- 		// No version number? Oh snap...this is an upgrade from a clean installation < 1.7.
 
- 		// Run all upgrades without error reporting and hope for the best.
 
- 		// See https://github.com/elgg/elgg/issues/1432 for more.
 
- 		$quiet = !$dbversion;
 
- 		// Note: Database upgrades are deprecated as of 1.8.  Use code upgrades.  See #1433
 
- 		if ($this->dbUpgrade($dbversion, '', $quiet)) {
 
- 			system_message(_elgg_services()->translator->translate('upgrade:db'));
 
- 		}
 
- 		if ($this->upgradeCode($dbversion, $quiet)) {
 
- 			system_message(_elgg_services()->translator->translate('upgrade:core'));
 
- 			// Now we trigger an event to give the option for plugins to do something
 
- 			$upgrade_details = new \stdClass;
 
- 			$upgrade_details->from = $dbversion;
 
- 			$upgrade_details->to = elgg_get_version();
 
- 			_elgg_services()->events->trigger('upgrade', 'upgrade', $upgrade_details);
 
- 			return true;
 
- 		}
 
- 		return false;
 
- 	}
 
- 	/**
 
- 	 * Boot straps into 1.8 upgrade system from 1.7
 
- 	 *
 
- 	 * This runs all the 1.7 upgrades, then sets the processed_upgrades to all existing 1.7 upgrades.
 
- 	 * Control is then passed back to the main upgrade function which detects and runs the
 
- 	 * 1.8 upgrades, regardless of filename convention.
 
- 	 *
 
- 	 * @return bool
 
- 	 */
 
- 	protected function bootstrap17to18() {
 
- 		$db_version = (int) _elgg_services()->datalist->get('version');
 
- 		// the 1.8 upgrades before the upgrade system change that are interspersed with 1.7 upgrades.
 
- 		$upgrades_18 = array(
 
- 			'2010111501.php',
 
- 			'2010121601.php',
 
- 			'2010121602.php',
 
- 			'2010121701.php',
 
- 			'2010123101.php',
 
- 			'2011010101.php',
 
- 		);
 
- 		$upgrade_files = $this->getUpgradeFiles();
 
- 		$processed_upgrades = array();
 
- 		foreach ($upgrade_files as $upgrade_file) {
 
- 			// ignore if not in 1.7 format or if it's a 1.8 upgrade
 
- 			if (in_array($upgrade_file, $upgrades_18) || !preg_match("/[0-9]{10}\.php/", $upgrade_file)) {
 
- 				continue;
 
- 			}
 
- 			$upgrade_version = $this->getUpgradeFileVersion($upgrade_file);
 
- 			// this has already been run in a previous 1.7.X -> 1.7.X upgrade
 
- 			if ($upgrade_version < $db_version) {
 
- 				$this->setProcessedUpgrade($upgrade_file);
 
- 			}
 
- 		}
 
- 	}
 
- 	/**
 
- 	 * Creates a table {prefix}upgrade_lock that is used as a mutex for upgrades.
 
- 	 *
 
- 	 * @return bool
 
- 	 */
 
- 	protected function getUpgradeMutex() {
 
- 		
 
- 		if (!$this->isUpgradeLocked()) {
 
- 			// lock it
 
- 			_elgg_services()->db->insertData("create table {$this->CONFIG->dbprefix}upgrade_lock (id INT)");
 
- 			_elgg_services()->logger->notice('Locked for upgrade.');
 
- 			return true;
 
- 		}
 
- 		_elgg_services()->logger->warn('Cannot lock for upgrade: already locked');
 
- 		return false;
 
- 	}
 
- 	/**
 
- 	 * Unlocks upgrade.
 
- 	 *
 
- 	 * @return void
 
- 	 */
 
- 	public function releaseUpgradeMutex() {
 
- 		
 
- 		_elgg_services()->db->deleteData("drop table {$this->CONFIG->dbprefix}upgrade_lock");
 
- 		_elgg_services()->logger->notice('Upgrade unlocked.');
 
- 	}
 
- 	/**
 
- 	 * Checks if upgrade is locked
 
- 	 *
 
- 	 * @return bool
 
- 	 */
 
- 	public function isUpgradeLocked() {
 
- 		
 
- 		$is_locked = count(_elgg_services()->db->getData("SHOW TABLES LIKE '{$this->CONFIG->dbprefix}upgrade_lock'"));
 
- 		return (bool)$is_locked;
 
- 	}
 
- 	/**
 
- 	 * ***************************************************************************
 
- 	 * NOTE: If this is ever removed from Elgg, sites lose the ability to upgrade
 
- 	 * from 1.7.x and earlier to the latest version of Elgg without upgrading to
 
- 	 * 1.8 first.
 
- 	 * ***************************************************************************
 
- 	 *
 
- 	 * Upgrade the database schema in an ordered sequence.
 
- 	 *
 
- 	 * Executes all upgrade files in elgg/engine/schema/upgrades/ in sequential order.
 
- 	 * Upgrade files must be in the standard Elgg release format of YYYYMMDDII.sql
 
- 	 * where II is an incrementor starting from 01.
 
- 	 *
 
- 	 * Files that are < $version will be ignored.
 
- 	 *
 
- 	 * @param int    $version The version you are upgrading from in the format YYYYMMDDII.
 
- 	 * @param string $fromdir Optional directory to load upgrades from. default: engine/schema/upgrades/
 
- 	 * @param bool   $quiet   If true, suppress all error messages. Only use for the upgrade from <=1.6.
 
- 	 *
 
- 	 * @return int The number of upgrades run.
 
- 	 * @deprecated 1.8 Use PHP upgrades for sql changes.
 
- 	 */
 
- 	protected function dbUpgrade($version, $fromdir = "", $quiet = false) {
 
- 		
 
- 		$version = (int) $version;
 
- 		if (!$fromdir) {
 
- 			$fromdir = $this->CONFIG->path . 'engine/schema/upgrades/';
 
- 		}
 
- 		$i = 0;
 
- 		if ($handle = opendir($fromdir)) {
 
- 			$sqlupgrades = array();
 
- 			while ($sqlfile = readdir($handle)) {
 
- 				if (!is_dir($fromdir . $sqlfile)) {
 
- 					if (preg_match('/^([0-9]{10})\.(sql)$/', $sqlfile, $matches)) {
 
- 						$sql_version = (int) $matches[1];
 
- 						if ($sql_version > $version) {
 
- 							$sqlupgrades[] = $sqlfile;
 
- 						}
 
- 					}
 
- 				}
 
- 			}
 
- 			asort($sqlupgrades);
 
- 			if (sizeof($sqlupgrades) > 0) {
 
- 				foreach ($sqlupgrades as $sqlfile) {
 
- 					// hide all errors.
 
- 					if ($quiet) {
 
- 						try {
 
- 							_elgg_services()->db->runSqlScript($fromdir . $sqlfile);
 
- 						} catch (\DatabaseException $e) {
 
- 							error_log($e->getmessage());
 
- 						}
 
- 					} else {
 
- 						_elgg_services()->db->runSqlScript($fromdir . $sqlfile);
 
- 					}
 
- 					$i++;
 
- 				}
 
- 			}
 
- 		}
 
- 		return $i;
 
- 	}
 
- }
 
 
  |