| 1 | <?php |
|---|
| 2 | /* |
|---|
| 3 | Plugin Name: Content Tag Interpreter |
|---|
| 4 | Description: Can be used to replace buggy shortcodes |
|---|
| 5 | Version: 0.97 |
|---|
| 6 | Author: Jacob Beauregard |
|---|
| 7 | */ |
|---|
| 8 | |
|---|
| 9 | /** |
|---|
| 10 | * content_tag public interface |
|---|
| 11 | */ |
|---|
| 12 | function add_content_tag_handler($tag,$handler,$priority=10) { |
|---|
| 13 | return add_filter(_content_tag_ref_name($tag),$handler,$priority,3); |
|---|
| 14 | } |
|---|
| 15 | function apply_content_tag_handlers($tag,$content,$attrs) { |
|---|
| 16 | return apply_filters_ref_array(_content_tag_ref_name($tag),array($tag,$content,$attrs)); |
|---|
| 17 | } |
|---|
| 18 | function eval_content_tags($content) { |
|---|
| 19 | return _content_tag_eval($content); |
|---|
| 20 | } |
|---|
| 21 | function has_content_tag_handler($tag,$handler=false) { |
|---|
| 22 | return has_filter(_content_tag_ref_name($tag),$handler); |
|---|
| 23 | } |
|---|
| 24 | function remove_all_content_tag_handlers($tag,$priority=false) { |
|---|
| 25 | return remove_all_filters(_content_tag_ref_name($tag),$priority); |
|---|
| 26 | } |
|---|
| 27 | function remove_content_tag_handler($tag,$handler,$priority=10) { |
|---|
| 28 | return remove_filter(_content_tag_ref_name($tag),$handler,$priority,3); |
|---|
| 29 | } |
|---|
| 30 | |
|---|
| 31 | /** |
|---|
| 32 | * alias to use in filter for given tag name |
|---|
| 33 | */ |
|---|
| 34 | function _content_tag_ref_name($tag) { |
|---|
| 35 | return "_content_tag_".strtolower($tag); |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | /** |
|---|
| 39 | * types of content_tag expressions |
|---|
| 40 | */ |
|---|
| 41 | function _content_tag_types() { |
|---|
| 42 | return array( |
|---|
| 43 | 'esc_lsqbr', |
|---|
| 44 | 'esc_rsqbr', |
|---|
| 45 | 'tag_inline', |
|---|
| 46 | 'tag_open', |
|---|
| 47 | 'tag_close', |
|---|
| 48 | 'attr', |
|---|
| 49 | 'literal', |
|---|
| 50 | 'name', |
|---|
| 51 | 'text' |
|---|
| 52 | ); |
|---|
| 53 | } |
|---|
| 54 | function _content_tag_type_aliases($type) { |
|---|
| 55 | $table = array( |
|---|
| 56 | array('name','tag_name') |
|---|
| 57 | ); |
|---|
| 58 | $aliases = array($type); |
|---|
| 59 | foreach ($table as $entry) { |
|---|
| 60 | if (in_array($type,$entry,true)) { |
|---|
| 61 | $aliases = $entry; |
|---|
| 62 | } |
|---|
| 63 | } |
|---|
| 64 | return $aliases; |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | /* |
|---|
| 68 | * content_tag expressions that are subtypes of other content_tag expressions |
|---|
| 69 | */ |
|---|
| 70 | function _content_tag_sub_root() { |
|---|
| 71 | //top level types |
|---|
| 72 | return array('esc_lsqbr','esc_rsqbr','tag_inline','tag_open','tag_close','text'); |
|---|
| 73 | } |
|---|
| 74 | function _content_tag_sub_tag_inline() { |
|---|
| 75 | return array('tag_name','attr'); |
|---|
| 76 | } |
|---|
| 77 | function _content_tag_sub_tag_open() { |
|---|
| 78 | return array('tag_name','attr'); |
|---|
| 79 | } |
|---|
| 80 | function _content_tag_sub_tag_close() { |
|---|
| 81 | return array('tag_name'); |
|---|
| 82 | } |
|---|
| 83 | function _content_tag_sub_text() { |
|---|
| 84 | return array(); |
|---|
| 85 | } |
|---|
| 86 | function _content_tag_sub_name() { |
|---|
| 87 | return array(); |
|---|
| 88 | } |
|---|
| 89 | function _content_tag_sub_attr() { |
|---|
| 90 | return array('name','literal'); |
|---|
| 91 | } |
|---|
| 92 | function _content_tag_sub_literal() { |
|---|
| 93 | return array(); |
|---|
| 94 | } |
|---|
| 95 | function _content_tag_sub_char_str() { |
|---|
| 96 | return array(); |
|---|
| 97 | } |
|---|
| 98 | function _content_tag_sub_char_ref() { |
|---|
| 99 | return array(); |
|---|
| 100 | } |
|---|
| 101 | function _content_tag_sub_entity_ref() { |
|---|
| 102 | return array(); |
|---|
| 103 | } |
|---|
| 104 | function _content_tag_sub_esc_lsqbr() { |
|---|
| 105 | return array(); |
|---|
| 106 | } |
|---|
| 107 | function _content_tag_sub_esc_rsqbr() { |
|---|
| 108 | return array(); |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | /** |
|---|
| 112 | * returns subtypes of expression type |
|---|
| 113 | */ |
|---|
| 114 | function _content_tag_sub($type='root') { |
|---|
| 115 | if (in_array($type,_content_tag_types(),true) || $type == 'root') { |
|---|
| 116 | $func = "_content_tag_sub_{$type}"; |
|---|
| 117 | $subtypes = call_user_func($func); |
|---|
| 118 | } |
|---|
| 119 | return $subtypes; |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | /** |
|---|
| 123 | * regular expressions to match valid expressions |
|---|
| 124 | */ |
|---|
| 125 | function _content_tag_re_lsqbr() { |
|---|
| 126 | return '\['; |
|---|
| 127 | } |
|---|
| 128 | function _content_tag_re_rsqbr() { |
|---|
| 129 | return '\]'; |
|---|
| 130 | } |
|---|
| 131 | function _content_tag_re_fslash() { |
|---|
| 132 | return '\/'; |
|---|
| 133 | } |
|---|
| 134 | function _content_tag_re_eq() { |
|---|
| 135 | return '='; |
|---|
| 136 | } |
|---|
| 137 | function _content_tag_re_ws() { |
|---|
| 138 | return '\s'; |
|---|
| 139 | } |
|---|
| 140 | function _content_tag_re_name($named=false) { |
|---|
| 141 | $expr = '[A-Za-z][-A-Za-z0-9_:.]*'; |
|---|
| 142 | return $named?"(?P<name>{$expr})":$expr; |
|---|
| 143 | } |
|---|
| 144 | function _content_tag_re_tag_name($named=false) { |
|---|
| 145 | $name = _content_tag_re_name($named); |
|---|
| 146 | return "^{$name}"; |
|---|
| 147 | } |
|---|
| 148 | function _content_tag_re_char_str($named=false) { |
|---|
| 149 | $expr = "[^%&]+"; |
|---|
| 150 | return $named?"(?P<char_str>{$expr})":$expr; |
|---|
| 151 | } |
|---|
| 152 | function _content_tag_re_char_str_sq($named=false) { |
|---|
| 153 | //single quote version for shortcode_re_char_str |
|---|
| 154 | $expr = "[^%&']+"; |
|---|
| 155 | return $named?"(?P<char_str>{$expr})":$expr; |
|---|
| 156 | } |
|---|
| 157 | function _content_tag_re_char_str_dq($named=false) { |
|---|
| 158 | //double quote version for shortcode_re_char_str |
|---|
| 159 | $expr = '[^%&"]+'; |
|---|
| 160 | return $named?"(?P<char_str>{$expr})":$expr; |
|---|
| 161 | } |
|---|
| 162 | function _content_tag_re_char_ref($named=false) { |
|---|
| 163 | $expr = "&#(?:[0-9]+|x[0-9a-fA-F]+);"; |
|---|
| 164 | return $named?"(?P<char_ref>{$expr})":$expr; |
|---|
| 165 | } |
|---|
| 166 | function _content_tag_re_entity_ref($named=false) { |
|---|
| 167 | $name = _content_tag_re_name(); |
|---|
| 168 | $expr = "&{$name};"; |
|---|
| 169 | return $named?"(?P<entity_ref>{$expr})":$expr; |
|---|
| 170 | } |
|---|
| 171 | function _content_tag_re_literal($named=false) { |
|---|
| 172 | $nq = _content_tag_re_literal_nq(); |
|---|
| 173 | $sq = _content_tag_re_literal_sq(); |
|---|
| 174 | $dq = _content_tag_re_literal_dq(); |
|---|
| 175 | $expr = "(?:{$nq}|{$sq}|{$dq})"; |
|---|
| 176 | return $named?"(?P<literal>{$expr})":$expr; |
|---|
| 177 | } |
|---|
| 178 | function _content_tag_re_literal_nq() { |
|---|
| 179 | //literal without quotes |
|---|
| 180 | $expr = "[-a-zA-Z0-9_:.]+"; |
|---|
| 181 | return $expr; |
|---|
| 182 | } |
|---|
| 183 | function _content_tag_re_literal_sq() { |
|---|
| 184 | //literal with single quotes |
|---|
| 185 | $char_str = _content_tag_re_char_str_sq(); |
|---|
| 186 | $char_ref = _content_tag_re_char_ref(); |
|---|
| 187 | $entity_ref = _content_tag_re_entity_ref(); |
|---|
| 188 | $expr = "'(?:{$char_str}|{$char_ref}|{$entity_ref})*'"; |
|---|
| 189 | return $expr; |
|---|
| 190 | } |
|---|
| 191 | function _content_tag_re_literal_dq() { |
|---|
| 192 | //literal with double quotes |
|---|
| 193 | $char_str = _content_tag_re_char_str_dq(); |
|---|
| 194 | $char_ref = _content_tag_re_char_ref(); |
|---|
| 195 | $entity_ref = _content_tag_re_entity_ref(); |
|---|
| 196 | $expr = "\"(?:{$char_str}|{$char_ref}|{$entity_ref})*\""; |
|---|
| 197 | return $expr; |
|---|
| 198 | } |
|---|
| 199 | function _content_tag_re_attr($named=false) { |
|---|
| 200 | $name = _content_tag_re_name(); |
|---|
| 201 | $literal = _content_tag_re_literal(); |
|---|
| 202 | $eq = _content_tag_re_eq(); |
|---|
| 203 | $ws = _content_tag_re_ws(); |
|---|
| 204 | $expr = "{$name}(?:{$ws}*{$eq}{$ws}*{$literal})?"; |
|---|
| 205 | return $named?"(?P<attr>{$expr})":$expr; |
|---|
| 206 | } |
|---|
| 207 | function _content_tag_re_attr_list() { |
|---|
| 208 | $ws = _content_tag_re_ws(); |
|---|
| 209 | $attr = _content_tag_re_attr(); |
|---|
| 210 | return "(?:{$ws}+{$attr})*"; |
|---|
| 211 | } |
|---|
| 212 | function _content_tag_re_esc_lsqbr($named=false) { |
|---|
| 213 | $lsqbr = _content_tag_re_lsqbr(); |
|---|
| 214 | $expr = "{$lsqbr}{$lsqbr}"; |
|---|
| 215 | return $named?"(?P<esc_lsqbr>{$expr})":$expr; |
|---|
| 216 | } |
|---|
| 217 | function _content_tag_re_esc_rsqbr($named=false) { |
|---|
| 218 | $rsqbr = _content_tag_re_rsqbr(); |
|---|
| 219 | $expr = "{$rsqbr}{$rsqbr}"; |
|---|
| 220 | return $named?"(?P<esc_rsqbr>{$expr})":$expr; |
|---|
| 221 | } |
|---|
| 222 | function _content_tag_re_tag_inline($named=false) { |
|---|
| 223 | $lsqbr = _content_tag_re_lsqbr(); |
|---|
| 224 | $rsqbr = _content_tag_re_rsqbr(); |
|---|
| 225 | $fslash = _content_tag_re_fslash(); |
|---|
| 226 | $ws = _content_tag_re_ws(); |
|---|
| 227 | $name = _content_tag_re_name(); |
|---|
| 228 | $attr = _content_tag_re_attr(); |
|---|
| 229 | $attr_list = _content_tag_re_attr_list(); |
|---|
| 230 | $expr = "{$lsqbr}{$ws}*{$name}{$attr_list}{$ws}*{$fslash}{$ws}*{$rsqbr}"; |
|---|
| 231 | return $named?"(?P<tag_inline>{$expr})":$expr; |
|---|
| 232 | } |
|---|
| 233 | function _content_tag_re_tag_open($named=false) { |
|---|
| 234 | $lsqbr = _content_tag_re_lsqbr(); |
|---|
| 235 | $rsqbr = _content_tag_re_rsqbr(); |
|---|
| 236 | $ws = _content_tag_re_ws(); |
|---|
| 237 | $name = _content_tag_re_name(); |
|---|
| 238 | $attr = _content_tag_re_attr(); |
|---|
| 239 | $attr_list = _content_tag_re_attr_list(); |
|---|
| 240 | $expr = "{$lsqbr}{$ws}*{$name}{$attr_list}{$ws}*{$rsqbr}"; |
|---|
| 241 | return $named?"(?P<tag_open>{$expr})":$expr; |
|---|
| 242 | } |
|---|
| 243 | function _content_tag_re_tag_close($named=false) { |
|---|
| 244 | $lsqbr = _content_tag_re_lsqbr(); |
|---|
| 245 | $rsqbr = _content_tag_re_rsqbr(); |
|---|
| 246 | $fslash = _content_tag_re_fslash(); |
|---|
| 247 | $ws = _content_tag_re_ws(); |
|---|
| 248 | $name = _content_tag_re_name(); |
|---|
| 249 | $expr = "{$lsqbr}{$ws}*{$fslash}{$ws}*{$name}{$ws}*{$rsqbr}"; |
|---|
| 250 | return $named?"(?P<tag_close>{$expr})":$expr; |
|---|
| 251 | } |
|---|
| 252 | function _content_tag_re_text($named=false) { |
|---|
| 253 | $lsqbr = _content_tag_re_lsqbr(); |
|---|
| 254 | $rsqbr = _content_tag_re_rsqbr(); |
|---|
| 255 | //for speed, not the largest chunk |
|---|
| 256 | $expr = "(?s:.[^{$lsqbr}{$rsqbr}]*)"; |
|---|
| 257 | return $named?"(?P<text>{$expr})":$expr; |
|---|
| 258 | } |
|---|
| 259 | |
|---|
| 260 | /** |
|---|
| 261 | * cleans tags to prepare for faster parsing (specifically for re_tag_name) |
|---|
| 262 | */ |
|---|
| 263 | function _content_tag_clean_tag_inline($expr) { |
|---|
| 264 | $lsqbr = _content_tag_re_lsqbr(); |
|---|
| 265 | $rsqbr = _content_tag_re_rsqbr(); |
|---|
| 266 | $ws = _content_tag_re_ws(); |
|---|
| 267 | return preg_replace("/^{$lsqbr}{$ws}*/","",$expr); |
|---|
| 268 | } |
|---|
| 269 | function _content_tag_clean_tag_open($expr) { |
|---|
| 270 | $lsqbr = _content_tag_re_lsqbr(); |
|---|
| 271 | $rsqbr = _content_tag_re_rsqbr(); |
|---|
| 272 | $ws = _content_tag_re_ws(); |
|---|
| 273 | return preg_replace("/^{$lsqbr}{$ws}*/","",$expr); |
|---|
| 274 | } |
|---|
| 275 | function _content_tag_clean_tag_close($expr) { |
|---|
| 276 | $lsqbr = _content_tag_re_lsqbr(); |
|---|
| 277 | $rsqbr = _content_tag_re_rsqbr(); |
|---|
| 278 | $fslash = _content_tag_re_fslash(); |
|---|
| 279 | $ws = _content_tag_re_ws(); |
|---|
| 280 | return preg_replace("/^{$lsqbr}{$ws}*{$fslash}{$ws}*/","",$expr); |
|---|
| 281 | } |
|---|
| 282 | function _content_tag_clean_literal($expr) { |
|---|
| 283 | if ($expr[0] == "'" || $expr[0] == '"') { |
|---|
| 284 | $expr = substr($expr,1,strlen($expr)-2); |
|---|
| 285 | } |
|---|
| 286 | return $expr; |
|---|
| 287 | } |
|---|
| 288 | |
|---|
| 289 | /** |
|---|
| 290 | * returns a cleaned version of the expression for type |
|---|
| 291 | */ |
|---|
| 292 | function _content_tag_clean($type,$expr) { |
|---|
| 293 | $func = "_content_tag_clean_{$type}"; |
|---|
| 294 | if (is_callable($func)) { |
|---|
| 295 | $expr = call_user_func($func,$expr); |
|---|
| 296 | } |
|---|
| 297 | return $expr; |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | /** |
|---|
| 301 | * combines shortcode regex of types specified in parameters, returns delimited regex |
|---|
| 302 | */ |
|---|
| 303 | function _content_tag_re_combine() { |
|---|
| 304 | $types = func_get_args(); |
|---|
| 305 | $regexps = array(); |
|---|
| 306 | foreach ($types as $type) { |
|---|
| 307 | $re_func = "_content_tag_re_{$type}"; |
|---|
| 308 | array_push($regexps,call_user_func($re_func,1)); |
|---|
| 309 | } |
|---|
| 310 | $statement = '/' . join('|',$regexps) . '/'; |
|---|
| 311 | return $statement; |
|---|
| 312 | } |
|---|
| 313 | |
|---|
| 314 | /** |
|---|
| 315 | * returns regular expression to match subtypes of an expression (ex. attr => {$name}={$literal}) |
|---|
| 316 | */ |
|---|
| 317 | function _content_tag_re_subtypes($type) { |
|---|
| 318 | $subtypes = _content_tag_sub($type); |
|---|
| 319 | $re = null; |
|---|
| 320 | if (count($subtypes)) { |
|---|
| 321 | $re = call_user_func_array('_content_tag_re_combine',$subtypes); |
|---|
| 322 | } |
|---|
| 323 | return $re; |
|---|
| 324 | } |
|---|
| 325 | |
|---|
| 326 | /** |
|---|
| 327 | * returns a node structure with specified type and expression |
|---|
| 328 | */ |
|---|
| 329 | function _content_tag_init_node($type,$expr) { |
|---|
| 330 | return array( |
|---|
| 331 | 'type' => $type, |
|---|
| 332 | 'expression' => $expr, |
|---|
| 333 | 'name' => null, |
|---|
| 334 | 'attrs' => array(), |
|---|
| 335 | 'content' => '', |
|---|
| 336 | 'children' => array() |
|---|
| 337 | ); |
|---|
| 338 | } |
|---|
| 339 | |
|---|
| 340 | /** |
|---|
| 341 | * builds an expression tree from a content_tag expression |
|---|
| 342 | */ |
|---|
| 343 | function _content_tag_build_tree($expr) { |
|---|
| 344 | $node = is_string($expr)?_content_tag_init_node('root',$expr):$expr; |
|---|
| 345 | $parse_expr = _content_tag_clean($node['type'],$node['expression']); |
|---|
| 346 | $subtypes = _content_tag_sub($node['type']); |
|---|
| 347 | if (count($subtypes)) { |
|---|
| 348 | $re_subtypes = _content_tag_re_subtypes($node['type']); |
|---|
| 349 | preg_match_all($re_subtypes,$parse_expr,$matches,PREG_SET_ORDER); |
|---|
| 350 | foreach ($matches as $match) { |
|---|
| 351 | foreach ($match as $type => $expr) { |
|---|
| 352 | $aliases = _content_tag_type_aliases($type); |
|---|
| 353 | if ($expr && count(array_intersect($aliases,$subtypes))) { |
|---|
| 354 | array_push($node['children'],_content_tag_init_node($type,$expr)); |
|---|
| 355 | } |
|---|
| 356 | } |
|---|
| 357 | } |
|---|
| 358 | } |
|---|
| 359 | $node['children'] = array_map('_content_tag_build_tree',$node['children']); |
|---|
| 360 | return $node; |
|---|
| 361 | } |
|---|
| 362 | |
|---|
| 363 | /** |
|---|
| 364 | * adds a tag to the stack to have content added to |
|---|
| 365 | */ |
|---|
| 366 | function _content_tag_stack_push($tag,&$stack,&$refs) { |
|---|
| 367 | array_push($stack,$tag); |
|---|
| 368 | if (isset($refs[$tag['name']])) { |
|---|
| 369 | $refs[$tag['name']] += 1; |
|---|
| 370 | } else { |
|---|
| 371 | $refs[$tag['name']] = 1; |
|---|
| 372 | } |
|---|
| 373 | } |
|---|
| 374 | |
|---|
| 375 | /** |
|---|
| 376 | * evaluates all stack members above specified member as though they were inline |
|---|
| 377 | * evaluates specified member with own content and content of members above it |
|---|
| 378 | * evaluates nothing if specified member not on stack |
|---|
| 379 | */ |
|---|
| 380 | function _content_tag_stack_fold($name,&$stack,&$refs) { |
|---|
| 381 | if ($name === null || (isset($refs[$name]) && $refs[$name] > 0)) { |
|---|
| 382 | while (count($stack) > 1 && (!isset($tag) || $tag['name'] != $name)) { |
|---|
| 383 | $tag = array_pop($stack); |
|---|
| 384 | if ($tag['name'] === $name) { |
|---|
| 385 | //include intermediate content for matched tag |
|---|
| 386 | $expr = apply_content_tag_handlers($tag['name'],$tag['content'],$tag['attrs']); |
|---|
| 387 | } else { |
|---|
| 388 | //implicitly inline if the tag doesn't match. |
|---|
| 389 | $expr = apply_content_tag_handlers($tag['name'],'',$tag['attrs']).$tag['content']; |
|---|
| 390 | } |
|---|
| 391 | _content_tag_eval_text(array('expression'=>$expr),$stack); |
|---|
| 392 | $refs[$tag['name']] -= 1; |
|---|
| 393 | } |
|---|
| 394 | } |
|---|
| 395 | } |
|---|
| 396 | |
|---|
| 397 | /** |
|---|
| 398 | * evaluation of different content_tag expressions |
|---|
| 399 | */ |
|---|
| 400 | function _content_tag_eval_name($name) { |
|---|
| 401 | return strtolower($name['expression']); |
|---|
| 402 | } |
|---|
| 403 | function _content_tag_eval_char_str($char_str) { |
|---|
| 404 | return $char_str['expression']; |
|---|
| 405 | } |
|---|
| 406 | function _content_tag_eval_literal($literal) { |
|---|
| 407 | //this could alternatively be broken down to |
|---|
| 408 | //entity reference (with subexpression name) |
|---|
| 409 | //character reference |
|---|
| 410 | //character |
|---|
| 411 | $expr = $literal['expression']; |
|---|
| 412 | $expr = _content_tag_clean_literal($expr); |
|---|
| 413 | $expr = html_entity_decode($expr); |
|---|
| 414 | return $expr; |
|---|
| 415 | } |
|---|
| 416 | function _content_tag_eval_attr($attr) { |
|---|
| 417 | $children = $attr['children']; |
|---|
| 418 | $key = _content_tag_eval_name($children[0]); |
|---|
| 419 | if (isset($children[1])) { |
|---|
| 420 | $value = _content_tag_eval_literal($children[1]); |
|---|
| 421 | } else { |
|---|
| 422 | $value = $key; |
|---|
| 423 | } |
|---|
| 424 | return array('key' => $key, 'value' => $value); |
|---|
| 425 | } |
|---|
| 426 | function _content_tag_eval_esc_lsqbr($esc_lsqbr,&$stack) { |
|---|
| 427 | _content_tag_eval_text('[',$stack); |
|---|
| 428 | } |
|---|
| 429 | function _content_tag_eval_esc_rsqbr($esc_rsqbr,&$stack) { |
|---|
| 430 | _content_tag_eval_text(']',$stack); |
|---|
| 431 | } |
|---|
| 432 | function _content_tag_eval_tag($tag) { |
|---|
| 433 | $tag['name'] = _content_tag_eval_name($tag['children'][0]); |
|---|
| 434 | foreach ($tag['children'] as $child) { |
|---|
| 435 | if ($child['type'] == 'attr') { |
|---|
| 436 | $attr = _content_tag_eval_attr($child); |
|---|
| 437 | $tag['attrs'][$attr['key']] = $attr['value']; |
|---|
| 438 | } |
|---|
| 439 | } |
|---|
| 440 | return $tag; |
|---|
| 441 | } |
|---|
| 442 | function _content_tag_eval_tag_inline($tag,&$stack) { |
|---|
| 443 | $tag = _content_tag_eval_tag($tag); |
|---|
| 444 | if (has_content_tag_handler($tag['name'])) { |
|---|
| 445 | $expr = apply_content_tag_handlers($tag['name'],$tag['content'],$tag['attrs']); |
|---|
| 446 | } else { |
|---|
| 447 | $expr = $tag['expression']; |
|---|
| 448 | } |
|---|
| 449 | _content_tag_eval_text(array('expression' => $expr),$stack); |
|---|
| 450 | } |
|---|
| 451 | function _content_tag_eval_tag_open($tag,&$stack,&$refs) { |
|---|
| 452 | $tag = _content_tag_eval_tag($tag); |
|---|
| 453 | if (has_content_tag_handler($tag['name'])) { |
|---|
| 454 | _content_tag_stack_push($tag,$stack,$refs); |
|---|
| 455 | } else { |
|---|
| 456 | _content_tag_eval_text($tag,$stack); |
|---|
| 457 | } |
|---|
| 458 | } |
|---|
| 459 | function _content_tag_eval_tag_close($tag,&$stack,&$refs) { |
|---|
| 460 | $tag['name'] = _content_tag_eval_name($tag['children'][0]); |
|---|
| 461 | if (has_content_tag_handler($tag['name'])) { |
|---|
| 462 | _content_tag_stack_fold($tag['name'],$stack,$refs); |
|---|
| 463 | } else { |
|---|
| 464 | _content_tag_eval_text($tag,$stack); |
|---|
| 465 | } |
|---|
| 466 | } |
|---|
| 467 | function _content_tag_eval_text($text,&$stack) { |
|---|
| 468 | $node = array_pop($stack); |
|---|
| 469 | $node['content'] .= $text['expression']; |
|---|
| 470 | array_push($stack,$node); |
|---|
| 471 | } |
|---|
| 472 | function _content_tag_eval($expr) { |
|---|
| 473 | $stack = array(); |
|---|
| 474 | $refs = array(); |
|---|
| 475 | $root = _content_tag_build_tree($expr); |
|---|
| 476 | array_push($stack,$root); |
|---|
| 477 | $subtypes = _content_tag_sub('root'); |
|---|
| 478 | foreach ($root['children'] as $child) { |
|---|
| 479 | $func = "_content_tag_eval_{$child['type']}"; |
|---|
| 480 | if ($child['type'] == 'tag_inline') { |
|---|
| 481 | _content_tag_eval_tag_inline($child,$stack,$refs); |
|---|
| 482 | } elseif ($child['type'] == 'tag_open') { |
|---|
| 483 | _content_tag_eval_tag_open($child,$stack,$refs); |
|---|
| 484 | } elseif ($child['type'] == 'tag_close') { |
|---|
| 485 | _content_tag_eval_tag_close($child,$stack,$refs); |
|---|
| 486 | } elseif ($child['type'] == 'text') { |
|---|
| 487 | _content_tag_eval_text($child,$stack); |
|---|
| 488 | } elseif ($child['type'] == 'esc_lsqbr') { |
|---|
| 489 | _content_tag_eval_esc_lsqbr($child,$stack); |
|---|
| 490 | } elseif ($child['type'] == 'esc_rsqbr') { |
|---|
| 491 | _content_tag_eval_esc_rsqbr($child,$stack); |
|---|
| 492 | } elseif (in_array($child['type'],$subtypes,true)) { |
|---|
| 493 | call_user_func($func,$child); |
|---|
| 494 | } |
|---|
| 495 | } |
|---|
| 496 | _content_tag_stack_fold(null,$stack,$refs); |
|---|
| 497 | return $stack[0]['content']; |
|---|
| 498 | } |
|---|
| 499 | |
|---|
| 500 | add_filter('the_content','eval_content_tags',11); |
|---|