functions.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <?php
  2. require '../../vendor/autoload.php';
  3. require 'dotenv.php';
  4. use DevCoder\DotEnv;
  5. use Illuminate\Encryption\Encrypter;
  6. use Illuminate\Support\Str;
  7. use Monolog\Formatter\LineFormatter;
  8. use Monolog\Handler\StreamHandler;
  9. use Monolog\Logger;
  10. (new DotEnv(dirname(__FILE__, 3) . '/.env'))->load();
  11. $required_extensions = ['openssl', 'gd', 'mysql', 'PDO', 'mbstring', 'tokenizer', 'bcmath', 'xml', 'curl', 'zip', 'intl'];
  12. $requirements = [
  13. 'minPhp' => '8.1',
  14. 'maxPhp' => '8.2', // This version is not supported
  15. 'mysql' => '5.7.22',
  16. ];
  17. /**
  18. * Check if the minimum PHP version is present
  19. * @return string 'OK' on success and 'not OK' on failure.
  20. */
  21. function checkPhpVersion(): string
  22. {
  23. global $requirements;
  24. wh_log('php version: ' . phpversion(), 'debug');
  25. if (version_compare(phpversion(), $requirements['minPhp'], '>=') && version_compare(phpversion(), $requirements['maxPhp'], '<=')) {
  26. return 'OK';
  27. }
  28. return 'not OK';
  29. }
  30. /**
  31. * Check if the environment file is writable
  32. * @return bool Returns true on writable and false on not writable.
  33. */
  34. function checkWriteable(): bool
  35. {
  36. return is_writable('../../.env');
  37. }
  38. /**
  39. * Check if the server runs using HTTPS
  40. * @return bool Returns true on HTTPS or false on HTTP.
  41. */
  42. function checkHTTPS(): bool
  43. {
  44. $isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443;
  45. wh_log('https:', 'debug', (array)$isHttps);
  46. return $isHttps;
  47. }
  48. /**
  49. * Check if MySQL is installed and runs the correct version using a shell command
  50. * @return mixed|string 'OK' if required version is met, returns MySQL version if not met.
  51. */
  52. function getMySQLVersion(): mixed
  53. {
  54. global $requirements;
  55. wh_log('attempting to get mysql version', 'debug');
  56. $output = shell_exec('mysql -V') ?? '';
  57. preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version);
  58. $versionoutput = $version[0] ?? '0';
  59. wh_log('mysql version: ' . $versionoutput, 'debug');
  60. return intval($versionoutput) > intval($requirements['mysql']) ? 'OK' : $versionoutput;
  61. }
  62. /**
  63. * Check if zip is installed using a shell command
  64. * @return string 'OK' on success and 'not OK' on failure.
  65. */
  66. function getZipVersion(): string
  67. {
  68. wh_log('attempting to get zip version', 'debug');
  69. $output = shell_exec('zip -v') ?? '';
  70. preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version);
  71. $versionoutput = $version[0] ?? 0;
  72. wh_log('zip version: ' . $versionoutput, 'debug');
  73. return $versionoutput != 0 ? 'OK' : 'not OK';
  74. }
  75. /**
  76. * Check if git is installed using a shell command
  77. * @return string 'OK' on success and 'not OK' on failure.
  78. */
  79. function getGitVersion(): string
  80. {
  81. wh_log('attempting to get git version', 'debug');
  82. $output = shell_exec('git --version') ?? '';
  83. preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version);
  84. $versionoutput = $version[0] ?? 0;
  85. wh_log('git version: ' . $versionoutput, 'debug');
  86. return $versionoutput != 0 ? 'OK' : 'not OK';
  87. }
  88. /**
  89. * Check if tar is installed using a shell command
  90. * @return string 'OK' on success and 'not OK' on failure.
  91. */
  92. function getTarVersion(): string
  93. {
  94. wh_log('attempting to get tar version', 'debug');
  95. $output = shell_exec('tar --version') ?? '';
  96. preg_match('@[0-9]+\.[0-9]+@', $output, $version);
  97. $versionoutput = $version[0] ?? 0;
  98. wh_log('tar version: ' . $versionoutput, 'debug');
  99. return $versionoutput != 0 ? 'OK' : 'not OK';
  100. }
  101. /**
  102. * Check all extensions to see if they have loaded or not
  103. * @return array Returns an array of extensions that failed to load.
  104. */
  105. function checkExtensions(): array
  106. {
  107. global $required_extensions;
  108. wh_log('checking extensions', 'debug');
  109. $not_ok = [];
  110. $extentions = get_loaded_extensions();
  111. foreach ($required_extensions as $ext) {
  112. if (!preg_grep('/^(?=.*' . $ext . ').*$/', $extentions)) {
  113. array_push($not_ok, $ext);
  114. }
  115. }
  116. wh_log('loaded extensions:', 'debug', $extentions);
  117. wh_log('failed extensions:', 'debug', $not_ok);
  118. return $not_ok;
  119. }
  120. /**
  121. * Sets the environment variable into the env file
  122. * @param string $envKey The environment key to set or modify
  123. * @param string $envValue The environment variable to set
  124. * @return bool true on success or false on failure.
  125. */
  126. function setenv($envKey, $envValue)
  127. {
  128. $envFile = dirname(__FILE__, 3).'/.env';
  129. $str = file_get_contents($envFile);
  130. $str .= "\n"; // In case the searched variable is in the last line without \n
  131. $keyPosition = strpos($str, "{$envKey}=");
  132. $endOfLinePosition = strpos($str, PHP_EOL, $keyPosition);
  133. $oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
  134. $str = str_replace($oldLine, "{$envKey}={$envValue}", $str);
  135. $str = substr($str, 0, -1);
  136. $fp = fopen($envFile, 'w');
  137. fwrite($fp, $str);
  138. fclose($fp);
  139. }
  140. /**
  141. * Encrypt the given value
  142. * @param mixed $value The variable to be encrypted
  143. * @param bool $serialize If the encryption should be serialized
  144. * @return string Returns the encrypted variable.
  145. */
  146. function encryptSettingsValue(mixed $value, $serialize = true): string
  147. {
  148. $appKey = getenv('APP_KEY');
  149. $appKey = base64_decode(Str::after($appKey, 'base64:'));
  150. $encrypter = new Encrypter($appKey, 'AES-256-CBC');
  151. $encryptedKey = $encrypter->encrypt($value, $serialize);
  152. return $encryptedKey;
  153. }
  154. /**
  155. * Decrypt the given value
  156. * @param mixed $payload The payload to be decrypted
  157. * @param bool $unserialize If the encryption should be unserialized
  158. * @return mixed Returns the decrypted variable on success, throws otherwise.
  159. */
  160. function decryptSettingsValue(mixed $payload, $unserialize = true)
  161. {
  162. $appKey = getenv('APP_KEY');
  163. $appKey = base64_decode(Str::after($appKey, 'base64:'));
  164. $encrypter = new Encrypter($appKey, 'AES-256-CBC');
  165. $decryptedKey = $encrypter->decrypt($payload, $unserialize);
  166. return $decryptedKey;
  167. }
  168. /**
  169. * Run a shell command
  170. * @param string $command The command string to run
  171. * @param array|null $descriptors [optional]<p>
  172. * An indexed array where the key represents the descriptor number and the value represents how PHP will pass that descriptor to the child process. 0 is stdin, 1 is stdout, while 2 is stderr.
  173. * Default descriptors when null are 0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']
  174. * </p>
  175. * @param string|null $cwd [optional] <p>
  176. * The initial working dir for the command. This must be an
  177. * absolute directory path, or null
  178. * if you want to use the default value (the working dir of the current
  179. * PHP process)
  180. * </p>
  181. * @param array|null $options [optional] <p>
  182. * Allows you to specify additional options.
  183. * @link https://www.php.net/manual/en/function.proc-open.php proc_open
  184. * </p>
  185. * @return false|string|null Returns the result from the command.
  186. */
  187. function run_console(string $command, array $descriptors = null, string $cwd = null, array $options = null)
  188. {
  189. wh_log('running command: ' . $command, 'debug');
  190. $path = dirname(__FILE__, 3);
  191. $descriptors = $descriptors ?? [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
  192. $handle = proc_open("cd '$path' && bash -c 'exec -a ServerCPP $command'", $descriptors, $pipes, $cwd, null, $options);
  193. wh_log('command result: ' . stream_get_contents($pipes[1]), 'debug');
  194. return stream_get_contents($pipes[1]);
  195. }
  196. /**
  197. * Log to the default laravel.log file
  198. * @param string $message The message to log
  199. * @param string $level The log level to use (debug, info, warning, error, critical)
  200. * @param array $context [optional] The context to log extra information
  201. * @return void
  202. */
  203. function wh_log(string $message, string $level = 'info', array $context = []): void
  204. {
  205. $formatter = new LineFormatter(null, null, true, true);
  206. $stream = new StreamHandler(dirname(__FILE__, 3) . '/storage/logs/installer.log', Logger::DEBUG);
  207. $stream->setFormatter($formatter);
  208. $log = new Logger('ControlPanel');
  209. $log->pushHandler($stream);
  210. switch (strtolower($level)) {
  211. case 'debug': // Only log debug messages if APP_DEBUG is true
  212. if(getenv('APP_DEBUG') === false) return;
  213. $log->debug($message, $context);
  214. break;
  215. case 'info':
  216. $log->info($message, $context);
  217. break;
  218. case 'warning':
  219. $log->warning($message, $context);
  220. break;
  221. case 'error':
  222. $log->error($message, $context);
  223. break;
  224. case 'critical':
  225. $log->critical($message, $context);
  226. break;
  227. }
  228. // Prevent memory leaks by resetting the logger
  229. $log->reset();
  230. }
  231. /**
  232. * Generate a random string
  233. * @param int $length The length of the random string
  234. * @return string The randomly generated string.
  235. */
  236. function generateRandomString(int $length = 8): string
  237. {
  238. $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  239. $charactersLength = strlen($characters);
  240. $randomString = '';
  241. for ($i = 0; $i < $length; $i++) {
  242. $randomString .= $characters[rand(0, $charactersLength - 1)];
  243. }
  244. return $randomString;
  245. }