64 public function add( $name ): void {
65 $this->names[] = $name;
66 $this->hash = $this->baseRegex = $this->regex =
null;
75 if ( $this->hash === null ) {
76 $this->hash = [ 0 => [], 1 => [] ];
77 foreach ( $this->names as $name ) {
78 $magic = $this->factory->get( $name );
79 $case = intval( $magic->isCaseSensitive() );
80 foreach ( $magic->getSynonyms() as $syn ) {
82 $syn = $this->factory->getContentLanguage()->lc( $syn );
84 $this->hash[$case][$syn] = $name;
102 public function getBaseRegex(
bool $capture =
true,
string $delimiter =
'/' ): array {
103 if ( $capture && $delimiter ===
'/' && $this->baseRegex !== null ) {
104 return $this->baseRegex;
106 $regex = [ 0 => [], 1 => [] ];
107 foreach ( $this->names as $name ) {
108 $magic = $this->factory->get( $name );
109 $case = $magic->isCaseSensitive() ? 1 : 0;
110 foreach ( $magic->getSynonyms() as $i => $syn ) {
113 $it = strtr( $i,
'0123456789',
'abcdefghij' );
114 $groupName = $it .
'_' . $name;
115 $group =
'(?P<' . $groupName .
'>' . preg_quote( $syn, $delimiter ) .
')';
116 $regex[$case][] = $group;
118 $regex[$case][] = preg_quote( $syn, $delimiter );
122 '@phan-var array<int,string[]> $regex';
123 foreach ( $regex as $case => &$re ) {
124 $re = count( $re ) ? implode(
'|', $re ) :
'(?!)';
129 '@phan-var array<int,string> $regex';
131 if ( $capture && $delimiter ===
'/' ) {
132 $this->baseRegex = $regex;
142 private function getRegex(): array {
143 if ( $this->regex === null ) {
145 $base = $this->getBaseRegex(
true,
'/' );
146 foreach ( $base as $case => $re ) {
147 $this->regex[$case] =
"/$re/JS";
151 $this->regex[0] .=
'u';
161 private function getRegexStart(): array {
163 $base = $this->getBaseRegex(
true,
'/' );
164 foreach ( $base as $case => $re ) {
165 $newRegex[$case] =
"/^(?:$re)/JS";
178 private function getVariableStartToEndRegex(): array {
180 $base = $this->getBaseRegex(
true,
'/' );
181 foreach ( $base as $case => $re ) {
182 $newRegex[$case] = str_replace(
'\$1',
'(.*?)',
"/^(?:$re)$/JS" );
205 private function parseMatch( array
$matches ): array {
207 foreach (
$matches as $key => $match ) {
208 if ( $magicName !==
null ) {
214 return [ $magicName,
$matches[$key + 1] ?? false ];
217 if ( $match !==
'' && $key !== 0 ) {
218 $parts = explode(
'_', $key, 2 );
219 if ( !isset( $parts[1] ) ) {
220 throw new LogicException(
'Unexpected group name' );
222 $magicName = $parts[1];
225 throw new LogicException(
'Unexpected $m array with no match' );
236 $regexes = $this->getVariableStartToEndRegex();
237 foreach ( $regexes as $regex ) {
239 if ( preg_match( $regex, $text, $m ) ) {
240 return $this->parseMatch( $m );
243 return [
false, false ];
254 $hash = $this->getHash();
255 if ( isset( $hash[1][$text] ) ) {
256 return $hash[1][$text];
258 $lc = $this->factory->getContentLanguage()->lc( $text );
259 return $hash[0][$lc] ??
false;
274 $regexes = $this->getRegex();
275 $res = preg_replace_callback( $regexes,
function ( $m ) use ( &$found ) {
276 [ $name, $param ] = $this->parseMatch( $m );
277 $found[$name] = $param;
281 if ( $res ===
null ) {
282 $error = preg_last_error();
283 $errorText = preg_last_error_msg();
284 LoggerFactory::getInstance(
'parser' )->warning(
'preg_match_all error: {code} {errorText}', [
288 'errorText' => $errorText
290 if ( $error !== PREG_BAD_UTF8_ERROR ) {
291 throw new LogicException(
"preg_match_all error $error: $errorText" );
310 $regexes = $this->getRegexStart();
311 foreach ( $regexes as $regex ) {
312 if ( preg_match( $regex, $text, $m ) ) {
313 [ $id, ] = $this->parseMatch( $m );
314 if ( strlen( $m[0] ) >= strlen( $text ) ) {
317 $text = substr( $text, strlen( $m[0] ) );
327class_alias( MagicWordArray::class,
'MagicWordArray' );
if(!defined('MW_SETUP_CALLBACK'))