Passed
Push — version-4 ( 8b03a3...c08a3b )
by Sebastian
02:27
created

Functions::assertValidCallable()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7.6383

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 7
eloc 17
c 1
b 0
f 1
nc 7
nop 2
dl 0
loc 23
ccs 13
cts 17
cp 0.7647
crap 7.6383
rs 8.8333
1
<?php
2
declare(strict_types=1);
3
4
namespace Seboettg\Collection\Assert;
5
6
use ReflectionFunction;
7
use Seboettg\Collection\Assert\Exception\NotApplicableCallableException;
8
use Seboettg\Collection\Assert\Exception\NotConvertibleToStringException;
9
use Seboettg\Collection\Assert\Exception\TypeIsNotAScalarException;
10
use Seboettg\Collection\Assert\Exception\WrongTypeException;
11
use function is_scalar;
12
use function is_object;
13
use function method_exists;
14
15
final class Functions
16
{
17 29
    public static final function assertScalar($value, string $message): void
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
18
    {
19 29
        if (!is_scalar($value)) {
20 3
            throw new TypeIsNotAScalarException($message);
21
        }
22 29
    }
23
24 1
    public static final function assertType($value, string $fqcn, string $message)
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
25
    {
26 1
        if (!$value instanceof $fqcn) {
27
            throw new WrongTypeException($message);
28
        }
29 1
    }
30
31 4
    public static final function assertStringable($value, string $message)
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
32
    {
33 4
        if (!is_scalar($value)) {
34 2
            if (!self::isStrigableObject($value)) {
35 1
                throw new NotConvertibleToStringException($message);
36
            }
37
        }
38 3
    }
39
40 2
    private static function isStrigableObject($value): bool
41
    {
42 2
        return is_object($value) && method_exists($value, "__toString");
43
    }
44
45 5
    public static function assertValidCallable(callable $callable, array $parameters)
46
    {
47 5
        $reflected = new ReflectionFunction($callable);
48 5
        if (count($reflected->getParameters()) !== count($parameters)) {
49
            throw new NotApplicableCallableException(
50
                "The number of parameters of the given callable does not match the expected number."
51
            );
52
        }
53 5
        for ($i = 0; $i < count($reflected->getParameters()); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
54 5
            $reflectedParamType = $reflected->getParameters()[$i]->getType();
55 5
            $expectedParam = $parameters[$i];
56 5
            switch ($expectedParam) {
57 5
                case "scalar":
58 2
                    if (!in_array($reflectedParamType, ["int", "string", "bool", "float", null])) {
59
                        self::throwNotApplicableCallableException($i, "scalar", $reflectedParamType);
60
                    }
61 2
                    break;
62 5
                case "mixed":
63
                    //ignore, since every type is allowed
64 2
                    break;
65
                default:
66 3
                    if ($reflectedParamType->getName() !== $expectedParam) {
0 ignored issues
show
Bug introduced by
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

66
                    if ($reflectedParamType->/** @scrutinizer ignore-call */ getName() !== $expectedParam) {
Loading history...
67
                        self::throwNotApplicableCallableException($i, $expectedParam, $reflectedParamType);
68
                    }
69
            }
70
        }
71 5
    }
72
73
    private static function throwNotApplicableCallableException($paramNumber, $expectedType, $actualType)
74
    {
75
        throw new NotApplicableCallableException(
76
            sprintf(
77
                "Parameter %d of type %s does not match the expected type of %s",
78
                $paramNumber,
79
                $actualType,
80
                $expectedType
81
            )
82
        );
83
    }
84
}
85
86
/**
87
 * @param $value
88
 * @param string $message description that will be included in the failure message if the assertion fails.
89
 * @return void
90
 */
91
function assertScalar($value, string $message): void
92
{
93 29
    Functions::assertScalar($value, $message);
94 29
}
95
96
/**
97
 * @param $value
98
 * @param string $fqcn full qualified class name
99
 * @param string $message description that will be included in the failure message if the assertion fails.
100
 * @return void
101
 */
102
function assertType($value, string $fqcn, string $message): void
103
{
104 1
    Functions::assertType($value, $fqcn, $message);
105 1
}
106
107
/**
108
 * @param $value
109
 * @param string $message description that will be included in the failure message if the assertion fails.
110
 * @return void
111
 */
112
function assertStringable($value, string $message): void
113
{
114 4
    Functions::assertStringable($value, $message);
115 3
}
116
117
118
function assertValidCallable(callable $callable, array $parameters)
119
{
120
    Functions::assertValidCallable($callable, $parameters);
121
}