Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 1 | <?php |
| 2 | |
Dominic Sauer | 630cb1e | 2015-05-18 10:16:20 +0200 | [diff] [blame] | 3 | namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Checker; |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 4 | |
Lucas Werkmeister | c2aa62f | 2017-04-07 19:00:07 +0200 | [diff] [blame] | 5 | use Config; |
Thiemo Mättig | f330481 | 2016-03-22 17:13:31 +0100 | [diff] [blame] | 6 | use Wikibase\DataModel\Entity\EntityIdValue; |
Lucas Werkmeister | 2d0fe16 | 2017-06-07 13:27:38 +0200 | [diff] [blame] | 7 | use Wikibase\DataModel\Entity\ItemId; |
Umherirrender | 1bc588d | 2019-09-27 19:26:23 +0200 | [diff] [blame] | 8 | use Wikibase\DataModel\Entity\StatementListProvidingEntity; |
addshore | 44a87b9 | 2015-08-14 12:32:12 +0200 | [diff] [blame] | 9 | use Wikibase\DataModel\Services\Lookup\EntityLookup; |
Marius Hoch | 33931d8 | 2018-11-02 02:54:21 +0100 | [diff] [blame] | 10 | use Wikibase\DataModel\Services\Lookup\UnresolvedEntityRedirectException; |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 11 | use Wikibase\DataModel\Snak\PropertyValueSnak; |
addshore | 2a295b0 | 2020-04-30 15:48:03 +0100 | [diff] [blame] | 12 | use Wikibase\DataModel\Statement\Statement; |
Dominic Sauer | 630cb1e | 2015-05-18 10:16:20 +0200 | [diff] [blame] | 13 | use WikibaseQuality\ConstraintReport\Constraint; |
| 14 | use WikibaseQuality\ConstraintReport\ConstraintCheck\ConstraintChecker; |
Lucas Werkmeister | f50361f | 2017-08-01 17:24:27 +0200 | [diff] [blame] | 15 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context; |
Lucas Werkmeister | b849eec | 2017-07-07 12:46:59 +0200 | [diff] [blame] | 16 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException; |
Lucas Werkmeister | 03b74c6 | 2017-06-26 12:47:52 +0200 | [diff] [blame] | 17 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterParser; |
Lucas Werkmeister | 8f9b7b3 | 2017-06-12 15:20:37 +0200 | [diff] [blame] | 18 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\SparqlHelperException; |
Dominic Sauer | 630cb1e | 2015-05-18 10:16:20 +0200 | [diff] [blame] | 19 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\TypeCheckerHelper; |
addshore | 2a295b0 | 2020-04-30 15:48:03 +0100 | [diff] [blame] | 20 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage; |
| 21 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult; |
Lucas Werkmeister | 6ec3d2a | 2017-07-31 18:20:59 +0200 | [diff] [blame] | 22 | use WikibaseQuality\ConstraintReport\Role; |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 23 | |
| 24 | /** |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 25 | * @author BP2014N1 |
Lucas Werkmeister | 5129b6d | 2018-03-03 01:36:39 +0100 | [diff] [blame] | 26 | * @license GPL-2.0-or-later |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 27 | */ |
| 28 | class ValueTypeChecker implements ConstraintChecker { |
| 29 | |
| 30 | /** |
Lucas Werkmeister | 03b74c6 | 2017-06-26 12:47:52 +0200 | [diff] [blame] | 31 | * @var ConstraintParameterParser |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 32 | */ |
Lucas Werkmeister | 2d0fe16 | 2017-06-07 13:27:38 +0200 | [diff] [blame] | 33 | private $constraintParameterParser; |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 34 | |
| 35 | /** |
| 36 | * @var EntityLookup |
| 37 | */ |
| 38 | private $entityLookup; |
| 39 | |
| 40 | /** |
| 41 | * @var TypeCheckerHelper |
| 42 | */ |
| 43 | private $typeCheckerHelper; |
| 44 | |
Lucas Werkmeister | c2aa62f | 2017-04-07 19:00:07 +0200 | [diff] [blame] | 45 | /** |
| 46 | * @var Config |
| 47 | */ |
| 48 | private $config; |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 49 | |
Lucas Werkmeister | 2d0fe16 | 2017-06-07 13:27:38 +0200 | [diff] [blame] | 50 | public function __construct( |
| 51 | EntityLookup $lookup, |
Lucas Werkmeister | 03b74c6 | 2017-06-26 12:47:52 +0200 | [diff] [blame] | 52 | ConstraintParameterParser $constraintParameterParser, |
Lucas Werkmeister | 2d0fe16 | 2017-06-07 13:27:38 +0200 | [diff] [blame] | 53 | TypeCheckerHelper $typeCheckerHelper, |
| 54 | Config $config |
| 55 | ) { |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 56 | $this->entityLookup = $lookup; |
Lucas Werkmeister | 2d0fe16 | 2017-06-07 13:27:38 +0200 | [diff] [blame] | 57 | $this->constraintParameterParser = $constraintParameterParser; |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 58 | $this->typeCheckerHelper = $typeCheckerHelper; |
Lucas Werkmeister | c2aa62f | 2017-04-07 19:00:07 +0200 | [diff] [blame] | 59 | $this->config = $config; |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | /** |
Lucas Werkmeister | cccebd1 | 2018-01-08 16:36:55 +0100 | [diff] [blame] | 63 | * @codeCoverageIgnore This method is purely declarative. |
| 64 | */ |
| 65 | public function getSupportedContextTypes() { |
Lucas Werkmeister | 537b527 | 2021-08-25 10:49:00 +0200 | [diff] [blame] | 66 | return self::ALL_CONTEXT_TYPES_SUPPORTED; |
Lucas Werkmeister | cccebd1 | 2018-01-08 16:36:55 +0100 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | /** |
| 70 | * @codeCoverageIgnore This method is purely declarative. |
| 71 | */ |
| 72 | public function getDefaultContextTypes() { |
Lucas Werkmeister | 537b527 | 2021-08-25 10:49:00 +0200 | [diff] [blame] | 73 | return Context::ALL_CONTEXT_TYPES; |
Lucas Werkmeister | cccebd1 | 2018-01-08 16:36:55 +0100 | [diff] [blame] | 74 | } |
| 75 | |
Lucas Werkmeister | 44eedce | 2021-08-19 16:56:16 +0200 | [diff] [blame] | 76 | /** @codeCoverageIgnore This method is purely declarative. */ |
| 77 | public function getSupportedEntityTypes() { |
| 78 | return self::ALL_ENTITY_TYPES_SUPPORTED; |
| 79 | } |
| 80 | |
Lucas Werkmeister | cccebd1 | 2018-01-08 16:36:55 +0100 | [diff] [blame] | 81 | /** |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 82 | * Checks 'Value type' constraint. |
| 83 | * |
Lucas Werkmeister | f50361f | 2017-08-01 17:24:27 +0200 | [diff] [blame] | 84 | * @param Context $context |
Jonas Keutel | 5cd8d88 | 2015-05-05 16:44:32 +0200 | [diff] [blame] | 85 | * @param Constraint $constraint |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 86 | * |
Lucas Werkmeister | d9377e8 | 2018-01-10 17:06:56 +0100 | [diff] [blame] | 87 | * @throws ConstraintParameterException |
Lucas Werkmeister | 8f9b7b3 | 2017-06-12 15:20:37 +0200 | [diff] [blame] | 88 | * @throws SparqlHelperException if the checker uses SPARQL and the query times out or some other error occurs |
Lucas Werkmeister | d9377e8 | 2018-01-10 17:06:56 +0100 | [diff] [blame] | 89 | * @return CheckResult |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 90 | */ |
Lucas Werkmeister | f50361f | 2017-08-01 17:24:27 +0200 | [diff] [blame] | 91 | public function checkConstraint( Context $context, Constraint $constraint ) { |
| 92 | if ( $context->getSnakRank() === Statement::RANK_DEPRECATED ) { |
Lucas Werkmeister | 82bde5c | 2023-07-26 12:44:56 +0200 | [diff] [blame^] | 93 | return new CheckResult( $context, $constraint, CheckResult::STATUS_DEPRECATED ); |
Lucas Werkmeister | 9892581 | 2017-07-12 13:34:15 +0200 | [diff] [blame] | 94 | } |
| 95 | |
Jonas Keutel | de83ac4 | 2015-05-13 16:10:20 +0200 | [diff] [blame] | 96 | $constraintParameters = $constraint->getConstraintParameters(); |
Lucas Werkmeister | cb8298e | 2019-04-02 20:25:02 +0200 | [diff] [blame] | 97 | $constraintTypeItemId = $constraint->getConstraintTypeItemId(); |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 98 | |
Lucas Werkmeister | cb8298e | 2019-04-02 20:25:02 +0200 | [diff] [blame] | 99 | $classes = $this->constraintParameterParser->parseClassParameter( |
| 100 | $constraintParameters, |
| 101 | $constraintTypeItemId |
| 102 | ); |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 103 | |
Lucas Werkmeister | cb8298e | 2019-04-02 20:25:02 +0200 | [diff] [blame] | 104 | $relation = $this->constraintParameterParser->parseRelationParameter( |
| 105 | $constraintParameters, |
| 106 | $constraintTypeItemId |
| 107 | ); |
Lucas Werkmeister | 153883b | 2018-01-17 15:17:44 +0100 | [diff] [blame] | 108 | $relationIds = []; |
| 109 | if ( $relation === 'instance' || $relation === 'instanceOrSubclass' ) { |
| 110 | $relationIds[] = $this->config->get( 'WBQualityConstraintsInstanceOfId' ); |
| 111 | } |
| 112 | if ( $relation === 'subclass' || $relation === 'instanceOrSubclass' ) { |
| 113 | $relationIds[] = $this->config->get( 'WBQualityConstraintsSubclassOfId' ); |
Jonas Keutel | de83ac4 | 2015-05-13 16:10:20 +0200 | [diff] [blame] | 114 | } |
tamslo | 495f7a8 | 2015-06-17 16:54:21 +0200 | [diff] [blame] | 115 | |
Lucas Werkmeister | f50361f | 2017-08-01 17:24:27 +0200 | [diff] [blame] | 116 | $snak = $context->getSnak(); |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 117 | |
Lucas Werkmeister | f50361f | 2017-08-01 17:24:27 +0200 | [diff] [blame] | 118 | if ( !$snak instanceof PropertyValueSnak ) { |
Lucas Werkmeister | 09008e1 | 2017-08-02 11:26:18 +0200 | [diff] [blame] | 119 | // nothing to check |
Lucas Werkmeister | 82bde5c | 2023-07-26 12:44:56 +0200 | [diff] [blame^] | 120 | return new CheckResult( $context, $constraint, CheckResult::STATUS_COMPLIANCE ); |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 121 | } |
| 122 | |
Lucas Werkmeister | f50361f | 2017-08-01 17:24:27 +0200 | [diff] [blame] | 123 | $dataValue = $snak->getDataValue(); |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 124 | |
| 125 | /* |
| 126 | * error handling: |
| 127 | * type of $dataValue for properties with 'Value type' constraint has to be 'wikibase-entityid' |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 128 | */ |
Umherirrender | 9912098 | 2019-09-23 21:41:10 +0200 | [diff] [blame] | 129 | if ( !$dataValue instanceof EntityIdValue ) { |
Lucas Werkmeister | bed8658 | 2018-01-31 15:51:03 +0100 | [diff] [blame] | 130 | $message = ( new ViolationMessage( 'wbqc-violation-message-value-needed-of-type' ) ) |
Lucas Werkmeister | cb8298e | 2019-04-02 20:25:02 +0200 | [diff] [blame] | 131 | ->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM ) |
Lucas Werkmeister | bed8658 | 2018-01-31 15:51:03 +0100 | [diff] [blame] | 132 | ->withDataValueType( 'wikibase-entityid' ); |
Lucas Werkmeister | 82bde5c | 2023-07-26 12:44:56 +0200 | [diff] [blame^] | 133 | return new CheckResult( $context, $constraint, CheckResult::STATUS_VIOLATION, $message ); |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 134 | } |
Thiemo Mättig | f330481 | 2016-03-22 17:13:31 +0100 | [diff] [blame] | 135 | |
Marius Hoch | 33931d8 | 2018-11-02 02:54:21 +0100 | [diff] [blame] | 136 | try { |
| 137 | $item = $this->entityLookup->getEntity( $dataValue->getEntityId() ); |
| 138 | } catch ( UnresolvedEntityRedirectException $e ) { |
| 139 | // Edge case (double redirect): Pretend the entity doesn't exist |
| 140 | $item = null; |
| 141 | } |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 142 | |
Umherirrender | 1bc588d | 2019-09-27 19:26:23 +0200 | [diff] [blame] | 143 | if ( !( $item instanceof StatementListProvidingEntity ) ) { |
Lucas Werkmeister | 61c0104 | 2018-01-26 17:10:56 +0100 | [diff] [blame] | 144 | $message = new ViolationMessage( 'wbqc-violation-message-value-entity-must-exist' ); |
Lucas Werkmeister | 82bde5c | 2023-07-26 12:44:56 +0200 | [diff] [blame^] | 145 | return new CheckResult( $context, $constraint, CheckResult::STATUS_VIOLATION, $message ); |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 146 | } |
| 147 | |
| 148 | $statements = $item->getStatements(); |
| 149 | |
Lucas Werkmeister | 5707723 | 2017-11-14 18:57:42 +0100 | [diff] [blame] | 150 | $result = $this->typeCheckerHelper->hasClassInRelation( |
| 151 | $statements, |
Lucas Werkmeister | 153883b | 2018-01-17 15:17:44 +0100 | [diff] [blame] | 152 | $relationIds, |
Lucas Werkmeister | 5707723 | 2017-11-14 18:57:42 +0100 | [diff] [blame] | 153 | $classes |
| 154 | ); |
| 155 | |
| 156 | if ( $result->getBool() ) { |
Lucas Werkmeister | f91f883 | 2018-01-26 16:19:22 +0100 | [diff] [blame] | 157 | $message = null; |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 158 | $status = CheckResult::STATUS_COMPLIANCE; |
| 159 | } else { |
Lucas Werkmeister | 58e313e | 2017-05-02 16:46:37 +0200 | [diff] [blame] | 160 | $message = $this->typeCheckerHelper->getViolationMessage( |
Lucas Werkmeister | f50361f | 2017-08-01 17:24:27 +0200 | [diff] [blame] | 161 | $context->getSnak()->getPropertyId(), |
Lucas Werkmeister | 58e313e | 2017-05-02 16:46:37 +0200 | [diff] [blame] | 162 | $item->getId(), |
| 163 | $classes, |
| 164 | 'valueType', |
| 165 | $relation |
| 166 | ); |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 167 | $status = CheckResult::STATUS_VIOLATION; |
| 168 | } |
| 169 | |
Lucas Werkmeister | 82bde5c | 2023-07-26 12:44:56 +0200 | [diff] [blame^] | 170 | return ( new CheckResult( $context, $constraint, $status, $message ) ) |
Lucas Werkmeister | 8f81f95 | 2017-12-18 18:10:45 +0100 | [diff] [blame] | 171 | ->withMetadata( $result->getMetadata() ); |
Jonas Keutel | 0ff150f | 2015-04-27 17:24:21 +0200 | [diff] [blame] | 172 | } |
tamslo | 495f7a8 | 2015-06-17 16:54:21 +0200 | [diff] [blame] | 173 | |
Lucas Werkmeister | b849eec | 2017-07-07 12:46:59 +0200 | [diff] [blame] | 174 | public function checkConstraintParameters( Constraint $constraint ) { |
| 175 | $constraintParameters = $constraint->getConstraintParameters(); |
Lucas Werkmeister | cb8298e | 2019-04-02 20:25:02 +0200 | [diff] [blame] | 176 | $constraintTypeItemId = $constraint->getConstraintTypeItemId(); |
Lucas Werkmeister | b849eec | 2017-07-07 12:46:59 +0200 | [diff] [blame] | 177 | $exceptions = []; |
| 178 | try { |
Lucas Werkmeister | cb8298e | 2019-04-02 20:25:02 +0200 | [diff] [blame] | 179 | $this->constraintParameterParser->parseClassParameter( |
| 180 | $constraintParameters, |
| 181 | $constraintTypeItemId |
| 182 | ); |
Lucas Werkmeister | b849eec | 2017-07-07 12:46:59 +0200 | [diff] [blame] | 183 | } catch ( ConstraintParameterException $e ) { |
| 184 | $exceptions[] = $e; |
| 185 | } |
| 186 | try { |
Lucas Werkmeister | cb8298e | 2019-04-02 20:25:02 +0200 | [diff] [blame] | 187 | $this->constraintParameterParser->parseRelationParameter( |
| 188 | $constraintParameters, |
| 189 | $constraintTypeItemId |
| 190 | ); |
Lucas Werkmeister | b849eec | 2017-07-07 12:46:59 +0200 | [diff] [blame] | 191 | } catch ( ConstraintParameterException $e ) { |
| 192 | $exceptions[] = $e; |
| 193 | } |
| 194 | return $exceptions; |
| 195 | } |
| 196 | |
Thiemo Mättig | f026052 | 2016-03-03 15:32:33 +0100 | [diff] [blame] | 197 | } |