diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c807a6..1036497 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,32 +1,16 @@ name: CI on: + push: ~ pull_request: ~ - schedule: - - cron: 0 13 * * MON,THU +# schedule: +# - cron: 0 13 * * MON,THU permissions: read-all jobs: - ecs: - name: ECS - runs-on: ubuntu-latest - steps: - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.1 - extensions: dom, fileinfo, filter, gd, hash, intl, json, mbstring, mysqli, pcre, pdo_mysql, zlib - coverage: none - - - name: Checkout - uses: actions/checkout@v3 - - - name: Install the dependencies - run: composer install --no-interaction --no-progress - - - name: Run ECS - run: composer run check-cs + build-tools: + uses: 'terminal42/contao-build-tools/.github/workflows/build-tools.yml@main' tests: name: PHP ${{ matrix.php }} / Contao ${{ matrix.contao }} diff --git a/composer.json b/composer.json index af24289..d8aa982 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,8 @@ "phpunit/phpunit": "^9.3", "symfony/phpunit-bridge": "^5.4", "contao/test-case": "^4.13 || ^5.0", - "symfony/monolog-bundle": "^3.1" + "symfony/monolog-bundle": "^3.1", + "terminal42/service-annotation-bundle": "^1.1" }, "conflict": { "contao/manager-plugin": "<2.0 || >=3.0", @@ -66,9 +67,10 @@ }, "config": { "allow-plugins": { - "contao-components/installer": true, - "contao/manager-plugin": true, - "terminal42/contao-build-tools": true + "terminal42/contao-build-tools": true, + "contao-components/installer": false, + "contao/manager-plugin": false, + "php-http/discovery": false } } } diff --git a/contao/dca/tl_page.php b/contao/dca/tl_page.php index 93803c1..caa2f79 100755 --- a/contao/dca/tl_page.php +++ b/contao/dca/tl_page.php @@ -15,15 +15,12 @@ 'inputType' => 'pageTree', 'eval' => ['fieldType' => 'radio', 'multiple' => false, 'rootNodes' => [0], 'tl_class' => 'w50 clr'], 'sql' => "int(10) unsigned NOT NULL default '0'", - // 'load_callback' => [['Terminal42\ChangeLanguage\EventListener\DataContainer\PageFieldsListener', 'onLoadLanguageMain']], - // 'save_callback' => [['Terminal42\ChangeLanguage\EventListener\DataContainer\PageFieldsListener', 'onSaveLanguageMain']], ]; $GLOBALS['TL_DCA']['tl_page']['fields']['languageRoot'] = [ 'label' => &$GLOBALS['TL_LANG']['tl_page']['languageRoot'], 'exclude' => true, 'inputType' => 'select', - // 'options_callback' => array('Terminal42\ChangeLanguage\EventListener\DataContainer\PageFieldsListener', 'onLanguageRootOptions'), 'eval' => ['includeBlankOption' => true, 'blankOptionLabel' => &$GLOBALS['TL_LANG']['tl_page']['languageRoot'][2], 'tl_class' => 'w50'], 'sql' => "int(10) unsigned NOT NULL default '0'", ]; diff --git a/depcheck.php b/depcheck.php new file mode 100644 index 0000000..207338e --- /dev/null +++ b/depcheck.php @@ -0,0 +1,26 @@ +ignoreErrorsOnPackage('composer/semver', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('doctrine/dbal', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('doctrine/doctrine-bundle', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('knplabs/knp-menu-bundle', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('knplabs/knp-time-bundle', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('psr/log', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('scheb/2fa-bundle', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony-cmf/routing-bundle', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/config', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/dependency-injection', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/event-dispatcher-contracts', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/filesystem', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/framework-bundle', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/http-kernel', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/routing', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/security-bundle', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/security-core', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/service-contracts', [ErrorType::SHADOW_DEPENDENCY]) + ->ignoreErrorsOnPackage('symfony/twig-bundle', [ErrorType::SHADOW_DEPENDENCY]) +; diff --git a/ecs.php b/ecs.php new file mode 100644 index 0000000..5d0c404 --- /dev/null +++ b/ecs.php @@ -0,0 +1,10 @@ +skip([NoSuperfluousPhpdocTagsFixer::class]); +}; diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..32cfa91 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + ignoreErrors: + - '#Access to an undefined property Contao\\DataContainer::\$activeRecord.#' + - '#Parameter \#2 \$models#' diff --git a/src/EventListener/BackendView/ArticleViewListener.php b/src/EventListener/BackendView/ArticleViewListener.php index 40405a5..0749522 100644 --- a/src/EventListener/BackendView/ArticleViewListener.php +++ b/src/EventListener/BackendView/ArticleViewListener.php @@ -39,7 +39,7 @@ protected function getCurrentPage(): ?PageModel $this->currentArticle = ArticleModel::findOneBy(['tl_article.id=(SELECT pid FROM tl_content WHERE id=?)'], [$this->dataContainer->id]); } else { - $this->currentArticle = ArticleModel::findByPk($this->dataContainer->id); + $this->currentArticle = ArticleModel::findById($this->dataContainer->id); } } diff --git a/src/EventListener/BackendView/PageViewListener.php b/src/EventListener/BackendView/PageViewListener.php index a6b96ed..8432a06 100644 --- a/src/EventListener/BackendView/PageViewListener.php +++ b/src/EventListener/BackendView/PageViewListener.php @@ -26,7 +26,7 @@ protected function getCurrentPage(): ?PageModel return null; } - return PageModel::findByPk($node); + return PageModel::findById($node); } protected function getAvailableLanguages(PageModel $page): array diff --git a/src/EventListener/BackendView/ParentChildViewListener.php b/src/EventListener/BackendView/ParentChildViewListener.php index fdab28a..9ec77f2 100644 --- a/src/EventListener/BackendView/ParentChildViewListener.php +++ b/src/EventListener/BackendView/ParentChildViewListener.php @@ -17,7 +17,7 @@ class ParentChildViewListener extends AbstractViewListener { /** - * @var Model + * @var Model|false */ private $current = false; @@ -44,7 +44,7 @@ protected function getCurrentPage() $t = $class::getTable(); $this->current = $class::findOneBy(["$t.id=(SELECT pid FROM ".$this->getTable().' WHERE id=?)'], [$this->dataContainer->id]); } else { - $this->current = $class::findByPk($this->dataContainer->id); + $this->current = $class::findById($this->dataContainer->id); } } @@ -146,7 +146,7 @@ private function getModelClass(): string return Model::getClassFromTable($GLOBALS['TL_DCA'][$this->getTable()]['config']['ptable']); } - private function hasParent() + private function hasParent(): bool { /** @var Model $class */ $class = $this->getModelClass(); diff --git a/src/EventListener/CallbackSetupListener.php b/src/EventListener/CallbackSetupListener.php index b5b45d4..80fe46b 100644 --- a/src/EventListener/CallbackSetupListener.php +++ b/src/EventListener/CallbackSetupListener.php @@ -20,7 +20,7 @@ */ class CallbackSetupListener { - private static $listeners; + private static ?array $listeners = null; public function __invoke(string $table): void { diff --git a/src/EventListener/DataContainer/AbstractChildTableListener.php b/src/EventListener/DataContainer/AbstractChildTableListener.php index 509f0d0..38b4fb4 100644 --- a/src/EventListener/DataContainer/AbstractChildTableListener.php +++ b/src/EventListener/DataContainer/AbstractChildTableListener.php @@ -90,7 +90,7 @@ protected function getModel($id) /** @var Model $class */ $class = Model::getClassFromTable($this->table); - return $class::findByPk($id); + return $class::findById($id); } /** diff --git a/src/EventListener/DataContainer/ArticleListener.php b/src/EventListener/DataContainer/ArticleListener.php index 49cfc01..76b5875 100644 --- a/src/EventListener/DataContainer/ArticleListener.php +++ b/src/EventListener/DataContainer/ArticleListener.php @@ -33,10 +33,10 @@ public function onLoad(DataContainer $dc): void if ('editAll' === $action) { $this->addFieldsToPalettes(); } elseif ('edit' === $action) { - $article = ArticleModel::findByPk($dc->id); + $article = ArticleModel::findById($dc->id); if (null !== $article) { - $page = PageModel::findByPk($article->pid); + $page = PageModel::findById($article->pid); if (null !== $page && null !== (new PageFinder())->findAssociatedInMaster($page)) { $this->addFieldsToPalettes(); @@ -45,11 +45,11 @@ public function onLoad(DataContainer $dc): void } } - public function onLanguageMainOptions(DataContainer $dc) + public function onLanguageMainOptions(DataContainer $dc): array { $pageFinder = new PageFinder(); - $current = ArticleModel::findByPk($dc->id); - $page = PageModel::findByPk($current->pid); + $current = ArticleModel::findById($dc->id); + $page = PageModel::findById($current->pid); if (null === $page || null === ($master = $pageFinder->findAssociatedInMaster($page))) { return []; diff --git a/src/EventListener/DataContainer/MissingLanguageIconListener.php b/src/EventListener/DataContainer/MissingLanguageIconListener.php index 6517532..694a654 100644 --- a/src/EventListener/DataContainer/MissingLanguageIconListener.php +++ b/src/EventListener/DataContainer/MissingLanguageIconListener.php @@ -23,12 +23,14 @@ */ class MissingLanguageIconListener implements ResetInterface { - private static $callbacks; + private static ?array $callbacks = null; private Security $security; + private Connection $connection; private ?array $pageCache = null; + private ?array $translationCache = null; public function __construct(Security $security, Connection $connection) @@ -60,6 +62,8 @@ public function reset(): void /** * Adds missing translation warning to page tree. + * + * @param string|array|null $previousResult; */ private function onPageLabel(array $args, $previousResult = null): string { @@ -85,7 +89,7 @@ private function onPageLabel(array $args, $previousResult = null): string ($translation['languageMain'] ?? null) > 0 && $user instanceof BackendUser && \is_array($user->pageLanguageLabels) - && \in_array(($translation['rootId'] ?? null), $user->pageLanguageLabels, false) + && \in_array($translation['rootId'] ?? null, $user->pageLanguageLabels, false) ) { return sprintf( '%s (%s)', @@ -101,6 +105,8 @@ private function onPageLabel(array $args, $previousResult = null): string /** * Adds missing translation warning to article tree. + * + * @param string|array|null $previousResult */ private function onArticleLabel(array $args, $previousResult = null): string { @@ -123,6 +129,8 @@ private function onArticleLabel(array $args, $previousResult = null): string /** * Generate missing translation warning for news child records. + * + * @param string|array|null $previousResult */ private function onNewsChildRecords(array $args, $previousResult = null): string { @@ -147,6 +155,8 @@ private function onNewsChildRecords(array $args, $previousResult = null): string /** * Generate missing translation warning for calendar events child records. + * + * @param string|array|null $previousResult */ private function onCalendarEventChildRecords(array $args, $previousResult = null): string { @@ -154,7 +164,6 @@ private function onCalendarEventChildRecords(array $args, $previousResult = null $label = (string) $previousResult; if (0 === $this->getChildTranslation((int) $row['id'], 'tl_calendar_events', 'tl_calendar', 'master')) { - //return $this->generateLabelWithWarning($label); return preg_replace( '##', $this->generateLabelWithWarning('', 'position:absolute;top:6px').'', @@ -168,6 +177,8 @@ private function onCalendarEventChildRecords(array $args, $previousResult = null /** * Generate missing translation warning for faq child records. + * + * @param string|array|null $previousResult */ private function onFaqChildRecords(array $args, $previousResult = null): string { @@ -186,13 +197,7 @@ private function onFaqChildRecords(array $args, $previousResult = null): string return $label; } - /** - * @param string $label - * @param string $imgStyle - * - * @return string - */ - private function generateLabelWithWarning($label, $imgStyle = '') + private function generateLabelWithWarning(string $label, string $imgStyle = ''): string { return $label.sprintf( '%s', @@ -241,17 +246,17 @@ private function getPageTranslation(int $id): ?array $childRecords = function (array $parentIds, int $rootId, $return = []) use (&$childRecords) { $children = $this->connection->fetchAllAssociativeIndexed( <<translationCache[$table] = $this->connection->fetchAllKeyValue( << 0 - SQL + SELECT + c.id, + IFNULL(ct.id, 0) + FROM $table c + JOIN $ptable p ON c.pid=p.id + LEFT JOIN $table ct ON c.languageMain=ct.id + LEFT JOIN $ptable pt ON p.$parentField=pt.id + WHERE pt.id > 0 + SQL, ); return $this->translationCache[$table][$id] ?? null; diff --git a/src/EventListener/DataContainer/PageFieldsListener.php b/src/EventListener/DataContainer/PageFieldsListener.php index 1fef8c3..92de729 100644 --- a/src/EventListener/DataContainer/PageFieldsListener.php +++ b/src/EventListener/DataContainer/PageFieldsListener.php @@ -16,6 +16,10 @@ class PageFieldsListener /** * Sets rootNodes when initializing the languageMain field. * + * @param string|int $value + * + * @return string|int + * * @Callback(table="tl_page", target="fields.languageMain.load") */ public function onLoadLanguageMain($value, DataContainer $dc) @@ -25,11 +29,11 @@ public function onLoadLanguageMain($value, DataContainer $dc) } $page = PageModel::findWithDetails($dc->id); - $root = PageModel::findByPk($page->rootId); + $root = PageModel::findById($page->rootId); if ( !$root - || ($root->fallback && (!$root->languageRoot || null === PageModel::findByPk($root->languageRoot))) + || ($root->fallback && (!$root->languageRoot || null === PageModel::findById($root->languageRoot))) ) { return $value; } @@ -51,6 +55,10 @@ public function onLoadLanguageMain($value, DataContainer $dc) /** * Validate input value when saving tl_page.languageMain field. * + * @param string|int $value + * + * @return string|int + * * @Callback(table="tl_page", target="fields.languageMain.save") */ public function onSaveLanguageMain($value, DataContainer $dc) @@ -85,7 +93,8 @@ public function onSaveLanguageMain($value, DataContainer $dc) } /** - * Gets list of options for language root selection (linking multiple fallback roots on different domains). + * Gets list of options for language root selection (linking multiple fallback + * roots on different domains). * * @Callback(table="tl_page", target="fields.languageRoot.options") */ diff --git a/src/EventListener/DataContainer/PageInitializationListener.php b/src/EventListener/DataContainer/PageInitializationListener.php index 01b58f2..e75ea37 100644 --- a/src/EventListener/DataContainer/PageInitializationListener.php +++ b/src/EventListener/DataContainer/PageInitializationListener.php @@ -51,7 +51,7 @@ public function onLoad(DataContainer $dc): void private function handleEditMode(DataContainer $dc): void { - $page = PageModel::findByPk($dc->id); + $page = PageModel::findById($dc->id); if (null === $page) { return; @@ -65,12 +65,12 @@ private function handleEditMode(DataContainer $dc): void return; } - $root = PageModel::findByPk($page->loadDetails()->rootId); + $root = PageModel::findById($page->loadDetails()->rootId); $addLanguageMain = true; if ( !$root - || ($root->fallback && (!$root->languageRoot || null === PageModel::findByPk($root->languageRoot))) + || ($root->fallback && (!$root->languageRoot || null === PageModel::findById($root->languageRoot))) ) { $addLanguageMain = false; } diff --git a/src/EventListener/DataContainer/PageOperationListener.php b/src/EventListener/DataContainer/PageOperationListener.php index b6a85fd..d2cc0b5 100644 --- a/src/EventListener/DataContainer/PageOperationListener.php +++ b/src/EventListener/DataContainer/PageOperationListener.php @@ -44,7 +44,7 @@ private function onSubmit(DataContainer $dc): void if ( 'root' === $dc->activeRecord->type && $dc->activeRecord->fallback - && (!$dc->activeRecord->languageRoot || null === PageModel::findByPk($dc->activeRecord->languageRoot)) + && (!$dc->activeRecord->languageRoot || null === PageModel::findById($dc->activeRecord->languageRoot)) ) { $this->resetPageAndChildren((int) $dc->id); } diff --git a/src/EventListener/DataContainer/ParentTableListener.php b/src/EventListener/DataContainer/ParentTableListener.php index 6f3088f..7909690 100644 --- a/src/EventListener/DataContainer/ParentTableListener.php +++ b/src/EventListener/DataContainer/ParentTableListener.php @@ -56,7 +56,7 @@ public function register(): void */ public function onMasterOptions(DataContainer $dc) { - if (null === ($jumpTo = PageModel::findByPk($dc->activeRecord->jumpTo))) { + if (null === ($jumpTo = PageModel::findById($dc->activeRecord->jumpTo))) { return []; } @@ -89,6 +89,9 @@ public function onMasterOptions(DataContainer $dc) return $options; } + /** + * @param string|null $value + */ private function validateMaster($value, DataContainer $dc): void { if (!$value) { diff --git a/src/Helper/LabelCallback.php b/src/Helper/LabelCallback.php index dfa46b6..04be405 100644 --- a/src/Helper/LabelCallback.php +++ b/src/Helper/LabelCallback.php @@ -16,10 +16,8 @@ class LabelCallback /** * Registers callback for given table. - * - * @param string $table */ - public function register($table, callable $callback): void + public function register(string $table, callable $callback): void { Controller::loadDataContainer($table); @@ -45,25 +43,19 @@ public function register($table, callable $callback): void /** * Creates and registers new LabelCallback. - * - * @param string $table - * - * @return static */ - public static function createAndRegister($table, callable $callback) + public static function createAndRegister(string $table, callable $callback): self { - $instance = new static(); + $instance = new self(); $instance->register($table, $callback); return $instance; } /** - * @param callable $callback - * - * @return string|int + * @return mixed */ - private function executeCallback($callback, array $args) + private function executeCallback(callable $callback, array $args) { // Support Contao's getInstance() method when callback is an array if (\is_array($callback)) { diff --git a/src/Helper/LanguageText.php b/src/Helper/LanguageText.php index b6afcc4..45271d3 100644 --- a/src/Helper/LanguageText.php +++ b/src/Helper/LanguageText.php @@ -92,7 +92,7 @@ public static function createFromOptionWizard($config): self $config = StringUtil::deserialize($config); if (!\is_array($config)) { - return new static(); + return new self(); } $map = []; @@ -115,6 +115,6 @@ public static function createFromOptionWizard($config): self $map[$text['key']] = $text['value']; } - return new static($map); + return new self($map); } } diff --git a/src/Language.php b/src/Language.php index 54b5900..d3c1245 100644 --- a/src/Language.php +++ b/src/Language.php @@ -7,7 +7,8 @@ class Language { /** - * Normalizes a language representation by splitting language and country with given delimiter. + * Normalizes a language representation by splitting language and country with + * given delimiter. * * @throws \InvalidArgumentException */ @@ -21,7 +22,7 @@ public static function normalize(string $language, string $delimiter): string } /** - * Returns the language formatted as IETF Language Tag (BCP 47) + * Returns the language formatted as IETF Language Tag (BCP 47). * Example: en, en-US, de-CH. * * @see http://www.w3.org/International/articles/language-tags/ @@ -34,7 +35,7 @@ public static function toLanguageTag(string $language): string } /** - * Returns the language formatted as ICU Locale ID + * Returns the language formatted as ICU Locale ID. * Example: en, en_US, de_CH. * * @see http://userguide.icu-project.org/locale diff --git a/src/Navigation/NavigationItem.php b/src/Navigation/NavigationItem.php index ba69201..c017fea 100644 --- a/src/Navigation/NavigationItem.php +++ b/src/Navigation/NavigationItem.php @@ -132,7 +132,7 @@ public function getNormalizedLanguage(): string } /** - * Returns the language formatted as IETF Language Tag (BCP 47) + * Returns the language formatted as IETF Language Tag (BCP 47). * Example: en, en-US, de-CH. * * @see http://www.w3.org/International/articles/language-tags/ @@ -143,7 +143,7 @@ public function getLanguageTag(): string } /** - * Returns the language formatted as ICU Locale ID + * Returns the language formatted as ICU Locale ID. * Example: en, en_US, de_CH. * * @see http://userguide.icu-project.org/locale diff --git a/src/Navigation/UrlParameterBag.php b/src/Navigation/UrlParameterBag.php index a10e82b..5e9ea5b 100644 --- a/src/Navigation/UrlParameterBag.php +++ b/src/Navigation/UrlParameterBag.php @@ -49,7 +49,7 @@ public function getUrlAttribute(string $name) } /** - * @param int|float|string|bool $value + * @param string|int|object $value */ public function setUrlAttribute(string $name, $value): void { @@ -89,7 +89,7 @@ public function getQueryParameter(string $name) } /** - * @param int|float|string|bool $value + * @param string|int|object $value */ public function setQueryParameter(string $name, $value): void { @@ -168,6 +168,8 @@ public function generateQueryString(): ?string /** * Makes sure the given value is scalar or an array of scalar values. + * + * @param mixed $value */ private function validateScalar($value): void { diff --git a/src/PageFinder.php b/src/PageFinder.php index 5126f63..0b64b92 100644 --- a/src/PageFinder.php +++ b/src/PageFinder.php @@ -102,7 +102,7 @@ public function findAssociatedForPage(PageModel $page, bool $skipCurrent = false $page->loadDetails(); $t = $page::getTable(); - if ($page->rootIsFallback && null !== ($root = PageModel::findByPk($page->rootId)) && !$root->languageRoot) { + if ($page->rootIsFallback && null !== ($root = PageModel::findById($page->rootId)) && !$root->languageRoot) { $values = [$page->id, $page->id]; } elseif (!$page->languageMain) { return $skipCurrent ? [] : [$page]; diff --git a/tests/LanguageTest.php b/tests/LanguageTest.php index c99f59b..ebc751e 100644 --- a/tests/LanguageTest.php +++ b/tests/LanguageTest.php @@ -35,7 +35,7 @@ public function testInvalidLanguage(string $language): void Language::normalize($language, '-'); } - public function languagesProvider(): \Generator + public static function languagesProvider(): iterable { yield ['en', 'en']; yield ['de', 'de']; @@ -44,7 +44,7 @@ public function languagesProvider(): \Generator yield ['de_CH', 'de-CH']; } - public function invalidLanguagesProvider(): \Generator + public static function invalidLanguagesProvider(): iterable { yield ['']; yield ['-'];