1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Onurb\Doctrine\ORMMetadataGrapher\YumlMetadataGrapher; |
4
|
|
|
|
5
|
|
|
use Doctrine\Common\Persistence\Mapping\ClassMetadata; |
6
|
|
|
use Onurb\Doctrine\ORMMetadataGrapher\YumlMetadataGrapher\StringGenerator\StringGeneratorHelper; |
7
|
|
|
use Onurb\Doctrine\ORMMetadataGrapher\YumlMetadataGrapher\StringGenerator\StringGeneratorHelperInterface; |
8
|
|
|
use Onurb\Doctrine\ORMMetadataGrapher\YumlMetadataGrapher\StringGenerator\VisitedAssociationLogger; |
9
|
|
|
use Onurb\Doctrine\ORMMetadataGrapher\YumlMetadataGrapher\StringGenerator\VisitedAssociationLoggerInterface; |
10
|
|
|
|
11
|
|
|
class StringGenerator implements StringGeneratorInterface |
12
|
|
|
{ |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* @var array |
16
|
|
|
*/ |
17
|
|
|
protected $classStrings; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var StringGeneratorHelperInterface |
21
|
|
|
*/ |
22
|
|
|
protected $stringHelper; |
23
|
|
|
|
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var ClassStoreInterface |
27
|
|
|
*/ |
28
|
|
|
protected $classStore; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var VisitedAssociationLoggerInterface |
32
|
|
|
*/ |
33
|
|
|
protected $associationLogger; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @param ClassStoreInterface $classStore |
37
|
|
|
*/ |
38
|
11 |
|
public function __construct(ClassStoreInterface $classStore) |
39
|
|
|
{ |
40
|
11 |
|
$this->classStore = $classStore; |
41
|
11 |
|
$this->associationLogger = new VisitedAssociationLogger(); |
42
|
11 |
|
$this->stringHelper = new StringGeneratorHelper(); |
43
|
11 |
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @return VisitedAssociationLoggerInterface |
47
|
|
|
*/ |
48
|
1 |
|
public function getAssociationLogger() |
49
|
|
|
{ |
50
|
1 |
|
return $this->associationLogger; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Build the string representing the single graph item |
55
|
|
|
* |
56
|
|
|
* @param ClassMetadata $class |
57
|
|
|
* |
58
|
|
|
* @return string |
59
|
|
|
*/ |
60
|
9 |
|
public function getClassString(ClassMetadata $class) |
61
|
|
|
{ |
62
|
9 |
|
$className = $class->getName(); |
63
|
|
|
|
64
|
9 |
|
if (!isset($this->classStrings[$className])) { |
65
|
9 |
|
$this->associationLogger->visitAssociation($className); |
66
|
|
|
|
67
|
9 |
|
$parentFields = $this->getParentFields($class); |
68
|
9 |
|
$fields = $this->getClassFields($class, $parentFields); |
69
|
|
|
|
70
|
9 |
|
$this->classStrings[$className] = $this->stringHelper->getClassText($className, $fields); |
71
|
9 |
|
} |
72
|
|
|
|
73
|
9 |
|
return $this->classStrings[$className]; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Recursive function to get all fields in inheritance |
78
|
|
|
* |
79
|
|
|
* @param ClassMetadata $class |
80
|
|
|
* @param array $fields |
81
|
|
|
* @return array |
82
|
|
|
*/ |
83
|
9 |
|
public function getParentFields(ClassMetadata $class, $fields = array()) |
84
|
|
|
{ |
85
|
9 |
|
if ($parent = $this->classStore->getParent($class)) { |
86
|
2 |
|
$parentFields = $parent->getFieldNames(); |
87
|
2 |
|
foreach ($parentFields as $field) { |
88
|
2 |
|
if (!in_array($field, $fields)) { |
89
|
2 |
|
$fields[] = $field; |
90
|
2 |
|
} |
91
|
2 |
|
} |
92
|
2 |
|
$fields = $this->getParentFields($parent, $fields); |
93
|
2 |
|
} |
94
|
|
|
|
95
|
9 |
|
return $fields; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param ClassMetadata $class1 |
100
|
|
|
* @param string $association |
101
|
|
|
* @return string |
102
|
|
|
*/ |
103
|
6 |
|
public function getAssociationString(ClassMetadata $class1, $association) |
104
|
|
|
{ |
105
|
6 |
|
$targetClassName = $class1->getAssociationTargetClass($association); |
106
|
6 |
|
$class2 = $this->classStore->getClassByName($targetClassName); |
107
|
6 |
|
$isInverse = $class1->isAssociationInverseSide($association); |
108
|
6 |
|
$associationCount = $this->getClassCount($class1, $association); |
109
|
|
|
|
110
|
6 |
|
if (null === $class2) { |
111
|
2 |
|
return $this->stringHelper->makeSingleSidedLinkString( |
112
|
2 |
|
$this->getClassString($class1), |
113
|
2 |
|
$isInverse, |
114
|
2 |
|
$association, |
115
|
2 |
|
$associationCount, |
116
|
|
|
$targetClassName |
117
|
2 |
|
); |
118
|
|
|
} |
119
|
|
|
|
120
|
5 |
|
$reverseAssociationName = $this->getClassReverseAssociationName($class1, $association); |
121
|
|
|
|
122
|
5 |
|
$reverseAssociationCount = 0; |
123
|
5 |
|
$bidirectional = $this->isBidirectional( |
124
|
5 |
|
$reverseAssociationName, |
125
|
5 |
|
$isInverse, |
126
|
5 |
|
$class2, |
127
|
|
|
$reverseAssociationName |
|
|
|
|
128
|
5 |
|
); |
129
|
|
|
|
130
|
5 |
|
if ($bidirectional) { |
131
|
3 |
|
$reverseAssociationCount = $this->getClassCount($class2, $reverseAssociationName); |
132
|
3 |
|
} |
133
|
|
|
|
134
|
5 |
|
$this->associationLogger->visitAssociation($targetClassName, $reverseAssociationName); |
135
|
|
|
|
136
|
5 |
|
return $this->stringHelper->makeDoubleSidedLinkString( |
137
|
5 |
|
$this->getClassString($class1), |
138
|
5 |
|
$this->getClassString($class2), |
139
|
5 |
|
$bidirectional, |
140
|
5 |
|
$isInverse, |
141
|
5 |
|
$reverseAssociationName, |
142
|
5 |
|
$reverseAssociationCount, |
143
|
5 |
|
$association, |
144
|
|
|
$associationCount |
145
|
5 |
|
); |
146
|
|
|
} |
147
|
|
|
|
148
|
5 |
|
private function isBidirectional( |
149
|
|
|
$reverseAssociationName, |
150
|
|
|
$isInverse, |
151
|
|
|
ClassMetadata $class2, |
152
|
|
|
$reverseAssociationName |
|
|
|
|
153
|
|
|
) { |
154
|
5 |
|
return null !== $reverseAssociationName |
155
|
5 |
|
&& ($isInverse || $class2->isAssociationInverseSide($reverseAssociationName)); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* @param ClassMetadata $class |
160
|
|
|
* @param string $association |
161
|
|
|
* @return int |
162
|
|
|
*/ |
163
|
6 |
|
private function getClassCount(ClassMetadata $class, $association) |
164
|
|
|
{ |
165
|
6 |
|
return $class->isCollectionValuedAssociation($association) ? 2 : 1; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @param ClassMetadata $class |
170
|
|
|
* @param array $parentFields |
171
|
|
|
* @return array |
172
|
|
|
*/ |
173
|
9 |
|
private function getClassFields(ClassMetadata $class, $parentFields) |
174
|
|
|
{ |
175
|
9 |
|
$fields = array(); |
176
|
|
|
|
177
|
9 |
|
foreach ($class->getFieldNames() as $fieldName) { |
178
|
3 |
|
if (in_array($fieldName, $parentFields)) { |
179
|
2 |
|
continue; |
180
|
|
|
} |
181
|
|
|
|
182
|
3 |
|
$fields[] = $class->isIdentifier($fieldName) ? '+' . $fieldName : $fieldName; |
183
|
9 |
|
} |
184
|
|
|
|
185
|
9 |
|
return $fields; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Returns the $class2 association name for $class1 if reverse related (or null if not) |
190
|
|
|
* |
191
|
|
|
* @param ClassMetadata $class1 |
192
|
|
|
* @param string $association |
193
|
|
|
* |
194
|
|
|
* @return string|null |
195
|
|
|
*/ |
196
|
5 |
|
private function getClassReverseAssociationName(ClassMetadata $class1, $association) |
197
|
|
|
{ |
198
|
5 |
|
if ($class1->getAssociationMapping($association)['isOwningSide']) { |
199
|
5 |
|
return $class1->getAssociationMapping($association)['inversedBy']; |
200
|
|
|
} |
201
|
|
|
|
202
|
3 |
|
return $class1->getAssociationMapping($association)['mappedBy']; |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.