diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a9211ea --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,65 @@ +name: CI +on: [push] + +jobs: + phpstan: + runs-on: ubuntu-latest + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + + - name: Checkout + uses: actions/checkout@v6 + + - name: Install the dependencies + uses: ramsey/composer-install@v3 + with: + composer-options: "--no-plugins" + + - name: PHPStan + run: vendor/bin/phpstan analyse -c phpstan.neon + + rector: + runs-on: ubuntu-latest + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + + - name: Checkout + uses: actions/checkout@v6 + + - name: Install the dependencies + uses: ramsey/composer-install@v3 + with: + composer-options: "--no-plugins" + + - name: Rector + run: vendor/bin/rector --dry-run --no-progress-bar + + ecs: + name: ECS + runs-on: ubuntu-latest + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + extensions: dom, fileinfo, filter, gd, hash, intl, json, mbstring, mysqli, pcre, pdo_mysql, zlib + coverage: none + + - name: Checkout + uses: actions/checkout@v6 + with: + show-progress: false + + - name: Install the dependencies + uses: ramsey/composer-install@v3 + with: + composer-options: "--no-plugins" + + - name: Run ECS + run: vendor/bin/ecs check --no-progress-bar diff --git a/composer.json b/composer.json index 0d15999..d2545b4 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,16 @@ "contao/core-bundle": "^5.3", "symfony/http-kernel": "^6.4 || ^7.0" }, + "require-dev": { + "contao/core-bundle": "5.3.*", + "rector/rector": "^2.5", + "contao/contao-rector": "dev-main", + "symplify/easy-coding-standard": "^13.2", + "phpstan/phpstan": "^2.2", + "phpstan/phpstan-symfony": "^2.0", + "contao/manager-plugin": "^2.13", + "terminal42/contao-leads": "^3.2" + }, "autoload": { "psr-4": { "HeimrichHannot\\FormFieldsCollectionBundle\\": "src/" diff --git a/contao/dca/tl_form_field.php b/contao/dca/tl_form_field.php index 35b0b79..9c76bd8 100644 --- a/contao/dca/tl_form_field.php +++ b/contao/dca/tl_form_field.php @@ -5,14 +5,20 @@ $dca = &$GLOBALS['TL_DCA']['tl_form_field']; -$dca['palettes'][DateTimeWidget::TYPE] = '{type_legend},type,name,label;{fconfig_legend},mandatory,dateTimeType,defaultDateTimeValue;{expert_legend:hide},class,minval,maxval,accesskey,tabindex;{template_legend:hide},customTpl;{invisible_legend:hide},invisible'; +$dca['palettes'][DateTimeWidget::TYPE] = << &$GLOBALS['TL_LANG']['tl_form_field'], 'default' => 'date', - 'eval' => ['tl_class' => 'w50'], + 'eval' => [ + 'tl_class' => 'w50', + ], 'sql' => "varchar(4) NOT NULL default ''", ]; @@ -45,4 +53,3 @@ ], 'sql' => "varchar(8) NOT NULL default ''", ]; - diff --git a/contao/languages/de/default.php b/contao/languages/de/default.php index 990adfb..a9d0286 100644 --- a/contao/languages/de/default.php +++ b/contao/languages/de/default.php @@ -3,11 +3,11 @@ use HeimrichHannot\FormFieldsCollectionBundle\FrontendWidget\DateTimeWidget; use HeimrichHannot\FormFieldsCollectionBundle\FrontendWidget\SingleCheckboxWidget; -$GLOBALS['TL_LANG']['FFL'][DateTimeWidget::TYPE] = [ +$GLOBALS['TL_LANG']['FFL'][DateTimeWidget::TYPE] = [ 'Datum-/ Zeitfeld (Form Fields Collection)', - 'Ermöglicht die Eingabe von Datum und/ oder Zeit.' + 'Ermöglicht die Eingabe von Datum und/ oder Zeit.', ]; -$GLOBALS['TL_LANG']['FFL'][SingleCheckboxWidget::TYPE] = [ +$GLOBALS['TL_LANG']['FFL'][SingleCheckboxWidget::TYPE] = [ 'Checkbox Einzeln (Form Fields Collection)', - 'Eine einzelne Checkbox, die entweder aktiviert oder deaktiviert sein kann.' -]; \ No newline at end of file + 'Eine einzelne Checkbox, die entweder aktiviert oder deaktiviert sein kann.', +]; diff --git a/contao/languages/de/tl_form_field.php b/contao/languages/de/tl_form_field.php index 46290e0..34caa66 100644 --- a/contao/languages/de/tl_form_field.php +++ b/contao/languages/de/tl_form_field.php @@ -11,4 +11,4 @@ $lang['defaultDateTimeCustomValue'][0] = 'Benutzerdefinierte Zeit'; $lang['defaultDateTimeCustomValue'][1] = 'Bitte geben Sie die benutzerdefinierte Zeit ein.'; $lang['singleCheckboxValue'][0] = 'Checkbox Wert'; -$lang['singleCheckboxValue'][1] = 'Legen Sie den Checkbox-Wert fest. Wenn leer, wird der Wert 1 sein.'; \ No newline at end of file +$lang['singleCheckboxValue'][1] = 'Legen Sie den Checkbox-Wert fest. Wenn leer, wird der Wert 1 sein.'; diff --git a/contao/languages/en/default.php b/contao/languages/en/default.php index 3803533..94e79ca 100644 --- a/contao/languages/en/default.php +++ b/contao/languages/en/default.php @@ -3,11 +3,11 @@ use HeimrichHannot\FormFieldsCollectionBundle\FrontendWidget\DateTimeWidget; use HeimrichHannot\FormFieldsCollectionBundle\FrontendWidget\SingleCheckboxWidget; -$GLOBALS['TL_LANG']['FFL'][DateTimeWidget::TYPE] = [ +$GLOBALS['TL_LANG']['FFL'][DateTimeWidget::TYPE] = [ 'Date/ time (Form Fields Collection)', - 'A date or time field.' + 'A date or time field.', ]; -$GLOBALS['TL_LANG']['FFL'][SingleCheckboxWidget::TYPE] = [ +$GLOBALS['TL_LANG']['FFL'][SingleCheckboxWidget::TYPE] = [ 'Checkbox Single (Form Fields Collection)', - 'A single checkbox that can be either checked or unchecked.' -]; \ No newline at end of file + 'A single checkbox that can be either checked or unchecked.', +]; diff --git a/contao/languages/en/tl_form_field.php b/contao/languages/en/tl_form_field.php index 6569a24..194bed3 100644 --- a/contao/languages/en/tl_form_field.php +++ b/contao/languages/en/tl_form_field.php @@ -11,4 +11,4 @@ $lang['defaultDateTimeCustomValue'][0] = 'Custom date/ time'; $lang['defaultDateTimeCustomValue'][1] = 'Please enter a custom date/ time.'; $lang['singleCheckboxValue'][0] = 'Checkbox Value'; -$lang['singleCheckboxValue'][1] = 'Set the checkbox value. If empty, the value will be 1.'; \ No newline at end of file +$lang['singleCheckboxValue'][1] = 'Set the checkbox value. If empty, the value will be 1.'; diff --git a/ecs.php b/ecs.php new file mode 100644 index 0000000..4745de0 --- /dev/null +++ b/ecs.php @@ -0,0 +1,39 @@ +withPaths([ + __DIR__ . '/src', + __DIR__ . '/contao', + + ]) + + // add a single rule + ->withRules([ + NoUnusedImportsFixer::class, + BracesPositionFixer::class, + ]) + + // add sets - group of rules + ->withPreparedSets( + arrays: true, + comments: true, + docblocks: true, + spaces: true, + namespaces: true, + ) + ->withPhpCsFixerSets(symfony: true) + ->withSkip([ + NotOperatorWithSuccessorSpaceFixer::class, + MethodChainingIndentationFixer::class => [ + '*/DependencyInjection/Configuration.php', + 'src/*Bundle.php' + ], + ]); \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..182927a --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,11 @@ +parameters: + level: 4 + paths: + - src + - contao +# universalObjectCratesClasses: +# - Contao\Database\Result +# - Contao\Model +# - Contao\Template +includes: + - vendor/phpstan/phpstan-symfony/extension.neon \ No newline at end of file diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..d7561e4 --- /dev/null +++ b/rector.php @@ -0,0 +1,46 @@ +withPaths([ + __DIR__ . '/src', + __DIR__ . '/contao', + + ]) + ->withPhpVersion(PhpVersion::PHP_84) + ->withRules([ + AddVoidReturnTypeWhereNoReturnRector::class, + # In Vorbereitung für PHP 8.4: + ExplicitNullableParamTypeRector::class, + ]) + + ->withImportNames( + importShortClasses: false, + removeUnusedImports: true + ) + ->withComposerBased( + twig: true, + doctrine: true, + phpunit: true, + symfony: true, + ) + ->withSets([ + LevelSetList::UP_TO_PHP_82, + ContaoLevelSetList::UP_TO_CONTAO_53, + ContaoSetList::FQCN, + ContaoSetList::ANNOTATIONS_TO_ATTRIBUTES, + ]) + ->withSkip([ + ArrayToFirstClassCallableRector::class, + ]) + ; \ No newline at end of file diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index 77e800d..c0a31c8 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -24,4 +24,4 @@ public function registerContainerConfiguration(LoaderInterface $loader, array $m { $loader->load('@HeimrichHannotFormFieldsCollectionBundle/config/services.yaml'); } -} \ No newline at end of file +} diff --git a/src/EventListener/DateTimeListener.php b/src/EventListener/DateTimeListener.php index 18f2dca..edb99dc 100644 --- a/src/EventListener/DateTimeListener.php +++ b/src/EventListener/DateTimeListener.php @@ -12,12 +12,13 @@ class DateTimeListener #[AsCallback(table: 'tl_form_field', target: 'config.onload')] public function onLoadCallback(DataContainer $dc): void { - if (!$dc || !$dc->id) { + if (!$dc->id) { return; } + /** @var \HeimrichHannot\FormFieldsCollectionBundle\Model\FormFieldModel|null $widget */ $widget = FormFieldModel::findByPk($dc->id); - if (!$widget || (DateTimeWidget::TYPE !== $widget->type)) { + if (DateTimeWidget::TYPE !== $widget?->type) { return; } @@ -34,6 +35,6 @@ public function onLoadCallback(DataContainer $dc): void $GLOBALS['TL_DCA']['tl_form_field']['fields']['maxval']['eval']['rgxp'] = 'time'; $GLOBALS['TL_DCA']['tl_form_field']['fields']['value']['label'] = &$GLOBALS['TL_LANG']['tl_form_field']['defaultDateTimeCustomValue']; break; - }; + } } -} \ No newline at end of file +} diff --git a/src/EventListener/Integrations/LeadsListener.php b/src/EventListener/Integrations/LeadsListener.php index 6823af1..d88a6d9 100644 --- a/src/EventListener/Integrations/LeadsListener.php +++ b/src/EventListener/Integrations/LeadsListener.php @@ -20,17 +20,19 @@ public function onFormDataValueEvent(FormDataValueEvent $event): void $format = match ($event->getField()['dateTimeType']) { 'date' => Date::getNumericDateFormat(), 'time' => Date::getNumericTimeFormat(), - 'default' => null, + default => null, }; if (null === $format) { $event->stopPropagation(); + return; } try { $event->setValue((new Date($event->getValue(), $format))->tstamp); - } catch (\Exception) {} + } catch (\Exception) { + } $event->stopPropagation(); } @@ -45,18 +47,20 @@ public function onFormDataLabelEvent(FormDataLabelEvent $event): void $format = match ($event->getField()['dateTimeType']) { 'date' => Date::getNumericDateFormat(), 'time' => Date::getNumericTimeFormat(), - 'default' => null, + default => null, }; if (null === $format) { $event->stopPropagation(); + return; } try { $event->setLabel(Date::parse($format, $event->getValue())); - } catch (\Exception) {} + } catch (\Exception) { + } $event->stopPropagation(); } -} \ No newline at end of file +} diff --git a/src/EventListener/SingleCheckboxListener.php b/src/EventListener/SingleCheckboxListener.php index ec2eb88..82776b5 100644 --- a/src/EventListener/SingleCheckboxListener.php +++ b/src/EventListener/SingleCheckboxListener.php @@ -12,7 +12,7 @@ class SingleCheckboxListener #[AsCallback(table: 'tl_form_field', target: 'config.onload')] public function onLoadCallback(DataContainer $dc): void { - if (!$dc || !$dc->id) { + if (!$dc->id) { return; } @@ -26,4 +26,4 @@ public function onLoadCallback(DataContainer $dc): void $GLOBALS['TL_DCA']['tl_form_field']['fields']['text']['eval']['mandatory'] = true; $GLOBALS['TL_DCA']['tl_form_field']['fields']['value']['label'] = &$GLOBALS['TL_LANG']['tl_form_field']['singleCheckboxValue']; } -} \ No newline at end of file +} diff --git a/src/FormFieldModel.php b/src/FormFieldModel.php new file mode 100644 index 0000000..8856dd1 --- /dev/null +++ b/src/FormFieldModel.php @@ -0,0 +1,10 @@ +value = match ($this->dateTimeType) { 'date' => Date::parse('Y-m-d', $fieldModel->value), - 'time' => Date::parse("H:i", $fieldModel->value), + 'time' => Date::parse('H:i', $fieldModel->value), + default => '', }; } } elseif ('current' === $this->defaultDateTimeValue) { $this->value = match ($this->dateTimeType) { - "", 'date' => date('Y-m-d'), + '', 'date' => date('Y-m-d'), 'time' => date('H:i'), + default => '', }; } @@ -61,24 +68,28 @@ protected function validator($varInput) return parent::validator(match ($this->dateTimeType) { 'date' => \DateTime::createFromFormat('Y-m-d', $varInput)->format(Date::getNumericDateFormat()), 'time' => \DateTime::createFromFormat('H:i', $varInput)->format(Date::getNumericTimeFormat()), + default => $varInput, }); } - public function parse($arrAttributes=null) + public function parse($arrAttributes = null) { // restore field value on errors if ($this->hasErrors()) { $dateTime = match ($this->dateTimeType) { 'date' => \DateTime::createFromFormat(Date::getNumericDateFormat(), $this->value), 'time' => \DateTime::createFromFormat(Date::getNumericTimeFormat(), $this->value), + default => $this->value, }; if (false !== $dateTime) { $this->value = match ($this->dateTimeType) { 'date' => $dateTime->format('Y-m-d'), 'time' => $dateTime->format('H:i'), + default => $this->value, }; } } + return parent::parse($arrAttributes); } -} \ No newline at end of file +} diff --git a/src/FrontendWidget/SingleCheckboxWidget.php b/src/FrontendWidget/SingleCheckboxWidget.php index c9eec1e..ba0740e 100644 --- a/src/FrontendWidget/SingleCheckboxWidget.php +++ b/src/FrontendWidget/SingleCheckboxWidget.php @@ -6,6 +6,9 @@ use Contao\Input; use Contao\System; +/** + * @property string $text + */ class SingleCheckboxWidget extends FormCheckbox { public const TYPE = 'huh_single_checkbox'; @@ -14,10 +17,10 @@ class SingleCheckboxWidget extends FormCheckbox protected function getOption(): array { - $text = System::getContainer()->get('contao.insert_tag.parser')->replaceInline((string)$this->text); + $text = System::getContainer()->get('contao.insert_tag.parser')->replaceInline((string) $this->text); if ( - preg_match('/^\s*]*>(.*)<\/p>\s*$/is', $text, $matches) === 1 - && preg_match('/<\/?p\b/i', $matches[1]) !== 1 + 1 === preg_match('/^\s*]*>(.*)<\/p>\s*$/is', (string) $text, $matches) + && 1 !== preg_match('/<\/?p\b/i', $matches[1]) ) { $text = trim($matches[1]); } @@ -29,7 +32,7 @@ protected function getOption(): array 'id' => $this->strId, 'value' => $value, 'attributes' => $this->getAttributes(), - 'label' => $text + 'label' => $text, ]; if ($this->mandatory) { $option['mandatory'] = true; @@ -38,25 +41,26 @@ protected function getOption(): array if (!Input::isPost()) { $option['checked'] = false; } else { - $option['checked'] = (string)$this->varValue === (string)$value; + $option['checked'] = (string) $this->varValue === (string) $value; } return $option; } - public function getAttributes($arrStrip = array()) + public function getAttributes($arrStrip = []) { if ($this->mandatory) { $this->arrAttributes['required'] = 'required'; } + return parent::getAttributes($arrStrip); } - protected function isValidOption($varInput): bool { // set arrOptions to make parent validation work $this->arrOptions = [$this->getOption()]; + return parent::isValidOption($varInput); } -} \ No newline at end of file +} diff --git a/src/HeimrichHannotFormFieldsCollectionBundle.php b/src/HeimrichHannotFormFieldsCollectionBundle.php index f86f57a..9b00af8 100644 --- a/src/HeimrichHannotFormFieldsCollectionBundle.php +++ b/src/HeimrichHannotFormFieldsCollectionBundle.php @@ -10,5 +10,4 @@ public function getPath(): string { return \dirname(__DIR__); } - -} \ No newline at end of file +}