Skip to content

Commit

Permalink
Added attribute for marking forms and fieldsets as having customised …
Browse files Browse the repository at this point in the history
…type
  • Loading branch information
matt committed Feb 24, 2024
1 parent 6e377d9 commit 048c90a
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 9 deletions.
1 change: 1 addition & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<issueHandlers>
<MissingTemplateParam>
<errorLevel type="suppress">
<directory name="test/Form/Asset"/>
<directory name="test/Locator/Asset"/>
</errorLevel>
</MissingTemplateParam>
Expand Down
21 changes: 21 additions & 0 deletions src/Attribute/PsalmTypeCustomised.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Kynx\Laminas\FormShape\Attribute;

use Attribute;

/**
* Marks a form or fieldset as having a custom type
*
* The class will be loaded and any child fieldsets will be processed, but changes will not be written to disk. Provide
* the `@psalm-type` so it can be imported by other forms and fieldsets.
*/
#[Attribute(Attribute::TARGET_CLASS)]
final readonly class PsalmTypeCustomised
{
public function __construct(public string $psalmType)
{
}
}
6 changes: 6 additions & 0 deletions src/Attribute/PsalmTypeIgnore.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

use Attribute;

/**
* Marks a form or fieldset as ignored
*
* Ignored elements are not loaded from the `FormElementManager` and no child fieldsets will be processed. You typically
* only want to use this if the form cannot actually be loaded in a normal way.
*/
#[Attribute(Attribute::TARGET_CLASS)]
final class PsalmTypeIgnore
{
Expand Down
50 changes: 41 additions & 9 deletions src/Form/FormProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Kynx\Laminas\FormShape\Form;

use Kynx\Laminas\FormShape\Attribute\PsalmTypeCustomised;
use Kynx\Laminas\FormShape\Attribute\PsalmTypeIgnore;
use Kynx\Laminas\FormShape\ExceptionInterface;
use Kynx\Laminas\FormShape\InputFilter\ImportType;
use Kynx\Laminas\FormShape\Locator\FormFile;
Expand All @@ -17,7 +19,6 @@
use ReflectionClass;

use function array_merge;
use function count;
use function sprintf;

/**
Expand Down Expand Up @@ -55,8 +56,8 @@ public function process(
$types = $this->processFieldsets($formFiles, $listener);
}

$this->processForms($formFiles, $types, $listener, $removeGetDataReturn);
$listener->finally(count($formFiles));
$processed = $this->processForms($formFiles, $types, $listener, $removeGetDataReturn);
$listener->finally($processed);
}

/**
Expand Down Expand Up @@ -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;
}

/**
Expand All @@ -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) {
Expand All @@ -112,8 +119,10 @@ private function processForm(
$formFile->reflection->getName(),
$e->getMessage()
));
return;
return 0;
}

return 1;
}

/**
Expand All @@ -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",
Expand Down Expand Up @@ -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;
}
}
13 changes: 13 additions & 0 deletions test/Form/Asset/CustomFieldset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace KynxTest\Laminas\FormShape\Form\Asset;

use Kynx\Laminas\FormShape\Attribute\PsalmTypeCustomised;
use Laminas\Form\Fieldset;

#[PsalmTypeCustomised('TCustomType')]
final class CustomFieldset extends Fieldset
{
}
13 changes: 13 additions & 0 deletions test/Form/Asset/CustomForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace KynxTest\Laminas\FormShape\Form\Asset;

use Kynx\Laminas\FormShape\Attribute\PsalmTypeCustomised;
use Laminas\Form\Form;

#[PsalmTypeCustomised('TFormType')]
final class CustomForm extends Form
{
}
13 changes: 13 additions & 0 deletions test/Form/Asset/IgnoredFieldset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace KynxTest\Laminas\FormShape\Form\Asset;

use Kynx\Laminas\FormShape\Attribute\PsalmTypeIgnore;
use Laminas\Form\Fieldset;

#[PsalmTypeIgnore]
final class IgnoredFieldset extends Fieldset
{
}
53 changes: 53 additions & 0 deletions test/Form/FormProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
use Kynx\Laminas\FormShape\Locator\FormFile;
use Kynx\Laminas\FormShape\Locator\FormLocatorInterface;
use KynxTest\Laminas\FormShape\Form\Asset\ChildFieldset;
use KynxTest\Laminas\FormShape\Form\Asset\CustomFieldset;
use KynxTest\Laminas\FormShape\Form\Asset\CustomForm;
use KynxTest\Laminas\FormShape\Form\Asset\IgnoredFieldset;
use KynxTest\Laminas\FormShape\Form\Asset\TestFieldset;
use KynxTest\Laminas\FormShape\Writer\MockWriter;
use Laminas\Form\Element\Collection;
Expand Down Expand Up @@ -286,4 +289,54 @@ public function testProcessDoesNotRemoveGetDataReturn(): void
$written = $this->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']);
}
}

0 comments on commit 048c90a

Please sign in to comment.