core-bundle/src/Resources/contao/classes/Versions.php
public function compare($blnReturnBuffer=false)
	{
		$strBuffer = '';
		$arrVersions = array();
		$intTo = 0;
		$intFrom = 0;

		$objVersions = $this->Database->prepare("SELECT * FROM tl_version WHERE pid=? AND fromTable=? ORDER BY version DESC")
									  ->execute($this->intPid, $this->strTable);

		if ($objVersions->numRows < 2)
		{
			$strBuffer = '<p>There are no versions of ' . $this->strTable . '.id=' . $this->intPid . '</p>';
		}
		else
		{
			$intIndex = 0;
			$from = array();

			// Store the versions and mark the active one
			while ($objVersions->next())
			{
				if ($objVersions->active)
				{
					$intIndex = $objVersions->version;
				}

				$arrVersions[$objVersions->version] = $objVersions->row();
				$arrVersions[$objVersions->version]['info'] = $GLOBALS['TL_LANG']['MSC']['version'].' '.$objVersions->version.' ('.Date::parse(Config::get('datimFormat'), $objVersions->tstamp).') '.$objVersions->username;
			}

			// To
			if (Input::post('to') && isset($arrVersions[Input::post('to')]))
			{
				$intTo = Input::post('to');
				$to = StringUtil::deserialize($arrVersions[Input::post('to')]['data']);
			}
			elseif (Input::get('to') && isset($arrVersions[Input::get('to')]))
			{
				$intTo = Input::get('to');
				$to = StringUtil::deserialize($arrVersions[Input::get('to')]['data']);
			}
			else
			{
				$intTo = $intIndex;
				$to = StringUtil::deserialize($arrVersions[$intTo]['data']);
			}

			// From
			if (Input::post('from') && isset($arrVersions[Input::post('from')]))
			{
				$intFrom = Input::post('from');
				$from = StringUtil::deserialize($arrVersions[Input::post('from')]['data']);
			}
			elseif (Input::get('from') && isset($arrVersions[Input::get('from')]))
			{
				$intFrom = Input::get('from');
				$from = StringUtil::deserialize($arrVersions[Input::get('from')]['data']);
			}
			elseif ($objVersions->numRows > $intIndex)
			{
				$intFrom = $objVersions->first()->version;
				$from = StringUtil::deserialize($arrVersions[$intFrom]['data']);
			}
			elseif ($intIndex > 1)
			{
				$intFrom = $intIndex - 1;
				$from = StringUtil::deserialize($arrVersions[$intFrom]['data']);
			}

			// Only continue if both version numbers are set
			if ($intTo > 0 && $intFrom > 0)
			{
				System::loadLanguageFile($this->strTable);

				// Get the order fields
				$objDcaExtractor = DcaExtractor::getInstance($this->strTable);
				$arrOrder = $objDcaExtractor->getOrderFields();

				// Find the changed fields and highlight the changes
				foreach ($to as $k=>$v)
				{
					if ($from[$k] != $to[$k])
					{
						if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['doNotShow'] || $GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['hideInput'])
						{
							continue;
						}

						$blnIsBinary = ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['inputType'] == 'fileTree' || \in_array($k, $arrOrder));

						// Decrypt the values
						if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['encrypt'])
						{
							$to[$k] = Encryption::decrypt($to[$k]);
							$from[$k] = Encryption::decrypt($from[$k]);
						}

						if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['multiple'])
						{
							if (isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['csv']))
							{
								$delimiter = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['csv'];

								if (isset($to[$k]))
								{
									$to[$k] = preg_replace('/' . preg_quote($delimiter, ' ?/') . '/', $delimiter . ' ', $to[$k]);
								}
								if (isset($from[$k]))
								{
									$from[$k] = preg_replace('/' . preg_quote($delimiter, ' ?/') . '/', $delimiter . ' ', $from[$k]);
								}
							}
							else
							{
								// Convert serialized arrays into strings
								if (\is_array(($tmp = StringUtil::deserialize($to[$k]))) && !\is_array($to[$k]))
								{
									$to[$k] = $this->implodeRecursive($tmp, $blnIsBinary);
								}
								if (\is_array(($tmp = StringUtil::deserialize($from[$k]))) && !\is_array($from[$k]))
								{
									$from[$k] = $this->implodeRecursive($tmp, $blnIsBinary);
								}
							}
						}

						unset($tmp);

						// Convert binary UUIDs to their hex equivalents (see #6365)
						if ($blnIsBinary)
						{
							if (Validator::isBinaryUuid($to[$k]))
							{
								$to[$k] = StringUtil::binToUuid($to[$k]);
							}
							if (Validator::isBinaryUuid($from[$k]))
							{
								$to[$k] = StringUtil::binToUuid($from[$k]);
							}
						}

						// Convert date fields
						if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['rgxp'] == 'date')
						{
							$to[$k] = Date::parse(Config::get('dateFormat'), $to[$k] ?: '');
							$from[$k] = Date::parse(Config::get('dateFormat'), $from[$k] ?: '');
						}
						elseif ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['rgxp'] == 'time')
						{
							$to[$k] = Date::parse(Config::get('timeFormat'), $to[$k] ?: '');
							$from[$k] = Date::parse(Config::get('timeFormat'), $from[$k] ?: '');
						}
						elseif ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['rgxp'] == 'datim' || $k == 'tstamp')
						{
							$to[$k] = Date::parse(Config::get('datimFormat'), $to[$k] ?: '');
							$from[$k] = Date::parse(Config::get('datimFormat'), $from[$k] ?: '');
						}

						// Convert strings into arrays
						if (!\is_array($to[$k]))
						{
							$to[$k] = explode("\n", $to[$k]);
						}
						if (!\is_array($from[$k]))
						{
							$from[$k] = explode("\n", $from[$k]);
						}

						$objDiff = new \Diff($from[$k], $to[$k]);
						$strBuffer .= $objDiff->render(new DiffRenderer(array('field'=>($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['label'][0] ?: (isset($GLOBALS['TL_LANG']['MSC'][$k]) ? (\is_array($GLOBALS['TL_LANG']['MSC'][$k]) ? $GLOBALS['TL_LANG']['MSC'][$k][0] : $GLOBALS['TL_LANG']['MSC'][$k]) : $k)))));
					}
				}
			}
		}

		// Identical versions
		if ($strBuffer == '')
		{
			$strBuffer = '<p>'.$GLOBALS['TL_LANG']['MSC']['identicalVersions'].'</p>';
		}

		if ($blnReturnBuffer)
		{
			return $strBuffer;
		}

		$objTemplate = new BackendTemplate('be_diff');
		$objTemplate->content = $strBuffer;
		$objTemplate->versions = $arrVersions;
		$objTemplate->to = $intTo;
		$objTemplate->from = $intFrom;
		$objTemplate->showLabel = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['showDifferences']);
		$objTemplate->theme = Backend::getTheme();
		$objTemplate->base = Environment::get('base');
		$objTemplate->language = $GLOBALS['TL_LANGUAGE'];
		$objTemplate->title = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['showDifferences']);
		$objTemplate->charset = Config::get('characterSet');
		$objTemplate->action = ampersand(Environment::get('request'));

		throw new ResponseException($objTemplate->getResponse());
	}
