diff --git a/framework/core/src/Admin/AdminServiceProvider.php b/framework/core/src/Admin/AdminServiceProvider.php
index 92f187a579..7181baec0b 100644
--- a/framework/core/src/Admin/AdminServiceProvider.php
+++ b/framework/core/src/Admin/AdminServiceProvider.php
@@ -63,6 +63,7 @@ public function register(): void
HttpMiddleware\ReferrerPolicyHeader::class,
HttpMiddleware\ContentTypeOptionsHeader::class,
Middleware\DisableBrowserCache::class,
+ Middleware\GatherDebugInformation::class,
];
});
diff --git a/framework/core/src/Admin/Middleware/GatherDebugInformation.php b/framework/core/src/Admin/Middleware/GatherDebugInformation.php
new file mode 100644
index 0000000000..af70266459
--- /dev/null
+++ b/framework/core/src/Admin/Middleware/GatherDebugInformation.php
@@ -0,0 +1,66 @@
+upsertWebUser();
+
+ $this->upsertOpCacheStatus();
+
+ return $handler->handle($request);
+ }
+
+ /**
+ * Read current web user, so we can compare that against CLI executions,
+ * these often cause file permission issues.
+ */
+ public function upsertWebUser(): void
+ {
+ $user = $this->settings->get('core.debug.web_user');
+ $currentUser = get_current_user();
+
+ if ($user !== $currentUser) {
+ $this->settings->set(
+ 'core.debug.web_user',
+ $currentUser
+ );
+ }
+ }
+
+ /**
+ * Read the opcache situation, this is only visible in web.
+ * Cli has opcache disabled by default.
+ */
+ public function upsertOpCacheStatus(): void
+ {
+ $opcache = $this->settings->get('core.debug.opcache');
+ $opcacheStatus = function_exists('opcache_get_configuration') && opcache_get_configuration() !== false ? 'on' : 'off';
+
+ if ($opcache !== $opcacheStatus) {
+ $this->settings->set(
+ 'core.debug.opcache',
+ $opcacheStatus
+ );
+ }
+ }
+}
diff --git a/framework/core/src/Foundation/ApplicationInfoProvider.php b/framework/core/src/Foundation/ApplicationInfoProvider.php
index 7bdbec5085..764ea0e098 100644
--- a/framework/core/src/Foundation/ApplicationInfoProvider.php
+++ b/framework/core/src/Foundation/ApplicationInfoProvider.php
@@ -40,18 +40,21 @@ public function scheduledTasksRegistered(): bool
return count($this->schedule->events()) > 0;
}
- public function getSchedulerStatus(): string
+ public function getSchedulerStatus(bool $translated = true): string
{
$status = $this->settings->get('schedule.last_run');
- if (! $status) {
- return $this->translator->trans('core.admin.dashboard.status.scheduler.never-run');
+ $key = match (true) {
+ ! $status => 'never-run',
+ Carbon::parse($status) > Carbon::now()->subMinutes(5) => 'active',
+ default => 'inactive'
+ };
+
+ if ($translated) {
+ return $this->translator->trans("core.admin.dashboard.status.scheduler.$key");
}
- // If the schedule has not run in the last 5 minutes, mark it as inactive.
- return Carbon::parse($status) > Carbon::now()->subMinutes(5)
- ? $this->translator->trans('core.admin.dashboard.status.scheduler.active')
- : $this->translator->trans('core.admin.dashboard.status.scheduler.inactive');
+ return $key;
}
public function identifyQueueDriver(): string
diff --git a/framework/core/src/Foundation/Console/InfoCommand.php b/framework/core/src/Foundation/Console/InfoCommand.php
index 06b9c6e791..d26f1a2a68 100644
--- a/framework/core/src/Foundation/Console/InfoCommand.php
+++ b/framework/core/src/Foundation/Console/InfoCommand.php
@@ -10,24 +10,15 @@
namespace Flarum\Foundation\Console;
use Flarum\Console\AbstractCommand;
-use Flarum\Extension\ExtensionManager;
-use Flarum\Foundation\Application;
-use Flarum\Foundation\ApplicationInfoProvider;
-use Flarum\Foundation\Config;
-use Flarum\Settings\SettingsRepositoryInterface;
-use Illuminate\Database\ConnectionInterface;
+use Flarum\Foundation\Info\Renderer\CliRenderer;
+use Flarum\Foundation\Info\Report;
+use Illuminate\Contracts\Container\Container;
use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Helper\Table;
-use Symfony\Component\Console\Helper\TableStyle;
class InfoCommand extends AbstractCommand
{
public function __construct(
- protected ExtensionManager $extensions,
- protected Config $config,
- protected SettingsRepositoryInterface $settings,
- protected ConnectionInterface $db,
- protected ApplicationInfoProvider $appInfo
+ protected Container $container
) {
parent::__construct();
}
@@ -41,83 +32,13 @@ protected function configure(): void
protected function fire(): int
{
- $coreVersion = $this->findPackageVersion(__DIR__.'/../../../', Application::VERSION);
- $this->output->writeln("Flarum core: $coreVersion");
+ $report = new Report(
+ new CliRenderer($this->output),
+ $this->container
+ );
- $this->output->writeln('PHP version: '.$this->appInfo->identifyPHPVersion());
- $this->output->writeln(''.$this->appInfo->identifyDatabaseDriver().' version: '.$this->appInfo->identifyDatabaseVersion());
-
- $phpExtensions = implode(', ', get_loaded_extensions());
- $this->output->writeln("Loaded extensions: $phpExtensions");
-
- $this->getExtensionTable()->render();
-
- $this->output->writeln('Base URL: '.$this->config->url());
- $this->output->writeln('Installation path: '.getcwd());
- $this->output->writeln('Queue driver: '.$this->appInfo->identifyQueueDriver());
- $this->output->writeln('Session driver: '.$this->appInfo->identifySessionDriver());
-
- if ($this->appInfo->scheduledTasksRegistered()) {
- $this->output->writeln('Scheduler status: '.$this->appInfo->getSchedulerStatus());
- }
-
- $this->output->writeln('Mail driver: '.$this->settings->get('mail_driver', 'unknown'));
- $this->output->writeln('Debug mode: '.($this->config->inDebugMode() ? 'ON' : 'off'));
-
- if ($this->config->inDebugMode()) {
- $this->output->writeln('');
- $this->error(
- "Don't forget to turn off debug mode! It should never be turned on in a production system."
- );
- }
+ $report->render();
return Command::SUCCESS;
}
-
- private function getExtensionTable(): Table
- {
- $table = (new Table($this->output))
- ->setHeaders([
- ['Flarum Extensions'],
- ['ID', 'Version', 'Commit']
- ])->setStyle(
- (new TableStyle)->setCellHeaderFormat('%s')
- );
-
- foreach ($this->extensions->getEnabledExtensions() as $extension) {
- $table->addRow([
- $extension->getId(),
- $extension->getVersion(),
- $this->findPackageVersion($extension->getPath())
- ]);
- }
-
- return $table;
- }
-
- /**
- * Try to detect a package's exact version.
- *
- * If the package seems to be a Git version, we extract the currently
- * checked out commit using the command line.
- */
- private function findPackageVersion(string $path, ?string $fallback = null): ?string
- {
- if (file_exists("$path/.git")) {
- $cwd = getcwd();
- chdir($path);
-
- $output = [];
- $status = null;
- exec('git rev-parse HEAD 2>&1', $output, $status);
-
- chdir($cwd);
-
- if ($status == 0) {
- return isset($fallback) ? "$fallback ($output[0])" : $output[0];
- }
- }
-
- return $fallback;
- }
}
diff --git a/framework/core/src/Foundation/Info/Concerns/PackageVersionFromPath.php b/framework/core/src/Foundation/Info/Concerns/PackageVersionFromPath.php
new file mode 100644
index 0000000000..bc61113d36
--- /dev/null
+++ b/framework/core/src/Foundation/Info/Concerns/PackageVersionFromPath.php
@@ -0,0 +1,39 @@
+&1', $output, $status);
+
+ chdir($cwd);
+
+ if ($status == 0) {
+ return isset($fallback) ? "$fallback ($output[0])" : $output[0];
+ }
+ }
+
+ return $fallback;
+ }
+}
diff --git a/framework/core/src/Foundation/Info/Renderer/CliRenderer.php b/framework/core/src/Foundation/Info/Renderer/CliRenderer.php
new file mode 100644
index 0000000000..1d206027fa
--- /dev/null
+++ b/framework/core/src/Foundation/Info/Renderer/CliRenderer.php
@@ -0,0 +1,54 @@
+output->writeln("$title>");
+ }
+
+ public function keyValue(string $key, mixed $value): void
+ {
+ $this->output->writeln("$key: $value");
+ }
+
+ public function table(array $headers, array $rows): void
+ {
+ $table = (new Table($this->output))
+ ->setHeaders($headers)->setStyle(
+ (new TableStyle)->setCellHeaderFormat('%s')
+ );
+
+ foreach ($rows as $row) {
+ $table->addRow($row);
+ }
+
+ $table->render();
+ }
+
+ public function open(): void
+ {
+ }
+
+ public function close(): void
+ {
+ }
+}
diff --git a/framework/core/src/Foundation/Info/RendererInterface.php b/framework/core/src/Foundation/Info/RendererInterface.php
new file mode 100644
index 0000000000..236ee2785b
--- /dev/null
+++ b/framework/core/src/Foundation/Info/RendererInterface.php
@@ -0,0 +1,23 @@
+sections;
+
+ $this->renderer->open();
+
+ foreach ($sections as &$section) {
+ $section = $this->container->make($section);
+
+ $section($this->renderer);
+ }
+
+ $this->renderer->close();
+ }
+}
diff --git a/framework/core/src/Foundation/Info/Section/CoreVersion.php b/framework/core/src/Foundation/Info/Section/CoreVersion.php
new file mode 100644
index 0000000000..0c9b5b6925
--- /dev/null
+++ b/framework/core/src/Foundation/Info/Section/CoreVersion.php
@@ -0,0 +1,28 @@
+keyValue(
+ 'Flarum Core',
+ $this->findPackageVersion(__DIR__.'/../../../../', Application::VERSION)
+ );
+ }
+}
diff --git a/framework/core/src/Foundation/Info/Section/Database.php b/framework/core/src/Foundation/Info/Section/Database.php
new file mode 100644
index 0000000000..f2c5fa46f5
--- /dev/null
+++ b/framework/core/src/Foundation/Info/Section/Database.php
@@ -0,0 +1,34 @@
+keyValue(
+ 'Database driver',
+ $this->appInfo->identifyDatabaseDriver()
+ );
+
+ $renderer->keyValue(
+ 'Database version',
+ $this->appInfo->identifyDatabaseVersion()
+ );
+ }
+}
diff --git a/framework/core/src/Foundation/Info/Section/Debug.php b/framework/core/src/Foundation/Info/Section/Debug.php
new file mode 100644
index 0000000000..6360a2aa9d
--- /dev/null
+++ b/framework/core/src/Foundation/Info/Section/Debug.php
@@ -0,0 +1,33 @@
+keyValue(
+ 'Debug mode',
+ $this->config->inDebugMode() ? 'on' : 'off'
+ );
+
+ if ($this->config->inDebugMode()) {
+ $renderer->heading('Debug mode should not be enabled in production; this will cause javascript and stylesheets to be recompiled on each visit.');
+ }
+ }
+}
diff --git a/framework/core/src/Foundation/Info/Section/EnabledExtensions.php b/framework/core/src/Foundation/Info/Section/EnabledExtensions.php
new file mode 100644
index 0000000000..e912fc12af
--- /dev/null
+++ b/framework/core/src/Foundation/Info/Section/EnabledExtensions.php
@@ -0,0 +1,40 @@
+extensions->getEnabledExtensions())
+ ->map(fn (Extension $extension) => [
+ $extension->getId(),
+ $this->findPackageVersion($extension->getPath(), $extension->getVersion())
+ ])
+ ->toArray();
+
+ $renderer->table([
+ ['Flarum Extensions'],
+ ['ID', 'Version']
+ ], $rows);
+ }
+}
diff --git a/framework/core/src/Foundation/Info/Section/Features.php b/framework/core/src/Foundation/Info/Section/Features.php
new file mode 100644
index 0000000000..6034f875d9
--- /dev/null
+++ b/framework/core/src/Foundation/Info/Section/Features.php
@@ -0,0 +1,67 @@
+keyValue(
+ 'Scheduler required',
+ $this->appInfo->scheduledTasksRegistered() ? 'yes' : 'no'
+ );
+
+ $renderer->keyValue(
+ 'Scheduler status',
+ $this->appInfo->getSchedulerStatus(translated: false)
+ );
+
+ $renderer->keyValue(
+ 'Queue driver',
+ $this->appInfo->identifyQueueDriver()
+ );
+
+ $renderer->keyValue(
+ 'Session driver',
+ $this->appInfo->identifySessionDriver()
+ );
+
+ $renderer->keyValue(
+ 'Mail driver',
+ $this->mail()
+ );
+ }
+
+ protected function mail()
+ {
+ $driver = $this->container->make('mail.driver');
+ $configured = $this->settings->get('mail_driver');
+
+ if ($driver instanceof NullDriver) {
+ $configured .= ' (not active)';
+ }
+
+ return $configured;
+ }
+}
diff --git a/framework/core/src/Foundation/Info/Section/PHP.php b/framework/core/src/Foundation/Info/Section/PHP.php
new file mode 100644
index 0000000000..05928ee7ec
--- /dev/null
+++ b/framework/core/src/Foundation/Info/Section/PHP.php
@@ -0,0 +1,42 @@
+keyValue(
+ 'PHP version',
+ $this->appInfo->identifyPHPVersion()
+ );
+
+ $renderer->keyValue(
+ 'PHP extensions',
+ implode(', ', get_loaded_extensions())
+ );
+
+ $renderer->keyValue(
+ 'PHP OpCache',
+ $this->settings->get('core.debug.opcache') ?? 'visit admin to identify'
+ );
+ }
+}
diff --git a/framework/core/src/Foundation/Info/Section/ServiceUsers.php b/framework/core/src/Foundation/Info/Section/ServiceUsers.php
new file mode 100644
index 0000000000..dd23d6e889
--- /dev/null
+++ b/framework/core/src/Foundation/Info/Section/ServiceUsers.php
@@ -0,0 +1,40 @@
+settings->get('core.debug.web_user') ?? 'visit admin to identify']
+ ];
+
+ if (php_sapi_name() === 'cli') {
+ $rows[] = [
+ 'Current user',
+ posix_getpwuid(posix_geteuid())['name']
+ ];
+ }
+
+ $renderer->table([
+ ['Service/process users'],
+ ['Type', 'User']
+ ], $rows);
+ }
+}
diff --git a/framework/core/src/Foundation/Info/SectionInterface.php b/framework/core/src/Foundation/Info/SectionInterface.php
new file mode 100644
index 0000000000..2074e8578b
--- /dev/null
+++ b/framework/core/src/Foundation/Info/SectionInterface.php
@@ -0,0 +1,15 @@
+