Skip to content

Commit

Permalink
FormTypeGuesser by PropertyInfo (#40)
Browse files Browse the repository at this point in the history
* FormTypeGuesser by PropertyInfo

* ~ fix coding standard composer.json

* ~ test wrong type

* Update composer.json

bump minimum property-info version

* ~ fix wrong type test

* dependency-analysis only with higher php versions

* * use phpDocumentor as backup

* ~ fix guesser

* + fix tests

* ~ fix dependency analysis

* ~ cleanup

* ~ fixup static analysis
  • Loading branch information
Hanmac authored Apr 22, 2024
1 parent 94ba8c5 commit 58d9b3d
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 23 deletions.
6 changes: 0 additions & 6 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.0"
- "8.1"
- "8.2"

Expand All @@ -66,10 +64,6 @@ jobs:
- "^5.4"
- "^6.0"

exclude:
- php-version: "7.4"
symfony: "^6.0"

steps:
- name: "Checkout"
uses: "actions/checkout@v3"
Expand Down
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
],
"require": {
"php": ">=7.4",
"brick/reflection": "^0.4",
"doctrine/dbal": "^3.4",
"doctrine/doctrine-bundle": "^1.9 || ^2.0",
"dragonmantank/cron-expression": "^2.2 || ^3.0",
Expand All @@ -20,11 +19,13 @@
"symfony/form": "^4.4 || ^5.4 || ^6.0",
"symfony/http-kernel": "^4.4 || ^5.4 || ^6.0",
"symfony/options-resolver": "^4.4 || ^5.4 || ^6.0",
"symfony/property-info": "^4.4.49 || ^5.4 || ^6.0",
"symfony/validator": "^4.4 || ^5.4 || ^6.0",
"webmozart/assert": "^1.10"
},
"require-dev": {
"matthiasnoback/symfony-dependency-injection-test": "^4.3",
"phpdocumentor/reflection-docblock": "^5.3",
"phpunit/phpunit": "^9.5",
"psalm/plugin-phpunit": "^0.16.1",
"roave/security-advisories": "dev-latest",
Expand All @@ -43,8 +44,8 @@
},
"config": {
"allow-plugins": {
"ergebnis/composer-normalize": true,
"dealerdirect/phpcodesniffer-composer-installer": false
"dealerdirect/phpcodesniffer-composer-installer": false,
"ergebnis/composer-normalize": true
},
"sort-packages": true
},
Expand Down
41 changes: 27 additions & 14 deletions src/Form/TypeGuesser/CronExpressionTypeGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@

namespace Setono\CronExpressionBundle\Form\TypeGuesser;

use Brick\Reflection\ReflectionTools;
use Cron\CronExpression;
use ReflectionClass;
use ReflectionException;
use Setono\CronExpressionBundle\Form\Type\CronExpressionType;
use Symfony\Component\Form\FormTypeGuesserInterface;
use Symfony\Component\Form\Guess\Guess;
use Symfony\Component\Form\Guess\TypeGuess;
use Symfony\Component\Form\Guess\ValueGuess;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type;

final class CronExpressionTypeGuesser implements FormTypeGuesserInterface
{
private PropertyTypeExtractorInterface $extractor;

public function __construct(?PropertyTypeExtractorInterface $extractor = null)
{
$this->extractor = $extractor ?? $this->createExtractor();
}
/**
* @param string $class
* @param string $property
Expand All @@ -26,20 +34,17 @@ public function guessType($class, $property): ?TypeGuess
return null;
}

try {
$reflectionClass = new ReflectionClass($class);
$reflectionProperty = $reflectionClass->getProperty($property);
} catch (ReflectionException $e) {

$types = $this->extractor->getTypes($class, $property);
if (!$types) {
return null;
}

$reflectionTools = new ReflectionTools();
$propertyTypes = $reflectionTools->getPropertyTypes($reflectionProperty);

if (in_array(CronExpression::class, $propertyTypes, true)) {
return new TypeGuess(CronExpressionType::class, [], Guess::VERY_HIGH_CONFIDENCE);
foreach ($types as $type) {
if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType() &&
CronExpression::class === $type->getClassName()) {
return new TypeGuess(CronExpressionType::class, [], Guess::VERY_HIGH_CONFIDENCE);
}
}

return null;
}

Expand Down Expand Up @@ -69,4 +74,12 @@ public function guessPattern($class, $property): ?ValueGuess
{
return null;
}

private function createExtractor(): PropertyTypeExtractorInterface
{
return new PropertyInfoExtractor([], [
new PhpDocExtractor(),
new ReflectionExtractor(),
]);
}
}
1 change: 1 addition & 0 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<service id="setono_cron_expression.form.type_guesser.cron_expression"
class="Setono\CronExpressionBundle\Form\TypeGuesser\CronExpressionTypeGuesser">
<tag name="form.type_guesser"/>
<argument type="service" id="property_info"/>
</service>
</services>
</container>
8 changes: 8 additions & 0 deletions tests/Form/TypeGuesser/CronExpressionTypeGuesserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ public function it_returns_null_if_property_doesnt_exist(): void
$this->assertNull($this->typeGuesser->guessType(StubWithNoPhpDoc::class, 'property2'));
}

/**
* @test
*/
public function it_returns_null_if_property_has_wrong_type(): void
{
$this->assertNull($this->typeGuesser->guessType(StubWithWrongType::class, 'property'));
}

/**
* @test
*/
Expand Down
11 changes: 11 additions & 0 deletions tests/Form/TypeGuesser/StubWithWrongType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Setono\CronExpressionBundle\Tests\Form\TypeGuesser;

final class StubWithWrongType
{
/** @var \DateTime */
private $property;
}

0 comments on commit 58d9b3d

Please sign in to comment.