Skip to content

Commit

Permalink
Improve exception (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
vjik committed Sep 28, 2023
1 parent f7d854d commit d662a23
Show file tree
Hide file tree
Showing 34 changed files with 274 additions and 77 deletions.
2 changes: 1 addition & 1 deletion src/Attribute/Data/Map.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Attribute;
use Yiisoft\Hydrator\Data;
use Yiisoft\Hydrator\HydratorInterface;
use Yiisoft\Hydrator\AttributeHandling\UnexpectedAttributeException;
use Yiisoft\Hydrator\AttributeHandling\Exception\UnexpectedAttributeException;

/**
* Override mapping of object property names to keys in the data array in hydrator.
Expand Down
2 changes: 1 addition & 1 deletion src/Attribute/Parameter/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Attribute;
use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext;
use Yiisoft\Hydrator\Result;
use Yiisoft\Hydrator\AttributeHandling\UnexpectedAttributeException;
use Yiisoft\Hydrator\AttributeHandling\Exception\UnexpectedAttributeException;

/**
* Resolve value from the data array used for object hydration by key specified.
Expand Down
2 changes: 1 addition & 1 deletion src/Attribute/Parameter/DiResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use ReflectionUnionType;
use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext;
use Yiisoft\Hydrator\Result;
use Yiisoft\Hydrator\AttributeHandling\UnexpectedAttributeException;
use Yiisoft\Hydrator\AttributeHandling\Exception\UnexpectedAttributeException;

/**
* Resolver for {@see Di} attribute. Obtains dependency from container by ID specified or autoresolved ID by PHP type.
Expand Down
2 changes: 1 addition & 1 deletion src/Attribute/Parameter/ToString.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Stringable;
use Yiisoft\Hydrator\AttributeHandling\ParameterAttributeResolveContext;
use Yiisoft\Hydrator\Result;
use Yiisoft\Hydrator\AttributeHandling\UnexpectedAttributeException;
use Yiisoft\Hydrator\AttributeHandling\Exception\UnexpectedAttributeException;

/**
* Converts the resolved value to string. Non-resolved values is skip.
Expand Down
3 changes: 0 additions & 3 deletions src/AttributeHandling/DataAttributesHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use Yiisoft\Hydrator\Attribute\Data\DataAttributeInterface;
use Yiisoft\Hydrator\Attribute\Data\DataAttributeResolverInterface;
use Yiisoft\Hydrator\Data;
use Yiisoft\Hydrator\NonInstantiableException;

/**
* Handles data attributes that implement {@see DataAttributeInterface}.
Expand All @@ -31,8 +30,6 @@ public function __construct(
* @param ReflectionClass $reflectionClass Reflection of class to attributes handle.
* @param Data $data Current {@see Data} object.
*
* @throws NonInstantiableException
*
* @psalm-param ReflectionAttribute<DataAttributeInterface>[] $reflectionAttributes
*/
public function handle(ReflectionClass $reflectionClass, Data $data): void
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Hydrator\AttributeHandling\Exception;

use LogicException;

final class AttributeResolverNonInstantiableException extends LogicException
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Yiisoft\Hydrator\AttributeHandling;
namespace Yiisoft\Hydrator\AttributeHandling\Exception;

use InvalidArgumentException;
use Throwable;
Expand Down
2 changes: 0 additions & 2 deletions src/AttributeHandling/ParameterAttributesHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Yiisoft\Hydrator\Attribute\Parameter\ParameterAttributeInterface;
use Yiisoft\Hydrator\Attribute\Parameter\ParameterAttributeResolverInterface;
use Yiisoft\Hydrator\Data;
use Yiisoft\Hydrator\NonInstantiableException;
use Yiisoft\Hydrator\Result;

/**
Expand All @@ -33,7 +32,6 @@ public function __construct(
* @param Result|null $resolveResult The resolved value object to pass to attribute resolver via {@see ParameterAttributeResolveContext}.
* @param Data|null $data Raw data and map to pass to attribute resolver via {@see ParameterAttributeResolveContext}.
*
*@throws NonInstantiableException
* @return Result The resolved from attributes value object.
*/
public function handle(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

use Yiisoft\Hydrator\Attribute\Data\DataAttributeInterface;
use Yiisoft\Hydrator\Attribute\Parameter\ParameterAttributeInterface;
use Yiisoft\Hydrator\NonInstantiableException;
use Yiisoft\Hydrator\AttributeHandling\Exception\AttributeResolverNonInstantiableException;

interface AttributeResolverFactoryInterface
{
/**
* @throws NonInstantiableException
* @throws AttributeResolverNonInstantiableException
*/
public function create(DataAttributeInterface|ParameterAttributeInterface $attribute): object;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

namespace Yiisoft\Hydrator\AttributeHandling\ResolverFactory;

use LogicException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use RuntimeException;
use Yiisoft\Hydrator\Attribute\Data\DataAttributeInterface;
use Yiisoft\Hydrator\NonInstantiableException;
use Yiisoft\Hydrator\AttributeHandling\Exception\AttributeResolverNonInstantiableException;
use Yiisoft\Hydrator\Attribute\Parameter\ParameterAttributeInterface;

use function is_object;
Expand All @@ -26,7 +26,6 @@ public function __construct(

/**
* @throws ContainerExceptionInterface
* @throws NonInstantiableException
*/
public function create(DataAttributeInterface|ParameterAttributeInterface $attribute): object
{
Expand All @@ -36,17 +35,18 @@ public function create(DataAttributeInterface|ParameterAttributeInterface $attri
}

if (!$this->container->has($resolver)) {
throw new NonInstantiableException(
throw new AttributeResolverNonInstantiableException(
sprintf(
'Class "%s" does not exist.',
$resolver,
),
);
}

$result = $this->container->get($resolver);

if (!is_object($result)) {
throw new RuntimeException(
throw new LogicException(
sprintf(
'Resolver "%s" must be an object, "%s" given.',
$resolver,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,14 @@
namespace Yiisoft\Hydrator\AttributeHandling\ResolverFactory;

use ReflectionClass;
use ReflectionException;
use Yiisoft\Hydrator\Attribute\Data\DataAttributeInterface;
use Yiisoft\Hydrator\NonInstantiableException;
use Yiisoft\Hydrator\ObjectFactory\ReflectionObjectFactory;
use Yiisoft\Hydrator\AttributeHandling\Exception\AttributeResolverNonInstantiableException;
use Yiisoft\Hydrator\Attribute\Parameter\ParameterAttributeInterface;

use function is_string;

final class ReflectionAttributeResolverFactory implements AttributeResolverFactoryInterface
{
private ReflectionObjectFactory $objectFactory;

public function __construct()
{
$this->objectFactory = new ReflectionObjectFactory();
}

/**
* @throws NonInstantiableException
* @throws ReflectionException
*/
public function create(DataAttributeInterface|ParameterAttributeInterface $attribute): object
{
$resolver = $attribute->getResolver();
Expand All @@ -34,15 +21,46 @@ public function create(DataAttributeInterface|ParameterAttributeInterface $attri
}

if (!class_exists($resolver)) {
throw new NonInstantiableException(
throw new AttributeResolverNonInstantiableException(
sprintf(
'Class "%s" does not exist.',
$resolver,
),
);
}

$reflectionClass = new ReflectionClass($resolver);
if ($reflectionClass->isAbstract()) {
throw new AttributeResolverNonInstantiableException(
sprintf(
'%s is not instantiable because it is abstract.',
$reflectionClass->getName(),
),
);
}

$constructor = $reflectionClass->getConstructor();
if ($constructor !== null) {
if (!$constructor->isPublic()) {
throw new AttributeResolverNonInstantiableException(
sprintf(
'Class "%s" is not instantiable because contain non-public constructor.',
$constructor->getDeclaringClass()->getName(),
),
);
}

if ($constructor->getNumberOfRequiredParameters() > 0) {
throw new AttributeResolverNonInstantiableException(
sprintf(
'Class "%s" cannot be instantiated because it has %d required parameters in constructor.',
$constructor->getDeclaringClass()->getName(),
$constructor->getNumberOfRequiredParameters(),
)
);
}
}

return $this->objectFactory->create($reflectionClass, []);
return $reflectionClass->newInstance();
}
}
20 changes: 20 additions & 0 deletions src/Exception/AbstractClassException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Hydrator\Exception;

use ReflectionClass;

final class AbstractClassException extends NonInstantiableException
{
public function __construct(ReflectionClass $reflectionClass)
{
parent::__construct(

Check warning on line 13 in src/Exception/AbstractClassException.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ { public function __construct(ReflectionClass $reflectionClass) { - parent::__construct(sprintf('%s is not instantiable because it is abstract.', $reflectionClass->getName())); + } }
sprintf(
'%s is not instantiable because it is abstract.',
$reflectionClass->getName(),
),
);
}
}
18 changes: 18 additions & 0 deletions src/Exception/NonExistClassException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Hydrator\Exception;

final class NonExistClassException extends NonInstantiableException
{
public function __construct(string $class)
{
parent::__construct(

Check warning on line 11 in src/Exception/NonExistClassException.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ { public function __construct(string $class) { - parent::__construct(sprintf('Class %s not exist.', $class)); + } }
sprintf(
'Class %s not exist.',
$class
),
);
}
}
11 changes: 11 additions & 0 deletions src/Exception/NonInstantiableException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Hydrator\Exception;

use LogicException;

class NonInstantiableException extends LogicException
{
}
35 changes: 35 additions & 0 deletions src/Exception/NonPublicConstructorException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Hydrator\Exception;

use ReflectionMethod;

final class NonPublicConstructorException extends NonInstantiableException
{
public function __construct(ReflectionMethod $constructor)
{
$type = $this->getConstructorType($constructor);
parent::__construct(

Check warning on line 14 in src/Exception/NonPublicConstructorException.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ public function __construct(ReflectionMethod $constructor) { $type = $this->getConstructorType($constructor); - parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? ' (' . $type . ')' : '')); + } private function getConstructorType(ReflectionMethod $constructor) : ?string {
sprintf(
'%s is not instantiable because contain non-public%s constructor.',
$constructor->getDeclaringClass()->getName(),
$type !== null ? ' (' . $type . ')' : '',

Check warning on line 18 in src/Exception/NonPublicConstructorException.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "Concat": --- Original +++ New @@ @@ public function __construct(ReflectionMethod $constructor) { $type = $this->getConstructorType($constructor); - parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? ' (' . $type . ')' : '')); + parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? $type . ' (' . ')' : '')); } private function getConstructorType(ReflectionMethod $constructor) : ?string {

Check warning on line 18 in src/Exception/NonPublicConstructorException.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": --- Original +++ New @@ @@ public function __construct(ReflectionMethod $constructor) { $type = $this->getConstructorType($constructor); - parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? ' (' . $type . ')' : '')); + parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? $type . ')' : '')); } private function getConstructorType(ReflectionMethod $constructor) : ?string {

Check warning on line 18 in src/Exception/NonPublicConstructorException.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": --- Original +++ New @@ @@ public function __construct(ReflectionMethod $constructor) { $type = $this->getConstructorType($constructor); - parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? ' (' . $type . ')' : '')); + parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? ' (' . ')' : '')); } private function getConstructorType(ReflectionMethod $constructor) : ?string {

Check warning on line 18 in src/Exception/NonPublicConstructorException.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "Concat": --- Original +++ New @@ @@ public function __construct(ReflectionMethod $constructor) { $type = $this->getConstructorType($constructor); - parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? ' (' . $type . ')' : '')); + parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? ' (' . ')' . $type : '')); } private function getConstructorType(ReflectionMethod $constructor) : ?string {

Check warning on line 18 in src/Exception/NonPublicConstructorException.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": --- Original +++ New @@ @@ public function __construct(ReflectionMethod $constructor) { $type = $this->getConstructorType($constructor); - parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? ' (' . $type . ')' : '')); + parent::__construct(sprintf('%s is not instantiable because contain non-public%s constructor.', $constructor->getDeclaringClass()->getName(), $type !== null ? ' (' . $type : '')); } private function getConstructorType(ReflectionMethod $constructor) : ?string {
),
);
}

private function getConstructorType(ReflectionMethod $constructor): ?string
{
if ($constructor->isPrivate()) {
return 'private';
}

if ($constructor->isProtected()) {
return 'protected';
}

return null;
}
}
20 changes: 20 additions & 0 deletions src/Exception/WrongConstructorArgumentsCountException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Hydrator\Exception;

final class WrongConstructorArgumentsCountException extends NonInstantiableException
{
public function __construct(\ReflectionMethod $constructor, int $countArguments)
{
parent::__construct(
sprintf(
'Class "%s" cannot be instantiated because it has %d required parameters in constructor, but passed only %d.',
$constructor->getDeclaringClass()->getName(),
$constructor->getNumberOfRequiredParameters(),
$countArguments,
)
);
}
}
10 changes: 5 additions & 5 deletions src/Hydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Yiisoft\Hydrator\AttributeHandling\ResolverFactory\AttributeResolverFactoryInterface;
use Yiisoft\Hydrator\AttributeHandling\DataAttributesHandler;
use Yiisoft\Hydrator\AttributeHandling\ParameterAttributesHandler;
use Yiisoft\Hydrator\Exception\NonExistClassException;
use Yiisoft\Hydrator\Internal\ConstructorArgumentsExtractor;
use Yiisoft\Hydrator\Internal\ReflectionFilter;
use Yiisoft\Hydrator\ObjectFactory\ObjectFactoryInterface;
Expand Down Expand Up @@ -83,16 +84,17 @@ public function hydrate(object $object, array $data = [], array $map = [], bool
public function create(string $class, array $data = [], array $map = [], bool $strict = false): object
{
if (!class_exists($class)) {
throw new NonInstantiableException();
throw new NonExistClassException($class);
}

$dataObject = new Data($data, $map, $strict);
$reflectionClass = new ReflectionClass($class);
$constructor = $reflectionClass->getConstructor();

$dataObject = new Data($data, $map, $strict);
$this->dataAttributesHandler->handle($reflectionClass, $dataObject);

[$excludeProperties, $constructorArguments] = $this->constructorArgumentsExtractor->extract(
$reflectionClass,
$constructor,
$dataObject,
);

Expand All @@ -110,8 +112,6 @@ public function create(string $class, array $data = [], array $map = [], bool $s
/**
* @param array<string, ReflectionProperty> $reflectionProperties
* @psalm-param MapType $map
*
* @throws NonInstantiableException
*/
private function hydrateInternal(
object $object,
Expand Down
2 changes: 2 additions & 0 deletions src/HydratorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Yiisoft\Hydrator;

use Yiisoft\Hydrator\Exception\NonInstantiableException;

/**
* Creates or hydrate objects from a set of raw data.
*
Expand Down

0 comments on commit d662a23

Please sign in to comment.