public function compare($blnReturnBuffer=false)
	{
		$strBuffer = '';
		$arrVersions = array();
		$intTo = 0;
		$intFrom = 0;

		$objVersions = $this->Database->prepare("SELECT * FROM tl_version WHERE pid=? AND fromTable=? ORDER BY version DESC")
									  ->execute($this->intPid, $this->strTable);

		if ($objVersions->numRows < 2)
		{
			$strBuffer = '<p>There are no versions of ' . $this->strTable . '.id=' . $this->intPid . '</p>';
		}
		else
		{
			$intIndex = 0;
			$from = array();

			// Store the versions and mark the active one
			while ($objVersions->next())
			{
				if ($objVersions->active)
				{
					$intIndex = $objVersions->version;
				}

				$arrVersions[$objVersions->version] = $objVersions->row();
				$arrVersions[$objVersions->version]['info'] = $GLOBALS['TL_LANG']['MSC']['version'].' '.$objVersions->version.' ('.Date::parse(Config::get('datimFormat'), $objVersions->tstamp).') '.$objVersions->username;
			}

			// To
			if (Input::post('to') && isset($arrVersions[Input::post('to')]))
			{
				$intTo = Input::post('to');
				$to = StringUtil::deserialize($arrVersions[Input::post('to')]['data']);
			}
			elseif (Input::get('to') && isset($arrVersions[Input::get('to')]))
			{
				$intTo = Input::get('to');
				$to = StringUtil::deserialize($arrVersions[Input::get('to')]['data']);
			}
			else
			{
				$intTo = $intIndex;
				$to = StringUtil::deserialize($arrVersions[$intTo]['data']);
			}

			// From
			if (Input::post('from') && isset($arrVersions[Input::post('from')]))
			{
				$intFrom = Input::post('from');
				$from = StringUtil::deserialize($arrVersions[Input::post('from')]['data']);
			}
			elseif (Input::get('from') && isset($arrVersions[Input::get('from')]))
			{
				$intFrom = Input::get('from');
				$from = StringUtil::deserialize($arrVersions[Input::get('from')]['data']);
			}
			elseif ($objVersions->numRows > $intIndex)
			{
				$intFrom = $objVersions->first()->version;
				$from = StringUtil::deserialize($arrVersions[$intFrom]['data']);
			}
			elseif ($intIndex > 1)
			{
				$intFrom = $intIndex - 1;
				$from = StringUtil::deserialize($arrVersions[$intFrom]['data']);
			}

			// Only continue if both version numbers are set
			if ($intTo > 0 && $intFrom > 0)
			{
				System::loadLanguageFile($this->strTable);

				// Get the order fields
				$objDcaExtractor = DcaExtractor::getInstance($this->strTable);
				$arrOrder = $objDcaExtractor->getOrderFields();

				// Find the changed fields and highlight the changes
				foreach ($to as $k=>$v)
				{
					if ($from[$k] != $to[$k])
					{
						if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['doNotShow'] || $GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['hideInput'])
						{
							continue;
						}

						$blnIsBinary = ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['inputType'] == 'fileTree' || \in_array($k, $arrOrder));

						// Decrypt the values
						if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['encrypt'])
						{
							$to[$k] = Encryption::decrypt($to[$k]);
							$from[$k] = Encryption::decrypt($from[$k]);
						}

						if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['multiple'])
						{
							if (isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['csv']))
							{
								$delimiter = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['csv'];

								if (isset($to[$k]))
								{
									$to[$k] = preg_replace('/' . preg_quote($delimiter, ' ?/') . '/', $delimiter . ' ', $to[$k]);
								}
								if (isset($from[$k]))
								{
									$from[$k] = preg_replace('/' . preg_quote($delimiter, ' ?/') . '/', $delimiter . ' ', $from[$k]);
								}
							}
							else
							{
								// Convert serialized arrays into strings
								if (\is_array(($tmp = StringUtil::deserialize($to[$k]))) && !\is_array($to[$k]))
								{
									$to[$k] = $this->implodeRecursive($tmp, $blnIsBinary);
								}
								if (\is_array(($tmp = StringUtil::deserialize($from[$k]))) && !\is_array($from[$k]))
								{
									$from[$k] = $this->implodeRecursive($tmp, $blnIsBinary);
								}
							}
						}

						unset($tmp);

						// Convert binary UUIDs to their hex equivalents (see #6365)
						if ($blnIsBinary)
						{
							if (Validator::isBinaryUuid($to[$k]))
							{
								$to[$k] = StringUtil::binToUuid($to[$k]);
							}
							if (Validator::isBinaryUuid($from[$k]))
							{
								$to[$k] = StringUtil::binToUuid($from[$k]);
							}
						}

						// Convert date fields
						if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['rgxp'] == 'date')
						{
							$to[$k] = Date::parse(Config::get('dateFormat'), $to[$k] ?: '');
							$from[$k] = Date::parse(Config::get('dateFormat'), $from[$k] ?: '');
						}
						elseif ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['rgxp'] == 'time')
						{
							$to[$k] = Date::parse(Config::get('timeFormat'), $to[$k] ?: '');
							$from[$k] = Date::parse(Config::get('timeFormat'), $from[$k] ?: '');
						}
						elseif ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['eval']['rgxp'] == 'datim' || $k == 'tstamp')
						{
							$to[$k] = Date::parse(Config::get('datimFormat'), $to[$k] ?: '');
							$from[$k] = Date::parse(Config::get('datimFormat'), $from[$k] ?: '');
						}

						// Convert strings into arrays
						if (!\is_array($to[$k]))
						{
							$to[$k] = explode("\n", $to[$k]);
						}
						if (!\is_array($from[$k]))
						{
							$from[$k] = explode("\n", $from[$k]);
						}

						$objDiff = new \Diff($from[$k], $to[$k]);
						$strBuffer .= $objDiff->render(new DiffRenderer(array('field'=>($GLOBALS['TL_DCA'][$this->strTable]['fields'][$k]['label'][0] ?: (isset($GLOBALS['TL_LANG']['MSC'][$k]) ? (\is_array($GLOBALS['TL_LANG']['MSC'][$k]) ? $GLOBALS['TL_LANG']['MSC'][$k][0] : $GLOBALS['TL_LANG']['MSC'][$k]) : $k)))));
					}
				}
			}
		}

		// Identical versions
		if ($strBuffer == '')
		{
			$strBuffer = '<p>'.$GLOBALS['TL_LANG']['MSC']['identicalVersions'].'</p>';
		}

		if ($blnReturnBuffer)
		{
			return $strBuffer;
		}

		$objTemplate = new BackendTemplate('be_diff');
		$objTemplate->content = $strBuffer;
		$objTemplate->versions = $arrVersions;
		$objTemplate->to = $intTo;
		$objTemplate->from = $intFrom;
		$objTemplate->showLabel = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['showDifferences']);
		$objTemplate->theme = Backend::getTheme();
		$objTemplate->base = Environment::get('base');
		$objTemplate->language = $GLOBALS['TL_LANGUAGE'];
		$objTemplate->title = StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['showDifferences']);
		$objTemplate->charset = Config::get('characterSet');
		$objTemplate->action = ampersand(Environment::get('request'));

		throw new ResponseException($objTemplate->getResponse());
	}
Compare versions

@param bool $blnReturnBuffer

@return string

@throws ResponseException