PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/xoops_trust_path/modules/pico/class/FormProcessByHtml.class.php

http://xoopscube-modules.googlecode.com/
PHP | 581 lines | 452 code | 95 blank | 34 comment | 107 complexity | 8e0b2c00300f733a822f4168f75c899d MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, AGPL-1.0
  1. <?php
  2. class FormProcessByHtml
  3. {
  4. var $fields = array() ;
  5. var $form_html = '' ;
  6. var $column_separator = ',' ;
  7. var $types = array( 'int' , 'double' , 'singlebytes' , 'email' , 'telephone' ) ;
  8. var $validator_dir ;
  9. function FormProcessByHtml()
  10. {
  11. return $this->__construct() ;
  12. }
  13. function __construct()
  14. {
  15. // register validators
  16. $this->validator_dir = dirname(__FILE__) . '/validators' ;
  17. if( $handler = @opendir( $this->validator_dir ) ) {
  18. while( ( $file = readdir( $handler ) ) !== false ) {
  19. if( substr( $file , 0 , 1 ) == '.' ) continue ;
  20. $this->types[] = substr( $file , 0 , -4 ) ;
  21. }
  22. }
  23. }
  24. function setFieldsByForm( $form_html , $ignore_names = array() )
  25. {
  26. // initialize
  27. $this->fields = array() ;
  28. $this->form_html = $form_html ;
  29. // get name="..." from the form
  30. preg_match_all( '#<[^>]+name=([\'"]?)([^\'" ]+)\\1[^>]*>#iU' , $this->form_html , $matches , PREG_SET_ORDER ) ;
  31. $tags = array() ;
  32. foreach( $matches as $match ) {
  33. $tags[] = array( $match[0] , $match[2] ) ;
  34. }
  35. // parse HTML and file label
  36. foreach( $tags as $tag_and_name ) {
  37. list( $tag , $field_name_raw ) = $tag_and_name ;
  38. // judge whether the field is array or not (TODO)
  39. $count = 1 ; // number of controllers with the "name"
  40. if( ( $pos = strpos( $field_name_raw , '[' ) ) > 0 ) {
  41. $field_name = substr( $field_name_raw , 0 , $pos ) ;
  42. $array_type = 'linear' ;
  43. } else {
  44. $field_name = $field_name_raw ;
  45. $array_type = '' ;
  46. }
  47. // ignore the form with specified name like cancel button
  48. if( in_array( $field_name , $ignore_names ) ) continue ;
  49. // options for radio/checkbox or multiple text with the same "name"
  50. $options = array() ;
  51. if( isset( $this->fields[ $field_name ] ) ) {
  52. $this->fields[ $field_name ]['count'] ++ ;
  53. $this->fields[ $field_name ]['tags'][] = $tag ;
  54. $this->fields[ $field_name ]['options'][] = $this->fildValueFromTag( $tag ) ;
  55. continue ;
  56. } else {
  57. $options[] = $this->fildValueFromTag( $tag ) ;
  58. }
  59. // tag kind
  60. if( strncasecmp( $tag , '<textarea' , 9 ) === 0 ) {
  61. $tag_kind = 'textarea' ;
  62. } else if( strncasecmp( $tag , '<select' , 7 ) === 0 ) {
  63. $tag_kind = 'select' ;
  64. if( stristr( $tag , 'multiple' ) ) {
  65. $count = 0x10000 ; // large enough
  66. }
  67. } else if( stristr( $tag , 'type="checkbox"' ) ) {
  68. $tag_kind = 'checkbox' ;
  69. } else if( stristr( $tag , 'type="radio"' ) ) {
  70. $tag_kind = 'radio' ;
  71. } else if( stristr( $tag , 'type="hidden"' ) ) {
  72. $tag_kind = 'hidden' ;
  73. } else if( stristr( $tag , 'type="text"' ) ) {
  74. $tag_kind = 'text' ;
  75. } else if( stristr( $tag , 'type="submit"' ) ) {
  76. $tag_kind = 'submit' ;
  77. } else {
  78. continue ;
  79. }
  80. // get id of the tag
  81. $id = '' ;
  82. if( preg_match( '/id\s*\=\s*"([^"]+)"/' , $tag , $regs ) ) {
  83. $id = trim( $regs[1] ) ;
  84. }
  85. // get title of the tag
  86. $title = '' ;
  87. if( preg_match( '/title\s*\=\s*"([^"]+)"/' , $tag , $regs ) ) {
  88. $title = trim( $regs[1] ) ;
  89. }
  90. // get classes of the tag
  91. $classes = array() ;
  92. if( preg_match( '/class\s*\=\s*"([^"]+)"/' , $tag , $regs ) ) {
  93. $classes = array_map( 'trim' , explode( ' ' , trim( $regs[1] ) ) ) ;
  94. }
  95. // required
  96. $required = in_array( 'required' , $classes ) ? true : false ;
  97. // type judgement
  98. $type = 'string' ;
  99. foreach( $this->types as $eachtype ) {
  100. if( in_array( $eachtype , $classes ) ) {
  101. $type = $eachtype ;
  102. break ;
  103. }
  104. }
  105. // get label as title of the field
  106. $label = empty( $title ) ? $field_name : $title ;
  107. if( ! in_array( $tag_kind , array( 'radio' , 'checkbox' ) ) ) {
  108. // search <label> for other than radio/checkbox
  109. if( preg_match( '#for\s*\=\s*([\'"]?)'.preg_quote($id).'\\1\>(.*)\<\/label\>#iU' , $this->form_html , $regs ) ) {
  110. $label = strip_tags( @$regs[2] ) ;
  111. }
  112. } else {
  113. // search the nearest <legend> for radio/checkbox
  114. foreach( preg_split( '#</fieldset>#i' , $form_html ) as $fieldsetblock ) {
  115. if( strstr( $fieldsetblock , $tag ) && preg_match( '#<legend[^>]*>([^<]+)</legend>#' , $fieldsetblock , $sub_regs ) ) {
  116. $label = strip_tags( @$sub_regs[1] ) ;
  117. break ;
  118. }
  119. }
  120. }
  121. $this->fields[ $field_name ] = array(
  122. 'field_name_raw' => $field_name_raw ,
  123. 'tags' => array( $tag ) ,
  124. 'tag_kind' => $tag_kind ,
  125. 'id' => $id ,
  126. 'classes' => $classes ,
  127. 'label' => $label ,
  128. 'required' => $required ,
  129. 'array_type' => $array_type ,
  130. 'type' => $type ,
  131. 'options' => $options ,
  132. 'count' => $count ,
  133. 'errors' => array() ,
  134. ) ;
  135. }
  136. }
  137. function importSession( $session_data , $check_fields = true )
  138. {
  139. if( $check_fields ) {
  140. foreach( array_keys( $this->fields ) as $field_name ) {
  141. if( isset( $session_data[ $field_name ] ) ) {
  142. $this->fields[ $field_name ] = $session_data[ $field_name ] ;
  143. }
  144. }
  145. } else {
  146. $this->fields = $session_data ;
  147. }
  148. }
  149. function fetchPost( $input_encoding = null )
  150. {
  151. $myts =& MyTextSanitizer::getInstance() ;
  152. $_post = $this->_getPostAsArray( $input_encoding ) ;
  153. foreach( $this->fields as $field_name => $attribs ) {
  154. $value = @$_post[ $field_name ] ;
  155. // array checks (TODO)
  156. $value4reqcheck = $value ;
  157. if( is_array( $value ) ) {
  158. if( $attribs['count'] <= 1 ) {
  159. $value = strval( array_pop( $value ) ) ;
  160. $value4reqcheck = $value ;
  161. } else {
  162. $value = array_slice( $value , 0 , $attribs['count'] ) ;
  163. $value4reqcheck = implode( '' , $value ) ;
  164. }
  165. }
  166. // missing required
  167. if( $attribs['required'] == true && ( $value4reqcheck === '' || $value4reqcheck === null ) ) {
  168. $this->fields[ $field_name ]['errors'][] = in_array( $attribs['tag_kind'] , array( 'text' , 'textarea' ) ) ? 'missing required' : 'missing selected' ;
  169. }
  170. $value = $this->_validateValueRecursive( $value , $field_name , $attribs ) ;
  171. $this->fields[ $field_name ]['value'] = $value ;
  172. }
  173. return $this->fields ;
  174. }
  175. function _validateValueRecursive( $value , $fn = null , $at = null )
  176. {
  177. static $field_name , $attribs ;
  178. if( ! empty( $fn ) ) $field_name = $fn ;
  179. if( ! empty( $at ) ) $attribs = $at ;
  180. if( is_array( $value ) ) {
  181. return array_map( array( $this , '_validateValueRecursive' ) , $value ) ;
  182. } else {
  183. // tag_kind validation (range check)
  184. // select
  185. if( $attribs['tag_kind'] == 'select' && ! $this->validateSelectOption( $attribs['tags'][0] , $value ) ) {
  186. $this->fields[ $field_name ]['errors'][] = 'invalid option' ;
  187. }
  188. // radio/checkbox
  189. if( in_array( $attribs['tag_kind'] , array( 'radio' , 'checkbox' ) ) && ! empty( $value ) ) {
  190. if( ! in_array( $value , $attribs['options'] ) ) {
  191. $this->fields[ $field_name ]['errors'][] = 'invalid option' ;
  192. }
  193. }
  194. // hidden
  195. if( $attribs['tag_kind'] == 'hidden' ) {
  196. $value = @$attribs['options'][0] ;
  197. }
  198. // type checks & conversions
  199. switch( $attribs['type'] ) {
  200. case 'int' :
  201. $value = $this->convertZenToHan( trim( $value ) ) ;
  202. if( ! empty( $value ) ) {
  203. if( is_numeric( $value ) ) {
  204. $value = intval( $value ) ;
  205. } else {
  206. $this->fields[ $field_name ]['errors'][] = 'invalid number' ;
  207. }
  208. }
  209. break ;
  210. case 'double' :
  211. $value = $this->convertZenToHan( trim( $value ) ) ;
  212. if( ! empty( $value ) ) {
  213. if( is_numeric( $value ) ) {
  214. $value = doubleval( $value ) ;
  215. } else {
  216. $this->fields[ $field_name ]['errors'][] = 'invalid number' ;
  217. }
  218. }
  219. break ;
  220. case 'telephone' :
  221. $value = $this->convertZenToHan( trim( $value ) ) ;
  222. if( ! empty( $value ) && preg_match( '/[^()0-9+.-]/' , $value ) ) {
  223. $this->fields[ $field_name ]['errors'][] = 'invalid general' ;
  224. }
  225. break ;
  226. case 'email' :
  227. $value = $this->convertZenToHan( $value ) ;
  228. if( ! empty( $value ) && ! $this->checkEmailAddress( $value ) ) {
  229. $this->fields[ $field_name ]['errors'][] = 'invalid email' ;
  230. }
  231. break ;
  232. case 'singlebytes' :
  233. $value = $this->convertZenToHan( $value ) ;
  234. break ;
  235. default :
  236. if( in_array( $attribs['type'] , $this->types ) ) {
  237. // custom validator
  238. require_once $this->validator_dir.'/'.$attribs['type'].'.php' ;
  239. $func_name = 'formprocess_validator_'.$attribs['type'] ;
  240. $value = $func_name( $value , $field_name , $this ) ;
  241. }
  242. break ;
  243. }
  244. return $value ;
  245. }
  246. }
  247. function stripMQGPC( $data )
  248. {
  249. if( ! get_magic_quotes_gpc() ) return $data ;
  250. if( is_array( $data ) ) {
  251. return array_map( array( $this , 'stripMQGPC' ) , $data ) ;
  252. } else {
  253. return stripslashes( $data ) ;
  254. }
  255. }
  256. function convertEncodingToIE( $string , $input_encoding )
  257. {
  258. if( function_exists( 'mb_convert_encoding' ) ) {
  259. return mb_convert_encoding( $string , _CHARSET , $input_encoding ) ;
  260. } else if( function_exists( 'iconv' ) ) {
  261. return iconv( $string , $input_encoding , _CHARSET ) ;
  262. }
  263. return $string ;
  264. }
  265. // fetch post data from RAW DATA
  266. function _getPostAsArray( $input_encoding = null )
  267. {
  268. $ret = array() ;
  269. $query = file_get_contents( 'php://input' ) ;
  270. $key_vals = explode( '&' , $query ) ;
  271. foreach( $key_vals as $key_val ) {
  272. @list( $key , $val ) = array_map( 'urldecode' , explode( '=' , $key_val ) ) ;
  273. if( $input_encoding ) {
  274. $key = $this->convertEncodingToIE( $key , $input_encoding ) ;
  275. $val = $this->convertEncodingToIE( $val , $input_encoding ) ;
  276. }
  277. @list( $key_pref , ) = explode( '[' , $key ) ;
  278. if( $key_pref != $key ) {
  279. // don't parse explicit array with []
  280. $ret[ $key_pref ] = $this->stripMQGPC( @$_POST[ $key_pref ] ) ;
  281. } else if( isset( $ret[ $key ] ) ) {
  282. // implicit array without []
  283. if( is_array( $ret[ $key ] ) ) {
  284. // add a member of the array
  285. $ret[ $key ][] = $val ;
  286. } else {
  287. // convert string into array
  288. $ret[ $key ] = array( $ret[ $key ] , $val ) ;
  289. }
  290. } else {
  291. // string
  292. $ret[ $key ] = $val ;
  293. }
  294. }
  295. return $ret ;
  296. }
  297. function getErrors()
  298. {
  299. $ret = array() ;
  300. foreach( $this->fields as $field_name => $attribs ) {
  301. if( ! empty( $attribs['errors'] ) && is_array( $attribs['errors'] ) ) {
  302. foreach( $attribs['errors'] as $error_msg ) {
  303. $ret[] = array(
  304. 'name' => $field_name ,
  305. 'label4disp' => htmlspecialchars( $attribs['label'] , ENT_QUOTES ) ,
  306. 'message' => $error_msg ,
  307. ) ;
  308. }
  309. }
  310. }
  311. return $ret ;
  312. }
  313. function replaceValues( $form_html = null )
  314. {
  315. if( empty( $form_html ) ) $form_html = $this->form_html ;
  316. foreach( $this->fields as $field_name => $attribs ) {
  317. switch( $attribs['tag_kind'] ) {
  318. case 'textarea' :
  319. $form_html = $this->replaceContentTextarea( $form_html , $attribs ) ;
  320. break ;
  321. case 'text' :
  322. $form_html = $this->replaceValueTextbox( $form_html , $attribs ) ;
  323. break ;
  324. case 'select' :
  325. $form_html = $this->replaceSelectedOptions( $form_html , $attribs ) ;
  326. break ;
  327. case 'radio' :
  328. $form_html = $this->replaceCheckedRadios( $form_html , $attribs , $field_name ) ;
  329. break ;
  330. case 'checkbox' :
  331. $form_html = $this->replaceCheckedCheckboxes( $form_html , $attribs , $field_name ) ;
  332. break ;
  333. default :
  334. break ;
  335. }
  336. }
  337. return $form_html ;
  338. }
  339. function replaceValueTextbox( $form_html , $attribs )
  340. {
  341. $values = $attribs['value'] ;
  342. if( ! is_array( $values ) ) $values = array( $values ) ;
  343. foreach( array_keys( $values ) as $i ) {
  344. $value = $values[ $i ] ;
  345. $tag = @$attribs['tags'][ $i ] ;
  346. $old_tag = $tag ;
  347. $value4html = htmlspecialchars( $value , ENT_QUOTES ) ;
  348. if( stristr( $tag , 'value=' ) ) {
  349. $new_tag = preg_replace( '/value\=\"(.*)\"/' , 'value="'.$value4html.'"' , $old_tag ) ;
  350. } else {
  351. $new_tag = str_replace( '/>' , 'value="'.$value4html.'" />' , $old_tag ) ;
  352. }
  353. $form_html = str_replace( $old_tag , $new_tag , $form_html ) ;
  354. }
  355. return $form_html ;
  356. }
  357. function replaceContentTextarea( $form_html , $attribs )
  358. {
  359. $value4html = htmlspecialchars($attribs['value'],ENT_QUOTES) ;
  360. list( $before , $content_html_tmp ) = explode( $attribs['tags'][0] , $form_html , 2 ) ;
  361. list( $content_html , $after ) = explode( '</textarea>' , $content_html_tmp , 2 ) ;
  362. return $before . $attribs['tags'][0] . $value4html . '</textarea>' . $after ;
  363. }
  364. function replaceSelectedOptions( $form_html , $attribs )
  365. {
  366. $values = $attribs['value'] ;
  367. if( ! is_array( $values ) ) $values = array( $values ) ;
  368. list( $before , $options_html_tmp ) = explode( $attribs['tags'][0] , $form_html , 2 ) ;
  369. list( $options_html , $after ) = explode( '</select>' , $options_html_tmp , 2 ) ;
  370. $new_options_html = str_replace( 'selected="selected"' , '' , $options_html ) ;
  371. foreach( $values as $value ) {
  372. $value4html = htmlspecialchars( $value , ENT_QUOTES ) ;
  373. $new_options_html = str_replace( 'value="'.$value4html.'"' , 'value="'.$value4html.'" selected="selected"' , $new_options_html ) ;
  374. }
  375. return $before . $attribs['tags'][0] . $new_options_html . '</select>' . $after ;
  376. }
  377. function replaceCheckedRadios( $form_html , $attribs , $field_name )
  378. {
  379. $value4html = htmlspecialchars($attribs['value'],ENT_QUOTES) ;
  380. preg_match_all( '/<input\s+type\="radio"[^>]*name\="'.preg_quote($field_name).'"[^>]*>/' , $form_html , $matches , PREG_PATTERN_ORDER ) ;
  381. $ret = $form_html ;
  382. foreach( $matches[0] as $match_from ) {
  383. $match_to = str_replace( 'checked="checked"' , '' , $match_from ) ;
  384. if( strstr( $match_from , 'value="'.$value4html.'"' ) ) {
  385. $match_to = str_replace( 'value="'.$value4html.'"' , 'value="'.$value4html.'" checked="checked"' , $match_to ) ;
  386. }
  387. $ret = str_replace( $match_from , $match_to , $ret ) ;
  388. }
  389. return $ret ;
  390. }
  391. function replaceCheckedCheckboxes( $form_html , $attribs , $field_name )
  392. {
  393. $values = $attribs['value'] ;
  394. if( ! is_array( $values ) ) $values = array( $values ) ;
  395. preg_match_all( '/<input\s+type\="checkbox"[^>]*name\="'.preg_quote($attribs['field_name_raw']).'"[^>]*>/' , $form_html , $matches , PREG_PATTERN_ORDER ) ;
  396. $ret = $form_html ;
  397. foreach( $matches[0] as $match_from ) {
  398. $match_to = str_replace( 'checked="checked"' , '' , $match_from ) ;
  399. foreach( $values as $value ) {
  400. $value4html = htmlspecialchars($value,ENT_QUOTES) ;
  401. if( strstr( $match_from , 'value="'.$value4html.'"' ) ) {
  402. $match_to = str_replace( 'value="'.$value4html.'"' , 'value="'.$value4html.'" checked="checked"' , $match_to ) ;
  403. break ;
  404. }
  405. }
  406. $ret = str_replace( $match_from , $match_to , $ret ) ;
  407. }
  408. return $ret ;
  409. }
  410. function validateSelectOption( $tag , $value )
  411. {
  412. $value4html = htmlspecialchars($value,ENT_QUOTES) ;
  413. list( $before , $options_html_tmp ) = explode( $tag , $this->form_html , 2 ) ;
  414. list( $options_html , $after ) = explode( '</select>' , $options_html_tmp , 2 ) ;
  415. if( strstr( $options_html , 'value="'.$value4html.'"' ) ) return true ;
  416. else false ;
  417. }
  418. function renderForMail( $field_separator = "\n" , $mid_separator = "\n" )
  419. {
  420. $ret = '' ;
  421. foreach( $this->fields as $field_name => $attribs ) {
  422. $ret .= $field_separator . $attribs['label'] . $mid_separator ;
  423. if( $attribs['array_type'] == 'linear' ) {
  424. $ret .= implode( $this->column_separator , $attribs['value'] ) ;
  425. } else if( $attribs['count'] > 1 && is_array( $attribs['value'] ) ) {
  426. $ret .= implode( $this->column_separator , $attribs['value'] ) ;
  427. } else {
  428. $ret .= $attribs['value'] ;
  429. }
  430. }
  431. return $ret ;
  432. }
  433. function renderForDB()
  434. {
  435. $ret = array() ;
  436. foreach( $this->fields as $field_name => $attribs ) {
  437. $ret[ $attribs['label'] ] = $attribs['value'] ;
  438. }
  439. return $ret ;
  440. }
  441. function convertZenToHan( $text )
  442. {
  443. if( function_exists( 'mb_convert_kana' ) ) {
  444. return mb_convert_kana( $text , 'as' ) ;
  445. }
  446. return $text ;
  447. }
  448. function fildValueFromTag( $tag )
  449. {
  450. if( preg_match( '/value\s*\=\s*"([^"]+)"/' , $tag , $regs ) ) {
  451. return trim( $regs[1] ) ;
  452. } else {
  453. return 'on' ;
  454. }
  455. }
  456. // http://www.ilovejackdaniels.com/php/email-address-validation/
  457. function checkEmailAddress( $email )
  458. {
  459. // First, we check that there's one @ symbol, and that the lengths are right
  460. if (!ereg("^[^@]{1,64}@[^@]{1,255}$", $email)) {
  461. // Email invalid because wrong number of characters in one section, or wrong number of @ symbols.
  462. return false;
  463. }
  464. // Split it into sections to make life easier
  465. $email_array = explode("@", $email);
  466. $local_array = explode(".", $email_array[0]);
  467. for ($i = 0; $i < sizeof($local_array); $i++) {
  468. if (!ereg("^(([A-Za-z0-9!#$%&'*+/=?^_`{|}~-][A-Za-z0-9!#$%&'*+/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$", $local_array[$i])) {
  469. return false;
  470. }
  471. }
  472. if (!ereg("^\[?[0-9\.]+\]?$", $email_array[1])) { // Check if domain is IP. If not, it should be valid domain name
  473. $domain_array = explode(".", $email_array[1]);
  474. if (sizeof($domain_array) < 2) {
  475. return false; // Not enough parts to domain
  476. }
  477. for ($i = 0; $i < sizeof($domain_array); $i++) {
  478. if (!ereg("^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$", $domain_array[$i])) {
  479. return false;
  480. }
  481. }
  482. }
  483. return true;
  484. }
  485. }
  486. ?>