Repository: JsonMapper/JsonMapper (php)

Issue:
Issue parsing nullable arrays on Docblocks
First of all, thank you very much for maintaining this and releasing the last version that quick.
There is just another tricky type that doesn't seem to be correctly parsed, nullable arrays.

DocBlocks like this one raise an exception as are not considered nullable arrays, but the whole `string[]` gets taken as the type:
```php
/**
 * string[]|null
 */
$myVAr
```

I've created some [failing tests in this commit](https://github.com/namelivia/JsonMapper/commit/3f9d7d9e7b328b514833ca771ac346a69792816a), so its easier to debug.
Again, thank you for your support!


Pull request description:
Correctly process nullable array docblock annotations.
Would resolve #60 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fba41fa..579cd7f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+## Fixed
+- Fix null array support in DocBlock middleware. [PR#60](https://github.com/JsonMapper/JsonMapper/pull/60)
 
 ## [1.4.1] - 2020-10-27
 ## Fixed
diff --git a/src/Middleware/DocBlockAnnotations.php b/src/Middleware/DocBlockAnnotations.php
index fd70da7..1a021e6 100644
--- a/src/Middleware/DocBlockAnnotations.php
+++ b/src/Middleware/DocBlockAnnotations.php
@@ -80,15 +80,14 @@ class DocBlockAnnotations extends AbstractMiddleware
         }
 
         $type = $annotations->getVar();
+        $nullable = stripos('|' . $type . '|', '|null|') !== false;
+        $cleanedType = str_replace(['null|', '|null'], '', $type);
 
-        $isArray = substr($type, -2) === '[]';
+        $isArray = substr($cleanedType, -2) === '[]';
         if ($isArray) {
-            $type = substr($type, 0, -2);
+            $cleanedType = substr($cleanedType, 0, -2);
         }
 
-        $nullable = stripos($docBlock, '|null') !== false;
-        $cleanedType = str_replace(['null|', '|null'], '', $type);
-
         return new PropertyType($cleanedType, $nullable, $isArray);
     }
 


Test patch:
diff --git a/tests/Integration/JsonMapperTest.php b/tests/Integration/JsonMapperTest.php
index a6ceb65..7e4d537 100644
--- a/tests/Integration/JsonMapperTest.php
+++ b/tests/Integration/JsonMapperTest.php
@@ -330,6 +330,40 @@ class JsonMapperTest extends TestCase
         self::assertSame('two', $object->wrappee->name);
     }
 
+    public function testItCanMapANullableArrayOfScalarValues(): void
+    {
+        // Arrange
+        $mapper = (new JsonMapperFactory())->bestFit();
+        $object = new class {
+            /** @var int[]|null */
+            public $numbers;
+        };
+        $json = (object) ['numbers' => null];
+
+        // Act
+        $mapper->mapObject($json, $object);
+
+        // Assert
+        self::assertNull($object->numbers);
+    }
+
+    public function testItCanMapANullableArrayOfObjects(): void
+    {
+        // Arrange
+        $mapper = (new JsonMapperFactory())->bestFit();
+        $object = new class {
+            /** @var \DateTime[]|null */
+            public $dates;
+        };
+        $json = (object) ['dates' => null];
+
+        // Act
+        $mapper->mapObject($json, $object);
+
+        // Assert
+        self::assertNull($object->dates);
+    }
+
     public function scalarValueDataTypes(): array
     {
         return [
diff --git a/tests/Unit/Middleware/DocBlockAnnotationsTest.php b/tests/Unit/Middleware/DocBlockAnnotationsTest.php
index a7e5348..21d472a 100644
--- a/tests/Unit/Middleware/DocBlockAnnotationsTest.php
+++ b/tests/Unit/Middleware/DocBlockAnnotationsTest.php
@@ -144,5 +144,46 @@ class DocBlockAnnotationsTest extends TestCase
 
         self::assertEquals('NullableNumber', $propertyMap->getProperty('nullableNumber')->getType());
         self::assertTrue($propertyMap->getProperty('nullableNumber')->isNullable());
+        self::assertFalse($propertyMap->getProperty('nullableNumber')->isArray());
+    }
+
+    /**
+     * @covers \JsonMapper\Middleware\DocBlockAnnotations
+     */
+    public function testTypeIsCorrectlyCalculatedForNullableArray(): void
+    {
+        $middleware = new DocBlockAnnotations(new NullCache());
+        $object = new class {
+            /** @var Number[]|null This is a nullable array number*/
+            public $numbers;
+        };
+        $propertyMap = new PropertyMap();
+        $jsonMapper = $this->createMock(JsonMapperInterface::class);
+
+        $middleware->handle(new \stdClass(), new ObjectWrapper($object), $propertyMap, $jsonMapper);
+
+        self::assertEquals('Number', $propertyMap->getProperty('numbers')->getType());
+        self::assertTrue($propertyMap->getProperty('numbers')->isNullable());
+        self::assertTrue($propertyMap->getProperty('numbers')->isArray());
+    }
+
+    /**
+     * @covers \JsonMapper\Middleware\DocBlockAnnotations
+     */
+    public function testTypeIsCorrectlyCalculatedForNullableArrayWhenNullIsProvidedFirst(): void
+    {
+        $middleware = new DocBlockAnnotations(new NullCache());
+        $object = new class {
+            /** @var null|Number[] This is a nullable array number*/
+            public $numbers;
+        };
+        $propertyMap = new PropertyMap();
+        $jsonMapper = $this->createMock(JsonMapperInterface::class);
+
+        $middleware->handle(new \stdClass(), new ObjectWrapper($object), $propertyMap, $jsonMapper);
+
+        self::assertEquals('Number', $propertyMap->getProperty('numbers')->getType());
+        self::assertTrue($propertyMap->getProperty('numbers')->isNullable());
+        self::assertTrue($propertyMap->getProperty('numbers')->isArray());
     }
 }