Passed
Push — master ( 010c64...640715 )
by Jesse
05:03
created

Extract::extract()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 4
nop 4
dl 0
loc 17
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Stratadox\EntityState;
5
6
use function array_map as extractWith;
7
use function array_merge as these;
8
use function get_class as classOfThe;
9
use function is_iterable as itIsACollection;
10
use function sprintf;
11
use Stratadox\EntityState\Internal\CollectionExtractor;
12
use Stratadox\EntityState\Internal\Extractor;
13
use Stratadox\EntityState\Internal\Name;
14
use Stratadox\EntityState\Internal\ObjectExtractor;
15
use Stratadox\EntityState\Internal\PropertyExtractor;
16
use Stratadox\EntityState\Internal\ReflectionProperties;
17
use Stratadox\EntityState\Internal\ScalarExtractor;
18
use Stratadox\EntityState\Internal\ShouldStringify;
19
use Stratadox\EntityState\Internal\Visited;
20
use Stratadox\IdentityMap\MapsObjectsByIdentity as Map;
21
use Stratadox\IdentityMap\NoSuchObject;
22
23
/**
24
 * Extract the state of the entities.
25
 *
26
 * @author Stratadox
27
 */
28
final class Extract implements ExtractsEntityState
29
{
30
    private $extractor;
31
32
    private function __construct(Extractor $extractor)
33
    {
34
        $this->extractor = $extractor;
35
    }
36
37
    /**
38
     * Produces a state extractor.
39
     *
40
     * @return ExtractsEntityState
41
     */
42
    public static function state(): ExtractsEntityState
43
    {
44
        return new Extract(
45
            PropertyExtractor::using(CollectionExtractor::withAlternative(
46
                ObjectExtractor::withAlternative(ScalarExtractor::asLastResort())
47
            ))
48
        );
49
    }
50
51
    /**
52
     * Produces a state extractor that converts certain types to string.
53
     *
54
     * @param mixed ...$classes
55
     * @return ExtractsEntityState
56
     */
57
    public static function stringifying(...$classes): ExtractsEntityState
58
    {
59
        return new Extract(
60
            PropertyExtractor::using(CollectionExtractor::withAlternative(
61
                ObjectExtractor::stringifyingWithAlternative(
62
                    ShouldStringify::these(...$classes),
63
                    ScalarExtractor::asLastResort()
64
                )
65
            ))
66
        );
67
    }
68
69
    /** @inheritdoc */
70
    public function from(Map $map): State
71
    {
72
        return $this->fromOnly($map, ...$map->objects());
73
    }
74
75
    /** @inheritdoc */
76
    public function fromOnly(Map $map, object ...$objects): State
77
    {
78
        return StateRepresentation::with(
79
            EntityStates::list(...extractWith(
80
                function (object $entity) use ($map): RepresentsEntity {
81
                    return $this->stateOfThe($entity, $map);
82
                }, $objects
83
            )),
84
            $map
85
        );
86
    }
87
88
    /** @throws NoSuchObject */
89
    private function stateOfThe(object $entity, Map $map): RepresentsEntity
90
    {
91
        $properties = [];
92
        if (itIsACollection($entity)) {
93
            $count = 0;
94
            foreach ($entity as $key => $item) {
95
                $properties[] = $this->extractor->extract(
96
                    Name::fromCollectionEntry($entity, (string) $key),
97
                    $item,
98
                    $map,
99
                    Visited::noneYet()
100
                );
101
                $count++;
102
            }
103
            $properties[] = [PropertyState::with(sprintf(
104
                'count(%s)',
105
                classOfThe($entity)
106
            ), $count)];
107
        }
108
        foreach (ReflectionProperties::ofThe($entity) as $property) {
109
            $properties[] = $this->extractor->extract(
110
                Name::fromReflection($property),
111
                $property->getValue($entity),
112
                $map,
113
                Visited::noneYet()
114
            );
115
        }
116
        return EntityState::ofThe(
117
            classOfThe($entity),
118
            $map->idOf($entity),
119
            empty($properties) ? PropertyStates::list() : PropertyStates::list(
120
                ...these(...$properties)
121
            )
122
        );
123
    }
124
}
125