PageRenderTime 44ms CodeModel.GetById 0ms RepoModel.GetById 1ms app.codeStats 0ms

/kirby.php

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