• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

brick / orm / 23255296146

18 Mar 2026 04:24PM UTC coverage: 47.104%. Remained the same
23255296146

push

github

BenMorel
Avoid \Exception that somehow confuses ECS

1 of 5 new or added lines in 1 file covered. (20.0%)

402 existing lines in 24 files now uncovered.

553 of 1174 relevant lines covered (47.1%)

10.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/ClassMetadataBuilder.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\ORM;
6

7
use Brick\ORM\PropertyMapping\EntityMapping;
8
use Brick\ORM\PropertyMapping\IntMapping;
9
use Brick\ORM\PropertyMapping\StringMapping;
10
use LogicException;
11
use ReflectionClass;
12
use ReflectionProperty;
13

14
use function array_diff;
15
use function array_flip;
16
use function array_values;
17
use function get_class;
18
use function is_subclass_of;
19
use function sprintf;
20

21
/**
22
 * Builds ClassMetadata instances for all entities.
23
 */
24
class ClassMetadataBuilder
25
{
26
    private Configuration $configuration;
27

28
    /**
29
     * @var array<class-string, EntityMetadata>
30
     */
31
    private array $entityMetadata;
32

33
    /**
34
     * @var array<class-string, EmbeddableMetadata>
35
     */
36
    private array $embeddableMetadata;
37

38
    public function __construct(Configuration $configuration)
39
    {
UNCOV
40
        $this->configuration = $configuration;
×
41
    }
42

43
    /**
44
     * @return array<class-string, EntityMetadata> A map of FQCN to EntityMetadata instances for all entities.
45
     */
46
    public function build(): array
47
    {
UNCOV
48
        $this->entityMetadata = [];
×
49
        $this->embeddableMetadata = [];
×
50

51
        $classConfigurations = $this->configuration->getClasses();
×
52

53
        foreach ($classConfigurations as $classConfiguration) {
×
54
            if ($classConfiguration instanceof EntityConfiguration) {
×
UNCOV
55
                foreach ($classConfiguration->getClassHierarchy() as $className) {
×
56
                    $this->entityMetadata[$className] = new EntityMetadata();
×
57
                }
UNCOV
58
            } elseif ($classConfiguration instanceof EmbeddableConfiguration) {
×
UNCOV
59
                $this->embeddableMetadata[$classConfiguration->getClassName()] = new EmbeddableMetadata();
×
60
            }
61
        }
62

63
        // This needs to be done in 2 steps, as references to all ClassMetadata instances must be available below.
64

65
        foreach ($classConfigurations as $classConfiguration) {
×
66
            if ($classConfiguration instanceof EntityConfiguration) {
×
UNCOV
67
                foreach ($classConfiguration->getClassHierarchy() as $className) {
×
68
                    $this->fillEntityMetadata($this->entityMetadata[$className], $className, $classConfiguration);
×
69
                }
70
            } elseif ($classConfiguration instanceof EmbeddableConfiguration) {
×
UNCOV
71
                $className = $classConfiguration->getClassName();
×
UNCOV
72
                $this->fillEmbeddableMetadata($this->embeddableMetadata[$className], $className, $classConfiguration);
×
73
            }
74
        }
75

UNCOV
76
        return $this->entityMetadata;
×
77
    }
78

79
    /**
80
     * @param class-string $className
81
     *
82
     * @throws LogicException
83
     */
84
    private function fillEntityMetadata(EntityMetadata $classMetadata, string $className, EntityConfiguration $entityConfiguration): void
85
    {
86
        $reflectionClass = new ReflectionClass($className);
×
87

88
        $classMetadata->className = $className;
×
89

UNCOV
90
        $classMetadata->discriminatorColumn = $entityConfiguration->getDiscriminatorColumn();
×
91
        $classMetadata->discriminatorValue = null;
×
92

93
        foreach ($entityConfiguration->getDiscriminatorMap() as $discriminatorValue => $targetClassName) {
×
94
            if ($targetClassName === $className) {
×
UNCOV
95
                $classMetadata->discriminatorValue = $discriminatorValue;
×
96

UNCOV
97
                break;
×
98
            }
99
        }
100

101
        $classMetadata->discriminatorMap = $entityConfiguration->getDiscriminatorMap();
×
UNCOV
102
        $classMetadata->inverseDiscriminatorMap = array_flip($classMetadata->discriminatorMap);
×
103

104
        $classMetadata->childClasses = [];
×
105

UNCOV
106
        foreach ($entityConfiguration->getClassHierarchy() as $hClassName) {
×
UNCOV
107
            if (is_subclass_of($hClassName, $className)) {
×
UNCOV
108
                $classMetadata->childClasses[] = $hClassName;
×
109
            }
110
        }
111

112
        $classMetadata->rootClassName = $entityConfiguration->getClassName();
×
113

114
        if ($reflectionClass->isAbstract()) {
×
UNCOV
115
            $classMetadata->proxyClassName = null;
×
116
        } else {
117
            $classMetadata->proxyClassName = $this->configuration->getProxyClassName($className);
×
118
        }
119

120
        $classMetadata->tableName = $entityConfiguration->getTableName();
×
121
        $classMetadata->isAutoIncrement = $entityConfiguration->isAutoIncrement();
×
122

123
        $persistentProperties = $entityConfiguration->getPersistentProperties($className);
×
124
        $identityProperties = $entityConfiguration->getIdentityProperties();
×
125

UNCOV
126
        $classMetadata->properties = $persistentProperties;
×
127
        $classMetadata->idProperties = $identityProperties;
×
UNCOV
128
        $classMetadata->nonIdProperties = array_values(array_diff($persistentProperties, $identityProperties));
×
129

130
        $classMetadata->selfNonIdProperties = [];
×
131

132
        foreach ($classMetadata->nonIdProperties as $nonIdProperty) {
×
UNCOV
133
            $r = new ReflectionProperty($className, $nonIdProperty);
×
UNCOV
134
            if ($r->getDeclaringClass()->getName() === $className) {
×
UNCOV
135
                $classMetadata->selfNonIdProperties[] = $nonIdProperty;
×
136
            }
137
        }
138

139
        $classMetadata->propertyMappings = [];
×
140

UNCOV
141
        foreach ($persistentProperties as $propertyName) {
×
UNCOV
142
            $propertyMapping = $entityConfiguration->getPropertyMapping($className, $propertyName, $this->entityMetadata, $this->embeddableMetadata);
×
UNCOV
143
            $classMetadata->propertyMappings[$propertyName] = $propertyMapping;
×
144
        }
145

146
        // Enforce non-nullable identities, that ultimately map to int or string properties.
147
        // We need this guarantee for our identity map, and other types do not make much sense anyway.
148

149
        foreach ($classMetadata->idProperties as $idProperty) {
×
150
            $propertyMapping = $classMetadata->propertyMappings[$idProperty];
×
151

152
            if ($propertyMapping->isNullable()) {
×
153
                throw new LogicException(sprintf(
×
154
                    'Identity property %s::$%s must not be nullable.',
×
UNCOV
155
                    $classMetadata->className,
×
UNCOV
156
                    $idProperty,
×
157
                ));
×
158
            }
159

UNCOV
160
            if ($propertyMapping instanceof IntMapping) {
×
161
                continue;
×
162
            }
163

UNCOV
164
            if ($propertyMapping instanceof StringMapping) {
×
165
                continue;
×
166
            }
167

UNCOV
168
            if ($propertyMapping instanceof EntityMapping) {
×
169
                continue;
×
170
            }
171

172
            throw new LogicException(sprintf(
×
173
                'Identity property %s::$%s uses an unsupported mapping type %s. ' .
×
174
                'Identities must ultimately map to int or string properties.',
×
175
                $classMetadata->className,
×
UNCOV
176
                $idProperty,
×
UNCOV
177
                get_class($propertyMapping),
×
UNCOV
178
            ));
×
179
        }
180
    }
181

182
    /**
183
     * @param class-string $className
184
     */
185
    private function fillEmbeddableMetadata(EmbeddableMetadata $classMetadata, string $className, EmbeddableConfiguration $embeddableConfiguration): void
186
    {
UNCOV
187
        $classMetadata->className = $className;
×
188

UNCOV
189
        $persistentProperties = $embeddableConfiguration->getPersistentProperties($className);
×
190

UNCOV
191
        $classMetadata->properties = $persistentProperties;
×
192

193
        $classMetadata->propertyMappings = [];
×
194

UNCOV
195
        foreach ($persistentProperties as $propertyName) {
×
UNCOV
196
            $propertyMapping = $embeddableConfiguration->getPropertyMapping($className, $propertyName, $this->entityMetadata, $this->embeddableMetadata);
×
UNCOV
197
            $classMetadata->propertyMappings[$propertyName] = $propertyMapping;
×
198
        }
199
    }
200
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc