PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/raxan/sdk/raxan/pdi/shared/raxan.datasanitizer.php

http://raxan.googlecode.com/
PHP | 605 lines | 406 code | 41 blank | 158 comment | 40 complexity | 55cf701b6e7c9ab1d1697240c805e0c1 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * Raxan Data Sanitizer/Filter
  4. * Copyright (c) 2011 Raymond Irving (http://raxanpdi.com)
  5. * @package Raxan
  6. */
  7. /**
  8. * Provides APIs to filter, sanitizer and format user inputs and file uploads
  9. * @property RaxanDateTime $_date
  10. */
  11. class RaxanDataSanitizer {
  12. protected static $validators = array();
  13. protected static $badCharacters = array("\r","\n","\t","\x00","\x1a");
  14. protected $_data;
  15. protected $_date;
  16. protected $_directInp;
  17. protected $_charset;
  18. public function __construct($array=null,$charset = null) {
  19. $this->_charset = $charset ? $charset : Raxan::config('site.charset');
  20. $this->setDataArray($array);
  21. }
  22. // handle calls for custom validators
  23. public function __call($name,$args){
  24. $validator = isset(self::$validators[$name]) ? self::$validators[$name] : '';
  25. if (!$validator) {
  26. throw new Exception('Undefined Method \''.$name.'\'');
  27. }
  28. $isPattern = substr($validator,0,1)=='#';
  29. $value = $this->value($args[0]);
  30. if ($isPattern) return preg_match(substr($validator,1),$value);
  31. elseif (is_callable($validator)) {
  32. $fn = $validator;
  33. if (is_string($fn)) $rt = $fn($value); // function callback
  34. else $rt = $fn[0]->{$fn[1]}($value); // object callback
  35. return $rt ? $rt : false;
  36. }
  37. else {
  38. throw new Exception('Unable to execute validator callback function or method: '.print_r($validator,true));
  39. }
  40. }
  41. /**
  42. * Returns the sanitized text for the specified key. All html charcters will be removed.
  43. * @return String
  44. */
  45. public function __get($name) {
  46. return $this->textVal($name);
  47. }
  48. /**
  49. * Sets the value for a key to be sanitized or formatted.
  50. * Example:
  51. * $format->keyName = 'Mary Jane<br />';
  52. * echo $format->textVal('keyName');
  53. * @return String
  54. */
  55. public function __set($name,$value) {
  56. $this->_data[$name] = $value;
  57. }
  58. /**
  59. * Adds a custom data validator using regex patterns or callback function
  60. * Used as a wrapper to the RaxanDataSanitizer::addDataValidator() static method
  61. */
  62. public function addValidator($name,$pattern){
  63. self::addDataValidator($name,$pattern);
  64. }
  65. /**
  66. * Returns an alphanumeric value for the specified field name
  67. * @return string If input is an array then an array of alphanumeric values will be returned
  68. */
  69. public function alphanumericVal($key) {
  70. $v = $this->value($key);
  71. if (!is_array($v)) $v = trim(((preg_match('/^[a-zA-Z0-9]+$/',$t = trim($v))) ? $t : ''));
  72. else foreach ($v as $k=>$b){
  73. $v[$k] = trim(((preg_match('/^[a-zA-Z0-9]+$/',$t = trim($b))) ? $t : ''));
  74. }
  75. return $v;
  76. }
  77. /**
  78. * Returns a date value for the specified field name
  79. * @param string $format
  80. * @return mixed Returns a date/time string value based on the $format parameter or null if value is not a valid date. If input is an array then an array of date values is returned
  81. */
  82. public function dateVal($key, $format = null) {
  83. return ($v = $this->formatDate($key,$format)) ? $v : null;
  84. }
  85. /**
  86. * Enables direct data input. This will allow values to be passed directly to validator functions.
  87. * @example
  88. * <p>$sanitize->enableDirectInput();</p>
  89. * <p>echo $sanitize->textVal('This is a &lt;strong&gt;text&lt;/strong&gt; message.');</p>
  90. * @param boolean $state Defaults to true
  91. */
  92. public function enableDirectInput($state = true) {
  93. $this->_directInp = $state;
  94. }
  95. /**
  96. * Returns sanitized email address for the selected field
  97. * @return string Returns sanitized email address or an empty string if input value is not a valid email address. If input is an array then an array of email values is returned
  98. */
  99. public function emailVal($key) {
  100. $v = $this->value($key);
  101. if (!is_array($v)) $v = trim(str_replace(self::$badCharacters,'',strip_tags($v)));
  102. else foreach ($v as $k=>$b){
  103. $v[$k] = trim(str_replace(self::$badCharacters,'',strip_tags($b)));
  104. }
  105. return $v;
  106. }
  107. /**
  108. * Returns text with special xml/html characters encoded for the selected field
  109. * @return string If input is an array then an array of escaped values is returned
  110. */
  111. public function escapeVal($key) {
  112. $v = $this->value($key);
  113. if (!is_array($v)) $v = htmlspecialchars($v);
  114. else foreach ($v as $k=>$b){
  115. $v[$k] = htmlspecialchars($b);
  116. }
  117. return $v;
  118. }
  119. /**
  120. * Returns the content of an uploaded file based on the selected field name
  121. * @param string $fld Form element field name
  122. * @return string
  123. */
  124. public function fileContent($fld) {
  125. $fl = isset($_FILES[$fld]) ? $_FILES[$fld]['tmp_name'] : '';
  126. return (is_file($fl)) ? file_get_contents($fl) : '';
  127. }
  128. /**
  129. * Copies an uploaded files (based on the selected field name) to the specified destination.
  130. * @param string $fld Form element field name
  131. * @param string $dest Destination path and file name
  132. * @return boolean
  133. */
  134. public function fileCopy($fld,$dest) {
  135. $fl = isset($_FILES[$fld]) ? $_FILES[$fld] : null;
  136. if($fl) return copy($fl['tmp_name'],$dest);
  137. else return false;
  138. }
  139. /**
  140. * Returns a total number of file uploaded
  141. * @return int
  142. */
  143. public function fileCount() {
  144. return isset($_FILES) ? count($_FILES) : 0;
  145. }
  146. /**
  147. * Returns an array containing the width, height and type for the uploaded image file
  148. * @param string $fld Form element field name
  149. * @return mixed Array or null on error
  150. */
  151. public function fileImageSize($fld) {
  152. $fl = isset($_FILES[$fld]) ? $_FILES[$fld] : null;
  153. $fl = $fl ? $fl['tmp_name'] : null;
  154. return Raxan::imageSize($fl);
  155. }
  156. /**
  157. * Resamples (convert/resize) the uploaded image. You can specify a new width, height and type
  158. * @param string $fld Form element field name
  159. * @see Raxan::imageResample()
  160. * @return boolean
  161. */
  162. public function fileImageResample($fld,$w,$h,$type=null) {
  163. $fl = isset($_FILES[$fld]) ? $_FILES[$fld] : null;
  164. $fl = $fl ? $fl['tmp_name'] : null;
  165. return Raxan::imageResample($fl,$w,$h,$type);
  166. }
  167. /**
  168. * Moves an uploaded files (based on the selected field name) to the specified destination.
  169. * @param string $fld Form element field name
  170. * @param string $dest Destination path and file name
  171. * @return boolean
  172. */
  173. public function fileMove($fld, $dest) {
  174. $fl = isset($_FILES[$fld]) ? $_FILES[$fld] : null;
  175. if($fl) return move_uploaded_file($fl['tmp_name'], $dest);
  176. else return false;
  177. }
  178. /**
  179. * Returns the original name of the uploaded file based on the selected field name
  180. * @param string $fld Form element field name
  181. * @return string
  182. */
  183. public function fileOrigName($fld) {
  184. return isset($_FILES[$fld]) ?
  185. str_replace(array('/','\\'),'',strip_tags($_FILES[$fld]['name'])) : '';
  186. }
  187. /**
  188. * Returns the size of the uploaded file based on the selected field name
  189. * @param string $fld Form element field name
  190. * @return integer
  191. */
  192. public function fileSize($fld) {
  193. return isset($_FILES[$fld]) ? $_FILES[$fld]['size'] : '';
  194. }
  195. /**
  196. * Returns the file type (as reported by browser) of an uploaded file based on the selected field name
  197. * @param string $fld Form element field name
  198. * @return string
  199. */
  200. public function fileType($fld) {
  201. return isset($_FILES[$fld]) ? $_FILES[$fld]['type'] : '';
  202. }
  203. /**
  204. * Returns the file upload error code for uploaded file
  205. * @param string $fld Form element field name
  206. * @return string
  207. */
  208. public function fileUploadError($fld) {
  209. return isset($_FILES[$fld]) ? $_FILES[$fld]['error'] : '';
  210. }
  211. /**
  212. * Returns the temporary file name and path of the uploaded file based on the selected field name
  213. * @param string $fld Form element field name
  214. * @return string
  215. */
  216. public function fileTmpName($fld) {
  217. return isset($_FILES[$fld]) ? $_FILES[$fld]['tmp_name'] : '';
  218. }
  219. /**
  220. * Returns associative array of filtered/sanitized values
  221. * @param mixed $keyFields Optional. Comma (,) delimitted key/field names or associative array of field names and filter type. Default to all keys/fields with textVal filter applied
  222. * @return array
  223. */
  224. public function filterValues($keyFields = null) {
  225. $data = $fields = array();
  226. if (($kIsArray = is_array($keyFields))) $fields = array_keys($keyFields);
  227. else $fields = ($keyFields===null) ? array_keys($this->_data) : explode(',',(string)$keyFields) ;
  228. $oldDirectInp = $this->_directInp;
  229. $this->_directInp = true; // activate direct input mode
  230. foreach($fields as $k) {
  231. $k = trim($k);
  232. $v = isset($this->_data[$k]) ? $this->_data[$k] : null;
  233. $p1 = $p2 = $p3 = null;
  234. $filter = ($kIsArray && isset($keyFields[$k])) ? $keyFields[$k] : 'text';
  235. if (is_array($filter)) {
  236. $p1 = isset($filter[1]) ? $filter[1] : null;
  237. $p2 = isset($filter[2]) ? $filter[2] : null;
  238. $p3 = isset($filter[3]) ? $filter[3] : null;
  239. $filter = $filter[0];
  240. }
  241. if (strpos('alphanumeric,date,email,escape,float,html,int,match,number,text,timestamp,url',$filter)===false) $filter = '';
  242. if (is_scalar($v)) $data[$k] = $filter ? $this->{$filter.'Val'}($v,$p1,$p2,$p3) : null; // directly pass value to filter function
  243. else if (is_array($v)) {
  244. $data[$k] = array();
  245. foreach ($v as $a=>$b) $data[$k][$a] = $filter ? $this->{$filter.'Val'}($b,$p1,$p2,$p3) : null;
  246. }
  247. }
  248. $this->_directInp = $oldDirectInp; //restore input mode
  249. return $data;
  250. }
  251. /**
  252. * Converts input value/key to a float value
  253. * @return float Returns float if value is numeric or null if there was an error. If input is an array then an array of float values is returned
  254. */
  255. public function floatVal($key,$decimal = null) {
  256. $v = $this->value($key);
  257. if (!is_array($v)) {
  258. $v = is_numeric($v) ? (float)$v : null;
  259. $v = $v && is_numeric($decimal) ? number_format($v,$decimal) : $v;
  260. }
  261. else foreach ($v as $k=>$b){
  262. $b = is_numeric($b) ? (float)$b : null;
  263. $v[$k] = $b && is_numeric($decimal) ? number_format($b,$decimal) : $b;
  264. }
  265. return $v;
  266. }
  267. /**
  268. * Returns formated date value
  269. * @param string $key Key name or input value (direct input must be enabled)
  270. * @param string $format Date format
  271. * @return string If input is an array then an array of formated date values is returned
  272. */
  273. public function formatDate($key,$format = null) {
  274. if ($format===null) $format = 'iso';
  275. $noTrans = false;
  276. switch ($format) {
  277. case 'iso':
  278. case 'mysql':
  279. $format = 'Y-m-d'; $noTrans = true;
  280. break;
  281. case 'mssql':
  282. $format = 'm/d/Y'; $noTrans = true;
  283. break;
  284. case 'short':
  285. $format = Raxan::locale('date.short');
  286. break;
  287. case 'long':
  288. $format = Raxan::locale('date.long');
  289. break;
  290. }
  291. if (!isset($this->_date)) $this->_date = Raxan::cDate();
  292. $v = $this->value($key);
  293. if (!($isa = is_array($v))) {
  294. try {
  295. $v = $v ? $this->_date->format($format,$v,$noTrans) : '';
  296. } catch (Exception $e) { $v = ''; }
  297. }
  298. else foreach ($v as $k=>$b) {
  299. try {
  300. $b = $b ? $this->_date->format($format,$b,$noTrans) : '';
  301. } catch (Exception $e) { $b = ''; }
  302. $v[$k] = $b;
  303. }
  304. return $v;
  305. }
  306. /**
  307. * Returns formatted money value based on locale settings
  308. * @param string $key Key name or input value (direct input must be enabled)
  309. * @param int $decimal Optional. Total number of decimal places to return
  310. * @param string $symbol Optional. Currency symbol
  311. * @return string If input is an array then an array of formated values is returned
  312. */
  313. public function formatMoney($key,$decimal = null,$symbol = null) {
  314. $mf = Raxan::locale('money.format',$symbol);
  315. $ds = Raxan::locale('decimal.separator');
  316. $ts = Raxan::locale('thousand.separator');
  317. $cl = Raxan::locale('currency.location');
  318. $cs = $symbol ? $symbol : Raxan::locale('currency.symbol');
  319. $value = $this->value($key);
  320. $isa = is_array($value);
  321. if(!$isa) $value = array($value);
  322. foreach($value as $k=>$v) {
  323. if (!is_numeric($v)) $v = '';
  324. else {
  325. $v = number_format($v,$decimal,$ds,$ts);
  326. if ($mf) $v = money_format($mf, $v); // @todo: Test money_format;
  327. else $v = $cl=='rt' ? $v.$cs : $cs.$v;
  328. }
  329. $value[$k] = $v;
  330. }
  331. return $isa ? $value : $value[0];
  332. }
  333. /**
  334. * Returns formatted number value based on locale settings
  335. * @param string $key Key name or input value (direct input must be enabled)
  336. * @param int $decimal Optional. Total number of decimal places to return
  337. * @return string If input is an array then an array of formatted numeric values is returned
  338. */
  339. public function formatNumber($key,$decimal = null) {
  340. $ds = Raxan::locale('decimal.separator');
  341. $ts = Raxan::locale('thousand.separator');
  342. $v = $this->value($key);
  343. if (!is_array($v)) $v = is_numeric($v) ? number_format($v,$decimal,$ds,$ts) : '';
  344. else foreach ($v as $k=>$b){
  345. $v[$k] = is_numeric($b) ? number_format($b,$decimal,$ds,$ts) : '';
  346. }
  347. return $v;
  348. }
  349. /**
  350. * Sanitized html by removing javascript tags and inline events
  351. * @param string $key Key name or input value (direct input must be enabled)
  352. * @param string $allowable Optional. Allowable html tags. Example <p><a>
  353. * @param string $allowStyle Optional. Allow css styles inside html. Defaults to true
  354. * @return string If input is an array then an array of html values is returned
  355. */
  356. public function htmlVal($key,$allowable = null,$allowStyle = true) {
  357. $value = $this->value($key);
  358. $isa = is_array($value);
  359. if (!$isa) $value = array($value);
  360. foreach($value as $k => $v) { // support for array input
  361. if ($allowable==null) {
  362. // remove script & style tags
  363. $rx1 = '#<script[^>]*?>.*?</script>'.(!$allowStyle ? '|<style[^>]*?>.*?</style>' :'').'#is';
  364. $v = preg_replace($rx1,'',$v);
  365. }
  366. else {
  367. // allow specified html tags
  368. $v = strip_tags($v,$allowable);
  369. }
  370. // nutralize inline styles and events
  371. $rx1 = '/<\w+\s*.*(on\w+\s*=|style\s*=)[^>]*?>/is';
  372. $rx2 = '/on\w+\s*=\s*'.(!$allowStyle ? '|style\s*=\s*' : '').'/is';
  373. $rx3 = '/nXtra=(["\']).*?\1|javascript\:|\s*expression\s*.*\(.*[^\)]?\)/is';
  374. if (preg_match_all($rx1,$v,$m)) {
  375. $tags = preg_replace($rx2,'nXtra=',$m[0]); // nutralize inline scripts/styles
  376. $tags = preg_replace($rx3,'',$tags);
  377. $v = str_replace($m[0],$tags,$v);
  378. }
  379. $value[$k] = $v;
  380. }
  381. return $isa ? $value : $value[0];
  382. }
  383. /**
  384. * Converts input value/key to an integer value
  385. * @return int Returns interger if value is numeric or null if there was an error
  386. */
  387. public function intVal($key) {
  388. $v = $this->value($key);
  389. if (!is_array($v)) $v = is_numeric($v) ? (int)$v : null;
  390. else foreach ($v as $k=>$b){
  391. $v[$k] = is_numeric($b) ? (int)$b : null;
  392. }
  393. return $v;
  394. }
  395. /**
  396. * Returns true if the selected field contains a valid date
  397. * @param string $key Key name or input value (direct input must be enabled)
  398. * @param string $format Optional date input format. Accepts the same format as formatDate()
  399. * @return boolean
  400. */
  401. public function isDate($key,$format = null) {
  402. try {
  403. if ($format===null) return $this->timestampVal($key) > 0 ? true : false;
  404. else {
  405. $dt = trim($this->value($key));
  406. return $dt && strtolower($dt) === strtolower($this->formatDate($key,$format));
  407. }
  408. }
  409. catch(Exception $e) { return false; }
  410. }
  411. /**
  412. * Returns true if the selected field contains a valid email address
  413. * @return boolean
  414. */
  415. public function isEmail($key) {
  416. // Based on Regex by Geert De Deckere. http://pastie.textmate.org/159503
  417. $regex = '/^[-_a-z0-9\'+^~]++(?:\.[-_a-z0-9\'+^~]+)*+@'.
  418. '(?:(?![-.])[-a-z0-9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d++)?$/iD';
  419. return preg_match($regex, $this->value($key));
  420. }
  421. /**
  422. * Returns true if the selected field is numeric
  423. * @param mixed $key Key field name or value
  424. * @param float $min Optional. Minimum value
  425. * @param float $max Optional. Maximum value
  426. * @return boolean
  427. */
  428. public function isNumeric($key, $min = null, $max = null) {
  429. $v = $this->value($key);
  430. $ok = is_numeric($v) && !(($min && $v<$min) || ($max && $v>$max));
  431. return $ok;
  432. }
  433. /**
  434. * Returns true if the selected field contains a valid url
  435. * @return boolean
  436. */
  437. public function isUrl($key) {
  438. // @todo: Optimize isUrl() - replace rexgex if necessary
  439. // regex based on http://geekswithblogs.net/casualjim/archive/2005/12/01/61722.aspx
  440. $regex = '>^(?#Protocol)(?:(?:ht|f)tp(?:s?)\:\/\/|~/|/)?(?#Username:Password)(?:\w+:\w+@)'.
  441. '?(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))'.
  442. '(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|'.
  443. '%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)'.
  444. '*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?$>i ';
  445. $ok = preg_match($regex, $this->value($key),$m);
  446. return $ok;
  447. }
  448. /**
  449. * Returns the length of the speicifed field value
  450. * @return int
  451. */
  452. public function length($key) {
  453. return strlen($this->value($key));
  454. }
  455. /**
  456. * Returns matched characters
  457. * @param string $key Key name or input value (direct input must be enabled)
  458. * @param string $pattern Optional Regex pattern. Defaults to /[a-zA-Z0-9-.]/
  459. * @return string
  460. */
  461. public function matchVal($key,$pattern = '/[a-zA-Z0-9-.]/') {
  462. $v = $this->value($key);
  463. if (!is_array($v)) $v = (preg_match($pattern, $v, $m)) ? $m[0] : '';
  464. else foreach ($v as $k=>$b){
  465. $v[$k] = (preg_match($pattern, $b, $m)) ? $m[0] : '';
  466. }
  467. return $v;
  468. }
  469. /**
  470. * Sets the array source for the sanitizer
  471. * @return RaxanDataSanitizer
  472. */
  473. public function setDataArray($array) {
  474. $this->_data = is_array($array) ? $array : $_POST;
  475. return $this;
  476. }
  477. /**
  478. * Remove html tags from input values
  479. * @param string $key Key name or input value (direct input must be enabled)
  480. * @param int $maxlength Optional. Length of text value to be returned
  481. * @return string
  482. */
  483. public function textVal($key,$maxlength = null) {
  484. $v = $this->value($key);
  485. if (!is_array($v)) {
  486. $v = strip_tags($v);
  487. $v = ($maxlength!==null && is_numeric($maxlength)) ? substr($v,0,$maxlength) : $v;
  488. }
  489. else foreach ($v as $k=>$b){
  490. $b = strip_tags($b);
  491. $v[$k] = ($maxlength!==null && is_numeric($maxlength)) ? substr($b,0,$maxlength) : $b;
  492. }
  493. return $v;
  494. }
  495. /**
  496. * Converts input value/key to a valid timestamp
  497. * @return int Returns timestamp if value is a valid datetime string or null if there was an error
  498. */
  499. public function timestampVal($key) {
  500. if (!isset($this->_date)) $this->_date = Raxan::CDate();
  501. $v = $this->value($key);
  502. if (!is_array($v)) {
  503. try { $v = $v ? $this->_date->getTimestamp($v) : null; }
  504. catch( Exception $e ) { $v = null; }
  505. }
  506. else foreach ($v as $k=>$b){
  507. try { $b = $b ? $this->_date->getTimestamp($b) : null; }
  508. catch( Exception $e ) { $b = null; }
  509. $v[$k] = $b;
  510. }
  511. return $v;
  512. }
  513. /**
  514. * Returns sanitized url for the selected field
  515. * @param string $key Key name or input value (direct input must be enabled)
  516. * @param boolean $encoded Optional. Encode url string. Defaults to false
  517. * @return string
  518. */
  519. public function urlVal($key, $encoded = false) {
  520. $v = $this->value($key);
  521. if (!is_array($v)) {
  522. $v = trim(str_replace(self::$badCharacters,'',strip_tags($v)));
  523. $v = $encoded ? url_encode($v) : $v;
  524. }
  525. else foreach ($v as $k=>$b){
  526. $b = trim(str_replace(self::$badCharacters,'',strip_tags($b)));
  527. $v[$k] = $encoded ? url_encode($b) : $b;
  528. }
  529. return $v;
  530. }
  531. /**
  532. * Returns a value based on the specified key
  533. * @return mixed
  534. */
  535. public function value($key) {
  536. if ($this->_directInp) return $key;
  537. else return isset($this->_data[$key]) ? $this->_data[$key] : null;
  538. }
  539. // Static Functions
  540. // -----------------------
  541. /**
  542. * Adds a custom data validator using regex patterns or callback function
  543. * @param string $key Key name or input value (direct input must be enabled)
  544. * @param mixed $pattern Optional. Regex pattern or a callback function
  545. * @return null
  546. */
  547. public static function addDataValidator($name,$pattern){
  548. $isRegEx = is_string($pattern) && preg_match('/^\W/',trim($pattern));
  549. self::$validators['is'.ucfirst($name)] = ($isRegEx ? '#':'').$pattern;
  550. }
  551. }
  552. ?>