VersionInformation.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. /**
  3. * Responsible for retrieving version information and notifying about latest version
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin;
  7. use PhpMyAdmin\Utils\HttpRequest;
  8. use stdClass;
  9. use const PHP_VERSION;
  10. use function count;
  11. use function explode;
  12. use function intval;
  13. use function is_numeric;
  14. use function is_object;
  15. use function json_decode;
  16. use function preg_match;
  17. use function strlen;
  18. use function strpos;
  19. use function substr;
  20. use function time;
  21. use function version_compare;
  22. /**
  23. * Responsible for retrieving version information and notifying about latest version
  24. */
  25. class VersionInformation
  26. {
  27. /**
  28. * Returns information with latest version from phpmyadmin.net
  29. *
  30. * @return stdClass|null JSON decoded object with the data
  31. */
  32. public function getLatestVersion(): ?stdClass
  33. {
  34. if (! $GLOBALS['cfg']['VersionCheck']) {
  35. return null;
  36. }
  37. // Get response text from phpmyadmin.net or from the session
  38. // Update cache every 6 hours
  39. if (isset($_SESSION['cache']['version_check'])
  40. && time() < $_SESSION['cache']['version_check']['timestamp'] + 3600 * 6
  41. ) {
  42. $save = false;
  43. $response = $_SESSION['cache']['version_check']['response'];
  44. } else {
  45. $save = true;
  46. $file = 'https://www.phpmyadmin.net/home_page/version.json';
  47. $httpRequest = new HttpRequest();
  48. $response = $httpRequest->create($file, 'GET');
  49. }
  50. $response = $response ?: '{}';
  51. /* Parse response */
  52. $data = json_decode($response);
  53. /* Basic sanity checking */
  54. if (! is_object($data)
  55. || empty($data->version)
  56. || empty($data->releases)
  57. || empty($data->date)
  58. ) {
  59. return null;
  60. }
  61. if ($save) {
  62. $_SESSION['cache']['version_check'] = [
  63. 'response' => $response,
  64. 'timestamp' => time(),
  65. ];
  66. }
  67. return $data;
  68. }
  69. /**
  70. * Calculates numerical equivalent of phpMyAdmin version string
  71. *
  72. * @param string $version version
  73. *
  74. * @return mixed false on failure, integer on success
  75. */
  76. public function versionToInt($version)
  77. {
  78. $parts = explode('-', $version);
  79. if (count($parts) > 1) {
  80. $suffix = $parts[1];
  81. } else {
  82. $suffix = '';
  83. }
  84. $parts = explode('.', $parts[0]);
  85. $result = 0;
  86. if (count($parts) >= 1 && is_numeric($parts[0])) {
  87. $result += 1000000 * (int) $parts[0];
  88. }
  89. if (count($parts) >= 2 && is_numeric($parts[1])) {
  90. $result += 10000 * (int) $parts[1];
  91. }
  92. if (count($parts) >= 3 && is_numeric($parts[2])) {
  93. $result += 100 * (int) $parts[2];
  94. }
  95. if (count($parts) >= 4 && is_numeric($parts[3])) {
  96. $result += 1 * (int) $parts[3];
  97. }
  98. if (! empty($suffix)) {
  99. $matches = [];
  100. if (preg_match('/^(\D+)(\d+)$/', $suffix, $matches)) {
  101. $suffix = $matches[1];
  102. $result += intval($matches[2]);
  103. }
  104. switch ($suffix) {
  105. case 'pl':
  106. $result += 60;
  107. break;
  108. case 'rc':
  109. $result += 30;
  110. break;
  111. case 'beta':
  112. $result += 20;
  113. break;
  114. case 'alpha':
  115. $result += 10;
  116. break;
  117. case 'dev':
  118. $result += 0;
  119. break;
  120. }
  121. } else {
  122. $result += 50; // for final
  123. }
  124. return $result;
  125. }
  126. /**
  127. * Returns the version and date of the latest phpMyAdmin version compatible
  128. * with the available PHP and MySQL versions
  129. *
  130. * @param array $releases array of information related to each version
  131. *
  132. * @return array|null containing the version and date of latest compatible version
  133. */
  134. public function getLatestCompatibleVersion(array $releases)
  135. {
  136. // Maintains the latest compatible version
  137. $latestRelease = null;
  138. foreach ($releases as $release) {
  139. $phpVersions = $release->php_versions;
  140. $phpConditions = explode(',', $phpVersions);
  141. foreach ($phpConditions as $phpCondition) {
  142. if (! $this->evaluateVersionCondition('PHP', $phpCondition)) {
  143. continue 2;
  144. }
  145. }
  146. // We evaluate MySQL version constraint if there are only
  147. // one server configured.
  148. if (count($GLOBALS['cfg']['Servers']) === 1) {
  149. $mysqlVersions = $release->mysql_versions;
  150. $mysqlConditions = explode(',', $mysqlVersions);
  151. foreach ($mysqlConditions as $mysqlCondition) {
  152. if (! $this->evaluateVersionCondition('MySQL', $mysqlCondition)) {
  153. continue 2;
  154. }
  155. }
  156. }
  157. // To compare the current release with the previous latest release or no release is set
  158. if ($latestRelease !== null && ! version_compare($latestRelease['version'], $release->version, '<')) {
  159. continue;
  160. }
  161. $latestRelease = [
  162. 'version' => $release->version,
  163. 'date' => $release->date,
  164. ];
  165. }
  166. // no compatible version
  167. return $latestRelease;
  168. }
  169. /**
  170. * Checks whether PHP or MySQL version meets supplied version condition
  171. *
  172. * @param string $type PHP or MySQL
  173. * @param string $condition version condition
  174. *
  175. * @return bool whether the condition is met
  176. */
  177. public function evaluateVersionCondition(string $type, string $condition)
  178. {
  179. $operator = null;
  180. $version = null;
  181. $operators = [
  182. '<=',
  183. '>=',
  184. '!=',
  185. '<>',
  186. '<',
  187. '>',
  188. '=',
  189. ]; // preserve order
  190. foreach ($operators as $oneOperator) {
  191. if (strpos($condition, $oneOperator) === 0) {
  192. $operator = $oneOperator;
  193. $version = substr($condition, strlen($oneOperator));
  194. break;
  195. }
  196. }
  197. $myVersion = null;
  198. if ($type === 'PHP') {
  199. $myVersion = $this->getPHPVersion();
  200. } elseif ($type === 'MySQL') {
  201. $myVersion = $this->getMySQLVersion();
  202. }
  203. if ($myVersion !== null && $version !== null && $operator !== null) {
  204. return version_compare($myVersion, $version, $operator);
  205. }
  206. return false;
  207. }
  208. /**
  209. * Returns the PHP version
  210. *
  211. * @return string PHP version
  212. */
  213. protected function getPHPVersion()
  214. {
  215. return PHP_VERSION;
  216. }
  217. /**
  218. * Returns the MySQL version if connected to a database
  219. *
  220. * @return string|null MySQL version
  221. */
  222. protected function getMySQLVersion()
  223. {
  224. global $dbi;
  225. if (isset($dbi)) {
  226. return $dbi->getVersionString();
  227. }
  228. return null;
  229. }
  230. }