├── index.php
├── .DS_Store
├── logout.php
├── includes
├── class.objects.php
├── class.tag.php
├── class.pagepref.php
├── class.dbpager.php
├── master.inc.php
├── class.loop.php
├── class.dbsession.php
├── class.dbloop.php
├── class.urlcache.php
├── class.pager.php
├── class.config-sample.php
├── class.stats.php
├── class.rss.php
├── class.gd.php
├── class.dbobject.php
├── class.database.php
├── class.spferror.php
├── class.auth.php
└── functions.inc.php
├── .htaccess
├── mysql.sql
├── LICENSE
├── login.php
└── README.markdown
/index.php:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerhall/simple-php-framework/HEAD/.DS_Store
--------------------------------------------------------------------------------
/logout.php:
--------------------------------------------------------------------------------
1 | logout();
6 | redirect('index.php');
--------------------------------------------------------------------------------
/includes/class.objects.php:
--------------------------------------------------------------------------------
1 | select($id, 'name');
8 | if(!$this->ok())
9 | {
10 | $this->name = $id;
11 | $this->insert();
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 | # This turns on mod_rewrite and redirects any paths that don't physically exist
2 | # to /index.php. You can then access that path info (to determine what to do) in
3 | # PHP using $_SERVER['REQUEST_URI'] and $_SERVER['REDIRECT_URL']. This is an easy
4 | # way to avoid having hundreds of rewrite rules slowing down Apache and making things
5 | # more complicated than they should be.
6 |
7 | # Note: If you're having trouble making Apache pickup your .htaccess file,
8 | # make sure AllowOverride is set to "All" instead of "None".
9 |
10 | # RewriteEngine On
11 | # RewriteBase /
12 |
13 | # RewriteCond %{REQUEST_FILENAME} !-f # If not a file...
14 | # RewriteCond %{REQUEST_FILENAME} !-d # and not a directory...
15 | # RewriteRule . /index.php [L] # serve index.php
--------------------------------------------------------------------------------
/includes/class.pagepref.php:
--------------------------------------------------------------------------------
1 | _id = 'pp' . sha1($_SERVER['PHP_SELF']);
11 |
12 | if(isset($_SESSION[$this->_id]))
13 | $this->_data = unserialize($_SESSION[$this->_id]);
14 | }
15 |
16 | public function __get($key)
17 | {
18 | return $this->_data[$key];
19 | }
20 |
21 | public function __set($key, $val)
22 | {
23 | if(!is_array($this->_data)) $this->_data = array();
24 | $this->_data[$key] = $val;
25 | $_SESSION[$this->_id] = serialize($this->_data);
26 | }
27 |
28 | public function clear()
29 | {
30 | unset($_SESSION[$this->_id]);
31 | unset($this->_data);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/includes/class.dbpager.php:
--------------------------------------------------------------------------------
1 | itemClass = $itemClass;
11 | $this->countSql = $countSql;
12 | $this->pageSql = $pageSql;
13 |
14 | $db = Database::getDatabase();
15 | $num_records = intval($db->getValue($countSql));
16 |
17 | parent::__construct($page, $per_page, $num_records);
18 | }
19 |
20 | public function calculate()
21 | {
22 | parent::calculate();
23 | // load records .. see $this->firstRecord, $this->perPage
24 | $limitSql = sprintf(' LIMIT %s,%s', $this->firstRecord, $this->perPage);
25 | $this->records = array_values(DBObject::glob($this->itemClass, $this->pageSql . $limitSql));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/mysql.sql:
--------------------------------------------------------------------------------
1 | # Dump of table sessions
2 | # ------------------------------------------------------------
3 |
4 | CREATE TABLE `sessions` (
5 | `id` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
6 | `data` text COLLATE utf8_unicode_ci NOT NULL,
7 | `updated_on` int(10) NOT NULL DEFAULT '0',
8 | PRIMARY KEY (`id`)
9 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
10 |
11 |
12 |
13 | # Dump of table users
14 | # ------------------------------------------------------------
15 |
16 | CREATE TABLE `users` (
17 | `id` int(11) NOT NULL AUTO_INCREMENT,
18 | `nid` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
19 | `username` varchar(65) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
20 | `password` varchar(65) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
21 | `level` enum('user','admin') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'user',
22 | `twostep` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL,
23 | PRIMARY KEY (`id`),
24 | UNIQUE KEY `username` (`username`)
25 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
26 |
--------------------------------------------------------------------------------
/includes/master.inc.php:
--------------------------------------------------------------------------------
1 | index = 0;
11 | $this->elements = func_get_args();
12 | $this->numElements = func_num_args();
13 | }
14 |
15 | public function __tostring()
16 | {
17 | return (string) $this->get();
18 | }
19 |
20 | public function get()
21 | {
22 | if($this->numElements == 0) return null;
23 |
24 | $val = $this->elements[$this->index];
25 |
26 | if(++$this->index >= $this->numElements)
27 | $this->index = 0;
28 |
29 | return $val;
30 | }
31 |
32 | public function rand()
33 | {
34 | return $this->elements[array_rand($this->elements)];
35 | }
36 | }
37 |
38 | // Example:
39 | // $color = new Loop('white', 'black');
40 | //
41 | // echo "
";
42 | // echo " ";
43 | // echo " ";
44 | //
45 | // Or
46 | //
47 | // while($row = mysql_fetch_array($result))
48 | // echo "the row colors will alternate ";
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | * Copyright (c) 2006 - 2019, Simple PHP Framework
2 | *
3 | * http://github.com/tylerhall/simple-php-framework/
4 | *
5 | * This software is released under the MIT License
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8 | * documentation files (the "Software"), to deal in the Software without restriction, including without
9 | * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
10 | * Software, and to permit persons to whom the Software is furnished to do so, subject to the following
11 | * conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions
14 | * of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 | * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
19 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/includes/class.dbsession.php:
--------------------------------------------------------------------------------
1 | isConnected();
13 | }
14 |
15 | public static function close()
16 | {
17 | return true;
18 | }
19 |
20 | public static function read($id)
21 | {
22 | $db = Database::getDatabase(true);
23 | $db->query('SELECT `data` FROM `sessions` WHERE `id` = :id', array('id' => $id));
24 | return $db->hasRows() ? $db->getValue() : '';
25 | }
26 |
27 | public static function write($id, $data)
28 | {
29 | $db = Database::getDatabase(true);
30 | $db->query('DELETE FROM `sessions` WHERE `id` = :id', array('id' => $id));
31 | $db->query('INSERT INTO `sessions` (`id`, `data`, `updated_on`) VALUES (:id, :data, :updated_on)', array('id' => $id, 'data' => $data, 'updated_on' => time()));
32 | return ($db->affectedRows() == 1);
33 | }
34 |
35 | public static function destroy($id)
36 | {
37 | $db = Database::getDatabase(true);
38 | $db->query('DELETE FROM `sessions` WHERE `id` = :id', array('id' => $id));
39 | return ($db->affectedRows() == 1);
40 | }
41 |
42 | public static function gc($max)
43 | {
44 | $db = Database::getDatabase(true);
45 | $db->query('DELETE FROM `sessions` WHERE `updated_on` < :updated_on', array('updated_on' => time() - $max));
46 | return true;
47 | }
48 | }
--------------------------------------------------------------------------------
/login.php:
--------------------------------------------------------------------------------
1 | loggedIn()) redirect('index.php');
6 |
7 | // Try to log in...
8 | if(!empty($_POST['username'])) {
9 | if(empty($_POST['PIN'])) {
10 | $Auth->sendTwoStep($_POST['username']);
11 | } else {
12 | $Auth->login($_POST['username'], $_POST['password'], $_POST['PIN']);
13 | if($Auth->loggedIn()) {
14 | redirect('index.php');
15 | }
16 | }
17 | }
18 |
19 | // Clean the submitted username before redisplaying it.
20 | $username = isset($_POST['username']) ? htmlspecialchars($_POST['username']) : '';
21 | ?>
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | SPF Sample Login Page
30 |
31 |
32 |
33 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/includes/class.dbloop.php:
--------------------------------------------------------------------------------
1 | position = 0;
12 | $this->className = $class_name;
13 | $this->extraColumns = $extra_columns;
14 |
15 | // Make sure the class exists before we instantiate it...
16 | if(!class_exists($class_name))
17 | return;
18 |
19 | $tmp_obj = new $class_name;
20 |
21 | // Also, it needs to be a subclass of DBObject...
22 | if(!is_subclass_of($tmp_obj, 'DBObject'))
23 | return;
24 |
25 | if(is_null($sql))
26 | $sql = "SELECT * FROM `{$tmp_obj->tableName}`";
27 |
28 | $db = Database::getDatabase();
29 | $this->result = $db->query($sql);
30 | }
31 |
32 | public function rewind()
33 | {
34 | $this->position = 0;
35 | }
36 |
37 | public function current()
38 | {
39 | mysqli_data_seek($this->result, $this->position);
40 | $row = mysqli_fetch_array($this->result, MYSQLI_ASSOC);
41 | if($row === false)
42 | return false;
43 |
44 | $o = new $this->className;
45 | $o->load($row);
46 |
47 | foreach($this->extraColumns as $c)
48 | {
49 | $o->addColumn($c);
50 | $o->$c = isset($row[$c]) ? $row[$c] : null;
51 | }
52 |
53 | return $o;
54 | }
55 |
56 | public function key()
57 | {
58 | return $this->position;
59 | }
60 |
61 | public function next()
62 | {
63 | $this->position++;
64 | }
65 |
66 | public function valid()
67 | {
68 | if($this->position < mysqli_num_rows($this->result))
69 | return mysqli_data_seek($this->result, $this->position);
70 | else
71 | return false;
72 | }
73 |
74 | public function count()
75 | {
76 | return mysqli_num_rows($this->result);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/includes/class.urlcache.php:
--------------------------------------------------------------------------------
1 | query("SELECT * FROM url_cache WHERE url = :url LIMIT 1", array('url' => $url));
21 | $row = $db->getRow();
22 |
23 | if($row === false)
24 | {
25 | return self::refreshContent($url, $expires_in);
26 | }
27 | elseif(strtotime($row['dt_expires']) < time())
28 | {
29 | $data = self::refreshContent($url, $expires_in);
30 | return ($data === false) ? $row['data'] : $data;
31 | }
32 | else
33 | {
34 | return $row['data'];
35 | }
36 | }
37 |
38 | public static function refreshContent($url, $expires_in = 300)
39 | {
40 | $str = self::getURL($url);
41 | $data = self::decodeStrData($str);
42 | if($data === false) return false;
43 |
44 | $db = Database::getDatabase();
45 | $db->query("REPLACE INTO url_cache (url, dt_refreshed, dt_expires, data) VALUES (:url, :dt_refreshed, :dt_expires, :data)",
46 | array('url' => $url,
47 | 'dt_refreshed' => dater_utc(),
48 | 'dt_expires' => dater_utc(time() + $expires_in),
49 | 'data' => $str));
50 | return $str;
51 | }
52 |
53 | private static function getURL($url)
54 | {
55 | $ch = curl_init();
56 | curl_setopt($ch, CURLOPT_URL, $url);
57 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
58 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
59 | // curl_setopt($ch, CURLOPT_VERBOSE, 1);
60 | $data = curl_exec($ch);
61 | curl_close($ch);
62 | return $data;
63 | }
64 | }
65 |
66 | class XMLCache extends URLCache
67 | {
68 | public static function decodeStrData($str)
69 | {
70 | return simplexml_load_string($str);
71 | }
72 | }
73 |
74 | class JSONCache extends URLCache
75 | {
76 | public static function decodeStrData($str)
77 | {
78 | return json_decode($str);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | [The Simple PHP Framework](http://github.com/tylerhall/simple-php-framework/) is a pragmatic approach to building websites with PHP 7.2+. It's geared towards web design shops and freelance programmers looking for a common foundation to quickly bring web projects to life. Without getting too technical, SPF follows the [no-framework Framework](http://toys.lerdorf.com/archives/38-The-no-framework-PHP-MVC-framework.html) method coined by Rasmus Lerdorf -- with a little [Active Record](http://en.wikipedia.org/wiki/Active_record_pattern) thrown in for good measure.
2 |
3 | ### Project History ###
4 |
5 | This framework is the foundation that (almost) all of my websites are built with. I've been using this code base (or some form of it) since 2006 (yes, it's that old, but still works great) across hundreds of different projects - both personal and professional. It's served me well for the smallest of projects up to sites receiving millions of visitors per month. Less framework and more foundation, it provides a quick starting point and does a lot of the grunt work — user authentication, database calls, object lifecycle management, etc. It's exactly enough to get your project bootstrapped and moving forward quickly.
6 |
7 | This framework wasn't built overnight or even on purpose. It's really a development pattern and collection of (useful) classes that have evolved naturally over the last thirteen years or so. I've tried to walk a fine line and not add unnecessary features that most people won't use. I've done my best to keep it as minimal as possible yet still allow plenty of flexibility.
8 |
9 | The Simple PHP Framework is designed to _help_ you build websites — not build them for you. There are plenty out there that already try to do that.
10 |
11 | > All the web frameworks in the world won't turn a shitty programmer into a good one." — [uncov](https://web.archive.org/web/20070510011917/http://www.uncov.com/2007/5/4/contactify-the-hello-world-of-web-2-0)
12 |
13 | A branch of the framework was forked internally at Yahoo! in 2008. Improvements from that branch made their way back into the main trunk as appropriate.
14 |
15 | ### Download the Code ###
16 |
17 | The Simple PHP Framework is hosted on [GitHub](http://github.com/tylerhall/simple-php-framework/)
18 | and licensed under the [MIT Open Source License](http://www.opensource.org/licenses/mit-license.php).
19 |
20 | ### Documentation and Examples ###
21 |
22 | As is the tradition with most open source software, the code is self-documenting — which is a nice way of saying I'm too lazy to write any formal documentation myself. That said, I'm always happy to answer questions about the code. You're also welcome to join our [discussion group](http://groups.google.com/group/simple-php-framework). There's not much activity, but if you ask a question you'll typically get an answer back quickly.
23 |
24 | If you'd like to see a full website built using the framework, take a look at [Shine](https://github.com/tylerhall/Shine). It's a good, (mostly) clean example of how to use the framework.
25 |
--------------------------------------------------------------------------------
/includes/class.pager.php:
--------------------------------------------------------------------------------
1 | page = $page;
20 | $this->perPage = $per_page;
21 | $this->numRecords = $num_records;
22 | $this->calculate();
23 | }
24 |
25 | // Do the math.
26 | // Note: Pager always calculates there to be *at least* 1 page. Even if there are 0 records, we still,
27 | // by convention, assume it takes 1 page to display those 0 records. While mathematically stupid, it
28 | // makes sense from a UI perspective.
29 | public function calculate()
30 | {
31 | $this->numPages = ceil($this->numRecords / $this->perPage);
32 | if($this->numPages == 0) $this->numPages = 1;
33 |
34 | $this->page = intval($this->page);
35 | if($this->page < 1) $this->page = 1;
36 | if($this->page > $this->numPages) $this->page = $this->numPages;
37 |
38 | $this->firstRecord = (int) ($this->page - 1) * $this->perPage;
39 | $this->lastRecord = (int) $this->firstRecord + $this->perPage - 1;
40 | if($this->lastRecord >= $this->numRecords) $this->lastRecord = $this->numRecords - 1;
41 |
42 | $this->records = range($this->firstRecord, $this->lastRecord, 1);
43 | }
44 |
45 | // Will return current page if no previous page exists
46 | public function prevPage()
47 | {
48 | return max(1, $this->page - 1);
49 | }
50 |
51 | // Will return current page if no next page exists
52 | public function nextPage()
53 | {
54 | return min($this->numPages, $this->page + 1);
55 | }
56 |
57 | // Is there a valid previous page?
58 | public function hasPrevPage()
59 | {
60 | return $this->page > 1;
61 | }
62 |
63 | // Is there a valid next page?
64 | public function hasNextPage()
65 | {
66 | return $this->page < $this->numPages;
67 | }
68 |
69 | public function rewind()
70 | {
71 | reset($this->records);
72 | }
73 |
74 | public function current()
75 | {
76 | return current($this->records);
77 | }
78 |
79 | public function key()
80 | {
81 | return key($this->records);
82 | }
83 |
84 | public function next()
85 | {
86 | return next($this->records);
87 | }
88 |
89 | public function valid()
90 | {
91 | return $this->current() !== false;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/includes/class.config-sample.php:
--------------------------------------------------------------------------------
1 | everywhere();
42 |
43 | $i_am_here = $this->whereAmI();
44 |
45 | if('production' == $i_am_here)
46 | $this->production();
47 | elseif('staging' == $i_am_here)
48 | $this->staging();
49 | elseif('local' == $i_am_here)
50 | $this->local();
51 | elseif('shell' == $i_am_here)
52 | $this->shell();
53 | else
54 | die('Where am I? You need to setup your server names in class.config.php
55 | $_SERVER[\'HTTP_HOST\'] reported ' . $_SERVER['HTTP_HOST'] . '
');
56 | }
57 |
58 | // Get Singleton object
59 | public static function getConfig()
60 | {
61 | if(is_null(self::$me)) {
62 | self::$me = new Config();
63 | }
64 | return self::$me;
65 | }
66 |
67 | // Allow access to config settings statically.
68 | // Ex: Config::get('some_value')
69 | public static function get($key)
70 | {
71 | return self::$me->$key;
72 | }
73 |
74 | // Add code to be run on all servers
75 | private function everywhere()
76 | {
77 | // Store sesions in the database?
78 | $this->useDBSessions = true;
79 |
80 | // Settings for the Auth class
81 | $this->authDomain = $_SERVER['HTTP_HOST'];
82 | $this->useHashedPasswords = true;
83 | $this->authSalt = 'Pick any random string of characters';
84 | }
85 |
86 | // Add code/variables to be run only on production servers
87 | private function production()
88 | {
89 | ini_set('display_errors', '0');
90 | ini_set('error_reporting', 0); // disable
91 |
92 | define('WEB_ROOT', '/');
93 |
94 | $this->dbHost = '';
95 | $this->dbName = '';
96 | $this->dbUsername = '';
97 | $this->dbPassword = '';
98 | $this->dbDieOnError = false;
99 |
100 | $this->useTwoStepAuth = false;
101 | }
102 |
103 | // Add code/variables to be run only on staging servers
104 | private function staging()
105 | {
106 | ini_set('display_errors', '1');
107 | ini_set('error_reporting', E_ALL);
108 |
109 | define('WEB_ROOT', '');
110 |
111 | $this->dbHost = '';
112 | $this->dbName = '';
113 | $this->dbUsername = '';
114 | $this->dbPassword = '';
115 | $this->dbDieOnError = false;
116 | }
117 |
118 | // Add code/variables to be run only on local (testing) servers
119 | private function local()
120 | {
121 | ini_set('display_errors', '1');
122 | ini_set('error_reporting', E_ALL);
123 |
124 | define('WEB_ROOT', '/');
125 |
126 | $this->dbHost = '';
127 | $this->dbName = '';
128 | $this->dbUsername = '';
129 | $this->dbPassword = '';
130 | $this->dbDieOnError = true;
131 | }
132 |
133 | // Add code/variables to be run only on when script is launched from the shell
134 | private function shell()
135 | {
136 | ini_set('display_errors', '1');
137 | ini_set('error_reporting', E_ALL);
138 |
139 | define('WEB_ROOT', '');
140 |
141 | $this->dbHost = '';
142 | $this->dbName = '';
143 | $this->dbUsername = '';
144 | $this->dbPassword = '';
145 | $this->dbDieOnError = true;
146 | }
147 |
148 | public function whereAmI()
149 | {
150 | if(in_array($_SERVER['HTTP_HOST'], $this->productionServers))
151 | return 'production';
152 | elseif(in_array($_SERVER['HTTP_HOST'], $this->stagingServers))
153 | return 'staging';
154 | elseif(in_array($_SERVER['HTTP_HOST'], $this->localServers))
155 | return 'local';
156 | elseif(isset($_ENV['SHELL']))
157 | return 'shell';
158 | else
159 | return false;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/includes/class.stats.php:
--------------------------------------------------------------------------------
1 | numQueries();
41 |
42 | $sql = "INSERT INTO stats (dt, referer, referer_is_local, url, page_title, search_terms, img_search, browser_family, browser_version, os, os_version, ip, user_agent, exec_time, num_queries)
43 | VALUES (:dt, :referer, :referer_is_local, :url, :page_title, :search_terms, :img_search, :browser_family, :browser_version, :os, :os_version, :ip, :user_agent, :exec_time, :num_queries)";
44 | $vals = array('dt' => $dt,
45 | 'referer_is_local' => $referer_is_local,
46 | 'referer' => $referer,
47 | 'url' => $url,
48 | 'page_title' => $page_title,
49 | 'search_terms' => $search_terms,
50 | 'img_search' => $img_search,
51 | 'ip' => $ip,
52 | 'browser_family' => $browser_family,
53 | 'browser_version' => $browser_version,
54 | 'os_version' => $os_version,
55 | 'os' => $os,
56 | 'user_agent' => $user_agent,
57 | 'exec_time' => $exec_time,
58 | 'num_queries' => $num_queries);
59 | $db->query($sql, $vals);
60 | }
61 |
62 | public static function refererIsLocal($referer = null)
63 | {
64 | if(is_null($referer)) $referer = getenv('HTTP_REFERER');
65 | if(!strlen($referer)) return 0;
66 | $regex_host = preg_quote(getenv('HTTP_HOST'));
67 | return (preg_match("!^https?://$regex_host!i", $referer) !== false) ? 1 : 0;
68 | }
69 |
70 | public static function getIP()
71 | {
72 | $ip = getenv('HTTP_X_FORWARDED_FOR');
73 | if(!$ip) $ip = getenv('HTTP_CLIENT_IP');
74 | if(!$ip) $ip = getenv('REMOTE_ADDR');
75 | return $ip;
76 | }
77 |
78 | public static function searchTerms($url = null)
79 | {
80 | if(is_null($url)) $url = full_url();
81 | // if(self::refererIsLocal($url)) return;
82 |
83 | $arr = array();
84 | parse_str(parse_url($url, PHP_URL_QUERY), $arr);
85 |
86 | return isset($arr['q']) ? $arr['q'] : '';
87 | }
88 |
89 | // From http://us3.php.net/get_browser comments
90 | public static function browserInfo($a_browser = false, $a_version = false, $name = false)
91 | {
92 | $browser_list = 'msie firefox konqueror chrome safari netscape navigator opera mosaic lynx amaya omniweb avant camino flock seamonkey aol mozilla gecko';
93 | $user_browser = strtolower(getenv('HTTP_USER_AGENT'));
94 | $this_version = $this_browser = '';
95 |
96 | $browser_limit = strlen($user_browser);
97 | foreach(explode(' ', $browser_list) as $row)
98 | {
99 | $row = ($a_browser !== false) ? $a_browser : $row;
100 | $n = stristr($user_browser, $row);
101 | if(!$n || !empty($this_browser)) continue;
102 |
103 | $this_browser = $row;
104 | $j = strpos($user_browser, $row) + strlen($row) + 1;
105 | for(; $j <= $browser_limit; $j++)
106 | {
107 | $s = trim(substr($user_browser, $j, 1));
108 | $this_version .= $s;
109 |
110 | if($s === '') break;
111 | }
112 | }
113 |
114 | if($a_browser !== false)
115 | {
116 | $ret = false;
117 | if(strtolower($a_browser) == $this_browser)
118 | {
119 | $ret = true;
120 |
121 | if($a_version !== false && !empty($this_version))
122 | {
123 | $a_sign = explode(' ', $a_version);
124 | if(version_compare($this_version, $a_sign[1], $a_sign[0]) === false)
125 | {
126 | $ret = false;
127 | }
128 | }
129 | }
130 |
131 | return $ret;
132 | }
133 |
134 | $this_platform = '';
135 | if(strpos($user_browser, 'linux'))
136 | {
137 | $this_platform = 'linux';
138 | }
139 | elseif(strpos($user_browser, 'macintosh') || strpos($user_browser, 'mac platform x'))
140 | {
141 | $this_platform = 'mac';
142 | }
143 | elseif(strpos($user_browser, 'windows') || strpos($user_browser, 'win32'))
144 | {
145 | $this_platform = 'windows';
146 | }
147 |
148 | if($name !== false)
149 | {
150 | return $this_browser . ' ' . $this_version;
151 | }
152 |
153 | return array("browser" => $this_browser,
154 | "version" => $this_version,
155 | "platform" => $this_platform,
156 | "useragent" => $user_browser);
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/includes/class.rss.php:
--------------------------------------------------------------------------------
1 | items = array();
17 | $this->tags = array();
18 | $this->useCDataTags = true;
19 | $this->setPubDate();
20 | $this->url = $this->fullUrl();
21 | }
22 |
23 | public function addItem($item)
24 | {
25 | $this->items[] = $item;
26 | }
27 |
28 | public function setPubDate($date = null)
29 | {
30 | if(is_null($date)) $date = time();
31 | if(!ctype_digit($date)) $date = strtotime($date);
32 | $this->pubDate = date('D, d M Y H:i:s O', $date);
33 | }
34 |
35 | public function addTag($tag, $value)
36 | {
37 | $this->tags[$tag] = $value;
38 | }
39 |
40 | public function loadRecordset($result, $title, $link, $description, $pub_date)
41 | {
42 | while($row = mysql_fetch_array($result, MYSQL_ASSOC))
43 | {
44 | $item = new RSSItem();
45 | $item->title = $row[$title];
46 | $item->link = $row[$link];
47 | $item->description = $row[$description];
48 | $item->setPubDate($row[$pub_date]);
49 | $this->addItem($item);
50 | }
51 | }
52 |
53 | public function out()
54 | {
55 | $bad = array('&', '<');
56 | $good = array('&', '<');
57 | $title = str_replace($bad, $good, $this->title);
58 | $description = str_replace($bad, $good, $this->description);
59 |
60 | $out = $this->header();
61 | $out .= "\n";
62 | $out .= "" . $title . " \n";
63 | $out .= " " . $this->link . "\n";
64 | $out .= "" . $description . " \n";
65 | $out .= "" . $this->language . " \n";
66 | $out .= "" . $this->pubDate . " \n";
67 | $out .= ' ' . "\n";
68 |
69 | foreach($this->tags as $k => $v)
70 | $out .= "<$k>$v$k>\n";
71 |
72 | foreach($this->items as $item)
73 | $out .= $item->out();
74 |
75 | $out .= " \n";
76 |
77 | $out .= $this->footer();
78 |
79 | return $out;
80 | }
81 |
82 | public function serve($contentType = 'application/xml')
83 | {
84 | $xml = $this->out();
85 | header("Content-type: $contentType");
86 | echo $xml;
87 | }
88 |
89 | private function header()
90 | {
91 | $out = '' . "\n";
92 | $out .= '' . "\n";
93 | return $out;
94 | }
95 |
96 | private function footer()
97 | {
98 | return ' ';
99 | }
100 |
101 | private function fullUrl()
102 | {
103 | $s = empty($_SERVER['HTTPS']) ? '' : ($_SERVER['HTTPS'] == 'on') ? 's' : '';
104 | $protocol = substr(strtolower($_SERVER['SERVER_PROTOCOL']), 0, strpos(strtolower($_SERVER['SERVER_PROTOCOL']), '/')) . $s;
105 | $port = ($_SERVER['SERVER_PORT'] == '80') ? '' : (":".$_SERVER['SERVER_PORT']);
106 | return $protocol . "://" . $_SERVER['HTTP_HOST'] . $port . $_SERVER['REQUEST_URI'];
107 | }
108 |
109 | private function cdata($str)
110 | {
111 | if($this->useCDataTags)
112 | {
113 | $str = '';
114 | }
115 | return $str;
116 | }
117 | }
118 |
119 | class RSSItem
120 | {
121 | public $title;
122 | public $link;
123 | public $description;
124 | public $pubDate;
125 | public $guid;
126 | public $tags;
127 | public $enclosureUrl;
128 | public $enclosureType;
129 | public $enclosureLength;
130 | public $useCDataTags;
131 |
132 | public function __construct()
133 | {
134 | $this->useCDataTags = true;
135 | $this->tags = array();
136 | $this->setPubDate();
137 | }
138 |
139 | public function setPubDate($date = null)
140 | {
141 | if(is_null($date)) $date = time();
142 | if(!ctype_digit($date)) $date = strtotime($date);
143 | $this->pubDate = date('D, d M Y H:i:s O', $date);
144 | }
145 |
146 | public function addTag($tag, $value)
147 | {
148 | $this->tags[$tag] = $value;
149 | }
150 |
151 | public function out()
152 | {
153 | $bad = array('&', '<');
154 | $good = array('&', '<');
155 | $title = str_replace($bad, $good, $this->title);
156 |
157 | $out = "- \n";
158 | $out .= "
" . $title . " \n";
159 | $out .= " " . $this->link . "\n";
160 | $out .= "" . $this->cdata($this->description) . " \n";
161 | $out .= "" . $this->pubDate . " \n";
162 |
163 | if(is_null($this->guid))
164 | $this->guid = $this->link;
165 |
166 | $out .= "" . $this->guid . " \n";
167 |
168 | if(!is_null($this->enclosureUrl))
169 | $out .= " \n";
170 |
171 | foreach($this->tags as $k => $v)
172 | $out .= "<$k>$v$k>\n";
173 |
174 | $out .= " \n";
175 | return $out;
176 | }
177 |
178 | public function enclosure($url, $type, $length)
179 | {
180 | $this->enclosureUrl = $url;
181 | $this->enclosureType = $type;
182 | $this->enclosureLength = $length;
183 | }
184 |
185 | private function cdata($str)
186 | {
187 | if($this->useCDataTags)
188 | {
189 | $str = '';
190 | }
191 | return $str;
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/includes/class.gd.php:
--------------------------------------------------------------------------------
1 | loadResource($data);
15 | elseif(@file_exists($data) && is_readable($data))
16 | return $this->loadFile($data);
17 | elseif(is_string($data))
18 | return $this->loadString($data);
19 | else
20 | return false;
21 | }
22 |
23 | private function loadResource($im)
24 | {
25 | if(!is_resource($im) || !get_resource_type($im) == 'gd') return false;
26 |
27 | $this->im = $im;
28 | $this->width = imagesx($im);
29 | $this->height = imagesy($im);
30 |
31 | return true;
32 | }
33 |
34 | private function loadFile($filename)
35 | {
36 | if(!file_exists($filename) || !is_readable($filename)) return false;
37 |
38 | $info = getimagesize($filename);
39 | $this->width = $info[0];
40 | $this->height = $info[1];
41 | $this->type = image_type_to_extension($info[2], false);
42 | $this->mime = $info['mime'];
43 |
44 | if($this->type == 'jpeg' && (imagetypes() & IMG_JPG))
45 | $this->im = imagecreatefromjpeg($filename);
46 | elseif($this->type == 'png' && (imagetypes() & IMG_PNG))
47 | $this->im = imagecreatefrompng($filename);
48 | elseif($this->type == 'gif' && (imagetypes() & IMG_GIF))
49 | $this->im = imagecreatefromgif($filename);
50 | else
51 | return false;
52 |
53 | return true;
54 | }
55 |
56 | private function loadString($str)
57 | {
58 | $im = imagecreatefromstring($str);
59 | return ($im === false) ? false : $this->loadResource($im);
60 | }
61 |
62 | public function saveAs($filename, $type = 'jpg', $quality = 75)
63 | {
64 | if($type == 'jpg' && (imagetypes() & IMG_JPG))
65 | return imagejpeg($this->im, $filename, $quality);
66 | elseif($type == 'png' && (imagetypes() & IMG_PNG))
67 | return imagepng($this->im, $filename);
68 | elseif($type == 'gif' && (imagetypes() & IMG_GIF))
69 | return imagegif($this->im, $filename);
70 | else
71 | return false;
72 | }
73 |
74 | // Output file to browser
75 | public function output($type = 'jpg', $quality = 75)
76 | {
77 | if($type == 'jpg' && (imagetypes() & IMG_JPG))
78 | {
79 | header("Content-Type: image/jpeg");
80 | imagejpeg($this->im, null, $quality);
81 | return true;
82 | }
83 | elseif($type == 'png' && (imagetypes() & IMG_PNG))
84 | {
85 | header("Content-Type: image/png");
86 | imagepng($this->im);
87 | return true;
88 | }
89 | elseif($type == 'gif' && (imagetypes() & IMG_GIF))
90 | {
91 | header("Content-Type: image/gif");
92 | imagegif($this->im);
93 | return true;
94 | }
95 | else
96 | return false;
97 | }
98 |
99 | // Return image data as a string.
100 | // Is there a way to do this without using output buffering?
101 | public function toString($type = 'jpg', $quality = 75)
102 | {
103 | ob_start();
104 |
105 | if($type == 'jpg' && (imagetypes() & IMG_JPG))
106 | imagejpeg($this->im, null, $quality);
107 | elseif($type == 'png' && (imagetypes() & IMG_PNG))
108 | imagepng($this->im);
109 | elseif($type == 'gif' && (imagetypes() & IMG_GIF))
110 | imagegif($this->im);
111 |
112 | return ob_get_clean();
113 | }
114 |
115 | // Resizes an image and maintains aspect ratio.
116 | public function scale($new_width = null, $new_height = null)
117 | {
118 | if(!is_null($new_width) && is_null($new_height))
119 | $new_height = $new_width * $this->height / $this->width;
120 | elseif(is_null($new_width) && !is_null($new_height))
121 | $new_width = $this->width / $this->height * $new_height;
122 | elseif(!is_null($new_width) && !is_null($new_height))
123 | {
124 | if($this->width < $this->height)
125 | $new_width = $this->width / $this->height * $new_height;
126 | else
127 | $new_height = $new_width * $this->height / $this->width;
128 | }
129 | else
130 | return false;
131 |
132 | return $this->resize($new_width, $new_height);
133 | }
134 |
135 | // Resizes an image to an exact size
136 | public function resize($new_width, $new_height)
137 | {
138 | $dest = imagecreatetruecolor($new_width, $new_height);
139 |
140 | // Transparency fix contributed by Google Code user 'desfrenes'
141 | imagealphablending($dest, false);
142 | imagesavealpha($dest, true);
143 |
144 | if(imagecopyresampled($dest, $this->im, 0, 0, 0, 0, $new_width, $new_height, $this->width, $this->height))
145 | {
146 | $this->im = $dest;
147 | $this->width = imagesx($this->im);
148 | $this->height = imagesy($this->im);
149 | return true;
150 | }
151 |
152 | return false;
153 | }
154 |
155 | public function crop($x, $y, $w, $h)
156 | {
157 | $dest = imagecreatetruecolor($w, $h);
158 |
159 | if(imagecopyresampled($dest, $this->im, 0, 0, $x, $y, $w, $h, $w, $h))
160 | {
161 | $this->im = $dest;
162 | $this->width = $w;
163 | $this->height = $h;
164 | return true;
165 | }
166 |
167 | return false;
168 | }
169 |
170 | public function cropCentered($w, $h)
171 | {
172 | $cx = $this->width / 2;
173 | $cy = $this->height / 2;
174 | $x = $cx - $w / 2;
175 | $y = $cy - $h / 2;
176 | if($x < 0) $x = 0;
177 | if($y < 0) $y = 0;
178 | return $this->crop($x, $y, $w, $h);
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/includes/class.dbobject.php:
--------------------------------------------------------------------------------
1 | tableName = $table_name;
12 |
13 | // A note on hardcoding $this->idColumnName = 'id'...
14 | // In many years working with this framework, I've used
15 | // a different id name exactly once - so I've decided to
16 | // drop the option from the constructor. You can overload
17 | // the constructor yourself if you have the need.
18 | $this->idColumnName = 'id';
19 |
20 | foreach($columns as $col) {
21 | $this->columns[$col] = null;
22 | }
23 |
24 | if(!is_null($id)) {
25 | $this->select($id);
26 | }
27 | }
28 |
29 | public function __get($key)
30 | {
31 | if(array_key_exists($key, $this->columns)) {
32 | return $this->columns[$key];
33 | }
34 |
35 | if((substr($key, 0, 2) == '__') && array_key_exists(substr($key, 2), $this->columns)) {
36 | return htmlspecialchars($this->columns[substr($key, 2)]);
37 | }
38 |
39 | $trace = debug_backtrace();
40 | trigger_error("Undefined property via DBObject::__get(): $key in {$trace[0]['file']} on line {$trace[0]['line']}", E_USER_NOTICE);
41 | return null;
42 | }
43 |
44 | public function __set($key, $value)
45 | {
46 | if(array_key_exists($key, $this->columns)) {
47 | $this->columns[$key] = $value;
48 | }
49 |
50 | return $value; // Seriously.
51 | }
52 |
53 | public function __unset($key)
54 | {
55 | unset($this->columns[$key]);
56 | }
57 |
58 | public function __isset($key)
59 | {
60 | return array_key_exists($key, $this->columns);
61 | }
62 |
63 | public function select($id, $column = null, $order_by = null, $sort_direction = 'DESC')
64 | {
65 | $db = Database::getDatabase();
66 |
67 | if(is_null($column)) $column = $this->idColumnName;
68 | $column = $db->escape($column);
69 |
70 | if(isset($order_by) && isset($sort_direction)) {
71 | $db->query("SELECT * FROM `{$this->tableName}` WHERE `$column` = :id ORDER BY `$order_by` $sort_direction LIMIT 1", array('id' => $id));
72 | } else {
73 | $db->query("SELECT * FROM `{$this->tableName}` WHERE `$column` = :id LIMIT 1", array('id' => $id));
74 | }
75 |
76 | if($db->hasRows())
77 | {
78 | $row = $db->getRow();
79 | $this->load($row);
80 | return true;
81 | }
82 |
83 | return false;
84 | }
85 |
86 | public function ok()
87 | {
88 | return !is_null($this->id);
89 | }
90 |
91 | public function save()
92 | {
93 | if(is_null($this->id))
94 | $this->insert();
95 | else
96 | $this->update();
97 | return $this->id;
98 | }
99 |
100 | public function insert($cmd = 'INSERT INTO')
101 | {
102 | $db = Database::getDatabase();
103 |
104 | if(count($this->columns) == 0) return false;
105 |
106 | $data = array();
107 | foreach($this->columns as $k => $v) {
108 | if(isset($v) && !is_null($v)) {
109 | if(is_bool($v)) {
110 | $data[$k] = $v ? 'TRUE' : 'FALSE';
111 | } else if(is_int($v) || is_float($v)) {
112 | $data[$k] = "$v";
113 | } else if(is_string($v)) {
114 | $data[$k] = $db->quote($v);
115 | }
116 | } else {
117 | $data[$k] = 'NULL';
118 | }
119 | }
120 |
121 | $columns = '`' . implode('`, `', array_keys($data)) . '`';
122 | $values = implode(',', $data);
123 |
124 | $db->query("$cmd `{$this->tableName}` ($columns) VALUES ($values)");
125 | $this->id = $db->insertID();
126 | return $this->id;
127 | }
128 |
129 | public function replace()
130 | {
131 | return $this->delete() && $this->insert();
132 | }
133 |
134 | public function update()
135 | {
136 | if(is_null($this->id)) return false;
137 |
138 | $db = Database::getDatabase();
139 |
140 | if(count($this->columns) == 0) return;
141 |
142 | $data = array();
143 | foreach($this->columns as $k => $v) {
144 | if(isset($v) && !is_null($v)) {
145 | if(is_bool($v)) {
146 | $data[$k] = $v ? 'TRUE' : 'FALSE';
147 | } else if(is_int($v) || is_float($v)) {
148 | $data[$k] = "$v";
149 | } else if(is_string($v)) {
150 | $data[$k] = $db->quote($v);
151 | }
152 | } else {
153 | $data[$k] = 'NULL';
154 | }
155 | }
156 |
157 | $sql = "UPDATE {$this->tableName} SET ";
158 | foreach($data as $k => $v) {
159 | $sql .= "`$k` = $v,";
160 | }
161 | $sql[strlen($sql) - 1] = ' ';
162 |
163 | $sql .= " WHERE `{$this->idColumnName}` = " . $db->quote($this->id);
164 | $db->query($sql);
165 |
166 | return $db->affectedRows();
167 | }
168 |
169 | public function delete()
170 | {
171 | if(is_null($this->id)) return false;
172 | $db = Database::getDatabase();
173 | $db->query("DELETE FROM `{$this->tableName}` WHERE `{$this->idColumnName}` = :id LIMIT 1", array('id' => $this->id));
174 | return $db->affectedRows();
175 | }
176 |
177 | public function load($row)
178 | {
179 | foreach($row as $k => $v)
180 | {
181 | if($k == $this->idColumnName)
182 | $this->id = $v;
183 | elseif(array_key_exists($k, $this->columns))
184 | $this->columns[$k] = $v;
185 | }
186 | }
187 |
188 | // Grabs a large block of instantiated $class_name objects from the database using only one query.
189 | public static function glob($sql = null, $extra_columns = array())
190 | {
191 | $db = Database::getDatabase();
192 |
193 | // Make sure the class exists before we instantiate it...
194 | $class_name = get_called_class();
195 | if(!class_exists($class_name)) {
196 | return false;
197 | }
198 |
199 | $tmp_obj = new $class_name;
200 |
201 | // Also, it needs to be a subclass of DBObject...
202 | if(!is_subclass_of($tmp_obj, 'DBObject')) {
203 | return false;
204 | }
205 |
206 | if(is_null($sql)) {
207 | $sql = "SELECT * FROM `{$tmp_obj->tableName}`";
208 | }
209 |
210 | $objs = array();
211 | $rows = $db->getRows($sql);
212 | foreach($rows as $row)
213 | {
214 | $o = new $class_name;
215 | $o->load($row);
216 | $objs[$o->id] = $o;
217 |
218 | foreach($extra_columns as $c)
219 | {
220 | $o->addColumn($c);
221 | $o->$c = isset($row[$c]) ? $row[$c] : null;
222 | }
223 | }
224 | return $objs;
225 | }
226 |
227 | public function addColumn($key, $val = null)
228 | {
229 | if(!in_array($key, array_keys($this->columns)))
230 | $this->columns[$key] = $val;
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/includes/class.database.php:
--------------------------------------------------------------------------------
1 | host = $Config->dbHost;
24 | $this->name = $Config->dbName;
25 | $this->username = $Config->dbUsername;
26 | $this->password = $Config->dbPassword;
27 | $this->dieOnError = $Config->dbDieOnError;
28 |
29 | $this->db = false;
30 | $this->queries = array();
31 |
32 | if($connect === true)
33 | $this->connect();
34 | }
35 |
36 | // Waiting (not so) patiently for 5.3.0...
37 | public static function __callStatic($name, $args)
38 | {
39 | return self::$me->__call($name, $args);
40 | }
41 |
42 | // Get Singleton object
43 | public static function getDatabase($connect = true)
44 | {
45 | if(is_null(self::$me))
46 | self::$me = new Database($connect);
47 | return self::$me;
48 | }
49 |
50 | // Do we have a valid database connection?
51 | public function isConnected()
52 | {
53 | return is_object($this->db);
54 | }
55 |
56 | // Do we have a valid database connection and have we selected a database?
57 | public function databaseSelected()
58 | {
59 | if(!$this->isConnected()) return false;
60 | $result = mysqli_query($this->db, "SHOW TABLES");
61 | return is_object($result);
62 | }
63 |
64 | public function connect()
65 | {
66 | $this->db = mysqli_connect($this->host, $this->username, $this->password, $this->name) or $this->notify();
67 | if($this->db === false) return false;
68 | return $this->isConnected();
69 | }
70 |
71 | public function query($sql, $args_to_prepare = null, $exception_on_missing_args = true)
72 | {
73 | if(!$this->isConnected()) $this->connect();
74 |
75 | // Allow for prepared arguments. Example:
76 | // query("SELECT * FROM table WHERE id = :id", array('id' => $some_val));
77 | if(is_array($args_to_prepare))
78 | {
79 | foreach($args_to_prepare as $name => $val)
80 | {
81 | $val = $this->quote($val);
82 | $sql = str_replace(":$name", $val, $sql, $count);
83 | if($exception_on_missing_args && (0 == $count))
84 | throw new Exception(":$name was not found in prepared SQL query.");
85 | }
86 | }
87 |
88 | $this->queries[] = $sql;
89 | $this->result = mysqli_query($this->db, $sql) or $this->notify();
90 | return $this->result;
91 | }
92 |
93 | // Returns the number of rows.
94 | // You can pass in nothing, a string, or a db result
95 | public function numRows($arg = null)
96 | {
97 | $result = $this->resulter($arg);
98 | return ($result !== false) ? mysqli_num_rows($result) : false;
99 | }
100 |
101 | // Returns true / false if the result has one or more rows
102 | public function hasRows($arg = null)
103 | {
104 | $result = $this->resulter($arg);
105 | return is_object($result) && (mysqli_num_rows($result) > 0);
106 | }
107 |
108 | // Returns the number of rows affected by the previous operation
109 | public function affectedRows()
110 | {
111 | if(!$this->isConnected()) return false;
112 | return mysqli_affected_rows($this->db);
113 | }
114 |
115 | // Returns the auto increment ID generated by the previous insert statement
116 | public function insertID()
117 | {
118 | if(!$this->isConnected()) return false;
119 | $id = mysqli_insert_id($this->db);
120 | if($id === 0 || $id === false)
121 | return false;
122 | else
123 | return $id;
124 | }
125 |
126 | // Returns a single value.
127 | // You can pass in nothing, a string, or a db result
128 | public function getValue($arg = null)
129 | {
130 | $result = $this->resulter($arg);
131 | if($this->hasRows($result))
132 | {
133 | $row = mysqli_fetch_row($result);
134 | return $row[0];
135 | }
136 | else
137 | {
138 | return false;
139 | }
140 | }
141 |
142 | // Returns an array of the first value in each row.
143 | // You can pass in nothing, a string, or a db result
144 | public function getValues($arg = null)
145 | {
146 | $result = $this->resulter($arg);
147 | if(!$this->hasRows($result)) return array();
148 |
149 | $values = array();
150 | mysqli_data_seek($result, 0);
151 | while($row = mysqli_fetch_row($result))
152 | $values[] = array_pop($row);
153 | return $values;
154 | }
155 |
156 | // Returns the first row.
157 | // You can pass in nothing, a string, or a db result
158 | public function getRow($arg = null)
159 | {
160 | $result = $this->resulter($arg);
161 | return $this->hasRows() ? mysqli_fetch_array($result, MYSQLI_ASSOC) : false;
162 | }
163 |
164 | // Returns an array of all the rows.
165 | // You can pass in nothing, a string, or a db result
166 | public function getRows($arg = null)
167 | {
168 | $result = $this->resulter($arg);
169 | if(!$this->hasRows($result)) return array();
170 |
171 | $rows = array();
172 | mysqli_data_seek($result, 0);
173 | while($row = mysqli_fetch_array($result, MYSQLI_BOTH))
174 | $rows[] = $row;
175 | return $rows;
176 | }
177 |
178 | // Escapes a value and wraps it in single quotes.
179 | public function quote($var)
180 | {
181 | if(!$this->isConnected()) $this->connect();
182 | return "'" . $this->escape($var) . "'";
183 | }
184 |
185 | // Escapes a value.
186 | public function escape($var)
187 | {
188 | if(!$this->isConnected()) $this->connect();
189 | return mysqli_real_escape_string($this->db, $var);
190 | }
191 |
192 | public function numQueries()
193 | {
194 | return count($this->queries);
195 | }
196 |
197 | public function lastQuery()
198 | {
199 | if($this->numQueries() > 0)
200 | return $this->queries[$this->numQueries() - 1];
201 | else
202 | return false;
203 | }
204 |
205 | private function notify()
206 | {
207 | $err_msg = mysqli_error($this->db);
208 | error_log($err_msg);
209 |
210 | if($this->dieOnError === true)
211 | {
212 | echo "Database Error: $err_msg
";
213 | echo "Last Query: " . $this->lastQuery() . "
";
214 | echo "";
215 | debug_print_backtrace();
216 | echo " ";
217 | exit;
218 | }
219 |
220 | if(is_string($this->redirect))
221 | {
222 | header("Location: {$this->redirect}");
223 | exit;
224 | }
225 | }
226 |
227 | // Takes nothing, a MySQL result, or a query string and returns
228 | // the correspsonding MySQL result resource or false if none available.
229 | private function resulter($arg = null)
230 | {
231 | if(is_null($arg) && is_object($this->result))
232 | return $this->result;
233 | elseif(is_object($arg))
234 | return $arg;
235 | elseif(is_string($arg))
236 | {
237 | $this->query($arg);
238 | if(is_object($this->result))
239 | return $this->result;
240 | else
241 | return false;
242 | }
243 | else
244 | return false;
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/includes/class.spferror.php:
--------------------------------------------------------------------------------
1 | errors = array();
13 | $this->style = $style;
14 | }
15 |
16 | // Get Singleton object
17 | public static function getError()
18 | {
19 | if(is_null(self::$me))
20 | self::$me = new SPFError();
21 | return self::$me;
22 | }
23 |
24 | // Returns an unordered list of error messages
25 | public function __tostring()
26 | {
27 | return $this->alert();
28 | }
29 |
30 | // Returns true if there are no errors
31 | public function ok()
32 | {
33 | return count($this->errors) == 0;
34 | }
35 |
36 | // Manually add an error
37 | public function add($id, $msg)
38 | {
39 | if(isset($this->errors[$id]) && !is_array($this->errors[$id]))
40 | $this->errors[$id] = array($msg);
41 | else
42 | $this->errors[$id][] = $msg;
43 | }
44 |
45 | // Delete all errors associated with an element's id
46 | public function delete($id)
47 | {
48 | unset($this->errors[$id]);
49 | }
50 |
51 | // Returns the error message associated with an element.
52 | // This may return a string or an array - so be sure to test before echoing!
53 | public function msg($id)
54 | {
55 | return $this->errors[$id];
56 | }
57 |
58 | // Outputs the CSS to style the error elements
59 | public function css($header = true)
60 | {
61 | $out = '';
62 | if(count($this->errors) > 0)
63 | {
64 | if($header) $out .= '';
67 | }
68 | echo $out;
69 | }
70 |
71 | // Returns an unordered list of error messages
72 | public function ul($class = 'warn')
73 | {
74 | if(count($this->errors) == 0) return '';
75 |
76 | $out = "";
77 | foreach($this->errors as $error)
78 | $out .= "" . implode(" ", $error) . " ";
79 | $out .= " ";
80 |
81 | return $out;
82 | }
83 |
84 | // Returns error alerts
85 | public function alert()
86 | {
87 | if(count($this->errors) == 0) return '';
88 | $out = '';
89 | foreach($this->errors as $error)
90 | $out .= "" . implode(' ', $error) . "
";
91 |
92 | return $out;
93 | }
94 |
95 | // Below are a collection of tests for error conditions in your user's input...
96 | // Be sure to customize these to suit your app's needs. Especially the error messages.
97 |
98 | // Is the (string) value empty?
99 | public function blank($val, $id, $name = null)
100 | {
101 | if(trim($val) == '')
102 | {
103 | if(is_null($name)) $name = ucwords($id);
104 | $this->add($id, "$name cannot be left blank.");
105 | return false;
106 | }
107 |
108 | return true;
109 | }
110 |
111 | // Is a number between a given range? (inclusive)
112 | public function range($val, $lower, $upper, $id, $name = null)
113 | {
114 | if($val < $lower || $val > $upper)
115 | {
116 | if(is_null($name)) $name = ucwords($id);
117 | $this->add($id, "$name must be between $lower and $upper.");
118 | return false;
119 | }
120 |
121 | return true;
122 | }
123 |
124 | // Is a string an appropriate length?
125 | public function length($val, $lower, $upper, $id, $name = null)
126 | {
127 | if(strlen($val) < $lower)
128 | {
129 | if(is_null($name)) $name = ucwords($id);
130 | $this->add($id, "$name must be at least $lower characters.");
131 | return false;
132 | }
133 | elseif(strlen($val) > $upper)
134 | {
135 | if(is_null($name)) $name = ucwords($id);
136 | $this->add($id, "$name cannot be more than $upper characters long.");
137 | return false;
138 | }
139 |
140 | return true;
141 | }
142 |
143 | // Do the passwords match?
144 | public function passwords($pass1, $pass2, $id)
145 | {
146 | if($pass1 !== $pass2)
147 | {
148 | $this->add($id, 'The passwords you entered do not match.');
149 | return false;
150 | }
151 |
152 | return true;
153 | }
154 |
155 | // Does a value match a given regex?
156 | public function regex($val, $regex, $id, $msg)
157 | {
158 | if(preg_match($regex, $val) === 0)
159 | {
160 | $this->add($id, $msg);
161 | return false;
162 | }
163 |
164 | return true;
165 | }
166 |
167 | // Is an email address valid?
168 | public function email($val, $id = 'email')
169 | {
170 | if(!preg_match("/^.+@.+\..+$/i", $val))
171 | {
172 | $this->add($id, 'The email address you entered is not valid.');
173 | return false;
174 | }
175 |
176 | return true;
177 | }
178 |
179 | // Is a string a parseable and valid date?
180 | public function date($val, $id)
181 | {
182 | if(chkdate($val) === false)
183 | {
184 | $this->add($id, 'Please enter a valid date');
185 | return false;
186 | }
187 |
188 | return true;
189 | }
190 |
191 | // Is a birth date at least 18 years old?
192 | public function adult($val, $id)
193 | {
194 | if( dater($val) > ( (date('Y') - 18) . date('-m-d H:i:s') ) )
195 | {
196 | $this->add($id, 'You must be at least 18 years old.');
197 | return false;
198 | }
199 |
200 | return true;
201 | }
202 |
203 | // Is a string a valid phone number?
204 | public function phone($val, $id)
205 | {
206 | $val = preg_replace('/[^0-9]/', '', $val);
207 | if(strlen($val) != 7 && strlen($val) != 10)
208 | {
209 | $this->add($id, 'Please enter a valid 7 or 10 digit phone number.');
210 | return false;
211 | }
212 |
213 | return true;
214 | }
215 |
216 | // Did we get a successful file upload?
217 | // Typically, you'd pass in $_FILES['file']
218 | public function upload($val, $id)
219 | {
220 | if(!is_uploaded_file($val['tmp_name']) || !is_readable($val['tmp_name']))
221 | {
222 | $this->add($id, 'Your file was not uploaded successfully. Please try again.');
223 | return false;
224 | }
225 |
226 | return true;
227 | }
228 |
229 | // Valid 5 digit zip code?
230 | public function zip($val, $id, $name = null)
231 | {
232 | // From http://www.zend.com//code/codex.php?ozid=991&single=1
233 | $ranges = array(array('99500', '99929'), array('35000', '36999'), array('71600', '72999'), array('75502', '75505'), array('85000', '86599'), array('90000', '96199'), array('80000', '81699'), array('06000', '06999'), array('20000', '20099'), array('20200', '20599'), array('19700', '19999'), array('32000', '33999'), array('34100', '34999'), array('30000', '31999'), array('96700', '96798'), array('96800', '96899'), array('50000', '52999'), array('83200', '83899'), array('60000', '62999'), array('46000', '47999'), array('66000', '67999'), array('40000', '42799'), array('45275', '45275'), array('70000', '71499'), array('71749', '71749'), array('01000', '02799'), array('20331', '20331'), array('20600', '21999'), array('03801', '03801'), array('03804', '03804'), array('03900', '04999'), array('48000', '49999'), array('55000', '56799'), array('63000', '65899'), array('38600', '39799'), array('59000', '59999'), array('27000', '28999'), array('58000', '58899'), array('68000', '69399'), array('03000', '03803'), array('03809', '03899'), array('07000', '08999'), array('87000', '88499'), array('89000', '89899'), array('00400', '00599'), array('06390', '06390'), array('09000', '14999'), array('43000', '45999'), array('73000', '73199'), array('73400', '74999'), array('97000', '97999'), array('15000', '19699'), array('02800', '02999'), array('06379', '06379'), array('29000', '29999'), array('57000', '57799'), array('37000', '38599'), array('72395', '72395'), array('73300', '73399'), array('73949', '73949'), array('75000', '79999'), array('88501', '88599'), array('84000', '84799'), array('20105', '20199'), array('20301', '20301'), array('20370', '20370'), array('22000', '24699'), array('05000', '05999'), array('98000', '99499'), array('49936', '49936'), array('53000', '54999'), array('24700', '26899'), array('82000', '83199'));
234 | foreach($ranges as $r)
235 | {
236 | if($val >= $r[0] && $val <= $r[1])
237 | return true;
238 | }
239 |
240 | if(is_null($name)) $name = ucwords($id);
241 | $this->add($id, "Please enter a valid, 5-digit zip code.");
242 | return false;
243 | }
244 |
245 | // Test if string $val is a valid, decimal number.
246 | public function nan($val, $id, $name = null)
247 | {
248 | if(preg_match('/^-?[0-9]+(\.[0-9]+)?$/', $val) == 0)
249 | {
250 | if(is_null($name)) $name = ucwords($id);
251 | $this->add($id, "$name must be a number.");
252 | return false;
253 | }
254 | return true;
255 | }
256 |
257 | // Valid URL?
258 | // This is hardly perfect, but it's good enough for now...
259 | // TODO: Make URL validation more robust
260 | public function url($val, $id, $name = null)
261 | {
262 | $info = @parse_url($val);
263 | if(($info === false) || ($info['scheme'] != 'http' && $info['scheme'] != 'https') || ($info['host'] == ''))
264 | {
265 | if(is_null($name)) $name = ucwords($id);
266 | $this->add($id, "$name is not a valid URL.");
267 | return false;
268 | }
269 | return true;
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/includes/class.auth.php:
--------------------------------------------------------------------------------
1 | id = null;
20 | $this->nid = null;
21 | $this->username = null;
22 | $this->user = null;
23 | $this->loggedIn = false;
24 | $this->expiryDate = mktime(0, 0, 0, 6, 2, 2037);
25 | $this->user = new User();
26 | }
27 |
28 | public static function getAuth()
29 | {
30 | if(is_null(self::$me))
31 | {
32 | self::$me = new Auth();
33 | self::$me->init();
34 | }
35 | return self::$me;
36 | }
37 |
38 | public function init()
39 | {
40 | $this->setACookie();
41 | $this->loggedIn = $this->attemptCookieLogin();
42 | }
43 |
44 | public function sendTwoStep($id_or_username)
45 | {
46 | if(ctype_digit($id_or_username)) {
47 | $u = new User($id_or_username);
48 | } else {
49 | $u = new User();
50 | $u->select($id_or_username, 'username');
51 | }
52 |
53 | if($u->ok())
54 | {
55 | $u->twostep = $this->generateNewTwoStep();
56 | $u->update();
57 | }
58 |
59 | // At this point you would send the 2FA code to the user somehow.
60 | // This could be via an email, text, etc. It's up to you to write
61 | // a function to deliver the code.
62 | // some_function($u->twostep);
63 | }
64 |
65 | public function login($username, $password, $twostep = null)
66 | {
67 | $this->loggedIn = false;
68 |
69 | $db = Database::getDatabase();
70 | $hashed_password = self::hashedPassword($password);
71 | $row = $db->getRow("SELECT * FROM users WHERE username = " . $db->quote($username) . " AND password = " . $db->quote($hashed_password));
72 |
73 | if($row === false)
74 | return false;
75 |
76 | if(Config::get('useTwoStepAuth')) {
77 | if($twostep !== $row['twostep']) {
78 | return false;
79 | }
80 | }
81 |
82 | $this->id = $row['id'];
83 | $this->nid = $row['nid'];
84 | $this->username = $row['username'];
85 | $this->user = new User();
86 | $this->user->id = $this->id;
87 | $this->user->load($row);
88 |
89 | $this->generateBCCookies();
90 |
91 | $this->loggedIn = true;
92 |
93 | return true;
94 | }
95 |
96 | public function logout()
97 | {
98 | $this->loggedIn = false;
99 | $this->clearCookies();
100 | $this->sendToLoginPage();
101 | }
102 |
103 | public function loggedIn()
104 | {
105 | return $this->loggedIn;
106 | }
107 |
108 | public function requireUser()
109 | {
110 | if(!$this->loggedIn())
111 | $this->sendToLoginPage();
112 | }
113 |
114 | public function requireAdmin()
115 | {
116 | if(!$this->loggedIn() || !$this->isAdmin())
117 | $this->sendToLoginPage();
118 | }
119 |
120 | public function isAdmin()
121 | {
122 | return ($this->user->level === 'admin');
123 | }
124 |
125 | public function generateNewTwoStep()
126 | {
127 | $twostep = '';
128 | while(strlen($twostep) < 6) {
129 | $twostep .= preg_replace('/[^0-9]/', '', sha1(microtime() . self::SALT));
130 | }
131 | $twostep = substr($twostep, 0, 6);
132 | return $twostep;
133 | }
134 |
135 | public function changeCurrentUsername($new_username)
136 | {
137 | $db = Database::getDatabase();
138 | srand(time());
139 | $this->user->nid = Auth::newNid();
140 | $this->nid = $this->user->nid;
141 | $this->user->username = $new_username;
142 | $this->username = $this->user->username;
143 | $this->user->update();
144 | $this->generateBCCookies();
145 | }
146 |
147 | public function changeCurrentPassword($new_password)
148 | {
149 | $db = Database::getDatabase();
150 | srand(time());
151 | $this->user->nid = self::newNid();
152 | $this->user->password = self::hashedPassword($new_password);
153 | $this->user->update();
154 | $this->nid = $this->user->nid;
155 | $this->generateBCCookies();
156 | }
157 |
158 | public static function changeUsername($id_or_username, $new_username)
159 | {
160 | if(ctype_digit($id_or_username))
161 | $u = new User($id_or_username);
162 | else
163 | {
164 | $u = new User();
165 | $u->select($id_or_username, 'username');
166 | }
167 |
168 | if($u->ok())
169 | {
170 | $u->username = $new_username;
171 | $u->update();
172 | }
173 | }
174 |
175 | public static function changePassword($id_or_username, $new_password)
176 | {
177 | if(ctype_digit($id_or_username))
178 | $u = new User($id_or_username);
179 | else
180 | {
181 | $u = new User();
182 | $u->select($id_or_username, 'username');
183 | }
184 |
185 | if($u->ok())
186 | {
187 | $u->nid = self::newNid();
188 | $u->password = self::hashedPassword($new_password);
189 | $u->update();
190 | }
191 | }
192 |
193 | public static function createNewUser($username, $password = null, $level = 'user')
194 | {
195 | if(is_null($password))
196 | $password = Auth::generateStrongPassword();
197 |
198 | srand(time());
199 | $u = new User();
200 | $u->username = $username;
201 | $u->nid = self::newNid();
202 | $u->password = self::hashedPassword($password);
203 | $u->level = $level;
204 | $u->insert();
205 | return $u;
206 | }
207 |
208 | public static function generateStrongPassword($length = 9, $add_dashes = false, $available_sets = 'luds')
209 | {
210 | $sets = array();
211 | if(strpos($available_sets, 'l') !== false)
212 | $sets[] = 'abcdefghjkmnpqrstuvwxyz';
213 | if(strpos($available_sets, 'u') !== false)
214 | $sets[] = 'ABCDEFGHJKMNPQRSTUVWXYZ';
215 | if(strpos($available_sets, 'd') !== false)
216 | $sets[] = '23456789';
217 | if(strpos($available_sets, 's') !== false)
218 | $sets[] = '!@#$%&*?';
219 |
220 | $all = '';
221 | $password = '';
222 | foreach($sets as $set)
223 | {
224 | $password .= $set[array_rand(str_split($set))];
225 | $all .= $set;
226 | }
227 |
228 | $all = str_split($all);
229 | for($i = 0; $i < $length - count($sets); $i++)
230 | $password .= $all[array_rand($all)];
231 |
232 | $password = str_shuffle($password);
233 |
234 | if(!$add_dashes)
235 | return $password;
236 |
237 | $dash_len = floor(sqrt($length));
238 | $dash_str = '';
239 | while(strlen($password) > $dash_len)
240 | {
241 | $dash_str .= substr($password, 0, $dash_len) . '-';
242 | $password = substr($password, $dash_len);
243 | }
244 | $dash_str .= $password;
245 | return $dash_str;
246 | }
247 |
248 | public function impersonateUser($id_or_username)
249 | {
250 | if(ctype_digit($id_or_username))
251 | $u = new User($id_or_username);
252 | else
253 | {
254 | $u = new User();
255 | $u->select($id_or_username, 'username');
256 | }
257 |
258 | if(!$u->ok()) return false;
259 |
260 | $this->id = $u->id;
261 | $this->nid = $u->nid;
262 | $this->username = $u->username;
263 | $this->user = $u;
264 | $this->generateBCCookies();
265 |
266 | return true;
267 | }
268 |
269 | private function attemptCookieLogin()
270 | {
271 | if(!isset($_COOKIE['A']) || !isset($_COOKIE['B']) || !isset($_COOKIE['C']))
272 | return false;
273 |
274 | $ccookie = base64_decode(str_rot13($_COOKIE['C']));
275 | if($ccookie === false)
276 | return false;
277 |
278 | $c = array();
279 | parse_str($ccookie, $c);
280 | if(!isset($c['n']) || !isset($c['l']))
281 | return false;
282 |
283 | $bcookie = base64_decode(str_rot13($_COOKIE['B']));
284 | if($bcookie === false)
285 | return false;
286 |
287 | $b = array();
288 | parse_str($bcookie, $b);
289 | if(!isset($b['s']) || !isset($b['x']))
290 | return false;
291 |
292 | if($b['x'] < time())
293 | return false;
294 |
295 | $computed_sig = sha1(str_rot13(base64_encode($ccookie)) . $b['x'] . self::SALT);
296 | if($computed_sig != $b['s'])
297 | return false;
298 |
299 | $nid = base64_decode($c['n']);
300 | if($nid === false)
301 | return false;
302 |
303 | $db = Database::getDatabase();
304 |
305 | // We SELECT * so we can load the full user record into the user DBObject later
306 | $row = $db->getRow('SELECT * FROM users WHERE nid = ' . $db->quote($nid));
307 | if($row === false)
308 | return false;
309 |
310 | $this->id = $row['id'];
311 | $this->nid = $row['nid'];
312 | $this->username = $row['username'];
313 | $this->user = new User();
314 | $this->user->id = $this->id;
315 | $this->user->load($row);
316 |
317 | return true;
318 | }
319 |
320 | private function setACookie()
321 | {
322 | if(!isset($_COOKIE['A']))
323 | {
324 | srand(time());
325 | $a = sha1(rand() . microtime());
326 | setcookie('A', $a, $this->expiryDate, '/', Config::get('authDomain'));
327 | }
328 | }
329 |
330 | private function generateBCCookies()
331 | {
332 | $c = '';
333 | $c .= 'n=' . base64_encode($this->nid) . '&';
334 | $c .= 'l=' . str_rot13($this->username) . '&';
335 | $c = base64_encode($c);
336 | $c = str_rot13($c);
337 |
338 | $sig = sha1($c . $this->expiryDate . self::SALT);
339 | $b = "x={$this->expiryDate}&s=$sig";
340 | $b = base64_encode($b);
341 | $b = str_rot13($b);
342 |
343 | setcookie('B', $b, $this->expiryDate, '/', Config::get('authDomain'));
344 | setcookie('C', $c, $this->expiryDate, '/', Config::get('authDomain'));
345 | }
346 |
347 | private function clearCookies()
348 | {
349 | setcookie('B', '', time() - 3600, '/', Config::get('authDomain'));
350 | setcookie('C', '', time() - 3600, '/', Config::get('authDomain'));
351 | }
352 |
353 | private function sendToLoginPage()
354 | {
355 | $url = $this->loginUrl;
356 |
357 | $full_url = full_url();
358 | if(strpos($full_url, 'logout') === false)
359 | {
360 | $url .= '?r=' . $full_url;
361 | }
362 |
363 | redirect($url);
364 | }
365 |
366 | private static function hashedPassword($password)
367 | {
368 | return sha1($password . self::SALT);
369 | }
370 |
371 | private static function newNid()
372 | {
373 | srand(time());
374 | return sha1(rand() . microtime());
375 | }
376 | }
377 |
--------------------------------------------------------------------------------
/includes/functions.inc.php:
--------------------------------------------------------------------------------
1 | setTimezone(new DateTimeZone('UTC'));
10 | return $dt->format('Y-m-d H:i:s');
11 | }
12 |
13 | function twitterfy($str)
14 | {
15 | // Via http://www.snipe.net/2009/09/php-twitter-clickable-links/
16 | $str = preg_replace("#(^|[\n ])([\w]+?://[\w]+[^ \"\n\r\t< ]*)#", "\\1\\2 ", $str);
17 | $str = preg_replace("#(^|[\n ])((www|ftp)\.[^ \"\t\n\r< ]*)#", "\\1\\2 ", $str);
18 | $str = preg_replace("/@(\w+)/", "@\\1 ", $str);
19 | $str = preg_replace("/#(\w+)/", "#\\1 ", $str);
20 | return $str;
21 | }
22 |
23 | function set_option($key, $val)
24 | {
25 | $db = Database::getDatabase();
26 | $db->query('REPLACE INTO options (`key`, `value`) VALUES (:key, :value)', array('key' => $key, 'value' => $val));
27 | }
28 |
29 | function get_option($key, $default = null)
30 | {
31 | $db = Database::getDatabase();
32 | $db->query('SELECT `value` FROM options WHERE `key` = :key', array('key' => $key));
33 | if($db->hasRows())
34 | return $db->getValue();
35 | else
36 | return $default;
37 | }
38 |
39 | function printr($var)
40 | {
41 | $output = print_r($var, true);
42 | $output = str_replace("\n", " ", $output);
43 | $output = str_replace(' ', ' ', $output);
44 | echo "$output
";
45 | }
46 |
47 | // Formats a given number of seconds into proper mm:ss format
48 | function format_time($seconds)
49 | {
50 | return floor($seconds / 60) . ':' . str_pad($seconds % 60, 2, '0');
51 | }
52 |
53 | // Given a string such as "comment_123" or "id_57", it returns the final, numeric id.
54 | function split_id($str)
55 | {
56 | return match('/[_-]([0-9]+)$/', $str, 1);
57 | }
58 |
59 | // Creates a friendly URL slug from a string
60 | function slugify($str)
61 | {
62 | $str = preg_replace('/[^a-zA-Z0-9 -]/', '', $str);
63 | $str = strtolower(str_replace(' ', '-', trim($str)));
64 | $str = preg_replace('/-+/', '-', $str);
65 | return $str;
66 | }
67 |
68 | // Computes the *full* URL of the current page (protocol, server, path, query parameters, etc)
69 | function full_url()
70 | {
71 | $s = empty($_SERVER['HTTPS']) ? '' : ($_SERVER['HTTPS'] == 'on') ? 's' : '';
72 | $protocol = substr(strtolower($_SERVER['SERVER_PROTOCOL']), 0, strpos(strtolower($_SERVER['SERVER_PROTOCOL']), '/')) . $s;
73 | $port = ($_SERVER['SERVER_PORT'] == '80') ? '' : (":".$_SERVER['SERVER_PORT']);
74 | return $protocol . "://" . $_SERVER['HTTP_HOST'] . $port . $_SERVER['REQUEST_URI'];
75 | }
76 |
77 | // Returns an English representation of a past date within the last month
78 | // Graciously stolen from http://ejohn.org/files/pretty.js
79 | function time2str($ts)
80 | {
81 | if(!ctype_digit($ts)) {
82 | $ts = strtotime($ts);
83 | }
84 |
85 | $diff = time() - $ts;
86 | if($diff == 0) {
87 | return 'now';
88 | } else if($diff > 0) {
89 | $day_diff = floor($diff / 86400);
90 | if($day_diff == 0)
91 | {
92 | if($diff < 60) return 'just now';
93 | if($diff < 120) return '1 minute ago';
94 | if($diff < 3600) return floor($diff / 60) . ' minutes ago';
95 | if($diff < 7200) return '1 hour ago';
96 | if($diff < 86400) return floor($diff / 3600) . ' hours ago';
97 | }
98 | if($day_diff == 1) return 'Yesterday';
99 | if($day_diff < 7) return $day_diff . ' days ago';
100 | if($day_diff < 31) return ceil($day_diff / 7) . ' weeks ago';
101 | if($day_diff < 60) return 'last month';
102 | $ret = date('F Y', $ts);
103 | return ($ret == 'December 1969') ? '' : $ret;
104 | } else {
105 | $diff = abs($diff);
106 | $day_diff = floor($diff / 86400);
107 | if($day_diff == 0)
108 | {
109 | if($diff < 120) return 'in a minute';
110 | if($diff < 3600) return 'in ' . floor($diff / 60) . ' minutes';
111 | if($diff < 7200) return 'in an hour';
112 | if($diff < 86400) return 'in ' . floor($diff / 3600) . ' hours';
113 | }
114 | if($day_diff == 1) return 'Tomorrow';
115 | if($day_diff < 4) return date('l', $ts);
116 | if($day_diff < 7 + (7 - date('w'))) return 'next week';
117 | if(ceil($day_diff / 7) < 4) return 'in ' . ceil($day_diff / 7) . ' weeks';
118 | if(date('n', $ts) == date('n') + 1) return 'next month';
119 | $ret = date('F Y', $ts);
120 | return ($ret == 'December 1969') ? '' : $ret;
121 | }
122 | }
123 |
124 | // Returns an array representation of the given calendar month.
125 | // The array values are timestamps which allow you to easily format
126 | // and manipulate the dates as needed.
127 | function calendar($month = null, $year = null)
128 | {
129 | if(is_null($month)) $month = date('n');
130 | if(is_null($year)) $year = date('Y');
131 |
132 | $first = mktime(0, 0, 0, $month, 1, $year);
133 | $last = mktime(23, 59, 59, $month, date('t', $first), $year);
134 |
135 | $start = $first - (86400 * date('w', $first));
136 | $stop = $last + (86400 * (7 - date('w', $first)));
137 |
138 | $out = array();
139 | while($start < $stop)
140 | {
141 | $week = array();
142 | if($start > $last) break;
143 | for($i = 0; $i < 7; $i++)
144 | {
145 | $week[$i] = $start;
146 | $start += 86400;
147 | }
148 | $out[] = $week;
149 | }
150 |
151 | return $out;
152 | }
153 |
154 | // Processes mod_rewrite URLs into key => value pairs
155 | // See .htacess for more info.
156 | function pick_off($grab_first = false, $sep = '/')
157 | {
158 | $ret = array();
159 | $arr = explode($sep, trim($_SERVER['REQUEST_URI'], $sep));
160 | if($grab_first) $ret[0] = array_shift($arr);
161 | while(count($arr) > 0) {
162 | $ret[array_shift($arr)] = array_shift($arr);
163 | }
164 | return (count($ret) > 0) ? $ret : false;
165 | }
166 |
167 | // Creates a list of s from the given database table.
168 | // table name, column to use as value, column(s) to use as text, default value(s) to select (can accept an array of values), extra sql to limit results
169 | function get_options($table, $val, $text, $default = null, $sql = '')
170 | {
171 | $db = Database::getDatabase(true);
172 | $out = '';
173 |
174 | $table = $db->escape($table);
175 | $rows = $db->getRows("SELECT * FROM `$table` $sql");
176 | foreach($rows as $row)
177 | {
178 | $the_text = '';
179 | if(!is_array($text)) $text = array($text); // Allows you to concat multiple fields for display
180 | foreach($text as $t)
181 | $the_text .= $row[$t] . ' ';
182 | $the_text = htmlspecialchars(trim($the_text));
183 |
184 | if(!is_null($default) && $row[$val] == $default)
185 | $out .= ' ' . $the_text . ' ';
186 | elseif(is_array($default) && in_array($row[$val],$default))
187 | $out .= '' . $the_text . ' ';
188 | else
189 | $out .= '' . $the_text . ' ';
190 | }
191 | return $out;
192 | }
193 |
194 | // More robust strict date checking for string representations
195 | function chkdate($str)
196 | {
197 | $info = date_parse($str);
198 | if($info !== false && $info['error_count'] == 0)
199 | {
200 | if(checkdate($info['month'], $info['day'], $info['year'])) {
201 | return true;
202 | }
203 | }
204 |
205 | return false;
206 | }
207 |
208 | // Converts a date/timestamp into the specified format
209 | function dater($date = null, $format = null)
210 | {
211 | if(is_null($format)) {
212 | $format = 'Y-m-d H:i:s';
213 | }
214 |
215 | if(is_null($date)) {
216 | $date = time();
217 | }
218 |
219 | // if $date contains only numbers, treat it as a timestamp
220 | if(ctype_digit($date) === true)
221 | return date($format, $date);
222 | else
223 | return date($format, strtotime($date));
224 | }
225 |
226 | // Formats a phone number as (xxx) xxx-xxxx or xxx-xxxx depending on the length.
227 | function format_phone($phone)
228 | {
229 | $phone = preg_replace("/[^0-9]/", '', $phone);
230 |
231 | if(strlen($phone) == 7)
232 | return preg_replace("/([0-9]{3})([0-9]{4})/", "$1-$2", $phone);
233 | elseif(strlen($phone) == 10)
234 | return preg_replace("/([0-9]{3})([0-9]{3})([0-9]{4})/", "($1) $2-$3", $phone);
235 | else
236 | return $phone;
237 | }
238 |
239 | // Outputs hour, minute, am/pm dropdown boxes
240 | function hourmin($hid = 'hour', $mid = 'minute', $pid = 'ampm', $hval = null, $mval = null, $pval = null)
241 | {
242 | // Dumb hack to let you just pass in a timestamp instead
243 | if(func_num_args() == 1)
244 | {
245 | list($hval, $mval, $pval) = explode(' ', date('g i a', strtotime($hid)));
246 | $hid = 'hour';
247 | $mid = 'minute';
248 | $aid = 'ampm';
249 | }
250 | else
251 | {
252 | if(is_null($hval)) $hval = date('h');
253 | if(is_null($mval)) $mval = date('i');
254 | if(is_null($pval)) $pval = date('a');
255 | }
256 |
257 | $hours = array(12, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11);
258 | $out = "";
259 | foreach($hours as $hour)
260 | if(intval($hval) == intval($hour)) $out .= "$hour ";
261 | else $out .= "$hour ";
262 | $out .= " ";
263 |
264 | $minutes = array('00', 15, 30, 45);
265 | $out .= "";
266 | foreach($minutes as $minute)
267 | if(intval($mval) == intval($minute)) $out .= "$minute ";
268 | else $out .= "$minute ";
269 | $out .= " ";
270 |
271 | $out .= "";
272 | $out .= "am ";
273 | if($pval == 'pm') $out .= "pm ";
274 | else $out .= "pm ";
275 | $out .= " ";
276 |
277 | return $out;
278 | }
279 |
280 | // Returns the HTML for a month, day, and year dropdown boxes.
281 | // You can set the default date by passing in a timestamp OR a parseable date string.
282 | // $prefix_ will be appened to the name/id's of each dropdown, allowing for multiple calls in the same form.
283 | // $output_format lets you specify which dropdowns appear and in what order.
284 | function mdy($date = null, $prefix = null, $output_format = 'm d y')
285 | {
286 | if(is_null($date)) $date = time();
287 | if(!ctype_digit($date)) $date = strtotime($date);
288 | if(!is_null($prefix)) $prefix .= '_';
289 | list($yval, $mval, $dval) = explode(' ', date('Y n j', $date));
290 |
291 | $month_dd = "";
292 | for($i = 1; $i <= 12; $i++)
293 | {
294 | $selected = ($mval == $i) ? ' selected="selected"' : '';
295 | $month_dd .= "" . date('F', mktime(0, 0, 0, $i, 1, 2000)) . " ";
296 | }
297 | $month_dd .= " ";
298 |
299 | $day_dd = "";
300 | for($i = 1; $i <= 31; $i++)
301 | {
302 | $selected = ($dval == $i) ? ' selected="selected"' : '';
303 | $day_dd .= "$i ";
304 | }
305 | $day_dd .= " ";
306 |
307 | $year_dd = "";
308 | for($i = date('Y'); $i < date('Y') + 10; $i++)
309 | {
310 | $selected = ($yval == $i) ? ' selected="selected"' : '';
311 | $year_dd .= "$i ";
312 | }
313 | $year_dd .= " ";
314 |
315 | $trans = array('m' => $month_dd, 'd' => $day_dd, 'y' => $year_dd);
316 | return strtr($output_format, $trans);
317 | }
318 |
319 | // Redirects user to $url
320 | function redirect($url = null)
321 | {
322 | if(is_null($url)) $url = $_SERVER['PHP_SELF'];
323 | header("Location: $url");
324 | exit();
325 | }
326 |
327 | // Ensures $str ends with a single /
328 | function slash($str)
329 | {
330 | return rtrim($str, '/') . '/';
331 | }
332 |
333 | // Ensures $str DOES NOT end with a /
334 | function unslash($str)
335 | {
336 | return rtrim($str, '/');
337 | }
338 |
339 | // Returns an array of the values of the specified column from a multi-dimensional array
340 | function gimme($arr, $key = null, $mod = 1)
341 | {
342 | if(is_null($key))
343 | $key = current(array_keys($arr));
344 |
345 | if(is_numeric($key)) {
346 | $keys = array_keys($arr[0]);
347 | $key = $keys[$key];
348 | }
349 |
350 | $out = array();
351 | $i = 0;
352 | foreach($arr as $k => $a)
353 | {
354 | if($i % $mod == 0)
355 | $out[] = $a[$key];
356 | $i++;
357 | }
358 |
359 | return $out;
360 | }
361 |
362 | // Returns the first $num words of $str
363 | function max_words($str, $num, $suffix = '')
364 | {
365 | $words = explode(' ', $str);
366 | if(count($words) < $num)
367 | return $str;
368 | else
369 | return implode(' ', array_slice($words, 0, $num)) . $suffix;
370 | }
371 |
372 | // Serves an external document for download as an HTTP attachment.
373 | function download_document($filename, $mimetype = 'application/octet-stream')
374 | {
375 | if(!file_exists($filename) || !is_readable($filename)) return false;
376 | $base = basename($filename);
377 | header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
378 | header("Content-Disposition: attachment; filename=$base");
379 | header("Content-Length: " . filesize($filename));
380 | header("Content-Type: $mimetype");
381 | readfile($filename);
382 | exit();
383 | }
384 |
385 | // Retrieves the filesize of a remote file.
386 | function remote_filesize($url, $user = null, $pw = null)
387 | {
388 | $ch = curl_init($url);
389 | curl_setopt($ch, CURLOPT_HEADER, 1);
390 | curl_setopt($ch, CURLOPT_NOBODY, 1);
391 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
392 |
393 | if(!is_null($user) && !is_null($pw))
394 | {
395 | $headers = array('Authorization: Basic ' . base64_encode("$user:$pw"));
396 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
397 | }
398 |
399 | $head = curl_exec($ch);
400 | curl_close($ch);
401 |
402 | preg_match('/Content-Length:\s([0-9].+?)\s/', $head, $matches);
403 |
404 | return isset($matches[1]) ? $matches[1] : false;
405 | }
406 |
407 | // Outputs a filesize in human readable format.
408 | function bytes2str($val, $round = 0)
409 | {
410 | $unit = array('','K','M','G','T','P','E','Z','Y');
411 | while($val >= 1000)
412 | {
413 | $val /= 1024;
414 | array_shift($unit);
415 | }
416 | return round($val, $round) . array_shift($unit) . 'B';
417 | }
418 |
419 | // Tests for a valid email address and optionally tests for valid MX records, too.
420 | function valid_email($email, $test_mx = false)
421 | {
422 | list($user, $domain) = explode('@', $email);
423 | if(strlen($user) > 0 && strlen($domain) > 0) {
424 | $parts = explode('.', $domain);
425 | if($parts >= 2) {
426 | if($test_mx) {
427 | return getmxrr($domain, $mxrecords);
428 | } else {
429 | return true;
430 | }
431 | }
432 | }
433 |
434 | return false;
435 | }
436 |
437 | // Grabs the contents of a remote URL. Can perform basic authentication if un/pw are provided.
438 | function geturl($url, $username = null, $password = null)
439 | {
440 | if(function_exists('curl_init'))
441 | {
442 | $ch = curl_init();
443 | if(!is_null($username) && !is_null($password)) {
444 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Basic ' . base64_encode("$username:$password")));
445 | }
446 | curl_setopt($ch, CURLOPT_URL, $url);
447 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
448 | $html = curl_exec($ch);
449 | curl_close($ch);
450 | return $html;
451 | }
452 | elseif(ini_get('allow_url_fopen') == true)
453 | {
454 | if(!is_null($username) && !is_null($password))
455 | $url = str_replace("://", "://$username:$password@", $url);
456 | $html = file_get_contents($url);
457 | return $html;
458 | }
459 | else
460 | {
461 | // Cannot open url. Either install curl-php or set allow_url_fopen = true in php.ini
462 | return false;
463 | }
464 | }
465 |
466 | // Returns the user's browser info.
467 | // browscap.ini must be available for this to work.
468 | // See the PHP manual for more details.
469 | function browser_info()
470 | {
471 | $info = get_browser(null, true);
472 | $browser = $info['browser'] . ' ' . $info['version'];
473 | $os = $info['platform'];
474 | $ip = $_SERVER['REMOTE_ADDR'];
475 | return array('ip' => $ip, 'browser' => $browser, 'os' => $os);
476 | }
477 |
478 | // Quick wrapper for preg_match
479 | function match($regex, $str, $i = 0)
480 | {
481 | if(preg_match($regex, $str, $match) == 1)
482 | return $match[$i];
483 | else
484 | return false;
485 | }
486 |
487 | // Sends an HTML formatted email
488 | function send_html_mail($to, $subject, $msg, $from, $plaintext = '')
489 | {
490 | if(!is_array($to)) $to = array($to);
491 |
492 | foreach($to as $address)
493 | {
494 | $boundary = uniqid(rand(), true);
495 |
496 | $headers = "From: $from\n";
497 | $headers .= "MIME-Version: 1.0\n";
498 | $headers .= "Content-Type: multipart/alternative; boundary = $boundary\n";
499 | $headers .= "This is a MIME encoded message.\n\n";
500 | $headers .= "--$boundary\n" .
501 | "Content-Type: text/plain; charset=ISO-8859-1\n" .
502 | "Content-Transfer-Encoding: base64\n\n";
503 | $headers .= chunk_split(base64_encode($plaintext));
504 | $headers .= "--$boundary\n" .
505 | "Content-Type: text/html; charset=ISO-8859-1\n" .
506 | "Content-Transfer-Encoding: base64\n\n";
507 | $headers .= chunk_split(base64_encode($msg));
508 | $headers .= "--$boundary--\n" .
509 |
510 | mail($address, $subject, '', $headers);
511 | }
512 | }
513 |
514 | // Quick and dirty wrapper for curl scraping.
515 | function curl($url, $referer = null, $post = null)
516 | {
517 | static $tmpfile;
518 |
519 | if(!isset($tmpfile) || ($tmpfile == '')) $tmpfile = tempnam('/tmp', 'FOO');
520 |
521 | $ch = curl_init($url);
522 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
523 | curl_setopt($ch, CURLOPT_COOKIEFILE, $tmpfile);
524 | curl_setopt($ch, CURLOPT_COOKIEJAR, $tmpfile);
525 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
526 | curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15");
527 | // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
528 | // curl_setopt($ch, CURLOPT_VERBOSE, 1);
529 |
530 | if(!is_null($referer)) {
531 | curl_setopt($ch, CURLOPT_REFERER, $referer);
532 | }
533 |
534 | if(!is_null($post)) {
535 | curl_setopt($ch, CURLOPT_POST, true);
536 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
537 | }
538 |
539 | $html = curl_exec($ch);
540 |
541 | // $last_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
542 | return $html;
543 | }
544 |
545 | // Accepts any number of arguments and returns the first non-empty one
546 | function pick()
547 | {
548 | foreach(func_get_args() as $arg) {
549 | if(!empty($arg)) {
550 | return $arg;
551 | }
552 | }
553 | return '';
554 | }
555 |
556 | // Secure a PHP script using basic HTTP authentication
557 | function http_auth($un, $pw, $realm = "Secured Area")
558 | {
559 | if(!(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) && $_SERVER['PHP_AUTH_USER'] == $un && $_SERVER['PHP_AUTH_PW'] == $pw))
560 | {
561 | header('WWW-Authenticate: Basic realm="' . $realm . '"');
562 | header('Status: 401 Unauthorized');
563 | exit();
564 | }
565 | }
566 |
567 | // This is easier than typing 'echo WEB_ROOT'
568 | function WEBROOT()
569 | {
570 | echo WEB_ROOT;
571 | }
572 |
573 | // Class Autloader
574 | function spf_autoload($class_name)
575 | {
576 | $fn = DOC_ROOT . '/includes/class.' . strtolower($class_name) . '.php';
577 | if(file_exists($fn))
578 | {
579 | return include $fn;
580 | }
581 | }
582 |
583 | // Returns a file's mimetype based on its extension
584 | function mime_type($filename, $default = 'application/octet-stream')
585 | {
586 | $mime_types = array('323' => 'text/h323',
587 | 'acx' => 'application/internet-property-stream',
588 | 'ai' => 'application/postscript',
589 | 'aif' => 'audio/x-aiff',
590 | 'aifc' => 'audio/x-aiff',
591 | 'aiff' => 'audio/x-aiff',
592 | 'asf' => 'video/x-ms-asf',
593 | 'asr' => 'video/x-ms-asf',
594 | 'asx' => 'video/x-ms-asf',
595 | 'au' => 'audio/basic',
596 | 'avi' => 'video/x-msvideo',
597 | 'axs' => 'application/olescript',
598 | 'bas' => 'text/plain',
599 | 'bcpio' => 'application/x-bcpio',
600 | 'bin' => 'application/octet-stream',
601 | 'bmp' => 'image/bmp',
602 | 'c' => 'text/plain',
603 | 'cat' => 'application/vnd.ms-pkiseccat',
604 | 'cdf' => 'application/x-cdf',
605 | 'cer' => 'application/x-x509-ca-cert',
606 | 'class' => 'application/octet-stream',
607 | 'clp' => 'application/x-msclip',
608 | 'cmx' => 'image/x-cmx',
609 | 'cod' => 'image/cis-cod',
610 | 'cpio' => 'application/x-cpio',
611 | 'crd' => 'application/x-mscardfile',
612 | 'crl' => 'application/pkix-crl',
613 | 'crt' => 'application/x-x509-ca-cert',
614 | 'csh' => 'application/x-csh',
615 | 'css' => 'text/css',
616 | 'dcr' => 'application/x-director',
617 | 'der' => 'application/x-x509-ca-cert',
618 | 'dir' => 'application/x-director',
619 | 'dll' => 'application/x-msdownload',
620 | 'dms' => 'application/octet-stream',
621 | 'doc' => 'application/msword',
622 | 'dot' => 'application/msword',
623 | 'dvi' => 'application/x-dvi',
624 | 'dxr' => 'application/x-director',
625 | 'eps' => 'application/postscript',
626 | 'etx' => 'text/x-setext',
627 | 'evy' => 'application/envoy',
628 | 'exe' => 'application/octet-stream',
629 | 'fif' => 'application/fractals',
630 | 'flac' => 'audio/flac',
631 | 'flr' => 'x-world/x-vrml',
632 | 'gif' => 'image/gif',
633 | 'gtar' => 'application/x-gtar',
634 | 'gz' => 'application/x-gzip',
635 | 'h' => 'text/plain',
636 | 'hdf' => 'application/x-hdf',
637 | 'hlp' => 'application/winhlp',
638 | 'hqx' => 'application/mac-binhex40',
639 | 'hta' => 'application/hta',
640 | 'htc' => 'text/x-component',
641 | 'htm' => 'text/html',
642 | 'html' => 'text/html',
643 | 'htt' => 'text/webviewhtml',
644 | 'ico' => 'image/x-icon',
645 | 'ief' => 'image/ief',
646 | 'iii' => 'application/x-iphone',
647 | 'ins' => 'application/x-internet-signup',
648 | 'isp' => 'application/x-internet-signup',
649 | 'jfif' => 'image/pipeg',
650 | 'jpe' => 'image/jpeg',
651 | 'jpeg' => 'image/jpeg',
652 | 'jpg' => 'image/jpeg',
653 | 'js' => 'application/x-javascript',
654 | 'latex' => 'application/x-latex',
655 | 'lha' => 'application/octet-stream',
656 | 'lsf' => 'video/x-la-asf',
657 | 'lsx' => 'video/x-la-asf',
658 | 'lzh' => 'application/octet-stream',
659 | 'm13' => 'application/x-msmediaview',
660 | 'm14' => 'application/x-msmediaview',
661 | 'm3u' => 'audio/x-mpegurl',
662 | 'man' => 'application/x-troff-man',
663 | 'mdb' => 'application/x-msaccess',
664 | 'me' => 'application/x-troff-me',
665 | 'mht' => 'message/rfc822',
666 | 'mhtml' => 'message/rfc822',
667 | 'mid' => 'audio/mid',
668 | 'mny' => 'application/x-msmoney',
669 | 'mov' => 'video/quicktime',
670 | 'movie' => 'video/x-sgi-movie',
671 | 'mp2' => 'video/mpeg',
672 | 'mp3' => 'audio/mpeg',
673 | 'mpa' => 'video/mpeg',
674 | 'mpe' => 'video/mpeg',
675 | 'mpeg' => 'video/mpeg',
676 | 'mpg' => 'video/mpeg',
677 | 'mpp' => 'application/vnd.ms-project',
678 | 'mpv2' => 'video/mpeg',
679 | 'ms' => 'application/x-troff-ms',
680 | 'mvb' => 'application/x-msmediaview',
681 | 'nws' => 'message/rfc822',
682 | 'oda' => 'application/oda',
683 | 'oga' => 'audio/ogg',
684 | 'ogg' => 'audio/ogg',
685 | 'ogv' => 'video/ogg',
686 | 'ogx' => 'application/ogg',
687 | 'p10' => 'application/pkcs10',
688 | 'p12' => 'application/x-pkcs12',
689 | 'p7b' => 'application/x-pkcs7-certificates',
690 | 'p7c' => 'application/x-pkcs7-mime',
691 | 'p7m' => 'application/x-pkcs7-mime',
692 | 'p7r' => 'application/x-pkcs7-certreqresp',
693 | 'p7s' => 'application/x-pkcs7-signature',
694 | 'pbm' => 'image/x-portable-bitmap',
695 | 'pdf' => 'application/pdf',
696 | 'pfx' => 'application/x-pkcs12',
697 | 'pgm' => 'image/x-portable-graymap',
698 | 'pko' => 'application/ynd.ms-pkipko',
699 | 'pma' => 'application/x-perfmon',
700 | 'pmc' => 'application/x-perfmon',
701 | 'pml' => 'application/x-perfmon',
702 | 'pmr' => 'application/x-perfmon',
703 | 'pmw' => 'application/x-perfmon',
704 | 'pnm' => 'image/x-portable-anymap',
705 | 'pot' => 'application/vnd.ms-powerpoint',
706 | 'ppm' => 'image/x-portable-pixmap',
707 | 'pps' => 'application/vnd.ms-powerpoint',
708 | 'ppt' => 'application/vnd.ms-powerpoint',
709 | 'prf' => 'application/pics-rules',
710 | 'ps' => 'application/postscript',
711 | 'pub' => 'application/x-mspublisher',
712 | 'qt' => 'video/quicktime',
713 | 'ra' => 'audio/x-pn-realaudio',
714 | 'ram' => 'audio/x-pn-realaudio',
715 | 'ras' => 'image/x-cmu-raster',
716 | 'rgb' => 'image/x-rgb',
717 | 'rmi' => 'audio/mid',
718 | 'roff' => 'application/x-troff',
719 | 'rtf' => 'application/rtf',
720 | 'rtx' => 'text/richtext',
721 | 'scd' => 'application/x-msschedule',
722 | 'sct' => 'text/scriptlet',
723 | 'setpay' => 'application/set-payment-initiation',
724 | 'setreg' => 'application/set-registration-initiation',
725 | 'sh' => 'application/x-sh',
726 | 'shar' => 'application/x-shar',
727 | 'sit' => 'application/x-stuffit',
728 | 'snd' => 'audio/basic',
729 | 'spc' => 'application/x-pkcs7-certificates',
730 | 'spl' => 'application/futuresplash',
731 | 'src' => 'application/x-wais-source',
732 | 'sst' => 'application/vnd.ms-pkicertstore',
733 | 'stl' => 'application/vnd.ms-pkistl',
734 | 'stm' => 'text/html',
735 | 'svg' => "image/svg+xml",
736 | 'sv4cpio' => 'application/x-sv4cpio',
737 | 'sv4crc' => 'application/x-sv4crc',
738 | 't' => 'application/x-troff',
739 | 'tar' => 'application/x-tar',
740 | 'tcl' => 'application/x-tcl',
741 | 'tex' => 'application/x-tex',
742 | 'texi' => 'application/x-texinfo',
743 | 'texinfo' => 'application/x-texinfo',
744 | 'tgz' => 'application/x-compressed',
745 | 'tif' => 'image/tiff',
746 | 'tiff' => 'image/tiff',
747 | 'tr' => 'application/x-troff',
748 | 'trm' => 'application/x-msterminal',
749 | 'tsv' => 'text/tab-separated-values',
750 | 'txt' => 'text/plain',
751 | 'uls' => 'text/iuls',
752 | 'ustar' => 'application/x-ustar',
753 | 'vcf' => 'text/x-vcard',
754 | 'vrml' => 'x-world/x-vrml',
755 | 'wav' => 'audio/x-wav',
756 | 'wcm' => 'application/vnd.ms-works',
757 | 'wdb' => 'application/vnd.ms-works',
758 | 'wks' => 'application/vnd.ms-works',
759 | 'wmf' => 'application/x-msmetafile',
760 | 'wps' => 'application/vnd.ms-works',
761 | 'wri' => 'application/x-mswrite',
762 | 'wrl' => 'x-world/x-vrml',
763 | 'wrz' => 'x-world/x-vrml',
764 | 'xaf' => 'x-world/x-vrml',
765 | 'xbm' => 'image/x-xbitmap',
766 | 'xla' => 'application/vnd.ms-excel',
767 | 'xlc' => 'application/vnd.ms-excel',
768 | 'xlm' => 'application/vnd.ms-excel',
769 | 'xls' => 'application/vnd.ms-excel',
770 | 'xlt' => 'application/vnd.ms-excel',
771 | 'xlw' => 'application/vnd.ms-excel',
772 | 'xof' => 'x-world/x-vrml',
773 | 'xpm' => 'image/x-xpixmap',
774 | 'xwd' => 'image/x-xwindowdump',
775 | 'z' => 'application/x-compress',
776 | 'zip' => 'application/zip');
777 | $ext = pathinfo($filename, PATHINFO_EXTENSION);
778 | return isset($mime_types[$ext]) ? $mime_types[$ext] : $default;
779 | }
780 |
--------------------------------------------------------------------------------