diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0cd59f59..a7db9479 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,10 @@ permissions: jobs: testsuite: - uses: ADmad/.github/.github/workflows/testsuite-without-db.yml@master + uses: ADmad/.github/.github/workflows/testsuite-without-db.yml@cake6 secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} cs-stan: - uses: cakephp/.github/.github/workflows/cs-stan.yml@5.x + uses: cakephp/.github/.github/workflows/cs-stan.yml@6.x secrets: inherit diff --git a/.phive/phars.xml b/.phive/phars.xml index eaf11a6b..2274b71b 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,5 +1,5 @@ - - + + diff --git a/composer.json b/composer.json index 1d352857..b0f45ddc 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,13 @@ } ], "require": { - "php": ">=8.2", - "cakephp/cakephp": "^5.1.4" + "php": ">=8.5", + "cakephp/cakephp": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^11.5.3 || ^12.4 || ^13.0", - "cakephp/bake": "^3.0", - "cakephp/cakephp-codesniffer": "^5.1" + "phpunit/phpunit": "^13.0", + "cakephp/bake": "^4.0", + "cakephp/cakephp-codesniffer": "^6.0" }, "support": { "issues": "http://github.com/friendsofcake/bootstrap-ui/issues", @@ -60,5 +60,7 @@ "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } - } + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/phpcs.xml b/phpcs.xml index adf3ad1b..a0fb1259 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,6 +1,6 @@ - + src/ tests/ diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0c8957f8..de231af3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,5 @@ - + tests/TestCase/ diff --git a/psalm.xml b/psalm.xml index bb4ae33a..c8cee210 100644 --- a/psalm.xml +++ b/psalm.xml @@ -15,4 +15,10 @@ + + + + + + diff --git a/src/Command/BootstrapCommand.php b/src/Command/BootstrapCommand.php index 600eca8d..6e8a9500 100644 --- a/src/Command/BootstrapCommand.php +++ b/src/Command/BootstrapCommand.php @@ -4,10 +4,8 @@ namespace BootstrapUI\Command; use Cake\Command\Command; -use Cake\Console\Arguments; use Cake\Console\CommandCollection; use Cake\Console\CommandCollectionAwareInterface; -use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; use Cake\Utility\Text; @@ -42,9 +40,9 @@ public function setCommandCollection(CommandCollection $commands): void /** * @inheritDoc */ - public function execute(Arguments $args, ConsoleIo $io): ?int + public function execute(): ?int { - $io->warning('No command provided. Run `bootstrap --help` to get a list of commands.'); + $this->io->warning('No command provided. Run `bootstrap --help` to get a list of commands.'); return static::CODE_ERROR; } @@ -52,20 +50,20 @@ public function execute(Arguments $args, ConsoleIo $io): ?int /** * @inheritDoc */ - protected function displayHelp(ConsoleOptionParser $parser, Arguments $args, ConsoleIo $io): void + protected function displayHelp(ConsoleOptionParser $parser): void { - $io->out(Text::wrap($parser->getDescription(), 72), 2); - $io->info('Available Commands:', 2); + $this->io->out(Text::wrap($parser->getDescription(), 72), 2); + $this->io->info('Available Commands:', 2); foreach ($this->commands as $command => $class) { if (substr($command, 0, 10) === 'bootstrap ') { - $io->out("- $command"); + $this->io->out("- $command"); } } - $io->out(); - $io->out('To run a command, type `bootstrap command_name [args|options]`'); - $io->out('To get help on a specific command, type `bootstrap command_name --help`', 2); + $this->io->out(); + $this->io->out('To run a command, type `bootstrap command_name [args|options]`'); + $this->io->out('To get help on a specific command, type `bootstrap command_name --help`', 2); } /** diff --git a/src/Command/CopyLayoutsCommand.php b/src/Command/CopyLayoutsCommand.php index bdb5cf06..8ed2b929 100644 --- a/src/Command/CopyLayoutsCommand.php +++ b/src/Command/CopyLayoutsCommand.php @@ -4,8 +4,6 @@ namespace BootstrapUI\Command; use Cake\Command\Command; -use Cake\Console\Arguments; -use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; use Cake\Core\Plugin; use Cake\Utility\Filesystem; @@ -26,21 +24,18 @@ public static function getDescription(): string /** * @inheritDoc */ - public function execute(Arguments $args, ConsoleIo $io): ?int + public function execute(): ?int { - $io->info('Copying sample layouts...'); - - $target = $args->getArgument('target'); + $this->io->info('Copying sample layouts...'); + $target = $this->args->getArgument('target'); if ($target === null) { $target = $this->_getDefaultTargetPath(); } - - if (!$this->_copyLayouts($target)) { - $io->error("Sample layouts could not be copied to `$target`."); + if (!$this->copyLayouts($target)) { + $this->io->error("Sample layouts could not be copied to `$target`."); $this->abort(); } - - $io->success("Sample layouts copied successfully to `$target`."); + $this->io->success("Sample layouts copied successfully to `$target`."); return static::CODE_SUCCESS; } @@ -51,7 +46,7 @@ public function execute(Arguments $args, ConsoleIo $io): ?int * @param string $targetPath The path where to copy the files to. * @return bool */ - protected function _copyLayouts(string $targetPath): bool + protected function copyLayouts(string $targetPath): bool { $source = Plugin::path('BootstrapUI') . 'templates' . DS . 'layout' . DS . 'examples'; diff --git a/src/Command/InstallCommand.php b/src/Command/InstallCommand.php index 04775d1c..c87cd8f5 100644 --- a/src/Command/InstallCommand.php +++ b/src/Command/InstallCommand.php @@ -7,7 +7,7 @@ use Cake\Command\PluginAssetsRemoveCommand; use Cake\Command\PluginAssetsSymlinkCommand; use Cake\Console\Arguments; -use Cake\Console\ConsoleIo; +use Cake\Console\ConsoleIoInterface; use Cake\Console\ConsoleOptionParser; use Cake\Core\Plugin; use Cake\Utility\Filesystem; @@ -28,94 +28,83 @@ public static function getDescription(): string /** * @inheritDoc */ - public function execute(Arguments $args, ConsoleIo $io): ?int + public function execute(): ?int { - $this->installPackages($args, $io); - $this->refreshAssetBuffer($io); - $this->removePluginAssets($io); - $this->linkPluginAssets($io); - - $io->out(); - $io->success('Installation completed.'); + $this->installPackages(); + $this->refreshAssetBuffer(); + $this->removePluginAssets(); + $this->linkPluginAssets(); + $this->io->out(); + $this->io->success('Installation completed.'); return static::CODE_SUCCESS; } /** * Installs Bootstrap dependencies using NPM. - * - * @param \Cake\Console\Arguments $args The command arguments. - * @param \Cake\Console\ConsoleIo $io The console io. + * @return void */ - public function installPackages(Arguments $args, ConsoleIo $io): void + public function installPackages(): void { - if (!$this->_isNPMAvailable()) { - $io->error('NPM (https://www.npmjs.com/) is required, but not installed. Aborting.'); + if (!$this->isNPMAvailable()) { + $this->io->error('NPM (https://www.npmjs.com/) is required, but not installed. Aborting.'); $this->abort(); } - - $io->info('Clearing `node_modules` folder (this can take a while)...'); - if (!$this->_deleteNodeModules()) { - $io->error('Could not clear `node_modules` folder.'); + $this->io->info('Clearing `node_modules` folder (this can take a while)...'); + if (!$this->deleteNodeModules()) { + $this->io->error('Could not clear `node_modules` folder.'); $this->abort(); } - $io->success('Cleared `node_modules` folder.'); - - $io->info('Installing packages...'); - + $this->io->success('Cleared `node_modules` folder.'); + $this->io->info('Installing packages...'); $output = []; $return = 0; - $this->_runNPMInstall($output, $return, $io, $args->getOption('latest') === true); - $io->out($output); - + $this->runNPMInstall($output, $return, $this->args->getOption('latest') === true); + $this->io->out($output); if ($return !== 0) { - $io->error('Package installation failed.'); + $this->io->error('Package installation failed.'); $this->abort($return); } - $io->success('Packages installed successfully.'); + $this->io->success('Packages installed successfully.'); } /** * Extracts assets from node packages into the plugin's webroot. * - * @param \Cake\Console\ConsoleIo $io The console io. * @return void */ - public function refreshAssetBuffer(ConsoleIo $io): void + public function refreshAssetBuffer(): void { - $io->info('Refreshing package asset buffer...'); - if (!$this->_deleteBufferedPackageAssets($io)) { - $io->error('Could not clear all buffered files.'); + $this->io->info('Refreshing package asset buffer...'); + if (!$this->deleteBufferedPackageAssets()) { + $this->io->error('Could not clear all buffered files.'); $this->abort(); } else { - $io->success('All buffered files cleared.'); + $this->io->success('All buffered files cleared.'); } - - if (!$this->_bufferPackageAssets($io)) { - $io->error('Could not buffer all files.'); + if (!$this->bufferPackageAssets()) { + $this->io->error('Could not buffer all files.'); $this->abort(); } else { - $io->success('All files buffered.'); + $this->io->success('All files buffered.'); } } /** * Removes possibly already linked plugin assets from the application's webroot. * - * @param \Cake\Console\ConsoleIo $io The console io. * @return void */ - public function removePluginAssets(ConsoleIo $io): void + public function removePluginAssets(): void { - $io->info('Removing possibly existing plugin assets...'); - - $result = $this->executeCommand(PluginAssetsRemoveCommand::class, ['name' => 'BootstrapUI'], $io); + $this->io->info('Removing possibly existing plugin assets...'); + $result = $this->executeCommand(PluginAssetsRemoveCommand::class, ['name' => 'BootstrapUI']); if ( $result !== static::CODE_SUCCESS && $result !== null ) { - $io->error('Removing plugin assets failed.'); + $this->io->error('Removing plugin assets failed.'); $this->abort($result); } } @@ -123,19 +112,17 @@ public function removePluginAssets(ConsoleIo $io): void /** * Links the plugin assets into the application's webroot. * - * @param \Cake\Console\ConsoleIo $io The console io. * @return void */ - public function linkPluginAssets(ConsoleIo $io): void + public function linkPluginAssets(): void { - $io->info('Linking plugin assets...'); - - $result = $this->executeCommand(PluginAssetsSymlinkCommand::class, ['name' => 'BootstrapUI'], $io); + $this->io->info('Linking plugin assets...'); + $result = $this->executeCommand(PluginAssetsSymlinkCommand::class, ['name' => 'BootstrapUI']); if ( $result !== static::CODE_SUCCESS && $result !== null ) { - $io->error('Linking plugin assets failed.'); + $this->io->error('Linking plugin assets failed.'); $this->abort($result); } } @@ -145,9 +132,9 @@ public function linkPluginAssets(ConsoleIo $io): void * * @return bool */ - protected function _isNPMAvailable(): bool + protected function isNPMAvailable(): bool { - if ($this->_isWindows()) { + if ($this->isWindows()) { $command = 'where npm'; } else { $command = 'which npm'; @@ -161,7 +148,7 @@ protected function _isNPMAvailable(): bool * * @return bool */ - protected function _isWindows(): bool + protected function isWindows(): bool { return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; } @@ -171,7 +158,7 @@ protected function _isWindows(): bool * * @return bool */ - protected function _deleteNodeModules(): bool + protected function deleteNodeModules(): bool { $filesystem = new Filesystem(); @@ -183,16 +170,15 @@ protected function _deleteNodeModules(): bool * * @param array $output The variable to write the output to. * @param int $return The variable to write the return status code to. - * @param \Cake\Console\ConsoleIo $io The console io. * @param bool $useLatest Whether to install the latest minor versions. * @return void */ - protected function _runNPMInstall(array &$output, int &$return, ConsoleIo $io, bool $useLatest = false): void + protected function runNPMInstall(array &$output, int &$return, bool $useLatest = false): void { $pluginPath = Plugin::path('BootstrapUI'); $previousCwd = getcwd(); - if (!$this->_changeWorkingDirectory($pluginPath)) { - $io->error("Could not change into plugin directory `$pluginPath`."); + if (!$this->changeWorkingDirectory($pluginPath)) { + $this->io->error("Could not change into plugin directory `$pluginPath`."); $this->abort(); } @@ -201,9 +187,9 @@ protected function _runNPMInstall(array &$output, int &$return, ConsoleIo $io, b if ($useLatest) { $args[] = '--package-lock false'; } - switch ($io->level()) { - case ConsoleIo::QUIET: - if ($this->_isWindows()) { + switch ($this->io->level()) { + case ConsoleIoInterface::QUIET: + if ($this->isWindows()) { $null = 'NUL'; } else { $null = '/dev/null'; @@ -212,7 +198,7 @@ protected function _runNPMInstall(array &$output, int &$return, ConsoleIo $io, b $args[] = "--silent > $null"; break; - case ConsoleIo::VERBOSE: + case ConsoleIoInterface::VERBOSE: $args[] = '--verbose'; break; } @@ -221,7 +207,7 @@ protected function _runNPMInstall(array &$output, int &$return, ConsoleIo $io, b exec("npm install $args", $output, $return); } finally { if ($previousCwd !== false) { - $this->_changeWorkingDirectory($previousCwd); + $this->changeWorkingDirectory($previousCwd); } } } @@ -232,7 +218,7 @@ protected function _runNPMInstall(array &$output, int &$return, ConsoleIo $io, b * @param string $path The path to change to. * @return bool */ - protected function _changeWorkingDirectory(string $path): bool + protected function changeWorkingDirectory(string $path): bool { return chdir($path); } @@ -240,21 +226,19 @@ protected function _changeWorkingDirectory(string $path): bool /** * Deletes the buffered package assets. * - * @param \Cake\Console\ConsoleIo $io The console io. * @return bool */ - protected function _deleteBufferedPackageAssets(ConsoleIo $io): bool + protected function deleteBufferedPackageAssets(): bool { $result = true; - - foreach ($this->_findBufferedPackageAssets() as $file) { + foreach ($this->findBufferedPackageAssets() as $file) { if ( is_file($file->getPathname()) && unlink($file->getPathname()) ) { - $io->success("`{$file->getFilename()}` successfully deleted.", 1, ConsoleIo::VERBOSE); + $this->io->success("`{$file->getFilename()}` successfully deleted.", 1, ConsoleIoInterface::VERBOSE); } else { - $io->warning("`{$file->getFilename()}` could not be deleted."); + $this->io->warning("`{$file->getFilename()}` could not be deleted."); $result = false; } } @@ -267,7 +251,7 @@ protected function _deleteBufferedPackageAssets(ConsoleIo $io): bool * * @return array<\SplFileInfo> */ - protected function _findBufferedPackageAssets(): array + protected function findBufferedPackageAssets(): array { $filesystem = new Filesystem(); @@ -296,20 +280,17 @@ protected function _findBufferedPackageAssets(): array /** * Buffers the MPN package assets in the respective folders in the plugin's webroot. * - * @param \Cake\Console\ConsoleIo $io The console io. * @return bool */ - protected function _bufferPackageAssets(ConsoleIo $io): bool + protected function bufferPackageAssets(): bool { $filesystem = new Filesystem(); - $webrootPath = Plugin::path('BootstrapUI') . 'webroot' . DS; $cssPath = $webrootPath . 'css' . DS; $fontPath = $webrootPath . 'font' . DS; $jsPath = $webrootPath . 'js' . DS; - $result = true; - foreach ($this->_findPackageAssets() as $file) { + foreach ($this->findPackageAssets() as $file) { $assetPath = null; $matches = []; @@ -329,7 +310,7 @@ protected function _bufferPackageAssets(ConsoleIo $io): bool } if ($assetPath === null) { - $io->info("Skipped `{$file->getFilename()}`.", 1, ConsoleIo::VERBOSE); + $this->io->info("Skipped `{$file->getFilename()}`.", 1, ConsoleIoInterface::VERBOSE); continue; } @@ -346,9 +327,9 @@ protected function _bufferPackageAssets(ConsoleIo $io): bool is_file($file->getPathname()) && copy($file->getPathname(), $assetPath . $file->getFilename()) ) { - $io->success("`{$file->getFilename()}` successfully copied.", 1, ConsoleIo::VERBOSE); + $this->io->success("`{$file->getFilename()}` successfully copied.", 1, ConsoleIoInterface::VERBOSE); } else { - $io->warning("`{$file->getFilename()}` could not be copied."); + $this->io->warning("`{$file->getFilename()}` could not be copied."); $result = false; } } @@ -361,7 +342,7 @@ protected function _bufferPackageAssets(ConsoleIo $io): bool * * @return array<\SplFileInfo> */ - protected function _findPackageAssets(): array + protected function findPackageAssets(): array { $filesystem = new Filesystem(); @@ -400,4 +381,15 @@ protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOption 'short' => 'l', ]); } + + /** + * Sets the command line arguments. + * + * @param \Cake\Console\Arguments $args The command line arguments. + * @return void + */ + public function setArgs(Arguments $args): void + { + $this->args = $args; + } } diff --git a/src/Command/ModifyViewCommand.php b/src/Command/ModifyViewCommand.php index 7d481e2d..3bf50490 100644 --- a/src/Command/ModifyViewCommand.php +++ b/src/Command/ModifyViewCommand.php @@ -4,8 +4,6 @@ namespace BootstrapUI\Command; use Cake\Command\Command; -use Cake\Console\Arguments; -use Cake\Console\ConsoleIo; use Cake\Console\ConsoleOptionParser; /** @@ -24,21 +22,18 @@ public static function getDescription(): string /** * @inheritDoc */ - public function execute(Arguments $args, ConsoleIo $io): ?int + public function execute(): ?int { - $io->info('Modifying view...'); - - $file = $args->getArgument('file'); + $this->io->info('Modifying view...'); + $file = $this->args->getArgument('file'); if ($file === null) { - $file = $this->_getDefaultFilePath(); + $file = $this->getDefaultFilePath(); } - - if (!$this->_modifyView($file)) { - $io->error("Could not modify `$file`."); + if (!$this->modifyView($file)) { + $this->io->error("Could not modify `$file`."); $this->abort(); } - - $io->success("Modified `$file`."); + $this->io->success("Modified `$file`."); return static::CODE_SUCCESS; } @@ -49,13 +44,13 @@ public function execute(Arguments $args, ConsoleIo $io): ?int * @param string $filePath The path of the file to modify. * @return bool */ - protected function _modifyView(string $filePath): bool + protected function modifyView(string $filePath): bool { - if (!$this->_isFile($filePath)) { + if (!$this->isFile($filePath)) { return false; } - $content = $this->_readFile($filePath); + $content = $this->readFile($filePath); if ($content === false) { return false; } @@ -88,7 +83,7 @@ protected function _modifyView(string $filePath): bool return false; } - return $this->_writeFile($filePath, $content); + return $this->writeFile($filePath, $content); } /** @@ -97,7 +92,7 @@ protected function _modifyView(string $filePath): bool * @param string $filePath The file path. * @return bool */ - protected function _isFile(string $filePath): bool + protected function isFile(string $filePath): bool { return is_file($filePath); } @@ -108,7 +103,7 @@ protected function _isFile(string $filePath): bool * @param string $filePath The file path. * @return string|false */ - protected function _readFile(string $filePath): false|string + protected function readFile(string $filePath): false|string { return file_get_contents($filePath); } @@ -120,7 +115,7 @@ protected function _readFile(string $filePath): false|string * @param string $content The content to write. * @return bool */ - protected function _writeFile(string $filePath, string $content): bool + protected function writeFile(string $filePath, string $content): bool { return file_put_contents($filePath, $content) !== false; } @@ -130,7 +125,7 @@ protected function _writeFile(string $filePath, string $content): bool * * @return string */ - protected function _getDefaultFilePath(): string + protected function getDefaultFilePath(): string { return APP . 'View' . DS . 'AppView.php'; } @@ -147,7 +142,7 @@ protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOption ->addArgument('file', [ 'help' => sprintf( 'The path of the `AppView.php` file. Defaults to `%s`.', - $this->_getDefaultFilePath(), + $this->getDefaultFilePath(), ), 'required' => false, ]) diff --git a/src/View/Helper/BreadcrumbsHelper.php b/src/View/Helper/BreadcrumbsHelper.php index d9d0545e..974ce7e0 100644 --- a/src/View/Helper/BreadcrumbsHelper.php +++ b/src/View/Helper/BreadcrumbsHelper.php @@ -12,12 +12,12 @@ class BreadcrumbsHelper extends CoreBreadcrumbsHelper /** * @inheritDoc */ - protected array $_defaultConfig = [ + protected array $defaultConfig = [ 'ariaCurrent' => 'last', 'templates' => [ 'wrapper' => '', - 'item' => '{{title}}', - 'itemWithoutLink' => '{{title}}', + 'item' => '{{content}}', + 'itemWithoutLink' => '{{content}}', 'separator' => '', ], ]; @@ -27,7 +27,7 @@ class BreadcrumbsHelper extends CoreBreadcrumbsHelper * * @var array */ - protected array $_defaultAttributes = [ + protected array $defaultAttributes = [ 'class' => [ 'wrapper' => 'breadcrumb', 'item' => 'breadcrumb-item', @@ -39,9 +39,9 @@ class BreadcrumbsHelper extends CoreBreadcrumbsHelper */ public function render(array $attributes = [], array $separator = []): string { - $attributes = $this->injectClasses($this->_defaultAttributes['class']['wrapper'], $attributes); + $attributes = $this->injectClasses($this->defaultAttributes['class']['wrapper'], $attributes); - $this->_markActiveCrumb(); + $this->markActiveCrumb(); return parent::render($attributes, $separator); } @@ -49,51 +49,57 @@ public function render(array $attributes = [], array $separator = []): string /** * @inheritDoc */ - public function add(array|string $title, array|string|null $url = null, array $options = []) + public function add(string $content, array|string|null $url = null, array $options = []): static { - if (is_array($title)) { - $crumbs = []; - foreach ($title as $crumb) { - $options = []; - if (isset($crumb['options'])) { - $options = $crumb['options']; - } + $options = $this->injectClasses($this->defaultAttributes['class']['item'], $options); - $crumb['options'] = $this->injectClasses($this->_defaultAttributes['class']['item'], $options); - $crumbs[] = $crumb + ['title' => '', 'url' => null, 'options' => []]; - } + parent::add($content, $url, $options); - /** @phpstan-ignore function.alreadyNarrowedType */ - if (method_exists(parent::class, 'addMany')) { - return parent::addMany($crumbs); - } + return $this; + } - return parent::add($crumbs); + /** + * @inheritDoc + */ + public function addMany(array $crumbs, array $options = []): static + { + $crumbsToAdd = []; + foreach ($crumbs as $crumb) { + $crumbOptions = ($crumb['options'] ?? []) + $options; + $crumb['options'] = $this->injectClasses( + $this->defaultAttributes['class']['item'], + $crumbOptions, + ); + $crumbsToAdd[] = $crumb + ['content' => '', 'url' => null, 'options' => []]; } - $options = $this->injectClasses($this->_defaultAttributes['class']['item'], $options); + parent::addMany($crumbsToAdd); - return parent::add($title, $url, $options); + return $this; } /** * @inheritDoc */ - public function prepend(array|string $title, array|string|null $url = null, array $options = []) + public function prepend(string $content, array|string|null $url = null, array $options = []): static { - $options = $this->injectClasses($this->_defaultAttributes['class']['item'], $options); + $options = $this->injectClasses($this->defaultAttributes['class']['item'], $options); - return parent::prepend($title, $url, $options); + parent::prepend($content, $url, $options); + + return $this; } /** * @inheritDoc */ - public function insertAt(int $index, string $title, array|string|null $url = null, array $options = []) + public function insertAt(int $index, string $content, array|string|null $url = null, array $options = []): static { - $options = $this->injectClasses($this->_defaultAttributes['class']['item'], $options); + $options = $this->injectClasses($this->defaultAttributes['class']['item'], $options); + + parent::insertAt($index, $content, $url, $options); - return parent::insertAt($index, $title, $url, $options); + return $this; } /** @@ -102,13 +108,13 @@ public function insertAt(int $index, string $title, array|string|null $url = nul * * @return void */ - protected function _markActiveCrumb(): void + protected function markActiveCrumb(): void { if (!$this->crumbs) { return; } - $this->_clearActiveCrumb(); + $this->clearActiveCrumb(); $key = null; if ($this->getConfig('ariaCurrent') === 'lastWithLink') { @@ -141,7 +147,7 @@ protected function _markActiveCrumb(): void * * @return void */ - protected function _clearActiveCrumb(): void + protected function clearActiveCrumb(): void { foreach ($this->crumbs as $key => $crumb) { $this->crumbs[$key]['options'] = $this->removeClasses('active', $this->crumbs[$key]['options']); diff --git a/src/View/Helper/ColorModeHelper.php b/src/View/Helper/ColorModeHelper.php index bb3a3f93..1be4eb91 100644 --- a/src/View/Helper/ColorModeHelper.php +++ b/src/View/Helper/ColorModeHelper.php @@ -23,7 +23,7 @@ class ColorModeHelper extends Helper /** * @var array */ - protected array $_defaultConfig = [ + protected array $defaultConfig = [ // localStorage key under which the user's choice is persisted. 'storageKey' => 'bs-theme', // Mode used when nothing is stored yet. Accepts either a ColorMode @@ -68,9 +68,9 @@ public function script(array $options = []): string { $config = $options + $this->getConfig(); - $storageKey = $this->_jsonValue($config['storageKey']); - $default = $this->_jsonValue($this->_modeValue($config['default'])); - $target = $this->_jsonValue($config['target']); + $storageKey = $this->jsonValue($config['storageKey']); + $default = $this->jsonValue($this->modeValue($config['default'])); + $target = $this->jsonValue($config['target']); // Inline IIFE; ASCII-only so it survives any reasonable CSP/serializer. // localStorage access is wrapped in try/catch — when storage is blocked @@ -117,18 +117,18 @@ public function toggle(array $options = []): string $buttons = ''; foreach ($modes as $mode) { - $value = $this->_modeValue($mode); - $label = $this->_modeLabel($mode); - $buttons .= $this->_button($value, $label, (string)$config['buttonClass']); + $value = $this->modeValue($mode); + $label = $this->modeLabel($mode); + $buttons .= $this->button($value, $label, (string)$config['buttonClass']); } $initJs = '(function(){' . 'var w=document.currentScript&&document.currentScript.previousElementSibling;' . 'if(!w||!w.matches("[data-bs-theme-toggle]")){return;}' . 'var active=(window.BootstrapUIColorMode&&BootstrapUIColorMode.get())||' - . $this->_jsonValue($this->_modeValue($config['default'])) + . $this->jsonValue($this->modeValue($config['default'])) . ';' - . 'var ac=' . $this->_jsonValue($config['activeClass']) . ';' + . 'var ac=' . $this->jsonValue($config['activeClass']) . ';' . 'function sync(active){w.querySelectorAll("[data-bs-theme-value]").forEach(function(x){' . 'var on=x.getAttribute("data-bs-theme-value")===active;' . 'x.classList.toggle(ac,on);x.setAttribute("aria-pressed",on?"true":"false");});}' @@ -138,7 +138,7 @@ public function toggle(array $options = []): string . 'sync(active);' . '})();'; - return $this->_wrap($wrapperAttrs, $buttons) . ''; + return $this->wrap($wrapperAttrs, $buttons) . ''; } /** @@ -148,7 +148,7 @@ public function toggle(array $options = []): string * @param string $content Inner HTML. * @return string */ - protected function _wrap(array $attrs, string $content): string + protected function wrap(array $attrs, string $content): string { $rendered = ''; foreach ($attrs as $name => $value) { @@ -166,7 +166,7 @@ protected function _wrap(array $attrs, string $content): string * @param string $buttonClass Button CSS classes (without active state). * @return string */ - protected function _button(string $value, string $label, string $buttonClass): string + protected function button(string $value, string $label, string $buttonClass): string { return '', ]; $this->templates = new StringTemplate($templates); - $this->context = $this->getMockBuilder('Cake\View\Form\ContextInterface')->getMock(); + $this->context = $this->createStub(ContextInterface::class); } /** @@ -42,7 +43,7 @@ public function setUp(): void * * @return void */ - public function testRenderSimple() + public function testRenderSimple(): void { $button = new ButtonWidget($this->templates); $result = $button->render(['name' => 'my_input'], $this->context); @@ -58,7 +59,7 @@ public function testRenderSimple() * * @return void */ - public function testRenderDifferentStyles() + public function testRenderDifferentStyles(): void { $styles = [ 'primary', @@ -93,7 +94,7 @@ public function testRenderDifferentStyles() * * @return void */ - public function testRenderType() + public function testRenderType(): void { $button = new ButtonWidget($this->templates); $data = [ @@ -115,12 +116,12 @@ public function testRenderType() * * @return void */ - public function testRenderWithText() + public function testRenderWithText(): void { $button = new ButtonWidget($this->templates); $data = [ 'text' => 'Some ', - 'escapeTitle' => false, + 'escape' => false, ]; $result = $button->render($data, $this->context); $expected = [ @@ -130,7 +131,7 @@ public function testRenderWithText() ]; $this->assertHtml($expected, $result); - $data['escapeTitle'] = true; + $data['escape'] = true; $result = $button->render($data, $this->context); $expected = [ 'button' => ['type' => 'submit', 'class' => 'btn btn-secondary'], @@ -145,7 +146,7 @@ public function testRenderWithText() * * @return void */ - public function testRenderAttributes() + public function testRenderAttributes(): void { $button = new ButtonWidget($this->templates); $data = [ @@ -173,7 +174,7 @@ public function testRenderAttributes() * * @return void */ - public function testRenderTemplateVars() + public function testRenderTemplateVars(): void { $this->templates->add([ 'button' => '',