problems.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. <?php
  2. namespace Grav\Plugin;
  3. use Grav\Common\Cache;
  4. use Grav\Common\Plugin;
  5. use Grav\Common\Uri;
  6. class ProblemsPlugin extends Plugin
  7. {
  8. protected $results = array();
  9. protected $check;
  10. /**
  11. * @return array
  12. */
  13. public static function getSubscribedEvents()
  14. {
  15. return [
  16. 'onPluginsInitialized' => ['onPluginsInitialized', 100001],
  17. 'onFatalException' => ['onFatalException', 0]
  18. ];
  19. }
  20. public function onFatalException()
  21. {
  22. if ($this->isAdmin()) {
  23. $this->active = false;
  24. return;
  25. }
  26. // Run through potential issues
  27. if ($this->problemChecker()) {
  28. $this->renderProblems();
  29. }
  30. }
  31. public function onPluginsInitialized()
  32. {
  33. if ($this->isAdmin()) {
  34. $this->active = false;
  35. return;
  36. }
  37. /** @var Cache $cache */
  38. $cache = $this->grav['cache'];
  39. $validated_prefix = 'problem-check-';
  40. $this->check = CACHE_DIR . $validated_prefix . $cache->getKey();
  41. if (!file_exists($this->check)) {
  42. // If no issues remain, save a state file in the cache
  43. if (!$this->problemChecker()) {
  44. // delete any existing validated files
  45. foreach (new \GlobIterator(CACHE_DIR . $validated_prefix . '*') as $fileInfo) {
  46. @unlink($fileInfo->getPathname());
  47. }
  48. // create a file in the cache dir so it only runs on cache changes
  49. touch($this->check);
  50. } else {
  51. $this->renderProblems();
  52. }
  53. }
  54. }
  55. protected function renderProblems()
  56. {
  57. $theme = 'antimatter';
  58. /** @var Uri $uri */
  59. $uri = $this->grav['uri'];
  60. $baseUrlRelative = $uri->rootUrl(false);
  61. $themeUrl = $baseUrlRelative . '/' . USER_PATH . basename(THEMES_DIR) . '/' . $theme;
  62. $problemsUrl = $baseUrlRelative . '/user/plugins/problems';
  63. $html = file_get_contents(__DIR__ . '/html/problems.html');
  64. /**
  65. * Process the results, ignore the statuses passed as $ignore_status
  66. *
  67. * @param $results
  68. * @param $ignore_status
  69. */
  70. $processResults = function ($results, $ignore_status) {
  71. $problems = '';
  72. foreach ($results as $key => $result) {
  73. if ($key == 'files' || $key == 'apache' || $key == 'execute') {
  74. foreach ($result as $key_text => $value_text) {
  75. foreach ($value_text as $status => $text) {
  76. if ($status == $ignore_status) continue;
  77. $problems .= $this->getListRow($status, '<b>' . $key_text . '</b> ' . $text);
  78. }
  79. }
  80. } else {
  81. foreach ($result as $status => $text) {
  82. if ($status == $ignore_status) continue;
  83. $problems .= $this->getListRow($status, $text);
  84. }
  85. }
  86. }
  87. return $problems;
  88. };
  89. // First render the errors
  90. $problems = $processResults($this->results, 'success');
  91. // Then render the successful checks
  92. $problems .= $processResults($this->results, 'error');
  93. $html = str_replace('%%BASE_URL%%', $baseUrlRelative, $html);
  94. $html = str_replace('%%THEME_URL%%', $themeUrl, $html);
  95. $html = str_replace('%%PROBLEMS_URL%%', $problemsUrl, $html);
  96. $html = str_replace('%%PROBLEMS%%', $problems, $html);
  97. echo $html;
  98. http_response_code(500);
  99. exit();
  100. }
  101. protected function getListRow($status, $text)
  102. {
  103. if ($status == 'error') {
  104. $icon = 'fa-times';
  105. } elseif ($status == 'info') {
  106. $icon = 'fa-info';
  107. } else {
  108. $icon = 'fa-check';
  109. }
  110. $output = "\n";
  111. $output .= '<li class="' . $status . ' clearfix"><i class="fa fa-fw '. $icon . '"></i><p>'. $text . '</p></li>';
  112. return $output;
  113. }
  114. protected function problemChecker()
  115. {
  116. $min_php_version = defined('GRAV_PHP_MIN') ? GRAV_PHP_MIN : '5.4.0';
  117. $problems_found = false;
  118. $essential_files = [
  119. 'cache' => true,
  120. 'logs' => true,
  121. 'images' => true,
  122. 'assets' => true,
  123. 'system' => false,
  124. 'user/data' => true,
  125. 'user/pages' => false,
  126. 'user/config' => false,
  127. 'user/plugins/error' => false,
  128. 'user/plugins' => false,
  129. 'user/themes' => false,
  130. 'vendor' => false
  131. ];
  132. if (version_compare(GRAV_VERSION, '0.9.27', ">=")) {
  133. $essential_files['backup'] = true;
  134. $backup_folder = ROOT_DIR . 'backup';
  135. // try to create backup folder if missing
  136. if (!file_exists($backup_folder)) {
  137. @mkdir($backup_folder, 0770);
  138. }
  139. }
  140. if (version_compare(GRAV_VERSION, '1.1.4', ">=")) {
  141. $essential_files['tmp'] = true;
  142. $tmp_folder = ROOT_DIR . 'tmp';
  143. // try to create tmp folder if missing
  144. if (!file_exists($tmp_folder)) {
  145. @mkdir($tmp_folder, 0770);
  146. }
  147. }
  148. // Perform some Apache checks
  149. if (strpos(php_sapi_name(), 'apache') !== false) {
  150. $require_apache_modules = ['mod_rewrite'];
  151. $apache_modules = apache_get_modules();
  152. $apache_status = [];
  153. foreach ($require_apache_modules as $module) {
  154. if (in_array($module, $apache_modules)) {
  155. $apache_module_adjective = ' Apache module is enabled';
  156. $apache_module_status = 'success';
  157. } else {
  158. $problems_found = true;
  159. $apache_module_adjective = ' Apache module is not installed or enabled';
  160. $apache_module_status = 'error';
  161. }
  162. $apache_status[$module] = [$apache_module_status => $apache_module_adjective];
  163. }
  164. if (sizeof($apache_status) > 0) {
  165. $this->results['apache'] = $apache_status;
  166. }
  167. }
  168. // Check PHP version
  169. if (version_compare(phpversion(), $min_php_version, '<')) {
  170. $problems_found = true;
  171. $php_version_adjective = 'lower';
  172. $php_version_status = 'error';
  173. } else {
  174. $php_version_adjective = 'greater';
  175. $php_version_status = 'success';
  176. }
  177. $this->results['php'] = [$php_version_status => 'Your PHP version (' . phpversion() . ') is '. $php_version_adjective . ' than the minimum required: <b>' . $min_php_version . '</b> - <a href="http://getgrav.org/blog/changing-php-requirements-to-5.5">Additional Information</a>'];
  178. // Check for GD library
  179. if (defined('GD_VERSION') && function_exists('gd_info')) {
  180. $gd_adjective = '';
  181. $gd_status = 'success';
  182. } else {
  183. $problems_found = true;
  184. $gd_adjective = 'not ';
  185. $gd_status = 'error';
  186. }
  187. $this->results['gd'] = [$gd_status => 'PHP GD (Image Manipulation Library) is '. $gd_adjective . 'installed'];
  188. // Check for PHP CURL library
  189. if (function_exists('curl_version')) {
  190. $curl_adjective = '';
  191. $curl_status = 'success';
  192. } else {
  193. $problems_found = true;
  194. $curl_adjective = 'not ';
  195. $curl_status = 'error';
  196. }
  197. $this->results['curl'] = [$curl_status => 'PHP Curl (Data Transfer Library) is '. $curl_adjective . 'installed'];
  198. // Check for PHP Open SSL library
  199. if (extension_loaded('openssl') && defined('OPENSSL_VERSION_TEXT')) {
  200. $ssl_adjective = '';
  201. $ssl_status = 'success';
  202. } else {
  203. $problems_found = true;
  204. $ssl_adjective = 'not ';
  205. $ssl_status = 'error';
  206. }
  207. $this->results['ssl'] = [$ssl_status => 'PHP OpenSSL (Secure Sockets Library) is '. $ssl_adjective . 'installed'];
  208. // Check for PHP XML library
  209. if (extension_loaded('xml')) {
  210. $xml_adjective = '';
  211. $xml_status = 'success';
  212. } else {
  213. $problems_found = true;
  214. $xml_adjective = 'not ';
  215. $xml_status = 'error';
  216. }
  217. $this->results['xml'] = [$xml_status => 'PHP XML Library is '. $xml_adjective . 'installed'];
  218. // Check for PHP MbString library
  219. if (extension_loaded('mbstring')) {
  220. $mbstring_adjective = '';
  221. $mbstring_status = 'success';
  222. } else {
  223. $problems_found = true;
  224. $mbstring_adjective = 'not ';
  225. $mbstring_status = 'error';
  226. }
  227. $this->results['mbstring'] = [$mbstring_status => 'PHP Mbstring (Multibyte String Library) is '. $mbstring_adjective . 'installed'];
  228. // Check Exif if enabled
  229. if ($this->grav['config']->get('system.media.auto_metadata_exif')) {
  230. if(extension_loaded('exif')) {
  231. $exif_adjective = '';
  232. $exif_status = 'success';
  233. } else {
  234. $problems_found = true;
  235. $exif_adjective = 'not ';
  236. $exif_status = 'error';
  237. }
  238. $this->results['exif'] = [$exif_status => 'PHP Exif (Exchangeable Image File Format) is '. $exif_adjective . 'installed'];
  239. }
  240. // Check for PHP Zip library
  241. if (extension_loaded('zip')) {
  242. $zip_adjective = '';
  243. $zip_status = 'success';
  244. } else {
  245. $problems_found = true;
  246. $zip_adjective = 'not ';
  247. $zip_status = 'error';
  248. }
  249. $this->results['zip'] = [$zip_status => 'PHP Zip extension is '. $zip_adjective . 'installed'];
  250. // Check for essential files & perms
  251. $file_problems = [];
  252. foreach ($essential_files as $file => $check_writable) {
  253. $file_path = ROOT_DIR . $file;
  254. $is_dir = false;
  255. if (!file_exists($file_path)) {
  256. $problems_found = true;
  257. $file_status = 'error';
  258. $file_adjective = 'does not exist';
  259. } else {
  260. $file_status = 'success';
  261. $file_adjective = 'exists';
  262. $is_writeable = is_writable($file_path);
  263. $is_dir = is_dir($file_path);
  264. if ($check_writable) {
  265. if (!$is_writeable) {
  266. $file_status = 'error';
  267. $problems_found = true;
  268. $file_adjective .= ' but is <b class="underline">not writeable</b>';
  269. } else {
  270. $file_adjective .= ' and <b class="underline">is writeable</b>';
  271. }
  272. }
  273. }
  274. $file_problems[$file_path] = [$file_status => $file_adjective];
  275. }
  276. if (sizeof($file_problems) > 0) {
  277. $this->results['files'] = $file_problems;
  278. }
  279. return $problems_found;
  280. }
  281. }