PageRenderTime 83ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 1ms

/kirby/lib/kirby.php

https://github.com/jordanstephens/kirbycms
PHP | 3472 lines | 1402 code | 517 blank | 1553 comment | 256 complexity | abe9bf18ff915876ff532432f9b6ef25 MD5 | raw file
  1. <?php
  2. /**
  3. * Kirby -- A stripped down and easy to use toolkit for PHP
  4. *
  5. * @version 0.95
  6. * @author Bastian Allgeier <bastian@getkirby.com>
  7. * @link http://toolkit.getkirby.com
  8. * @copyright Copyright 2009-2012 Bastian Allgeier
  9. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  10. * @package Kirby
  11. */
  12. c::set('version', 0.94);
  13. c::set('language', 'en');
  14. c::set('charset', 'utf-8');
  15. c::set('root', dirname(__FILE__));
  16. /**
  17. * Redirects the user to a new URL
  18. *
  19. * @param string $url The URL to redirect to
  20. * @param boolean $code The HTTP status code, which should be sent (301, 302 or 303)
  21. * @package Kirby
  22. */
  23. function go($url=false, $code=false) {
  24. if(empty($url)) $url = c::get('url', '/');
  25. // send an appropriate header
  26. if($code) {
  27. switch($code) {
  28. case 301:
  29. header('HTTP/1.1 301 Moved Permanently');
  30. break;
  31. case 302:
  32. header('HTTP/1.1 302 Found');
  33. break;
  34. case 303:
  35. header('HTTP/1.1 303 See Other');
  36. break;
  37. }
  38. }
  39. // send to new page
  40. header('Location:' . $url);
  41. exit();
  42. }
  43. /**
  44. * Returns the status from a Kirby response
  45. *
  46. * @param array $response The Kirby response array
  47. * @return string "error" or "success"
  48. * @package Kirby
  49. */
  50. function status($response) {
  51. return a::get($response, 'status');
  52. }
  53. /**
  54. * Returns the message from a Kirby response
  55. *
  56. * @param array $response The Kirby response array
  57. * @return string The message
  58. * @package Kirby
  59. */
  60. function msg($response) {
  61. return a::get($response, 'msg');
  62. }
  63. /**
  64. * Checks if a Kirby response is an error response or not.
  65. *
  66. * @param array $response The Kirby response array
  67. * @return boolean Returns true if the response is an error, returns false if no error occurred
  68. * @package Kirby
  69. */
  70. function error($response) {
  71. return (status($response) == 'error') ? true : false;
  72. }
  73. /**
  74. * Checks if a Kirby response is a success response.
  75. *
  76. * @param array $response The Kirby response array
  77. * @return boolean Returns true if the response is a success, returns false if an error occurred
  78. * @package Kirby
  79. */
  80. function success($response) {
  81. return !error($response);
  82. }
  83. /**
  84. * Loads additional PHP files
  85. *
  86. * You can set the root directory with c::set('root', 'my/root');
  87. * By default the same directory in which the kirby toolkit file is located will be used.
  88. *
  89. * @param args A list filenames as individual arguments
  90. * @return array Returns a Kirby response array. On error the response array includes the unloadable files (errors).
  91. * @package Kirby
  92. */
  93. function load() {
  94. $root = c::get('root');
  95. $files = func_get_args();
  96. $errors = array();
  97. foreach((array)$files AS $f) {
  98. $file = $root . '/' . $f . '.php';
  99. if(file_exists($file)) {
  100. include_once($file);
  101. } else {
  102. $errors[] = $file;
  103. }
  104. }
  105. if(!empty($errors)) return array(
  106. 'status' => 'error',
  107. 'msg' => 'some files could not be loaded',
  108. 'errors' => $errors
  109. );
  110. return array(
  111. 'status' => 'success',
  112. 'msg' => 'all files have been loaded'
  113. );
  114. }
  115. /**
  116. *
  117. * Array
  118. *
  119. * This class is supposed to simplify array handling
  120. * and make it more consistent.
  121. *
  122. * @package Kirby
  123. */
  124. class a {
  125. /**
  126. * Gets an element of an array by key
  127. *
  128. * @param array $array The source array
  129. * @param mixed $key The key to look for
  130. * @param mixed $default Optional default value, which should be returned if no element has been found
  131. * @return mixed
  132. */
  133. static function get($array, $key, $default=null) {
  134. return (isset($array[ $key ])) ? $array[ $key ] : $default;
  135. }
  136. /**
  137. * Gets all elements for an array of key
  138. *
  139. * @param array $array The source array
  140. * @keys array $keys An array of keys to fetch
  141. * @return array An array of keys and matching values
  142. */
  143. static function getall($array, $keys) {
  144. $result = array();
  145. foreach($keys as $key) $result[$key] = $array[$key];
  146. return $result;
  147. }
  148. /**
  149. * Removes an element from an array
  150. *
  151. * @param array $array The source array
  152. * @param mixed $search The value or key to look for
  153. * @param boolean $key Pass true to search for an key, pass false to search for an value.
  154. * @return array The result array without the removed element
  155. */
  156. static function remove($array, $search, $key=true) {
  157. if($key) {
  158. unset($array[$search]);
  159. } else {
  160. $found_all = false;
  161. while(!$found_all) {
  162. $index = array_search($search, $array);
  163. if($index !== false) {
  164. unset($array[$index]);
  165. } else {
  166. $found_all = true;
  167. }
  168. }
  169. }
  170. return $array;
  171. }
  172. /**
  173. * Injects an element into an array
  174. *
  175. * @param array $array The source array
  176. * @param int $position The position, where to inject the element
  177. * @param mixed $element The element, which should be injected
  178. * @return array The result array including the new element
  179. */
  180. static function inject($array, $position, $element='placeholder') {
  181. $start = array_slice($array, 0, $position);
  182. $end = array_slice($array, $position);
  183. return array_merge($start, (array)$element, $end);
  184. }
  185. /**
  186. * Shows an entire array or object in a human readable way
  187. * This is perfect for debugging
  188. *
  189. * @param array $array The source array
  190. * @param boolean $echo By default the result will be echoed instantly. You can switch that off here.
  191. * @return mixed If echo is false, this will return the generated array output.
  192. */
  193. static function show($array, $echo=true) {
  194. $output = '<pre>';
  195. $output .= htmlspecialchars(print_r($array, true));
  196. $output .= '</pre>';
  197. if($echo==true) echo $output;
  198. return $output;
  199. }
  200. /**
  201. * Converts an array to a JSON string
  202. * It's basically a shortcut for json_encode()
  203. *
  204. * @param array $array The source array
  205. * @return string The JSON string
  206. */
  207. static function json($array) {
  208. return @json_encode( (array)$array );
  209. }
  210. /**
  211. * Converts an array to a XML string
  212. *
  213. * @param array $array The source array
  214. * @param string $tag The name of the root element
  215. * @param boolean $head Include the xml declaration head or not
  216. * @param string $charset The charset, which should be used for the header
  217. * @param int $level The indendation level
  218. * @return string The XML string
  219. */
  220. static function xml($array, $tag='root', $head=true, $charset='utf-8', $tab=' ', $level=0) {
  221. $result = ($level==0 && $head) ? '<?xml version="1.0" encoding="' . $charset . '"?>' . "\n" : '';
  222. $nlevel = ($level+1);
  223. $result .= str_repeat($tab, $level) . '<' . $tag . '>' . "\n";
  224. foreach($array AS $key => $value) {
  225. $key = str::lower($key);
  226. if(is_array($value)) {
  227. $mtags = false;
  228. foreach($value AS $key2 => $value2) {
  229. if(is_array($value2)) {
  230. $result .= self::xml($value2, $key, $head, $charset, $tab, $nlevel);
  231. } else if(trim($value2) != '') {
  232. $value2 = (htmlspecialchars($value2) != $value2) ? '<![CDATA[' . $value2 . ']]>' : $value2;
  233. $result .= str_repeat($tab, $nlevel) . '<' . $key . '>' . $value2 . '</' . $key . '>' . "\n";
  234. }
  235. $mtags = true;
  236. }
  237. if(!$mtags && count($value) > 0) {
  238. $result .= self::xml($value, $key, $head, $charset, $tab, $nlevel);
  239. }
  240. } else if(trim($value) != '') {
  241. $value = (htmlspecialchars($value) != $value) ? '<![CDATA[' . $value . ']]>' : $value;
  242. $result .= str_repeat($tab, $nlevel) . '<' . $key . '>' . $value . '</' . $key . '>' . "\n";
  243. }
  244. }
  245. return $result . str_repeat($tab, $level) . '</' . $tag . '>' . "\n";
  246. }
  247. /**
  248. * Extracts a single column from an array
  249. *
  250. * @param array $array The source array
  251. * @param string $key The key name of the column to extract
  252. * @return array The result array with all values from that column.
  253. */
  254. static function extract($array, $key) {
  255. $output = array();
  256. foreach($array AS $a) if(isset($a[$key])) $output[] = $a[ $key ];
  257. return $output;
  258. }
  259. /**
  260. * Shuffles an array and keeps the keys
  261. *
  262. * @param array $array The source array
  263. * @return array The shuffled result array
  264. */
  265. static function shuffle($array) {
  266. $keys = array_keys($array);
  267. shuffle($keys);
  268. return array_merge(array_flip($keys), $array);
  269. }
  270. /**
  271. * Returns the first element of an array
  272. *
  273. * I always have to lookup the names of that function
  274. * so I decided to make this shortcut which is
  275. * easier to remember.
  276. *
  277. * @param array $array The source array
  278. * @return mixed The first element
  279. */
  280. static function first($array) {
  281. return array_shift($array);
  282. }
  283. /**
  284. * Returns the last element of an array
  285. *
  286. * I always have to lookup the names of that function
  287. * so I decided to make this shortcut which is
  288. * easier to remember.
  289. *
  290. * @param array $array The source array
  291. * @return mixed The last element
  292. */
  293. static function last($array) {
  294. return array_pop($array);
  295. }
  296. /**
  297. * Search for elements in an array by regular expression
  298. *
  299. * @param array $array The source array
  300. * @param string $search The regular expression
  301. * @return array The array of results
  302. */
  303. static function search($array, $search) {
  304. return preg_grep('#' . preg_quote($search) . '#i' , $array);
  305. }
  306. /**
  307. * Checks if an array contains a certain string
  308. *
  309. * @param array $array The source array
  310. * @param string $search The string to search for
  311. * @return boolean true: the array contains the string, false: it doesn't
  312. */
  313. static function contains($array, $search) {
  314. $search = self::search($array, $search);
  315. return (empty($search)) ? false : true;
  316. }
  317. /**
  318. * Fills an array up with additional elements to certain amount.
  319. *
  320. * @param array $array The source array
  321. * @param int $limit The number of elements the array should contain after filling it up.
  322. * @param mixed $fill The element, which should be used to fill the array
  323. * @return array The filled-up result array
  324. */
  325. static function fill($array, $limit, $fill='placeholder') {
  326. if(count($array) < $limit) {
  327. $diff = $limit-count($array);
  328. for($x=0; $x<$diff; $x++) $array[] = $fill;
  329. }
  330. return $array;
  331. }
  332. /**
  333. * Checks for missing elements in an array
  334. *
  335. * This is very handy to check for missing
  336. * user values in a request for example.
  337. *
  338. * @param array $array The source array
  339. * @param array $required An array of required keys
  340. * @return array An array of missing fields. If this is empty, nothing is missing.
  341. */
  342. static function missing($array, $required=array()) {
  343. $missing = array();
  344. foreach($required AS $r) {
  345. if(empty($array[$r])) $missing[] = $r;
  346. }
  347. return $missing;
  348. }
  349. /**
  350. * Sorts a multi-dimensional array by a certain column
  351. *
  352. * @param array $array The source array
  353. * @param string $field The name of the column
  354. * @param string $direction desc (descending) or asc (ascending)
  355. * @param const $method A PHP sort method flag or 'natural' for natural sorting, which is not supported in PHP by sort flags
  356. * @return array The sorted array
  357. */
  358. static function sort($array, $field, $direction='desc', $method=SORT_REGULAR) {
  359. $direction = (strtolower($direction) == 'desc') ? SORT_DESC : SORT_ASC;
  360. $helper = array();
  361. foreach($array as $key => $row) {
  362. $helper[$key] = (is_object($row)) ? (method_exists($row, $field)) ? str::lower($row->$field()) : str::lower($row->$field) : str::lower($row[$field]);
  363. }
  364. // natural sorting
  365. if($method === 'natural') {
  366. natsort($helper);
  367. if($direction === SORT_DESC) $helper = array_reverse($helper);
  368. $result = array();
  369. foreach($helper as $key => $val) {
  370. $result[$key] = $array[$key];
  371. }
  372. return $result;
  373. } else {
  374. array_multisort($helper, $direction, $method, $array);
  375. return $array;
  376. }
  377. }
  378. }
  379. /**
  380. *
  381. * Browser
  382. *
  383. * Browser sniffing is bad - I know!
  384. * But sometimes this class is very helpful to
  385. * react on certain browsers and build browser-specific
  386. * css selectors for example. It's up to you to use it.
  387. *
  388. * @package Kirby
  389. */
  390. class browser {
  391. /**
  392. * The entire user agent string
  393. *
  394. * @var string
  395. */
  396. static public $ua = false;
  397. /**
  398. * The readable name of the browser
  399. * For example: "ie"
  400. *
  401. * @var string
  402. */
  403. static public $name = false;
  404. /**
  405. * The readable browser engine name
  406. * For example: "webkit"
  407. *
  408. * @var string
  409. */
  410. static public $engine = false;
  411. /**
  412. * The browser version number
  413. * For example: "3.6"
  414. *
  415. * @var string
  416. */
  417. static public $version = false;
  418. /**
  419. * The platform name
  420. * For example: "mac"
  421. *
  422. * @var string
  423. */
  424. static public $platform = false;
  425. /**
  426. * True or false if it is a mobile device or not
  427. *
  428. * @var boolean
  429. */
  430. static public $mobile = false;
  431. /**
  432. * True or false if it is an iOS device or not
  433. *
  434. * @var boolean
  435. */
  436. static public $ios = false;
  437. /**
  438. * True or false if it is an iPhone or not
  439. *
  440. * @var boolean
  441. */
  442. static public $iphone = false;
  443. /**
  444. * Returns the name of the browser
  445. *
  446. * @param string $ua The user agent string
  447. * @return string The browser name
  448. */
  449. static function name($ua=null) {
  450. self::detect($ua);
  451. return self::$name;
  452. }
  453. /**
  454. * Returns the browser engine
  455. *
  456. * @param string $ua The user agent string
  457. * @return string The browser engine
  458. */
  459. static function engine($ua=null) {
  460. self::detect($ua);
  461. return self::$engine;
  462. }
  463. /**
  464. * Returns the browser version
  465. *
  466. * @param string $ua The user agent string
  467. * @return string The browser version
  468. */
  469. static function version($ua=null) {
  470. self::detect($ua);
  471. return self::$version;
  472. }
  473. /**
  474. * Returns the platform
  475. *
  476. * @param string $ua The user agent string
  477. * @return string The platform name
  478. */
  479. static function platform($ua=null) {
  480. self::detect($ua);
  481. return self::$platform;
  482. }
  483. /**
  484. * Checks if the user agent string is from a mobile device
  485. *
  486. * @param string $ua The user agent string
  487. * @return boolean True: mobile device, false: not a mobile device
  488. */
  489. static function mobile($ua=null) {
  490. self::detect($ua);
  491. return self::$mobile;
  492. }
  493. /**
  494. * Checks if the user agent string is from an iPhone
  495. *
  496. * @param string $ua The user agent string
  497. * @return boolean True: iPhone, false: not an iPhone
  498. */
  499. static function iphone($ua=null) {
  500. self::detect($ua);
  501. return self::$iphone;
  502. }
  503. /**
  504. * Checks if the user agent string is from an iOS device
  505. *
  506. * @param string $ua The user agent string
  507. * @return boolean True: iOS device, false: not an iOS device
  508. */
  509. static function ios($ua=null) {
  510. self::detect($ua);
  511. return self::$ios;
  512. }
  513. /**
  514. * Returns a browser-specific css selector string
  515. *
  516. * @param string $ua The user agent string
  517. * @param boolean $array True: return an array, false: return a string
  518. * @return mixed
  519. */
  520. static function css($ua=null, $array=false) {
  521. self::detect($ua);
  522. $css[] = self::$engine;
  523. $css[] = self::$name;
  524. if(self::$version) $css[] = self::$name . str_replace('.', '_', self::$version);
  525. $css[] = self::$platform;
  526. return ($array) ? $css : implode(' ', $css);
  527. }
  528. /**
  529. * The core detection method, which parses the user agent string
  530. *
  531. * @todo add new browser versions
  532. * @param string $ua The user agent string
  533. * @return array An array with all parsed info
  534. */
  535. static function detect($ua=null) {
  536. $ua = ($ua) ? str::lower($ua) : str::lower(server::get('http_user_agent'));
  537. // don't do the detection twice
  538. if(self::$ua == $ua) return array(
  539. 'name' => self::$name,
  540. 'engine' => self::$engine,
  541. 'version' => self::$version,
  542. 'platform' => self::$platform,
  543. 'agent' => self::$ua,
  544. 'mobile' => self::$mobile,
  545. 'iphone' => self::$iphone,
  546. 'ios' => self::$ios,
  547. );
  548. self::$ua = $ua;
  549. self::$name = false;
  550. self::$engine = false;
  551. self::$version = false;
  552. self::$platform = false;
  553. // browser
  554. if(!preg_match('/opera|webtv/i', self::$ua) && preg_match('/msie\s(\d)/', self::$ua, $array)) {
  555. self::$version = $array[1];
  556. self::$name = 'ie';
  557. self::$engine = 'trident';
  558. } else if(strstr(self::$ua, 'firefox/3.6')) {
  559. self::$version = 3.6;
  560. self::$name = 'fx';
  561. self::$engine = 'gecko';
  562. } else if (strstr(self::$ua, 'firefox/3.5')) {
  563. self::$version = 3.5;
  564. self::$name = 'fx';
  565. self::$engine = 'gecko';
  566. } else if(preg_match('/firefox\/(\d+)/i', self::$ua, $array)) {
  567. self::$version = $array[1];
  568. self::$name = 'fx';
  569. self::$engine = 'gecko';
  570. } else if(preg_match('/opera(\s|\/)(\d+)/', self::$ua, $array)) {
  571. self::$engine = 'presto';
  572. self::$name = 'opera';
  573. self::$version = $array[2];
  574. } else if(strstr(self::$ua, 'konqueror')) {
  575. self::$name = 'konqueror';
  576. self::$engine = 'webkit';
  577. } else if(strstr(self::$ua, 'iron')) {
  578. self::$name = 'iron';
  579. self::$engine = 'webkit';
  580. } else if(strstr(self::$ua, 'chrome')) {
  581. self::$name = 'chrome';
  582. self::$engine = 'webkit';
  583. if(preg_match('/chrome\/(\d+)/i', self::$ua, $array)) { self::$version = $array[1]; }
  584. } else if(strstr(self::$ua, 'applewebkit/')) {
  585. self::$name = 'safari';
  586. self::$engine = 'webkit';
  587. if(preg_match('/version\/(\d+)/i', self::$ua, $array)) { self::$version = $array[1]; }
  588. } else if(strstr(self::$ua, 'mozilla/')) {
  589. self::$engine = 'gecko';
  590. self::$name = 'fx';
  591. }
  592. // platform
  593. if(strstr(self::$ua, 'j2me')) {
  594. self::$platform = 'mobile';
  595. } else if(strstr(self::$ua, 'iphone')) {
  596. self::$platform = 'iphone';
  597. } else if(strstr(self::$ua, 'ipod')) {
  598. self::$platform = 'ipod';
  599. } else if(strstr(self::$ua, 'ipad')) {
  600. self::$platform = 'ipad';
  601. } else if(strstr(self::$ua, 'mac')) {
  602. self::$platform = 'mac';
  603. } else if(strstr(self::$ua, 'darwin')) {
  604. self::$platform = 'mac';
  605. } else if(strstr(self::$ua, 'webtv')) {
  606. self::$platform = 'webtv';
  607. } else if(strstr(self::$ua, 'win')) {
  608. self::$platform = 'win';
  609. } else if(strstr(self::$ua, 'freebsd')) {
  610. self::$platform = 'freebsd';
  611. } else if(strstr(self::$ua, 'x11') || strstr(self::$ua, 'linux')) {
  612. self::$platform = 'linux';
  613. }
  614. self::$mobile = (self::$platform == 'mobile') ? true : false;
  615. self::$iphone = (in_array(self::$platform, array('ipod', 'iphone'))) ? true : false;
  616. self::$ios = (in_array(self::$platform, array('ipod', 'iphone', 'ipad'))) ? true : false;
  617. return array(
  618. 'name' => self::$name,
  619. 'engine' => self::$engine,
  620. 'version' => self::$version,
  621. 'platform' => self::$platform,
  622. 'agent' => self::$ua,
  623. 'mobile' => self::$mobile,
  624. 'iphone' => self::$iphone,
  625. 'ios' => self::$ios,
  626. );
  627. }
  628. }
  629. /**
  630. *
  631. * Config
  632. *
  633. * This is the core class to handle
  634. * configuration values/constants.
  635. *
  636. * @package Kirby
  637. */
  638. class c {
  639. /**
  640. * The static config array
  641. * It contains all config values
  642. *
  643. * @var array
  644. */
  645. private static $config = array();
  646. /**
  647. * Gets a config value by key
  648. *
  649. * @param string $key The key to look for. Pass false to get the entire config array
  650. * @param mixed $default The default value, which will be returned if the key has not been found
  651. * @return mixed The found config value
  652. */
  653. static function get($key=null, $default=null) {
  654. if(empty($key)) return self::$config;
  655. return a::get(self::$config, $key, $default);
  656. }
  657. /**
  658. * Sets a config value by key
  659. *
  660. * @param string $key The key to define
  661. * @param mixed $value The value for the passed key
  662. */
  663. static function set($key, $value=null) {
  664. if(is_array($key)) {
  665. // set all new values
  666. self::$config = array_merge(self::$config, $key);
  667. } else {
  668. self::$config[$key] = $value;
  669. }
  670. }
  671. /**
  672. * Loads an additional config file
  673. * Returns the entire configuration array
  674. *
  675. * @param string $file The path to the config file
  676. * @return array The entire config array
  677. */
  678. static function load($file) {
  679. if(file_exists($file)) require_once($file);
  680. return c::get();
  681. }
  682. }
  683. /**
  684. *
  685. * Content
  686. *
  687. * This class handles output buffering,
  688. * content loading and setting content type headers.
  689. *
  690. * @package Kirby
  691. */
  692. class content {
  693. /**
  694. * Starts the output buffer
  695. *
  696. */
  697. static function start() {
  698. ob_start();
  699. }
  700. /**
  701. * Stops the output buffer
  702. * and flush the content or return it.
  703. *
  704. * @param boolean $return Pass true to return the content instead of flushing it
  705. * @return mixed
  706. */
  707. static function end($return=false) {
  708. if($return) {
  709. $content = ob_get_contents();
  710. ob_end_clean();
  711. return $content;
  712. }
  713. ob_end_flush();
  714. }
  715. /**
  716. * Loads content from a passed file
  717. *
  718. * @param string $file The path to the file
  719. * @param boolean $return True: return the content of the file, false: echo the content
  720. * @return mixed
  721. */
  722. static function load($file, $return=true) {
  723. self::start();
  724. require_once($file);
  725. $content = self::end(true);
  726. if($return) return $content;
  727. echo $content;
  728. }
  729. /**
  730. * Simplifies setting content type headers
  731. *
  732. * @param string $ctype The shortcut for the content type. See the keys of the $ctypes array for all available shortcuts
  733. * @param string $charset The charset definition for the content type header. Default is "utf-8"
  734. */
  735. static function type() {
  736. $args = func_get_args();
  737. // shortcuts for content types
  738. $ctypes = array(
  739. 'html' => 'text/html',
  740. 'css' => 'text/css',
  741. 'js' => 'text/javascript',
  742. 'jpg' => 'image/jpeg',
  743. 'png' => 'image/png',
  744. 'gif' => 'image/gif',
  745. 'json' => 'application/json'
  746. );
  747. $ctype = a::get($args, 0, c::get('content_type', 'text/html'));
  748. $ctype = a::get($ctypes, $ctype, $ctype);
  749. $charset = a::get($args, 1, c::get('charset', 'utf-8'));
  750. header('Content-type: ' . $ctype . '; charset=' . $charset);
  751. }
  752. }
  753. /**
  754. *
  755. * Cookie
  756. *
  757. * This class makes cookie handling easy
  758. *
  759. * @package Kirby
  760. */
  761. class cookie {
  762. /**
  763. * Set a new cookie
  764. *
  765. * @param string $key The name of the cookie
  766. * @param string $value The cookie content
  767. * @param int $expires The number of seconds until the cookie expires
  768. * @param string $domain The domain to set this cookie for.
  769. * @return boolean true: the cookie has been created, false: cookie creation failed
  770. */
  771. static function set($key, $value, $expires=3600, $domain='/') {
  772. if(is_array($value)) $value = a::json($value);
  773. $_COOKIE[$key] = $value;
  774. return @setcookie($key, $value, time()+$expires, $domain);
  775. }
  776. /**
  777. * Get a cookie value
  778. *
  779. * @param string $key The name of the cookie
  780. * @param string $default The default value, which should be returned if the cookie has not been found
  781. * @return mixed The found value
  782. */
  783. static function get($key, $default=null) {
  784. return a::get($_COOKIE, $key, $default);
  785. }
  786. /**
  787. * Remove a cookie
  788. *
  789. * @param string $key The name of the cookie
  790. * @param string $domain The domain of the cookie
  791. * @return mixed true: the cookie has been removed, false: the cookie could not be removed
  792. */
  793. static function remove($key, $domain='/') {
  794. $_COOKIE[$key] = false;
  795. return @setcookie($key, false, time()-3600, $domain);
  796. }
  797. }
  798. /**
  799. *
  800. * Database
  801. *
  802. * Database handling sucks - not with this class :)
  803. *
  804. * Configure your database connection like this:
  805. *
  806. * <code>
  807. * c::set('db.host', 'localhost');
  808. * c::set('db.user', 'root');
  809. * c::set('db.password', '');
  810. * c::set('db.name', 'mydb');
  811. * c::set('db.prefix', '');
  812. * </code>
  813. *
  814. * @package Kirby
  815. */
  816. class db {
  817. /**
  818. * Traces all db queries
  819. *
  820. * @var array
  821. */
  822. public static $trace = array();
  823. /**
  824. * The connection resource
  825. *
  826. * @var mixed
  827. */
  828. private static $connection = false;
  829. /**
  830. * The selected database
  831. *
  832. * @var string
  833. */
  834. private static $database = false;
  835. /**
  836. * The used charset
  837. * Default is "utf8"
  838. *
  839. * @var string
  840. */
  841. private static $charset = false;
  842. /**
  843. * The last used query
  844. *
  845. * @var string
  846. */
  847. private static $last_query = false;
  848. /**
  849. * The number of affected rows
  850. * for the last query
  851. *
  852. * @var int
  853. */
  854. private static $affected = 0;
  855. /**
  856. * The core connection method
  857. * Tries to connect to the server
  858. * Selects the database and sets the charset
  859. *
  860. * It will only connect once and return
  861. * that same connection for all following queries
  862. *
  863. * @return mixed
  864. */
  865. static function connect() {
  866. $connection = self::connection();
  867. $args = func_get_args();
  868. $host = a::get($args, 0, c::get('db.host', 'localhost'));
  869. $user = a::get($args, 1, c::get('db.user', 'root'));
  870. $password = a::get($args, 2, c::get('db.password'));
  871. $database = a::get($args, 3, c::get('db.name'));
  872. $charset = a::get($args, 4, c::get('db.charset', 'utf8'));
  873. // don't connect again if it's already done
  874. $connection = (!$connection) ? @mysql_connect($host, $user, $password) : $connection;
  875. // react on connection failures
  876. if(!$connection) return self::error(l::get('db.errors.connect', 'Database connection failed'), true);
  877. self::$connection = $connection;
  878. // select the database
  879. $database = self::database($database);
  880. if(error($database)) return $database;
  881. // set the right charset
  882. $charset = self::charset($charset);
  883. if(error($charset)) return $charset;
  884. return $connection;
  885. }
  886. /**
  887. * Returns the current connection or false
  888. *
  889. * @return mixed
  890. */
  891. static function connection() {
  892. return (is_resource(self::$connection)) ? self::$connection : false;
  893. }
  894. /**
  895. * Disconnects from the server
  896. *
  897. * @return boolean
  898. */
  899. static function disconnect() {
  900. if(!c::get('db.disconnect')) return false;
  901. $connection = self::connection();
  902. if(!$connection) return false;
  903. // kill the connection
  904. $disconnect = @mysql_close($connection);
  905. self::$connection = false;
  906. if(!$disconnect) return self::error(l::get('db.errors.disconnect', 'Disconnecting database failed'));
  907. return true;
  908. }
  909. /**
  910. * Selects a database
  911. *
  912. * @param string $database
  913. * @return mixed
  914. */
  915. static function database($database) {
  916. if(!$database) return self::error(l::get('db.errors.missing_db_name', 'Please provide a database name'), true);
  917. // check if there is a selected database
  918. if(self::$database == $database) return true;
  919. // select a new database
  920. $select = @mysql_select_db($database, self::connection());
  921. if(!$select) return self::error(l::get('db.errors.missing_db', 'Selecting database failed'), true);
  922. self::$database = $database;
  923. return $database;
  924. }
  925. /**
  926. * Sets the charset for all queries
  927. * The default and recommended charset is utf8
  928. *
  929. * @param string $charset
  930. * @return mixed
  931. */
  932. static function charset($charset='utf8') {
  933. // check if there is a assigned charset and compare it
  934. if(self::$charset == $charset) return true;
  935. // set the new charset
  936. $set = @mysql_query('SET NAMES ' . $charset);
  937. if(!$set) return self::error(l::get('db.errors.setting_charset_failed', 'Setting database charset failed'));
  938. // save the new charset to the globals
  939. self::$charset = $charset;
  940. return $charset;
  941. }
  942. /**
  943. * Runs a MySQL query.
  944. * You can use any valid MySQL query here.
  945. * This is also the fallback method if you
  946. * can't use one of the provided shortcut methods
  947. * from this class.
  948. *
  949. * @param string $sql The sql query
  950. * @param boolean $fetch True: apply db::fetch to the result, false: go without db::fetch
  951. * @return mixed
  952. */
  953. static function query($sql, $fetch=true) {
  954. $connection = self::connect();
  955. if(error($connection)) return $connection;
  956. // save the query
  957. self::$last_query = $sql;
  958. // execute the query
  959. $result = @mysql_query($sql, $connection);
  960. self::$affected = @mysql_affected_rows();
  961. self::$trace[] = $sql;
  962. if(!$result) return self::error(l::get('db.errors.query_failed', 'The database query failed'));
  963. if(!$fetch) return $result;
  964. $array = array();
  965. while($r = self::fetch($result)) array_push($array, $r);
  966. return $array;
  967. }
  968. /**
  969. * Executes a MySQL query without result set.
  970. * This is used for queries like update, delete or insert
  971. *
  972. * @param string $sql The sql query
  973. * @return mixed
  974. */
  975. static function execute($sql) {
  976. $connection = self::connect();
  977. if(error($connection)) return $connection;
  978. // save the query
  979. self::$last_query = $sql;
  980. // execute the query
  981. $execute = @mysql_query($sql, $connection);
  982. self::$affected = @mysql_affected_rows();
  983. self::$trace[] = $sql;
  984. if(!$execute) return self::error(l::get('db.errors.query_failed', 'The database query failed'));
  985. $last_id = self::last_id();
  986. return ($last_id === false) ? self::$affected : self::last_id();
  987. }
  988. /**
  989. * Returns the number of affected rows for the last query
  990. *
  991. * @return int
  992. */
  993. static function affected() {
  994. return self::$affected;
  995. }
  996. /**
  997. * Returns the last returned insert id
  998. *
  999. * @return int
  1000. */
  1001. static function last_id() {
  1002. $connection = self::connection();
  1003. return @mysql_insert_id($connection);
  1004. }
  1005. /**
  1006. * Shortcut for mysql_fetch_array
  1007. *
  1008. * @param resource $result the unfetched result from db::query()
  1009. * @param const $type PHP flag for mysql_fetch_array
  1010. * @return array The key/value result array
  1011. */
  1012. static function fetch($result, $type=MYSQL_ASSOC) {
  1013. if(!$result) return array();
  1014. return @mysql_fetch_array($result, $type);
  1015. }
  1016. /**
  1017. * Returns an array of fields in a given table
  1018. *
  1019. * @param string $table The table name
  1020. * @return array The array of field names
  1021. */
  1022. static function fields($table) {
  1023. $connection = self::connect();
  1024. if(error($connection)) return $connection;
  1025. $fields = @mysql_list_fields(self::$database, self::prefix($table), $connection);
  1026. if(!$fields) return self::error(l::get('db.errors.listing_fields_failed', 'Listing fields failed'));
  1027. $output = array();
  1028. $count = @mysql_num_fields($fields);
  1029. for($x=0; $x<$count; $x++) {
  1030. $output[] = @mysql_field_name($fields, $x);
  1031. }
  1032. return $output;
  1033. }
  1034. /**
  1035. * Runs a INSERT query
  1036. *
  1037. * @param string $table The table name
  1038. * @param mixed $input Either a key/value array or a valid MySQL insert string
  1039. * @param boolean $ignore Set this to true to ignore duplicates
  1040. * @return mixed The last inserted id if everything went fine or an error response.
  1041. */
  1042. static function insert($table, $input, $ignore=false) {
  1043. $ignore = ($ignore) ? ' IGNORE' : '';
  1044. return self::execute('INSERT' . ($ignore) . ' INTO ' . self::prefix($table) . ' SET ' . self::values($input));
  1045. }
  1046. /**
  1047. * Runs a INSERT query with values
  1048. *
  1049. * @param string $table The table name
  1050. * @param array $fields an array of field names
  1051. * @param array $values an array of array of keys and values.
  1052. * @return mixed The last inserted id if everything went fine or an error response.
  1053. */
  1054. static function insert_all($table, $fields, $values) {
  1055. $query = 'INSERT INTO ' . self::prefix($table) . ' (' . implode(',', $fields) . ') VALUES ';
  1056. $rows = array();
  1057. foreach($values AS $v) {
  1058. $str = '(\'';
  1059. $sep = '';
  1060. foreach($v AS $input) {
  1061. $str .= $sep . db::escape($input);
  1062. $sep = "','";
  1063. }
  1064. $str .= '\')';
  1065. $rows[] = $str;
  1066. }
  1067. $query .= implode(',', $rows);
  1068. return db::execute($query);
  1069. }
  1070. /**
  1071. * Runs a REPLACE query
  1072. *
  1073. * @param string $table The table name
  1074. * @param mixed $input Either a key/value array or a valid MySQL insert string
  1075. * @return mixed The last inserted id if everything went fine or an error response.
  1076. */
  1077. static function replace($table, $input) {
  1078. return self::execute('REPLACE INTO ' . self::prefix($table) . ' SET ' . self::values($input));
  1079. }
  1080. /**
  1081. * Runs an UPDATE query
  1082. *
  1083. * @param string $table The table name
  1084. * @param mixed $input Either a key/value array or a valid MySQL insert string
  1085. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1086. * @return mixed The number of affected rows or an error response
  1087. */
  1088. static function update($table, $input, $where) {
  1089. return self::execute('UPDATE ' . self::prefix($table) . ' SET ' . self::values($input) . ' WHERE ' . self::where($where));
  1090. }
  1091. /**
  1092. * Runs a DELETE query
  1093. *
  1094. * @param string $table The table name
  1095. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1096. * @return mixed The number of affected rows or an error response
  1097. */
  1098. static function delete($table, $where='') {
  1099. $sql = 'DELETE FROM ' . self::prefix($table);
  1100. if(!empty($where)) $sql .= ' WHERE ' . self::where($where);
  1101. return self::execute($sql);
  1102. }
  1103. /**
  1104. * Returns multiple rows from a table
  1105. *
  1106. * @param string $table The table name
  1107. * @param mixed $select Either an array of fields or a MySQL string of fields
  1108. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1109. * @param string $order Order clause without the order keyword. ie: "added desc"
  1110. * @param int $page a page number
  1111. * @param int $limit a number for rows to return
  1112. * @param boolean $fetch true: apply db::fetch(), false: don't apply db::fetch()
  1113. * @return mixed
  1114. */
  1115. static function select($table, $select='*', $where=null, $order=null, $page=null, $limit=null, $fetch=true) {
  1116. if($limit === 0) return array();
  1117. if(is_array($select)) $select = self::select_clause($select);
  1118. $sql = 'SELECT ' . $select . ' FROM ' . self::prefix($table);
  1119. if(!empty($where)) $sql .= ' WHERE ' . self::where($where);
  1120. if(!empty($order)) $sql .= ' ORDER BY ' . $order;
  1121. if($page !== null && $limit !== null) $sql .= ' LIMIT ' . $page . ',' . $limit;
  1122. return self::query($sql, $fetch);
  1123. }
  1124. /**
  1125. * Returns a single row from a table
  1126. *
  1127. * @param string $table The table name
  1128. * @param mixed $select Either an array of fields or a MySQL string of fields
  1129. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1130. * @param string $order Order clause without the order keyword. ie: "added desc"
  1131. * @return mixed
  1132. */
  1133. static function row($table, $select='*', $where=null, $order=null) {
  1134. $result = self::select($table, $select, $where, $order, 0,1, false);
  1135. return self::fetch($result);
  1136. }
  1137. /**
  1138. * Returns all values from single column of a table
  1139. *
  1140. * @param string $table The table name
  1141. * @param string $column The name of the column
  1142. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1143. * @param string $order Order clause without the order keyword. ie: "added desc"
  1144. * @param int $page a page number
  1145. * @param int $limit a number for rows to return
  1146. * @return mixed
  1147. */
  1148. static function column($table, $column, $where=null, $order=null, $page=null, $limit=null) {
  1149. $result = self::select($table, $column, $where, $order, $page, $limit, false);
  1150. $array = array();
  1151. while($r = self::fetch($result)) array_push($array, a::get($r, $column));
  1152. return $array;
  1153. }
  1154. /**
  1155. * Returns a single field value from a table
  1156. *
  1157. * @param string $table The table name
  1158. * @param string $field The name of the field
  1159. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1160. * @param string $order Order clause without the order keyword. ie: "added desc"
  1161. * @return mixed
  1162. */
  1163. static function field($table, $field, $where=null, $order=null) {
  1164. $result = self::row($table, $field, $where, $order);
  1165. return a::get($result, $field);
  1166. }
  1167. /**
  1168. * Joins two tables and returns data from them
  1169. *
  1170. * @param string $table_1 The table name of the first table
  1171. * @param string $table_2 The table name of the second table
  1172. * @param string $on The MySQL ON clause without the ON keyword. ie: "user_id = comment_user"
  1173. * @param mixed $select Either an array of fields or a MySQL string of fields
  1174. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1175. * @param string $order Order clause without the order keyword. ie: "added desc"
  1176. * @param int $page a page number
  1177. * @param int $limit a number for rows to return
  1178. * @param string $type The join type (JOIN, LEFT, RIGHT, INNER)
  1179. * @return mixed
  1180. */
  1181. static function join($table_1, $table_2, $on, $select, $where=null, $order=null, $page=null, $limit=null, $type="JOIN") {
  1182. return self::select(
  1183. self::prefix($table_1) . ' ' . $type . ' ' .
  1184. self::prefix($table_2) . ' ON ' .
  1185. self::where($on),
  1186. $select,
  1187. self::where($where),
  1188. $order,
  1189. $page,
  1190. $limit
  1191. );
  1192. }
  1193. /**
  1194. * Runs a LEFT JOIN
  1195. *
  1196. * @param string $table_1 The table name of the first table
  1197. * @param string $table_2 The table name of the second table
  1198. * @param string $on The MySQL ON clause without the ON keyword. ie: "user_id = comment_user"
  1199. * @param mixed $select Either an array of fields or a MySQL string of fields
  1200. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1201. * @param string $order Order clause without the order keyword. ie: "added desc"
  1202. * @param int $page a page number
  1203. * @param int $limit a number for rows to return
  1204. * @return mixed
  1205. */
  1206. static function left_join($table_1, $table_2, $on, $select, $where=null, $order=null, $page=null, $limit=null) {
  1207. return self::join($table_1, $table_2, $on, $select, $where, $order, $page, $limit, 'LEFT JOIN');
  1208. }
  1209. /**
  1210. * Counts a number of rows in a table
  1211. *
  1212. * @param string $table The table name
  1213. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1214. * @return int
  1215. */
  1216. static function count($table, $where='') {
  1217. $result = self::row($table, 'count(*)', $where);
  1218. return ($result) ? a::get($result, 'count(*)') : 0;
  1219. }
  1220. /**
  1221. * Gets the minimum value in a column of a table
  1222. *
  1223. * @param string $table The table name
  1224. * @param string $column The name of the column
  1225. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1226. * @return mixed
  1227. */
  1228. static function min($table, $column, $where=null) {
  1229. $sql = 'SELECT MIN(' . $column . ') AS min FROM ' . self::prefix($table);
  1230. if(!empty($where)) $sql .= ' WHERE ' . self::where($where);
  1231. $result = self::query($sql, false);
  1232. $result = self::fetch($result);
  1233. return a::get($result, 'min', 1);
  1234. }
  1235. /**
  1236. * Gets the maximum value in a column of a table
  1237. *
  1238. * @param string $table The table name
  1239. * @param string $column The name of the column
  1240. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1241. * @return mixed
  1242. */
  1243. static function max($table, $column, $where=null) {
  1244. $sql = 'SELECT MAX(' . $column . ') AS max FROM ' . self::prefix($table);
  1245. if(!empty($where)) $sql .= ' WHERE ' . self::where($where);
  1246. $result = self::query($sql, false);
  1247. $result = self::fetch($result);
  1248. return a::get($result, 'max', 1);
  1249. }
  1250. /**
  1251. * Gets the sum of values in a column of a table
  1252. *
  1253. * @param string $table The table name
  1254. * @param string $column The name of the column
  1255. * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string
  1256. * @return mixed
  1257. */
  1258. static function sum($table, $column, $where=null) {
  1259. $sql = 'SELECT SUM(' . $column . ') AS sum FROM ' . self::prefix($table);
  1260. if(!empty($where)) $sql .= ' WHERE ' . self::where($where);
  1261. $result = self::query($sql, false);
  1262. $result = self::fetch($result);
  1263. return a::get($result, 'sum', 0);
  1264. }
  1265. /**
  1266. * Adds a prefix to a table name if set in c::set('db.prefix', 'myprefix_');
  1267. * This makes it possible to use table names in all methods without prefix
  1268. * and it will still be applied automatically.
  1269. *
  1270. * @param string $table The name of the table with or without prefix
  1271. * @return string The sanitized table name.
  1272. */
  1273. static function prefix($table) {
  1274. $prefix = c::get('db.prefix');
  1275. if(!$prefix) return $table;
  1276. return (!str::contains($table,$prefix)) ? $prefix . $table : $table;
  1277. }
  1278. /**
  1279. * Strips table specific column prefixes from the result array
  1280. *
  1281. * If you use column names like user_username, user_id, etc.
  1282. * use this method on the result array to strip user_ of all fields
  1283. *
  1284. * @param array $array The result array
  1285. * @return array The result array without those damn prefixes.
  1286. */
  1287. static function simple_fields($array) {
  1288. if(empty($array)) return false;
  1289. $output = array();
  1290. foreach($array AS $key => $value) {
  1291. $key = substr($key, strpos($key, '_')+1);
  1292. $output[$key] = $value;
  1293. }
  1294. return $output;
  1295. }
  1296. /**
  1297. * Makes it possible to use arrays for inputs instead of MySQL strings
  1298. *
  1299. * @param array $input
  1300. * @return string The final MySQL string, which will be used in the queries.
  1301. */
  1302. static function values($input) {
  1303. if(!is_array($input)) return $input;
  1304. $output = array();
  1305. foreach($input AS $key => $value) {
  1306. if($value === 'NOW()')
  1307. $output[] = $key . ' = NOW()';
  1308. elseif(is_array($value))
  1309. $output[] = $key . ' = \'' . a::json($value) . '\'';
  1310. else
  1311. $output[] = $key . ' = \'' . self::escape($value) . '\'';
  1312. }
  1313. return implode(', ', $output);
  1314. }
  1315. /**
  1316. * Escapes unwanted stuff in values like slashes, etc.
  1317. *
  1318. * @param string $value
  1319. * @return string Returns the escaped string
  1320. */
  1321. static function escape($value) {
  1322. $value = str::stripslashes($value);
  1323. return mysql_real_escape_string((string)$value, self::connect());
  1324. }
  1325. /**
  1326. * A simplifier to build search clauses
  1327. *
  1328. * @param string $search The search word
  1329. * @param array $fields An array of fields to search
  1330. * @param string $mode OR or AND
  1331. * @return string Returns the final where clause
  1332. */
  1333. static function search_clause($search, $fields, $mode='OR') {
  1334. if(empty($search)) return false;
  1335. $arr = array();
  1336. foreach($fields AS $f) {
  1337. array_push($arr, $f . ' LIKE \'%' . $search . '%\'');
  1338. //array_push($arr, $f . ' REGEXP "[[:<:]]' . db::escape($search) . '[[:>:]]"');
  1339. }
  1340. return '(' . implode(' ' . trim($mode) . ' ', $arr) . ')';
  1341. }
  1342. /**
  1343. * An easy method to build a part of the where clause to find stuff by its first character
  1344. *
  1345. * @param string $field The name of the field
  1346. * @param string $char The character to search for
  1347. * @return string Returns the where clause part
  1348. */
  1349. static function with($field, $char) {
  1350. return 'LOWER(SUBSTRING(' . $field . ',1,1)) = "' . db::escape($char) . '"';
  1351. }
  1352. /**
  1353. * Builds a select clause from a simple array
  1354. *
  1355. * @param array $field An array of field names
  1356. * @return string The MySQL string
  1357. */
  1358. static function select_clause($fields) {
  1359. return implode(', ', $fields);
  1360. }
  1361. /**
  1362. * A simplifier to build IN clauses
  1363. *
  1364. * @param array $array An array of fieldnames
  1365. * @return string The MySQL string for the where clause
  1366. */
  1367. static function in($array) {
  1368. return '\'' . implode('\',\'', $array) . '\'';
  1369. }
  1370. /**
  1371. * A handler to convert key/value arrays to an where clause
  1372. *
  1373. * @param array $array keys/values for the where clause
  1374. * @param string $method AND or OR
  1375. * @return string The MySQL string for the where clause
  1376. */
  1377. static function where($array, $method='AND') {
  1378. if(!is_array($array)) return $array;
  1379. $output = array();
  1380. foreach($array AS $field => $value) {
  1381. $output[] = $field . ' = \'' . self::escape($value) . '\'';
  1382. $separator = ' ' . $method . ' ';
  1383. }
  1384. return implode(' ' . $method . ' ', $output);
  1385. }
  1386. /**
  1387. * An internal error handler
  1388. *
  1389. * @param string $msg The error/success message to return
  1390. * @param boolean $exit die after this error?
  1391. * @return mixed
  1392. */
  1393. static function error($msg=null, $exit=false) {
  1394. $connection = self::connection();
  1395. $error = (mysql_error()) ? @mysql_error($connection) : false;
  1396. $number = (mysql_errno()) ? @mysql_errno($connection) : 0;
  1397. if(c::get('db.debugging')) {
  1398. if($error) $msg .= ' -> ' . $error . ' (' . $number . ')';
  1399. if(self::$last_query) $msg .= ' Query: ' . self::$last_query;
  1400. } else $msg .= ' - ' . l::get('db.errors.msg', 'This will be fixed soon!');
  1401. if($exit || c::get('db.debugging')) die($msg);
  1402. return array(
  1403. 'status' => 'error',
  1404. 'msg' => $msg
  1405. );
  1406. }
  1407. }
  1408. /**
  1409. *
  1410. * Directory
  1411. *
  1412. * This class makes it easy to create/edit/delete
  1413. * directories on the filesystem
  1414. *
  1415. * @package Kirby
  1416. */
  1417. class dir {
  1418. /**
  1419. * Creates a new directory
  1420. *
  1421. * @param string $dir The path for the new directory
  1422. * @return boolean True: the dir has been created, false: creating failed
  1423. */
  1424. static function make($dir) {
  1425. if(is_dir($dir)) return true;
  1426. if(!@mkdir($dir, 0755)) return false;
  1427. @chmod($dir, 0755);
  1428. return true;
  1429. }
  1430. /**
  1431. * Reads all files from a directory and returns them as an array.
  1432. * It skips unwanted invisible stuff.
  1433. *
  1434. * @param string $dir The path of directory
  1435. * @param array $ignore Optional array with filenames, which should be ignored
  1436. * @return mixed An array of filenames or false
  1437. */
  1438. static function read($dir, $ignore=array()) {
  1439. if(!is_dir($dir)) return false;
  1440. $skip = array_merge(array('.', '..', '.DS_Store'), $ignore);
  1441. return array_diff(scandir($dir),$skip);
  1442. }
  1443. /**
  1444. * Reads a directory and returns a full set of info about it
  1445. *
  1446. * @param string $dir The path of directory
  1447. * @param array $ignore Optional array with filenames, which should be ignored
  1448. * @return mixed An info array or false
  1449. */
  1450. static function inspect($dir, $ignore=array()) {
  1451. if(!is_dir($dir)) return array();
  1452. $files = dir::read($dir, $ignore);
  1453. $modified = filemtime($dir);
  1454. $data = array(
  1455. 'name' => basename($dir),
  1456. 'root' => $dir,
  1457. 'modified' => $modified,
  1458. 'files' => array(),
  1459. 'children' => array()
  1460. );
  1461. foreach($files AS $file) {
  1462. if(is_dir($dir . '/' . $file)) {
  1463. $data['children'][] = $file;
  1464. } else {
  1465. $data['files'][] = $file;
  1466. }
  1467. }
  1468. return $data;
  1469. }
  1470. /**
  1471. * Moves a directory to a new location
  1472. *
  1473. * @param string $old The current path of the directory
  1474. * @param string $new The desired path where the dir should be moved to
  1475. * @return boolean True: the directory has been moved, false: moving failed
  1476. */
  1477. static function move($old, $new) {
  1478. if(!is_dir($old)) return false;
  1479. return (@rename($old, $new) && is_dir($new)) ? true : false;
  1480. }
  1481. /**
  1482. * Deletes a directory
  1483. *
  1484. * @param string $dir The path of the directory
  1485. * @param boolean $keep If set to true, the directory will flushed but not removed.
  1486. * @return boolean True: the directory has been removed, false: removing failed
  1487. */
  1488. static function remove($dir, $keep=false) {
  1489. if(!is_dir($dir)) return false;
  1490. $handle = @opendir($dir);
  1491. $skip = array('.', '..');
  1492. if(!$handle) return false;
  1493. while($item = @readdir($handle)) {
  1494. if(is_dir($dir . '/' . $item) && !in_array($item, $skip)) {
  1495. self::remove($dir . '/' . $item);
  1496. } else if(!in_array($item, $skip)) {
  1497. @unlink($dir . '/' . $item);
  1498. }
  1499. }
  1500. @closedir($handle);
  1501. if(!$keep) return @rmdir($dir);
  1502. return true;
  1503. }
  1504. /**
  1505. * Flushes a directory
  1506. *
  1507. * @param string $dir The path of the directory
  1508. * @return boolean True: the directory has been flushed, false: flushing failed
  1509. */
  1510. static function clean($dir) {
  1511. return self::remove($dir, true);
  1512. }
  1513. /**
  1514. * Gets the size of the directory and all subfolders and files
  1515. *
  1516. * @param string $dir The path of the directory
  1517. * @param boolean $recursive
  1518. * @param boolean $nice returns the size in a human readable size
  1519. * @return mixed
  1520. */
  1521. static function size($path, $recursive=true, $nice=false) {
  1522. if(!file_exists($path)) return false;
  1523. if(is_file($path)) return self::size($path, $nice);
  1524. $size = 0;
  1525. foreach(glob($path."/*") AS $file) {
  1526. if($file != "." && $file != "..") {
  1527. if($recursive) {
  1528. $size += self::size($file, true);
  1529. } else {
  1530. $size += f::size($path);
  1531. }
  1532. }
  1533. }
  1534. return ($nice) ? f::nice_size($size) : $size;
  1535. }
  1536. /**
  1537. * Recursively check when the dir and all
  1538. * subfolders have been modified for the last time.
  1539. *
  1540. * @param string $dir The path of the directory
  1541. * @param int $modified internal modified store
  1542. * @return int
  1543. */
  1544. static function modified($dir, $modified=false) {
  1545. if($modified === false) $modified = filemtime($dir);
  1546. $files = self::read($dir);
  1547. foreach($files AS $file) {
  1548. if(!is_dir($dir . '/' . $file)) continue;
  1549. $filectime = filemtime($dir . '/' . $file);
  1550. $modified = ($filectime > $modified) ? $filectime : $modified;
  1551. $modified = self::modified($dir . '/' . $file, $modified);
  1552. }
  1553. return $modified;
  1554. }
  1555. }
  1556. /**
  1557. *
  1558. * File
  1559. *
  1560. * This class makes it easy to
  1561. * create/edit/delete files
  1562. *
  1563. * @package Kirby
  1564. */
  1565. class f {
  1566. /**
  1567. * Creates a new file
  1568. *
  1569. * @param string $file The path for the new file
  1570. * @param mixed $content Either a string or an array. Arrays will be converted to JSON.
  1571. * @param boolean $append true: append the content to an exisiting file if available. false: overwrite.
  1572. * @return boolean
  1573. */
  1574. static function write($file,$content,$append=false){
  1575. if(is_array($content)) $content = a::json($content);
  1576. $mode = ($append) ? FILE_APPEND : false;
  1577. $write = @file_put_contents($file, $content, $mode);
  1578. @chmod($file, 0666);
  1579. return $write;
  1580. }
  1581. /**
  1582. * Appends new content to an existing file
  1583. *
  1584. * @param string $file The path for the file
  1585. * @param mixed $content Either a string or an array. Arrays will be converted to JSON.
  1586. * @return boolean
  1587. */
  1588. static function append($file,$content){
  1589. return self::write($file,$content,true);
  1590. }
  1591. /**
  1592. * Reads the content of a file
  1593. *
  1594. * @param string $file The path for the file
  1595. * @param mixed $parse if set to true, parse the result with the passed method. See: "str::parse()" for more info about available methods.
  1596. * @return mixed
  1597. */
  1598. static function read($file, $parse=false) {
  1599. $content = @file_get_contents($file);
  1600. return ($parse) ? str::parse($content, $parse) : $content;
  1601. }
  1602. /**
  1603. * Moves a file to a new location
  1604. *
  1605. * @param string $old The current path for the file
  1606. * @param string $new The path to the new location
  1607. * @return boolean
  1608. */
  1609. static function move($old, $new) {
  1610. if(!file_exists($old)) return false;
  1611. return (@rename($old, $new) && file_exists($new)) ? true : false;
  1612. }
  1613. /**
  1614. * Deletes a file
  1615. *
  1616. * @param string $file The path for the file
  1617. * @return boolean
  1618. */
  1619. static function remove($file) {
  1620. return (file_exists($file) && is_file($file) && !empty($file)) ? @unlink($file) : false;
  1621. }
  1622. /**
  1623. * Gets the extension of a file
  1624. *
  1625. * @param string $file The filename or path
  1626. * @return string
  1627. */
  1628. static function extension($filename) {
  1629. $ext = str_replace('.', '', strtolower(strrchr(trim($filename), '.')));
  1630. $ext = url::strip_query($ext);
  1631. $ext = preg_replace('!\:.*!i', '', $ext);
  1632. $ext = preg_replace('!\#.*!i', '', $ext);
  1633. return $ext;
  1634. }
  1635. /**
  1636. * Extracts the filename from a file path
  1637. *
  1638. * @param string $file The path
  1639. * @return string
  1640. */
  1641. static function filename($name) {
  1642. return basename($name);
  1643. }
  1644. /**
  1645. * Extracts the name from a file path or filename without extension
  1646. *
  1647. * @param string $file The path or filename
  1648. * @param boolean $remove_path remove the path from the name
  1649. * @return string
  1650. */
  1651. static function name($name, $remove_path = false) {
  1652. if($remove_path == true) $name = self::filename($name);
  1653. $dot=strrpos($name,'.');
  1654. if($dot) $name=substr($name,0,$dot);
  1655. return $name;
  1656. }
  1657. /**
  1658. * Just an alternative for dirname() to stay consistent
  1659. *
  1660. * @param string $file The path
  1661. * @return string
  1662. */
  1663. static function dirname($file=__FILE__) {
  1664. return dirname($file);
  1665. }
  1666. /**
  1667. * Returns the size of a file.
  1668. *
  1669. * @param string $file The path
  1670. * @param boolean $nice True: return the size in a human readable format
  1671. * @return mixed
  1672. */
  1673. static function size($file, $nice=false) {
  1674. @clearstatcache();
  1675. $size = @filesize($file);
  1676. if(!$size) return false;
  1677. return ($nice) ? self::nice_size($size) : $size;
  1678. }
  1679. /**
  1680. * Converts an integer size into a human readable format
  1681. *
  1682. * @param int $size The file size
  1683. * @return string
  1684. */
  1685. static function nice_size($size) {
  1686. $size = str::sanitize($size, 'int');
  1687. if($size < 1) return '0 kb';
  1688. $unit=array('b','kb','mb','gb','tb','pb');
  1689. return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
  1690. }
  1691. /**
  1692. * Convert the filename to a new extension
  1693. *
  1694. * @param string $name The file name
  1695. * @param string $type The new extension
  1696. * @return string
  1697. */
  1698. static function convert($name, $type='jpg') {
  1699. return self::name($name) . $type;
  1700. }
  1701. /**
  1702. * Sanitize a filename to strip unwanted special characters
  1703. *
  1704. * @param string $string The file name
  1705. * @return string
  1706. */
  1707. static function safe_name($string) {
  1708. return str::urlify($string);
  1709. }
  1710. }
  1711. /**
  1712. *
  1713. * Globals
  1714. *
  1715. * The Kirby Globals Class
  1716. * Easy setting/getting of globals
  1717. *
  1718. * @package Kirby
  1719. */
  1720. class g {
  1721. /**
  1722. * Gets an global value by key
  1723. *
  1724. * @param mixed $key The key to look for. Pass false or null to return the entire globals array.
  1725. * @param mixed $default Optional default value, which should be returned if no element has been found
  1726. * @return mixed
  1727. */
  1728. static function get($key=null, $default=null) {
  1729. if(empty($key)) return $GLOBALS;
  1730. return a::get($GLOBALS, $key, $default);
  1731. }
  1732. /**
  1733. * Sets a global by key
  1734. *
  1735. * @param string $key The key to define
  1736. * @param mixed $value The value for the passed key
  1737. */
  1738. static function set($key, $value=null) {
  1739. if(is_array($key)) {
  1740. // set all new values
  1741. $GLOBALS = array_merge($GLOBALS, $key);
  1742. } else {
  1743. $GLOBALS[$key] = $value;
  1744. }
  1745. }
  1746. }
  1747. /**
  1748. *
  1749. * Language
  1750. *
  1751. * Some handy methods to handle multi-language support
  1752. *
  1753. * @todo rework all but set() and get()
  1754. * @package Kirby
  1755. */
  1756. class l {
  1757. /**
  1758. * The global language array
  1759. *
  1760. * @var array
  1761. */
  1762. public static $lang = array();
  1763. /**
  1764. * Gets a language value by key
  1765. *
  1766. * @param mixed $key The key to look for. Pass false or null to return the entire language array.
  1767. * @param mixed $default Optional default value, which should be returned if no element has been found
  1768. * @return mixed
  1769. */
  1770. static function get($key=null, $default=null) {
  1771. if(empty($key)) return self::$lang;
  1772. return a::get(self::$lang, $key, $default);
  1773. }
  1774. /**
  1775. * Sets a language value by key
  1776. *
  1777. * @param mixed $key The key to define
  1778. * @param mixed $value The value for the passed key
  1779. */
  1780. static function set($key, $value=null) {
  1781. if(is_array($key)) {
  1782. self::$lang = array_merge(self::$lang, $key);
  1783. } else {
  1784. self::$lang[$key] = $value;
  1785. }
  1786. }
  1787. /**
  1788. * @todo rework
  1789. */
  1790. static function change($language='en') {
  1791. s::set('language', l::sanitize($language));
  1792. return s::get('language');
  1793. }
  1794. /**
  1795. * @todo rework
  1796. */
  1797. static function current() {
  1798. if(s::get('language')) return s::get('language');
  1799. $lang = str::split(server::get('http_accept_language'), '-');
  1800. $lang = str::trim(a::get($lang, 0));
  1801. $lang = l::sanitize($lang);
  1802. s::set('language', $lang);
  1803. return $lang;
  1804. }
  1805. /**
  1806. * @todo rework
  1807. */
  1808. static function locale($language=false) {
  1809. if(!$language) $language = l::current();
  1810. $default_locales = array(
  1811. 'de' => array('de_DE.UTF8','de_DE@euro','de_DE','de','ge'),
  1812. 'fr' => array('fr_FR.UTF8','fr_FR','fr'),
  1813. 'es' => array('es_ES.UTF8','es_ES','es'),
  1814. 'it' => array('it_IT.UTF8','it_IT','it'),
  1815. 'pt' => array('pt_PT.UTF8','pt_PT','pt'),
  1816. 'zh' => array('zh_CN.UTF8','zh_CN','zh'),
  1817. 'en' => array('en_US.UTF8','en_US','en'),
  1818. );
  1819. $locales = c::get('locales', array());
  1820. $locales = array_merge($default_locales, $locales);
  1821. setlocale(LC_ALL, a::get($locales, $language, array('en_US.UTF8','en_US','en')));
  1822. return setlocale(LC_ALL, 0);
  1823. }
  1824. /**
  1825. * @todo rework
  1826. */
  1827. static function load($file) {
  1828. // replace the language variable
  1829. $file = str_replace('{language}', l::current(), $file);
  1830. // check if it exists
  1831. if(file_exists($file)) {
  1832. require($file);
  1833. return l::get();
  1834. }
  1835. // try to find the default language file
  1836. $file = str_replace('{language}', c::get('language', 'en'), $file);
  1837. // check again if it exists
  1838. if(file_exists($file)) require($file);
  1839. return l::get();
  1840. }
  1841. /**
  1842. * @todo rework
  1843. */
  1844. static function sanitize($language) {
  1845. if(!in_array($language, c::get('languages', array('en')) )) $language = c::get('language', 'en');
  1846. return $language;
  1847. }
  1848. }
  1849. /**
  1850. *
  1851. * Request
  1852. *
  1853. * Handles all incoming requests
  1854. *
  1855. * @package Kirby
  1856. */
  1857. class r {
  1858. /**
  1859. * Stores all sanitized request data
  1860. *
  1861. * @var array
  1862. */
  1863. static private $_ = false;
  1864. // fetch all data from the request and sanitize it
  1865. static function data() {
  1866. if(self::$_) return self::$_;
  1867. return self::$_ = self::sanitize($_REQUEST);
  1868. }
  1869. /**
  1870. * Sanitizes the incoming data
  1871. *
  1872. * @param array $data
  1873. * @return array
  1874. */
  1875. static function sanitize($data) {
  1876. foreach($data as $key => $value) {
  1877. if(!is_array($value)) {
  1878. $value = trim(str::stripslashes($value));
  1879. } else {
  1880. $value = self::sanitize($value);
  1881. }
  1882. $data[$key] = $value;
  1883. }
  1884. return $data;
  1885. }
  1886. /**
  1887. * Sets a request value by key
  1888. *
  1889. * @param mixed $key The key to define
  1890. * @param mixed $value The value for the passed key
  1891. */
  1892. static function set($key, $value=null) {
  1893. $data = self::data();
  1894. if(is_array($key)) {
  1895. self::$_ = array_merge($data, $key);
  1896. } else {
  1897. self::$_[$key] = $value;
  1898. }
  1899. }
  1900. /**
  1901. * Gets a request value by key
  1902. *
  1903. * @param mixed $key The key to look for. Pass false or null to return the entire request array.
  1904. * @param mixed $default Optional default value, which should be returned if no element has been found
  1905. * @return mixed
  1906. */
  1907. static function get($key=false, $default=null) {
  1908. $request = (self::method() == 'GET') ? self::data() : array_merge(self::data(), self::body());
  1909. if(empty($key)) return $request;
  1910. return a::get($request, $key, $default);
  1911. }
  1912. /**
  1913. * Returns the current request method
  1914. *
  1915. * @return string POST, GET, DELETE, PUT
  1916. */
  1917. static function method() {
  1918. return strtoupper(server::get('request_method'));
  1919. }
  1920. /**
  1921. * Returns the request body from POST requests for example
  1922. *
  1923. * @return array
  1924. */
  1925. static function body() {
  1926. @parse_str(@file_get_contents('php://input'), $body);
  1927. return self::sanitize((array)$body);
  1928. }
  1929. /**
  1930. * Checks if the current request is an AJAX request
  1931. *
  1932. * @return boolean
  1933. */
  1934. static function is_ajax() {
  1935. return (strtolower(server::get('http_x_requested_with')) == 'xmlhttprequest') ? true : false;
  1936. }
  1937. /**
  1938. * Checks if the current request is a GET request
  1939. *
  1940. * @return boolean
  1941. */
  1942. static function is_get() {
  1943. return (self::method() == 'GET') ? true : false;
  1944. }
  1945. /**
  1946. * Checks if the current request is a POST request
  1947. *
  1948. * @return boolean
  1949. */
  1950. static function is_post() {
  1951. return (self::method() == 'POST') ? true : false;
  1952. }
  1953. /**
  1954. * Checks if the current request is a DELETE request
  1955. *
  1956. * @return boolean
  1957. */
  1958. static function is_delete() {
  1959. return (self::method() == 'DELETE') ? true : false;
  1960. }
  1961. /**
  1962. * Checks if the current request is a PUT request
  1963. *
  1964. * @return boolean
  1965. */
  1966. static function is_put() {
  1967. return (self::method() == 'PUT') ? true : false;
  1968. }
  1969. /**
  1970. * Returns the HTTP_REFERER
  1971. *
  1972. * @param string $default Define a default URL if no referer has been found
  1973. * @return string
  1974. */
  1975. static function referer($default=null) {
  1976. if(empty($default)) $default = '/';
  1977. return server::get('http_referer', $default);
  1978. }
  1979. }
  1980. /**
  1981. * Shortcut for r::get()
  1982. *
  1983. * @param mixed $key The key to look for. Pass false or null to return the entire request array.
  1984. * @param mixed $default Optional default value, which should be returned if no element has been found
  1985. * @return mixed
  1986. * @package Kirby
  1987. */
  1988. function get($key=false, $default=null) {
  1989. return r::get($key, $default);
  1990. }
  1991. /**
  1992. *
  1993. * Session
  1994. *
  1995. * Handles all session fiddling
  1996. *
  1997. * @package Kirby
  1998. */
  1999. class s {
  2000. /**
  2001. * Returns the current session id
  2002. *
  2003. * @return string
  2004. */
  2005. static function id() {
  2006. return @session_id();
  2007. }
  2008. /**
  2009. * Sets a session value by key
  2010. *
  2011. * @param mixed $key The key to define
  2012. * @param mixed $value The value for the passed key
  2013. */
  2014. static function set($key, $value=false) {
  2015. if(!isset($_SESSION)) return false;
  2016. if(is_array($key)) {
  2017. $_SESSION = array_merge($_SESSION, $key);
  2018. } else {
  2019. $_SESSION[$key] = $value;
  2020. }
  2021. }
  2022. /**
  2023. * Gets a session value by key
  2024. *
  2025. * @param mixed $key The key to look for. Pass false or null to return the entire session array.
  2026. * @param mixed $default Optional default value, which should be returned if no element has been found
  2027. * @return mixed
  2028. */
  2029. static function get($key=false, $default=null) {
  2030. if(!isset($_SESSION)) return false;
  2031. if(empty($key)) return $_SESSION;
  2032. return a::get($_SESSION, $key, $default);
  2033. }
  2034. /**
  2035. * Removes a value from the session by key
  2036. *
  2037. * @param mixed $key The key to remove by
  2038. * @return array The session array without the value
  2039. */
  2040. static function remove($key) {
  2041. if(!isset($_SESSION)) return false;
  2042. $_SESSION = a::remove($_SESSION, $key, true);
  2043. return $_SESSION;
  2044. }
  2045. /**
  2046. * Starts a new session
  2047. *
  2048. */
  2049. static function start() {
  2050. @session_start();
  2051. }
  2052. /**
  2053. * Destroys a session
  2054. *
  2055. */
  2056. static function destroy() {
  2057. @session_destroy();
  2058. }
  2059. /**
  2060. * Destroys a session first and then starts it again
  2061. *
  2062. */
  2063. static function restart() {
  2064. self::destroy();
  2065. self::start();
  2066. }
  2067. }
  2068. /**
  2069. *
  2070. * Server
  2071. *
  2072. * Makes it more convenient to get variables
  2073. * from the global server array
  2074. *
  2075. * @package Kirby
  2076. */
  2077. class server {
  2078. /**
  2079. * Gets a value from the _SERVER array
  2080. *
  2081. * @param mixed $key The key to look for. Pass false or null to return the entire server array.
  2082. * @param mixed $default Optional default value, which should be returned if no element has been found
  2083. * @return mixed
  2084. */
  2085. static function get($key=false, $default=null) {
  2086. if(empty($key)) return $_SERVER;
  2087. return a::get($_SERVER, str::upper($key), $default);
  2088. }
  2089. }
  2090. /**
  2091. *
  2092. * Size
  2093. *
  2094. * Makes it easy to recalculate image dimensions
  2095. *
  2096. * @package Kirby
  2097. */
  2098. class size {
  2099. /**
  2100. * Gets the ratio by width and height
  2101. *
  2102. * @param int $width
  2103. * @param int $height
  2104. * @return float
  2105. */
  2106. static function ratio($width, $height) {
  2107. return ($width / $height);
  2108. }
  2109. /**
  2110. * Fits width and height into a defined box and keeps the ratio
  2111. *
  2112. * @param int $width
  2113. * @param int $height
  2114. * @param int $box
  2115. * @param boolean $force If width and height are smaller than the box this will force upscaling
  2116. * @return array An array with a key and value for width and height
  2117. */
  2118. static function fit($width, $height, $box, $force=false) {
  2119. if($width == 0 || $height == 0) return array('width' => $box, 'height' => $box);
  2120. $ratio = self::ratio($width, $height);
  2121. if($width > $height) {
  2122. if($width > $box || $force == true) $width = $box;
  2123. $height = floor($width / $ratio);
  2124. } elseif($height > $width) {
  2125. if($height > $box || $force == true) $height = $box;
  2126. $width = floor($height * $ratio);
  2127. } elseif($width > $box) {
  2128. $width = $box;
  2129. $height = $box;
  2130. }
  2131. $output = array();
  2132. $output['width'] = $width;
  2133. $output['height'] = $height;
  2134. return $output;
  2135. }
  2136. /**
  2137. * Fits width and height by a passed width and keeps the ratio
  2138. *
  2139. * @param int $width
  2140. * @param int $height
  2141. * @param int $fit The new width
  2142. * @param boolean $force If width and height are smaller than the box this will force upscaling
  2143. * @return array An array with a key and value for width and height
  2144. */
  2145. static function fit_width($width, $height, $fit, $force=false) {
  2146. if($width <= $fit && !$force) return array(
  2147. 'width' => $width,
  2148. 'height' => $height
  2149. );
  2150. $ratio = self::ratio($width, $height);
  2151. return array(
  2152. 'width' => $fit,
  2153. 'height' => floor($fit / $ratio)
  2154. );
  2155. }
  2156. /**
  2157. * Fits width and height by a passed height and keeps the ratio
  2158. *
  2159. * @param int $width
  2160. * @param int $height
  2161. * @param int $fit The new height
  2162. * @param boolean $force If width and height are smaller than the box this will force upscaling
  2163. * @return array An array with a key and value for width and height
  2164. */
  2165. static function fit_height($width, $height, $fit, $force=false) {
  2166. if($height <= $fit && !$force) return array(
  2167. 'width' => $width,
  2168. 'height' => $height
  2169. );
  2170. $ratio = self::ratio($width, $height);
  2171. return array(
  2172. 'width' => floor($fit * $ratio),
  2173. 'height' => $fit
  2174. );
  2175. }
  2176. }
  2177. /**
  2178. *
  2179. * String
  2180. *
  2181. * A set of handy string methods
  2182. *
  2183. * @package Kirby
  2184. */
  2185. class str {
  2186. /**
  2187. * Converts a string to a html-safe string
  2188. *
  2189. * @param string $string
  2190. * @param boolean $keep_html True: lets stuff inside html tags untouched.
  2191. * @return string The html string
  2192. */
  2193. static function html($string, $keep_html=true) {
  2194. if($keep_html) {
  2195. return stripslashes(implode('', preg_replace('/^([^<].+[^>])$/e', "htmlentities('\\1', ENT_COMPAT, 'utf-8')", preg_split('/(<.+?>)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE))));
  2196. } else {
  2197. return htmlentities($string, ENT_COMPAT, 'utf-8');
  2198. }
  2199. }
  2200. /**
  2201. * Removes all html tags and encoded chars from a string
  2202. *
  2203. * @param string $string
  2204. * @return string The html string
  2205. */
  2206. static function unhtml($string) {
  2207. $string = strip_tags($string);
  2208. return html_entity_decode($string, ENT_COMPAT, 'utf-8');
  2209. }
  2210. /**
  2211. * An internal store for a html entities translation table
  2212. *
  2213. * @return array
  2214. */
  2215. static function entities() {
  2216. return array(
  2217. '&nbsp;' => '&#160;', '&iexcl;' => '&#161;', '&cent;' => '&#162;', '&pound;' => '&#163;', '&curren;' => '&#164;', '&yen;' => '&#165;', '&brvbar;' => '&#166;', '&sect;' => '&#167;',
  2218. '&uml;' => '&#168;', '&copy;' => '&#169;', '&ordf;' => '&#170;', '&laquo;' => '&#171;', '&not;' => '&#172;', '&shy;' => '&#173;', '&reg;' => '&#174;', '&macr;' => '&#175;',
  2219. '&deg;' => '&#176;', '&plusmn;' => '&#177;', '&sup2;' => '&#178;', '&sup3;' => '&#179;', '&acute;' => '&#180;', '&micro;' => '&#181;', '&para;' => '&#182;', '&middot;' => '&#183;',
  2220. '&cedil;' => '&#184;', '&sup1;' => '&#185;', '&ordm;' => '&#186;', '&raquo;' => '&#187;', '&frac14;' => '&#188;', '&frac12;' => '&#189;', '&frac34;' => '&#190;', '&iquest;' => '&#191;',
  2221. '&Agrave;' => '&#192;', '&Aacute;' => '&#193;', '&Acirc;' => '&#194;', '&Atilde;' => '&#195;', '&Auml;' => '&#196;', '&Aring;' => '&#197;', '&AElig;' => '&#198;', '&Ccedil;' => '&#199;',
  2222. '&Egrave;' => '&#200;', '&Eacute;' => '&#201;', '&Ecirc;' => '&#202;', '&Euml;' => '&#203;', '&Igrave;' => '&#204;', '&Iacute;' => '&#205;', '&Icirc;' => '&#206;', '&Iuml;' => '&#207;',
  2223. '&ETH;' => '&#208;', '&Ntilde;' => '&#209;', '&Ograve;' => '&#210;', '&Oacute;' => '&#211;', '&Ocirc;' => '&#212;', '&Otilde;' => '&#213;', '&Ouml;' => '&#214;', '&times;' => '&#215;',
  2224. '&Oslash;' => '&#216;', '&Ugrave;' => '&#217;', '&Uacute;' => '&#218;', '&Ucirc;' => '&#219;', '&Uuml;' => '&#220;', '&Yacute;' => '&#221;', '&THORN;' => '&#222;', '&szlig;' => '&#223;',
  2225. '&agrave;' => '&#224;', '&aacute;' => '&#225;', '&acirc;' => '&#226;', '&atilde;' => '&#227;', '&auml;' => '&#228;', '&aring;' => '&#229;', '&aelig;' => '&#230;', '&ccedil;' => '&#231;',
  2226. '&egrave;' => '&#232;', '&eacute;' => '&#233;', '&ecirc;' => '&#234;', '&euml;' => '&#235;', '&igrave;' => '&#236;', '&iacute;' => '&#237;', '&icirc;' => '&#238;', '&iuml;' => '&#239;',
  2227. '&eth;' => '&#240;', '&ntilde;' => '&#241;', '&ograve;' => '&#242;', '&oacute;' => '&#243;', '&ocirc;' => '&#244;', '&otilde;' => '&#245;', '&ouml;' => '&#246;', '&divide;' => '&#247;',
  2228. '&oslash;' => '&#248;', '&ugrave;' => '&#249;', '&uacute;' => '&#250;', '&ucirc;' => '&#251;', '&uuml;' => '&#252;', '&yacute;' => '&#253;', '&thorn;' => '&#254;', '&yuml;' => '&#255;',
  2229. '&fnof;' => '&#402;', '&Alpha;' => '&#913;', '&Beta;' => '&#914;', '&Gamma;' => '&#915;', '&Delta;' => '&#916;', '&Epsilon;' => '&#917;', '&Zeta;' => '&#918;', '&Eta;' => '&#919;',
  2230. '&Theta;' => '&#920;', '&Iota;' => '&#921;', '&Kappa;' => '&#922;', '&Lambda;' => '&#923;', '&Mu;' => '&#924;', '&Nu;' => '&#925;', '&Xi;' => '&#926;', '&Omicron;' => '&#927;',
  2231. '&Pi;' => '&#928;', '&Rho;' => '&#929;', '&Sigma;' => '&#931;', '&Tau;' => '&#932;', '&Upsilon;' => '&#933;', '&Phi;' => '&#934;', '&Chi;' => '&#935;', '&Psi;' => '&#936;',
  2232. '&Omega;' => '&#937;', '&alpha;' => '&#945;', '&beta;' => '&#946;', '&gamma;' => '&#947;', '&delta;' => '&#948;', '&epsilon;' => '&#949;', '&zeta;' => '&#950;', '&eta;' => '&#951;',
  2233. '&theta;' => '&#952;', '&iota;' => '&#953;', '&kappa;' => '&#954;', '&lambda;' => '&#955;', '&mu;' => '&#956;', '&nu;' => '&#957;', '&xi;' => '&#958;', '&omicron;' => '&#959;',
  2234. '&pi;' => '&#960;', '&rho;' => '&#961;', '&sigmaf;' => '&#962;', '&sigma;' => '&#963;', '&tau;' => '&#964;', '&upsilon;' => '&#965;', '&phi;' => '&#966;', '&chi;' => '&#967;',
  2235. '&psi;' => '&#968;', '&omega;' => '&#969;', '&thetasym;' => '&#977;', '&upsih;' => '&#978;', '&piv;' => '&#982;', '&bull;' => '&#8226;', '&hellip;' => '&#8230;', '&prime;' => '&#8242;',
  2236. '&Prime;' => '&#8243;', '&oline;' => '&#8254;', '&frasl;' => '&#8260;', '&weierp;' => '&#8472;', '&image;' => '&#8465;', '&real;' => '&#8476;', '&trade;' => '&#8482;', '&alefsym;' => '&#8501;',
  2237. '&larr;' => '&#8592;', '&uarr;' => '&#8593;', '&rarr;' => '&#8594;', '&darr;' => '&#8595;', '&harr;' => '&#8596;', '&crarr;' => '&#8629;', '&lArr;' => '&#8656;', '&uArr;' => '&#8657;',
  2238. '&rArr;' => '&#8658;', '&dArr;' => '&#8659;', '&hArr;' => '&#8660;', '&forall;' => '&#8704;', '&part;' => '&#8706;', '&exist;' => '&#8707;', '&empty;' => '&#8709;', '&nabla;' => '&#8711;',
  2239. '&isin;' => '&#8712;', '&notin;' => '&#8713;', '&ni;' => '&#8715;', '&prod;' => '&#8719;', '&sum;' => '&#8721;', '&minus;' => '&#8722;', '&lowast;' => '&#8727;', '&radic;' => '&#8730;',
  2240. '&prop;' => '&#8733;', '&infin;' => '&#8734;', '&ang;' => '&#8736;', '&and;' => '&#8743;', '&or;' => '&#8744;', '&cap;' => '&#8745;', '&cup;' => '&#8746;', '&int;' => '&#8747;',
  2241. '&there4;' => '&#8756;', '&sim;' => '&#8764;', '&cong;' => '&#8773;', '&asymp;' => '&#8776;', '&ne;' => '&#8800;', '&equiv;' => '&#8801;', '&le;' => '&#8804;', '&ge;' => '&#8805;',
  2242. '&sub;' => '&#8834;', '&sup;' => '&#8835;', '&nsub;' => '&#8836;', '&sube;' => '&#8838;', '&supe;' => '&#8839;', '&oplus;' => '&#8853;', '&otimes;' => '&#8855;', '&perp;' => '&#8869;',
  2243. '&sdot;' => '&#8901;', '&lceil;' => '&#8968;', '&rceil;' => '&#8969;', '&lfloor;' => '&#8970;', '&rfloor;' => '&#8971;', '&lang;' => '&#9001;', '&rang;' => '&#9002;', '&loz;' => '&#9674;',
  2244. '&spades;' => '&#9824;', '&clubs;' => '&#9827;', '&hearts;' => '&#9829;', '&diams;' => '&#9830;', '&quot;' => '&#34;', '&amp;' => '&#38;', '&lt;' => '&#60;', '&gt;' => '&#62;', '&OElig;' => '&#338;',
  2245. '&oelig;' => '&#339;', '&Scaron;' => '&#352;', '&scaron;' => '&#353;', '&Yuml;' => '&#376;', '&circ;' => '&#710;', '&tilde;' => '&#732;', '&ensp;' => '&#8194;', '&emsp;' => '&#8195;',
  2246. '&thinsp;' => '&#8201;', '&zwnj;' => '&#8204;', '&zwj;' => '&#8205;', '&lrm;' => '&#8206;', '&rlm;' => '&#8207;', '&ndash;' => '&#8211;', '&mdash;' => '&#8212;', '&lsquo;' => '&#8216;',
  2247. '&rsquo;' => '&#8217;', '&sbquo;' => '&#8218;', '&ldquo;' => '&#8220;', '&rdquo;' => '&#8221;', '&bdquo;' => '&#8222;', '&dagger;' => '&#8224;', '&Dagger;' => '&#8225;', '&permil;' => '&#8240;',
  2248. '&lsaquo;' => '&#8249;', '&rsaquo;' => '&#8250;', '&euro;' => '&#8364;'
  2249. );
  2250. }
  2251. /**
  2252. * Converts a string to a xml-safe string
  2253. * Converts it to html-safe first and then it
  2254. * will replace html entities to xml entities
  2255. *
  2256. * @param string $text
  2257. * @param boolean $html True: convert to html first
  2258. * @return string
  2259. */
  2260. static function xml($text, $html=true) {
  2261. // convert raw text to html safe text
  2262. if($html) $text = self::html($text);
  2263. // convert html entities to xml entities
  2264. return strtr($text, self::entities());
  2265. }
  2266. /**
  2267. * Removes all xml entities from a string
  2268. * and convert them to html entities first
  2269. * and remove all html entities afterwards.
  2270. *
  2271. * @param string $string
  2272. * @return string
  2273. */
  2274. static function unxml($string) {
  2275. // flip the conversion table
  2276. $table = array_flip(self::entities());
  2277. // convert xml entities to html entities
  2278. $string = strtr($string, $table);
  2279. return str::unhtml($string);
  2280. }
  2281. /**
  2282. * Parses a string by a set of available methods
  2283. *
  2284. * Available methods:
  2285. * - json
  2286. * - xml
  2287. * - url
  2288. * - query
  2289. * - php
  2290. *
  2291. * @param string $string
  2292. * @param string $mode
  2293. * @return string
  2294. */
  2295. static function parse($string, $mode='json') {
  2296. if(is_array($string)) return $string;
  2297. switch($mode) {
  2298. case 'json':
  2299. $result = (array)@json_decode($string, true);
  2300. break;
  2301. case 'xml':
  2302. $result = x::parse($string);
  2303. break;
  2304. case 'url':
  2305. $result = (array)@parse_url($string);
  2306. break;
  2307. case 'query':
  2308. if(url::has_query($string)) {
  2309. $string = self::split($string, '?');
  2310. $string = a::last($string);
  2311. }
  2312. @parse_str($string, $result);
  2313. break;
  2314. case 'php':
  2315. $result = @unserialize($string);
  2316. break;
  2317. default:
  2318. $result = $string;
  2319. break;
  2320. }
  2321. return $result;
  2322. }
  2323. /**
  2324. * Encode a string (used for email addresses)
  2325. *
  2326. * @param string $string
  2327. * @return string
  2328. */
  2329. static function encode($string) {
  2330. $decoded = utf8_decode($string);
  2331. $encoded = '';
  2332. $length = str::length($string);
  2333. for($i=0; $i<$length; $i++) {
  2334. if($decoded[$i] === $string[$i]) {
  2335. $encoded .= (rand(1,2)==1) ? '&#'.ord($string[$i]).';' : '&#x'.dechex(ord($string[$i])).';';
  2336. } else {
  2337. $encoded .= (rand(1,2)==1) ? '&#'.ord($decoded[$i]).';' : '&#x'.dechex(ord($decoded[$i])).';';
  2338. }
  2339. }
  2340. return $encoded;
  2341. }
  2342. /**
  2343. * Creates an encoded email address, including proper html-tags
  2344. *
  2345. * @param string $email The email address
  2346. * @param string $text Specify a text for the email link. If false the email address will be used
  2347. * @param string $title An optional title for the html tag.
  2348. * @param string $class An optional class name for the html tag.
  2349. * @return string
  2350. */
  2351. static function email($email, $text=false, $title=false, $class=false) {
  2352. if(empty($email)) return false;
  2353. $email = (string)$email;
  2354. $string = (empty($text)) ? $email : $text;
  2355. $email = self::encode($email, 3);
  2356. if(!empty($class)) $class = ' class="' . $class . '"';
  2357. if(!empty($title)) $title = ' title="' . html($title) . '"';
  2358. return '<a' . $title . $class . ' href="mailto:' . $email . '">' . self::encode($string, 3) . '</a>';
  2359. }
  2360. /**
  2361. * Creates a link tag
  2362. *
  2363. * @param string $link The URL
  2364. * @param string $text Specify a text for the link tag. If false the URL will be used
  2365. * @return string
  2366. */
  2367. static function link($link, $text=false) {
  2368. $text = ($text) ? $text : $link;
  2369. return '<a href="' . $link . '">' . str::html($text) . '</a>';
  2370. }
  2371. /**
  2372. * Shortens a string and adds an ellipsis if the string is too long
  2373. *
  2374. * @param string $string The string to be shortened
  2375. * @param int $chars The final number of characters the string should have
  2376. * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default.
  2377. * @return string The shortened string
  2378. */
  2379. static function short($string, $chars, $rep='…') {
  2380. if($chars == 0) return $string;
  2381. if(str::length($string) <= $chars) return $string;
  2382. $string = self::substr($string,0,($chars-str::length($rep)));
  2383. $punctuation = '.!?:;,-';
  2384. $string = (strspn(strrev($string), $punctuation)!=0) ? substr($string, 0, -strspn(strrev($string), $punctuation)) : $string;
  2385. return $string . $rep;
  2386. }
  2387. /**
  2388. * Shortens an URL
  2389. * It removes http:// or https:// and uses str::short afterwards
  2390. *
  2391. * @param string $url The URL to be shortened
  2392. * @param int $chars The final number of characters the URL should have
  2393. * @param boolean $base True: only take the base of the URL.
  2394. * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default.
  2395. * @return string The shortened URL
  2396. */
  2397. static function shorturl($url, $chars=false, $base=false, $rep='…') {
  2398. return url::short($url, $chars, $base, $rep);
  2399. }
  2400. /**
  2401. * Creates an exceprt of a string
  2402. * It removes all html tags first and then uses str::short
  2403. *
  2404. * @param string $string The string to be shortened
  2405. * @param int $chars The final number of characters the string should have
  2406. * @param boolean $removehtml True: remove the HTML tags from the string first
  2407. * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default.
  2408. * @return string The shortened string
  2409. */
  2410. static function excerpt($string, $chars=140, $removehtml=true, $rep='…') {
  2411. if($removehtml) $string = strip_tags($string);
  2412. $string = str::trim($string);
  2413. $string = str_replace("\n", ' ', $string);
  2414. if(str::length($string) <= $chars) return $string;
  2415. return ($chars==0) ? $string : substr($string, 0, strrpos(substr($string, 0, $chars), ' ')) . $rep;
  2416. }
  2417. /**
  2418. * Shortens a string by cutting out chars in the middle
  2419. * This method mimicks the shortening which is used for filenames in the Finder
  2420. *
  2421. * @param string $string The string to be shortened
  2422. * @param int $length The final number of characters the string should have
  2423. * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default.
  2424. * @return string The shortened string
  2425. */
  2426. static function cutout($str, $length, $rep='…') {
  2427. $strlength = str::length($str);
  2428. if($length >= $strlength) return $str;
  2429. // calc the how much we have to cut off
  2430. $cut = (($strlength+str::length($rep)) - $length);
  2431. // divide it to cut left and right from the center
  2432. $cutp = round($cut/2);
  2433. // get the center of the string
  2434. $strcenter = round($strlength/2);
  2435. // get the start of the cut
  2436. $strlcenter = ($strcenter-$cutp);
  2437. // get the end of the cut
  2438. $strrcenter = ($strcenter+$cutp);
  2439. // cut and glue
  2440. return str::substr($str, 0, $strlcenter) . $rep . str::substr($str, $strrcenter);
  2441. }
  2442. /**
  2443. * Adds an apostrohpe to a string/name if applicable
  2444. *
  2445. * @param string $name The string to be shortened
  2446. * @return string The string + apostrophe
  2447. */
  2448. static function apostrophe($name) {
  2449. return (substr($name,-1,1) == 's' || substr($name,-1,1) == 'z') ? $name .= "'" : $name .= "'s";
  2450. }
  2451. /**
  2452. * A switch to display either one or the other string dependend on a counter
  2453. *
  2454. * @param int $count The counter
  2455. * @param string $many The string to be displayed for a counter > 1
  2456. * @param string $one The string to be displayed for a counter == 1
  2457. * @param string $zero The string to be displayed for a counter == 0
  2458. * @return string The string
  2459. */
  2460. static function plural($count, $many, $one, $zero = '') {
  2461. if($count == 1) return $one;
  2462. else if($count == 0 && !empty($zero)) return $zero;
  2463. else return $many;
  2464. }
  2465. /**
  2466. * An UTF-8 safe version of substr()
  2467. *
  2468. * @param string $str
  2469. * @param int $start
  2470. * @param int $end
  2471. * @return string
  2472. */
  2473. static function substr($str, $start, $end = null) {
  2474. return mb_substr($str, $start, ($end == null) ? mb_strlen($str, 'UTF-8') : $end, 'UTF-8');
  2475. }
  2476. /**
  2477. * An UTF-8 safe version of strtolower()
  2478. *
  2479. * @param string $str
  2480. * @return string
  2481. */
  2482. static function lower($str) {
  2483. return mb_strtolower($str, 'UTF-8');
  2484. }
  2485. /**
  2486. * An UTF-8 safe version of strotoupper()
  2487. *
  2488. * @param string $str
  2489. * @return string
  2490. */
  2491. static function upper($str) {
  2492. return mb_strtoupper($str, 'UTF-8');
  2493. }
  2494. /**
  2495. * An UTF-8 safe version of strlen()
  2496. *
  2497. * @param string $str
  2498. * @return string
  2499. */
  2500. static function length($str) {
  2501. return mb_strlen($str, 'UTF-8');
  2502. }
  2503. /**
  2504. * Checks if a str contains another string
  2505. *
  2506. * @param string $str
  2507. * @param string $needle
  2508. * @param boolean $i ignore upper/lowercase
  2509. * @return string
  2510. */
  2511. static function contains($str, $needle, $i=true) {
  2512. if($i) {
  2513. $str = str::lower($str);
  2514. $needle = str::lower($needle);
  2515. }
  2516. return (strstr($str, $needle)) ? true : false;
  2517. }
  2518. /**
  2519. * preg_match sucks! This tries to make it more convenient
  2520. *
  2521. * @param string $string
  2522. * @param string $preg Regular expression
  2523. * @param string $get Which part should be returned from the result array
  2524. * @param string $placeholder Default value if nothing will be found
  2525. * @return mixed
  2526. */
  2527. static function match($string, $preg, $get=false, $placeholder=false) {
  2528. $match = @preg_match($preg, $string, $array);
  2529. if(!$match) return false;
  2530. if($get === false) return $array;
  2531. return a::get($array, $get, $placeholder);
  2532. }
  2533. /**
  2534. * Generates a random string
  2535. *
  2536. * @param int $length The length of the random string
  2537. * @return string
  2538. */
  2539. static function random($length=false) {
  2540. $length = ($length) ? $length : rand(5,10);
  2541. $chars = range('a','z');
  2542. $num = range(0,9);
  2543. $pool = array_merge($chars, $num);
  2544. $string = '';
  2545. for($x=0; $x<$length; $x++) {
  2546. shuffle($pool);
  2547. $string .= current($pool);
  2548. }
  2549. return $string;
  2550. }
  2551. /**
  2552. * Convert a string to a safe version to be used in an URL
  2553. *
  2554. * @param string $text The unsafe string
  2555. * @return string The safe string
  2556. */
  2557. static function urlify($text) {
  2558. $text = trim($text);
  2559. $text = str::lower($text);
  2560. $replace = array(
  2561. // French + Spanish
  2562. 'á' => 'a',
  2563. 'à' => 'a',
  2564. 'â' => 'a',
  2565. 'é' => 'e',
  2566. 'è' => 'e',
  2567. 'ê' => 'e',
  2568. 'í' => 'i',
  2569. 'ì' => 'i',
  2570. 'î' => 'i',
  2571. 'ó' => 'o',
  2572. 'ò' => 'o',
  2573. 'ô' => 'o',
  2574. 'ú' => 'u',
  2575. 'ù' => 'u',
  2576. 'û' => 'u',
  2577. 'ç' => 'c',
  2578. 'ñ' => 'n',
  2579. // German Characters
  2580. 'ä' => 'ae',
  2581. 'ö' => 'oe',
  2582. 'ü' => 'ue',
  2583. 'ß' => 'ss',
  2584. // Skandinavian Characters
  2585. 'å' => 'a',
  2586. 'ø' => 'o',
  2587. // Polish Characters
  2588. 'ę' => 'e',
  2589. 'ó' => 'o',
  2590. 'ą' => 'a',
  2591. 'ś' => 's',
  2592. 'ł' => 'l',
  2593. 'ż' => 'z',
  2594. 'ź' => 'z',
  2595. 'ć' => 'c',
  2596. 'ń' => 'n',
  2597. // Special Characters
  2598. '€' => ' euro ',
  2599. '@' => ' at ',
  2600. '$' => ' dollar '
  2601. );
  2602. // replace all special characters
  2603. $text = str_replace(array_keys($replace), array_values($replace), $text);
  2604. // replace spaces with simple dashes
  2605. $text = preg_replace('![^a-z0-9._-]!i','-', $text);
  2606. // remove double dashes
  2607. $text = preg_replace('![-]{2,}!','-', $text);
  2608. // trim trailing and leading dashes
  2609. $text = trim($text, '-');
  2610. return $text;
  2611. }
  2612. /**
  2613. * Better alternative for explode()
  2614. * It takes care of removing empty values
  2615. * and it has a built-in way to skip values
  2616. * which are too short.
  2617. *
  2618. * @param string $string The string to split
  2619. * @param string $separator The string to split by
  2620. * @param int $length The min length of values.
  2621. * @return array An array of found values
  2622. */
  2623. static function split($string, $separator=',', $length=1) {
  2624. if(is_array($string)) return $string;
  2625. $string = trim($string, $separator);
  2626. $parts = explode($separator, $string);
  2627. $out = array();
  2628. foreach($parts AS $p) {
  2629. $p = trim($p);
  2630. if(str::length($p) > 0 && str::length($p) >= $length) $out[] = $p;
  2631. }
  2632. return $out;
  2633. }
  2634. /**
  2635. * A more brutal way to trim.
  2636. * It removes double spaces.
  2637. * Can be useful in some cases but
  2638. * be careful as it might remove too much.
  2639. *
  2640. * @param string $string The string to trim
  2641. * @return string The trimmed string
  2642. */
  2643. static function trim($string) {
  2644. $string = preg_replace('/\s\s+/u', ' ', $string);
  2645. return trim($string);
  2646. }
  2647. /**
  2648. * A set of sanitizer methods
  2649. *
  2650. * @param string $string The string to sanitize
  2651. * @param string $type The method
  2652. * @param string $default The default value if the string will be empty afterwards
  2653. * @return string The sanitized string
  2654. */
  2655. static function sanitize($string, $type='str', $default=null) {
  2656. $string = stripslashes((string)$string);
  2657. $string = urldecode($string);
  2658. $string = str::utf8($string);
  2659. switch($type) {
  2660. case 'int':
  2661. $string = (int)$string;
  2662. break;
  2663. case 'str':
  2664. $string = (string)$string;
  2665. break;
  2666. case 'array':
  2667. $string = (array)$string;
  2668. break;
  2669. case 'nohtml':
  2670. $string = self::unhtml($string);
  2671. break;
  2672. case 'noxml':
  2673. $string = self::unxml($string);
  2674. break;
  2675. case 'enum':
  2676. $string = (in_array($string, array('y', 'n'))) ? $string : $default;
  2677. $string = (in_array($string, array('y', 'n'))) ? $string : 'n';
  2678. break;
  2679. case 'checkbox':
  2680. $string = ($string == 'on') ? 'y' : 'n';
  2681. break;
  2682. case 'url':
  2683. $string = (v::url($string)) ? $string : '';
  2684. break;
  2685. case 'email':
  2686. $string = (v::email($string)) ? $string : '';
  2687. break;
  2688. case 'plain':
  2689. $string = str::unxml($string);
  2690. $string = str::unhtml($string);
  2691. $string = str::trim($string);
  2692. break;
  2693. case 'lower':
  2694. $string = str::lower($string);
  2695. break;
  2696. case 'upper':
  2697. $string = str::upper($string);
  2698. break;
  2699. case 'words':
  2700. $string = str::sanitize($string, 'plain');
  2701. $string = preg_replace('/[^\pL]/u', ' ', $string);
  2702. case 'tags':
  2703. $string = str::sanitize($string, 'plain');
  2704. $string = preg_replace('/[^\pL\pN]/u', ' ', $string);
  2705. $string = str::trim($string);
  2706. case 'nobreaks':
  2707. $string = str_replace('\n','',$string);
  2708. $string = str_replace('\r','',$string);
  2709. $string = str_replace('\t','',$string);
  2710. break;
  2711. case 'url':
  2712. $string = self::urlify($string);
  2713. break;
  2714. case 'filename':
  2715. $string = f::save_name($string);
  2716. break;
  2717. }
  2718. return trim($string);
  2719. }
  2720. /**
  2721. * An UTF-8 safe version of ucwords()
  2722. *
  2723. * @param string $string
  2724. * @return string
  2725. */
  2726. static function ucwords($str) {
  2727. return mb_convert_case($str, MB_CASE_TITLE, 'UTF-8');
  2728. }
  2729. /**
  2730. * An UTF-8 safe version of ucfirst()
  2731. *
  2732. * @param string $string
  2733. * @return string
  2734. */
  2735. static function ucfirst($str) {
  2736. return str::upper(str::substr($str, 0, 1)) . str::substr($str, 1);
  2737. }
  2738. /**
  2739. * Converts a string to UTF-8
  2740. *
  2741. * @param string $string
  2742. * @return string
  2743. */
  2744. static function utf8($string) {
  2745. $encoding = mb_detect_encoding($string,'UTF-8, ISO-8859-1, GBK');
  2746. return ($encoding != 'UTF-8') ? iconv($encoding,'utf-8',$string) : $string;
  2747. }
  2748. /**
  2749. * A better way to strip slashes
  2750. *
  2751. * @param string $string
  2752. * @return string
  2753. */
  2754. static function stripslashes($string) {
  2755. if(is_array($string)) return $string;
  2756. return (get_magic_quotes_gpc()) ? stripslashes(stripslashes($string)) : $string;
  2757. }
  2758. }
  2759. /**
  2760. *
  2761. * URL
  2762. *
  2763. * A bunch of handy methods to work with URLs
  2764. *
  2765. * @package Kirby
  2766. */
  2767. class url {
  2768. /**
  2769. * Returns the current URL
  2770. *
  2771. * @return string
  2772. */
  2773. static function current() {
  2774. return 'http://' . server::get('http_host') . server::get('request_uri');
  2775. }
  2776. /**
  2777. * Shortens an URL
  2778. * It removes http:// or https:// and uses str::short afterwards
  2779. *
  2780. * @param string $url The URL to be shortened
  2781. * @param int $chars The final number of characters the URL should have
  2782. * @param boolean $base True: only take the base of the URL.
  2783. * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default.
  2784. * @return string The shortened URL
  2785. */
  2786. static function short($url, $chars=false, $base=false, $rep='…') {
  2787. $url = str_replace('http://','',$url);
  2788. $url = str_replace('https://','',$url);
  2789. $url = str_replace('ftp://','',$url);
  2790. $url = str_replace('www.','',$url);
  2791. if($base) {
  2792. $a = explode('/', $url);
  2793. $url = a::get($a, 0);
  2794. }
  2795. return ($chars) ? str::short($url, $chars, $rep) : $url;
  2796. }
  2797. /**
  2798. * Checks if the URL has a query string attached
  2799. *
  2800. * @param string $url
  2801. * @return boolean
  2802. */
  2803. static function has_query($url) {
  2804. return (str::contains($url, '?')) ? true : false;
  2805. }
  2806. /**
  2807. * Strips the query from the URL
  2808. *
  2809. * @param string $url
  2810. * @return string
  2811. */
  2812. static function strip_query($url) {
  2813. return preg_replace('/\?.*$/is', '', $url);
  2814. }
  2815. /**
  2816. * Strips a hash value from the URL
  2817. *
  2818. * @param string $url
  2819. * @return string
  2820. */
  2821. static function strip_hash($url) {
  2822. return preg_replace('/#.*$/is', '', $url);
  2823. }
  2824. /**
  2825. * Checks for a valid URL
  2826. *
  2827. * @param string $url
  2828. * @return boolean
  2829. */
  2830. static function valid($url) {
  2831. return v::url($url);
  2832. }
  2833. }
  2834. /**
  2835. *
  2836. * Validator
  2837. *
  2838. * Makes input validation easier
  2839. *
  2840. * @package Kirby
  2841. */
  2842. class v {
  2843. /**
  2844. * Core method to create a new validator
  2845. *
  2846. * @param string $string
  2847. * @param array $options
  2848. * @return boolean
  2849. */
  2850. static function string($string, $options) {
  2851. $format = null;
  2852. $min_length = $max_length = 0;
  2853. if(is_array($options)) extract($options);
  2854. if($format && !preg_match('/^[' . $format . ']*$/is', $string)) return false;
  2855. if($min_length && str::length($string) < $min_length) return false;
  2856. if($max_length && str::length($string) > $max_length) return false;
  2857. return true;
  2858. }
  2859. /**
  2860. * Checks for a valid password
  2861. *
  2862. * @param string $password
  2863. * @return boolean
  2864. */
  2865. static function password($password) {
  2866. return self::string($password, array('min_length' => 4));
  2867. }
  2868. /**
  2869. * Checks for two valid, matching password
  2870. *
  2871. * @param string $password1
  2872. * @param string $password2
  2873. * @return boolean
  2874. */
  2875. static function passwords($password1, $password2) {
  2876. if($password1 == $password2
  2877. && self::password($password1)
  2878. && self::password($password2)) {
  2879. return true;
  2880. } else {
  2881. return false;
  2882. }
  2883. }
  2884. /**
  2885. * Checks for valid date
  2886. *
  2887. * @param string $date
  2888. * @return boolean
  2889. */
  2890. static function date($date) {
  2891. $time = strtotime($date);
  2892. if(!$time) return false;
  2893. $year = date('Y', $time);
  2894. $month = date('m', $time);
  2895. $day = date('d', $time);
  2896. return (checkdate($month, $day, $year)) ? $time : false;
  2897. }
  2898. /**
  2899. * Checks for valid email address
  2900. *
  2901. * @param string $email
  2902. * @return boolean
  2903. */
  2904. static function email($email) {
  2905. $regex = '/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i';
  2906. return (preg_match($regex, $email)) ? true : false;
  2907. }
  2908. /**
  2909. * Checks for valid URL
  2910. *
  2911. * @param string $url
  2912. * @return boolean
  2913. */
  2914. static function url($url) {
  2915. $regex = '/^(https?|ftp|rmtp|mms|svn):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i';
  2916. return (preg_match($regex, $url)) ? true : false;
  2917. }
  2918. /**
  2919. * Checks for valid filename
  2920. *
  2921. * @param string $string
  2922. * @return boolean
  2923. */
  2924. static function filename($string) {
  2925. $options = array(
  2926. 'format' => 'a-zA-Z0-9_-',
  2927. 'min_length' => 2,
  2928. );
  2929. return self::string($string, $options);
  2930. }
  2931. }
  2932. /**
  2933. *
  2934. * XML
  2935. *
  2936. * The Kirby XML Parser Class
  2937. *
  2938. * @package Kirby
  2939. */
  2940. class x {
  2941. /**
  2942. * Parses a XML string and returns an array
  2943. *
  2944. * @param string $xml
  2945. * @return mixed
  2946. */
  2947. static function parse($xml) {
  2948. $xml = preg_replace('/(<\/?)(\w+):([^>]*>)/', '$1$2$3', $xml);
  2949. $xml = @simplexml_load_string($xml, null, LIBXML_NOENT);
  2950. $xml = @json_encode($xml);
  2951. $xml = @json_decode($xml, true);
  2952. return (is_array($xml)) ? $xml : false;
  2953. }
  2954. }