Fix for bug 31374: reintroduce recursive unstrip as in r27667, somehow omitted during...
[lhc/web/wiklou.git] / includes / parser / StripState.php
1 <?php
2
3 /**
4 * @todo document, briefly.
5 * @ingroup Parser
6 */
7 class StripState {
8 protected $prefix;
9 protected $data;
10 protected $regex;
11
12 protected $tempType, $tempMergePrefix;
13
14 function __construct( $prefix ) {
15 $this->prefix = $prefix;
16 $this->data = array(
17 'nowiki' => array(),
18 'general' => array()
19 );
20 $this->regex = "/{$this->prefix}([^\x7f]+)" . Parser::MARKER_SUFFIX . '/';
21 }
22
23 /**
24 * Add a nowiki strip item
25 * @param $marker
26 * @param $value
27 */
28 function addNoWiki( $marker, $value ) {
29 $this->addItem( 'nowiki', $marker, $value );
30 }
31
32 /**
33 * @param $marker
34 * @param $value
35 */
36 function addGeneral( $marker, $value ) {
37 $this->addItem( 'general', $marker, $value );
38 }
39
40 /**
41 * @throws MWException
42 * @param $type
43 * @param $marker
44 * @param $value
45 */
46 protected function addItem( $type, $marker, $value ) {
47 if ( !preg_match( $this->regex, $marker, $m ) ) {
48 throw new MWException( "Invalid marker: $marker" );
49 }
50
51 $this->data[$type][$m[1]] = $value;
52 }
53
54 /**
55 * @param $text
56 * @return mixed
57 */
58 function unstripGeneral( $text ) {
59 return $this->unstripType( 'general', $text );
60 }
61
62 /**
63 * @param $text
64 * @return mixed
65 */
66 function unstripNoWiki( $text ) {
67 return $this->unstripType( 'nowiki', $text );
68 }
69
70 /**
71 * @param $text
72 * @return mixed
73 */
74 function unstripBoth( $text ) {
75 $text = $this->unstripType( 'general', $text );
76 $text = $this->unstripType( 'nowiki', $text );
77 return $text;
78 }
79
80 /**
81 * @param $type
82 * @param $text
83 * @return mixed
84 */
85 protected function unstripType( $type, $text ) {
86 // Shortcut
87 if ( !count( $this->data[$type] ) ) {
88 return $text;
89 }
90
91 wfProfileIn( __METHOD__ );
92 $this->tempType = $type;
93 do {
94 $oldText = $text;
95 $text = preg_replace_callback( $this->regex, array( $this, 'unstripCallback' ), $text );
96 } while ( $text !== $oldText );
97 $this->tempType = null;
98 wfProfileOut( __METHOD__ );
99 return $text;
100 }
101
102 /**
103 * @param $m array
104 * @return array
105 */
106 protected function unstripCallback( $m ) {
107 if ( isset( $this->data[$this->tempType][$m[1]] ) ) {
108 return $this->data[$this->tempType][$m[1]];
109 } else {
110 return $m[0];
111 }
112 }
113
114 /**
115 * Get a StripState object which is sufficient to unstrip the given text.
116 * It will contain the minimum subset of strip items necessary.
117 *
118 * @param $text string
119 *
120 * @return StripState
121 */
122 function getSubState( $text ) {
123 $subState = new StripState( $this->prefix );
124 $pos = 0;
125 while ( true ) {
126 $startPos = strpos( $text, $this->prefix, $pos );
127 $endPos = strpos( $text, Parser::MARKER_SUFFIX, $pos );
128 if ( $startPos === false || $endPos === false ) {
129 break;
130 }
131
132 $endPos += strlen( Parser::MARKER_SUFFIX );
133 $marker = substr( $text, $startPos, $endPos - $startPos );
134 if ( !preg_match( $this->regex, $marker, $m ) ) {
135 continue;
136 }
137
138 $key = $m[1];
139 if ( isset( $this->data['nowiki'][$key] ) ) {
140 $subState->data['nowiki'][$key] = $this->data['nowiki'][$key];
141 } elseif ( isset( $this->data['general'][$key] ) ) {
142 $subState->data['general'][$key] = $this->data['general'][$key];
143 }
144 $pos = $endPos;
145 }
146 return $subState;
147 }
148
149 /**
150 * Merge another StripState object into this one. The strip marker keys
151 * will not be preserved. The strings in the $texts array will have their
152 * strip markers rewritten, the resulting array of strings will be returned.
153 *
154 * @param $otherState StripState
155 * @param $texts Array
156 * @return Array
157 */
158 function merge( $otherState, $texts ) {
159 $mergePrefix = Parser::getRandomString();
160
161 foreach ( $otherState->data as $type => $items ) {
162 foreach ( $items as $key => $value ) {
163 $this->data[$type]["$mergePrefix-$key"] = $value;
164 }
165 }
166
167 $this->tempMergePrefix = $mergePrefix;
168 $texts = preg_replace_callback( $otherState->regex, array( $this, 'mergeCallback' ), $texts );
169 $this->tempMergePrefix = null;
170 return $texts;
171 }
172
173 protected function mergeCallback( $m ) {
174 $key = $m[1];
175 return "{$this->prefix}{$this->tempMergePrefix}-$key" . Parser::MARKER_SUFFIX;
176 }
177 }
178