From 048c90a4d1a8f0f9b7c0e5d01554732444cbe2a5 Mon Sep 17 00:00:00 2001 From: matt Date: Sat, 24 Feb 2024 16:33:47 +0000 Subject: [PATCH] Added attribute for marking forms and fieldsets as having customised type --- psalm.xml | 1 + src/Attribute/PsalmTypeCustomised.php | 21 +++++++++++ src/Attribute/PsalmTypeIgnore.php | 6 +++ src/Form/FormProcessor.php | 50 ++++++++++++++++++++----- test/Form/Asset/CustomFieldset.php | 13 +++++++ test/Form/Asset/CustomForm.php | 13 +++++++ test/Form/Asset/IgnoredFieldset.php | 13 +++++++ test/Form/FormProcessorTest.php | 53 +++++++++++++++++++++++++++ 8 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 src/Attribute/PsalmTypeCustomised.php create mode 100644 test/Form/Asset/CustomFieldset.php create mode 100644 test/Form/Asset/CustomForm.php create mode 100644 test/Form/Asset/IgnoredFieldset.php diff --git a/psalm.xml b/psalm.xml index b54f1a2..0995cdb 100644 --- a/psalm.xml +++ b/psalm.xml @@ -19,6 +19,7 @@ + diff --git a/src/Attribute/PsalmTypeCustomised.php b/src/Attribute/PsalmTypeCustomised.php new file mode 100644 index 0000000..fac53c0 --- /dev/null +++ b/src/Attribute/PsalmTypeCustomised.php @@ -0,0 +1,21 @@ +processFieldsets($formFiles, $listener); } - $this->processForms($formFiles, $types, $listener, $removeGetDataReturn); - $listener->finally(count($formFiles)); + $processed = $this->processForms($formFiles, $types, $listener, $removeGetDataReturn); + $listener->finally($processed); } /** @@ -87,10 +88,12 @@ private function processForms( array $types, ProgressListenerInterface $listener, bool $removeGetDataReturn - ): void { + ): int { + $count = 0; foreach ($formFiles as $formFile) { - $this->processForm($formFile, $types, $listener, $removeGetDataReturn); + $count += $this->processForm($formFile, $types, $listener, $removeGetDataReturn); } + return $count; } /** @@ -101,9 +104,13 @@ private function processForm( array $types, ProgressListenerInterface $listener, bool $removeGetDataReturn - ): void { + ): int { try { $union = $this->formVisitor->visit($formFile->form, $types); + if ($this->getCustomType($formFile->reflection) !== null) { + return 0; + } + $this->fileWriter->write($formFile->reflection, $union, $types, $removeGetDataReturn); $listener->success($formFile->reflection); } catch (ExceptionInterface $e) { @@ -112,8 +119,10 @@ private function processForm( $formFile->reflection->getName(), $e->getMessage() )); - return; + return 0; } + + return 1; } /** @@ -126,11 +135,18 @@ private function processFieldset( ProgressListenerInterface $listener ): array { $reflection = new ReflectionClass($fieldset); + if (! $this->canProcess($reflection)) { + return []; + } try { $union = $this->fieldsetVisitor->visit($fieldset, $types); - $typeName = $this->fileWriter->write($reflection, $union, $types); - $listener->success($reflection); + $typeName = $this->getCustomType($reflection); + + if ($typeName === null) { + $typeName = $this->fileWriter->write($reflection, $union, $types); + $listener->success($reflection); + } } catch (ExceptionInterface $e) { $listener->error(sprintf( "Error processing %s: %s", @@ -165,4 +181,20 @@ private function getFieldsets(FieldsetInterface $fieldset): array return $fieldsets; } + + private function canProcess(ReflectionClass $reflection): bool + { + return $reflection->getAttributes(PsalmTypeIgnore::class) === []; + } + + private function getCustomType(ReflectionClass $reflection): ?string + { + $attributes = $reflection->getAttributes(PsalmTypeCustomised::class); + if ($attributes === []) { + return null; + } + + $attribute = $attributes[0]->newInstance(); + return $attribute->psalmType; + } } diff --git a/test/Form/Asset/CustomFieldset.php b/test/Form/Asset/CustomFieldset.php new file mode 100644 index 0000000..05fd77e --- /dev/null +++ b/test/Form/Asset/CustomFieldset.php @@ -0,0 +1,13 @@ +fileWriter->written[0]; self::assertFalse($written['remove-getdata']); } + + public function testProcessDoesNotWriteCustomisedForm(): void + { + $form = new CustomForm(); + $form->add(new Text('bar')); + + $reflection = new ReflectionClass($form); + $this->formLocator->method('locate') + ->willReturn([new FormFile($reflection, $form)]); + + $this->processor->process(['foo'], $this->listener, true, false); + self::assertCount(0, $this->fileWriter->written); + } + + public function testProcessDoesNotProcessIgnoredFieldset(): void + { + $form = new Form(); + $fieldset = new IgnoredFieldset('foo'); + $fieldset->add(new Text('bar')); + $form->add($fieldset); + + $reflection = new ReflectionClass($form); + $this->formLocator->method('locate') + ->willReturn([new FormFile($reflection, $form)]); + + $this->processor->process(['foo'], $this->listener); + self::assertCount(1, $this->fileWriter->written); + } + + public function testProcessUsesCustomType(): void + { + $expected = new Union([ + new TKeyedArray([ + 'foo' => new Union([new TTypeAlias(CustomFieldset::class, 'TCustomType')]), + ]), + ]); + $form = new Form(); + $fieldset = new CustomFieldset('foo'); + $fieldset->add(new Text('bar')); + $form->add($fieldset); + + $reflection = new ReflectionClass($form); + $this->formLocator->method('locate') + ->willReturn([new FormFile($reflection, $form)]); + + $this->processor->process(['foo'], $this->listener); + self::assertCount(1, $this->fileWriter->written); + $written = $this->fileWriter->written[0]; + self::assertEquals($expected, $written['type']); + } }