setProperties([ 'id' => 'phpstan', 'name' => __('PHPStan'), 'description' => __('Analyse php code using PHPStan'), 'priority' => 910, 'configurator' => true, 'types' => ['plugin'], ]); $this->getPhpPath(); $run_level = $this->getSetting('run_level'); $this->run_level = is_int($run_level) ? $run_level : 5; $ignored_vars = $this->getSetting('ignored_vars'); $this->ignored_vars = is_string($ignored_vars) ? $ignored_vars : ''; dcCore::app()->auth->user_prefs->addWorkspace('interface'); self::$user_ui_colorsyntax = dcCore::app()->auth->user_prefs->interface->colorsyntax; self::$user_ui_colorsyntax_theme = dcCore::app()->auth->user_prefs->interface->colorsyntax_theme; return true; } public function isConfigured(): bool { return true; } public function header(): ?string { if (self::$user_ui_colorsyntax) { return dcPage::jsLoadCodeMirror(self::$user_ui_colorsyntax_theme); } return null; } public function configure($url): ?string { if (!empty($_POST['save'])) { $this->setSettings([ 'phpexe_path' => (!empty($_POST['phpexe_path']) ? $_POST['phpexe_path'] : ''), 'run_level' => (int) $_POST['run_level'], 'ignored_vars' => (!empty($_POST['ignored_vars']) ? $_POST['ignored_vars'] : ''), 'split_report' => !empty($_POST['split_report']), ]); $this->redirect($url); } $content = (string) file_get_contents(dirname(__FILE__) . '/phpstan/phpstan.rules.conf'); return '

' . __('You must enable improve details to view analyse results !') . '

' . '

' . '

' . '

' . __('If this module does not work you can try to put here directory to php executable (without executable file name).') . ' C:\path_to\php

' . '

' . form::number('run_level', ['min' => 0, 'max' => 9, 'default' => $this->run_level]) . '

' . '

' . '

' . '

' . sprintf( __('If you have errors like "%s", you can add this var here. Use ; as separator and do not put $ ahead.'), 'Variable $var might not be defined' ) . ' ' . __('For exemple: var;_othervar;avar') . '
' . __('Some variables like core, _menu, are already set in ignored list.') . '

' . '

' . '

' . __('Enable this can cause timeout.') . '

' . '

' . '

' . form::textarea('file_content', 120, 14, [ 'default' => html::escapeHTML($content), 'class' => 'maximal', 'extra_html' => 'readonly="true"', ]) . '

' . ( !self::$user_ui_colorsyntax ? '' : dcPage::jsLoad(dcPage::getPF('improved/inc/module/phpstan/phpstan.improve.js')) . dcPage::jsRunCodeMirror('editor', 'file_content', 'dotclear', self::$user_ui_colorsyntax_theme) ); } public function openModule(): bool { if (!$this->writeConf()) { $this->setError(__('Failed to write phpstan configuration')); return false; } return true; } public function closeFile(): ?bool { if (!$this->getSetting('split_report') || !in_array($this->path_extension, ['php', 'in']) ) { return null; } return $this->execFixer($this->path_full); } public function closeModule(): ?bool { if ($this->getSetting('split_report')) { return null; } if ($this->hasError()) { return false; } return $this->execFixer(); } private function execFixer(string $path = null): bool { if (!empty($path)) { $path .= ' '; } $command = sprintf( '%sphp %s/phpstan/libs/phpstan.phar analyse ' . $path . '--configuration=%s', $this->phpexe_path, dirname(__FILE__), DC_VAR . '/phpstan.neon' ); try { exec($command, $output, $error); if (!empty($error) && empty($output)) { throw new Exception('oops'); } if (count($output) < 4) { $this->setSuccess(__('No errors found')); } else { $this->setWarning(sprintf('
%s
', implode('
', $output))); } return true; } catch (Exception $e) { $this->setError(__('Failed to run phpstan')); return false; } } /** * Get php executable path */ private function getPhpPath(): void { $phpexe_path = $this->getSetting('phpexe_path'); if (!is_string($phpexe_path)) { $phpexe_path = ''; } if (empty($phpexe_path) && !empty(PHP_BINDIR)) { $phpexe_path = PHP_BINDIR; } $phpexe_path = (string) path::real($phpexe_path); if (!empty($phpexe_path)) { $phpexe_path .= '/'; } $this->phpexe_path = $phpexe_path; } private function writeConf(): bool { $content = str_replace( [ '%LEVEL%', '%MODULE_ROOT%', '%DC_ROOT%', '%BOOTSTRAP_ROOT%', ], [ $this->run_level, $this->module['sroot'], DC_ROOT, dirname(__FILE__) . '/phpstan', ], (string) file_get_contents(dirname(__FILE__) . '/phpstan/phpstan.rules.conf') ); $ignored = explode(';', $this->ignored_vars); foreach ($ignored as $var) { $var = trim($var); if (empty($var)) { continue; } $content .= ' # $' . $var . ' variable may not be defined (globally)' . "\n" . ' - message: \'#Variable \$' . $var . ' might not be defined#\'' . "\n" . ' path: *' . "\n\n"; } return (bool) file_put_contents(DC_VAR . '/phpstan.neon', $content); } }