1: <?php
2: /**
3: * @filesource Kotchasan/InputItem.php
4: *
5: * @copyright 2016 Goragod.com
6: * @license http://www.kotchasan.com/license/
7: *
8: * @see http://www.kotchasan.com/
9: */
10:
11: namespace Kotchasan;
12:
13: /**
14: * Input Object
15: *
16: * @author Goragod Wiriya <admin@goragod.com>
17: *
18: * @since 1.0
19: */
20: class InputItem
21: {
22: /**
23: * ตัวแปรบอกประเภท Input เช่น GET POST SESSION COOKIE
24: *
25: * @var string|null
26: */
27: protected $type;
28: /**
29: * ตัวแปรเก็บค่าของ Object
30: *
31: * @var mixed
32: */
33: protected $value;
34:
35: /**
36: * Class Constructer
37: *
38: * @param mixed $value null (default)
39: * @param string|null $type ประเภท Input เช่น GET POST SESSION COOKIE หรือ null ถ้าไม่ได้มาจากรายการข้างต้น
40: */
41: public function __construct($value = null, $type = null)
42: {
43: $this->value = $value;
44: $this->type = $type;
45: }
46:
47: /**
48: * คืนค่าตามข้อมูลที่ส่งมา
49: *
50: * @return mixed
51: */
52: public function all()
53: {
54: return $this->value;
55: }
56:
57: /**
58: * คืนค่า ค่าสี String หรือ แอเรย์ของ String
59: *
60: * @assert create('#000')->color() [==] '#000'
61: * @assert create('red')->color() [==] 'red'
62: *
63: * @return string|array
64: */
65: public function color()
66: {
67: return $this->filter('\#a-zA-Z0-9');
68: }
69:
70: /**
71: * สร้าง Object
72: *
73: * @param mixed $value
74: * @param string|null $type ประเภท Input เช่น GET POST SESSION COOKIE หรือ null ถ้าไม่ได้มาจากรายการข้างต้น
75: *
76: * @return \static
77: */
78: public static function create($value, $type = null)
79: {
80: return new static($value, $type);
81: }
82:
83: /**
84: * วันที่และเวลา
85: * คืนค่า null ถ้าข้อมูลวันที่ว่างเปล่าหรือมีรูปแบบไม่ถูกต้อง
86: *
87: * @assert create('2016-01-01 20:20:20')->date() [==] '2016-01-01 20:20:20'
88: * @assert create('2016-01-01 20:20:20')->date() [==] '2016-01-01 20:20:20'
89: * @assert create('2016-01-01 20:20:20')->date(true) [==] '2016-01-01 20:20:20'
90: * @assert create('20:20:20')->date() [==] '20:20:20'
91: * @assert create('20:20')->date() [==] '20:20'
92: * @assert create('20:20')->date(true) [==] '20:20:00'
93: * @assert create('2016-01-01')->date() [==] '2016-01-01'
94: * @assert create('')->date() [==] null
95: * @assert create(null)->date() [==] null
96: *
97: * @param bool $strict true ตรวจสอบความถูกต้องของวันที่ด้วย, false (default) ไม่ต้องตรวจสอบ
98: *
99: * @return string|array|null
100: */
101: public function date($strict = false)
102: {
103: $ret = $this->filter('\d\s\-:');
104: if ($strict) {
105: if (preg_match('/^([0-9]{4,4}\-[0-9]{1,2}\-[0-9]{1,2})?[\s]{0,}([0-9]{1,2}:[0-9]{1,2})?(:[0-9]{1,2})?$/', $ret, $match)) {
106: $ret = empty($match[1]) ? '' : $match[1];
107: if (!empty($match[2])) {
108: $ret .= ($ret == '' ? '' : ' ').(empty($match[2]) ? '' : $match[2].(empty($match[3]) ? ':00' : $match[3]));
109: }
110: } else {
111: $ret = null;
112: }
113: }
114: return empty($ret) ? null : $ret;
115: }
116:
117: /**
118: * เวลา
119: * คืนค่า null ถ้าข้อมูลเวลาว่างเปล่า หรือเท่าบ --:--.
120: *
121: * @assert create('20:20:20')->time() [==] '20:20:20'
122: * @assert create('--:--')->time() [==] null
123: * @assert create('')->time() [==] null
124: * @assert create('20:20:20')->time() [==] '20:20:20'
125: * @assert create('20:20')->time() [==] '20:20'
126: * @assert create('20:20')->time(true) [==] '20:20:00'
127: *
128: * @param bool $strict true ตรวจสอบความถูกต้องของวันที่ด้วย, false (default) ไม่ต้องตรวจสอบ
129: *
130: * @return string|array|null
131: */
132: public function time($strict = false)
133: {
134: if (!empty($this->value) && preg_match('/^([0-9]{1,2}:[0-9]{1,2})?(:[0-9]{1,2})?$/', $this->value, $match)) {
135: if (empty($match[2])) {
136: $match[2] = $strict ? ':00' : '';
137: }
138: return $match[1].$match[2];
139: }
140: return null;
141: }
142:
143: /**
144: * ลบ tag, BBCode ออก ให้เหลือแต่ข้อความล้วน
145: * ลบช่องว่างไม่เกิน 1 ช่อง ไม่ขึ้นบรรทัดใหม่
146: * และลบช่องว่างหัวท้าย
147: * ใช้เป็น description
148: *
149: *
150: * @assert create("ท.ด(ส )อ\"บ'\r\n\t<?php echo '555'?>")->description() [==] 'ท.ด(ส )อ บ'
151: * @assert create('ทดสอบ<style>body {color: red}</style>')->description() [==] 'ทดสอบ'
152: * @assert create('ทดสอบ<b>ตัวหนา</b>')->description() [==] 'ทดสอบตัวหนา'
153: * @assert create('ทดสอบ{LNG_Language name}')->description() [==] 'ทดสอบ'
154: * @assert create('ทดสอบ[code]ตัวหนา[/code]')->description() [==] 'ทดสอบ'
155: * @assert create('ทดสอบ[b]ตัวหนา[/b]')->description() [==] 'ทดสอบตัวหนา'
156: * @assert create('2 > 1 < 3 > 2{WIDGET_XXX}')->description() [==] '2 > 1 < 3 > 2'
157: * @assert create('ทดสอบ<!--WIDGET_XXX-->')->description() [==] 'ทดสอบ'
158: * @assert create('ท&ด"\ /__ส-อ+บ')->description() [==] 'ท ด \ /__ส-อ+บ'
159: * @assert create('ภาคภูมิ')->description(2) [==] 'ภา'
160: * @assert create('U1.username ทดสอบภาษาไทย')->description(5) [throws] \Kotchasan\InputItemException
161: *
162: * @param int $len ความยาวของ description หมายถึงคืนค่าทั้งหมด
163: *
164: * @return string|array
165: */
166: public function description($len = 0)
167: {
168: $patt = array(
169: /* style */
170: '@<(script|style)[^>]*?>.*?</\\1>@isu' => '',
171: /* tag */
172: '@<[a-z\/\!\?][^>]{0,}>@isu' => '',
173: /* keywords */
174: '/{(WIDGET|LNG)_[\w\s\.\-\'\(\),%\/:&\#;]+}/su' => '',
175: /* BBCode (code) */
176: '/(\[code(.+)?\]|\[\/code\]|\[ex(.+)?\])/ui' => '',
177: /* BBCode ทั่วไป [b],[i] */
178: '/\[([a-z]+)([\s=].*)?\](.*?)\[\/\\1\]/ui' => '\\3',
179: /* ตัวอักษรที่ไม่ต้องการ */
180: '/(”|"| |&|[\r\n\s\t\"\']){1,}/isu' => ' ',
181: );
182: $text = trim(preg_replace(array_keys($patt), array_values($patt), $this->value));
183: return $this->checkValue($this->cut($text, $len));
184: }
185:
186: /**
187: * ลบ PHP tag และแปลง \ { } สำหรับใช้รับข้อมูลจาก editor
188: * เช่นเนื้อหาของบทความ
189: *
190: * @assert create('{ทด\/สอบ<?php echo "555"?>}')->detail() [==] '{ทด\/สอบ}'
191: * @assert create('<?=555?>U1.username')->detail() [throws] \Kotchasan\InputItemException
192: *
193: * @return string|array
194: */
195: public function detail()
196: {
197: return $this->checkValue(preg_replace(
198: array('#<\?(.*?)\?>#is', '#\{#', '#\}#', '#\\\#'),
199: array('', '{', '}', '\'),
200: $this->value
201: ));
202: }
203:
204: /**
205: * ตรวจสอบว่ามีตัวแปรส่งมาหรือไม่
206: * คืนค่า true ถ้ามีตัวแปรส่งมา
207: *
208: * @return bool
209: */
210: public function exists()
211: {
212: return $this->type !== null;
213: }
214:
215: /**
216: * ฟังก์ชั่นแทนที่อักขระที่ไม่ต้องการ
217: *
218: * @assert create('admin,1234')->filter('0-9a-zA-Z,') [==] 'admin,1234'
219: * @assert create('adminกข,12ฟ34')->filter('0-9a-zA-Z,') [==] 'admin,1234'
220: * @assert create('U1.username')->filter('a-zA-Z0-9\.') [throws] \Kotchasan\InputItemException
221: *
222: * @param string $format Regular Expression อักขระที่ยอมรับ เช่น \d\s\-:
223: * @param string $replace ข้อความแทนที่
224: *
225: * @return string|array
226: */
227: public function filter($format, $replace = '')
228: {
229: return $this->checkValue(trim(preg_replace('/[^'.$format.']/', $replace, $this->value)));
230: }
231:
232: /**
233: * ตรวจสอบว่ามาจาก $_COOKIE หรือไม่
234: * คืนค่า true ถ้ามาจาก $_COOKIE.
235: *
236: * @return bool
237: */
238: public function isCookie()
239: {
240: return $this->type === 'COOKIE';
241: }
242:
243: /**
244: * ตรวจสอบว่ามาจาก $_GET หรือไม่
245: * คืนค่า true ถ้ามาจาก $_GET
246: *
247: * @return bool
248: */
249: public function isGet()
250: {
251: return $this->type === 'GET';
252: }
253:
254: /**
255: * ตรวจสอบว่ามาจาก $_POST หรือไม่
256: * คืนค่า true ถ้ามาจาก $_POST
257: *
258: * @return bool
259: */
260: public function isPost()
261: {
262: return $this->type === 'POST';
263: }
264:
265: /**
266: * ตรวจสอบว่ามาจาก $_SESSION หรือไม่
267: * คืนค่า true ถ้ามาจาก $_SESSION
268: *
269: * @return bool
270: */
271: public function isSession()
272: {
273: return $this->type === 'SESSION';
274: }
275:
276: /**
277: * ลบ tags และ ลบช่องว่างไม่เกิน 1 ช่อง ไม่ขึ้นบรรทัดใหม่
278: * และลบช่องว่างหัวท้าย
279: * ใช้เป็น tags หรือ keywords
280: *
281: * @assert create("<b>ทด</b> \r\nสอบ")->keywords() [==] 'ทด สอบ'
282: * @assert create('<b>U1.username</b> ทดสอบ')->keywords(5) [throws] \Kotchasan\InputItemException
283: *
284: * @param int $len ความยาวของ keywords หมายถึงคืนค่าทั้งหมด
285: *
286: * @return string|array
287: */
288: public function keywords($len = 0)
289: {
290: $text = trim(preg_replace('/[\r\n\s\t\"\'<>]{1,}/isu', ' ', strip_tags($this->value)));
291: return $this->checkValue($this->cut($text, $len));
292: }
293:
294: /**
295: * ตัวเลข หรือ แอเรย์ของ ตัวเลข
296: *
297: * @assert create(12345)->number() [==] '12345'
298: * @assert create(0.12345)->number() [==] '012345'
299: * @assert create('ทด0123สอ4บ5')->number() [==] '012345'
300: *
301: * @return string|array
302: */
303: public function number()
304: {
305: return $this->filter('\d');
306: }
307:
308: /**
309: * รับค่าสำหรับ password อักขระทุกตัวไม่มีช่องว่าง
310: *
311: * @return string|array
312: */
313: public function password()
314: {
315: return $this->checkValue(Text::password($this->value));
316: }
317:
318: /**
319: * ฟังก์ชั่นรับข้อความ ยอมรับอักขระทั้งหมด
320: * และแปลง ' เป็น '
321: * และลบช่องว่างหัวท้าย
322: *
323: * @assert create("ทด'สอบ")->quote() [==] "ทด'สอบ"
324: * @assert create(' U1.username ')->quote() [throws] \Kotchasan\InputItemException
325: *
326: * @return string|array
327: */
328: public function quote()
329: {
330: return $this->checkValue(str_replace("'", ''', trim($this->value)));
331: }
332:
333: /**
334: * ฟังก์ชั่น แปลง & " ' < > \ เป็น HTML entities
335: * และลบช่องว่างหัวท้าย
336: * ใช้แปลงค่าที่รับจาก input ที่ไม่ยอมรับ tag
337: *
338: * @assert create(" ทด\/สอบ<?php echo '555'?> ")->text() [==] 'ทด\/สอบ<?php echo '555'?>'
339: * @assert create('U1.username')->text() [throws] \Kotchasan\InputItemException
340: *
341: * @return string|array
342: */
343: public function text()
344: {
345: return $this->checkValue(trim(Text::htmlspecialchars($this->value)));
346: }
347:
348: /**
349: * แปลง < > \ { } เป็น HTML entities และแปลง \n เป็น <br>
350: * และลบช่องว่างหัวท้าย
351: * ใช้รับข้อมูลที่มาจาก textarea
352: *
353: * @assert create("ทด\/สอบ\n<?php echo '$555'?>")->textarea() [==] "ทด\/สอบ\n<?php echo '$555'?>"
354: * @assert create('U1.username')->textarea() [throws] \Kotchasan\InputItemException
355: *
356: * @return string|array
357: */
358: public function textarea()
359: {
360: return $this->checkValue(trim(preg_replace(
361: array('/</s', '/>/s', '/\\\/s', '/\{/', '/\}/', '/\$/'),
362: array('<', '>', '\', '{', '}', '$'),
363: $this->value
364: )));
365: }
366:
367: /**
368: * คืนค่าเป็น boolean หรือ แอเรย์ของ boolean
369: *
370: * @assert create(true)->toBoolean() [==] 1
371: * @assert create(false)->toBoolean() [==] 0
372: * @assert create(1)->toBoolean() [==] 1
373: * @assert create(0)->toBoolean() [==] 0
374: * @assert create(null)->toBoolean() [==] 0
375: *
376: * @return bool|array
377: */
378: public function toBoolean()
379: {
380: return empty($this->value) ? 0 : 1;
381: }
382:
383: /**
384: * คืนค่าเป็น double
385: *
386: * @assert create(0.454)->toDouble() [==] 0.454
387: * @assert create(0.545)->toDouble() [==] 0.545
388: * @assert create('15,362.454')->toDouble() [==] 15362.454
389: *
390: * @return float|array
391: */
392: public function toDouble()
393: {
394: return (float) str_replace(',', '', $this->value);
395: }
396:
397: /**
398: * คืนค่าเป็น float หรือ แอเรย์ของ float
399: *
400: * @assert create(0.454)->toFloat() [==] 0.454
401: * @assert create(0.545)->toFloat() [==] 0.545
402: *
403: * @return float|array
404: */
405: public function toFloat()
406: {
407: return (float) $this->value;
408: }
409:
410: /**
411: * คืนค่าเป็น integer หรือ แอเรย์ของ integer
412: *
413: * @assert create(0.454)->toInt() [==] 0
414: * @assert create(2.945)->toInt() [==] 2
415: *
416: * @return int|array
417: */
418: public function toInt()
419: {
420: return (int) $this->value;
421: }
422:
423: /**
424: * คืนค่าเป็น Object หรือ แอเรย์ของ Object
425: *
426: * @assert create('test')->toObject() [==] (object)'test'
427: *
428: * @return object|array
429: */
430: public function toObject()
431: {
432: return (object) $this->value;
433: }
434:
435: /**
436: * คืนค่าเป็น string หรือ แอเรย์ของ string หรือ null
437: * ***ฟังก์ชั่นนี้ใช้สำหรับรับค่าเป็น string โดยไม่ผ่านการตรวจสอบใดๆทั้งสิ้น
438: * ควรใช้อย่างระมัดระวัง***
439: *
440: * @assert create('ทดสอบ')->toString() [==] 'ทดสอบ'
441: * @assert create('1')->toString() [==] '1'
442: * @assert create(1)->toString() [==] '1'
443: * @assert create(null)->toString() [==] null
444: *
445: * @return string|array|null
446: */
447: public function toString()
448: {
449: return $this->value === null ? null : (string) $this->value;
450: }
451:
452: /**
453: * คืนค่าเป็น array เท่านั้น
454: * ถ้าไม่ใช่แอเรย์จะ error
455: *
456: * @assert create(array('one', 'two'))->toArray() [==] array('one', 'two')
457: *
458: * @return array
459: */
460: public function toArray()
461: {
462: return $this->value;
463: }
464:
465: /**
466: * แปลง tag และ ลบช่องว่างไม่เกิน 1 ช่อง ไม่ขึ้นบรรทัดใหม่
467: * เช่นหัวข้อของบทความ
468: *
469: * @param bool $double_encode true (default) แปลง รหัส HTML เช่น & เป็น &amp;, false ไม่แปลง
470: *
471: * @assert create(' ทด\/สอบ'."\r\n\t".'<?php echo \'555\'?> ')->topic() [==] 'ทด\/สอบ <?php echo '555'?>'
472: * @assert create('U1.username')->topic() [throws] \Kotchasan\InputItemException
473: *
474: * @return string|array
475: */
476: public function topic($double_encode = true)
477: {
478: return $this->checkValue(Text::topic($this->value, $double_encode));
479: }
480:
481: /**
482: * แปลง tag ไม่แปลง &
483: * และลบช่องว่างหัวท้าย
484: * สำหรับ URL หรือ email
485: *
486: * @assert create(" http://www.kotchasan.com?a=1&b=2&c=3 ")->url() [==] 'http://www.kotchasan.com?a=1&b=2&c=3'
487: * @assert create("javascript:alert('xxx')")->url() [==] 'alertxxx'
488: * @assert create("http://www.xxx.com/javascript/")->url() [==] 'http://www.xxx.com/javascript/'
489: * @assert create('U1.username')->url() [throws] \Kotchasan\InputItemException
490: *
491: * @return string|array
492: */
493: public function url()
494: {
495: return $this->checkValue(Text::url($this->value));
496: }
497:
498: /**
499: * รับค่าอีเมลและหมายเลขโทรศัพท์เท่านั้น
500: *
501: * @assert create(' admin@demo.com')->username() [==] 'admin@demo.com'
502: * @assert create('012 3465')->username() [==] '0123465'
503: * @assert create('U1.username')->username() [throws] \Kotchasan\InputItemException
504: *
505: * @return string|array
506: */
507: public function username()
508: {
509: return $this->checkValue(Text::username($this->value));
510: }
511:
512: /**
513: * ตัดสตริงค์
514: *
515: * @param string $str
516: * @param int $len ความยาวที่ต้องการ
517: *
518: * @return string
519: */
520: private function cut($str, $len)
521: {
522: if (!empty($len) && !empty($str)) {
523: $str = mb_substr($str, 0, (int) $len);
524: }
525: return $str;
526: }
527:
528: /**
529: * ตรวจสอบว่าเป็นชื่อคอลัมน์หรือไม่
530: * เช่น U.user_id U1.user_id
531: *
532: * @param string $value
533: *
534: * @return string
535: *
536: * @throws \Kotchasan\InputItemException ถ้า $value เป็นชื่อคอลัมน์
537: */
538: private function checkValue($value)
539: {
540: if (preg_match('/^[A-Z]{1,1}[0-9]{0,1}\.[a-z0-9_]+$/', $value)) {
541: throw new \Kotchasan\InputItemException('Cannot use "'.$value.'"');
542: }
543: return $value;
544: }
545: }
546: