├── .gitignore ├── readme.mdown ├── tests ├── a.tests.php ├── c.tests.php ├── db.tests.php ├── f.tests.php ├── g.tests.php ├── l.tests.php ├── r.tests.php ├── s.tests.php ├── v.tests.php ├── x.tests.php ├── dir.tests.php ├── size.tests.php ├── url.tests.php ├── browser.tests.php ├── content.tests.php ├── cookie.tests.php ├── server.tests.php └── str.tests.php ├── composer.json ├── plugins ├── timer.php ├── template.php ├── uri.php ├── crypt.php ├── pager.php ├── cache.php ├── upload.php ├── remote.php └── router.php ├── changelog.mdown └── kirby.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /readme.mdown: -------------------------------------------------------------------------------- 1 | # Kirby Toolkit 2 | 3 | **This is a deprecated version of the Kirby toolkit. 4 | Please upgrade to the current version: https://github.com/getkirby/toolkit** 5 | -------------------------------------------------------------------------------- /tests/a.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/c.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/db.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/f.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/g.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/l.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/r.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/s.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/v.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/x.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dir.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/size.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/url.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/browser.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/content.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cookie.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/server.tests.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bastianallgeier/kirby", 3 | "type": "library", 4 | "description": "A stripped down and easy to use toolkit for PHP", 5 | "keywords": ["kirby"], 6 | "homepage": "http://github.com/bastianallgeier/kirby", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Bastian Allgeier", 11 | "email": "bastian@getkirby.com", 12 | "homepage": "http://bastianallgeier.com", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.2.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /plugins/timer.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugins/template.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugins/uri.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugins/crypt.php: -------------------------------------------------------------------------------- 1 | sizeof(self::$encryption) || $mode <= 0) $mode = 1; 19 | echo(sizeof(self::$encryption)); 20 | $iv_size = mcrypt_get_iv_size(self::$encryption[$mode-1], MCRYPT_MODE_ECB); 21 | $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); 22 | $result = mcrypt_encrypt(self::$encryption[$mode-1], c::get('salt').$key, $text, MCRYPT_MODE_ECB, $iv); 23 | 24 | return trim($result); 25 | } 26 | 27 | function decode($text, $key, $mode=1) { 28 | //uses the mcrypt library to decode a string 29 | if(!$text || !$key) return false; 30 | if($mode > sizeof(self::$encryption)) $mode = 1; 31 | $iv_size = mcrypt_get_iv_size(self::$encryption[$mode-1], MCRYPT_MODE_ECB); 32 | $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); 33 | $result = mcrypt_decrypt(self::$encryption[$mode-1], c::get('salt').$key, $text, MCRYPT_MODE_ECB, $iv); 34 | 35 | return trim($result); 36 | } 37 | } 38 | 39 | ?> -------------------------------------------------------------------------------- /plugins/pager.php: -------------------------------------------------------------------------------- 1 | 0) ? ceil($entries / $limit) : 0; 14 | self::$page = self::sanitize($page, self::$pages); 15 | } 16 | 17 | function get() { 18 | return self::$page; 19 | } 20 | 21 | function next() { 22 | return (self::$page+1 <= self::$pages) ? self::$page+1 : self::$page; 23 | } 24 | 25 | function previous() { 26 | return (self::$page-1 >= 1) ? self::$page-1 : self::$page; 27 | } 28 | 29 | function first() { 30 | return 1; 31 | } 32 | 33 | function last() { 34 | return self::$pages; 35 | } 36 | 37 | function is_first() { 38 | return (self::$page == 1) ? true : false; 39 | } 40 | 41 | function is_last() { 42 | return (self::$page == self::$pages) ? true : false; 43 | } 44 | 45 | function count() { 46 | return self::$pages; 47 | } 48 | 49 | function sanitize($page, $pages) { 50 | $page = intval($page); 51 | if($page > $pages) $page = $pages; 52 | if($page < 1) $page = 1; 53 | return $page; 54 | } 55 | 56 | function db() { 57 | return (self::$page-1)*self::$limit; 58 | } 59 | 60 | } 61 | 62 | ?> -------------------------------------------------------------------------------- /plugins/cache.php: -------------------------------------------------------------------------------- 1 | 0) { 29 | $data = unserialize(fread($fp, filesize($cache_path))); 30 | } else { 31 | $data = null; 32 | } 33 | 34 | flock($fp, LOCK_UN); 35 | fclose($fp); 36 | return $data; 37 | } 38 | 39 | function set($key, $data) { 40 | $dir = c::get('cache.dir'); 41 | if(!is_dir($dir) OR !is_writable($dir)) return false; 42 | 43 | $cache_path = self::name($key); 44 | if(!$fp = fopen($cache_path, 'wb')) return false; 45 | 46 | if(flock($fp, LOCK_EX)) { 47 | fwrite($fp, serialize($data)); 48 | flock($fp, LOCK_UN); 49 | } else { 50 | return false; 51 | } 52 | 53 | fclose($fp); 54 | @chmod($cache_path, 0777); 55 | return true; 56 | } 57 | 58 | function name($key) { 59 | $filename = (c::get('cache.encrypt')) ? sha1($key) : $key ; 60 | return sprintf("%s/%s", c::get('cache.dir'), $filename); 61 | } 62 | 63 | function clear($key) { 64 | $cache_path = self::name($key); 65 | if(file_exists($cache_path)) return unlink($cache_path); 66 | return false; 67 | } 68 | } 69 | 70 | ?> -------------------------------------------------------------------------------- /changelog.mdown: -------------------------------------------------------------------------------- 1 | # Kirby Toolkit Changelog 2 | 3 | ## 0.9.4 4 | 5 | - fixed a bug in v::string 6 | - moved to less tiny version numbers 7 | 8 | ## 0.9.3.0 9 | 10 | - fixed a bug in str::substr 11 | - fixed a bug in str::short 12 | - fixed a bug in str::unxml 13 | - fixed str::contains it's now case insensitive. 14 | 15 | ## 0.9.2.9 16 | 17 | - added MIT license 18 | - full documentation 19 | - removed c::set_all and c::get_all 20 | - removed c::get_array and c::set_array 21 | - added content::type('json') 22 | - repaired dir::size 23 | - changed chmod definitions 24 | - removed r::parse() 25 | - removed s::expired() 26 | 27 | ## 0.9.2.8 28 | 29 | fixed a bug in str::excerpt 30 | 31 | ## 0.9.2.7 32 | 33 | fixed a bug in the split function and replaced the str::trim() function for each token in str::split() with the native trim(). 34 | 35 | ## 0.9.2.6 36 | 37 | a::sort can now handle method calls in objects. 38 | 39 | ## 0.9.2.5 40 | 41 | made class and title in str::email variable. 42 | 43 | ## 0.9.2.4 44 | 45 | fixed a::sort. multi column sort is no longer working but instead the a::sort function works properly on a single column. you can now use it like this: a::sort($array, 'mycolumn', 'desc', SORT_NUMERIC); 46 | 47 | ## 0.9.2.3 48 | 49 | removed str::trim from the r::sanitize 50 | 51 | ## 0.9.2.2 52 | 53 | added a few suggestions from mthie. more will follow soon 54 | 55 | ## 0.9.1 56 | 57 | - removed the docs from the repository 58 | - a new optimized a::shuffle() function, which preserves array keys while shuffling it. 59 | - removed the core class. moved error, status, msg functions to helper functions 60 | - added a::getall($array,$keys) 61 | - updated the browser sniffer 62 | - declared all static functions 63 | - new content::load() function 64 | - put the missing db class back in 65 | - added a new s::id() function 66 | - added a new s::restart() function 67 | - better str::split function 68 | 69 | ## Before 0.9 70 | 71 | Sorry, but I can't really track the changes before 0.9 anymore. I will be more careful about this in the future :) 72 | 73 | -------------------------------------------------------------------------------- /plugins/upload.php: -------------------------------------------------------------------------------- 1 | 'error', 15 | 'msg' => l::get('upload.errors.missing-file', 'The file has not been found'), 16 | ); 17 | 18 | $name = a::get($file, 'name'); 19 | $type = a::get($file, 'type'); 20 | $tmp_name = a::get($file, 'tmp_name'); 21 | $error = a::get($file, 'error'); 22 | $size = a::get($file, 'size'); 23 | $msg = false; 24 | $extension = self::mime_to_extension($type, 'jpg'); 25 | 26 | // convert the filename to a save name 27 | $fname = ($sanitize) ? f::safe_name(f::name($name)) : f::name($name); 28 | 29 | // setup the destination 30 | $destination = str_replace('{name}', $fname, $destination); 31 | $destination = str_replace('{extension}', $extension, $destination); 32 | 33 | if(file_exists($destination) && $overwrite == false) return array( 34 | 'status' => 'error', 35 | 'msg' => l::get('upload.errors.file-exists', 'The file exists and cannot be overwritten'), 36 | ); 37 | 38 | if(empty($tmp_name)) return array( 39 | 'status' => 'error', 40 | 'msg' => l::get('upload.errors.missing-file', 'The file has not been found'), 41 | ); 42 | 43 | if($error != 0) return array( 44 | 'status' => 'error', 45 | 'msg' => l::get('upload.errors.invalid-upload', 'The upload failed'), 46 | ); 47 | 48 | if($size > $maxsize) return array( 49 | 'status' => 'error', 50 | 'msg' => l::get('upload.errors.too-big', 'The file is too big'), 51 | ); 52 | 53 | if(!in_array($type, $allowed)) return array( 54 | 'status' => 'error', 55 | 'msg' => l::get('upload.errors.invalid-file', 'The file type is not allowed') . ': ' . $type, 56 | ); 57 | 58 | // try to change the permissions for the destination 59 | @chmod(dirname($destination), 0777); 60 | 61 | if(!@copy($tmp_name, $destination)) return array( 62 | 'status' => 'error', 63 | 'msg' => l::get('upload.errors.move-error', 'The file could not be moved to the server'), 64 | ); 65 | 66 | // try to change the permissions for the final file 67 | @chmod($destination, 0777); 68 | 69 | return array( 70 | 'status' => 'success', 71 | 'msg' => l::get('upload.success', 'The file has been uploaded'), 72 | 'type' => $type, 73 | 'extension' => $extension, 74 | 'file' => $destination, 75 | 'size' => $size, 76 | 'name' => f::filename($destination), 77 | ); 78 | } 79 | 80 | function max_size() { 81 | $val = ini_get('post_max_size'); 82 | $val = trim($val); 83 | $last = strtolower($val[strlen($val)-1]); 84 | switch($last) { 85 | case 'g': 86 | $val *= 1024; 87 | case 'm': 88 | $val *= 1024; 89 | case 'k': 90 | $val *= 1024; 91 | } 92 | return $val; 93 | } 94 | 95 | function mime_to_extension($mime, $default='') { 96 | $types = array( 97 | 'image/jpeg' => 'jpg', 98 | 'image/pjpeg' => 'jpg', 99 | 'image/png' => 'png', 100 | 'image/x-png' => 'png', 101 | 'image/gif' => 'gif', 102 | 'text/plain' => 'txt', 103 | 'text/html' => 'html', 104 | 'application/xhtml+xml' => 'html', 105 | 'text/javascript' => 'js', 106 | 'text/css' => 'css', 107 | 'text/rtf' => 'rtf', 108 | 'application/msword' => 'doc', 109 | 'application/msexcel' => 'xls', 110 | 'application/vnd.ms-excel' => 'xls', 111 | 'application/mspowerpoint' => 'ppt', 112 | 'application/pdf' => 'pdf', 113 | 'application/zip' => 'zip', 114 | ); 115 | return a::get($types, $mime, $default); 116 | } 117 | 118 | } 119 | 120 | ?> -------------------------------------------------------------------------------- /plugins/remote.php: -------------------------------------------------------------------------------- 1 | 10, 48 | 'headers' => array(), 49 | 'encoding' => 'utf-8', 50 | 'agent' => self::agent() 51 | ); 52 | 53 | $options = array_merge($defaults, $options); 54 | $ch = curl_init(); 55 | 56 | $params = array( 57 | CURLOPT_URL => $url, 58 | CURLOPT_RETURNTRANSFER => true, 59 | CURLOPT_FOLLOWLOCATION => true, 60 | CURLOPT_ENCODING => $options['encoding'], 61 | CURLOPT_USERAGENT => $options['agent'], 62 | CURLOPT_AUTOREFERER => true, 63 | CURLOPT_CONNECTTIMEOUT => $options['timeout'], 64 | CURLOPT_TIMEOUT => $options['timeout'], 65 | CURLOPT_MAXREDIRS => 10, 66 | CURLOPT_SSL_VERIFYPEER => false, 67 | ); 68 | 69 | if(!empty($options['headers'])) $params[CURLOPT_HTTPHEADER] = $options['headers']; 70 | 71 | if(!empty($post)) { 72 | $params[CURLOPT_POST] = true; 73 | $params[CURLOPT_POSTFIELDS] = $post; 74 | } 75 | 76 | curl_setopt_array($ch, $params); 77 | 78 | $content = curl_exec($ch); 79 | $error = curl_errno($ch); 80 | $message = curl_error($ch); 81 | $response = curl_getinfo($ch); 82 | 83 | curl_close($ch); 84 | 85 | $response['error'] = $error; 86 | $response['message'] = $message; 87 | $response['content'] = $content; 88 | 89 | self::$response = $response; 90 | 91 | if(a::get($response, 'error')) return array( 92 | 'status' => 'error', 93 | 'msg' => 'The remote request failed: ' . $response['message'] 94 | ); 95 | 96 | if(a::get($response, 'http_code') >= 400) return array( 97 | 'status' => 'error', 98 | 'msg' => 'The remote request failed - code: ' . $response['http_code'], 99 | 'code' => $response['http_code'] 100 | ); 101 | 102 | return $response; 103 | 104 | } 105 | 106 | function parse($result, $format='plain') { 107 | 108 | switch($format) { 109 | case 'xml': 110 | case 'json': 111 | case 'php': 112 | $result = str::parse($result, $format); 113 | return (is_array($result)) ? $result : false; 114 | break; 115 | default: 116 | return $result; 117 | break; 118 | } 119 | 120 | } 121 | 122 | function agent($id=false) { 123 | 124 | $agents = array( 125 | 'safari' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; de-de) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Safari/530.17', 126 | 'firefox' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.10) Gecko/2009042315 Firefox/3.0.10', 127 | 'ie' => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)', 128 | 'opera' => 'Opera/9.64 (Macintosh; Intel Mac OS X; U; en) Presto/2.1.1', 129 | 'iphone' => 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16', 130 | ); 131 | 132 | if(!$id) { 133 | // return a random agent 134 | shuffle($agents); 135 | return a::get($agents, 0); 136 | } 137 | 138 | return a::get($agents, $id); 139 | 140 | } 141 | 142 | function response() { 143 | return self::$response; 144 | } 145 | 146 | } 147 | 148 | ?> -------------------------------------------------------------------------------- /tests/str.tests.php: -------------------------------------------------------------------------------- 1 | assertEquals('Super Äwesøme String', str::html($this->sample)); 12 | } 13 | 14 | public function test_unhtml() { 15 | $this->assertEquals($this->sample, str::unhtml('Super Äwesøme String')); 16 | } 17 | 18 | public function test_xml() { 19 | $this->assertEquals('Super Äwesøme String', str::xml($this->sample)); 20 | } 21 | 22 | public function test_unxml() { 23 | $this->assertEquals($this->sample, str::unxml('Super Äwesøme String')); 24 | } 25 | 26 | public function test_parse() { 27 | // no test yet 28 | } 29 | 30 | public function test_encode() { 31 | // no test yet 32 | } 33 | 34 | public function test_email() { 35 | // no test yet 36 | } 37 | 38 | public function test_link() { 39 | 40 | // without text 41 | $this->assertEquals('http://getkirby.com', str::link('http://getkirby.com')); 42 | 43 | // with text 44 | $this->assertEquals('Kirby', str::link('http://getkirby.com', 'Kirby')); 45 | 46 | } 47 | 48 | public function test_short() { 49 | 50 | // too long 51 | $this->assertEquals('Supe…', str::short($this->sample, 5)); 52 | 53 | // not too long 54 | $this->assertEquals($this->sample, str::short($this->sample, 100)); 55 | 56 | // zero chars 57 | $this->assertEquals($this->sample, str::short($this->sample, 0)); 58 | 59 | // with different ellipsis character 60 | $this->assertEquals('Su---', str::short($this->sample, 5, '---')); 61 | 62 | } 63 | 64 | public function test_shorturl() { 65 | 66 | $sample = 'http://getkirby.com'; 67 | $long = 'http://getkirby.com/docs/example/1/2/3'; 68 | 69 | // without shortening 70 | $this->assertEquals('getkirby.com', str::shorturl($sample, false)); 71 | 72 | // zero chars 73 | $this->assertEquals('getkirby.com', str::shorturl($sample, 0)); 74 | 75 | // with shortening 76 | $this->assertEquals('getk…', str::shorturl($sample, 5)); 77 | 78 | // with different ellipsis character 79 | $this->assertEquals('ge---', str::shorturl($sample, 5, false, '---')); 80 | 81 | // only keeping the base 82 | $this->assertEquals('getkirby.com', str::shorturl($long, false, true)); 83 | 84 | } 85 | 86 | public function test_excerpt() { 87 | // no test yet 88 | } 89 | 90 | public function test_cutout() { 91 | // no test yet 92 | } 93 | 94 | public function test_apostrophe() { 95 | // no test yet 96 | } 97 | 98 | public function test_plural() { 99 | 100 | // 0 101 | $this->assertEquals('zero', str::plural(0, 'many', 'one', 'zero')); 102 | 103 | // 1 104 | $this->assertEquals('one', str::plural(1, 'many', 'one', 'zero')); 105 | 106 | // 2 107 | $this->assertEquals('many', str::plural(2, 'many', 'one', 'zero')); 108 | 109 | // 0 without zero 110 | $this->assertEquals('many', str::plural(0, 'many', 'one')); 111 | 112 | } 113 | 114 | public function test_substr() { 115 | 116 | $this->assertEquals('Super', str::substr($this->sample, 0, 5)); 117 | 118 | $this->assertEquals(' Äwes', str::substr($this->sample, 5, 5)); 119 | 120 | $this->assertEquals(' Äwesøme String', str::substr($this->sample, 5)); 121 | 122 | $this->assertEquals('tring', str::substr($this->sample, -5)); 123 | 124 | } 125 | 126 | public function test_lower() { 127 | $this->assertEquals('super äwesøme string', str::lower($this->sample)); 128 | } 129 | 130 | public function test_upper() { 131 | $this->assertEquals('SUPER ÄWESØME STRING', str::upper($this->sample)); 132 | } 133 | 134 | public function test_length() { 135 | $this->assertEquals(20, str::length($this->sample)); 136 | } 137 | 138 | public function test_contains() { 139 | 140 | $this->assertTrue(str::contains($this->sample, 'Äwesøme')); 141 | $this->assertTrue(str::contains($this->sample, 'äwesøme')); 142 | 143 | // don't ignore upper/lowercase 144 | $this->assertFalse(str::contains($this->sample, 'äwesøme', false)); 145 | 146 | // check for something which isn't there 147 | $this->assertFalse(str::contains($this->sample, 'Peter')); 148 | 149 | } 150 | 151 | public function test_match() { 152 | // no test yet 153 | } 154 | 155 | public function test_random() { 156 | // no test yet 157 | } 158 | 159 | public function test_urlify() { 160 | // no test yet 161 | } 162 | 163 | public function test_split() { 164 | 165 | $array = array('Super', 'Äwesøme', 'String'); 166 | $this->assertEquals($array, str::split($this->sample, ' ')); 167 | 168 | $array = array('Äwesøme', 'String'); 169 | $this->assertEquals($array, str::split($this->sample, ' ', 6)); 170 | 171 | $array = array('design', 'super', 'fun', 'great', 'nice/super'); 172 | $this->assertEquals($array, str::split('design, super,, fun, great,,, nice/super')); 173 | 174 | } 175 | 176 | public function test_trim() { 177 | // no test yet 178 | } 179 | 180 | public function test_sanitize() { 181 | // no test yet 182 | } 183 | 184 | public function test_ucwords() { 185 | 186 | $string = str::lower($this->sample); 187 | $this->assertEquals($this->sample, str::ucwords($string)); 188 | 189 | } 190 | 191 | public function test_ucfirst() { 192 | 193 | $string = str::lower($this->sample); 194 | 195 | $this->assertEquals('Super äwesøme string', str::ucfirst($string)); 196 | 197 | } 198 | 199 | public function test_utf8() { 200 | 201 | $this->assertEquals($this->sample, str::utf8($this->sample)); 202 | 203 | } 204 | 205 | public function test_stripslashes() { 206 | // no test yet 207 | } 208 | 209 | } 210 | 211 | ?> -------------------------------------------------------------------------------- /plugins/router.php: -------------------------------------------------------------------------------- 1 | 'home', 50 | * '/welcome/@name' => 'welcome', 51 | * '/contact => array(array('GET', 'POST'), 'contact_callback_function'), 52 | * '/page/@page:[a-zA-Z0-9-_]+' => function() { echo 'viewing the "'.router::param('page').'" page'; }, 53 | * '/user/profile' => array(array('GET'), 'controller.view_user'), 54 | * '/users/edit/@id:[0-9]+' => array(array('GET', 'POST'), 'users.edit'), 55 | * '/users/new' => array(array('POST'), 'users.create') 56 | * )); 57 | * 58 | * --------------------------------------------------------------------------------------------- 59 | * 60 | * @param array $methods an array of request methods allowed (GET, POST, PUT and/or DELETE) 61 | * 62 | * @param string $url 63 | * 64 | * 1) literal match: 65 | * /contact 66 | * /about 67 | * 68 | * 2) named parameters, with optional paramters in parentheses: 69 | * /blog/category/@slug ... matches '/blog/category/photography' 70 | * /blog(/@year(/@month(/@day))) .. matches '/blog/2013', 'blog/2013/2' and 'blog/2013/2/14' 71 | * 72 | * 3) named parameters with regular expressions: 73 | * /blog(/@year:[0-9]{4}(/@month:[0-9]{1,2}(/@day:[0-9]{1,2}))) 74 | * /page/@page:[a-zA-Z0-9-_] 75 | * /user/edit/@id:[0-9]+ 76 | * /users/@id:[0-9] 77 | * 78 | * @param mixed $callback 79 | * 80 | * 1) string name of any defined / accessible function 81 | * 2) closure / anonymous function 82 | * 3) string in the format 'class.method' (the "." can be configured via c::set('router.delimiter', ' / ')) 83 | * 84 | * All $callback routes receive an $args associative array of matched route paramters 85 | * These paramters can also be accessed with the the router's "param" method: 86 | * 87 | * router::param($key, $default) 88 | * 89 | * For example: 90 | * router::get(/report.@format:xml|csv|json', function() { echo 'Here is your report in '.router::param('format').''; }); 91 | * 92 | */ 93 | 94 | if(!c::get('router.root')) c::set('router.root', '/'); 95 | if(!c::get('router.delimiter')) c::set('router.delimiter', '.'); 96 | 97 | class router { 98 | 99 | static public $routes = array(); 100 | static public $params = array(); 101 | static public $not_found = null; 102 | 103 | function to($url) { 104 | return rtrim(c::get('router.root'), '/').'/'.ltrim($url, '/'); 105 | } 106 | 107 | function param($key, $default = null) { 108 | return a::get(self::$params, $key, $default); 109 | } 110 | 111 | function set($methods, $url, $callback = null) { 112 | 113 | $has_methods = is_array($methods); 114 | $key = ($has_methods) ? $url : $methods; 115 | $key = '/'.trim($key, '/'); 116 | 117 | self::$routes[$key] = array( 118 | 'methods' => ($has_methods) ? $methods : array('GET', 'POST', 'PUT', 'DELETE'), 119 | 'callback' => ($has_methods) ? $callback : $url, 120 | ); 121 | } 122 | 123 | function get($url, $callback) { 124 | self::set(array('GET'), $url, $callback); 125 | } 126 | 127 | function post($url, $callback) { 128 | self::set(array('POST'), $url, $callback); 129 | } 130 | 131 | function put($url, $callback) { 132 | self::set(array('PUT'), $url, $callback); 133 | } 134 | 135 | function delete($url, $callback) { 136 | self::set(array('DELETE'), $url, $callback); 137 | } 138 | 139 | function not_found($callback) { 140 | self::$not_found = $callback; 141 | } 142 | 143 | function run($routes = null) { 144 | 145 | if(is_array($routes)) { 146 | foreach($routes as $key => $value) { 147 | if(is_array($value)) { 148 | self::set($value[0], $key, $value[1]); 149 | } else { 150 | self::set($key, $value); 151 | } 152 | } 153 | } 154 | if($callback = self::map()) { 155 | self::call($callback); 156 | } elseif(self::$not_found !== false) { 157 | self::call(self::$not_found); 158 | } 159 | } 160 | 161 | function map() { 162 | 163 | $url = url::strip_query( server::get('request_uri') ); 164 | $url = str_replace( rtrim(c::get('router.root'), '/'), '', $url); 165 | $method = r::method(); 166 | 167 | foreach(self::$routes as $key => $route) { 168 | 169 | if( ! in_array($method, $route['methods'])) continue; 170 | 171 | $key = str_replace(')', ')?', $key); 172 | $args = array(); 173 | $regex = preg_replace_callback( 174 | '#@([\w]+)(:([^/\(\)]*))?#', 175 | function($matches) use (&$args) { 176 | $args[$matches[1]] = null; 177 | if(isset($matches[3])) { 178 | return '(?P<'.$matches[1].'>'.$matches[3].')'; 179 | } 180 | return '(?P<'.$matches[1].'>[^/\?]+)'; 181 | }, 182 | $key 183 | ); 184 | 185 | if(preg_match('#^'.$regex.'(?:\?.*)?$#i', $url, $matches)) { 186 | foreach($args as $k => $v) { 187 | self::$params[$k] = (array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null; 188 | } 189 | return $route['callback']; 190 | } 191 | } 192 | 193 | return false; 194 | } 195 | 196 | function call($callback) { 197 | 198 | if(is_callable($callback)) { 199 | call_user_func($callback, self::$params); 200 | } else { 201 | $parts = explode(c::get('router.delimiter'), $callback); 202 | $class = $parts[0]; 203 | $method = (isset($parts[1])) ? $parts[1] : null; 204 | 205 | if(class_exists($class)) { 206 | 207 | $controller = new $class; 208 | 209 | if($method && method_exists($controller, $method)) { 210 | $controller->$method(self::$params); 211 | } else { 212 | throw new BadMethodCallException('Method, '.$method.', not supported.'); 213 | } 214 | 215 | } else { 216 | throw new Exception('Class, '.$class.', not found.'); 217 | } 218 | } 219 | } 220 | } 221 | 222 | ?> -------------------------------------------------------------------------------- /kirby.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://toolkit.getkirby.com 9 | * @copyright Copyright 2009-2012 Bastian Allgeier 10 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 11 | * @package Kirby 12 | */ 13 | 14 | 15 | c::set('version', 0.94); 16 | c::set('language', 'en'); 17 | c::set('charset', 'utf-8'); 18 | c::set('root', dirname(__FILE__)); 19 | 20 | 21 | /** 22 | * Redirects the user to a new URL 23 | * 24 | * @param string $url The URL to redirect to 25 | * @param boolean $code The HTTP status code, which should be sent (301, 302 or 303) 26 | * @package Kirby 27 | */ 28 | function go($url=false, $code=false) { 29 | 30 | if(empty($url)) $url = c::get('url', '/'); 31 | 32 | // send an appropriate header 33 | if($code) { 34 | switch($code) { 35 | case 301: 36 | header('HTTP/1.1 301 Moved Permanently'); 37 | break; 38 | case 302: 39 | header('HTTP/1.1 302 Found'); 40 | break; 41 | case 303: 42 | header('HTTP/1.1 303 See Other'); 43 | break; 44 | } 45 | } 46 | // send to new page 47 | header('Location:' . $url); 48 | exit(); 49 | } 50 | 51 | /** 52 | * Returns the status from a Kirby response 53 | * 54 | * @param array $response The Kirby response array 55 | * @return string "error" or "success" 56 | * @package Kirby 57 | */ 58 | function status($response) { 59 | return a::get($response, 'status'); 60 | } 61 | 62 | /** 63 | * Returns the message from a Kirby response 64 | * 65 | * @param array $response The Kirby response array 66 | * @return string The message 67 | * @package Kirby 68 | */ 69 | function msg($response) { 70 | return a::get($response, 'msg'); 71 | } 72 | 73 | /** 74 | * Checks if a Kirby response is an error response or not. 75 | * 76 | * @param array $response The Kirby response array 77 | * @return boolean Returns true if the response is an error, returns false if no error occurred 78 | * @package Kirby 79 | */ 80 | function error($response) { 81 | return (status($response) == 'error') ? true : false; 82 | } 83 | 84 | /** 85 | * Checks if a Kirby response is a success response. 86 | * 87 | * @param array $response The Kirby response array 88 | * @return boolean Returns true if the response is a success, returns false if an error occurred 89 | * @package Kirby 90 | */ 91 | function success($response) { 92 | return !error($response); 93 | } 94 | 95 | /** 96 | * Loads additional PHP files 97 | * 98 | * You can set the root directory with c::set('root', 'my/root'); 99 | * By default the same directory in which the kirby toolkit file is located will be used. 100 | * 101 | * @param args A list filenames as individual arguments 102 | * @return array Returns a Kirby response array. On error the response array includes the unloadable files (errors). 103 | * @package Kirby 104 | */ 105 | function load() { 106 | 107 | $root = c::get('root'); 108 | $files = func_get_args(); 109 | $errors = array(); 110 | 111 | foreach((array)$files AS $f) { 112 | $file = $root . '/' . $f . '.php'; 113 | if(file_exists($file)) { 114 | include_once($file); 115 | } else { 116 | $errors[] = $file; 117 | } 118 | } 119 | 120 | if(!empty($errors)) return array( 121 | 'status' => 'error', 122 | 'msg' => 'some files could not be loaded', 123 | 'errors' => $errors 124 | ); 125 | 126 | return array( 127 | 'status' => 'success', 128 | 'msg' => 'all files have been loaded' 129 | ); 130 | 131 | } 132 | 133 | 134 | 135 | 136 | /** 137 | * 138 | * Array 139 | * 140 | * This class is supposed to simplify array handling 141 | * and make it more consistent. 142 | * 143 | * @package Kirby 144 | */ 145 | class a { 146 | 147 | /** 148 | * Gets an element of an array by key 149 | * 150 | * @param array $array The source array 151 | * @param mixed $key The key to look for 152 | * @param mixed $default Optional default value, which should be returned if no element has been found 153 | * @return mixed 154 | */ 155 | static function get($array, $key, $default=null) { 156 | return (isset($array[ $key ])) ? $array[ $key ] : $default; 157 | } 158 | 159 | /** 160 | * Gets all elements for an array of key 161 | * 162 | * @param array $array The source array 163 | * @keys array $keys An array of keys to fetch 164 | * @return array An array of keys and matching values 165 | */ 166 | static function getall($array, $keys) { 167 | $result = array(); 168 | foreach($keys as $key) $result[$key] = $array[$key]; 169 | return $result; 170 | } 171 | 172 | /** 173 | * Removes an element from an array 174 | * 175 | * @param array $array The source array 176 | * @param mixed $search The value or key to look for 177 | * @param boolean $key Pass true to search for an key, pass false to search for an value. 178 | * @return array The result array without the removed element 179 | */ 180 | static function remove($array, $search, $key=true) { 181 | if($key) { 182 | unset($array[$search]); 183 | } else { 184 | $found_all = false; 185 | while(!$found_all) { 186 | $index = array_search($search, $array); 187 | if($index !== false) { 188 | unset($array[$index]); 189 | } else { 190 | $found_all = true; 191 | } 192 | } 193 | } 194 | return $array; 195 | } 196 | 197 | /** 198 | * Injects an element into an array 199 | * 200 | * @param array $array The source array 201 | * @param int $position The position, where to inject the element 202 | * @param mixed $element The element, which should be injected 203 | * @return array The result array including the new element 204 | */ 205 | static function inject($array, $position, $element='placeholder') { 206 | $start = array_slice($array, 0, $position); 207 | $end = array_slice($array, $position); 208 | return array_merge($start, (array)$element, $end); 209 | } 210 | 211 | /** 212 | * Shows an entire array or object in a human readable way 213 | * This is perfect for debugging 214 | * 215 | * @param array $array The source array 216 | * @param boolean $echo By default the result will be echoed instantly. You can switch that off here. 217 | * @return mixed If echo is false, this will return the generated array output. 218 | */ 219 | static function show($array, $echo=true) { 220 | $output = '
';
 221 |     $output .= htmlspecialchars(print_r($array, true));
 222 |     $output .= '
