/includes/form-tags-manager.php

  1. <?php 
  2.  
  3. function wpcf7_add_form_tag( $tag, $func, $features = '' ) { 
  4. $manager = WPCF7_FormTagsManager::get_instance(); 
  5.  
  6. return $manager->add( $tag, $func, $features ); 
  7.  
  8. function wpcf7_remove_form_tag( $tag ) { 
  9. $manager = WPCF7_FormTagsManager::get_instance(); 
  10.  
  11. return $manager->remove( $tag ); 
  12.  
  13. function wpcf7_replace_all_form_tags( $content ) { 
  14. $manager = WPCF7_FormTagsManager::get_instance(); 
  15.  
  16. return $manager->replace_all( $content ); 
  17.  
  18. function wpcf7_scan_form_tags( $cond = null ) { 
  19. $contact_form = WPCF7_ContactForm::get_current(); 
  20.  
  21. if ( $contact_form ) { 
  22. return $contact_form->scan_form_tags( $cond ); 
  23.  
  24. return array(); 
  25.  
  26. function wpcf7_form_tag_supports( $tag, $feature ) { 
  27. $manager = WPCF7_FormTagsManager::get_instance(); 
  28.  
  29. return $manager->tag_type_supports( $tag, $feature ); 
  30.  
  31. class WPCF7_FormTagsManager { 
  32.  
  33. private static $instance; 
  34.  
  35. private $tag_types = array(); 
  36. private $scanned_tags = null; // Tags scanned at the last time of scan() 
  37.  
  38. private function __construct() {} 
  39.  
  40. public static function get_instance() { 
  41. if ( empty( self::$instance ) ) { 
  42. self::$instance = new self; 
  43.  
  44. return self::$instance; 
  45.  
  46. public function get_scanned_tags() { 
  47. return $this->scanned_tags; 
  48.  
  49. public function add( $tag, $func, $features = '' ) { 
  50. if ( ! is_callable( $func ) ) { 
  51. return; 
  52.  
  53. if ( true === $features ) { // for back-compat 
  54. $features = array( 'name-attr' => true ); 
  55.  
  56. $features = wp_parse_args( $features, array() ); 
  57.  
  58. $tags = array_filter( array_unique( (array) $tag ) ); 
  59.  
  60. foreach ( $tags as $tag ) { 
  61. $tag = $this->sanitize_tag_type( $tag ); 
  62.  
  63. if ( ! $this->tag_type_exists( $tag ) ) { 
  64. $this->tag_types[$tag] = array( 
  65. 'function' => $func,  
  66. 'features' => $features,  
  67. ); 
  68.  
  69. public function tag_type_exists( $tag ) { 
  70. return isset( $this->tag_types[$tag] ); 
  71.  
  72. public function tag_type_supports( $tag, $feature ) { 
  73. if ( isset( $this->tag_types[$tag]['features'] ) ) { 
  74. return ! empty( $this->tag_types[$tag]['features'][$feature] ); 
  75.  
  76. return false; 
  77.  
  78. private function sanitize_tag_type( $tag ) { 
  79. $tag = preg_replace( '/[^a-zA-Z0-9_*]+/', '_', $tag ); 
  80. $tag = rtrim( $tag, '_' ); 
  81. $tag = strtolower( $tag ); 
  82. return $tag; 
  83.  
  84. public function remove( $tag ) { 
  85. unset( $this->tag_types[$tag] ); 
  86.  
  87. public function normalize( $content ) { 
  88. if ( empty( $this->tag_types ) ) { 
  89. return $content; 
  90.  
  91. $content = preg_replace_callback( 
  92. '/' . $this->tag_regex() . '/s',  
  93. array( $this, 'normalize_callback' ),  
  94. $content ); 
  95.  
  96. return $content; 
  97.  
  98. private function normalize_callback( $m ) { 
  99. // allow [[foo]] syntax for escaping a tag 
  100. if ( $m[1] == '[' && $m[6] == ']' ) { 
  101. return $m[0]; 
  102.  
  103. $tag = $m[2]; 
  104.  
  105. $attr = trim( preg_replace( '/[\r\n\t ]+/', ' ', $m[3] ) ); 
  106. $attr = strtr( $attr, array( '<' => '<', '>' => '>' ) ); 
  107.  
  108. $content = trim( $m[5] ); 
  109. $content = str_replace( "\n", '<WPPreserveNewline />', $content ); 
  110.  
  111. $result = $m[1] . '[' . $tag 
  112. . ( $attr ? ' ' . $attr : '' ) 
  113. . ( $m[4] ? ' ' . $m[4] : '' ) 
  114. . ']' 
  115. . ( $content ? $content . '[/' . $tag . ']' : '' ) 
  116. . $m[6]; 
  117.  
  118. return $result; 
  119.  
  120. public function replace_all( $content ) { 
  121. return $this->scan( $content, true ); 
  122.  
  123. public function scan( $content, $replace = false ) { 
  124. $this->scanned_tags = array(); 
  125.  
  126. if ( empty( $this->tag_types ) ) { 
  127. if ( $replace ) { 
  128. return $content; 
  129. } else { 
  130. return $this->scanned_tags; 
  131.  
  132. if ( $replace ) { 
  133. $content = preg_replace_callback( 
  134. '/' . $this->tag_regex() . '/s',  
  135. array( $this, 'replace_callback' ),  
  136. $content ); 
  137.  
  138. return $content; 
  139. } else { 
  140. preg_replace_callback( 
  141. '/' . $this->tag_regex() . '/s',  
  142. array( $this, 'scan_callback' ),  
  143. $content ); 
  144.  
  145. return $this->scanned_tags; 
  146.  
  147. public function filter( $content, $cond ) { 
  148. if ( is_array( $content ) ) { 
  149. $tags = $content; 
  150. } elseif ( is_string( $content ) ) { 
  151. $tags = $this->scan( $content ); 
  152. } else { 
  153. $tags = $this->scanned_tags; 
  154.  
  155. if ( empty( $tags ) ) { 
  156. return array(); 
  157.  
  158. if ( ! is_array( $cond ) || empty( $cond ) ) { 
  159. return $tags; 
  160.  
  161. for ( $i = 0, $size = count( $tags ); $i < $size; $i++ ) { 
  162.  
  163. if ( isset( $cond['type'] ) ) { 
  164. if ( is_string( $cond['type'] ) && ! empty( $cond['type'] ) ) { 
  165. if ( $tags[$i]['type'] != $cond['type'] ) { 
  166. unset( $tags[$i] ); 
  167. continue; 
  168. } elseif ( is_array( $cond['type'] ) ) { 
  169. if ( ! in_array( $tags[$i]['type'], $cond['type'] ) ) { 
  170. unset( $tags[$i] ); 
  171. continue; 
  172.  
  173. if ( isset( $cond['name'] ) ) { 
  174. if ( is_string( $cond['name'] ) && ! empty( $cond['name'] ) ) { 
  175. if ( $tags[$i]['name'] != $cond['name'] ) { 
  176. unset ( $tags[$i] ); 
  177. continue; 
  178. } elseif ( is_array( $cond['name'] ) ) { 
  179. if ( ! in_array( $tags[$i]['name'], $cond['name'] ) ) { 
  180. unset( $tags[$i] ); 
  181. continue; 
  182.  
  183. return array_values( $tags ); 
  184.  
  185. private function tag_regex() { 
  186. $tagnames = array_keys( $this->tag_types ); 
  187. $tagregexp = join( '|', array_map( 'preg_quote', $tagnames ) ); 
  188.  
  189. return '(\[?)' 
  190. . '\[(' . $tagregexp . ')(?:[\r\n\t ](.*?))?(?:[\r\n\t ](\/))?\]' 
  191. . '(?:([^[]*?)\[\/\2\])?' 
  192. . '(\]?)'; 
  193.  
  194. private function replace_callback( $m ) { 
  195. return $this->scan_callback( $m, true ); 
  196.  
  197. private function scan_callback( $m, $replace = false ) { 
  198. // allow [[foo]] syntax for escaping a tag 
  199. if ( $m[1] == '[' && $m[6] == ']' ) { 
  200. return substr( $m[0], 1, -1 ); 
  201.  
  202. $tag = $m[2]; 
  203. $attr = $this->parse_atts( $m[3] ); 
  204.  
  205. $scanned_tag = array( 
  206. 'type' => $tag,  
  207. 'basetype' => trim( $tag, '*' ),  
  208. 'name' => '',  
  209. 'options' => array(),  
  210. 'raw_values' => array(),  
  211. 'values' => array(),  
  212. 'pipes' => null,  
  213. 'labels' => array(),  
  214. 'attr' => '',  
  215. 'content' => '',  
  216. ); 
  217.  
  218. if ( is_array( $attr ) ) { 
  219. if ( is_array( $attr['options'] ) ) { 
  220. if ( $this->tag_type_supports( $tag, 'name-attr' ) 
  221. && ! empty( $attr['options'] ) ) { 
  222. $scanned_tag['name'] = array_shift( $attr['options'] ); 
  223.  
  224. if ( ! wpcf7_is_name( $scanned_tag['name'] ) ) { 
  225. return $m[0]; // Invalid name is used. Ignore this tag. 
  226.  
  227. $scanned_tag['options'] = (array) $attr['options']; 
  228.  
  229. $scanned_tag['raw_values'] = (array) $attr['values']; 
  230.  
  231. if ( WPCF7_USE_PIPE ) { 
  232. $pipes = new WPCF7_Pipes( $scanned_tag['raw_values'] ); 
  233. $scanned_tag['values'] = $pipes->collect_befores(); 
  234. $scanned_tag['pipes'] = $pipes; 
  235. } else { 
  236. $scanned_tag['values'] = $scanned_tag['raw_values']; 
  237.  
  238. $scanned_tag['labels'] = $scanned_tag['values']; 
  239.  
  240. } else { 
  241. $scanned_tag['attr'] = $attr; 
  242.  
  243. $scanned_tag['values'] = array_map( 'trim', $scanned_tag['values'] ); 
  244. $scanned_tag['labels'] = array_map( 'trim', $scanned_tag['labels'] ); 
  245.  
  246. $content = trim( $m[5] ); 
  247. $content = preg_replace( "/<br[\r\n\t ]*\/?>$/m", '', $content ); 
  248. $scanned_tag['content'] = $content; 
  249.  
  250. $scanned_tag = apply_filters( 'wpcf7_form_tag', $scanned_tag, $replace ); 
  251.  
  252. $this->scanned_tags[] = $scanned_tag; 
  253.  
  254. if ( $replace ) { 
  255. $func = $this->tag_types[$tag]['function']; 
  256. return $m[1] . call_user_func( $func, $scanned_tag ) . $m[6]; 
  257. } else { 
  258. return $m[0]; 
  259.  
  260. private function parse_atts( $text ) { 
  261. $atts = array( 'options' => array(), 'values' => array() ); 
  262. $text = preg_replace( "/[\x{00a0}\x{200b}]+/u", " ", $text ); 
  263. $text = stripcslashes( trim( $text ) ); 
  264.  
  265. $pattern = '%^([-+*=0-9a-zA-Z:.!?#$&@_/|\%\r\n\t ]*?)((?:[\r\n\t ]*"[^"]*"|[\r\n\t ]*\'[^\']*\')*)$%'; 
  266.  
  267. if ( preg_match( $pattern, $text, $match ) ) { 
  268. if ( ! empty( $match[1] ) ) { 
  269. $atts['options'] = preg_split( '/[\r\n\t ]+/', trim( $match[1] ) ); 
  270.  
  271. if ( ! empty( $match[2] ) ) { 
  272. preg_match_all( '/"[^"]*"|\'[^\']*\'/', $match[2], $matched_values ); 
  273. $atts['values'] = wpcf7_strip_quote_deep( $matched_values[0] ); 
  274. } else { 
  275. $atts = $text; 
  276.  
  277. return $atts; 
.