$data Raw rules. */ public static function from_array( array $data ): self { $set = new self(); if ( count( $data ) > self::MAX_RULES ) { ServerLog::write( sprintf( 'Shiftora: a find and replace list of %d rules was cut to %d.', count( $data ), self::MAX_RULES ) ); $data = array_slice( $data, 0, self::MAX_RULES ); } foreach ( $data as $rule_data ) { // A find value of "0" is a valid literal string; only a truly empty // find is meaningless. if ( '' !== (string) ( $rule_data['find'] ?? '' ) ) { $set->add( Rule::from_array( $rule_data ) ); } } return $set; } /** * Add a rule to the set. */ public function add( Rule $rule ): void { $this->rules[] = $rule; } /** * The rules that may touch a table, in declaration order. An empty table name * means no table to scope against, so every rule applies. * * @param string $prefix Site prefix, so a bare scope entry resolves. * * @return Rule[] */ public function rules_for_table( string $table, string $prefix = '' ): array { if ( '' === $table ) { return $this->rules; } return array_values( array_filter( $this->rules, static fn( Rule $rule ): bool => $rule->applies_to_table( $table, $prefix ) ) ); } /** * Whether the rule set has any rules. */ public function is_empty(): bool { return empty( $this->rules ); } /** * Export rules for JSON storage. * * @return array */ public function to_array(): array { return array_map( fn( Rule $r ) => $r->to_array(), $this->rules ); } }