Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
18 / 18
93.33% covered (success)
93.33%
14 / 15
90.00% covered (success)
90.00%
9 / 10
85.71% covered (warning)
85.71%
6 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
HydrationFailure
100.00% covered (success)
100.00%
18 / 18
93.33% covered (success)
93.33%
14 / 15
90.00% covered (success)
90.00%
9 / 10
100.00% covered (success)
100.00%
7 / 7
9.08
100.00% covered (success)
100.00%
1 / 1
 reason
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withReason
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromHydratableFailure
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 message
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 source
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 formatRowKeys
100.00% covered (success)
100.00%
1 / 1
85.71% covered (warning)
85.71%
6 / 7
66.67% covered (warning)
66.67%
2 / 3
100.00% covered (success)
100.00%
1 / 1
2.15
 valueType
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Quma\Exception;
6
7use RuntimeException;
8use Throwable;
9
10/** @api */
11class HydrationFailure extends RuntimeException
12{
13    private string $reason = '';
14
15    /**
16     * The failure detail without target, source and row keys, so that an
17     * enclosing failure can embed it without repeating its own context.
18     *
19     * @internal
20     */
21    public function reason(): string
22    {
23        return $this->reason;
24    }
25
26    protected function withReason(string $reason): static
27    {
28        $this->reason = $reason;
29
30        return $this;
31    }
32
33    /**
34     * @param class-string $class
35     * @param list<string> $rowKeys
36     */
37    public static function fromHydratableFailure(
38        string $class,
39        ?string $sourcePath,
40        array $rowKeys,
41        Throwable $previous,
42    ): self {
43        return new self(
44            self::message(
45                $class,
46                $sourcePath,
47                'Hydratable::fromRow() failed. Row keys: ' . self::formatRowKeys($rowKeys) . '.',
48            ),
49            0,
50            $previous,
51        );
52    }
53
54    /** @param class-string $class */
55    protected static function message(string $class, ?string $sourcePath, string $detail): string
56    {
57        return "Could not hydrate {$class} from " . self::source($sourcePath) . ": {$detail}";
58    }
59
60    protected static function source(?string $sourcePath): string
61    {
62        return $sourcePath ?? 'ad-hoc SQL';
63    }
64
65    /** @param list<string> $rowKeys */
66    protected static function formatRowKeys(array $rowKeys): string
67    {
68        return $rowKeys === [] ? '(none)' : implode(', ', $rowKeys);
69    }
70
71    protected static function valueType(mixed $value): string
72    {
73        if (is_resource($value)) {
74            return get_resource_type($value) . ' resource';
75        }
76
77        return get_debug_type($value);
78    }
79}