3 * Holder for stripped items when parsing wiki markup.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
25 * @todo document, briefly.
35 protected $circularRefGuard;
37 protected $highestDepth = 0;
38 protected $expandSize = 0;
40 protected $depthLimit = 20;
41 protected $sizeLimit = 5000000;
46 * @param Parser|null $parser
47 * @param array $options
49 public function __construct( Parser
$parser = null, $options = [] ) {
54 $this->regex
= '/' . Parser
::MARKER_PREFIX
. "([^\x7f<>&'\"]+)" . Parser
::MARKER_SUFFIX
. '/';
55 $this->circularRefGuard
= [];
56 $this->parser
= $parser;
58 if ( isset( $options['depthLimit'] ) ) {
59 $this->depthLimit
= $options['depthLimit'];
61 if ( isset( $options['sizeLimit'] ) ) {
62 $this->sizeLimit
= $options['sizeLimit'];
67 * Add a nowiki strip item
68 * @param string $marker
69 * @param string|Closure $value
71 public function addNoWiki( $marker, $value ) {
72 $this->addItem( 'nowiki', $marker, $value );
76 * @param string $marker
77 * @param string|Closure $value
79 public function addGeneral( $marker, $value ) {
80 $this->addItem( 'general', $marker, $value );
85 * @param-taint $type none
86 * @param string $marker
87 * @param-taint $marker none
88 * @param string|Closure $value
89 * @param-taint $value exec_html
91 protected function addItem( $type, $marker, $value ) {
92 if ( !preg_match( $this->regex
, $marker, $m ) ) {
93 throw new InvalidArgumentException( "Invalid marker: $marker" );
96 $this->data
[$type][$m[1]] = $value;
100 * @param string $text
103 public function unstripGeneral( $text ) {
104 return $this->unstripType( 'general', $text );
108 * @param string $text
111 public function unstripNoWiki( $text ) {
112 return $this->unstripType( 'nowiki', $text );
116 * @param string $text
117 * @param callable $callback
120 public function replaceNoWikis( string $text, callable
$callback ): string {
122 if ( !count( $this->data
['nowiki'] ) ) {
126 $callback = function ( $m ) use ( $callback ) {
128 if ( isset( $this->data
['nowiki'][$marker] ) ) {
129 $value = $this->data
['nowiki'][$marker];
130 if ( $value instanceof Closure
) {
134 $this->expandSize +
= strlen( $value );
135 if ( $this->expandSize
> $this->sizeLimit
) {
136 return $this->getLimitationWarning( 'unstrip-size', $this->sizeLimit
);
139 return call_user_func( $callback, $value );
145 return preg_replace_callback( $this->regex
, $callback, $text );
149 * @param string $text
152 public function unstripBoth( $text ) {
153 $text = $this->unstripType( 'general', $text );
154 $text = $this->unstripType( 'nowiki', $text );
159 * @param string $type
160 * @param string $text
163 protected function unstripType( $type, $text ) {
165 if ( !count( $this->data
[$type] ) ) {
169 $callback = function ( $m ) use ( $type ) {
171 if ( isset( $this->data
[$type][$marker] ) ) {
172 if ( isset( $this->circularRefGuard
[$marker] ) ) {
173 return $this->getWarning( 'parser-unstrip-loop-warning' );
176 if ( $this->depth
> $this->highestDepth
) {
177 $this->highestDepth
= $this->depth
;
179 if ( $this->depth
>= $this->depthLimit
) {
180 return $this->getLimitationWarning( 'unstrip-depth', $this->depthLimit
);
183 $value = $this->data
[$type][$marker];
184 if ( $value instanceof Closure
) {
188 $this->expandSize +
= strlen( $value );
189 if ( $this->expandSize
> $this->sizeLimit
) {
190 return $this->getLimitationWarning( 'unstrip-size', $this->sizeLimit
);
193 $this->circularRefGuard
[$marker] = true;
195 $ret = $this->unstripType( $type, $value );
197 unset( $this->circularRefGuard
[$marker] );
205 $text = preg_replace_callback( $this->regex
, $callback, $text );
210 * Get warning HTML and register a limitation warning with the parser
212 * @param string $type
213 * @param int|string $max
216 private function getLimitationWarning( $type, $max = '' ) {
217 if ( $this->parser
) {
218 $this->parser
->limitationWarn( $type, $max );
220 return $this->getWarning( "$type-warning", $max );
226 * @param string $message
227 * @param int|string $max
230 private function getWarning( $message, $max = '' ) {
231 return '<span class="error">' .
232 wfMessage( $message )
233 ->numParams( $max )->inContentLanguage()->text() .
238 * Get an array of parameters to pass to ParserOutput::setLimitReportData()
240 * @internal Should only be called by Parser
243 public function getLimitReport() {
245 [ 'limitreport-unstrip-depth',
251 [ 'limitreport-unstrip-size',
261 * Remove any strip markers found in the given text.
263 * @param string $text
266 public function killMarkers( $text ) {
267 return preg_replace( $this->regex
, '', $text );