44 private readonly array $evaluators = [],
46 $this->hookRunner =
new HookRunner( $hookContainer );
47 $this->options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
64 $isPerformingRequest = !defined(
'MW_NO_SESSION' ) && $user->equals( $this->context->getUser() );
66 $conditionType = $cond[0];
67 $args = array_slice( $cond, 1 );
69 foreach ( $this->evaluators as $evaluator ) {
70 $result = $evaluator->checkCondition( $conditionType, $args, $user, $isPerformingRequest );
71 if ( $result !==
null ) {
78 $args = array_slice( $cond, 1 );
79 $this->hookRunner->onUserRequirementsCondition( $conditionType, $args, $user, $isPerformingRequest, $result );
81 $isCurrentWiki = ( $user->
getWikiId() === false ) || WikiMap::isCurrentWikiId( $user->
getWikiId() );
82 if ( $isPerformingRequest && $isCurrentWiki ) {
87 $userObject = $this->userFactory->newFromUserIdentity( $user );
88 $this->hookRunner->onAutopromoteCondition( $conditionType, $args, $userObject, $result );
91 if ( $result ===
null ) {
92 throw new InvalidArgumentException(
93 "Unrecognized condition $type in UserRequirementsCondition!"
126 if ( !$this->userRequirementsConditionValidator->isValid( $cond ) ) {
130 $skippedConditions = [];
131 if ( !$usePrivateConditions ) {
133 $skippedConditions = array_fill_keys( $skippedConditions,
true );
136 return $this->recursivelyCheckConditionInternal( $cond, $user, $skippedConditions );
150 private function recursivelyCheckConditionInternal( $cond, UserIdentity $user, array $skippedConditions ): ?bool {
151 if ( is_array( $cond ) && count( $cond ) >= 2 && in_array( $cond[0], self::VALID_OPS ) ) {
155 if ( $cond[0] ===
'&' ) {
157 foreach ( array_slice( $cond, 1 ) as $subcond ) {
158 $result = $this->recursivelyCheckConditionInternal( $subcond, $user, $skippedConditions );
159 if ( $result ===
false ) {
162 $hasNulls = $hasNulls || $result ===
null;
165 return $hasNulls ? null :
true;
169 if ( $cond[0] ===
'|' ) {
171 foreach ( array_slice( $cond, 1 ) as $subcond ) {
172 $result = $this->recursivelyCheckConditionInternal( $subcond, $user, $skippedConditions );
173 if ( $result ===
true ) {
176 $hasNulls = $hasNulls || $result ===
null;
179 return $hasNulls ? null :
false;
183 if ( $cond[0] ===
'^' ) {
184 $result1 = $this->recursivelyCheckConditionInternal( $cond[1], $user, $skippedConditions );
185 $result2 = $this->recursivelyCheckConditionInternal( $cond[2], $user, $skippedConditions );
186 if ( $result1 ===
null || $result2 ===
null ) {
189 return $result1 xor $result2;
193 if ( $cond[0] ===
'!' ) {
195 foreach ( array_slice( $cond, 1 ) as $subcond ) {
196 $result = $this->recursivelyCheckConditionInternal( $subcond, $user, $skippedConditions );
197 if ( $result ===
true ) {
200 $hasNulls = $hasNulls || $result ===
null;
203 return $hasNulls ? null :
true;
208 if ( !is_array( $cond ) ) {
213 if ( count( $cond ) < 1 ) {
217 if ( isset( $skippedConditions[$cond[0]] ) ) {
221 return $this->checkCondition( $cond, $user );
231 $allPrivateConditions = $this->options->get(
MainConfigNames::UserRequirementsPrivateConditions );
232 $allConditionsUsed = $this->extractConditions( $cond );
233 $privateConditionsUsed = array_intersect( $allPrivateConditions, $allConditionsUsed );
234 return array_values( $privateConditionsUsed );
247 $result = $this->extractConditionsInternal( $cond );
248 return array_values( array_unique( $result ) );
257 private function extractConditionsInternal( $cond ): array {
258 if ( $cond === [] ) {
263 if ( is_array( $cond ) ) {
265 if ( in_array( $op, self::VALID_OPS ) ) {
266 foreach ( array_slice( $cond, 1 ) as $subcond ) {
267 $result = array_merge(
268 $result, $this->extractConditionsInternal( $subcond ) );