functions.php 9.3 KB

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