PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/pirate-forms/inc/PhpFormBuilder.php

https://gitlab.com/em645jn/brochure
PHP | 539 lines | 370 code | 88 blank | 81 comment | 65 complexity | 9998282affefd4444c21c7e7e44d2572 MD5 | raw file
  1. <?php
  2. // v 0.8.6
  3. class PhpFormBuilder {
  4. // Stores all form inputs
  5. private $inputs = array();
  6. // Stores all form attributes
  7. private $form = array();
  8. // Does this form have a submit value?
  9. private $has_submit = false;
  10. /**
  11. * Constructor function to set form action and attributes
  12. *
  13. * @param string $action
  14. * @param bool $args
  15. */
  16. function __construct( $action = '', $args = false ) {
  17. /* if the form has an attachment option change the enctype to multipart/form-data */
  18. $pirateformsopt_attachment_field = pirate_forms_get_key('pirateformsopt_attachment_field');
  19. if( !empty($pirateformsopt_attachment_field) && ($pirateformsopt_attachment_field == 'yes') ) {
  20. $pirate_forms_enctype = 'multipart/form-data';
  21. }
  22. else {
  23. $pirate_forms_enctype = 'application/x-www-form-urlencoded';
  24. }
  25. // Default form attributes
  26. $defaults = array(
  27. 'action' => $action,
  28. 'method' => 'post',
  29. 'enctype' => $pirate_forms_enctype,
  30. 'class' => array(),
  31. 'id' => '',
  32. 'markup' => 'html',
  33. 'novalidate' => false,
  34. 'add_nonce' => false,
  35. 'add_honeypot' => true,
  36. 'form_element' => true,
  37. 'add_submit' => true
  38. );
  39. // Merge with arguments, if present
  40. if ( $args ) {
  41. $settings = array_merge( $defaults, $args );
  42. } // Otherwise, use the defaults wholesale
  43. else {
  44. $settings = $defaults;
  45. }
  46. // Iterate through and save each option
  47. foreach ( $settings as $key => $val ) {
  48. // Try setting with user-passed setting
  49. // If not, try the default with the same key name
  50. if ( ! $this->set_att( $key, $val ) ) {
  51. $this->set_att( $key, $defaults[ $key ] );
  52. }
  53. }
  54. }
  55. /**
  56. * Validate and set form
  57. *
  58. * @param string $key A valid key; switch statement ensures validity
  59. * @param string | bool $val A valid value; validated for each key
  60. *
  61. * @return bool
  62. */
  63. function set_att( $key, $val ) {
  64. switch ( $key ) :
  65. case 'action':
  66. break;
  67. case 'method':
  68. if ( ! in_array( $val, array( 'post', 'get' ) ) ) {
  69. return false;
  70. }
  71. break;
  72. case 'enctype':
  73. if ( ! in_array( $val, array( 'application/x-www-form-urlencoded', 'multipart/form-data' ) ) ) {
  74. return false;
  75. }
  76. break;
  77. case 'markup':
  78. if ( ! in_array( $val, array( 'html', 'xhtml' ) ) ) {
  79. return false;
  80. }
  81. break;
  82. case 'class':
  83. case 'id':
  84. if ( ! $this->_check_valid_attr( $val ) ) {
  85. return false;
  86. }
  87. break;
  88. case 'novalidate':
  89. case 'add_honeypot':
  90. case 'form_element':
  91. case 'add_submit':
  92. if ( ! is_bool( $val ) ) {
  93. return false;
  94. }
  95. break;
  96. case 'add_nonce':
  97. if ( ! is_string( $val ) && ! is_bool( $val ) ) {
  98. return false;
  99. }
  100. break;
  101. default:
  102. return false;
  103. endswitch;
  104. $this->form[ $key ] = $val;
  105. return true;
  106. }
  107. /**
  108. * Add an input field to the form for outputting later
  109. *
  110. * @param string $label
  111. * @param string $args
  112. * @param string $slug
  113. */
  114. function add_input( $label, $args = '', $slug = '' ) {
  115. if ( empty( $args ) ) {
  116. $args = array();
  117. }
  118. // Create a valid id or class attribute
  119. if ( empty( $slug ) ) {
  120. $slug = $this->_make_slug( $label );
  121. }
  122. $defaults = array(
  123. 'type' => 'text',
  124. 'name' => $slug,
  125. 'id' => $slug,
  126. 'label' => $label,
  127. 'value' => '',
  128. 'placeholder' => '',
  129. 'class' => array(),
  130. 'min' => '',
  131. 'max' => '',
  132. 'step' => '',
  133. 'autofocus' => false,
  134. 'checked' => false,
  135. 'selected' => false,
  136. 'required' => false,
  137. 'add_label' => true,
  138. 'options' => array(),
  139. 'wrap_tag' => 'div',
  140. 'wrap_class' => array( 'form_field_wrap' ),
  141. 'wrap_id' => '',
  142. 'wrap_style' => '',
  143. 'before_html' => '',
  144. 'after_html' => '',
  145. 'request_populate' => true
  146. );
  147. // Combined defaults and arguments
  148. // Arguments override defaults
  149. $args = array_merge( $defaults, $args );
  150. $this->inputs[ $slug ] = $args;
  151. }
  152. /**
  153. * Add multiple inputs to the input queue
  154. *
  155. * @param $arr
  156. *
  157. * @return bool
  158. */
  159. function add_inputs( $arr ) {
  160. if ( ! is_array( $arr ) ) {
  161. return false;
  162. }
  163. foreach ( $arr as $field ) {
  164. $this->add_input(
  165. $field[0], isset( $field[1] ) ? $field[1] : '',
  166. isset( $field[2] ) ? $field[2] : ''
  167. );
  168. }
  169. return true;
  170. }
  171. /**
  172. * Build the HTML for the form based on the input queue
  173. *
  174. * @param bool $echo Should the HTML be echoed or returned?
  175. *
  176. * @return string
  177. */
  178. function build_form( $echo = true ) {
  179. $output = '';
  180. if ( $this->form['form_element'] ) {
  181. $output .= '<form method="' . $this->form['method'] . '" ';
  182. if ( ! empty( $this->form['enctype'] ) ) {
  183. $output .= ' enctype="' . $this->form['enctype'] . '"';
  184. }
  185. if ( ! empty( $this->form['action'] ) ) {
  186. $output .= ' action="' . $this->form['action'] . '"';
  187. }
  188. if ( ! empty( $this->form['id'] ) ) {
  189. $output .= ' id="' . $this->form['id'] . '"';
  190. }
  191. if ( count( $this->form['class'] ) > 0 ) {
  192. $output .= $this->_output_classes( $this->form['class'] );
  193. }
  194. if ( $this->form['novalidate'] ) {
  195. $output .= ' novalidate';
  196. }
  197. $output .= '>';
  198. }
  199. // Add honeypot anti-spam field
  200. if ( $this->form['add_honeypot'] ) {
  201. $this->add_input( 'Leave blank to submit', array(
  202. 'name' => 'honeypot',
  203. 'slug' => 'honeypot',
  204. 'id' => 'form_honeypot',
  205. 'wrap_tag' => 'div',
  206. 'wrap_class' => array( 'form_field_wrap', 'hidden' ),
  207. 'wrap_id' => '',
  208. 'wrap_style' => 'display: none',
  209. 'request_populate' => false
  210. ) );
  211. }
  212. // Add a WordPress nonce field
  213. if ( $this->form['add_nonce'] && function_exists( 'wp_create_nonce' ) ) {
  214. $this->add_input( 'WordPress nonce', array(
  215. 'value' => wp_create_nonce( $this->form['add_nonce'] ),
  216. 'add_label' => false,
  217. 'type' => 'hidden',
  218. 'request_populate' => false
  219. ) );
  220. }
  221. // Iterate through the input queue and add input HTML
  222. foreach ( $this->inputs as $val ) :
  223. $min_max_range = $element = $end = $attr = $field = $label_html = '';
  224. // Automatic population of values using $_REQUEST data
  225. if ( $val['request_populate'] && isset( $_REQUEST[ $val['name'] ] ) ) {
  226. // Can this field be populated directly?
  227. if ( ! in_array( $val['type'], array( 'html', 'title', 'radio', 'checkbox', 'select', 'submit' ) ) ) {
  228. $val['value'] = $_REQUEST[ $val['name'] ];
  229. }
  230. }
  231. // Automatic population for checkboxes and radios
  232. if (
  233. $val['request_populate'] &&
  234. ( $val['type'] == 'radio' || $val['type'] == 'checkbox' ) &&
  235. empty( $val['options'] )
  236. ) {
  237. $val['checked'] = isset( $_REQUEST[ $val['name'] ] ) ? true : $val['checked'];
  238. }
  239. switch ( $val['type'] ) {
  240. case 'html':
  241. $element = '';
  242. $end = $val['label'];
  243. break;
  244. case 'title':
  245. $element = '';
  246. $end = '
  247. <h3>' . $val['label'] . '</h3>';
  248. break;
  249. case 'textarea':
  250. $element = 'textarea';
  251. $end = ' class="form-control" placeholder="'.$val['placeholder'].'">' . $val['value'] . '</textarea>';
  252. break;
  253. case 'select':
  254. $element = 'select';
  255. $end .= '>';
  256. foreach ( $val['options'] as $key => $opt ) {
  257. $opt_insert = '';
  258. if (
  259. // Is this field set to automatically populate?
  260. $val['request_populate'] &&
  261. // Do we have $_REQUEST data to use?
  262. isset( $_REQUEST[ $val['name'] ] ) &&
  263. // Are we currently outputting the selected value?
  264. $_REQUEST[ $val['name'] ] === $key
  265. ) {
  266. $opt_insert = ' selected';
  267. // Does the field have a default selected value?
  268. } else if ( $val['selected'] === $key ) {
  269. $opt_insert = ' selected';
  270. }
  271. $end .= '<option value="' . $key . '"' . $opt_insert . '>' . $opt . '</option>';
  272. }
  273. $end .= '</select>';
  274. break;
  275. case 'captcha':
  276. $element = 'div';
  277. $end = ' class="g-recaptcha pirate-forms-g-recaptcha" data-sitekey="' .$val['value'] . '"></div>';
  278. break;
  279. case 'file':
  280. $element = 'input';
  281. $end = ' class="" type="' . $val['type'] . '">';
  282. break;
  283. case 'radio':
  284. case 'checkbox':
  285. // Special case for multiple check boxes
  286. if ( count( $val['options'] ) > 0 ) :
  287. $element = '';
  288. foreach ( $val['options'] as $key => $opt ) {
  289. $slug = $this->_make_slug( $opt );
  290. $end .= sprintf(
  291. '<input type="%s" name="%s[]" value="%s" id="%s"',
  292. $val['type'],
  293. $val['name'],
  294. $key,
  295. $slug
  296. );
  297. if (
  298. // Is this field set to automatically populate?
  299. $val['request_populate'] &&
  300. // Do we have $_REQUEST data to use?
  301. isset( $_REQUEST[ $val['name'] ] ) &&
  302. // Is the selected item(s) in the $_REQUEST data?
  303. in_array( $key, $_REQUEST[ $val['name'] ] )
  304. ) {
  305. $end .= ' checked';
  306. }
  307. $end .= $this->field_close();
  308. $end .= ' <label for="' . $slug . '">' . $opt . '</label>';
  309. }
  310. $label_html = '<div class="checkbox_header">' . $val['label'] . '</div>';
  311. break;
  312. endif;
  313. case 'submit':
  314. $element = 'div class="col-xs-12 col-sm-6 col-lg-6 form_field_wrap contact_submit_wrap"><button';
  315. $end .= ' type="' . $val['type'] . '">' . $val['value'] . '</button></div>';
  316. break;
  317. default :
  318. $element = 'input';
  319. /* don't add a placeholder attribute for input type=hidden */
  320. if( !empty($val['type']) && ($val['type'] == 'hidden' ) ) {
  321. $end .= ' class="form-control" type="' . $val['type'] . '" value="' . $val['value'] . '"';
  322. }
  323. else {
  324. $end .= ' class="form-control" type="' . $val['type'] . '" value="' . $val['value'] . '" placeholder="' . $val['placeholder'] . '"';
  325. }
  326. $end .= $val['checked'] ? ' checked' : '';
  327. $end .= $this->field_close();
  328. break;
  329. }
  330. // Added a submit button, no need to auto-add one
  331. if ( $val['type'] === 'submit' ) {
  332. $this->has_submit = true;
  333. }
  334. // Special number values for range and number types
  335. if ( $val['type'] === 'range' || $val['type'] === 'number' ) {
  336. $min_max_range .= ! empty( $val['min'] ) ? ' min="' . $val['min'] . '"' : '';
  337. $min_max_range .= ! empty( $val['max'] ) ? ' max="' . $val['max'] . '"' : '';
  338. $min_max_range .= ! empty( $val['step'] ) ? ' step="' . $val['step'] . '"' : '';
  339. }
  340. // Add an ID field, if one is present
  341. $id = ! empty( $val['id'] ) ? ' id="' . $val['id'] . '"' : '';
  342. // Output classes
  343. $class = $this->_output_classes( $val['class'] );
  344. // Special HTML5 fields, if set
  345. $attr .= $val['autofocus'] ? ' autofocus' : '';
  346. $attr .= $val['checked'] ? ' checked' : '';
  347. $attr .= $val['required'] ? ' required' : '';
  348. // Build the label
  349. if ( ! empty( $label_html ) ) {
  350. $field .= $label_html;
  351. } elseif ( $val['add_label'] && ! in_array( $val['type'], array( 'hidden', 'submit', 'title', 'html', 'textarea', 'captcha' ) ) ) {
  352. $field .= '<label for="' . $val['id'] . '">' . $val['label'] . '</label>';
  353. }
  354. // An $element was set in the $val['type'] switch statement above so use that
  355. if ( ! empty( $element ) ) {
  356. if ( $val['type'] === 'checkbox' ) {
  357. $field = '
  358. <' . $element . $id . ' name="' . $val['name'] . '"' . $min_max_range . $class . $attr . $end .
  359. $field;
  360. }
  361. elseif ( $val['type'] === 'captcha' ) { /* don't add name attribute to div's holding recaptcha keys */
  362. $field .= '
  363. <' . $element . $id . ' ' . $min_max_range . $class . $attr . $end;
  364. }
  365. else {
  366. $field .= '
  367. <' . $element . $id . ' name="' . $val['name'] . '"' . $min_max_range . $class . $attr . $end;
  368. }
  369. // Not a form element
  370. } else {
  371. $field .= $end;
  372. }
  373. // Parse and create wrap, if needed
  374. if ( $val['type'] != 'hidden' && $val['type'] != 'html' ) :
  375. $wrap_before = $val['before_html'];
  376. if ( ! empty( $val['wrap_tag'] ) ) {
  377. $wrap_before .= '<' . $val['wrap_tag'];
  378. $wrap_before .= count( $val['wrap_class'] ) > 0 ? $this->_output_classes( $val['wrap_class'] ) : '';
  379. $wrap_before .= ! empty( $val['wrap_style'] ) ? ' style="' . $val['wrap_style'] . '"' : '';
  380. $wrap_before .= ! empty( $val['wrap_id'] ) ? ' id="' . $val['wrap_id'] . '"' : '';
  381. $wrap_before .= '>';
  382. }
  383. $wrap_after = $val['after_html'];
  384. if ( ! empty( $val['wrap_tag'] ) ) {
  385. $wrap_after = '</' . $val['wrap_tag'] . '>' . $wrap_after;
  386. }
  387. $output .= $wrap_before . $field . $wrap_after;
  388. else :
  389. $output .= $field;
  390. endif;
  391. endforeach;
  392. // Auto-add submit button
  393. if ( ! $this->has_submit && $this->form['add_submit'] ) {
  394. $output .= '<div class="form_field_wrap"><input type="submit" value="Submit" name="submit"></div>';
  395. }
  396. // Close the form tag if one was added
  397. if ( $this->form['form_element'] ) {
  398. $output .= '</form>';
  399. }
  400. // Output or return?
  401. if ( $echo ) {
  402. echo $output;
  403. } else {
  404. return $output;
  405. }
  406. }
  407. // Easy way to auto-close fields, if necessary
  408. function field_close() {
  409. return $this->form['markup'] === 'xhtml' ? ' />' : '>';
  410. }
  411. // Validates id and class attributes
  412. // TODO: actually validate these things
  413. private function _check_valid_attr( $string ) {
  414. $result = true;
  415. // Check $name for correct characters
  416. // "^[a-zA-Z0-9_-]*$"
  417. return $result;
  418. }
  419. // Create a slug from a label name
  420. private function _make_slug( $string ) {
  421. $result = '';
  422. $result = str_replace( '"', '', $string );
  423. $result = str_replace( "'", '', $result );
  424. $result = str_replace( '_', '-', $result );
  425. $result = preg_replace( '~[\W\s]~', '-', $result );
  426. $result = strtolower( $result );
  427. return $result;
  428. }
  429. // Parses and builds the classes in multiple places
  430. private function _output_classes( $classes ) {
  431. $output = '';
  432. if ( is_array( $classes ) && count( $classes ) > 0 ) {
  433. $output .= ' class="';
  434. foreach ( $classes as $class ) {
  435. $output .= $class . ' ';
  436. }
  437. $output .= '"';
  438. } else if ( is_string( $classes ) ) {
  439. $output .= ' class="' . $classes . '"';
  440. }
  441. return $output;
  442. }
  443. }