PluginProbe ʕ •ᴥ•ʔ
Search Regex / trunk
Search Regex vtrunk
trunk 1.4.12 1.4.13 1.4.14 1.4.15 1.4.16 2.0 2.0.1 2.1 2.2 2.2.1 2.3 2.3.1 2.3.2 2.3.3 2.4 2.4.1 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1 3.1.1 3.1.2 3.2 3.3 3.3.0 3.3.1 3.4 3.4.1 3.4.2
search-regex / includes / search / class-text.php
search-regex / includes / search Last commit date
class-column.php 6 months ago class-flags.php 6 months ago class-preset.php 1 month ago class-result.php 1 month ago class-search.php 6 months ago class-text.php 6 months ago class-totals.php 6 months ago
class-text.php
213 lines
1 <?php
2
3 namespace SearchRegex\Search;
4
5 use SearchRegex\Context;
6
7 /**
8 * Represents a single match
9 *
10 * @phpstan-type MatchTextJson array{
11 * pos_id: int,
12 * context_offset: int,
13 * match: string,
14 * replacement: string|null,
15 * captures: string[]
16 * }
17 */
18 class Text {
19 /**
20 * Position ID
21 *
22 * @readonly
23 */
24 private int $pos_id;
25
26 /**
27 * Matched string
28 *
29 * @readonly
30 */
31 private string $match;
32
33 /**
34 * Context offset
35 */
36 private int $context_offset = 0;
37
38 /**
39 * Replacement
40 */
41 private ?string $replacement = null;
42
43 /**
44 * Array of captured data
45 *
46 * @var string[]
47 */
48 private array $captures;
49
50 /**
51 * Create a Match given the matched phrase, the match offset, and what the match is replaced with
52 *
53 * @param string $match The matched phrased. Typical the search phrase unless a regular expression search.
54 * @param int $match_offset The offset within the column.
55 * @param string $replacement The replaced value, if one is supplied.
56 */
57 public function __construct( $match, $match_offset = 0, $replacement = null ) {
58 $this->pos_id = intval( $match_offset, 10 );
59 $this->match = "$match";
60 $this->replacement = $replacement;
61 $this->captures = [];
62 }
63
64 /**
65 * Set the replacement text for this match
66 *
67 * @param string $replacement Replacement text.
68 * @return void
69 */
70 public function set_replacement( $replacement ) {
71 $this->replacement = $replacement;
72 }
73
74 /**
75 * Add a regular expression capture value
76 *
77 * @param string $capture Captured value.
78 * @return void
79 */
80 public function add_capture( $capture ) {
81 $this->captures[] = $capture;
82 }
83
84 /**
85 * Get match position
86 *
87 * @return int Position
88 */
89 public function get_position() {
90 return $this->pos_id;
91 }
92
93 /**
94 * Get matched text
95 *
96 * @return string Matched text
97 */
98 public function get_matched_text() {
99 return $this->match;
100 }
101
102 /**
103 * Set the context offset - the offset within the context that this match belongs to
104 *
105 * @param int $context_offset The context offset.
106 * @return void
107 */
108 public function set_context( $context_offset ) {
109 $this->context_offset = $context_offset;
110 }
111
112 /**
113 * Convert this match to JSON
114 *
115 * @return MatchTextJson JSON
116 */
117 public function to_json() {
118 return [
119 'pos_id' => $this->pos_id,
120 'context_offset' => $this->context_offset,
121 'match' => $this->match,
122 'replacement' => $this->replacement,
123 'captures' => $this->captures,
124 ];
125 }
126
127 /**
128 * Encode a search as a regular expression
129 *
130 * @param string $search Search phrase.
131 * @param Flags $flags Is this regular expression.
132 * @return string Encoded search phrase
133 */
134 public static function get_pattern( $search, Flags $flags ) {
135 $pattern = \preg_quote( $search, '@' );
136
137 if ( $flags->is_regex() ) {
138 $pattern = str_replace( '@', '\\@', $search );
139 }
140
141 $pattern = '@' . $pattern . '@';
142
143 if ( $flags->is_case_insensitive() ) {
144 $pattern .= 'i';
145 }
146
147 // UTF-8 support
148 return $pattern . 'u';
149 }
150
151 /**
152 * Get all matches for a search phrase on a column
153 *
154 * @param string $search The search phrase.
155 * @param Flags $flags Any search flags.
156 * @param array<int, string|null> $replacements A matching set of replacements.
157 * @param string $column_value The content to match.
158 * @return array<int, Context\Type\Text> Array of Match contexts
159 */
160 public static function get_all( $search, Flags $flags, array $replacements, $column_value ) {
161 $pattern = self::get_pattern( $search, $flags );
162 $contexts = [];
163
164 if ( \preg_match_all( $pattern, $column_value, $searches, PREG_OFFSET_CAPTURE ) > 0 ) {
165 $current_context = new Context\Type\Text( $search, $flags );
166 $current_context->set_type( Context\Value_Type::get( $column_value ) );
167 $contexts[] = $current_context;
168
169 // Go through each search match and create a Match
170 foreach ( $searches[0] as $match_pos => $match ) {
171 // Adjust for UTF8 strings
172 $pos = mb_strlen( substr( $column_value, 0, $match[1] ), 'utf-8' );
173
174 // Create a match
175 $match = new self( $match[0], $pos, $replacements[ $match_pos ] ?? null );
176
177 // Add any captures
178 foreach ( array_slice( $searches, 1 ) as $capture ) {
179 $match->add_capture( $capture[ $match_pos ][0] );
180 }
181
182 // Is the match within range of the current context
183 if ( ! $current_context->is_within_context( $match ) ) {
184 // No - create a new context
185 $current_context = new Context\Type\Text( $search, $flags );
186 $current_context->set_type( Context\Value_Type::get( $column_value ) );
187 $current_context->set_context_id( count( $contexts ) );
188 $contexts[] = $current_context;
189 }
190
191 // Add the match to the context
192 $current_context->add_match( $match, $column_value );
193 }
194 }
195
196 return $contexts;
197 }
198
199 /**
200 * Return the replacement at the matches position of the given text.
201 *
202 * @param string $text Text value.
203 * @return string The $text value, with the replacement inserted at the Match position
204 */
205 public function replace_at_position( $text ) {
206 if ( $this->replacement !== null ) {
207 return mb_substr( $text, 0, $this->pos_id, 'UTF-8' ) . $this->replacement . mb_substr( $text, $this->pos_id + mb_strlen( $this->match, 'UTF-8' ), null, 'UTF-8' );
208 }
209
210 return $text;
211 }
212 }
213