Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like ConstraintParameterParser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ConstraintParameterParser, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
40 | class ConstraintParameterParser { |
||
41 | |||
42 | /** |
||
43 | * @var Config |
||
44 | */ |
||
45 | private $config; |
||
46 | |||
47 | /** |
||
48 | * @var SnakDeserializer |
||
49 | */ |
||
50 | private $snakDeserializer; |
||
51 | |||
52 | /** |
||
53 | * @var string[] |
||
54 | */ |
||
55 | private $conceptBaseUris; |
||
56 | |||
57 | /** |
||
58 | * @param Config $config |
||
59 | * contains entity IDs used in constraint parameters (constraint statement qualifiers) |
||
60 | * @param DeserializerFactory $factory |
||
61 | * used to parse constraint statement qualifiers into constraint parameters |
||
62 | * @param string[] $conceptBaseUris |
||
63 | * mapping from repository names to base URIs of concept URIs, |
||
64 | * used to obtain the full unit string from an entity ID given in constraint parameters |
||
65 | */ |
||
66 | public function __construct( |
||
75 | |||
76 | /** |
||
77 | * Check if any errors are recorded in the constraint parameters. |
||
78 | * @param array $parameters |
||
79 | * @throws ConstraintParameterException |
||
80 | */ |
||
81 | public function checkError( array $parameters ) { |
||
92 | |||
93 | /** |
||
94 | * Require that $parameters contains exactly one $parameterId parameter. |
||
95 | * @param array $parameters |
||
96 | * @param string $parameterId |
||
97 | * @throws ConstraintParameterException |
||
98 | */ |
||
99 | View Code Duplication | private function requireSingleParameter( array $parameters, $parameterId ) { |
|
107 | |||
108 | /** |
||
109 | * Require that $snak is a {@link PropertyValueSnak}. |
||
110 | * @param Snak $snak |
||
111 | * @param string $parameterId |
||
112 | * @return void |
||
113 | * @throws ConstraintParameterException |
||
114 | */ |
||
115 | View Code Duplication | private function requireValueParameter( Snak $snak, $parameterId ) { |
|
123 | |||
124 | /** |
||
125 | * Parse a single entity ID parameter. |
||
126 | * @param array $snakSerialization |
||
127 | * @param string $parameterId |
||
128 | * @throws ConstraintParameterException |
||
129 | * @return EntityId |
||
130 | */ |
||
131 | View Code Duplication | private function parseEntityIdParameter( array $snakSerialization, $parameterId ) { |
|
145 | |||
146 | /** |
||
147 | * @param array $constraintParameters see {@link \WikibaseQuality\ConstraintReport\Constraint::getConstraintParameters()} |
||
148 | * @param string $constraintTypeItemId used in error messages |
||
149 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
150 | * @return string[] class entity ID serializations |
||
151 | */ |
||
152 | public function parseClassParameter( array $constraintParameters, $constraintTypeItemId ) { |
||
169 | |||
170 | /** |
||
171 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
172 | * @param string $constraintTypeItemId used in error messages |
||
173 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
174 | * @return string 'instance', 'subclass', or 'instanceOrSubclass' |
||
175 | */ |
||
176 | public function parseRelationParameter( array $constraintParameters, $constraintTypeItemId ) { |
||
177 | $this->checkError( $constraintParameters ); |
||
178 | $relationId = $this->config->get( 'WBQualityConstraintsRelationId' ); |
||
179 | if ( !array_key_exists( $relationId, $constraintParameters ) ) { |
||
180 | throw new ConstraintParameterException( |
||
181 | ( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) ) |
||
182 | ->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM ) |
||
183 | ->withEntityId( new PropertyId( $relationId ), Role::CONSTRAINT_PARAMETER_PROPERTY ) |
||
184 | ); |
||
185 | } |
||
186 | |||
187 | $this->requireSingleParameter( $constraintParameters, $relationId ); |
||
188 | $relationEntityId = $this->parseEntityIdParameter( $constraintParameters[$relationId][0], $relationId ); |
||
189 | $instanceId = $this->config->get( 'WBQualityConstraintsInstanceOfRelationId' ); |
||
190 | $subclassId = $this->config->get( 'WBQualityConstraintsSubclassOfRelationId' ); |
||
191 | $instanceOrSubclassId = $this->config->get( 'WBQualityConstraintsInstanceOrSubclassOfRelationId' ); |
||
192 | switch ( $relationEntityId ) { |
||
193 | case $instanceId: |
||
194 | return 'instance'; |
||
195 | case $subclassId: |
||
196 | return 'subclass'; |
||
197 | case $instanceOrSubclassId: |
||
198 | return 'instanceOrSubclass'; |
||
199 | default: |
||
200 | throw new ConstraintParameterException( |
||
201 | ( new ViolationMessage( 'wbqc-violation-message-parameter-oneof' ) ) |
||
202 | ->withEntityId( new PropertyId( $relationId ), Role::CONSTRAINT_PARAMETER_PROPERTY ) |
||
203 | ->withEntityIdList( |
||
204 | [ |
||
205 | new ItemId( $instanceId ), |
||
206 | new ItemId( $subclassId ), |
||
207 | new ItemId( $instanceOrSubclassId ), |
||
208 | ], |
||
209 | Role::CONSTRAINT_PARAMETER_VALUE |
||
210 | ) |
||
211 | ); |
||
212 | } |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Parse a single property ID parameter. |
||
217 | * @param array $snakSerialization |
||
218 | * @param string $parameterId used in error messages |
||
219 | * @throws ConstraintParameterException |
||
220 | * @return PropertyId |
||
221 | */ |
||
222 | private function parsePropertyIdParameter( array $snakSerialization, $parameterId ) { |
||
238 | |||
239 | /** |
||
240 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
241 | * @param string $constraintTypeItemId used in error messages |
||
242 | * |
||
243 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
244 | * @return PropertyId |
||
245 | */ |
||
246 | View Code Duplication | public function parsePropertyParameter( array $constraintParameters, $constraintTypeItemId ) { |
|
260 | |||
261 | private function parseItemIdParameter( PropertyValueSnak $snak, $parameterId ) { |
||
262 | $dataValue = $snak->getDataValue(); |
||
263 | if ( $dataValue instanceof EntityIdValue ) { |
||
264 | $entityId = $dataValue->getEntityId(); |
||
265 | if ( $entityId instanceof ItemId ) { |
||
266 | return ItemIdSnakValue::fromItemId( $entityId ); |
||
267 | } |
||
268 | } |
||
269 | throw new ConstraintParameterException( |
||
270 | ( new ViolationMessage( 'wbqc-violation-message-parameter-item' ) ) |
||
271 | ->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY ) |
||
272 | ->withDataValue( $dataValue, Role::CONSTRAINT_PARAMETER_VALUE ) |
||
273 | ); |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
278 | * @param string $constraintTypeItemId used in error messages |
||
279 | * @param bool $required whether the parameter is required (error if absent) or not ([] if absent) |
||
280 | * @param string|null $parameterId the property ID to use, defaults to 'qualifier of property constraint' |
||
281 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
282 | * @return ItemIdSnakValue[] array of values |
||
283 | */ |
||
284 | public function parseItemsParameter( |
||
323 | |||
324 | /** |
||
325 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
326 | * @param string $constraintTypeItemId used in error messages |
||
327 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
328 | * @return PropertyId[] |
||
329 | */ |
||
330 | public function parsePropertiesParameter( array $constraintParameters, $constraintTypeItemId ) { |
||
354 | |||
355 | /** |
||
356 | * @param array $snakSerialization |
||
357 | * @param string $parameterId |
||
358 | * @throws ConstraintParameterException |
||
359 | * @return DataValue|null |
||
360 | */ |
||
361 | private function parseValueOrNoValueParameter( array $snakSerialization, $parameterId ) { |
||
374 | |||
375 | /** |
||
376 | * @param array $snakSerialization |
||
377 | * @param string $parameterId |
||
378 | * @return DataValue|null |
||
379 | */ |
||
380 | private function parseValueOrNoValueOrNowParameter( array $snakSerialization, $parameterId ) { |
||
388 | |||
389 | /** |
||
390 | * Checks whether there is exactly one non-null quantity with the given unit. |
||
391 | * @param DataValue|null $min |
||
392 | * @param DataValue|null $max |
||
393 | * @param string $unit |
||
394 | * @return bool |
||
395 | */ |
||
396 | private function exactlyOneQuantityWithUnit( DataValue $min = null, DataValue $max = null, $unit ) { |
||
405 | |||
406 | /** |
||
407 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
408 | * @param string $minimumId |
||
409 | * @param string $maximumId |
||
410 | * @param string $constraintTypeItemId used in error messages |
||
411 | * @param string $type 'quantity' or 'time' (can be data type or data value type) |
||
412 | * |
||
413 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
414 | * @return DataValue[] if the parameter is invalid or missing |
||
415 | */ |
||
416 | private function parseRangeParameter( array $constraintParameters, $minimumId, $maximumId, $constraintTypeItemId, $type ) { |
||
453 | |||
454 | /** |
||
455 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
456 | * @param string $constraintTypeItemId used in error messages |
||
457 | * |
||
458 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
459 | * @return DataValue[] a pair of two data values, either of which may be null to signify an open range |
||
460 | */ |
||
461 | public function parseQuantityRangeParameter( array $constraintParameters, $constraintTypeItemId ) { |
||
470 | |||
471 | /** |
||
472 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
473 | * @param string $constraintTypeItemId used in error messages |
||
474 | * |
||
475 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
476 | * @return DataValue[] a pair of two data values, either of which may be null to signify an open range |
||
477 | */ |
||
478 | public function parseTimeRangeParameter( array $constraintParameters, $constraintTypeItemId ) { |
||
487 | |||
488 | /** |
||
489 | * Parse a single string parameter. |
||
490 | * @param array $snakSerialization |
||
491 | * @param string $parameterId |
||
492 | * @throws ConstraintParameterException |
||
493 | * @return string |
||
494 | */ |
||
495 | View Code Duplication | private function parseStringParameter( array $snakSerialization, $parameterId ) { |
|
509 | |||
510 | /** |
||
511 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
512 | * @param string $constraintTypeItemId used in error messages |
||
513 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
514 | * @return string |
||
515 | */ |
||
516 | public function parseNamespaceParameter( array $constraintParameters, $constraintTypeItemId ) { |
||
526 | |||
527 | /** |
||
528 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
529 | * @param string $constraintTypeItemId used in error messages |
||
530 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
531 | * @return string |
||
532 | */ |
||
533 | View Code Duplication | public function parseFormatParameter( array $constraintParameters, $constraintTypeItemId ) { |
|
547 | |||
548 | /** |
||
549 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
550 | * @throws ConstraintParameterException if the parameter is invalid |
||
551 | * @return EntityId[] |
||
552 | */ |
||
553 | public function parseExceptionParameter( array $constraintParameters ) { |
||
567 | |||
568 | /** |
||
569 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
570 | * @throws ConstraintParameterException if the parameter is invalid |
||
571 | * @return string|null 'mandatory', 'suggestion' or null |
||
572 | */ |
||
573 | public function parseConstraintStatusParameter( array $constraintParameters ) { |
||
613 | |||
614 | /** |
||
615 | * Require that $dataValue is a {@link MonolingualTextValue}. |
||
616 | * @param DataValue $dataValue |
||
617 | * @param string $parameterId |
||
618 | * @return void |
||
619 | * @throws ConstraintParameterException |
||
620 | */ |
||
621 | private function requireMonolingualTextParameter( DataValue $dataValue, $parameterId ) { |
||
630 | |||
631 | /** |
||
632 | * Parse a series of monolingual text snaks (serialized) into a map from language code to string. |
||
633 | * |
||
634 | * @param array $snakSerializations |
||
635 | * @param string $parameterId |
||
636 | * @throws ConstraintParameterException if invalid snaks are found or a language has multiple texts |
||
637 | * @return MultilingualTextValue |
||
638 | */ |
||
639 | private function parseMultilingualTextParameter( array $snakSerializations, $parameterId ) { |
||
665 | |||
666 | /** |
||
667 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
668 | * @throws ConstraintParameterException if the parameter is invalid |
||
669 | * @return MultilingualTextValue |
||
670 | */ |
||
671 | public function parseSyntaxClarificationParameter( array $constraintParameters ) { |
||
685 | |||
686 | /** |
||
687 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
688 | * @param string $constraintTypeItemId used in error messages |
||
689 | * @param string[]|null $validScopes a list of Context::TYPE_* constants which are valid where this parameter appears. |
||
690 | * If this is not null and one of the specified scopes is not in this list, a ConstraintParameterException is thrown. |
||
691 | * @throws ConstraintParameterException if the parameter is invalid |
||
692 | * @return string[]|null Context::TYPE_* constants |
||
693 | */ |
||
694 | public function parseConstraintScopeParameter( array $constraintParameters, $constraintTypeItemId, array $validScopes = null ) { |
||
727 | |||
728 | /** |
||
729 | * Turn an item ID into a full unit string (using the concept URI). |
||
730 | * |
||
731 | * @param ItemId $unitId |
||
732 | * @return string unit |
||
733 | */ |
||
734 | private function parseUnitParameter( ItemId $unitId ) { |
||
744 | |||
745 | /** |
||
746 | * Turn an ItemIdSnakValue into a single unit parameter. |
||
747 | * |
||
748 | * @param ItemIdSnakValue $item |
||
749 | * @return UnitsParameter |
||
750 | * @throws ConstraintParameterException |
||
751 | */ |
||
752 | private function parseUnitItem( ItemIdSnakValue $item ) { |
||
771 | |||
772 | /** |
||
773 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
774 | * @param string $constraintTypeItemId used in error messages |
||
775 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
776 | * @return UnitsParameter |
||
777 | */ |
||
778 | public function parseUnitsParameter( array $constraintParameters, $constraintTypeItemId ) { |
||
799 | |||
800 | /** |
||
801 | * Turn an ItemIdSnakValue into a single entity type parameter. |
||
802 | * |
||
803 | * @param ItemIdSnakValue $item |
||
804 | * @return EntityTypesParameter |
||
805 | * @throws ConstraintParameterException |
||
806 | */ |
||
807 | private function parseEntityTypeItem( ItemIdSnakValue $item ) { |
||
855 | |||
856 | /** |
||
857 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
858 | * @param string $constraintTypeItemId used in error messages |
||
859 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
860 | * @return EntityTypesParameter |
||
861 | */ |
||
862 | public function parseEntityTypesParameter( array $constraintParameters, $constraintTypeItemId ) { |
||
884 | |||
885 | /** |
||
886 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
887 | * @throws ConstraintParameterException if the parameter is invalid |
||
888 | * @return PropertyId[] |
||
889 | */ |
||
890 | public function parseSeparatorsParameter( array $constraintParameters ) { |
||
906 | |||
907 | /** |
||
908 | * Turn an ItemIdSnakValue into a single context type parameter. |
||
909 | * |
||
910 | * @param ItemIdSnakValue $item |
||
911 | * @param string $use 'constraint scope' or 'property scope' |
||
912 | * @param string $parameterId used in error messages |
||
913 | * @return string one of the Context::TYPE_* constants |
||
914 | * @throws ConstraintParameterException |
||
915 | */ |
||
916 | private function parseContextTypeItem( ItemIdSnakValue $item, $use, $parameterId ) { |
||
951 | |||
952 | /** |
||
953 | * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()} |
||
954 | * @param string $constraintTypeItemId used in error messages |
||
955 | * @throws ConstraintParameterException if the parameter is invalid or missing |
||
956 | * @return string[] list of Context::TYPE_* constants |
||
957 | */ |
||
958 | public function parsePropertyScopeParameter( array $constraintParameters, $constraintTypeItemId ) { |
||
983 | |||
984 | } |
||
985 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.