├── 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>" : "<$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 |
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 | prefix |
155 | String |
156 | 'form' |
157 | フォームの接頭辞。各フォームの要素の name と id の前に付きます。 |
158 |
159 |
160 | ajax |
161 | Bool |
162 | true |
163 | Ajax で POST を処理するかどうかを指定します。Ajax 側で呼び出す際は _ajax_call=1 も POST するようにして下さい。 |
164 |
165 |
166 | mail |
167 | Bool |
168 | false |
169 | 結果をメールで送信するかどうかを指定します。 |
170 |
171 |
172 | nonce |
173 | String |
174 | '' |
175 | nonce 機能を利用する際は必ず設定してください。これは nonce の乱数値を生成する時に使用します。値は16文字のできるだけ的当なものにして下さい。こちらのサイトトを利用すると良いかもしれません。 |
176 |
177 |
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 | $name |
204 | String |
205 | 項目の名前です。英数字(a-zA-Z0-9)・アンダースコア(_)・ハイフン(-)を使って下さい。<input type="checkbox"> と <select> で複数の項目を選択させる場合は、名前の語尾に + を付けます。 |
206 |
207 |
208 | $required |
209 | Bool |
210 | 必須であれば true を指定します。必須でないときは、false を指定するか、引数の省略ができます。 |
211 |
212 |
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 | 1 |
276 | Bool |
277 | 入力を必須にする場合は true を指定します。 |
278 |
279 |
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 | 1 |
308 | Number |
309 | 文字列の長さの下限を指定します。入力がこの値より短いとエラーがでます。 |
310 |
311 |
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 | 1 |
333 | Number |
334 | 値の上限を指定します。入力がこの値より大きいとエラーがでます。 |
335 |
336 |
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 | 1 |
364 | Number |
365 | 選択できる項目の個数の上限を指定します。選択された項目の個数がこの値より多いとエラーがでます。 |
366 |
367 |
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 | $call |
478 | String |
479 | どちらの関数から呼ばれたのかを取得する際に使います。値は 'filter' か 'format' です。 |
480 |
481 |
482 | $val |
483 | Mixed |
484 | そのまんま値、データです。 |
485 |
486 |
487 | $arg |
488 | Array |
489 | filter や format に渡された第1引数以降が配列として取得出来ます。 |
490 |
491 |
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 | from |
635 | String |
636 | 必須。メールの送信アドレスを指定します。これは決まりではありませんが、「表示名 <メールアドレス>」の形式で書くことを推奨します。 |
637 |
638 |
639 | cc |
640 | String |
641 | Carbon copy を指定します。 |
642 |
643 |
644 | bcc |
645 | String |
646 | Blind carbon copy を指定します。 |
647 |
648 |
649 | reply |
650 | String |
651 | Reply-to を指定します。 |
652 |
653 |
654 | to |
655 | String |
656 | 必須。メールの受信先を指定します。メールアドレスを直接書くこともできますが、フォームの名前を入れればそれに宛てて送信することもできます。 |
657 |
658 |
659 | subject |
660 | String |
661 | 必須。件名を指定します。 |
662 |
663 |
664 | body |
665 | String |
666 | 必須。メールの本文を指定します。 |
667 |
668 |
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 | {{DATE}} |
697 | 現在の日付 |
698 |
699 |
700 | {{TIME}} |
701 | 現在の時刻 |
702 |
703 |
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 |
--------------------------------------------------------------------------------