Skip to content

Commit

Permalink
fix: Risky test case passing null value to DateTime (#1)
Browse files Browse the repository at this point in the history
fix: PHP-CS settings

feat: Helper for camel cases

feat: DataObject make function

feat: DataObject __call to set property values using a camelCase or snake_case method

chore: update readme

feat: add phpcs.xml for phpcs vs code extension
  • Loading branch information
sweptsquash authored Jun 5, 2022
1 parent 6a67108 commit 778df23
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 75 deletions.
40 changes: 16 additions & 24 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php

$finder = Symfony\Component\Finder\Finder::create()
use PhpCsFixer\Config;
use PhpCsFixer\Finder;

$finder = Finder::create()
->in([
__DIR__ . '/src',
__DIR__ . '/tests',
Expand All @@ -9,31 +12,20 @@
->ignoreDotFiles(true)
->ignoreVCS(true);

return (new PhpCsFixer\Config())
return (new Config())
->setRules([
'@PSR12' => true,
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
'not_operator_with_successor_space' => true,
'trailing_comma_in_multiline' => true,
'phpdoc_scalar' => true,
'unary_operator_spaces' => true,
'binary_operator_spaces' => true,
'blank_line_before_statement' => [
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
],
'phpdoc_single_line_var_spacing' => true,
'phpdoc_var_without_name' => true,
'class_attributes_separation' => [
'elements' => [
'method' => 'one',
'@Symfony' => true,
'phpdoc_no_empty_return' => false,
'array_syntax' => ['syntax' => 'short'],
'yoda_style' => false,
'binary_operator_spaces' => [
'operators' => [
'=>' => 'align',
'=' => 'align',
],
],
'method_argument_space' => [
'on_multiline' => 'ensure_fully_multiline',
'keep_multiple_spaces_after_comma' => true,
],
'single_trait_insert_per_statement' => true,
'concat_space' => ['spacing' => 'one'],
'increment_style' => ['style' => 'post'],
'not_operator_with_successor_space' => true,
])
->setFinder($finder);
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ $human = Human::fromArray($data);
Main advantage is autocompletion as well as better readability. Disadvantage is, that you have to extend your data
object. At least the parent. Nested object does not have to extend anything.

### DTO Making

For each of your DTO's properties you can use either a camelCase or snake_case approach to set their values which ever
suites your preference, in the example below we have the propeties `first_name` and `last_name` set on the DTO here.


```php
$person = Human::make()
->firstName('John')
->lastName('Doe')
->kids(3);
```

## Testing

```bash
Expand Down
83 changes: 83 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?xml version="1.0"?>
<ruleset name="Standard">
<description>PHPCS Standards</description>

<rule ref="PSR1" />

<rule ref="PSR2" />

<rule ref="PSR2.Namespaces.NamespaceDeclaration" />

<rule ref="Generic.Files.LineEndings">
<properties>
<property name="eolChar" value="\n" />
</properties>
</rule>

<rule ref="Zend.Files.ClosingTag" />

<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="120" />
<property name="absoluteLineLimit" value="0" />
</properties>
</rule>

<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
<properties>
<property name="ignoreBlankLines" value="true" />
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.StartFile">
<serverity>0</serverity>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EndFile">
<serverity>0</serverity>
</rule>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EmptyLines">
<serverity>0</serverity>
</rule>

<rule ref="Generic.Formatting.DisallowMultipleStatements" />

<rule ref="Generic.WhiteSpace.ScopeIndent">
<properties>
<property name="tabIndent" value="false" />
</properties>
</rule>

<rule ref="Generic.PHP.LowerCaseKeyword" />
<rule ref="Generic.PHP.LowerCaseConstant" />

<rule ref="Squiz.Scope.MethodScope" />
<rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing" />
<rule ref="Squiz.Functions.LowercaseFunctionKeywords" />

<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
<properties>
<property name="equalSpacing" value="1" />
</properties>
</rule>
<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint">
<serverity>0</serverity>
</rule>

<rule ref="PEAR.Functions.ValidDefaultValue" />

<rule ref="Squiz.Functions.MultiLineFunctionDeclaration" />

<rule ref="Generic.Functions.FunctionCallArgumentSpacing" />
<rule ref="PSR2.Methods.FunctionCallSignature.SpaceAfterCloseBracket">
<serverity>0</serverity>
</rule>

<rule ref="Squiz.WhiteSpace.ScopeClosingBrace" />
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration" />
<rule ref="Squiz.ControlStructures.ForLoopDeclaration" />
<rule ref="Squiz.ControlStructures.LowercaseDeclaration" />

<rule ref="Generic.ControlStructures.InlineControlStructure" />

<exclude-pattern>./vendor/*</exclude-pattern>

</ruleset>
26 changes: 25 additions & 1 deletion src/DataObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,34 @@

namespace AntoninMasek\SimpleHydrator;

abstract class DataObject
use ReflectionObject;
use ReflectionProperty;

class DataObject
{
public static function make(...$arguments): self
{
return new static(...$arguments);
}

public static function fromArray(array $data = null): ?static
{
return Hydrator::hydrate(static::class, $data);
}

public function __call($method, $arguments): self
{
$reflectionClass = new ReflectionObject($this);
$properties = $reflectionClass->getProperties(ReflectionProperty::IS_PUBLIC);

foreach ($properties as $property) {
$name = $property->getName();

if (Helper::camel($name) === Helper::camel($method)) {
$property->setValue($this, ...$arguments);
}
}

return $this;
}
}
15 changes: 15 additions & 0 deletions src/Helper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace AntoninMasek\SimpleHydrator;

final class Helper
{
public static function camel($value): ?string
{
$words = explode(' ', str_replace(['-', '_'], ' ', $value));

$words = implode(array_map(fn ($word) => ucfirst($word), $words));

return lcfirst($words);
}
}
6 changes: 3 additions & 3 deletions src/Hydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static function hydrate(string $className, array $data = null): ?object
return null;
}

$reflectionClass = new ReflectionObject($dto = new $className());
$reflectionClass = new ReflectionObject($dto = new $className());
$publicProperties = $reflectionClass->getProperties(ReflectionProperty::IS_PUBLIC);

foreach ($publicProperties as $property) {
Expand All @@ -24,8 +24,8 @@ public static function hydrate(string $className, array $data = null): ?object

if (! $property->getType()->isBuiltin()) {
$value = match ($property->getType()->getName()) {
DateTime::class => new DateTime($value),
default => self::hydrate($property->getType()->getName(), $value),
DateTime::class => $value ? new DateTime($value) : null,
default => self::hydrate($property->getType()->getName(), $value),
};
}

Expand Down
4 changes: 3 additions & 1 deletion tests/Models/Human.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
class Human extends DataObject
{
public string $name;
public ?string $first_name;
public ?string $last_name;
public int $kids;
public DateTime $dateOfBirth;
public ?DateTime $dateOfBirth;
public float $money;
public bool $male;
public array $items;
Expand Down
107 changes: 61 additions & 46 deletions tests/SimpleHydratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use AntoninMasek\SimpleHydrator\Tests\Models\Human;
use DateTime;
use PHPUnit\Framework\TestCase;
use TypeError;

class SimpleHydratorTest extends TestCase
{
Expand All @@ -17,21 +18,21 @@ protected function setUp(): void
parent::setUp();

$this->data = [
'name' => 'John',
'kids' => 0,
'name' => 'John',
'kids' => 0,
'dateOfBirth' => '1969-07-20',
'money' => 33.3,
'male' => true,
'items' => ['phone', 'wallet', 'keys'],
'car' => null,
'mother' => [
'name' => 'Jane',
'kids' => 2,
'money' => 33.3,
'male' => true,
'items' => ['phone', 'wallet', 'keys'],
'car' => null,
'mother' => [
'name' => 'Jane',
'kids' => 2,
'money' => 66.6,
'male' => false,
'male' => false,
'items' => ['phone', 'keys'],
'car' => [
'type' => '911',
'car' => [
'type' => '911',
'brand' => 'Porsche',
],
],
Expand All @@ -40,22 +41,22 @@ protected function setUp(): void

public function testItCanHydrateObjectUsingHydrator()
{
$tony = Hydrator::hydrate(Human::class, $this->data);

$this->assertSame('John', $tony->name);
$this->assertSame(0, $tony->kids);
$this->assertTrue($tony->dateOfBirth instanceof DateTime);
$this->assertSame(33.3, $tony->money);
$this->assertSame(true, $tony->male);
$this->assertCount(3, $tony->items);
$this->assertCount(3, $tony->items);
$this->assertSame('phone', $tony->items[0]);
$this->assertSame('wallet', $tony->items[1]);
$this->assertSame('keys', $tony->items[2]);
$this->assertSame(null, $tony->car);
$this->assertTrue($tony->mother instanceof Human);

$mother = $tony->mother;
$person = Hydrator::hydrate(Human::class, $this->data);

$this->assertSame('John', $person->name);
$this->assertSame(0, $person->kids);
$this->assertTrue($person->dateOfBirth instanceof DateTime);
$this->assertSame(33.3, $person->money);
$this->assertSame(true, $person->male);
$this->assertCount(3, $person->items);
$this->assertCount(3, $person->items);
$this->assertSame('phone', $person->items[0]);
$this->assertSame('wallet', $person->items[1]);
$this->assertSame('keys', $person->items[2]);
$this->assertSame(null, $person->car);
$this->assertTrue($person->mother instanceof Human);

$mother = $person->mother;
$this->assertSame('Jane', $mother->name);
$this->assertSame(2, $mother->kids);
$this->assertSame(66.6, $mother->money);
Expand All @@ -77,20 +78,20 @@ public function testItCanHydrateObjectUsingHydrator()

public function testObjectCanHydrateItselfWhenExtendingHydrator()
{
$tony = Human::fromArray($this->data);

$this->assertSame('John', $tony->name);
$this->assertSame(0, $tony->kids);
$this->assertSame(33.3, $tony->money);
$this->assertSame(true, $tony->male);
$this->assertCount(3, $tony->items);
$this->assertSame('phone', $tony->items[0]);
$this->assertSame('wallet', $tony->items[1]);
$this->assertSame('keys', $tony->items[2]);
$this->assertSame(null, $tony->car);
$this->assertTrue($tony->mother instanceof Human);

$mother = $tony->mother;
$person = Human::fromArray($this->data);

$this->assertSame('John', $person->name);
$this->assertSame(0, $person->kids);
$this->assertSame(33.3, $person->money);
$this->assertSame(true, $person->male);
$this->assertCount(3, $person->items);
$this->assertSame('phone', $person->items[0]);
$this->assertSame('wallet', $person->items[1]);
$this->assertSame('keys', $person->items[2]);
$this->assertSame(null, $person->car);
$this->assertTrue($person->mother instanceof Human);

$mother = $person->mother;
$this->assertSame('Jane', $mother->name);
$this->assertSame(2, $mother->kids);
$this->assertSame(66.6, $mother->money);
Expand All @@ -108,15 +109,29 @@ public function testObjectCanHydrateItselfWhenExtendingHydrator()

public function testItReturnsNullWhenNullIsSupplied()
{
$tony = Hydrator::hydrate(Human::class, null);
$person = Hydrator::hydrate(Human::class, null);

$this->assertNull($tony);
$this->assertNull($person);
}

public function testItReturnsNullWhenEmptyArrayIsSupplied()
{
$tony = Hydrator::hydrate(Human::class, []);
$person = Hydrator::hydrate(Human::class, []);

$this->assertNull($tony);
$this->assertNull($person);
}

public function testObjectCanSetValues()
{
$person = Human::fromArray($this->data)->firstName('John');

$this->assertSame('John', $person->first_name);
}

public function testObjectTypeError()
{
$this->expectException(TypeError::class);

$person = Human::make()->dateOfBirth('test');
}
}

0 comments on commit 778df23

Please sign in to comment.