'; 223 | if($echo==true) echo $output; 224 | return $output; 225 | } 226 | 227 | /** 228 | * Converts an array to a JSON string 229 | * It's basically a shortcut for json_encode() 230 | * 231 | * @param array $array The source array 232 | * @return string The JSON string 233 | */ 234 | static function json($array) { 235 | return @json_encode( (array)$array ); 236 | } 237 | 238 | /** 239 | * Converts an array to a XML string 240 | * 241 | * @param array $array The source array 242 | * @param string $tag The name of the root element 243 | * @param boolean $head Include the xml declaration head or not 244 | * @param string $charset The charset, which should be used for the header 245 | * @param int $level The indendation level 246 | * @return string The XML string 247 | */ 248 | static function xml($array, $tag='root', $head=true, $charset='utf-8', $tab=' ', $level=0) { 249 | $result = ($level==0 && $head) ? '' . "\n" : ''; 250 | $nlevel = ($level+1); 251 | $result .= str_repeat($tab, $level) . '<' . $tag . '>' . "\n"; 252 | foreach($array AS $key => $value) { 253 | $key = str::lower($key); 254 | if(is_array($value)) { 255 | $mtags = false; 256 | foreach($value AS $key2 => $value2) { 257 | if(is_array($value2)) { 258 | $result .= self::xml($value2, $key, $head, $charset, $tab, $nlevel); 259 | } else if(trim($value2) != '') { 260 | $value2 = (htmlspecialchars($value2) != $value2) ? '' : $value2; 261 | $result .= str_repeat($tab, $nlevel) . '<' . $key . '>' . $value2 . '' . "\n"; 262 | } 263 | $mtags = true; 264 | } 265 | if(!$mtags && count($value) > 0) { 266 | $result .= self::xml($value, $key, $head, $charset, $tab, $nlevel); 267 | } 268 | } else if(trim($value) != '') { 269 | $value = (htmlspecialchars($value) != $value) ? '' : $value; 270 | $result .= str_repeat($tab, $nlevel) . '<' . $key . '>' . $value . '' . "\n"; 271 | } 272 | } 273 | return $result . str_repeat($tab, $level) . '' . "\n"; 274 | } 275 | 276 | /** 277 | * Extracts a single column from an array 278 | * 279 | * @param array $array The source array 280 | * @param string $key The key name of the column to extract 281 | * @return array The result array with all values from that column. 282 | */ 283 | static function extract($array, $key) { 284 | $output = array(); 285 | foreach($array AS $a) if(isset($a[$key])) $output[] = $a[ $key ]; 286 | return $output; 287 | } 288 | 289 | /** 290 | * Shuffles an array and keeps the keys 291 | * 292 | * @param array $array The source array 293 | * @return array The shuffled result array 294 | */ 295 | static function shuffle($array) { 296 | $keys = array_keys($array); 297 | shuffle($keys); 298 | return array_merge(array_flip($keys), $array); 299 | } 300 | 301 | /** 302 | * Returns the first element of an array 303 | * 304 | * I always have to lookup the names of that function 305 | * so I decided to make this shortcut which is 306 | * easier to remember. 307 | * 308 | * @param array $array The source array 309 | * @return mixed The first element 310 | */ 311 | static function first($array) { 312 | return array_shift($array); 313 | } 314 | 315 | /** 316 | * Returns the last element of an array 317 | * 318 | * I always have to lookup the names of that function 319 | * so I decided to make this shortcut which is 320 | * easier to remember. 321 | * 322 | * @param array $array The source array 323 | * @return mixed The last element 324 | */ 325 | static function last($array) { 326 | return array_pop($array); 327 | } 328 | 329 | /** 330 | * Search for elements in an array by regular expression 331 | * 332 | * @param array $array The source array 333 | * @param string $search The regular expression 334 | * @return array The array of results 335 | */ 336 | static function search($array, $search) { 337 | return preg_grep('#' . preg_quote($search) . '#i' , $array); 338 | } 339 | 340 | /** 341 | * Checks if an array contains a certain string 342 | * 343 | * @param array $array The source array 344 | * @param string $search The string to search for 345 | * @return boolean true: the array contains the string, false: it doesn't 346 | */ 347 | static function contains($array, $search) { 348 | $search = self::search($array, $search); 349 | return (empty($search)) ? false : true; 350 | } 351 | 352 | /** 353 | * Fills an array up with additional elements to certain amount. 354 | * 355 | * @param array $array The source array 356 | * @param int $limit The number of elements the array should contain after filling it up. 357 | * @param mixed $fill The element, which should be used to fill the array 358 | * @return array The filled-up result array 359 | */ 360 | static function fill($array, $limit, $fill='placeholder') { 361 | if(count($array) < $limit) { 362 | $diff = $limit-count($array); 363 | for($x=0; $x<$diff; $x++) $array[] = $fill; 364 | } 365 | return $array; 366 | } 367 | 368 | /** 369 | * Checks for missing elements in an array 370 | * 371 | * This is very handy to check for missing 372 | * user values in a request for example. 373 | * 374 | * @param array $array The source array 375 | * @param array $required An array of required keys 376 | * @return array An array of missing fields. If this is empty, nothing is missing. 377 | */ 378 | static function missing($array, $required=array()) { 379 | $missing = array(); 380 | foreach($required AS $r) { 381 | if(empty($array[$r])) $missing[] = $r; 382 | } 383 | return $missing; 384 | } 385 | 386 | /** 387 | * Sorts a multi-dimensional array by a certain column 388 | * 389 | * @param array $array The source array 390 | * @param string $field The name of the column 391 | * @param string $direction desc (descending) or asc (ascending) 392 | * @param const $method A PHP sort method flag. 393 | * @return array The sorted array 394 | */ 395 | static function sort($array, $field, $direction='desc', $method=SORT_REGULAR) { 396 | 397 | $direction = (strtolower($direction) == 'desc') ? SORT_DESC : SORT_ASC; 398 | $helper = array(); 399 | 400 | foreach($array as $key => $row) { 401 | $helper[$key] = (is_object($row)) ? (method_exists($row, $field)) ? str::lower($row->$field()) : str::lower($row->$field) : str::lower($row[$field]); 402 | } 403 | 404 | array_multisort($helper, $direction, $method, $array); 405 | return $array; 406 | 407 | } 408 | 409 | } 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | /** 418 | * 419 | * Browser 420 | * 421 | * Browser sniffing is bad - I know! 422 | * But sometimes this class is very helpful to 423 | * react on certain browsers and build browser-specific 424 | * css selectors for example. It's up to you to use it. 425 | * 426 | * @package Kirby 427 | */ 428 | class browser { 429 | 430 | /** 431 | * The entire user agent string 432 | * 433 | * @var string 434 | */ 435 | static public $ua = false; 436 | 437 | /** 438 | * The readable name of the browser 439 | * For example: "ie" 440 | * 441 | * @var string 442 | */ 443 | static public $name = false; 444 | 445 | /** 446 | * The readable browser engine name 447 | * For example: "webkit" 448 | * 449 | * @var string 450 | */ 451 | static public $engine = false; 452 | 453 | /** 454 | * The browser version number 455 | * For example: "3.6" 456 | * 457 | * @var string 458 | */ 459 | static public $version = false; 460 | 461 | /** 462 | * The platform name 463 | * For example: "mac" 464 | * 465 | * @var string 466 | */ 467 | static public $platform = false; 468 | 469 | /** 470 | * True or false if it is a mobile device or not 471 | * 472 | * @var boolean 473 | */ 474 | static public $mobile = false; 475 | 476 | /** 477 | * True or false if it is an iOS device or not 478 | * 479 | * @var boolean 480 | */ 481 | static public $ios = false; 482 | 483 | /** 484 | * True or false if it is an iPhone or not 485 | * 486 | * @var boolean 487 | */ 488 | static public $iphone = false; 489 | 490 | /** 491 | * Returns the name of the browser 492 | * 493 | * @param string $ua The user agent string 494 | * @return string The browser name 495 | */ 496 | static function name($ua=null) { 497 | self::detect($ua); 498 | return self::$name; 499 | } 500 | 501 | /** 502 | * Returns the browser engine 503 | * 504 | * @param string $ua The user agent string 505 | * @return string The browser engine 506 | */ 507 | static function engine($ua=null) { 508 | self::detect($ua); 509 | return self::$engine; 510 | } 511 | 512 | /** 513 | * Returns the browser version 514 | * 515 | * @param string $ua The user agent string 516 | * @return string The browser version 517 | */ 518 | static function version($ua=null) { 519 | self::detect($ua); 520 | return self::$version; 521 | } 522 | 523 | /** 524 | * Returns the platform 525 | * 526 | * @param string $ua The user agent string 527 | * @return string The platform name 528 | */ 529 | static function platform($ua=null) { 530 | self::detect($ua); 531 | return self::$platform; 532 | } 533 | 534 | /** 535 | * Checks if the user agent string is from a mobile device 536 | * 537 | * @param string $ua The user agent string 538 | * @return boolean True: mobile device, false: not a mobile device 539 | */ 540 | static function mobile($ua=null) { 541 | self::detect($ua); 542 | return self::$mobile; 543 | } 544 | 545 | /** 546 | * Checks if the user agent string is from an iPhone 547 | * 548 | * @param string $ua The user agent string 549 | * @return boolean True: iPhone, false: not an iPhone 550 | */ 551 | static function iphone($ua=null) { 552 | self::detect($ua); 553 | return self::$iphone; 554 | } 555 | 556 | /** 557 | * Checks if the user agent string is from an iOS device 558 | * 559 | * @param string $ua The user agent string 560 | * @return boolean True: iOS device, false: not an iOS device 561 | */ 562 | static function ios($ua=null) { 563 | self::detect($ua); 564 | return self::$ios; 565 | } 566 | 567 | /** 568 | * Returns a browser-specific css selector string 569 | * 570 | * @param string $ua The user agent string 571 | * @param boolean $array True: return an array, false: return a string 572 | * @return mixed 573 | */ 574 | static function css($ua=null, $array=false) { 575 | self::detect($ua); 576 | $css[] = self::$engine; 577 | $css[] = self::$name; 578 | if(self::$version) $css[] = self::$name . str_replace('.', '_', self::$version); 579 | $css[] = self::$platform; 580 | return ($array) ? $css : implode(' ', $css); 581 | } 582 | 583 | /** 584 | * The core detection method, which parses the user agent string 585 | * 586 | * @todo add new browser versions 587 | * @param string $ua The user agent string 588 | * @return array An array with all parsed info 589 | */ 590 | static function detect($ua=null) { 591 | $ua = ($ua) ? str::lower($ua) : str::lower(server::get('http_user_agent')); 592 | 593 | // don't do the detection twice 594 | if(self::$ua == $ua) return array( 595 | 'name' => self::$name, 596 | 'engine' => self::$engine, 597 | 'version' => self::$version, 598 | 'platform' => self::$platform, 599 | 'agent' => self::$ua, 600 | 'mobile' => self::$mobile, 601 | 'iphone' => self::$iphone, 602 | 'ios' => self::$ios, 603 | ); 604 | 605 | self::$ua = $ua; 606 | self::$name = false; 607 | self::$engine = false; 608 | self::$version = false; 609 | self::$platform = false; 610 | 611 | // browser 612 | if(!preg_match('/opera|webtv/i', self::$ua) && preg_match('/msie\s(\d)/', self::$ua, $array)) { 613 | self::$version = $array[1]; 614 | self::$name = 'ie'; 615 | self::$engine = 'trident'; 616 | } else if(strstr(self::$ua, 'firefox/3.6')) { 617 | self::$version = 3.6; 618 | self::$name = 'fx'; 619 | self::$engine = 'gecko'; 620 | } else if (strstr(self::$ua, 'firefox/3.5')) { 621 | self::$version = 3.5; 622 | self::$name = 'fx'; 623 | self::$engine = 'gecko'; 624 | } else if(preg_match('/firefox\/(\d+)/i', self::$ua, $array)) { 625 | self::$version = $array[1]; 626 | self::$name = 'fx'; 627 | self::$engine = 'gecko'; 628 | } else if(preg_match('/opera(\s|\/)(\d+)/', self::$ua, $array)) { 629 | self::$engine = 'presto'; 630 | self::$name = 'opera'; 631 | self::$version = $array[2]; 632 | } else if(strstr(self::$ua, 'konqueror')) { 633 | self::$name = 'konqueror'; 634 | self::$engine = 'webkit'; 635 | } else if(strstr(self::$ua, 'iron')) { 636 | self::$name = 'iron'; 637 | self::$engine = 'webkit'; 638 | } else if(strstr(self::$ua, 'chrome')) { 639 | self::$name = 'chrome'; 640 | self::$engine = 'webkit'; 641 | if(preg_match('/chrome\/(\d+)/i', self::$ua, $array)) { self::$version = $array[1]; } 642 | } else if(strstr(self::$ua, 'applewebkit/')) { 643 | self::$name = 'safari'; 644 | self::$engine = 'webkit'; 645 | if(preg_match('/version\/(\d+)/i', self::$ua, $array)) { self::$version = $array[1]; } 646 | } else if(strstr(self::$ua, 'mozilla/')) { 647 | self::$engine = 'gecko'; 648 | self::$name = 'fx'; 649 | } 650 | 651 | // platform 652 | if(strstr(self::$ua, 'j2me')) { 653 | self::$platform = 'mobile'; 654 | } else if(strstr(self::$ua, 'iphone')) { 655 | self::$platform = 'iphone'; 656 | } else if(strstr(self::$ua, 'ipod')) { 657 | self::$platform = 'ipod'; 658 | } else if(strstr(self::$ua, 'ipad')) { 659 | self::$platform = 'ipad'; 660 | } else if(strstr(self::$ua, 'mac')) { 661 | self::$platform = 'mac'; 662 | } else if(strstr(self::$ua, 'darwin')) { 663 | self::$platform = 'mac'; 664 | } else if(strstr(self::$ua, 'webtv')) { 665 | self::$platform = 'webtv'; 666 | } else if(strstr(self::$ua, 'win')) { 667 | self::$platform = 'win'; 668 | } else if(strstr(self::$ua, 'freebsd')) { 669 | self::$platform = 'freebsd'; 670 | } else if(strstr(self::$ua, 'x11') || strstr(self::$ua, 'linux')) { 671 | self::$platform = 'linux'; 672 | } 673 | 674 | self::$mobile = (self::$platform == 'mobile') ? true : false; 675 | self::$iphone = (in_array(self::$platform, array('ipod', 'iphone'))) ? true : false; 676 | self::$ios = (in_array(self::$platform, array('ipod', 'iphone', 'ipad'))) ? true : false; 677 | 678 | return array( 679 | 'name' => self::$name, 680 | 'engine' => self::$engine, 681 | 'version' => self::$version, 682 | 'platform' => self::$platform, 683 | 'agent' => self::$ua, 684 | 'mobile' => self::$mobile, 685 | 'iphone' => self::$iphone, 686 | 'ios' => self::$ios, 687 | ); 688 | 689 | } 690 | 691 | } 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | /** 700 | * 701 | * Config 702 | * 703 | * This is the core class to handle 704 | * configuration values/constants. 705 | * 706 | * @package Kirby 707 | */ 708 | class c { 709 | 710 | /** 711 | * The static config array 712 | * It contains all config values 713 | * 714 | * @var array 715 | */ 716 | private static $config = array(); 717 | 718 | /** 719 | * Gets a config value by key 720 | * 721 | * @param string $key The key to look for. Pass false to get the entire config array 722 | * @param mixed $default The default value, which will be returned if the key has not been found 723 | * @return mixed The found config value 724 | */ 725 | static function get($key=null, $default=null) { 726 | if(empty($key)) return self::$config; 727 | return a::get(self::$config, $key, $default); 728 | } 729 | 730 | /** 731 | * Sets a config value by key 732 | * 733 | * @param string $key The key to define 734 | * @param mixed $value The value for the passed key 735 | */ 736 | static function set($key, $value=null) { 737 | if(is_array($key)) { 738 | // set all new values 739 | self::$config = array_merge(self::$config, $key); 740 | } else { 741 | self::$config[$key] = $value; 742 | } 743 | } 744 | 745 | /** 746 | * Loads an additional config file 747 | * Returns the entire configuration array 748 | * 749 | * @param string $file The path to the config file 750 | * @return array The entire config array 751 | */ 752 | static function load($file) { 753 | if(file_exists($file)) require_once($file); 754 | return c::get(); 755 | } 756 | 757 | } 758 | 759 | 760 | 761 | 762 | 763 | 764 | /** 765 | * 766 | * Content 767 | * 768 | * This class handles output buffering, 769 | * content loading and setting content type headers. 770 | * 771 | * @package Kirby 772 | */ 773 | class content { 774 | 775 | /** 776 | * Starts the output buffer 777 | * 778 | */ 779 | static function start() { 780 | ob_start(); 781 | } 782 | 783 | /** 784 | * Stops the output buffer 785 | * and flush the content or return it. 786 | * 787 | * @param boolean $return Pass true to return the content instead of flushing it 788 | * @return mixed 789 | */ 790 | static function end($return=false) { 791 | if($return) { 792 | $content = ob_get_contents(); 793 | ob_end_clean(); 794 | return $content; 795 | } 796 | ob_end_flush(); 797 | } 798 | 799 | /** 800 | * Loads content from a passed file 801 | * 802 | * @param string $file The path to the file 803 | * @param boolean $return True: return the content of the file, false: echo the content 804 | * @return mixed 805 | */ 806 | static function load($file, $return=true) { 807 | self::start(); 808 | require_once($file); 809 | $content = self::end(true); 810 | if($return) return $content; 811 | echo $content; 812 | } 813 | 814 | /** 815 | * Simplifies setting content type headers 816 | * 817 | * @param string $ctype The shortcut for the content type. See the keys of the $ctypes array for all available shortcuts 818 | * @param string $charset The charset definition for the content type header. Default is "utf-8" 819 | */ 820 | static function type() { 821 | $args = func_get_args(); 822 | 823 | // shortcuts for content types 824 | $ctypes = array( 825 | 'html' => 'text/html', 826 | 'css' => 'text/css', 827 | 'js' => 'text/javascript', 828 | 'jpg' => 'image/jpeg', 829 | 'png' => 'image/png', 830 | 'gif' => 'image/gif', 831 | 'json' => 'application/json' 832 | ); 833 | 834 | $ctype = a::get($args, 0, c::get('content_type', 'text/html')); 835 | $ctype = a::get($ctypes, $ctype, $ctype); 836 | $charset = a::get($args, 1, c::get('charset', 'utf-8')); 837 | 838 | header('Content-type: ' . $ctype . '; charset=' . $charset); 839 | 840 | } 841 | 842 | } 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | /** 851 | * 852 | * Cookie 853 | * 854 | * This class makes cookie handling easy 855 | * 856 | * @package Kirby 857 | */ 858 | class cookie { 859 | 860 | /** 861 | * Set a new cookie 862 | * 863 | * @param string $key The name of the cookie 864 | * @param string $value The cookie content 865 | * @param int $expires The number of seconds until the cookie expires 866 | * @param string $domain The domain to set this cookie for. 867 | * @return boolean true: the cookie has been created, false: cookie creation failed 868 | */ 869 | static function set($key, $value, $expires=3600, $domain='/') { 870 | if(is_array($value)) $value = a::json($value); 871 | $_COOKIE[$key] = $value; 872 | return @setcookie($key, $value, time()+$expires, $domain); 873 | } 874 | 875 | /** 876 | * Get a cookie value 877 | * 878 | * @param string $key The name of the cookie 879 | * @param string $default The default value, which should be returned if the cookie has not been found 880 | * @return mixed The found value 881 | */ 882 | static function get($key, $default=null) { 883 | return a::get($_COOKIE, $key, $default); 884 | } 885 | 886 | /** 887 | * Remove a cookie 888 | * 889 | * @param string $key The name of the cookie 890 | * @param string $domain The domain of the cookie 891 | * @return mixed true: the cookie has been removed, false: the cookie could not be removed 892 | */ 893 | static function remove($key, $domain='/') { 894 | $_COOKIE[$key] = false; 895 | return @setcookie($key, false, time()-3600, $domain); 896 | } 897 | 898 | } 899 | 900 | 901 | 902 | 903 | 904 | 905 | /** 906 | * 907 | * Database 908 | * 909 | * Database handling sucks - not with this class :) 910 | * 911 | * Configure your database connection like this: 912 | * 913 | * 914 | * c::set('db.host', 'localhost'); 915 | * c::set('db.user', 'root'); 916 | * c::set('db.password', ''); 917 | * c::set('db.name', 'mydb'); 918 | * c::set('db.prefix', ''); 919 | * 920 | * 921 | * @package Kirby 922 | */ 923 | class db { 924 | 925 | /** 926 | * Traces all db queries 927 | * 928 | * @var array 929 | */ 930 | public static $trace = array(); 931 | 932 | /** 933 | * The connection resource 934 | * 935 | * @var mixed 936 | */ 937 | private static $connection = false; 938 | 939 | /** 940 | * The selected database 941 | * 942 | * @var string 943 | */ 944 | private static $database = false; 945 | 946 | /** 947 | * The used charset 948 | * Default is "utf8" 949 | * 950 | * @var string 951 | */ 952 | private static $charset = false; 953 | 954 | /** 955 | * The last used query 956 | * 957 | * @var string 958 | */ 959 | private static $last_query = false; 960 | 961 | /** 962 | * The number of affected rows 963 | * for the last query 964 | * 965 | * @var int 966 | */ 967 | private static $affected = 0; 968 | 969 | /** 970 | * The core connection method 971 | * Tries to connect to the server 972 | * Selects the database and sets the charset 973 | * 974 | * It will only connect once and return 975 | * that same connection for all following queries 976 | * 977 | * @return mixed 978 | */ 979 | static function connect() { 980 | 981 | $connection = self::connection(); 982 | $args = func_get_args(); 983 | $host = a::get($args, 0, c::get('db.host', 'localhost')); 984 | $user = a::get($args, 1, c::get('db.user', 'root')); 985 | $password = a::get($args, 2, c::get('db.password')); 986 | $database = a::get($args, 3, c::get('db.name')); 987 | $charset = a::get($args, 4, c::get('db.charset', 'utf8')); 988 | 989 | // don't connect again if it's already done 990 | $connection = (!$connection) ? @mysql_connect($host, $user, $password) : $connection; 991 | 992 | // react on connection failures 993 | if(!$connection) return self::error(l::get('db.errors.connect', 'Database connection failed'), true); 994 | 995 | self::$connection = $connection; 996 | 997 | // select the database 998 | $database = self::database($database); 999 | if(error($database)) return $database; 1000 | 1001 | // set the right charset 1002 | $charset = self::charset($charset); 1003 | if(error($charset)) return $charset; 1004 | 1005 | return $connection; 1006 | 1007 | } 1008 | 1009 | /** 1010 | * Returns the current connection or false 1011 | * 1012 | * @return mixed 1013 | */ 1014 | static function connection() { 1015 | return (is_resource(self::$connection)) ? self::$connection : false; 1016 | } 1017 | 1018 | /** 1019 | * Disconnects from the server 1020 | * 1021 | * @return boolean 1022 | */ 1023 | static function disconnect() { 1024 | 1025 | if(!c::get('db.disconnect')) return false; 1026 | 1027 | $connection = self::connection(); 1028 | if(!$connection) return false; 1029 | 1030 | // kill the connection 1031 | $disconnect = @mysql_close($connection); 1032 | self::$connection = false; 1033 | 1034 | if(!$disconnect) return self::error(l::get('db.errors.disconnect', 'Disconnecting database failed')); 1035 | return true; 1036 | 1037 | } 1038 | 1039 | /** 1040 | * Selects a database 1041 | * 1042 | * @param string $database 1043 | * @return mixed 1044 | */ 1045 | static function database($database) { 1046 | 1047 | if(!$database) return self::error(l::get('db.errors.missing_db_name', 'Please provide a database name'), true); 1048 | 1049 | // check if there is a selected database 1050 | if(self::$database == $database) return true; 1051 | 1052 | // select a new database 1053 | $select = @mysql_select_db($database, self::connection()); 1054 | 1055 | if(!$select) return self::error(l::get('db.errors.missing_db', 'Selecting database failed'), true); 1056 | 1057 | self::$database = $database; 1058 | 1059 | return $database; 1060 | 1061 | } 1062 | 1063 | /** 1064 | * Sets the charset for all queries 1065 | * The default and recommended charset is utf8 1066 | * 1067 | * @param string $charset 1068 | * @return mixed 1069 | */ 1070 | static function charset($charset='utf8') { 1071 | 1072 | // check if there is a assigned charset and compare it 1073 | if(self::$charset == $charset) return true; 1074 | 1075 | // set the new charset 1076 | $set = @mysql_query('SET NAMES ' . $charset); 1077 | 1078 | if(!$set) return self::error(l::get('db.errors.setting_charset_failed', 'Setting database charset failed')); 1079 | 1080 | // save the new charset to the globals 1081 | self::$charset = $charset; 1082 | return $charset; 1083 | 1084 | } 1085 | 1086 | /** 1087 | * Runs a MySQL query. 1088 | * You can use any valid MySQL query here. 1089 | * This is also the fallback method if you 1090 | * can't use one of the provided shortcut methods 1091 | * from this class. 1092 | * 1093 | * @param string $sql The sql query 1094 | * @param boolean $fetch True: apply db::fetch to the result, false: go without db::fetch 1095 | * @return mixed 1096 | */ 1097 | static function query($sql, $fetch=true) { 1098 | 1099 | $connection = self::connect(); 1100 | if(error($connection)) return $connection; 1101 | 1102 | // save the query 1103 | self::$last_query = $sql; 1104 | 1105 | // execute the query 1106 | $result = @mysql_query($sql, $connection); 1107 | 1108 | self::$affected = @mysql_affected_rows(); 1109 | self::$trace[] = $sql; 1110 | 1111 | if(!$result) return self::error(l::get('db.errors.query_failed', 'The database query failed')); 1112 | if(!$fetch) return $result; 1113 | 1114 | $array = array(); 1115 | while($r = self::fetch($result)) array_push($array, $r); 1116 | return $array; 1117 | 1118 | } 1119 | 1120 | /** 1121 | * Executes a MySQL query without result set. 1122 | * This is used for queries like update, delete or insert 1123 | * 1124 | * @param string $sql The sql query 1125 | * @return mixed 1126 | */ 1127 | static function execute($sql) { 1128 | 1129 | $connection = self::connect(); 1130 | if(error($connection)) return $connection; 1131 | 1132 | // save the query 1133 | self::$last_query = $sql; 1134 | 1135 | // execute the query 1136 | $execute = @mysql_query($sql, $connection); 1137 | 1138 | self::$affected = @mysql_affected_rows(); 1139 | self::$trace[] = $sql; 1140 | 1141 | if(!$execute) return self::error(l::get('db.errors.query_failed', 'The database query failed')); 1142 | 1143 | $last_id = self::last_id(); 1144 | return ($last_id === false) ? self::$affected : self::last_id(); 1145 | } 1146 | 1147 | /** 1148 | * Returns the number of affected rows for the last query 1149 | * 1150 | * @return int 1151 | */ 1152 | static function affected() { 1153 | return self::$affected; 1154 | } 1155 | 1156 | /** 1157 | * Returns the last returned insert id 1158 | * 1159 | * @return int 1160 | */ 1161 | static function last_id() { 1162 | $connection = self::connection(); 1163 | return @mysql_insert_id($connection); 1164 | } 1165 | 1166 | /** 1167 | * Shortcut for mysql_fetch_array 1168 | * 1169 | * @param resource $result the unfetched result from db::query() 1170 | * @param const $type PHP flag for mysql_fetch_array 1171 | * @return array The key/value result array 1172 | */ 1173 | static function fetch($result, $type=MYSQL_ASSOC) { 1174 | if(!$result) return array(); 1175 | return @mysql_fetch_array($result, $type); 1176 | } 1177 | 1178 | /** 1179 | * Returns an array of fields in a given table 1180 | * 1181 | * @param string $table The table name 1182 | * @return array The array of field names 1183 | */ 1184 | static function fields($table) { 1185 | 1186 | $connection = self::connect(); 1187 | if(error($connection)) return $connection; 1188 | 1189 | $fields = @mysql_list_fields(self::$database, self::prefix($table), $connection); 1190 | 1191 | if(!$fields) return self::error(l::get('db.errors.listing_fields_failed', 'Listing fields failed')); 1192 | 1193 | $output = array(); 1194 | $count = @mysql_num_fields($fields); 1195 | 1196 | for($x=0; $x<$count; $x++) { 1197 | $output[] = @mysql_field_name($fields, $x); 1198 | } 1199 | 1200 | return $output; 1201 | 1202 | } 1203 | 1204 | /** 1205 | * Runs a INSERT query 1206 | * 1207 | * @param string $table The table name 1208 | * @param mixed $input Either a key/value array or a valid MySQL insert string 1209 | * @param boolean $ignore Set this to true to ignore duplicates 1210 | * @return mixed The last inserted id if everything went fine or an error response. 1211 | */ 1212 | static function insert($table, $input, $ignore=false) { 1213 | $ignore = ($ignore) ? ' IGNORE' : ''; 1214 | return self::execute('INSERT' . ($ignore) . ' INTO ' . self::prefix($table) . ' SET ' . self::values($input)); 1215 | } 1216 | 1217 | /** 1218 | * Runs a INSERT query with values 1219 | * 1220 | * @param string $table The table name 1221 | * @param array $fields an array of field names 1222 | * @param array $values an array of array of keys and values. 1223 | * @return mixed The last inserted id if everything went fine or an error response. 1224 | */ 1225 | static function insert_all($table, $fields, $values) { 1226 | 1227 | $query = 'INSERT INTO ' . self::prefix($table) . ' (' . implode(',', $fields) . ') VALUES '; 1228 | $rows = array(); 1229 | 1230 | foreach($values AS $v) { 1231 | $str = '(\''; 1232 | $sep = ''; 1233 | 1234 | foreach($v AS $input) { 1235 | $str .= $sep . db::escape($input); 1236 | $sep = "','"; 1237 | } 1238 | 1239 | $str .= '\')'; 1240 | $rows[] = $str; 1241 | } 1242 | 1243 | $query .= implode(',', $rows); 1244 | return db::execute($query); 1245 | 1246 | } 1247 | 1248 | /** 1249 | * Runs a REPLACE query 1250 | * 1251 | * @param string $table The table name 1252 | * @param mixed $input Either a key/value array or a valid MySQL insert string 1253 | * @return mixed The last inserted id if everything went fine or an error response. 1254 | */ 1255 | static function replace($table, $input) { 1256 | return self::execute('REPLACE INTO ' . self::prefix($table) . ' SET ' . self::values($input)); 1257 | } 1258 | 1259 | /** 1260 | * Runs an UPDATE query 1261 | * 1262 | * @param string $table The table name 1263 | * @param mixed $input Either a key/value array or a valid MySQL insert string 1264 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1265 | * @return mixed The number of affected rows or an error response 1266 | */ 1267 | static function update($table, $input, $where) { 1268 | return self::execute('UPDATE ' . self::prefix($table) . ' SET ' . self::values($input) . ' WHERE ' . self::where($where)); 1269 | } 1270 | 1271 | /** 1272 | * Runs a DELETE query 1273 | * 1274 | * @param string $table The table name 1275 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1276 | * @return mixed The number of affected rows or an error response 1277 | */ 1278 | static function delete($table, $where='') { 1279 | $sql = 'DELETE FROM ' . self::prefix($table); 1280 | if(!empty($where)) $sql .= ' WHERE ' . self::where($where); 1281 | return self::execute($sql); 1282 | } 1283 | 1284 | /** 1285 | * Returns multiple rows from a table 1286 | * 1287 | * @param string $table The table name 1288 | * @param mixed $select Either an array of fields or a MySQL string of fields 1289 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1290 | * @param string $order Order clause without the order keyword. ie: "added desc" 1291 | * @param int $page a page number 1292 | * @param int $limit a number for rows to return 1293 | * @param boolean $fetch true: apply db::fetch(), false: don't apply db::fetch() 1294 | * @return mixed 1295 | */ 1296 | static function select($table, $select='*', $where=null, $order=null, $page=null, $limit=null, $fetch=true) { 1297 | 1298 | if($limit === 0) return array(); 1299 | 1300 | if(is_array($select)) $select = self::select_clause($select); 1301 | 1302 | $sql = 'SELECT ' . $select . ' FROM ' . self::prefix($table); 1303 | 1304 | if(!empty($where)) $sql .= ' WHERE ' . self::where($where); 1305 | if(!empty($order)) $sql .= ' ORDER BY ' . $order; 1306 | if($page !== null && $limit !== null) $sql .= ' LIMIT ' . $page . ',' . $limit; 1307 | 1308 | return self::query($sql, $fetch); 1309 | 1310 | } 1311 | 1312 | /** 1313 | * Returns a single row from a table 1314 | * 1315 | * @param string $table The table name 1316 | * @param mixed $select Either an array of fields or a MySQL string of fields 1317 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1318 | * @param string $order Order clause without the order keyword. ie: "added desc" 1319 | * @return mixed 1320 | */ 1321 | static function row($table, $select='*', $where=null, $order=null) { 1322 | $result = self::select($table, $select, $where, $order, 0,1, false); 1323 | return self::fetch($result); 1324 | } 1325 | 1326 | /** 1327 | * Returns all values from single column of a table 1328 | * 1329 | * @param string $table The table name 1330 | * @param string $column The name of the column 1331 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1332 | * @param string $order Order clause without the order keyword. ie: "added desc" 1333 | * @param int $page a page number 1334 | * @param int $limit a number for rows to return 1335 | * @return mixed 1336 | */ 1337 | static function column($table, $column, $where=null, $order=null, $page=null, $limit=null) { 1338 | 1339 | $result = self::select($table, $column, $where, $order, $page, $limit, false); 1340 | 1341 | $array = array(); 1342 | while($r = self::fetch($result)) array_push($array, a::get($r, $column)); 1343 | return $array; 1344 | } 1345 | 1346 | /** 1347 | * Returns a single field value from a table 1348 | * 1349 | * @param string $table The table name 1350 | * @param string $field The name of the field 1351 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1352 | * @param string $order Order clause without the order keyword. ie: "added desc" 1353 | * @return mixed 1354 | */ 1355 | static function field($table, $field, $where=null, $order=null) { 1356 | $result = self::row($table, $field, $where, $order); 1357 | return a::get($result, $field); 1358 | } 1359 | 1360 | /** 1361 | * Joins two tables and returns data from them 1362 | * 1363 | * @param string $table_1 The table name of the first table 1364 | * @param string $table_2 The table name of the second table 1365 | * @param string $on The MySQL ON clause without the ON keyword. ie: "user_id = comment_user" 1366 | * @param mixed $select Either an array of fields or a MySQL string of fields 1367 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1368 | * @param string $order Order clause without the order keyword. ie: "added desc" 1369 | * @param int $page a page number 1370 | * @param int $limit a number for rows to return 1371 | * @param string $type The join type (JOIN, LEFT, RIGHT, INNER) 1372 | * @return mixed 1373 | */ 1374 | static function join($table_1, $table_2, $on, $select, $where=null, $order=null, $page=null, $limit=null, $type="JOIN") { 1375 | return self::select( 1376 | self::prefix($table_1) . ' ' . $type . ' ' . 1377 | self::prefix($table_2) . ' ON ' . 1378 | self::where($on), 1379 | $select, 1380 | self::where($where), 1381 | $order, 1382 | $page, 1383 | $limit 1384 | ); 1385 | } 1386 | 1387 | /** 1388 | * Runs a LEFT JOIN 1389 | * 1390 | * @param string $table_1 The table name of the first table 1391 | * @param string $table_2 The table name of the second table 1392 | * @param string $on The MySQL ON clause without the ON keyword. ie: "user_id = comment_user" 1393 | * @param mixed $select Either an array of fields or a MySQL string of fields 1394 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1395 | * @param string $order Order clause without the order keyword. ie: "added desc" 1396 | * @param int $page a page number 1397 | * @param int $limit a number for rows to return 1398 | * @return mixed 1399 | */ 1400 | static function left_join($table_1, $table_2, $on, $select, $where=null, $order=null, $page=null, $limit=null) { 1401 | return self::join($table_1, $table_2, $on, $select, $where, $order, $page, $limit, 'LEFT JOIN'); 1402 | } 1403 | 1404 | /** 1405 | * Counts a number of rows in a table 1406 | * 1407 | * @param string $table The table name 1408 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1409 | * @return int 1410 | */ 1411 | static function count($table, $where='') { 1412 | $result = self::row($table, 'count(*)', $where); 1413 | return ($result) ? a::get($result, 'count(*)') : 0; 1414 | } 1415 | 1416 | /** 1417 | * Gets the minimum value in a column of a table 1418 | * 1419 | * @param string $table The table name 1420 | * @param string $column The name of the column 1421 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1422 | * @return mixed 1423 | */ 1424 | static function min($table, $column, $where=null) { 1425 | 1426 | $sql = 'SELECT MIN(' . $column . ') AS min FROM ' . self::prefix($table); 1427 | if(!empty($where)) $sql .= ' WHERE ' . self::where($where); 1428 | 1429 | $result = self::query($sql, false); 1430 | $result = self::fetch($result); 1431 | 1432 | return a::get($result, 'min', 1); 1433 | 1434 | } 1435 | 1436 | /** 1437 | * Gets the maximum value in a column of a table 1438 | * 1439 | * @param string $table The table name 1440 | * @param string $column The name of the column 1441 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1442 | * @return mixed 1443 | */ 1444 | static function max($table, $column, $where=null) { 1445 | 1446 | $sql = 'SELECT MAX(' . $column . ') AS max FROM ' . self::prefix($table); 1447 | if(!empty($where)) $sql .= ' WHERE ' . self::where($where); 1448 | 1449 | $result = self::query($sql, false); 1450 | $result = self::fetch($result); 1451 | 1452 | return a::get($result, 'max', 1); 1453 | 1454 | } 1455 | 1456 | /** 1457 | * Gets the sum of values in a column of a table 1458 | * 1459 | * @param string $table The table name 1460 | * @param string $column The name of the column 1461 | * @param mixed $where Either a key/value array as AND connected where clause or a simple MySQL where clause string 1462 | * @return mixed 1463 | */ 1464 | static function sum($table, $column, $where=null) { 1465 | 1466 | $sql = 'SELECT SUM(' . $column . ') AS sum FROM ' . self::prefix($table); 1467 | if(!empty($where)) $sql .= ' WHERE ' . self::where($where); 1468 | 1469 | $result = self::query($sql, false); 1470 | $result = self::fetch($result); 1471 | 1472 | return a::get($result, 'sum', 0); 1473 | 1474 | } 1475 | 1476 | /** 1477 | * Adds a prefix to a table name if set in c::set('db.prefix', 'myprefix_'); 1478 | * This makes it possible to use table names in all methods without prefix 1479 | * and it will still be applied automatically. 1480 | * 1481 | * @param string $table The name of the table with or without prefix 1482 | * @return string The sanitized table name. 1483 | */ 1484 | static function prefix($table) { 1485 | $prefix = c::get('db.prefix'); 1486 | if(!$prefix) return $table; 1487 | return (!str::contains($table,$prefix)) ? $prefix . $table : $table; 1488 | } 1489 | 1490 | /** 1491 | * Strips table specific column prefixes from the result array 1492 | * 1493 | * If you use column names like user_username, user_id, etc. 1494 | * use this method on the result array to strip user_ of all fields 1495 | * 1496 | * @param array $array The result array 1497 | * @return array The result array without those damn prefixes. 1498 | */ 1499 | static function simple_fields($array) { 1500 | if(empty($array)) return false; 1501 | $output = array(); 1502 | foreach($array AS $key => $value) { 1503 | $key = substr($key, strpos($key, '_')+1); 1504 | $output[$key] = $value; 1505 | } 1506 | return $output; 1507 | } 1508 | 1509 | /** 1510 | * Makes it possible to use arrays for inputs instead of MySQL strings 1511 | * 1512 | * @param array $input 1513 | * @return string The final MySQL string, which will be used in the queries. 1514 | */ 1515 | static function values($input) { 1516 | if(!is_array($input)) return $input; 1517 | 1518 | $output = array(); 1519 | foreach($input AS $key => $value) { 1520 | if($value === 'NOW()') 1521 | $output[] = $key . ' = NOW()'; 1522 | elseif(is_array($value)) 1523 | $output[] = $key . ' = \'' . a::json($value) . '\''; 1524 | else 1525 | $output[] = $key . ' = \'' . self::escape($value) . '\''; 1526 | } 1527 | return implode(', ', $output); 1528 | 1529 | } 1530 | 1531 | /** 1532 | * Escapes unwanted stuff in values like slashes, etc. 1533 | * 1534 | * @param string $value 1535 | * @return string Returns the escaped string 1536 | */ 1537 | static function escape($value) { 1538 | $value = str::stripslashes($value); 1539 | return mysql_real_escape_string((string)$value, self::connect()); 1540 | } 1541 | 1542 | /** 1543 | * A simplifier to build search clauses 1544 | * 1545 | * @param string $search The search word 1546 | * @param array $fields An array of fields to search 1547 | * @param string $mode OR or AND 1548 | * @return string Returns the final where clause 1549 | */ 1550 | static function search_clause($search, $fields, $mode='OR') { 1551 | 1552 | if(empty($search)) return false; 1553 | 1554 | $arr = array(); 1555 | foreach($fields AS $f) { 1556 | array_push($arr, $f . ' LIKE \'%' . $search . '%\''); 1557 | //array_push($arr, $f . ' REGEXP "[[:<:]]' . db::escape($search) . '[[:>:]]"'); 1558 | } 1559 | return '(' . implode(' ' . trim($mode) . ' ', $arr) . ')'; 1560 | 1561 | } 1562 | 1563 | /** 1564 | * An easy method to build a part of the where clause to find stuff by its first character 1565 | * 1566 | * @param string $field The name of the field 1567 | * @param string $char The character to search for 1568 | * @return string Returns the where clause part 1569 | */ 1570 | static function with($field, $char) { 1571 | return 'LOWER(SUBSTRING(' . $field . ',1,1)) = "' . db::escape($char) . '"'; 1572 | } 1573 | 1574 | /** 1575 | * Builds a select clause from a simple array 1576 | * 1577 | * @param array $field An array of field names 1578 | * @return string The MySQL string 1579 | */ 1580 | static function select_clause($fields) { 1581 | return implode(', ', $fields); 1582 | } 1583 | 1584 | /** 1585 | * A simplifier to build IN clauses 1586 | * 1587 | * @param array $array An array of fieldnames 1588 | * @return string The MySQL string for the where clause 1589 | */ 1590 | static function in($array) { 1591 | return '\'' . implode('\',\'', $array) . '\''; 1592 | } 1593 | 1594 | /** 1595 | * A handler to convert key/value arrays to an where clause 1596 | * 1597 | * @param array $array keys/values for the where clause 1598 | * @param string $method AND or OR 1599 | * @return string The MySQL string for the where clause 1600 | */ 1601 | static function where($array, $method='AND') { 1602 | 1603 | if(!is_array($array)) return $array; 1604 | 1605 | $output = array(); 1606 | foreach($array AS $field => $value) { 1607 | $output[] = $field . ' = \'' . self::escape($value) . '\''; 1608 | $separator = ' ' . $method . ' '; 1609 | } 1610 | return implode(' ' . $method . ' ', $output); 1611 | 1612 | } 1613 | 1614 | /** 1615 | * An internal error handler 1616 | * 1617 | * @param string $msg The error/success message to return 1618 | * @param boolean $exit die after this error? 1619 | * @return mixed 1620 | */ 1621 | static function error($msg=null, $exit=false) { 1622 | 1623 | $connection = self::connection(); 1624 | 1625 | $error = (mysql_error()) ? @mysql_error($connection) : false; 1626 | $number = (mysql_errno()) ? @mysql_errno($connection) : 0; 1627 | 1628 | if(c::get('db.debugging')) { 1629 | if($error) $msg .= ' -> ' . $error . ' (' . $number . ')'; 1630 | if(self::$last_query) $msg .= ' Query: ' . self::$last_query; 1631 | } else $msg .= ' - ' . l::get('db.errors.msg', 'This will be fixed soon!'); 1632 | 1633 | if($exit || c::get('db.debugging')) die($msg); 1634 | 1635 | return array( 1636 | 'status' => 'error', 1637 | 'msg' => $msg 1638 | ); 1639 | 1640 | } 1641 | 1642 | } 1643 | 1644 | 1645 | 1646 | 1647 | 1648 | /** 1649 | * 1650 | * Directory 1651 | * 1652 | * This class makes it easy to create/edit/delete 1653 | * directories on the filesystem 1654 | * 1655 | * @package Kirby 1656 | */ 1657 | class dir { 1658 | 1659 | /** 1660 | * Creates a new directory 1661 | * 1662 | * @param string $dir The path for the new directory 1663 | * @return boolean True: the dir has been created, false: creating failed 1664 | */ 1665 | static function make($dir) { 1666 | if(is_dir($dir)) return true; 1667 | if(!@mkdir($dir, 0755)) return false; 1668 | @chmod($dir, 0755); 1669 | return true; 1670 | } 1671 | 1672 | /** 1673 | * Reads all files from a directory and returns them as an array. 1674 | * It skips unwanted invisible stuff. 1675 | * 1676 | * @param string $dir The path of directory 1677 | * @return mixed An array of filenames or false 1678 | */ 1679 | static function read($dir) { 1680 | if(!is_dir($dir)) return false; 1681 | $skip = array('.', '..', '.DS_Store'); 1682 | return array_diff(scandir($dir),$skip); 1683 | } 1684 | 1685 | /** 1686 | * Reads a directory and returns a full set of info about it 1687 | * 1688 | * @param string $dir The path of directory 1689 | * @return mixed An info array or false 1690 | */ 1691 | static function inspect($dir) { 1692 | 1693 | if(!is_dir($dir)) return array(); 1694 | 1695 | $files = dir::read($dir); 1696 | $modified = filemtime($dir); 1697 | 1698 | $data = array( 1699 | 'name' => basename($dir), 1700 | 'root' => $dir, 1701 | 'modified' => $modified, 1702 | 'files' => array(), 1703 | 'children' => array() 1704 | ); 1705 | 1706 | foreach($files AS $file) { 1707 | if(is_dir($dir . '/' . $file)) { 1708 | $data['children'][] = $file; 1709 | } else { 1710 | $data['files'][] = $file; 1711 | } 1712 | } 1713 | 1714 | return $data; 1715 | 1716 | } 1717 | 1718 | /** 1719 | * Moves a directory to a new location 1720 | * 1721 | * @param string $old The current path of the directory 1722 | * @param string $new The desired path where the dir should be moved to 1723 | * @return boolean True: the directory has been moved, false: moving failed 1724 | */ 1725 | static function move($old, $new) { 1726 | if(!is_dir($old)) return false; 1727 | return (@rename($old, $new) && is_dir($new)) ? true : false; 1728 | } 1729 | 1730 | /** 1731 | * Deletes a directory 1732 | * 1733 | * @param string $dir The path of the directory 1734 | * @param boolean $keep If set to true, the directory will flushed but not removed. 1735 | * @return boolean True: the directory has been removed, false: removing failed 1736 | */ 1737 | static function remove($dir, $keep=false) { 1738 | if(!is_dir($dir)) return false; 1739 | 1740 | $handle = @opendir($dir); 1741 | $skip = array('.', '..'); 1742 | 1743 | if(!$handle) return false; 1744 | 1745 | while($item = @readdir($handle)) { 1746 | if(is_dir($dir . '/' . $item) && !in_array($item, $skip)) { 1747 | self::remove($dir . '/' . $item); 1748 | } else if(!in_array($item, $skip)) { 1749 | @unlink($dir . '/' . $item); 1750 | } 1751 | } 1752 | 1753 | @closedir($handle); 1754 | if(!$keep) return @rmdir($dir); 1755 | return true; 1756 | 1757 | } 1758 | 1759 | /** 1760 | * Flushes a directory 1761 | * 1762 | * @param string $dir The path of the directory 1763 | * @return boolean True: the directory has been flushed, false: flushing failed 1764 | */ 1765 | static function clean($dir) { 1766 | return self::remove($dir, true); 1767 | } 1768 | 1769 | /** 1770 | * Gets the size of the directory and all subfolders and files 1771 | * 1772 | * @param string $dir The path of the directory 1773 | * @param boolean $recursive 1774 | * @param boolean $nice returns the size in a human readable size 1775 | * @return mixed 1776 | */ 1777 | static function size($path, $recursive=true, $nice=false) { 1778 | if(!file_exists($path)) return false; 1779 | if(is_file($path)) return self::size($path, $nice); 1780 | $size = 0; 1781 | foreach(glob($path."/*") AS $file) { 1782 | if($file != "." && $file != "..") { 1783 | if($recursive) { 1784 | $size += self::size($file, true); 1785 | } else { 1786 | $size += f::size($path); 1787 | } 1788 | } 1789 | } 1790 | return ($nice) ? f::nice_size($size) : $size; 1791 | } 1792 | 1793 | /** 1794 | * Recursively check when the dir and all 1795 | * subfolders have been modified for the last time. 1796 | * 1797 | * @param string $dir The path of the directory 1798 | * @param int $modified internal modified store 1799 | * @return int 1800 | */ 1801 | static function modified($dir, $modified=0) { 1802 | $files = self::read($dir); 1803 | foreach($files AS $file) { 1804 | if(!is_dir($dir . '/' . $file)) continue; 1805 | $filectime = filemtime($dir . '/' . $file); 1806 | $modified = ($filectime > $modified) ? $filectime : $modified; 1807 | $modified = self::modified($dir . '/' . $file, $modified); 1808 | } 1809 | return $modified; 1810 | } 1811 | 1812 | } 1813 | 1814 | 1815 | 1816 | 1817 | 1818 | 1819 | /** 1820 | * 1821 | * File 1822 | * 1823 | * This class makes it easy to 1824 | * create/edit/delete files 1825 | * 1826 | * @package Kirby 1827 | */ 1828 | class f { 1829 | 1830 | /** 1831 | * Creates a new file 1832 | * 1833 | * @param string $file The path for the new file 1834 | * @param mixed $content Either a string or an array. Arrays will be converted to JSON. 1835 | * @param boolean $append true: append the content to an exisiting file if available. false: overwrite. 1836 | * @return boolean 1837 | */ 1838 | static function write($file,$content,$append=false){ 1839 | if(is_array($content)) $content = a::json($content); 1840 | $mode = ($append) ? FILE_APPEND : false; 1841 | $write = @file_put_contents($file, $content, $mode); 1842 | @chmod($file, 0666); 1843 | return $write; 1844 | } 1845 | 1846 | /** 1847 | * Appends new content to an existing file 1848 | * 1849 | * @param string $file The path for the file 1850 | * @param mixed $content Either a string or an array. Arrays will be converted to JSON. 1851 | * @return boolean 1852 | */ 1853 | static function append($file,$content){ 1854 | return self::write($file,$content,true); 1855 | } 1856 | 1857 | /** 1858 | * Reads the content of a file 1859 | * 1860 | * @param string $file The path for the file 1861 | * @param mixed $parse if set to true, parse the result with the passed method. See: "str::parse()" for more info about available methods. 1862 | * @return mixed 1863 | */ 1864 | static function read($file, $parse=false) { 1865 | $content = @file_get_contents($file); 1866 | return ($parse) ? str::parse($content, $parse) : $content; 1867 | } 1868 | 1869 | /** 1870 | * Moves a file to a new location 1871 | * 1872 | * @param string $old The current path for the file 1873 | * @param string $new The path to the new location 1874 | * @return boolean 1875 | */ 1876 | static function move($old, $new) { 1877 | if(!file_exists($old)) return false; 1878 | return (@rename($old, $new) && file_exists($new)) ? true : false; 1879 | } 1880 | 1881 | /** 1882 | * Deletes a file 1883 | * 1884 | * @param string $file The path for the file 1885 | * @return boolean 1886 | */ 1887 | static function remove($file) { 1888 | return (file_exists($file) && is_file($file) && !empty($file)) ? @unlink($file) : false; 1889 | } 1890 | 1891 | /** 1892 | * Gets the extension of a file 1893 | * 1894 | * @param string $file The filename or path 1895 | * @return string 1896 | */ 1897 | static function extension($filename) { 1898 | $ext = str_replace('.', '', strtolower(strrchr(trim($filename), '.'))); 1899 | return url::strip_query($ext); 1900 | } 1901 | 1902 | /** 1903 | * Extracts the filename from a file path 1904 | * 1905 | * @param string $file The path 1906 | * @return string 1907 | */ 1908 | static function filename($name) { 1909 | return basename($name); 1910 | } 1911 | 1912 | /** 1913 | * Extracts the name from a file path or filename without extension 1914 | * 1915 | * @param string $file The path or filename 1916 | * @param boolean $remove_path remove the path from the name 1917 | * @return string 1918 | */ 1919 | static function name($name, $remove_path = false) { 1920 | if($remove_path == true) $name = self::filename($name); 1921 | $dot=strrpos($name,'.'); 1922 | if($dot) $name=substr($name,0,$dot); 1923 | return $name; 1924 | } 1925 | 1926 | /** 1927 | * Just an alternative for dirname() to stay consistent 1928 | * 1929 | * @param string $file The path 1930 | * @return string 1931 | */ 1932 | static function dirname($file=__FILE__) { 1933 | return dirname($file); 1934 | } 1935 | 1936 | /** 1937 | * Returns the size of a file. 1938 | * 1939 | * @param string $file The path 1940 | * @param boolean $nice True: return the size in a human readable format 1941 | * @return mixed 1942 | */ 1943 | static function size($file, $nice=false) { 1944 | @clearstatcache(); 1945 | $size = @filesize($file); 1946 | if(!$size) return false; 1947 | return ($nice) ? self::nice_size($size) : $size; 1948 | } 1949 | 1950 | /** 1951 | * Converts an integer size into a human readable format 1952 | * 1953 | * @param int $size The file size 1954 | * @return string 1955 | */ 1956 | static function nice_size($size) { 1957 | $size = str::sanitize($size, 'int'); 1958 | if($size < 1) return '0 kb'; 1959 | 1960 | $unit=array('b','kb','mb','gb','tb','pb'); 1961 | return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i]; 1962 | } 1963 | 1964 | /** 1965 | * Convert the filename to a new extension 1966 | * 1967 | * @param string $name The file name 1968 | * @param string $type The new extension 1969 | * @return string 1970 | */ 1971 | static function convert($name, $type='jpg') { 1972 | return self::name($name) . $type; 1973 | } 1974 | 1975 | /** 1976 | * Sanitize a filename to strip unwanted special characters 1977 | * 1978 | * @param string $string The file name 1979 | * @return string 1980 | */ 1981 | static function safe_name($string) { 1982 | return str::urlify($string); 1983 | } 1984 | 1985 | } 1986 | 1987 | 1988 | 1989 | 1990 | 1991 | 1992 | /** 1993 | * 1994 | * Globals 1995 | * 1996 | * The Kirby Globals Class 1997 | * Easy setting/getting of globals 1998 | * 1999 | * @package Kirby 2000 | */ 2001 | class g { 2002 | 2003 | /** 2004 | * Gets an global value by key 2005 | * 2006 | * @param mixed $key The key to look for. Pass false or null to return the entire globals array. 2007 | * @param mixed $default Optional default value, which should be returned if no element has been found 2008 | * @return mixed 2009 | */ 2010 | static function get($key=null, $default=null) { 2011 | if(empty($key)) return $GLOBALS; 2012 | return a::get($GLOBALS, $key, $default); 2013 | } 2014 | 2015 | /** 2016 | * Sets a global by key 2017 | * 2018 | * @param string $key The key to define 2019 | * @param mixed $value The value for the passed key 2020 | */ 2021 | static function set($key, $value=null) { 2022 | if(is_array($key)) { 2023 | // set all new values 2024 | $GLOBALS = array_merge($GLOBALS, $key); 2025 | } else { 2026 | $GLOBALS[$key] = $value; 2027 | } 2028 | } 2029 | 2030 | } 2031 | 2032 | 2033 | 2034 | 2035 | 2036 | 2037 | /** 2038 | * 2039 | * Language 2040 | * 2041 | * Some handy methods to handle multi-language support 2042 | * 2043 | * @todo rework all but set() and get() 2044 | * @package Kirby 2045 | */ 2046 | class l { 2047 | 2048 | /** 2049 | * The global language array 2050 | * 2051 | * @var array 2052 | */ 2053 | public static $lang = array(); 2054 | 2055 | /** 2056 | * Gets a language value by key 2057 | * 2058 | * @param mixed $key The key to look for. Pass false or null to return the entire language array. 2059 | * @param mixed $default Optional default value, which should be returned if no element has been found 2060 | * @return mixed 2061 | */ 2062 | static function get($key=null, $default=null) { 2063 | if(empty($key)) return self::$lang; 2064 | return a::get(self::$lang, $key, $default); 2065 | } 2066 | 2067 | /** 2068 | * Sets a language value by key 2069 | * 2070 | * @param mixed $key The key to define 2071 | * @param mixed $value The value for the passed key 2072 | */ 2073 | static function set($key, $value=null) { 2074 | if(is_array($key)) { 2075 | self::$lang = array_merge(self::$lang, $key); 2076 | } else { 2077 | self::$lang[$key] = $value; 2078 | } 2079 | } 2080 | 2081 | /** 2082 | * @todo rework 2083 | */ 2084 | static function change($language='en') { 2085 | s::set('language', l::sanitize($language)); 2086 | return s::get('language'); 2087 | } 2088 | 2089 | /** 2090 | * @todo rework 2091 | */ 2092 | static function current() { 2093 | if(s::get('language')) return s::get('language'); 2094 | $lang = str::split(server::get('http_accept_language'), '-'); 2095 | $lang = str::trim(a::get($lang, 0)); 2096 | $lang = l::sanitize($lang); 2097 | s::set('language', $lang); 2098 | return $lang; 2099 | } 2100 | 2101 | /** 2102 | * @todo rework 2103 | */ 2104 | static function locale($language=false) { 2105 | if(!$language) $language = l::current(); 2106 | $default_locales = array( 2107 | 'de' => array('de_DE.UTF8','de_DE@euro','de_DE','de','ge'), 2108 | 'fr' => array('fr_FR.UTF8','fr_FR','fr'), 2109 | 'es' => array('es_ES.UTF8','es_ES','es'), 2110 | 'it' => array('it_IT.UTF8','it_IT','it'), 2111 | 'pt' => array('pt_PT.UTF8','pt_PT','pt'), 2112 | 'zh' => array('zh_CN.UTF8','zh_CN','zh'), 2113 | 'en' => array('en_US.UTF8','en_US','en'), 2114 | ); 2115 | $locales = c::get('locales', array()); 2116 | $locales = array_merge($default_locales, $locales); 2117 | setlocale(LC_ALL, a::get($locales, $language, array('en_US.UTF8','en_US','en'))); 2118 | return setlocale(LC_ALL, 0); 2119 | } 2120 | 2121 | /** 2122 | * @todo rework 2123 | */ 2124 | static function load($file) { 2125 | 2126 | // replace the language variable 2127 | $file = str_replace('{language}', l::current(), $file); 2128 | 2129 | // check if it exists 2130 | if(file_exists($file)) { 2131 | require($file); 2132 | return l::get(); 2133 | } 2134 | 2135 | // try to find the default language file 2136 | $file = str_replace('{language}', c::get('language', 'en'), $file); 2137 | 2138 | // check again if it exists 2139 | if(file_exists($file)) require($file); 2140 | return l::get(); 2141 | 2142 | } 2143 | 2144 | /** 2145 | * @todo rework 2146 | */ 2147 | static function sanitize($language) { 2148 | if(!in_array($language, c::get('languages', array('en')) )) $language = c::get('language', 'en'); 2149 | return $language; 2150 | } 2151 | 2152 | } 2153 | 2154 | 2155 | 2156 | 2157 | 2158 | 2159 | 2160 | /** 2161 | * 2162 | * Request 2163 | * 2164 | * Handles all incoming requests 2165 | * 2166 | * @package Kirby 2167 | */ 2168 | class r { 2169 | 2170 | /** 2171 | * Stores all sanitized request data 2172 | * 2173 | * @var array 2174 | */ 2175 | static private $_ = false; 2176 | 2177 | // fetch all data from the request and sanitize it 2178 | static function data() { 2179 | if(self::$_) return self::$_; 2180 | return self::$_ = self::sanitize($_REQUEST); 2181 | } 2182 | 2183 | /** 2184 | * Sanitizes the incoming data 2185 | * 2186 | * @param array $data 2187 | * @return array 2188 | */ 2189 | static function sanitize($data) { 2190 | foreach($data as $key => $value) { 2191 | if(!is_array($value)) { 2192 | $value = trim(str::stripslashes($value)); 2193 | } else { 2194 | $value = self::sanitize($value); 2195 | } 2196 | $data[$key] = $value; 2197 | } 2198 | return $data; 2199 | } 2200 | 2201 | /** 2202 | * Sets a request value by key 2203 | * 2204 | * @param mixed $key The key to define 2205 | * @param mixed $value The value for the passed key 2206 | */ 2207 | static function set($key, $value=null) { 2208 | $data = self::data(); 2209 | if(is_array($key)) { 2210 | self::$_ = array_merge($data, $key); 2211 | } else { 2212 | self::$_[$key] = $value; 2213 | } 2214 | } 2215 | 2216 | /** 2217 | * Gets a request value by key 2218 | * 2219 | * @param mixed $key The key to look for. Pass false or null to return the entire request array. 2220 | * @param mixed $default Optional default value, which should be returned if no element has been found 2221 | * @return mixed 2222 | */ 2223 | static function get($key=false, $default=null) { 2224 | $request = (self::method() == 'GET') ? self::data() : array_merge(self::data(), self::body()); 2225 | if(empty($key)) return $request; 2226 | return a::get($request, $key, $default); 2227 | } 2228 | 2229 | /** 2230 | * Returns the current request method 2231 | * 2232 | * @return string POST, GET, DELETE, PUT 2233 | */ 2234 | static function method() { 2235 | return strtoupper(server::get('request_method')); 2236 | } 2237 | 2238 | /** 2239 | * Returns the request body from POST requests for example 2240 | * 2241 | * @return array 2242 | */ 2243 | static function body() { 2244 | @parse_str(@file_get_contents('php://input'), $body); 2245 | return self::sanitize((array)$body); 2246 | } 2247 | 2248 | /** 2249 | * Checks if the current request is an AJAX request 2250 | * 2251 | * @return boolean 2252 | */ 2253 | static function is_ajax() { 2254 | return (strtolower(server::get('http_x_requested_with')) == 'xmlhttprequest') ? true : false; 2255 | } 2256 | 2257 | /** 2258 | * Checks if the current request is a GET request 2259 | * 2260 | * @return boolean 2261 | */ 2262 | static function is_get() { 2263 | return (self::method() == 'GET') ? true : false; 2264 | } 2265 | 2266 | /** 2267 | * Checks if the current request is a POST request 2268 | * 2269 | * @return boolean 2270 | */ 2271 | static function is_post() { 2272 | return (self::method() == 'POST') ? true : false; 2273 | } 2274 | 2275 | /** 2276 | * Checks if the current request is a DELETE request 2277 | * 2278 | * @return boolean 2279 | */ 2280 | static function is_delete() { 2281 | return (self::method() == 'DELETE') ? true : false; 2282 | } 2283 | 2284 | /** 2285 | * Checks if the current request is a PUT request 2286 | * 2287 | * @return boolean 2288 | */ 2289 | static function is_put() { 2290 | return (self::method() == 'PUT') ? true : false; 2291 | } 2292 | 2293 | /** 2294 | * Returns the HTTP_REFERER 2295 | * 2296 | * @param string $default Define a default URL if no referer has been found 2297 | * @return string 2298 | */ 2299 | static function referer($default=null) { 2300 | if(empty($default)) $default = '/'; 2301 | return server::get('http_referer', $default); 2302 | } 2303 | 2304 | } 2305 | 2306 | 2307 | /** 2308 | * Shortcut for r::get() 2309 | * 2310 | * @param mixed $key The key to look for. Pass false or null to return the entire request array. 2311 | * @param mixed $default Optional default value, which should be returned if no element has been found 2312 | * @return mixed 2313 | * @package Kirby 2314 | */ 2315 | function get($key=false, $default=null) { 2316 | return r::get($key, $default); 2317 | } 2318 | 2319 | 2320 | 2321 | 2322 | 2323 | 2324 | 2325 | /** 2326 | * 2327 | * Session 2328 | * 2329 | * Handles all session fiddling 2330 | * 2331 | * @package Kirby 2332 | */ 2333 | class s { 2334 | 2335 | /** 2336 | * Returns the current session id 2337 | * 2338 | * @return string 2339 | */ 2340 | static function id() { 2341 | return @session_id(); 2342 | } 2343 | 2344 | /** 2345 | * Sets a session value by key 2346 | * 2347 | * @param mixed $key The key to define 2348 | * @param mixed $value The value for the passed key 2349 | */ 2350 | static function set($key, $value=false) { 2351 | if(!isset($_SESSION)) return false; 2352 | if(is_array($key)) { 2353 | $_SESSION = array_merge($_SESSION, $key); 2354 | } else { 2355 | $_SESSION[$key] = $value; 2356 | } 2357 | } 2358 | 2359 | /** 2360 | * Gets a session value by key 2361 | * 2362 | * @param mixed $key The key to look for. Pass false or null to return the entire session array. 2363 | * @param mixed $default Optional default value, which should be returned if no element has been found 2364 | * @return mixed 2365 | */ 2366 | static function get($key=false, $default=null) { 2367 | if(!isset($_SESSION)) return false; 2368 | if(empty($key)) return $_SESSION; 2369 | return a::get($_SESSION, $key, $default); 2370 | } 2371 | 2372 | /** 2373 | * Removes a value from the session by key 2374 | * 2375 | * @param mixed $key The key to remove by 2376 | * @return array The session array without the value 2377 | */ 2378 | static function remove($key) { 2379 | if(!isset($_SESSION)) return false; 2380 | $_SESSION = a::remove($_SESSION, $key, true); 2381 | return $_SESSION; 2382 | } 2383 | 2384 | /** 2385 | * Starts a new session 2386 | * 2387 | */ 2388 | static function start() { 2389 | @session_start(); 2390 | } 2391 | 2392 | /** 2393 | * Destroys a session 2394 | * 2395 | */ 2396 | static function destroy() { 2397 | @session_destroy(); 2398 | } 2399 | 2400 | /** 2401 | * Destroys a session first and then starts it again 2402 | * 2403 | */ 2404 | static function restart() { 2405 | self::destroy(); 2406 | self::start(); 2407 | } 2408 | 2409 | } 2410 | 2411 | 2412 | 2413 | 2414 | 2415 | 2416 | 2417 | /** 2418 | * 2419 | * Server 2420 | * 2421 | * Makes it more convenient to get variables 2422 | * from the global server array 2423 | * 2424 | * @package Kirby 2425 | */ 2426 | class server { 2427 | 2428 | /** 2429 | * Gets a value from the _SERVER array 2430 | * 2431 | * @param mixed $key The key to look for. Pass false or null to return the entire server array. 2432 | * @param mixed $default Optional default value, which should be returned if no element has been found 2433 | * @return mixed 2434 | */ 2435 | static function get($key=false, $default=null) { 2436 | if(empty($key)) return $_SERVER; 2437 | return a::get($_SERVER, str::upper($key), $default); 2438 | } 2439 | 2440 | } 2441 | 2442 | 2443 | 2444 | 2445 | 2446 | 2447 | 2448 | 2449 | /** 2450 | * 2451 | * Size 2452 | * 2453 | * Makes it easy to recalculate image dimensions 2454 | * 2455 | * @package Kirby 2456 | */ 2457 | class size { 2458 | 2459 | /** 2460 | * Gets the ratio by width and height 2461 | * 2462 | * @param int $width 2463 | * @param int $height 2464 | * @return float 2465 | */ 2466 | static function ratio($width, $height) { 2467 | return ($width / $height); 2468 | } 2469 | 2470 | /** 2471 | * Fits width and height into a defined box and keeps the ratio 2472 | * 2473 | * @param int $width 2474 | * @param int $height 2475 | * @param int $box 2476 | * @param boolean $force If width and height are smaller than the box this will force upscaling 2477 | * @return array An array with a key and value for width and height 2478 | */ 2479 | static function fit($width, $height, $box, $force=false) { 2480 | 2481 | if($width == 0 || $height == 0) return array('width' => $box, 'height' => $box); 2482 | 2483 | $ratio = self::ratio($width, $height); 2484 | 2485 | if($width > $height) { 2486 | if($width > $box || $force == true) $width = $box; 2487 | $height = floor($width / $ratio); 2488 | } elseif($height > $width) { 2489 | if($height > $box || $force == true) $height = $box; 2490 | $width = floor($height * $ratio); 2491 | } elseif($width > $box) { 2492 | $width = $box; 2493 | $height = $box; 2494 | } 2495 | 2496 | $output = array(); 2497 | $output['width'] = $width; 2498 | $output['height'] = $height; 2499 | 2500 | return $output; 2501 | 2502 | } 2503 | 2504 | /** 2505 | * Fits width and height by a passed width and keeps the ratio 2506 | * 2507 | * @param int $width 2508 | * @param int $height 2509 | * @param int $fit The new width 2510 | * @param boolean $force If width and height are smaller than the box this will force upscaling 2511 | * @return array An array with a key and value for width and height 2512 | */ 2513 | static function fit_width($width, $height, $fit, $force=false) { 2514 | if($width <= $fit && !$force) return array( 2515 | 'width' => $width, 2516 | 'height' => $height 2517 | ); 2518 | $ratio = self::ratio($width, $height); 2519 | return array( 2520 | 'width' => $fit, 2521 | 'height' => floor($fit / $ratio) 2522 | ); 2523 | } 2524 | 2525 | /** 2526 | * Fits width and height by a passed height and keeps the ratio 2527 | * 2528 | * @param int $width 2529 | * @param int $height 2530 | * @param int $fit The new height 2531 | * @param boolean $force If width and height are smaller than the box this will force upscaling 2532 | * @return array An array with a key and value for width and height 2533 | */ 2534 | static function fit_height($width, $height, $fit, $force=false) { 2535 | if($height <= $fit && !$force) return array( 2536 | 'width' => $width, 2537 | 'height' => $height 2538 | ); 2539 | $ratio = self::ratio($width, $height); 2540 | return array( 2541 | 'width' => floor($fit * $ratio), 2542 | 'height' => $fit 2543 | ); 2544 | 2545 | } 2546 | 2547 | } 2548 | 2549 | 2550 | 2551 | 2552 | 2553 | 2554 | 2555 | 2556 | /** 2557 | * 2558 | * String 2559 | * 2560 | * A set of handy string methods 2561 | * 2562 | * @package Kirby 2563 | */ 2564 | class str { 2565 | 2566 | /** 2567 | * Converts a string to a html-safe string 2568 | * 2569 | * @param string $string 2570 | * @param boolean $keep_html True: lets stuff inside html tags untouched. 2571 | * @return string The html string 2572 | */ 2573 | static function html($string, $keep_html=true) { 2574 | if($keep_html) { 2575 | return stripslashes(implode('', preg_replace_callback('/^([^<].+[^>])$/', function($m){return htmlentities($m[1], ENT_COMPAT, 'utf-8');}, preg_split('/(<.+?>)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE)))); 2576 | } else { 2577 | return htmlentities($string, ENT_COMPAT, 'utf-8'); 2578 | } 2579 | } 2580 | 2581 | /** 2582 | * Removes all html tags and encoded chars from a string 2583 | * 2584 | * @param string $string 2585 | * @return string The html string 2586 | */ 2587 | static function unhtml($string) { 2588 | $string = strip_tags($string); 2589 | return html_entity_decode($string, ENT_COMPAT, 'utf-8'); 2590 | } 2591 | 2592 | /** 2593 | * An internal store for a html entities translation table 2594 | * 2595 | * @return array 2596 | */ 2597 | static function entities() { 2598 | 2599 | return array( 2600 | ' ' => ' ', '¡' => '¡', '¢' => '¢', '£' => '£', '¤' => '¤', '¥' => '¥', '¦' => '¦', '§' => '§', 2601 | '¨' => '¨', '©' => '©', 'ª' => 'ª', '«' => '«', '¬' => '¬', '­' => '­', '®' => '®', '¯' => '¯', 2602 | '°' => '°', '±' => '±', '²' => '²', '³' => '³', '´' => '´', 'µ' => 'µ', '¶' => '¶', '·' => '·', 2603 | '¸' => '¸', '¹' => '¹', 'º' => 'º', '»' => '»', '¼' => '¼', '½' => '½', '¾' => '¾', '¿' => '¿', 2604 | 'À' => 'À', 'Á' => 'Á', 'Â' => 'Â', 'Ã' => 'Ã', 'Ä' => 'Ä', 'Å' => 'Å', 'Æ' => 'Æ', 'Ç' => 'Ç', 2605 | 'È' => 'È', 'É' => 'É', 'Ê' => 'Ê', 'Ë' => 'Ë', 'Ì' => 'Ì', 'Í' => 'Í', 'Î' => 'Î', 'Ï' => 'Ï', 2606 | 'Ð' => 'Ð', 'Ñ' => 'Ñ', 'Ò' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô', 'Õ' => 'Õ', 'Ö' => 'Ö', '×' => '×', 2607 | 'Ø' => 'Ø', 'Ù' => 'Ù', 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', 'Ý' => 'Ý', 'Þ' => 'Þ', 'ß' => 'ß', 2608 | 'à' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã', 'ä' => 'ä', 'å' => 'å', 'æ' => 'æ', 'ç' => 'ç', 2609 | 'è' => 'è', 'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'í' => 'í', 'î' => 'î', 'ï' => 'ï', 2610 | 'ð' => 'ð', 'ñ' => 'ñ', 'ò' => 'ò', 'ó' => 'ó', 'ô' => 'ô', 'õ' => 'õ', 'ö' => 'ö', '÷' => '÷', 2611 | 'ø' => 'ø', 'ù' => 'ù', 'ú' => 'ú', 'û' => 'û', 'ü' => 'ü', 'ý' => 'ý', 'þ' => 'þ', 'ÿ' => 'ÿ', 2612 | 'ƒ' => 'ƒ', 'Α' => 'Α', 'Β' => 'Β', 'Γ' => 'Γ', 'Δ' => 'Δ', 'Ε' => 'Ε', 'Ζ' => 'Ζ', 'Η' => 'Η', 2613 | 'Θ' => 'Θ', 'Ι' => 'Ι', 'Κ' => 'Κ', 'Λ' => 'Λ', 'Μ' => 'Μ', 'Ν' => 'Ν', 'Ξ' => 'Ξ', 'Ο' => 'Ο', 2614 | 'Π' => 'Π', 'Ρ' => 'Ρ', 'Σ' => 'Σ', 'Τ' => 'Τ', 'Υ' => 'Υ', 'Φ' => 'Φ', 'Χ' => 'Χ', 'Ψ' => 'Ψ', 2615 | 'Ω' => 'Ω', 'α' => 'α', 'β' => 'β', 'γ' => 'γ', 'δ' => 'δ', 'ε' => 'ε', 'ζ' => 'ζ', 'η' => 'η', 2616 | 'θ' => 'θ', 'ι' => 'ι', 'κ' => 'κ', 'λ' => 'λ', 'μ' => 'μ', 'ν' => 'ν', 'ξ' => 'ξ', 'ο' => 'ο', 2617 | 'π' => 'π', 'ρ' => 'ρ', 'ς' => 'ς', 'σ' => 'σ', 'τ' => 'τ', 'υ' => 'υ', 'φ' => 'φ', 'χ' => 'χ', 2618 | 'ψ' => 'ψ', 'ω' => 'ω', 'ϑ' => 'ϑ', 'ϒ' => 'ϒ', 'ϖ' => 'ϖ', '•' => '•', '…' => '…', '′' => '′', 2619 | '″' => '″', '‾' => '‾', '⁄' => '⁄', '℘' => '℘', 'ℑ' => 'ℑ', 'ℜ' => 'ℜ', '™' => '™', 'ℵ' => 'ℵ', 2620 | '←' => '←', '↑' => '↑', '→' => '→', '↓' => '↓', '↔' => '↔', '↵' => '↵', '⇐' => '⇐', '⇑' => '⇑', 2621 | '⇒' => '⇒', '⇓' => '⇓', '⇔' => '⇔', '∀' => '∀', '∂' => '∂', '∃' => '∃', '∅' => '∅', '∇' => '∇', 2622 | '∈' => '∈', '∉' => '∉', '∋' => '∋', '∏' => '∏', '∑' => '∑', '−' => '−', '∗' => '∗', '√' => '√', 2623 | '∝' => '∝', '∞' => '∞', '∠' => '∠', '∧' => '∧', '∨' => '∨', '∩' => '∩', '∪' => '∪', '∫' => '∫', 2624 | '∴' => '∴', '∼' => '∼', '≅' => '≅', '≈' => '≈', '≠' => '≠', '≡' => '≡', '≤' => '≤', '≥' => '≥', 2625 | '⊂' => '⊂', '⊃' => '⊃', '⊄' => '⊄', '⊆' => '⊆', '⊇' => '⊇', '⊕' => '⊕', '⊗' => '⊗', '⊥' => '⊥', 2626 | '⋅' => '⋅', '⌈' => '⌈', '⌉' => '⌉', '⌊' => '⌊', '⌋' => '⌋', '⟨' => '〈', '⟩' => '〉', '◊' => '◊', 2627 | '♠' => '♠', '♣' => '♣', '♥' => '♥', '♦' => '♦', '"' => '"', '&' => '&', '<' => '<', '>' => '>', 'Œ' => 'Œ', 2628 | 'œ' => 'œ', 'Š' => 'Š', 'š' => 'š', 'Ÿ' => 'Ÿ', 'ˆ' => 'ˆ', '˜' => '˜', ' ' => ' ', ' ' => ' ', 2629 | ' ' => ' ', '‌' => '‌', '‍' => '‍', '‎' => '‎', '‏' => '‏', '–' => '–', '—' => '—', '‘' => '‘', 2630 | '’' => '’', '‚' => '‚', '“' => '“', '”' => '”', '„' => '„', '†' => '†', '‡' => '‡', '‰' => '‰', 2631 | '‹' => '‹', '›' => '›', '€' => '€' 2632 | ); 2633 | 2634 | } 2635 | 2636 | /** 2637 | * Converts a string to a xml-safe string 2638 | * Converts it to html-safe first and then it 2639 | * will replace html entities to xml entities 2640 | * 2641 | * @param string $text 2642 | * @param boolean $html True: convert to html first 2643 | * @return string 2644 | */ 2645 | static function xml($text, $html=true) { 2646 | 2647 | // convert raw text to html safe text 2648 | if($html) $text = self::html($text); 2649 | 2650 | // convert html entities to xml entities 2651 | return strtr($text, self::entities()); 2652 | 2653 | } 2654 | 2655 | /** 2656 | * Removes all xml entities from a string 2657 | * and convert them to html entities first 2658 | * and remove all html entities afterwards. 2659 | * 2660 | * @param string $string 2661 | * @return string 2662 | */ 2663 | static function unxml($string) { 2664 | 2665 | // flip the conversion table 2666 | $table = array_flip(self::entities()); 2667 | 2668 | // convert xml entities to html entities 2669 | $string = strtr($string, $table); 2670 | return str::unhtml($string); 2671 | 2672 | } 2673 | 2674 | /** 2675 | * Parses a string by a set of available methods 2676 | * 2677 | * Available methods: 2678 | * - json 2679 | * - xml 2680 | * - url 2681 | * - query 2682 | * - php 2683 | * 2684 | * @param string $string 2685 | * @param string $mode 2686 | * @return string 2687 | */ 2688 | static function parse($string, $mode='json') { 2689 | 2690 | if(is_array($string)) return $string; 2691 | 2692 | switch($mode) { 2693 | case 'json': 2694 | $result = (array)@json_decode($string, true); 2695 | break; 2696 | case 'xml': 2697 | $result = x::parse($string); 2698 | break; 2699 | case 'url': 2700 | $result = (array)@parse_url($string); 2701 | break; 2702 | case 'query': 2703 | if(url::has_query($string)) { 2704 | $string = self::split($string, '?'); 2705 | $string = a::last($string); 2706 | } 2707 | @parse_str($string, $result); 2708 | break; 2709 | case 'php': 2710 | $result = @unserialize($string); 2711 | break; 2712 | default: 2713 | $result = $string; 2714 | break; 2715 | } 2716 | 2717 | return $result; 2718 | 2719 | } 2720 | 2721 | /** 2722 | * Encode a string (used for email addresses) 2723 | * 2724 | * @param string $string 2725 | * @return string 2726 | */ 2727 | static function encode($string) { 2728 | $encoded = ''; 2729 | $length = str::length($string); 2730 | for($i=0; $i<$length; $i++) { 2731 | $encoded .= (rand(1,2)==1) ? '&#' . ord($string[$i]) . ';' : '&#x' . dechex(ord($string[$i])) . ';'; 2732 | } 2733 | return $encoded; 2734 | } 2735 | 2736 | /** 2737 | * Creates an encoded email address, including proper html-tags 2738 | * 2739 | * @param string $email The email address 2740 | * @param string $text Specify a text for the email link. If false the email address will be used 2741 | * @param string $title An optional title for the html tag. 2742 | * @param string $class An optional class name for the html tag. 2743 | * @return string 2744 | */ 2745 | static function email($email, $text=false, $title=false, $class=false) { 2746 | if(empty($email)) return false; 2747 | $email = (string)$email; 2748 | $string = (empty($text)) ? $email : $text; 2749 | $email = self::encode($email, 3); 2750 | 2751 | if(!empty($class)) $class = ' class="' . $class . '"'; 2752 | if(!empty($title)) $title = ' title="' . html($title) . '"'; 2753 | 2754 | return '' . self::encode($string, 3) . ''; 2755 | } 2756 | 2757 | /** 2758 | * Creates a link tag 2759 | * 2760 | * @param string $link The URL 2761 | * @param string $text Specify a text for the link tag. If false the URL will be used 2762 | * @return string 2763 | */ 2764 | static function link($link, $text=false) { 2765 | $text = ($text) ? $text : $link; 2766 | return '' . str::html($text) . ''; 2767 | } 2768 | 2769 | /** 2770 | * Shortens a string and adds an ellipsis if the string is too long 2771 | * 2772 | * @param string $string The string to be shortened 2773 | * @param int $chars The final number of characters the string should have 2774 | * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default. 2775 | * @return string The shortened string 2776 | */ 2777 | static function short($string, $chars, $rep='…') { 2778 | if($chars == 0) return $string; 2779 | if(str::length($string) <= $chars) return $string; 2780 | $string = self::substr($string,0,($chars-str::length($rep))); 2781 | $punctuation = '.!?:;,-'; 2782 | $string = (strspn(strrev($string), $punctuation)!=0) ? substr($string, 0, -strspn(strrev($string), $punctuation)) : $string; 2783 | return $string . $rep; 2784 | } 2785 | 2786 | /** 2787 | * Shortens an URL 2788 | * It removes http:// or https:// and uses str::short afterwards 2789 | * 2790 | * @param string $url The URL to be shortened 2791 | * @param int $chars The final number of characters the URL should have 2792 | * @param boolean $base True: only take the base of the URL. 2793 | * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default. 2794 | * @return string The shortened URL 2795 | */ 2796 | static function shorturl($url, $chars=false, $base=false, $rep='…') { 2797 | return url::short($url, $chars, $base, $rep); 2798 | } 2799 | 2800 | /** 2801 | * Creates an exceprt of a string 2802 | * It removes all html tags first and then uses str::short 2803 | * 2804 | * @param string $string The string to be shortened 2805 | * @param int $chars The final number of characters the string should have 2806 | * @param boolean $removehtml True: remove the HTML tags from the string first 2807 | * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default. 2808 | * @return string The shortened string 2809 | */ 2810 | static function excerpt($string, $chars=140, $removehtml=true, $rep='…') { 2811 | if($removehtml) $string = strip_tags($string); 2812 | $string = str::trim($string); 2813 | $string = str_replace("\n", ' ', $string); 2814 | if(str::length($string) <= $chars) return $string; 2815 | return ($chars==0) ? $string : substr($string, 0, strrpos(substr($string, 0, $chars), ' ')) . $rep; 2816 | } 2817 | 2818 | /** 2819 | * Shortens a string by cutting out chars in the middle 2820 | * This method mimicks the shortening which is used for filenames in the Finder 2821 | * 2822 | * @param string $string The string to be shortened 2823 | * @param int $length The final number of characters the string should have 2824 | * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default. 2825 | * @return string The shortened string 2826 | */ 2827 | static function cutout($str, $length, $rep='…') { 2828 | 2829 | $strlength = str::length($str); 2830 | if($length >= $strlength) return $str; 2831 | 2832 | // calc the how much we have to cut off 2833 | $cut = (($strlength+str::length($rep)) - $length); 2834 | 2835 | // divide it to cut left and right from the center 2836 | $cutp = round($cut/2); 2837 | 2838 | // get the center of the string 2839 | $strcenter = round($strlength/2); 2840 | 2841 | // get the start of the cut 2842 | $strlcenter = ($strcenter-$cutp); 2843 | 2844 | // get the end of the cut 2845 | $strrcenter = ($strcenter+$cutp); 2846 | 2847 | // cut and glue 2848 | return str::substr($str, 0, $strlcenter) . $rep . str::substr($str, $strrcenter); 2849 | 2850 | } 2851 | 2852 | /** 2853 | * Adds an apostrohpe to a string/name if applicable 2854 | * 2855 | * @param string $name The string to be shortened 2856 | * @return string The string + apostrophe 2857 | */ 2858 | static function apostrophe($name) { 2859 | return (substr($name,-1,1) == 's' || substr($name,-1,1) == 'z') ? $name .= "'" : $name .= "'s"; 2860 | } 2861 | 2862 | /** 2863 | * A switch to display either one or the other string dependend on a counter 2864 | * 2865 | * @param int $count The counter 2866 | * @param string $many The string to be displayed for a counter > 1 2867 | * @param string $one The string to be displayed for a counter == 1 2868 | * @param string $zero The string to be displayed for a counter == 0 2869 | * @return string The string 2870 | */ 2871 | static function plural($count, $many, $one, $zero = '') { 2872 | if($count == 1) return $one; 2873 | else if($count == 0 && !empty($zero)) return $zero; 2874 | else return $many; 2875 | } 2876 | 2877 | /** 2878 | * An UTF-8 safe version of substr() 2879 | * 2880 | * @param string $str 2881 | * @param int $start 2882 | * @param int $end 2883 | * @return string 2884 | */ 2885 | static function substr($str, $start, $end = null) { 2886 | return mb_substr($str, $start, ($end == null) ? mb_strlen($str, 'UTF-8') : $end, 'UTF-8'); 2887 | } 2888 | 2889 | /** 2890 | * An UTF-8 safe version of strtolower() 2891 | * 2892 | * @param string $str 2893 | * @return string 2894 | */ 2895 | static function lower($str) { 2896 | return mb_strtolower($str, 'UTF-8'); 2897 | } 2898 | 2899 | /** 2900 | * An UTF-8 safe version of strotoupper() 2901 | * 2902 | * @param string $str 2903 | * @return string 2904 | */ 2905 | static function upper($str) { 2906 | return mb_strtoupper($str, 'UTF-8'); 2907 | } 2908 | 2909 | /** 2910 | * An UTF-8 safe version of strlen() 2911 | * 2912 | * @param string $str 2913 | * @return string 2914 | */ 2915 | static function length($str) { 2916 | return mb_strlen($str, 'UTF-8'); 2917 | } 2918 | 2919 | /** 2920 | * Checks if a str contains another string 2921 | * 2922 | * @param string $str 2923 | * @param string $needle 2924 | * @param boolean $i ignore upper/lowercase 2925 | * @return string 2926 | */ 2927 | static function contains($str, $needle, $i=true) { 2928 | if($i) { 2929 | $str = str::lower($str); 2930 | $needle = str::lower($needle); 2931 | } 2932 | return (strstr($str, $needle)) ? true : false; 2933 | } 2934 | 2935 | /** 2936 | * preg_match sucks! This tries to make it more convenient 2937 | * 2938 | * @param string $string 2939 | * @param string $preg Regular expression 2940 | * @param string $get Which part should be returned from the result array 2941 | * @param string $placeholder Default value if nothing will be found 2942 | * @return mixed 2943 | */ 2944 | static function match($string, $preg, $get=false, $placeholder=false) { 2945 | $match = @preg_match($preg, $string, $array); 2946 | if(!$match) return false; 2947 | if($get === false) return $array; 2948 | return a::get($array, $get, $placeholder); 2949 | } 2950 | 2951 | /** 2952 | * Generates a random string 2953 | * 2954 | * @param int $length The length of the random string 2955 | * @return string 2956 | */ 2957 | static function random($length=false) { 2958 | $length = ($length) ? $length : rand(5,10); 2959 | $chars = range('a','z'); 2960 | $num = range(0,9); 2961 | $pool = array_merge($chars, $num); 2962 | $string = ''; 2963 | for($x=0; $x<$length; $x++) { 2964 | shuffle($pool); 2965 | $string .= current($pool); 2966 | } 2967 | return $string; 2968 | } 2969 | 2970 | /** 2971 | * Convert a string to a safe version to be used in an URL 2972 | * 2973 | * @param string $text The unsafe string 2974 | * @return string The safe string 2975 | */ 2976 | static function urlify($text) { 2977 | $text = trim($text); 2978 | $text = str::lower($text); 2979 | $text = str_replace('ä', 'ae', $text); 2980 | $text = str_replace('ö', 'oe', $text); 2981 | $text = str_replace('ü', 'ue', $text); 2982 | $text = str_replace('ß', 'ss', $text); 2983 | $text = preg_replace("![^a-z0-9]!i","-", $text); 2984 | $text = preg_replace("![-]{2,}!","-", $text); 2985 | $text = preg_replace("!-$!","", $text); 2986 | return $text; 2987 | } 2988 | 2989 | /** 2990 | * Better alternative for explode() 2991 | * It takes care of removing empty values 2992 | * and it has a built-in way to skip values 2993 | * which are too short. 2994 | * 2995 | * @param string $string The string to split 2996 | * @param string $separator The string to split by 2997 | * @param int $length The min length of values. 2998 | * @return array An array of found values 2999 | */ 3000 | static function split($string, $separator=',', $length=1) { 3001 | 3002 | if(is_array($string)) return $string; 3003 | 3004 | $string = trim($string, $separator); 3005 | $parts = explode($separator, $string); 3006 | $out = array(); 3007 | 3008 | foreach($parts AS $p) { 3009 | $p = trim($p); 3010 | if(str::length($p) > 0 && str::length($p) >= $length) $out[] = $p; 3011 | } 3012 | 3013 | return $out; 3014 | 3015 | } 3016 | 3017 | /** 3018 | * A more brutal way to trim. 3019 | * It removes double spaces. 3020 | * Can be useful in some cases but 3021 | * be careful as it might remove too much. 3022 | * 3023 | * @param string $string The string to trim 3024 | * @return string The trimmed string 3025 | */ 3026 | static function trim($string) { 3027 | $string = preg_replace('/\s\s+/u', ' ', $string); 3028 | return trim($string); 3029 | } 3030 | 3031 | /** 3032 | * A set of sanitizer methods 3033 | * 3034 | * @param string $string The string to sanitize 3035 | * @param string $type The method 3036 | * @param string $default The default value if the string will be empty afterwards 3037 | * @return string The sanitized string 3038 | */ 3039 | static function sanitize($string, $type='str', $default=null) { 3040 | 3041 | $string = stripslashes((string)$string); 3042 | $string = urldecode($string); 3043 | $string = str::utf8($string); 3044 | 3045 | switch($type) { 3046 | case 'int': 3047 | $string = (int)$string; 3048 | break; 3049 | case 'str': 3050 | $string = (string)$string; 3051 | break; 3052 | case 'array': 3053 | $string = (array)$string; 3054 | break; 3055 | case 'nohtml': 3056 | $string = self::unhtml($string); 3057 | break; 3058 | case 'noxml': 3059 | $string = self::unxml($string); 3060 | break; 3061 | case 'enum': 3062 | $string = (in_array($string, array('y', 'n'))) ? $string : $default; 3063 | $string = (in_array($string, array('y', 'n'))) ? $string : 'n'; 3064 | break; 3065 | case 'checkbox': 3066 | $string = ($string == 'on') ? 'y' : 'n'; 3067 | break; 3068 | case 'url': 3069 | $string = (v::url($string)) ? $string : ''; 3070 | break; 3071 | case 'email': 3072 | $string = (v::email($string)) ? $string : ''; 3073 | break; 3074 | case 'plain': 3075 | $string = str::unxml($string); 3076 | $string = str::unhtml($string); 3077 | $string = str::trim($string); 3078 | break; 3079 | case 'lower': 3080 | $string = str::lower($string); 3081 | break; 3082 | case 'upper': 3083 | $string = str::upper($string); 3084 | break; 3085 | case 'words': 3086 | $string = str::sanitize($string, 'plain'); 3087 | $string = preg_replace('/[^\pL]/u', ' ', $string); 3088 | case 'tags': 3089 | $string = str::sanitize($string, 'plain'); 3090 | $string = preg_replace('/[^\pL\pN]/u', ' ', $string); 3091 | $string = str::trim($string); 3092 | case 'nobreaks': 3093 | $string = str_replace('\n','',$string); 3094 | $string = str_replace('\r','',$string); 3095 | $string = str_replace('\t','',$string); 3096 | break; 3097 | case 'url': 3098 | $string = self::urlify($string); 3099 | break; 3100 | case 'filename': 3101 | $string = f::save_name($string); 3102 | break; 3103 | } 3104 | 3105 | return trim($string); 3106 | 3107 | } 3108 | 3109 | /** 3110 | * An UTF-8 safe version of ucwords() 3111 | * 3112 | * @param string $string 3113 | * @return string 3114 | */ 3115 | static function ucwords($str) { 3116 | return mb_convert_case($str, MB_CASE_TITLE, 'UTF-8'); 3117 | } 3118 | 3119 | /** 3120 | * An UTF-8 safe version of ucfirst() 3121 | * 3122 | * @param string $string 3123 | * @return string 3124 | */ 3125 | static function ucfirst($str) { 3126 | return str::upper(str::substr($str, 0, 1)) . str::substr($str, 1); 3127 | } 3128 | 3129 | /** 3130 | * Converts a string to UTF-8 3131 | * 3132 | * @param string $string 3133 | * @return string 3134 | */ 3135 | static function utf8($string) { 3136 | $encoding = mb_detect_encoding($string,'UTF-8, ISO-8859-1, GBK'); 3137 | return ($encoding != 'UTF-8') ? iconv($encoding,'utf-8',$string) : $string; 3138 | } 3139 | 3140 | /** 3141 | * A better way to strip slashes 3142 | * 3143 | * @param string $string 3144 | * @return string 3145 | */ 3146 | static function stripslashes($string) { 3147 | if(is_array($string)) return $string; 3148 | return (get_magic_quotes_gpc()) ? stripslashes(stripslashes($string)) : $string; 3149 | } 3150 | 3151 | } 3152 | 3153 | 3154 | 3155 | 3156 | 3157 | 3158 | 3159 | 3160 | /** 3161 | * 3162 | * URL 3163 | * 3164 | * A bunch of handy methods to work with URLs 3165 | * 3166 | * @package Kirby 3167 | */ 3168 | class url { 3169 | 3170 | /** 3171 | * Returns the current URL 3172 | * 3173 | * @return string 3174 | */ 3175 | static function current() { 3176 | return 'http://' . server::get('http_host') . server::get('request_uri'); 3177 | } 3178 | 3179 | /** 3180 | * Shortens an URL 3181 | * It removes http:// or https:// and uses str::short afterwards 3182 | * 3183 | * @param string $url The URL to be shortened 3184 | * @param int $chars The final number of characters the URL should have 3185 | * @param boolean $base True: only take the base of the URL. 3186 | * @param string $rep The element, which should be added if the string is too long. Ellipsis is the default. 3187 | * @return string The shortened URL 3188 | */ 3189 | static function short($url, $chars=false, $base=false, $rep='…') { 3190 | $url = str_replace('http://','',$url); 3191 | $url = str_replace('https://','',$url); 3192 | $url = str_replace('ftp://','',$url); 3193 | $url = str_replace('www.','',$url); 3194 | if($base) { 3195 | $a = explode('/', $url); 3196 | $url = a::get($a, 0); 3197 | } 3198 | return ($chars) ? str::short($url, $chars, $rep) : $url; 3199 | } 3200 | 3201 | /** 3202 | * Checks if the URL has a query string attached 3203 | * 3204 | * @param string $url 3205 | * @return boolean 3206 | */ 3207 | static function has_query($url) { 3208 | return (str::contains($url, '?')) ? true : false; 3209 | } 3210 | 3211 | /** 3212 | * Strips the query from the URL 3213 | * 3214 | * @param string $url 3215 | * @return string 3216 | */ 3217 | static function strip_query($url) { 3218 | return preg_replace('/\?.*$/is', '', $url); 3219 | } 3220 | 3221 | /** 3222 | * Strips a hash value from the URL 3223 | * 3224 | * @param string $url 3225 | * @return string 3226 | */ 3227 | static function strip_hash($url) { 3228 | return preg_replace('/#.*$/is', '', $url); 3229 | } 3230 | 3231 | /** 3232 | * Checks for a valid URL 3233 | * 3234 | * @param string $url 3235 | * @return boolean 3236 | */ 3237 | static function valid($url) { 3238 | return v::url($url); 3239 | } 3240 | 3241 | } 3242 | 3243 | 3244 | 3245 | 3246 | 3247 | 3248 | 3249 | /** 3250 | * 3251 | * Validator 3252 | * 3253 | * Makes input validation easier 3254 | * 3255 | * @package Kirby 3256 | */ 3257 | class v { 3258 | 3259 | /** 3260 | * Core method to create a new validator 3261 | * 3262 | * @param string $string 3263 | * @param array $options 3264 | * @return boolean 3265 | */ 3266 | static function string($string, $options) { 3267 | $format = null; 3268 | $min_length = $max_length = 0; 3269 | if(is_array($options)) extract($options); 3270 | 3271 | if($format && !preg_match('/^[' . $format . ']*$/is', $string)) return false; 3272 | if($min_length && str::length($string) < $min_length) return false; 3273 | if($max_length && str::length($string) > $max_length) return false; 3274 | return true; 3275 | } 3276 | 3277 | /** 3278 | * Checks for a valid password 3279 | * 3280 | * @param string $password 3281 | * @return boolean 3282 | */ 3283 | static function password($password) { 3284 | return self::string($password, array('min_length' => 4)); 3285 | } 3286 | 3287 | /** 3288 | * Checks for two valid, matching password 3289 | * 3290 | * @param string $password1 3291 | * @param string $password2 3292 | * @return boolean 3293 | */ 3294 | static function passwords($password1, $password2) { 3295 | 3296 | if($password1 == $password2 3297 | && self::password($password1) 3298 | && self::password($password2)) { 3299 | return true; 3300 | } else { 3301 | return false; 3302 | } 3303 | 3304 | } 3305 | 3306 | /** 3307 | * Checks for valid date 3308 | * 3309 | * @param string $date 3310 | * @return boolean 3311 | */ 3312 | static function date($date) { 3313 | $time = strtotime($date); 3314 | if(!$time) return false; 3315 | 3316 | $year = date('Y', $time); 3317 | $month = date('m', $time); 3318 | $day = date('d', $time); 3319 | 3320 | return (checkdate($month, $day, $year)) ? $time : false; 3321 | 3322 | } 3323 | 3324 | /** 3325 | * Checks for valid email address 3326 | * 3327 | * @param string $email 3328 | * @return boolean 3329 | */ 3330 | static function email($email) { 3331 | $regex = '/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i'; 3332 | return (preg_match($regex, $email)) ? true : false; 3333 | } 3334 | 3335 | /** 3336 | * Checks for valid URL 3337 | * 3338 | * @param string $url 3339 | * @return boolean 3340 | */ 3341 | static function url($url) { 3342 | $regex = '/^(https?|ftp|rmtp|mms|svn):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i'; 3343 | return (preg_match($regex, $url)) ? true : false; 3344 | } 3345 | 3346 | /** 3347 | * Checks for valid filename 3348 | * 3349 | * @param string $string 3350 | * @return boolean 3351 | */ 3352 | static function filename($string) { 3353 | 3354 | $options = array( 3355 | 'format' => 'a-zA-Z0-9_-', 3356 | 'min_length' => 2, 3357 | ); 3358 | 3359 | return self::string($string, $options); 3360 | 3361 | } 3362 | 3363 | } 3364 | 3365 | 3366 | 3367 | 3368 | 3369 | 3370 | 3371 | /** 3372 | * 3373 | * XML 3374 | * 3375 | * The Kirby XML Parser Class 3376 | * 3377 | * @package Kirby 3378 | */ 3379 | class x { 3380 | 3381 | /** 3382 | * Parses a XML string and returns an array 3383 | * 3384 | * @param string $xml 3385 | * @return mixed 3386 | */ 3387 | static function parse($xml) { 3388 | 3389 | $xml = preg_replace('/(<\/?)(\w+):([^>]*>)/', '$1$2$3', $xml); 3390 | $xml = @simplexml_load_string($xml, null, LIBXML_NOENT); 3391 | $xml = @json_encode($xml); 3392 | $xml = @json_decode($xml, true); 3393 | return (is_array($xml)) ? $xml : false; 3394 | 3395 | } 3396 | 3397 | } 3398 | 3399 | ?> 3400 | --------------------------------------------------------------------------------