note 89092 added to function.preg-match-all

From: Date: Sat, 21 Feb 2009 10:55:15 +0000
Subject: note 89092 added to function.preg-match-all
Groups: php.notes 
Request: Send a blank email to php-notes+get-150914@lists.php.net to get a copy of this message
The power of pregs is limited only by your *imagination* :) I wrote this html2a() function using preg recursive match (?R) which provides quite safe and bulletproof html/xml extraction: <?php function html2a ( $html ) { if ( !preg_match_all( ' @ \<\s*?(\w+)((?:\b(?:\'[^\']*\'|"[^"]*"|[^\>])*)?)\> ((?:(?>[^\<]*)|(?R))*) \<\/\s*?\\1(?:\b[^\>]*)?\> |\<\s*(\w+)(\b(?:\'[^\']*\'|"[^"]*"|[^\>])*)?\/?\> @uxis', $html = trim($html), $m, PREG_OFFSET_CAPTURE | PREG_SET_ORDER) ) return $html; $i = 0; $ret = array(); foreach ($m as $set) { if ( strlen( $val = trim( substr($html, $i, $set[0][1] - $i) ) ) ) $ret[] = $val; $val = $set[1][1] < 0 ? array( 'tag' => strtolower($set[4][0]) ) : array( 'tag' => strtolower($set[1][0]), 'val' => html2a($set[3][0]) ); if ( preg_match_all( ' /(\w+)\s*(?:=\s*(?:"([^"]*)"|\'([^\']*)\'|(\w+)))?/usix ', isset($set[5]) && $set[2][1] < 0 ? $set[5][0] : $set[2][0] ,$attrs, PREG_SET_ORDER ) ) { foreach ($attrs as $a) { $val['attr'][$a[1]]=$a[count($a)-1]; } } $ret[] = $val; $i = $set[0][1]+strlen( $set[0][0] ); } $l = strlen($html); if ( $i < $l ) if ( strlen( $val = trim( substr( $html, $i, $l - $i ) ) ) ) $ret[] = $val; return $ret; } ?> Now let's try it with this example: (there are some really nasty xhtml compliant bugs, but ... we shouldn't worry) <?php $html = <<<EOT some leftover text... < DIV class=noCompliant style = "text-align:left;" > ... and some other ... < dIv > < empty> </ empty> <p> This is yet another text <br > that wasn't <b>compliant</b> too... <br /> </p> <div class="noClass" > this one is better but we don't care anyway </div ><P> <input type= "text" name ='my "name' value = "nothin really." readonly> end of paragraph </p> </Div> </div> some trailing text EOT; $a = html2a($html); //now we will make some neat html out of it echo a2html($a); function a2html ( $a, $in = "" ) { if ( is_array($a) ) { $s = ""; foreach ($a as $t) if ( is_array($t) ) { $attrs=""; if ( isset($t['attr']) ) foreach( $t['attr'] as $k => $v ) $attrs.=" ${k}=".( strpos( $v, '"' )!==false ? "'$v'" : "\"$v\"" ); $s.= $in."<".$t['tag'].$attrs.( isset( $t['val'] ) ? ">\n".a2html( $t['val'], $in." " ).$in."</".$t['tag'] : "/" ).">\n"; } else $s.= $in.$t."\n"; } else { $s = empty($a) ? "" : $in.$a."\n"; } return $s; } ?> This produces: some leftover text... <div class="noCompliant" style="text-align:left;"> ... and some other ... <div> <empty> </empty> <p> This is yet another text <br/> that wasn't <b> compliant </b> too... <br/> </p> <div class="noClass"> this one is better but we don't care anyway </div> <p> <input type="text" name='my "name' value="nothin really." readonly="readonly"/> end of paragraph </p> </div> </div> some trailing text ---- Server IP: 82.177.164.100 Probable Submitter: 85.222.27.82 ---- Manual Page -- http://www.php.net/manual/en/function.preg-match-all.php Edit -- https://master.php.net/note/edit/89092 Del: integrated -- https://master.php.net/note/delete/89092/integrated Del: useless -- https://master.php.net/note/delete/89092/useless Del: bad code -- https://master.php.net/note/delete/89092/bad+code Del: spam -- https://master.php.net/note/delete/89092/spam Del: non-english -- https://master.php.net/note/delete/89092/non-english Del: in docs -- https://master.php.net/note/delete/89092/in+docs Del: other reasons-- https://master.php.net/note/delete/89092 Reject -- https://master.php.net/note/reject/89092 Search -- https://master.php.net/manage/user-notes.php

« previous php.notes (#150914) next »