From 6b94db9768566c09f549639ff4a722b8d601e269 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Tue, 7 Apr 2026 06:27:40 -0700 Subject: [PATCH 1/4] Enahnce the CLI start screen --- console/create.php | 41 +++++++++++++++++++++++++++++++++++++++++ language/en/common.php | 2 ++ 2 files changed, 43 insertions(+) diff --git a/console/create.php b/console/create.php index 3692ac1..c1b6e8a 100644 --- a/console/create.php +++ b/console/create.php @@ -114,6 +114,8 @@ protected function interact(InputInterface $input, OutputInterface $output) $this->helper = $this->getHelper('question'); + $this->display_banner(); + $output->writeln($this->language->lang('SKELETON_CLI_COMPOSER_QUESTIONS')); $this->get_composer_data(); @@ -121,6 +123,45 @@ protected function interact(InputInterface $input, OutputInterface $output) $this->get_component_data(); } + /** + * Display a colorful ASCII art title banner. + */ + protected function display_banner(): void + { + $title_lines = [ + [' _____ __ __ __ ', 'cyan'], + [' / ___// /_____ / /__ / /_____ ____ ', 'cyan'], + [' \__ \/ //_/ _ \/ / _ \/ __/ __ \/ __ \ ', 'blue'], + [' ___/ / ,< / __/ / __/ /_/ /_/ / / / / ', 'blue'], + ['/____/_/|_|\___/_/\___/\__/\____/_/ /_/ ', 'magenta'], + ]; + + $sub_lines = [ + [' ______ __ _ ', 'magenta'], + [' / ____/ __/ /____ ____ _____(_)___ ____ ', 'red'], + [' / __/ | |/_/ __/ _ \/ __ \/ ___/ / __ \/ __ \ ', 'red'], + [' / /____> language->lang('SKELETON_CLI_BANNER_DESC'); + $line = str_repeat('─', 50); + + $this->output->writeln(''); + foreach ($title_lines as [$text, $color]) + { + $this->output->writeln("$text"); + } + foreach ($sub_lines as [$text, $color]) + { + $this->output->writeln("$text"); + } + $this->output->writeln(''); + $this->output->writeln(" $desc "); + $this->output->writeln("$line"); + $this->output->writeln(''); + } + /** * Get composer data from the user */ diff --git a/language/en/common.php b/language/en/common.php index 7b4665e..2590a96 100644 --- a/language/en/common.php +++ b/language/en/common.php @@ -28,6 +28,8 @@ 'PHPBB_CREATE_SKELETON_EXPLAIN' => 'Generate your extension’s foundation in seconds. No more setting up files by hand — Skeleton builds it all for you, with clean, fully documented templates based on phpBB’s best practices. Before you begin, be sure to review the 📖 [Skeleton Extension Documentation], 🛡️ [Extension Validation Rules], and 🛠️ [Coding Guidelines].', 'PHPBB_SKELETON_EXT_HELP' => 'Learn More', + 'SKELETON_CLI_BANNER_DESC' => 'The official phpBB skeleton extension generator', + 'EXTENSION_CLI_SKELETON_SUCCESS' => "Extension created successfully.\nCopy the extension from `store/tmp-ext/` into the `ext/` folder.", 'SKELETON_CLI_COMPOSER_QUESTIONS' => 'Enter composer.json details (hit enter to leave an option empty)', 'SKELETON_CLI_COMPONENT_QUESTIONS' => 'Install optional components. Default: No; [y/n]', From a218b90e88db96719cbe3799393211830ba1d12e Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Tue, 7 Apr 2026 06:36:25 -0700 Subject: [PATCH 2/4] Make hard-coded versions from lang files dynamic --- ext.php | 21 +++++++++++++++++---- language/en/common.php | 6 +++--- tests/ext_test.php | 4 +++- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/ext.php b/ext.php index 7734e98..34712f9 100644 --- a/ext.php +++ b/ext.php @@ -53,11 +53,11 @@ protected function phpbb_requirement($phpBB_version = PHPBB_VERSION) { if (phpbb_version_compare($phpBB_version, self::MIN_PHPBB_ALLOWED, '<')) { - $this->errors[] = 'PHPBB_VERSION_MIN_ERROR'; + $this->errors[] = ['PHPBB_VERSION_MIN_ERROR', self::MIN_PHPBB_ALLOWED, $phpBB_version]; } else if (phpbb_version_compare($phpBB_version, self::MAX_PHPBB_ALLOWED, '>=')) { - $this->errors[] = 'PHPBB_VERSION_MAX_ERROR'; + $this->errors[] = ['PHPBB_VERSION_MAX_ERROR', self::MAX_PHPBB_ALLOWED, $phpBB_version]; } } @@ -71,7 +71,7 @@ protected function php_requirement($php_version = PHP_VERSION_ID) { if ($php_version < self::MIN_PHP_ALLOWED) { - $this->errors[] = 'PHP_VERSION_ERROR'; + $this->errors[] = ['PHP_VERSION_ERROR', $this->version_parse(self::MIN_PHP_ALLOWED), $this->version_parse($php_version)]; } } @@ -101,9 +101,22 @@ protected function enable_failed() { $language = $this->container->get('language'); $language->add_lang('common', 'phpbb/skeleton'); - return array_map([$language, 'lang'], $this->errors); + return array_map(static function($error) use ($language) { + return is_array($error) ? call_user_func_array([$language, 'lang'], $error) : $language->lang($error); + }, $this->errors); } return false; } + + /** + * Convert a version id (70100) to a semantic version (7.1.0) + * + * @param int $version + * @return string + */ + private function version_parse($version) + { + return sprintf('%d.%d.%d', $version / 10000, ($version / 100) % 100, $version % 100); + } } diff --git a/language/en/common.php b/language/en/common.php index 2590a96..9be42cf 100644 --- a/language/en/common.php +++ b/language/en/common.php @@ -163,7 +163,7 @@ 'SKELETON_INVALID_PHPBB_MAX_VERSION'=> 'The maximum phpBB version requirement is invalid.', 'NO_ZIPARCHIVE_ERROR' => 'The ZipArchive class is required, but was not found in your PHP configuration.', - 'PHP_VERSION_ERROR' => 'PHP 5.6 or newer is required to use this extension.', - 'PHPBB_VERSION_MIN_ERROR' => 'phpBB 3.2.3 or newer is required to use this extension.', - 'PHPBB_VERSION_MAX_ERROR' => 'phpBB 4 is not supported with this version of the extension. Please check for a newer version of this extension.' + 'PHP_VERSION_ERROR' => 'PHP %1$s or newer is required to install this version of the Skeleton Extension. You are using PHP %2$s.', + 'PHPBB_VERSION_MIN_ERROR' => 'phpBB %1$s or newer is required to install this version of the Skeleton Extension. You are using phpBB %2$s.', + 'PHPBB_VERSION_MAX_ERROR' => 'phpBB %1$s or above is not supported by this version of the Skeleton Extension. You are using phpBB %2$s. Please check for a newer version of this extension.' ]); diff --git a/tests/ext_test.php b/tests/ext_test.php index 225580f..c1122eb 100644 --- a/tests/ext_test.php +++ b/tests/ext_test.php @@ -138,7 +138,9 @@ protected function getExtErrors($ext): array { $prop = (new \ReflectionClass($ext))->getProperty('errors'); $prop->setAccessible(true); - return $prop->getValue($ext); + return array_map(static function ($e) { + return is_array($e) ? $e[0] : $e; + }, $prop->getValue($ext)); } protected function setExtErrors($ext, array $errors): void From 62e7f02ae979bbbe9002cd374e11713614d46aa4 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Tue, 7 Apr 2026 08:48:04 -0700 Subject: [PATCH 3/4] Remove duplicated code and tweak CLI banner layout --- console/create.php | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/console/create.php b/console/create.php index c1b6e8a..bda9cc5 100644 --- a/console/create.php +++ b/console/create.php @@ -129,14 +129,11 @@ protected function interact(InputInterface $input, OutputInterface $output) protected function display_banner(): void { $title_lines = [ - [' _____ __ __ __ ', 'cyan'], - [' / ___// /_____ / /__ / /_____ ____ ', 'cyan'], - [' \__ \/ //_/ _ \/ / _ \/ __/ __ \/ __ \ ', 'blue'], - [' ___/ / ,< / __/ / __/ /_/ /_/ / / / / ', 'blue'], - ['/____/_/|_|\___/_/\___/\__/\____/_/ /_/ ', 'magenta'], - ]; - - $sub_lines = [ + [' _____ __ __ __ ', 'cyan'], + [' / ___// /_____ / /__ / /_____ ____ ', 'cyan'], + [' \__ \/ //_/ _ \/ / _ \/ __/ __ \/ __ \ ', 'blue'], + [' ___/ / ,< / __/ / __/ /_/ /_/ / / / / ', 'blue'], + [' /____/_/|_|\___/_/\___/\__/\____/_/ /_/ ', 'magenta'], [' ______ __ _ ', 'magenta'], [' / ____/ __/ /____ ____ _____(_)___ ____ ', 'red'], [' / __/ | |/_/ __/ _ \/ __ \/ ___/ / __ \/ __ \ ', 'red'], @@ -152,10 +149,6 @@ protected function display_banner(): void { $this->output->writeln("$text"); } - foreach ($sub_lines as [$text, $color]) - { - $this->output->writeln("$text"); - } $this->output->writeln(''); $this->output->writeln(" $desc "); $this->output->writeln("$line"); From 5c31f6a719d480a8e2a41e3cdcb34fe1b0cd0165 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Tue, 7 Apr 2026 10:43:40 -0700 Subject: [PATCH 4/4] Inspection corrections and fixes --- ext.php | 2 +- tests/ext_test.php | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/ext.php b/ext.php index 34712f9..e399f94 100644 --- a/ext.php +++ b/ext.php @@ -101,7 +101,7 @@ protected function enable_failed() { $language = $this->container->get('language'); $language->add_lang('common', 'phpbb/skeleton'); - return array_map(static function($error) use ($language) { + return array_map(static function ($error) use ($language) { return is_array($error) ? call_user_func_array([$language, 'lang'], $error) : $language->lang($error); }, $this->errors); } diff --git a/tests/ext_test.php b/tests/ext_test.php index c1122eb..2be7809 100644 --- a/tests/ext_test.php +++ b/tests/ext_test.php @@ -97,32 +97,57 @@ public function test_enable_failed_returns_expected() $this->assertEquals(['LANG: SOME_ERROR'], $method->invoke($ext)); } + public function test_enable_failed_returns_parameterized_error() + { + $ext = $this->getMockBuilder(ext::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->setExtErrors($ext, [['PHP_VERSION_ERROR', '7.1.0', '5.5.0']]); + + $languageMock = $this->createMock(language::class); + $languageMock->method('add_lang')->willReturn(null); + $languageMock->method('lang')->willReturnCallback(function (...$args) { + return implode(':', $args); + }); + + $containerMock = $this->createMock(ContainerInterface::class); + $containerMock->method('get')->with('language')->willReturn($languageMock); + + $this->setProperty($ext, 'container', $containerMock); + + $method = (new \ReflectionClass($ext))->getMethod('enable_failed'); + $method->setAccessible(true); + + $this->assertEquals(['PHP_VERSION_ERROR:7.1.0:5.5.0'], $method->invoke($ext)); + } + public function test_phpbb_requirement_min_error() { $this->setExtErrors($this->ext, []); $this->invokeProtectedMethod($this->ext, 'phpbb_requirement', ['3.2.2']); - $this->assertContains('PHPBB_VERSION_MIN_ERROR', $this->getExtErrors($this->ext)); + $this->assertContains('PHPBB_VERSION_MIN_ERROR', $this->getExtErrorKeys($this->ext)); } public function test_phpbb_requirement_max_error() { $this->setExtErrors($this->ext, []); $this->invokeProtectedMethod($this->ext, 'phpbb_requirement', ['4.0.0-dev']); - $this->assertContains('PHPBB_VERSION_MAX_ERROR', $this->getExtErrors($this->ext)); + $this->assertContains('PHPBB_VERSION_MAX_ERROR', $this->getExtErrorKeys($this->ext)); } public function test_php_requirement_error() { $this->setExtErrors($this->ext, []); $this->invokeProtectedMethod($this->ext, 'php_requirement', [50500]); - $this->assertContains('PHP_VERSION_ERROR', $this->getExtErrors($this->ext)); + $this->assertContains('PHP_VERSION_ERROR', $this->getExtErrorKeys($this->ext)); } public function test_ziparchive_exists_error() { $this->setExtErrors($this->ext, []); $this->invokeProtectedMethod($this->ext, 'ziparchive_exists', ['NotZipArchive']); - $this->assertContains('NO_ZIPARCHIVE_ERROR', $this->getExtErrors($this->ext)); + $this->assertContains('NO_ZIPARCHIVE_ERROR', $this->getExtErrorKeys($this->ext)); } // --- Helpers --- @@ -138,9 +163,14 @@ protected function getExtErrors($ext): array { $prop = (new \ReflectionClass($ext))->getProperty('errors'); $prop->setAccessible(true); + return $prop->getValue($ext); + } + + protected function getExtErrorKeys($ext): array + { return array_map(static function ($e) { return is_array($e) ? $e[0] : $e; - }, $prop->getValue($ext)); + }, $this->getExtErrors($ext)); } protected function setExtErrors($ext, array $errors): void