├── Form.php ├── LICENSE ├── README.md └── captcha ├── fonts ├── AntykwaBold.ttf ├── Candice.ttf ├── Ding-DongDaddyO.ttf ├── Duality.ttf ├── Heineken.ttf ├── Jura.ttf ├── StayPuft.ttf ├── TimesNewRomanBold.ttf └── VeraSansBold.ttf └── get.php /Form.php: -------------------------------------------------------------------------------- 1 | 'form', 23 | 'ajax' => true, 24 | 'mail' => false, 25 | 'date_format' => 'Y/m/d', 26 | 'time_format' => 'H:i:s', 27 | 'nonce' => '', 28 | ); 29 | 30 | $this->last_status = array( 31 | 'processed' => false, 32 | 'error_message' => array(), 33 | 'error_count' => 0, 34 | ); 35 | 36 | $this->config = $config; 37 | $this->validators = array(); 38 | $this->html = new Form_Html($this); 39 | $this->called = false; 40 | 41 | if ('POST' != $_SERVER['REQUEST_METHOD']) 42 | return; 43 | 44 | if ($config['ajax'] && !isset($_POST['_ajax_call'])) 45 | return; 46 | 47 | if ($config['nonce'] && !$this->check_nonce()) 48 | if ($config['ajax']) 49 | exit; 50 | else 51 | return; 52 | 53 | $this->called = true; 54 | } 55 | 56 | public function submit($arg = null) { 57 | if (!$this->called) 58 | return; 59 | 60 | $error = array(); 61 | $processed = true; 62 | $error_count = 0; 63 | 64 | foreach ($this->validators as $name => $validator) { 65 | if (!$validator->is_valid()) { 66 | $error[$name] = $validator->error; 67 | $error_count++; 68 | } elseif ($validator->meta['required']) { 69 | $error[$name] = 'valid'; 70 | } 71 | } 72 | 73 | if ($error_count > 0) 74 | $processed = false; 75 | else 76 | $processed &= $this->save($arg); 77 | 78 | if ($processed && $this->config['mail']) 79 | $processed &= $this->send($arg); 80 | 81 | $this->last_status = array( 82 | 'processed' => $processed, 83 | 'error_message' => $error, 84 | 'error_count' => $error_count, 85 | ); 86 | 87 | $this->post_process(); 88 | } 89 | 90 | public function ajax() { 91 | if (!$this->called) 92 | return; 93 | 94 | header('Content-type: application/json', true); 95 | 96 | echo json_encode($this->last_status); 97 | 98 | exit; 99 | } 100 | 101 | public function get_data($flatten = false) { 102 | if ($this->last_data) 103 | return $this->last_data; 104 | 105 | $data = array(); 106 | 107 | foreach ($this->validators as $name => $validator) { 108 | if (!isset($validator->value)) 109 | continue; 110 | 111 | if ($validator->meta['multi']) { 112 | $res = array(); 113 | 114 | foreach ($validator->value as $val) { 115 | $res[] = $validator->meta['option'][$val - 1]; 116 | } 117 | 118 | if ($flatten) 119 | $res = implode("\n", $res); 120 | 121 | $data[$name] = $res; 122 | } elseif (isset($validator->meta['option'])) { 123 | $data[$name] = $validator->meta['option'][$validator->value - 1]; 124 | } else { 125 | $data[$name] = $validator->value; 126 | } 127 | } 128 | 129 | return $this->last_data = $data; 130 | } 131 | 132 | public function save($arg = null) { 133 | return true; 134 | } 135 | 136 | public function send($arg = null) { 137 | if (!$arg || !isset($arg['from'], $arg['subject'], $arg['body'])) 138 | return; 139 | 140 | $mail_from = $arg['from']; 141 | 142 | if (preg_match('|^(.+?) <(.+?)>$|u', $mail_from, $m)) { 143 | $m[1] = mb_encode_mimeheader($m[1]); 144 | $arg['from'] = $m[2]; 145 | $mail_from = "{$m[1]} <{$m[2]}>"; 146 | } 147 | 148 | $data = $this->get_data(true); 149 | $data['DATE'] = date($this->config['date_format']); 150 | $data['TIME'] = date($this->config['time_format']); 151 | 152 | if (!$arg) 153 | return false; 154 | 155 | $body = $arg['body']; 156 | 157 | foreach ($data as $key => $value) { 158 | $body = str_replace('{{' . $key . '}}', $value, $body); 159 | } 160 | 161 | $body = preg_replace('|{{[\w\-\+\&]+}}|u', '', $body); 162 | $body = trim($body); 163 | $body = strip_tags($body); 164 | $body = wordwrap($body, 70); 165 | 166 | $header = array(); 167 | $header[] = 'From: ' . $mail_from; 168 | 169 | if ($arg['cc']) 170 | $header[] = 'Cc: ' . $arg['cc']; 171 | 172 | if ($arg['bcc']) 173 | $header[] = 'Bcc: ' . $arg['bcc']; 174 | 175 | if ($arg['reply']) 176 | $header[] = 'Reply-To: ' . $arg['reply']; 177 | 178 | $header = array_map(array(&$this, 'remove_line_feeds'), $header); 179 | $header = implode("\n", $header); 180 | 181 | if (!$arg['to']) 182 | $mail_to = $arg['from']; 183 | elseif (strpos($arg['to'], '@')) 184 | $mail_to = $arg['to']; 185 | else 186 | $mail_to = $data[$arg['to']]; 187 | 188 | return @mb_send_mail($mail_to, $subject, $body, $header); 189 | } 190 | 191 | private function remove_line_feeds($str) { 192 | return str_replace(array("\r\n","\r","\n"), '', $str); 193 | } 194 | 195 | public function post_process() { 196 | if ($this->config['ajax']) 197 | $this->ajax(); 198 | } 199 | 200 | private function check_nonce() { 201 | $name = $this->get_name('nonce'); 202 | $post = &$_POST[$name]; 203 | $session = &$_SESSION[$name]; 204 | 205 | return isset($post, $session) && !empty($post) && $post == $session; 206 | } 207 | 208 | public function get_name($name) { 209 | $name = preg_replace('|[^\w\-]|', '', $name); 210 | return $this->config['prefix'] . '-' . $name; 211 | } 212 | 213 | public function add($name, $requried = false) { 214 | if (!$name || empty($name)) 215 | return ''; 216 | 217 | $validator = new Form_Validator($this, $name); 218 | $validator->required($requried); 219 | return $this->validators[$name] = $validator; 220 | } 221 | 222 | public function add_captcha() { 223 | return $this->add('captcha', true)->check_captcha(); 224 | } 225 | } 226 | 227 | 228 | /** 229 | * Validator 230 | */ 231 | class Form_Validator { 232 | public static $pattern = array( 233 | 'email' => '|^[a-zA-Z][\w\+\.\-]{3,}\@[a-z\.]+?\.[a-z]{2,4}$|u', 234 | 'tel' => '%^(0\d{9}|0[5789]0\d{8})$%u', 235 | 'credit' => '/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})$/', 236 | 'url' => '/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i', 237 | ); 238 | 239 | public function __construct($form, $name) { 240 | preg_match('|^([\w\-]+)([\+])?(&)?$|', $name, $m); 241 | 242 | $name = $form->get_name($name); 243 | 244 | $val = $_POST[$name]; 245 | $val = is_array($val) 246 | ? array_map(array(&$this, 'sanitize'), $val) 247 | : $this->sanitize($val); 248 | 249 | $this->form = $form; 250 | 251 | $this->interr = false; 252 | $this->called = $form->called; 253 | 254 | $this->name = $name; 255 | $this->value = $val; 256 | $this->meta = array(); 257 | $this->valid = true; 258 | $this->error = 'none'; 259 | 260 | /* Meta 261 | -----------------------------------------------*/ 262 | $this->meta['name'] = $this->meta['id'] = $form->get_name($m[1]); 263 | 264 | if ($m[2]) { 265 | $this->meta['multi'] = true; 266 | $this->meta['name'] .= '[]'; 267 | } 268 | 269 | if ($m[3]) 270 | $this->meta['option'] = array(); 271 | 272 | return $this; 273 | } 274 | 275 | public function is_valid() { 276 | return $this->called && $this->valid; 277 | } 278 | 279 | /* Util 280 | -----------------------------------------------*/ 281 | private function sanitize($val) { 282 | if (!isset($val)) 283 | return ''; 284 | 285 | $val = str_replace("\0", '', $val); 286 | $val = trim($val); 287 | 288 | if ('0' === $val) 289 | $val = 0; 290 | elseif (preg_match('|^[\+\-]?[1-9]\d*$|u', $val)) 291 | $val = (int) $val; 292 | elseif (preg_match('|^[\+\-]?\d+?\.\d+$|u', $val)) 293 | $val = (float) $val; 294 | 295 | return $val; 296 | } 297 | private function value_size() { 298 | $val = $this->value; 299 | 300 | if (!isset($val) || !is_numeric($val) && empty($val)) 301 | return 0; 302 | elseif (is_array($val)) 303 | return sizeof($val); 304 | else 305 | return 1; 306 | } 307 | 308 | private function reject($message = 'unknown') { 309 | $this->valid = false; 310 | $this->error = $message; 311 | } 312 | 313 | private function interrupt() { 314 | return $this->interr || !$this->valid || !$this->called; 315 | } 316 | 317 | /* Data provide 318 | -----------------------------------------------*/ 319 | public function set_option($option) { 320 | if (!is_array($option)) { 321 | $this->reject('option_not_array'); 322 | return $this; 323 | } 324 | 325 | $this->meta['option'] = $option; 326 | $this->meta['type'] = $this->meta['multi'] ? 'checkbox' : 'radio'; 327 | 328 | if ($this->interrupt()) 329 | return $this; 330 | 331 | if (0 === $this->value) { 332 | if ($this->meta['required']) 333 | $this->reject('required'); 334 | else 335 | $this->interr = true; 336 | 337 | return $this; 338 | } 339 | 340 | $values = is_array($this->value) ? $this->value : array($this->value); 341 | $size = sizeof($option); 342 | 343 | foreach ($values as $val) { 344 | if (!is_numeric($val) || $val < 0 || $val > $size) { 345 | $this->reject('unexpected'); 346 | break; 347 | } 348 | } 349 | 350 | return $this; 351 | } 352 | public function set_val($text) { 353 | if (!$this->meta['option']) 354 | $val = array_search($text, $this->meta['option']); 355 | else if (0 !== $this->value && empty($this->value)) 356 | $this->value = ($val = $text); 357 | 358 | if (isset($val)) 359 | $this->meta['default'] = $val; 360 | 361 | return $this; 362 | } 363 | 364 | /* Low level rules 365 | -----------------------------------------------*/ 366 | public function required($bool) { 367 | if ($this->interrupt()) 368 | return $this; 369 | 370 | $this->meta['required'] = $bool; 371 | 372 | if (!is_numeric($this->value) && empty($this->value)) { 373 | if ($bool) 374 | $this->reject('required'); 375 | else 376 | $this->interr = true; 377 | } 378 | 379 | return $this; 380 | } 381 | public function minimum($min) { 382 | if ($this->interrupt()) 383 | return $this; 384 | 385 | if ($this->value < $min) 386 | $this->reject(array('minimum', $min)); 387 | 388 | return $this; 389 | } 390 | public function maximum($max) { 391 | if ($this->interrupt()) 392 | return $this; 393 | 394 | if ($this->value > $max) 395 | $this->reject(array('maximum', $max)); 396 | 397 | return $this; 398 | } 399 | public function minlen($min) { 400 | if ($this->interrupt()) 401 | return $this; 402 | 403 | if (mb_strlen((string) $this->value) < $min) 404 | $this->reject(array('minlen', $min)); 405 | 406 | return $this; 407 | } 408 | public function maxlen($max) { 409 | if ($this->interrupt()) 410 | return $this; 411 | 412 | if (mb_strlen((string) $this->value) > $max) 413 | $this->reject(array('maxlen', $max)); 414 | 415 | return $this; 416 | } 417 | public function minselect($min) { 418 | if ($this->interrupt()) 419 | return $this; 420 | 421 | 422 | if ($this->value_size() < $min) 423 | $this->reject(array('minselect', $min)); 424 | 425 | return $this; 426 | } 427 | public function maxselect($max) { 428 | if ($this->interrupt()) 429 | return $this; 430 | 431 | if ($this->value_size() > $max) 432 | $this->reject(array('maxselect', $max)); 433 | 434 | return $this; 435 | } 436 | public function select($num) { 437 | if ($this->interrupt()) 438 | return $this; 439 | 440 | if ($this->value_size() !== $num) 441 | $this->reject(array('select', $max)); 442 | 443 | return $this; 444 | } 445 | 446 | /* Rule & Filter 447 | -----------------------------------------------*/ 448 | private function _func_call($call, $filter, $arg = array()) { 449 | $filter = "validation_filter_$filter"; 450 | array_shift($arg); 451 | array_unshift($arg, $call, $this->value); 452 | 453 | if (function_exists($filter)) { 454 | $res = call_user_func_array($filter, $arg); 455 | 456 | if ($change) 457 | $this->value = $res; 458 | 459 | return $res; 460 | } 461 | 462 | return null; 463 | } 464 | public function format($filter) { 465 | $arg = func_get_args(); 466 | 467 | $this->_func_call('format', $filter, $arg); 468 | 469 | return $this; 470 | } 471 | public function filter($rule, $arg = null) { 472 | if ($this->interrupt()) 473 | return $this; 474 | 475 | if (preg_match('|^[a-z_]\w+$|', $rule)) { 476 | if (self::$pattern[$rule]) { 477 | if (!preg_match(self::$pattern[$rule], $this->value)) 478 | $this->reject('invalid'); 479 | } else { 480 | $_arg = isset($arg) ? $arg : func_get_args(); 481 | 482 | if (false === $this->_filter('filter', $rule, $_arg)) 483 | $this->reject('invalid'); 484 | } 485 | } else { // regexp 486 | if (!preg_match($rule, $this->value)) { 487 | $this->reject('invalid'); 488 | } 489 | } 490 | 491 | return $this; 492 | } 493 | public function type($type) { 494 | $arg = func_get_args(); 495 | 496 | $this->meta['type'] = $type; 497 | $this->filter($type, $arg); 498 | 499 | return $this; 500 | } 501 | 502 | /* Conditional 503 | -----------------------------------------------*/ 504 | public function when($case, $func) { 505 | if (!$this->called && is_callable($func)) { 506 | call_user_func($func, $this->form); 507 | return $this; 508 | } 509 | 510 | if ($this->interrupt()) 511 | return $this; 512 | 513 | if ($case == $this->value) { 514 | call_user_func($func, $this->form); 515 | } 516 | 517 | return $this; 518 | } 519 | 520 | /* Captcha 521 | -----------------------------------------------*/ 522 | public function check_captcha() { 523 | if ($this->interrupt()) 524 | return $this; 525 | 526 | $value = strtolower($this->value); 527 | 528 | $captcha = &$_SESSION['captcha']; 529 | 530 | if (!isset($captcha) || $value != $captcha) 531 | $this->reject('invalid'); 532 | 533 | return $this; 534 | } 535 | } 536 | 537 | 538 | /** 539 | * Form Html Helper 540 | */ 541 | class Form_Html { 542 | public function __construct($Form) { 543 | $this->form = $Form; 544 | } 545 | 546 | private function builder($tag, $attrs = array(), $content = null) { 547 | $att = ''; 548 | 549 | foreach ($attrs as $key => $val) { 550 | if (is_bool($val)) { 551 | if ($val) 552 | $att .= " $key"; 553 | } else { 554 | $att .= " $key=\"$val\""; 555 | } 556 | } 557 | 558 | return isset($content) ? "<$tag$att>$content" : "<$tag$att />"; 559 | } 560 | 561 | private function get_info($name) { 562 | $validator = $this->form->validators[$name]; 563 | 564 | return $validator->meta; 565 | } 566 | 567 | public function text($name, $attrs = array()) { 568 | $info = $this->get_info($name); 569 | 570 | echo $this->builder('input', array( 571 | 'type' => $info['type'] ? $info['type'] : 'text', 572 | 'name' => $info['name'], 573 | 'id' => $info['id'], 574 | ) + $attrs); 575 | } 576 | public function textarea($name, $attrs = array()) { 577 | $info = $this->get_info($name); 578 | 579 | echo $this->builder('textarea', array( 580 | 'name' => $info['name'], 581 | 'id' => $info['id'], 582 | ) + $attrs, ''); 583 | } 584 | 585 | public function option($name, $attrs = array()) { 586 | $info = $this->get_info($name); 587 | 588 | for ($i = 0, $len = sizeof($info['option']); $i < $len; $i++) { 589 | echo '
  • '; 597 | } 598 | } 599 | public function select($name, $attrs = array()) { 600 | $info = $this->get_info($name); 601 | 602 | $option = $this->builder('option', array( 603 | 'value' => 0, 604 | 'disabled' => true, 605 | 'selected' => !isset($info['default']), 606 | ), $this->form->config['text_selectbox']); 607 | 608 | for ($i = 0, $len = sizeof($info['option']); $i < $len; $i++) { 609 | $option .= $this->builder('option', array( 610 | 'value' => $i + 1, 611 | 'selected' => (isset($info['default']) && $i === $info['default']), 612 | ), $info['option'][$i]); 613 | } 614 | 615 | echo $this->builder('select', array( 616 | 'name' => $info['name'], 617 | 'id' => $info['id'], 618 | 'multiple' => !!$info['multi'], 619 | ) + $attrs, $option); 620 | } 621 | public function hidden($name, $value) { 622 | echo $this->builder('input', array( 623 | 'type' => 'hidden', 624 | 'name' => $this->form->get_name($name), 625 | 'value' => $value, 626 | )); 627 | } 628 | 629 | public function captcha($attrs = array()) { 630 | $this->text('captcha', $attrs); 631 | } 632 | public function captcha_image($attrs = array()) { 633 | echo $this->builder('img', array( 634 | 'src' => CAPTCHA_SCRIPT, 635 | 'id' => 'captcha-image', 636 | 'alt' => '' 637 | ) + $attrs); 638 | } 639 | 640 | public function nonce() { 641 | $prefix = $this->form->config['prefix']; 642 | $key = $this->form->config['nonce']; 643 | $key = $nonce . hash_hmac('ripemd160', substr($nonce, 0, 8), $prefix . mt_rand()); 644 | $nonce = hash_hmac('ripemd160', mt_rand(), $key); 645 | 646 | $_SESSION[$this->form->get_name('nonce')] = $nonce; 647 | 648 | $this->hidden('nonce', $nonce); 649 | echo "\n"; 650 | } 651 | 652 | } 653 | 654 | 655 | /*=== Default Filters 656 | ==============================================================================================*/ 657 | function validation_filter_kana($call, $val, $option = '') { 658 | if ('format' == $call) 659 | return mb_convert_kana($val, $option); 660 | 661 | return true; 662 | } 663 | 664 | function validation_filter_hiragana($call, $val) { 665 | if ('filter' == $call) 666 | return !!preg_match('/^[ぁ-んー \s]+$/u', $val); 667 | 668 | return true; 669 | } 670 | 671 | function validation_filter_katakana($call, $val) { 672 | if ('filter' == $call) 673 | return !!preg_match('/^[ァ-ヶー \s]+$/u', $val); 674 | 675 | return true; 676 | } 677 | 678 | function validation_filter_datetime($call, $val, $format = '') { 679 | $p = date_parse_from_format($format, $val); 680 | 681 | if ('filter' == $call) 682 | return $p['error_count'] === 0; 683 | 684 | if ('format' == $call) 685 | return $p; 686 | } 687 | 688 | 689 | 690 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Yuki Iwanaga 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | form-php 2 | ======== 3 | 4 | スタイリッシュなシンタックスが特徴のフォームバリデーションライブラリです。 5 | 6 | 7 | 特徴 8 | ---- 9 | 10 | - チェーンメソッドを使った簡単なシンタックス 11 | - バリデーションのルールとフォームの HTML のロジックが分離されている 12 | - バリデーション用のフィルターを拡張できる 13 | - フォームの HTML をルールを元に自動生成 14 | - Ajax 対応 15 | - 自動返信メール対応 16 | - nonce 機能 (CSRF対策) 17 | - CAPTCHA 対応 (スパム対策) 18 | 19 | 20 | Quick Start 21 | ----------- 22 | 23 | ファイルの一番最初でバリデーションのルールを設定します。 24 | 25 | ```php 26 | 'cf', 30 | 'mail' => true, 31 | 'nonce' => 'gYxfXm30Q7hruJKB', 32 | )); 33 | 34 | /* Validation Rules 35 | -----------------------------------------------*/ 36 | $cf->add('name', true)->minlen(3)->maxlen(12); 37 | 38 | $cf->add('email', true)->type('email'); 39 | 40 | $cf->add('birthdate')->filter('datetime', 'Y/m/d'); 41 | 42 | $cf->add('gender')->set_option(array( 43 | 'Male', 44 | 'Female', 45 | )); 46 | 47 | $cf->add('color+')->set_option(array( 48 | 'Red', 49 | 'Orange', 50 | 'Yellow', 51 | 'Green', 52 | 'Blue', 53 | 'Indigo', 54 | 'Violet', 55 | ))->maxselect(3); 56 | 57 | $cf->add('message')->maxlen(1000); 58 | 59 | /* Send Email 60 | -----------------------------------------------*/ 61 | $cf->submit(array( 62 | 'from' => 'Contact ', 63 | 'bcc' => 'contact@example.com', 64 | 'to' => 'email', 65 | 'subject' => 'Contact', 66 | 'body' => ' 67 | ---------------------------------------- 68 | Timestamp: {{DATE}} 69 | ---------------------------------------- 70 | Name: {{name}} 71 | ---------------------------------------- 72 | Email: {{email}} 73 | ---------------------------------------- 74 | Birthdate: {{birthdate}} 75 | ---------------------------------------- 76 | Gender: {{gender}} 77 | ---------------------------------------- 78 | Colors: 79 | {{color+}} 80 | ---------------------------------------- 81 | Message: 82 | {{message}} 83 | ---------------------------------------- 84 | ', 85 | )); 86 | 87 | ?> 88 | 89 |
    90 |

    Your name (required)

    91 |
    html->text('name'); ?>
    92 | 93 |

    Email (required)

    94 |
    html->text('email'); ?>
    95 | 96 |

    Birthdate

    97 |
    html->text('date'); ?>
    98 | 99 |

    Gender

    100 | 101 | 102 |

    Colors you like (select 3 at most)

    103 | 104 | 105 |

    Message

    106 | 107 | 108 | html->nonce(); ?> 109 | 110 | 111 |
    112 | 113 | 134 | ``` 135 | 136 | 137 | 初期化 138 | ------ 139 | 140 | ```php 141 | $cf = new Form($config); 142 | ``` 143 | 144 | ``Form`` に連想配列を渡して設定をします。 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 |
    キー初期値説明
    prefixString'form'フォームの接頭辞。各フォームの要素の nameid の前に付きます。
    ajaxBooltrueAjax で POST を処理するかどうかを指定します。Ajax 側で呼び出す際は _ajax_call=1 も POST するようにして下さい。
    mailBoolfalse結果をメールで送信するかどうかを指定します。
    nonceString''nonce 機能を利用する際は必ず設定してください。これは nonce の乱数値を生成する時に使用します。値は16文字のできるだけ的当なものにして下さい。こちらのサイトトを利用すると良いかもしれません。
    178 | 179 | 180 | 181 | 182 | バリデーションルールの設定 183 | ========================== 184 | 185 | 新しいルールを作る 186 | ------------------ 187 | 188 | ### 普通のフィールド 189 | 190 | **例** 191 | 192 | ```php 193 | $cf->add($name [, $required]); 194 | ``` 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 |
    引数説明
    $nameString項目の名前です。英数字(a-zA-Z0-9)・アンダースコア(_)・ハイフン(-)を使って下さい。<input type="checkbox"> と <select> で複数の項目を選択させる場合は、名前の語尾に + を付けます。
    $requiredBool必須であれば true を指定します。必須でないときは、false を指定するか、引数の省略ができます。
    213 | 214 | ### CAPTCHAを追加する 215 | 216 | ```php 217 | $cf->add_captcha(); 218 | ``` 219 | 220 | これだけです。 221 | 222 | 223 | set_option 224 | ---------- 225 | 226 | <input type="checkbox">, <input type="radio">, <select> に、選択できる項目を設定します。 227 | 228 | **例** 229 | 230 | ```php 231 | $cf->add('color')->set_option(array( 232 | 'Red', 233 | 'Orange', 234 | 'Yellow', 235 | 'Green', 236 | 'Blue', 237 | 'Indigo', 238 | 'Violet', 239 | )); 240 | ``` 241 | 242 | 複数選択させるには、名前の語尾に ``+`` を付けます。 243 | 244 | ```php 245 | $cf->add('color+')->set_option(array( 246 | 'Red', 247 | 'Orange', 248 | 'Yellow', 249 | 'Green', 250 | 'Blue', 251 | 'Indigo', 252 | 'Violet', 253 | )); 254 | ``` 255 | 256 | 257 | required 258 | -------- 259 | 260 | 項目を必須にします。 261 | 262 | **例** 263 | 264 | ```php 265 | $cf->add('foo')->required(true); 266 | ``` 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 |
    引数説明
    1Bool入力を必須にする場合は true を指定します。
    280 | 281 | なお、次の2つは全く同じ意味です。 282 | 283 | ```php 284 | $cf->add('foo')->required(true) 285 | $cf->add('foo', true) 286 | ``` 287 | 288 | 289 | minlen / maxlen 290 | --------------- 291 | 292 | ``minlen`` と ``maxlen`` は入力された文字列の長さの下限と上限を指定します。 293 | 294 | **例: minlen** 295 | 296 | ```php 297 | $cf->add('foo')->minlen(40) 298 | ``` 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 |
    引数説明
    1Number文字列の長さの下限を指定します。入力がこの値より短いとエラーがでます。
    312 | 313 | 314 | mininum / maximum 315 | ----------------- 316 | 317 | ``mininum`` と ``maximum`` は入力された値を数値として評価し、その値の下限と上限を指定します。 318 | 319 | **例: maximum** 320 | 321 | ```php 322 | $cf->add('foo')->maximum(40) 323 | ``` 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 |
    引数説明
    1Number値の上限を指定します。入力がこの値より大きいとエラーがでます。
    337 | 338 | 339 | minselect / maxselect / select 340 | ------------------------------ 341 | 342 | ``mininum`` と ``maximum`` は複数の値が選択できる項目の場合(<input type="checkbox">, <select>)に、選択できる個数の下限と上限を指定します。 343 | 344 | ``select`` は単に、選択できる個数を設定します。 345 | 346 | **例: maxselect** 347 | 348 | ```php 349 | $cf->add('foo+')->set_option(array( 350 | 'aeuio', 351 | 'kakikukeko', 352 | 'sasisuseso', 353 | ))->maxselect(2) 354 | ``` 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 |
    引数説明
    1Number選択できる項目の個数の上限を指定します。選択された項目の個数がこの値より多いとエラーがでます。
    368 | 369 | 370 | filter 371 | ------ 372 | 373 | 高度なバリデーションルールを設定します。 374 | 375 | ### メールアドレス 376 | 377 | ```php 378 | $cf->add('foo')->filter('email') 379 | ``` 380 | 381 | ### 電話番号 382 | 383 | ```php 384 | $cf->add('foo')->filter('tel') 385 | ``` 386 | 387 | ### URL 388 | 389 | ```php 390 | $cf->add('foo')->filter('url') 391 | ``` 392 | 393 | ### 全てひらがな / 全てカタカナ 394 | 395 | ```php 396 | $cf->add('foo')->filter('hiragana') 397 | $cf->add('foo')->filter('katakana') 398 | ``` 399 | 400 | ### 正当な日付・時刻 401 | 402 | 第2引数にフォーマット文字列を渡します。 403 | フォーマットについては、[PHP のマニュアル](http://www.php.net/manual/ja/datetime.createfromformat.php)を参考にして下さい。 404 | 405 | ```php 406 | $cf->add('foo')->filter('datetime', 'Y-m-d') 407 | $cf->add('foo')->filter('datetime', 'H:i:s') 408 | ``` 409 | 410 | 411 | type 412 | ---- 413 | 414 | 機能は ``filter`` とほとんど同じですが、フォームの要素の ``type`` 属性に値を設定する点において異なります。 415 | 416 | 例えば ``filter('email')`` や ``filter('tel')`` そして ``filter('url')`` は、 417 | こちらの ``type('email')`` と ``type('tel')`` と ``type('url')`` を使うようにして下さい。 418 | 419 | ### メールアドレス 420 | 421 | ```php 422 | $cf->add('foo')->type('email') 423 | ``` 424 | 425 | ### 電話番号 426 | 427 | ```php 428 | $cf->add('foo')->type('tel') 429 | ``` 430 | 431 | ### URL 432 | 433 | ```php 434 | $cf->add('foo')->type('url') 435 | ``` 436 | 437 | 438 | format 439 | ------ 440 | 441 | 値を判定するのではなく、加工します。主にデータの正規化に利用します。 442 | 443 | ### kana 444 | 445 | PHP の ``mb_convert_kana`` 関数を利用して、「半角」-「全角」変換を行います。 446 | 詳しいオプションについては [PHP のマニュアル](http://www.php.net/manual/ja/function.mb-convert-kana.php)を参照下さい。 447 | 448 | ```php 449 | $cf->add('foo')->format('kana', 'asKV') 450 | ``` 451 | 452 | ### datetime 453 | 454 | ``rule`` のところでも出てきましたが、入力された日付についての詳細情報を連想配列で保存します。 455 | 詳しくは、[PHP のマニュアル](http://www.php.net/manual/ja/function.date-parse-from-format.php)を参照下さい。 456 | 457 | ```php 458 | $cf->add('foo')->format('datetime', 'Y/m/d H:i') 459 | ``` 460 | 461 | 462 | 463 | 464 | filter / format で独自関数をつかう 465 | ================================== 466 | 467 | 関数の命名規則は ``validation_filter_{name}`` です。 468 | 関数の呼び出しには、つぎの引数が渡されます。 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 |
    引数説明
    $callStringどちらの関数から呼ばれたのかを取得する際に使います。値は 'filter''format' です。
    $valMixedそのまんま値、データです。
    $argArrayfilterformat に渡された第1引数以降が配列として取得出来ます。
    492 | 493 | **例** 494 | 495 | - ``filter('upper')`` なら英字全部かどうかを判定する 496 | - ``format('upper')`` なら英字を大文字に変換する 497 | 498 | ような関数をつくってみます。 499 | 500 | ```php 501 | function validation_filter_upper($call, $val, $args = array()) { 502 | if ('filter' == $call) 503 | return !!preg_match('|^[A-Z]+$|', $val); 504 | 505 | if ('format' == $call) 506 | return strtoupper($val); 507 | } 508 | ``` 509 | 510 | 511 | 512 | 513 | HTML ヘルパー 514 | ============= 515 | 516 | テキスト 517 | -------- 518 | 519 | ```php 520 | html->text('foo'); ?> 521 | ``` 522 | 523 | **生成例** 524 | 525 | ```html 526 | 527 | ``` 528 | 529 | 530 | テキストエリア 531 | -------------- 532 | 533 | ```php 534 | html->textarea('foo'); ?> 535 | ``` 536 | 537 | **生成例** 538 | 539 | ```html 540 | 541 | ``` 542 | 543 | 544 | チェックボックス / ラジオボタン 545 | ------------------------------- 546 | 547 | 出力がチェックボックスかラジオボタンかは、名前の語尾に ``+`` があるかどうかで自動で判断します。 548 | 549 | ```php 550 | 551 | 552 | ``` 553 | 554 | **生成例** 555 | 556 | ```html 557 | 562 | 567 | ``` 568 | 569 | 570 | セレクトボックス 571 | ---------------- 572 | 573 | 名前の語尾に ``+`` がある場合は自動で ``multiple`` 属性が追加されます。 574 | 575 | ```php 576 | html->select('foo'); ?> 577 | html->select('bar+'); ?> 578 | ``` 579 | 580 | **生成例** 581 | 582 | ```html 583 | 588 | 593 | ``` 594 | 595 | CAPTCHA の画像と入力フィールド 596 | ------------------------------ 597 | 598 | ```php 599 |
    html->captcha_image(); ?>
    600 | html->captcha(); ?> 601 | ``` 602 | 603 | **生成例** 604 | 605 | ```html 606 |
    607 | 608 | ``` 609 | 610 | 611 | 612 | 613 | フォーム処理 614 | ============ 615 | 616 | メール送信 617 | ---------- 618 | 619 | 初期化の時に ``'mail' => true`` を設定していることを確認する。 620 | 621 | ```php 622 | $cf->submit($setting); 623 | ``` 624 | 625 | すべてのルールを記述したあとに、``$cf->submit()`` で連想配列を渡して設定をします。 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 |
    キー説明
    fromString必須。メールの送信アドレスを指定します。これは決まりではありませんが、「表示名 <メールアドレス>」の形式で書くことを推奨します。
    ccStringCarbon copy を指定します。
    bccStringBlind carbon copy を指定します。
    replyStringReply-to を指定します。
    toString必須。メールの受信先を指定します。メールアドレスを直接書くこともできますが、フォームの名前を入れればそれに宛てて送信することもできます。
    subjectString必須。件名を指定します。
    bodyString必須。メールの本文を指定します。
    669 | 670 | ### body 内でフォームの値を使う 671 | 672 | ``add`` のときに設定した名前を ``{{`` と ``}}`` で囲うと文字列内で展開されます。 673 | 674 | 例えば 675 | 676 | ```php 677 | $cf->add('foo') 678 | ``` 679 | 680 | の値を、メール本文内で使いたいときは、 681 | 682 | Foo is {{foo}}. 683 | 684 | のように書きます。 685 | 686 | #### マジック変数 687 | 688 | フォームの値以外にも特殊な ``{{ }}`` を用意しています。 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 |
    変数説明
    {{DATE}}現在の日付
    {{TIME}}現在の時刻
    704 | 705 | 706 | メール以外での処理 707 | ------------------ 708 | 709 | ``Form`` クラスを直接使わずに、extends して新しいクラスを作って、それを使って下さい。 710 | 711 | ```php 712 | class MyForm extends Form { 713 | public function save($args = array()) { 714 | $data = $this->get_data(); 715 | 716 | // process... 717 | } 718 | public function post_process() { 719 | $status = $this->last_status; 720 | 721 | // some process... 722 | } 723 | } 724 | ``` 725 | 726 | -------------------------------------------------------------------------------- /captcha/fonts/AntykwaBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creasty-sandbox/form-php/7911c69ef070bdec91b451b5085ebddce103dc79/captcha/fonts/AntykwaBold.ttf -------------------------------------------------------------------------------- /captcha/fonts/Candice.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creasty-sandbox/form-php/7911c69ef070bdec91b451b5085ebddce103dc79/captcha/fonts/Candice.ttf -------------------------------------------------------------------------------- /captcha/fonts/Ding-DongDaddyO.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creasty-sandbox/form-php/7911c69ef070bdec91b451b5085ebddce103dc79/captcha/fonts/Ding-DongDaddyO.ttf -------------------------------------------------------------------------------- /captcha/fonts/Duality.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creasty-sandbox/form-php/7911c69ef070bdec91b451b5085ebddce103dc79/captcha/fonts/Duality.ttf -------------------------------------------------------------------------------- /captcha/fonts/Heineken.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creasty-sandbox/form-php/7911c69ef070bdec91b451b5085ebddce103dc79/captcha/fonts/Heineken.ttf -------------------------------------------------------------------------------- /captcha/fonts/Jura.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creasty-sandbox/form-php/7911c69ef070bdec91b451b5085ebddce103dc79/captcha/fonts/Jura.ttf -------------------------------------------------------------------------------- /captcha/fonts/StayPuft.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creasty-sandbox/form-php/7911c69ef070bdec91b451b5085ebddce103dc79/captcha/fonts/StayPuft.ttf -------------------------------------------------------------------------------- /captcha/fonts/TimesNewRomanBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creasty-sandbox/form-php/7911c69ef070bdec91b451b5085ebddce103dc79/captcha/fonts/TimesNewRomanBold.ttf -------------------------------------------------------------------------------- /captcha/fonts/VeraSansBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creasty-sandbox/form-php/7911c69ef070bdec91b451b5085ebddce103dc79/captcha/fonts/VeraSansBold.ttf -------------------------------------------------------------------------------- /captcha/get.php: -------------------------------------------------------------------------------- 1 | CreateImage(); 7 | 8 | 9 | class SimpleCaptcha { 10 | public $im; 11 | 12 | public $session_var = 'captcha'; 13 | 14 | public $width = 300; 15 | public $height = 100; 16 | 17 | public $minWordLength = 5; 18 | public $maxWordLength = 8; 19 | 20 | public $Yperiod = 12; 21 | public $Yamplitude = 14; 22 | public $Xperiod = 11; 23 | public $Xamplitude = 5; 24 | public $maxRotation = 8; 25 | public $scale = 2; 26 | 27 | public $backgroundColor = array(255, 255, 255); 28 | public $colors = array( 29 | array(27, 78, 181),// blue 30 | array(22, 163, 35),// green 31 | array(214, 36, 7) // red 32 | ); 33 | 34 | public $resourcesPath = 'fonts/'; 35 | 36 | public $fonts = array( 37 | array( 'spacing' => -3.0, 'minSize' => 27, 'maxSize' => 30, 'font' => 'AntykwaBold.ttf' ), 38 | array( 'spacing' => -1.5, 'minSize' => 28, 'maxSize' => 31, 'font' => 'Candice.ttf' ), 39 | array( 'spacing' => -2.0, 'minSize' => 24, 'maxSize' => 30, 'font' => 'Ding-DongDaddyO.ttf' ), 40 | array( 'spacing' => -2.0, 'minSize' => 30, 'maxSize' => 38, 'font' => 'Duality.ttf' ), 41 | array( 'spacing' => -2.0, 'minSize' => 24, 'maxSize' => 34, 'font' => 'Heineken.ttf' ), 42 | array( 'spacing' => -2.0, 'minSize' => 28, 'maxSize' => 32, 'font' => 'Jura.ttf' ), 43 | array( 'spacing' => -1.5, 'minSize' => 28, 'maxSize' => 32, 'font' => 'StayPuft.ttf' ), 44 | array( 'spacing' => -2.0, 'minSize' => 28, 'maxSize' => 34, 'font' => 'TimesNewRomanBold.ttf' ), 45 | array( 'spacing' => -1.0, 'minSize' => 20, 'maxSize' => 28, 'font' => 'VeraSansBold.ttf' ), 46 | ); 47 | 48 | public function __construct() {} 49 | 50 | public function CreateImage() { 51 | $this->ImageAllocate(); 52 | 53 | $text = $this->GetRandomCaptchaText(); 54 | $this->WriteText($text); 55 | 56 | $_SESSION[$this->session_var] = $text; 57 | 58 | $this->WaveImage(); 59 | 60 | imagefilter($this->im, IMG_FILTER_GAUSSIAN_BLUR); 61 | 62 | $this->ReduceImage(); 63 | 64 | $this->WriteImage(); 65 | $this->Cleanup(); 66 | } 67 | 68 | protected function ImageAllocate() { 69 | if (!empty($this->im)) 70 | imagedestroy($this->im); 71 | 72 | $width = $this->width * $this->scale; 73 | $height = $this->height * $this->scale; 74 | 75 | $this->im = imagecreatetruecolor($width, $height); 76 | 77 | $this->GdBgColor = imagecolorallocate( 78 | $this->im, 79 | $this->backgroundColor[0], 80 | $this->backgroundColor[1], 81 | $this->backgroundColor[2] 82 | ); 83 | imagefilledrectangle( 84 | $this->im, 85 | 0, 86 | 0, 87 | $width, 88 | $height, 89 | $this->GdBgColor 90 | ); 91 | 92 | $color = $this->colors[mt_rand(0, sizeof($this->colors) - 1)]; 93 | $this->GdFgColor = imagecolorallocate( 94 | $this->im, 95 | $color[0], 96 | $color[1], 97 | $color[2] 98 | ); 99 | 100 | } 101 | 102 | protected function GetRandomCaptchaText() { 103 | $length = rand($this->minWordLength, $this->maxWordLength); 104 | 105 | $words = 'abcdefghijlmnopqrstvwyz'; 106 | $vocals = 'aeiou'; 107 | 108 | $text = ''; 109 | $vocal = rand(0, 1); 110 | 111 | for ($i = 0; $i < $length; $i++) { 112 | if ($vocal) 113 | $text .= $vocals[mt_rand(0,4)]; 114 | else 115 | $text .= $words[mt_rand(0,22)]; 116 | 117 | $vocal = !$vocal; 118 | } 119 | return $text; 120 | } 121 | 122 | protected function WriteText($text) { 123 | $fontcfg = $this->fonts[mt_rand(0, sizeof($this->fonts) - 1)]; 124 | $fontfile = $this->resourcesPath . $fontcfg['font']; 125 | 126 | $lettersMissing = $this->maxWordLength - strlen($text); 127 | $fontSizefactor = 1.5 + $lettersMissing * 0.09; 128 | 129 | $x = 20 * $this->scale; 130 | $y = round(($this->height * 27 / 40) * $this->scale); 131 | 132 | for ($i = 0, $length = strlen($text); $i < $length; $i++) { 133 | $degree = rand($this->maxRotation * -1, $this->maxRotation); 134 | $fontsize = rand($fontcfg['minSize'], $fontcfg['maxSize']) * $this->scale * $fontSizefactor; 135 | $letter = $text[$i]; 136 | 137 | $coords = imagettftext( 138 | $this->im, 139 | $fontsize, 140 | $degree, 141 | $x, 142 | $y, 143 | $this->GdFgColor, 144 | $fontfile, 145 | $letter 146 | ); 147 | 148 | $x = $coords[2] + $fontcfg['spacing'] * $this->scale; 149 | } 150 | } 151 | 152 | protected function WaveImage() { 153 | // X-axis wave generation 154 | $k = rand(0, 100); 155 | $xp = $this->scale * $this->Xperiod * rand(1, 3); 156 | $width = $this->width * $this->scale; 157 | $height = $this->height * $this->scale; 158 | 159 | for ($i = 0; $i < $width; $i++) { 160 | imagecopy( 161 | $this->im, 162 | $this->im, 163 | $i-1, 164 | sin($k + $i / $xp) * $this->scale * $this->Xamplitude, 165 | $i, 166 | 0, 167 | 1, 168 | $height 169 | ); 170 | } 171 | 172 | // Y-axis wave generation 173 | $k = rand(0, 100); 174 | $yp = $this->scale * $this->Yperiod * rand(1, 2); 175 | 176 | for ($i = 0; $i < $height; $i++) { 177 | imagecopy( 178 | $this->im, 179 | $this->im, 180 | sin($k + $i / $yp) * $this->scale * $this->Yamplitude, 181 | $i-1, 182 | 0, 183 | $i, 184 | $width, 185 | 1 186 | ); 187 | } 188 | } 189 | 190 | protected function ReduceImage() { 191 | $imResampled = imagecreatetruecolor($this->width, $this->height); 192 | imagecopyresampled( 193 | $imResampled, 194 | $this->im, 195 | 0, 0, 0, 0, 196 | $this->width, 197 | $this->height, 198 | $this->width * $this->scale, 199 | $this->height * $this->scale 200 | ); 201 | imagedestroy($this->im); 202 | $this->im = $imResampled; 203 | } 204 | 205 | protected function WriteImage() { 206 | header('Content-type: image/png'); 207 | imagepng($this->im); 208 | } 209 | 210 | protected function Cleanup() { 211 | imagedestroy($this->im); 212 | } 213 | } 214 | 215 | --------------------------------------------------------------------------------