These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Wikibase\DataModel\Services\Lookup; |
||
4 | |||
5 | use Wikibase\DataModel\Entity\EntityId; |
||
6 | use Wikibase\DataModel\Entity\EntityIdValue; |
||
7 | use Wikibase\DataModel\Entity\PropertyId; |
||
8 | use Wikibase\DataModel\Services\Entity\EntityPrefetcher; |
||
9 | use Wikibase\DataModel\Snak\PropertyValueSnak; |
||
10 | use Wikibase\DataModel\Snak\Snak; |
||
11 | use Wikibase\DataModel\Statement\StatementListProvider; |
||
12 | |||
13 | /** |
||
14 | * Service for getting the closest entity (out of a specified set), |
||
15 | * from a given starting entity. The starting entity, and the target entities |
||
16 | * are (potentially indirectly, via intermediate entities) linked by statements |
||
17 | * with a given property ID, pointing from the starting entity to one of the |
||
18 | * target entities. |
||
19 | * |
||
20 | * @since 3.10 |
||
21 | * |
||
22 | * @license GPL-2.0-or-later |
||
23 | * @author Marius Hoch |
||
24 | */ |
||
25 | class EntityRetrievingClosestReferencedEntityIdLookup implements ReferencedEntityIdLookup { |
||
26 | |||
27 | /** |
||
28 | * @var EntityLookup |
||
29 | */ |
||
30 | private $entityLookup; |
||
31 | |||
32 | /** |
||
33 | * @var EntityPrefetcher |
||
34 | */ |
||
35 | private $entityPrefetcher; |
||
36 | |||
37 | /** |
||
38 | * @var int Maximum search depth: Maximum number of intermediate entities to search through. |
||
39 | * For example 0 means that only the entities immediately referenced will be found. |
||
40 | */ |
||
41 | private $maxDepth; |
||
42 | |||
43 | /** |
||
44 | * @var int Maximum number of entities to retrieve. |
||
45 | */ |
||
46 | private $maxEntityVisits; |
||
47 | |||
48 | /** |
||
49 | * Map (entity id => true) of already visited entities. |
||
50 | * |
||
51 | * @var bool[] |
||
52 | */ |
||
53 | private $alreadyVisited = []; |
||
54 | |||
55 | /** |
||
56 | * @param EntityLookup $entityLookup |
||
57 | * @param EntityPrefetcher $entityPrefetcher |
||
58 | * @param int $maxDepth Maximum search depth: Maximum number of intermediate entities to search through. |
||
59 | * For example if 0 is given, only the entities immediately referenced will be found. |
||
60 | * If this limit gets exhausted, a MaxReferenceDepthExhaustedException is thrown. |
||
61 | * @param int $maxEntityVisits Maximum number of entities to retrieve during a lookup. |
||
62 | * If this limit gets exhausted, a MaxReferencedEntityVisitsExhaustedException is thrown. |
||
63 | */ |
||
64 | public function __construct( |
||
65 | EntityLookup $entityLookup, |
||
66 | EntityPrefetcher $entityPrefetcher, |
||
67 | $maxDepth, |
||
68 | $maxEntityVisits |
||
69 | ) { |
||
70 | $this->entityLookup = $entityLookup; |
||
71 | $this->entityPrefetcher = $entityPrefetcher; |
||
72 | $this->maxDepth = $maxDepth; |
||
73 | $this->maxEntityVisits = $maxEntityVisits; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * Get the closest entity (out of $toIds), from a given entity. The starting entity, and |
||
78 | * the target entities are (potentially indirectly, via intermediate entities) linked by |
||
79 | * statements with the given property ID, pointing from the starting entity to one of the |
||
80 | * target entities. |
||
81 | * |
||
82 | * @since 3.10 |
||
83 | * |
||
84 | * @param EntityId $fromId |
||
85 | * @param PropertyId $propertyId |
||
86 | * @param EntityId[] $toIds |
||
87 | * |
||
88 | * @return EntityId|null Returns null in case none of the target entities are referenced. |
||
89 | * @throws ReferencedEntityIdLookupException |
||
90 | */ |
||
91 | public function getReferencedEntityId( EntityId $fromId, PropertyId $propertyId, array $toIds ) { |
||
92 | if ( !$toIds ) { |
||
93 | return null; |
||
94 | } |
||
95 | |||
96 | $this->alreadyVisited = []; |
||
97 | |||
98 | $steps = $this->maxDepth + 1; // Add one as checking $fromId already is a step |
||
99 | $toVisit = [ $fromId ]; |
||
100 | |||
101 | while ( $steps-- ) { |
||
102 | $this->entityPrefetcher->prefetch( $toVisit ); |
||
103 | $toVisitNext = []; |
||
104 | |||
105 | foreach ( $toVisit as $curId ) { |
||
106 | $result = $this->processEntityById( $curId, $fromId, $propertyId, $toIds, $toVisitNext ); |
||
0 ignored issues
–
show
|
|||
107 | if ( $result ) { |
||
108 | return $result; |
||
109 | } |
||
110 | } |
||
111 | // Remove already visited entities |
||
112 | $toVisit = array_unique( |
||
113 | array_diff( $toVisitNext, array_keys( $this->alreadyVisited ) ) |
||
114 | ); |
||
115 | |||
116 | if ( !$toVisit ) { |
||
117 | return null; |
||
118 | } |
||
119 | } |
||
120 | |||
121 | // Exhausted the max. depth without finding anything. |
||
122 | throw new MaxReferenceDepthExhaustedException( |
||
123 | $fromId, |
||
124 | $propertyId, |
||
125 | $toIds, |
||
126 | $this->maxDepth |
||
127 | ); |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * Find out whether an entity (directly) references one of the target ids. |
||
132 | * |
||
133 | * @param EntityId $id Id of the entity to process |
||
134 | * @param EntityId $fromId Id this lookup started from |
||
135 | * @param PropertyId $propertyId |
||
136 | * @param EntityId[] $toIds |
||
137 | * @param EntityId[] &$toVisit List of entities that still need to be checked |
||
138 | * @return EntityId|null Target id the entity refers to, null if none. |
||
139 | */ |
||
140 | private function processEntityById( |
||
141 | EntityId $id, |
||
142 | EntityId $fromId, |
||
143 | PropertyId $propertyId, |
||
144 | array $toIds, |
||
145 | array &$toVisit |
||
146 | ) { |
||
147 | $entity = $this->getEntity( $id, $fromId, $propertyId, $toIds ); |
||
148 | if ( !$entity ) { |
||
149 | return null; |
||
150 | } |
||
151 | |||
152 | $mainSnaks = $this->getMainSnaks( $entity, $propertyId ); |
||
153 | |||
154 | foreach ( $mainSnaks as $mainSnak ) { |
||
155 | $result = $this->processSnak( $mainSnak, $toVisit, $toIds ); |
||
0 ignored issues
–
show
Are you sure the assignment to
$result is correct as $this->processSnak($mainSnak, $toVisit, $toIds) (which targets Wikibase\DataModel\Servi...IdLookup::processSnak() ) seems to always return null.
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.
Loading history...
|
|||
156 | if ( $result ) { |
||
157 | return $result; |
||
158 | } |
||
159 | } |
||
160 | |||
161 | return null; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * @param EntityId $id Id of the entity to get |
||
166 | * @param EntityId $fromId Id this lookup started from |
||
167 | * @param PropertyId $propertyId |
||
168 | * @param EntityId[] $toIds |
||
169 | * |
||
170 | * @return StatementListProvider|null Null if not applicable. |
||
171 | */ |
||
172 | private function getEntity( EntityId $id, EntityId $fromId, PropertyId $propertyId, array $toIds ) { |
||
173 | if ( isset( $this->alreadyVisited[$id->getSerialization()] ) ) { |
||
174 | trigger_error( |
||
175 | 'Entity ' . $id->getSerialization() . ' already visited.', |
||
176 | E_USER_WARNING |
||
177 | ); |
||
178 | |||
179 | return null; |
||
180 | } |
||
181 | |||
182 | $this->alreadyVisited[$id->getSerialization()] = true; |
||
183 | |||
184 | if ( count( $this->alreadyVisited ) > $this->maxEntityVisits ) { |
||
185 | throw new MaxReferencedEntityVisitsExhaustedException( |
||
186 | $fromId, |
||
187 | $propertyId, |
||
188 | $toIds, |
||
189 | $this->maxEntityVisits |
||
190 | ); |
||
191 | } |
||
192 | |||
193 | try { |
||
194 | $entity = $this->entityLookup->getEntity( $id ); |
||
195 | } catch ( EntityLookupException $ex ) { |
||
196 | throw new ReferencedEntityIdLookupException( $fromId, $propertyId, $toIds, null, $ex ); |
||
197 | } |
||
198 | |||
199 | if ( !( $entity instanceof StatementListProvider ) ) { |
||
200 | return null; |
||
201 | } |
||
202 | |||
203 | return $entity; |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Decide whether a single Snak is pointing to one of the target ids. |
||
208 | * |
||
209 | * @param Snak $snak |
||
210 | * @param EntityId[] &$toVisit List of entities that still need to be checked |
||
211 | * @param EntityId[] $toIds |
||
212 | * @return EntityId|null Target id the Snak refers to, null if none. |
||
213 | */ |
||
214 | private function processSnak( Snak $snak, array &$toVisit, array $toIds ) { |
||
215 | if ( ! ( $snak instanceof PropertyValueSnak ) ) { |
||
216 | return null; |
||
217 | } |
||
218 | $dataValue = $snak->getDataValue(); |
||
219 | if ( ! ( $dataValue instanceof EntityIdValue ) ) { |
||
220 | return null; |
||
221 | } |
||
222 | |||
223 | $entityId = $dataValue->getEntityId(); |
||
224 | if ( in_array( $entityId, $toIds, false ) ) { |
||
225 | return $entityId; |
||
226 | } |
||
227 | |||
228 | $toVisit[] = $entityId; |
||
229 | |||
230 | return null; |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * @param StatementListProvider $statementListProvider |
||
235 | * @param PropertyId $propertyId |
||
236 | * @return Snak[] |
||
237 | */ |
||
238 | private function getMainSnaks( |
||
239 | StatementListProvider $statementListProvider, |
||
240 | PropertyId $propertyId |
||
241 | ) { |
||
242 | return $statementListProvider |
||
243 | ->getStatements() |
||
244 | ->getByPropertyId( $propertyId ) |
||
245 | ->getBestStatements() |
||
246 | ->getMainSnaks(); |
||
247 | } |
||
248 | |||
249 | } |
||
250 |
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
The method
getObject()
can return nothing but null, so it makes no sense to assign that value to a variable.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.