1
+ <?php
2
+
3
+ declare (strict_types=1 );
4
+
5
+ namespace Mapado \RestClientSdk \Mapping \Driver ;
6
+
7
+ use Mapado \RestClientSdk \Exception \MappingException ;
8
+ use Mapado \RestClientSdk \Mapping \Attribute ;
9
+ use Mapado \RestClientSdk \Mapping \Attributes ;
10
+ use Mapado \RestClientSdk \Mapping \ClassMetadata ;
11
+ use Mapado \RestClientSdk \Mapping \Relation ;
12
+
13
+ /**
14
+ * Class AttributeDriver
15
+ */
16
+ class AttributeDriver
17
+ {
18
+ /**
19
+ * @return array<ClassMetadata>
20
+ *
21
+ * @throws MappingException
22
+ */
23
+ public function loadDirectory (string $ path ): array
24
+ {
25
+ if (!is_dir ($ path )) {
26
+ throw new MappingException ($ path . ' is not a valid directory ' );
27
+ }
28
+
29
+ $ iterator = new \RegexIterator (
30
+ new \RecursiveIteratorIterator (
31
+ new \RecursiveDirectoryIterator (
32
+ $ path ,
33
+ \FilesystemIterator::SKIP_DOTS
34
+ ),
35
+ \RecursiveIteratorIterator::LEAVES_ONLY
36
+ ),
37
+ '/^.+\.php$/i ' ,
38
+ \RecursiveRegexIterator::GET_MATCH
39
+ );
40
+
41
+ $ classes = [];
42
+ $ includedFiles = [];
43
+
44
+ /** @var array $file */
45
+ foreach ($ iterator as $ file ) {
46
+ $ sourceFile = $ file [0 ] ?? null ;
47
+ if (is_string ($ sourceFile ) && !preg_match ('(^phar:)i ' , $ sourceFile )) {
48
+ $ sourceFile = realpath ($ sourceFile );
49
+ }
50
+
51
+ require_once $ sourceFile ;
52
+ $ includedFiles [] = $ sourceFile ;
53
+ }
54
+
55
+ $ declared = get_declared_classes ();
56
+ foreach ($ declared as $ className ) {
57
+ $ rc = new \ReflectionClass ($ className );
58
+ $ sourceFile = $ rc ->getFileName ();
59
+ if (in_array ($ sourceFile , $ includedFiles )) {
60
+ $ classes [] = $ className ;
61
+ }
62
+ }
63
+
64
+ $ mapping = [];
65
+ foreach ($ classes as $ class ) {
66
+ $ metadata = $ this ->getClassMetadataForClassname ($ class );
67
+ if ($ metadata ) {
68
+ $ mapping [] = $ metadata ;
69
+ }
70
+ }
71
+
72
+ return $ mapping ;
73
+ }
74
+
75
+ /**
76
+ * @param class-string $classname
77
+ *
78
+ * @return array<ClassMetadata>
79
+ */
80
+ public function loadClassname (string $ classname ): array
81
+ {
82
+ $ metadata = $ this ->getClassMetadataForClassname ($ classname );
83
+
84
+ return $ metadata ? [$ metadata ] : [];
85
+ }
86
+
87
+ /**
88
+ * @param class-string $classname
89
+ *
90
+ * @throws \ReflectionException
91
+ */
92
+ private function getClassMetadataForClassname (
93
+ string $ classname
94
+ ): ?ClassMetadata {
95
+ $ reflClass = new \ReflectionClass ($ classname );
96
+ $ classAttribute = $ this ->getClassAttribute ($ reflClass , Attributes \Entity::class);
97
+
98
+ if (!$ classAttribute ) {
99
+ return null ;
100
+ }
101
+
102
+ $ attributeList = [];
103
+ $ relationList = [];
104
+ foreach ($ reflClass ->getProperties () as $ property ) {
105
+ // manage attributes
106
+ $ propertyAttribute = $ this ->getPropertyAttribute ($ property , Attributes \Attribute::class);
107
+
108
+ if ($ propertyAttribute ) {
109
+ $ propertyIdAttribute = $ this ->getPropertyAttribute ($ property , Attributes \Id::class);
110
+
111
+ $ attributeList [] = new Attribute (
112
+ $ propertyAttribute ->name ,
113
+ $ property ->getName (),
114
+ $ propertyAttribute ->type ,
115
+ $ propertyIdAttribute instanceof Attributes \Id
116
+ );
117
+ } else {
118
+ $ relation = $ this ->getPropertyAttribute ($ property , Attributes \OneToMany::class);
119
+ if (!$ relation ) {
120
+ $ relation = $ this ->getPropertyAttribute ($ property , Attributes \ManyToOne::class);
121
+ }
122
+
123
+ if ($ relation ) {
124
+ $ attributeList [] = new Attribute (
125
+ $ relation ->name ,
126
+ $ property ->getName ()
127
+ );
128
+
129
+ $ targetEntity = $ relation ->targetEntity ;
130
+ if (false === mb_strpos ($ targetEntity , '/ ' )) {
131
+ $ targetEntity =
132
+ mb_substr (
133
+ $ classname ,
134
+ 0 ,
135
+ mb_strrpos ($ classname , '\\' ) + 1
136
+ ) . $ targetEntity ;
137
+ }
138
+
139
+ $ relationList [] = new Relation (
140
+ $ relation ->name ,
141
+ $ relation ->type ,
142
+ $ targetEntity
143
+ );
144
+ }
145
+ }
146
+ }
147
+
148
+ $ classMetadata = new ClassMetadata (
149
+ $ classAttribute ->key ,
150
+ $ classname ,
151
+ $ classAttribute ->repository
152
+ );
153
+ $ classMetadata ->setAttributeList ($ attributeList );
154
+ $ classMetadata ->setRelationList ($ relationList );
155
+
156
+ return $ classMetadata ;
157
+ }
158
+
159
+ /**
160
+ * @template T of Attributes\AbstractPropertyAttribute
161
+ *
162
+ * @param class-string<T> $classname
163
+ *
164
+ * @return T|null
165
+ */
166
+ private function getPropertyAttribute (\ReflectionProperty $ property , string $ classname )
167
+ {
168
+ return $ this ->getAttribute ($ property , $ classname );
169
+ }
170
+
171
+ /**
172
+ * @template T of Attributes\AbstractClassAttribute
173
+ *
174
+ * @param class-string<T> $className
175
+ *
176
+ * @return T|null
177
+ */
178
+ private function getClassAttribute (\ReflectionClass $ reflectionClass , string $ className )
179
+ {
180
+ return $ this ->getAttribute ($ reflectionClass , $ className );
181
+ }
182
+
183
+ /**
184
+ * @template T
185
+ *
186
+ * @param class-string<T> $className
187
+ *
188
+ * @return T|null
189
+ */
190
+ private function getAttribute (\ReflectionClass |\ReflectionProperty $ reflection , string $ className )
191
+ {
192
+ $ attribute = $ reflection ->getAttributes ($ className , \ReflectionAttribute::IS_INSTANCEOF )[0 ] ?? null ;
193
+
194
+ if (!$ attribute instanceof \ReflectionAttribute) {
195
+ return null ;
196
+ }
197
+
198
+ return $ attribute ->newInstance ();
199
+ }
200
+ }
0 commit comments