Open-Source PHP Framework - Designed for rapid development of performance-oriented scalable applications

/mvc/models/shorturl

[return to app]
1 <?php
2
/**
3  * Replace long URLs with short URLs that are easy to type and dictate
4  */
5
class shorturlModel extends model {
6     
/**
7      * Table/collection name - only need to change this if you will be operating multiple Vork account instances
8      * on the same database
9      * @var string
10      */
11     
protected $_table 'shorturls';
12
13     
/**
14      * Database type to use
15      * @var string Either sql or mongo
16      */
17     
protected $_db;
18
19     
/**
20      * Mongo collection object cache
21      * @var MongoCollection
22      */
23     
protected $_mongo;
24
25     
/**
26      * Sets $this->_db and if it is Mongo then it sets the MongoDB collection
27      */
28     
public function __construct() {
29         
$this->_db = (!in_array('mongo'config::$modelObjects) ? 'sql' 'mongo');
30     }
31
32     
/**
33      * Switchboard to route to either SQL or Mongo methods
34      *
35      * @param string $name
36      * @param array $args
37      */
38     
public function __call($name, array $args) {
39         
$name '_' $this->_db ucfirst($name);
40         if (
$this->_db == 'mongo' && !$this->_mongo) {
41             
$this->_mongo $this->mongo->selectCollection($this->_table);
42         }
43         return 
call_user_func_array(array($this$name), $args);
44     }
45
46     
/**
47      * Retrieve the full URL for a short URL
48      *
49      * @param string $shortUrl
50      * @return string Returns false if not found
51      */
52     
protected function _sqlGetShortUrl($shortUrl) {
53         
$sql 'select url from ' $this->_table ' where shorturl=' $this->db->cleanString($shortUrl);
54         
$res $this->db->query($sql);
55         
$row $res->fetch_row();
56         return (
$row current($row) : $row);
57     }
58     protected function 
_mongoGetShortUrl($shortUrl) {
59         
$row $this->_mongo->findOne(array('_id' => $shortUrl));
60         return (
$row $row['url'] : $row);
61     }
62
63     
/**
64      * Log the forwarding action
65      * @param string $shortUrl
66      */
67     
protected function _sqlLogForward($shortUrl) {
68         
$args['table'] = $this->_table '_log';
69         
$args['vals']['shorturl'] = $this->db->cleanString($shortUrl);
70         if (isset(
$_SERVER['HTTP_REFERER'])) {
71             
$args['vals']['referrer'] = $this->db->cleanString($_SERVER['HTTP_REFERER']);
72         }
73         if (isset(
$_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
74             
$args['vals']['ip'] = $this->_sqlIp($_SERVER['REMOTE_ADDR']);
75         }
76         
$args['vals']['occurrence'] = 'current_timestamp';
77         
$sql $this->db->insertSql($args);
78         
$this->db->query($sql);
79     }
80     protected function 
_mongoLogForward($shortUrl) {
81         
$args = array('shorturl' => $shortUrl'occurrence' => time());
82         if (isset(
$_SERVER['HTTP_REFERER'])) {
83             
$args['referrer'] = $_SERVER['HTTP_REFERER'];
84         }
85         if (isset(
$_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
86             
$args['ip'] = $_SERVER['REMOTE_ADDR'];
87         }
88         return 
$this->mongo->selectCollection($this->_table '_log')->insert($args);
89     }
90
91     
/**
92      * Adds a new short URL
93      *
94      * If a URL already has been shortened then this method will return the original shortUrl, it will not
 
reshorten it.
95      *
96      * @
param string $url
97      
* @return string
98      
*/
99     protected function 
_sqlAddShortUrl($url) {
100         if (
substr($url, -1) == '/') { //strip trailing slash for URL consistency
101             
$url substr($url0, -1);
102         }
103         
$sql 'select shorturl from ' $this->_table ' where url=' $this->db->cleanString($url);
104         if (!
$res $this->db->query($sql)) {
105             
$this->_initializeDatabase();
106             
$res $this->db->query($sql);
107         }
108         
$row $res->fetch_row();
109         if (
$row) { //if URL is already in the database just retrun the existing shortUrl
110             
$shortUrl current($row);
111         } else {
112             
$shortUrl $this->_generateShortUrl();
113             
$args['table'] = $this->_table;
114             
$quote $this->db->surroundingQuote;
115             
$args['vals'] = array('shorturl' => $quote $shortUrl $quote'added' => 'current_timestamp',
116                                   
'url' => $this->db->cleanString($url));
117             if (isset(
$_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
118                 
$args['vals']['ip'] = $this->_sqlIp($_SERVER['REMOTE_ADDR']);
119             }
120             
$sql $this->db->insertSql($args);
121             
$this->db->query($sql);
122         }
123         return 
$shortUrl;
124     }
125     protected function 
_mongoAddShortUrl($url) {
126         if (
substr($url, -1) == '/') { //strip trailing slash for URL consistency
127             
$url substr($url0, -1);
128         }
129         
$row $this->_mongo->findOne(array('url' => $url));
130         if (
$row) { //if URL is already in the database just retrun the existing shortUrl
131             
$shortUrl $row['_id'];
132         } else {
133             
$shortUrl $this->_generateShortUrl();
134             
$args = array('_id' => $shortUrl'added' => time(), 'url' => $url);
135             if (isset(
$_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
136                 
$args['ip'] = $_SERVER['REMOTE_ADDR'];
137             }
138             
$this->_mongo->save($args);
139             
$this->_mongo->ensureIndex(array('url' => true), array('unique' => true));
140         }
141         return 
$shortUrl;
142     }
143
144     
/**
145      * Generates a 4-digit alphanumeric shortUrl consisting of easily-differentiated characters only
146      * @return string
147      */
148     
protected function _generateShortUrl() {
149         
//only letters that are easy to visually differentiate
150         
$chars = array('b''c''d''f''g''h''j''k''m''n',
151                        
'p''q''r''s''t''v''w''x''y''z');
152         
$alphanums array_merge($charsrange(29));
153         
$charset array_rand($alphanums4); //28 chars to the power of 4 = 614,656 combinations
154         
$shortUrl $alphanums[$charset[0]] . $alphanums[$charset[1]]
155                   . 
$alphanums[$charset[2]] . $alphanums[$charset[3]];
156         if (
$this->getShortUrl($shortUrl)) { //that shorturl already exists, generate another
157             
$shortUrl $this->_generateShortUrl();
158         }
159         return 
$shortUrl;
160     }
161
162     
/**
163      * Wrap the IP address for inserting into SQL - for MySQL this will also convert A-to-N
164      *
165      * @param string $ip
166      * @return string
167      */
168     
protected function _sqlIp($ip) {
169         
$dbParents class_parents($this->db);
170         
$isMysql = (isset($dbParents['mysqli']) || isset($dbParents['mysql']));
171         
$quote $this->db->surroundingQuote;
172         return (
$isMysql 'inet_aton(' $quote $ip $quote ')' $this->db->cleanString($ip));
173
174     }
175
176     
/**
177      * Creates the database tables
178      *
179      * If your database does not support varchars over 255 chars or the datetime column type then you will need
180      * to adjust the data types used in this method. Oracle users may also swap varchar with varchar2.
181      */
182     
protected function _initializeDatabase() {
183         
$dbParents class_parents($this->db);
184         
$isMysql = (isset($dbParents['mysqli']) || isset($dbParents['mysql']));
185         
$sql 'create table ' $this->_table ' (
186                 shorturl char(4) not null primary key,
187                 url varchar(765) not null unique,
188                 added datetime not null,
189                 ip ' 
. ($isMysql 'int(10) unsigned' 'varchar(16)') . ' null)';
190         
$this->db->query($sql);
191
192         
$sql 'create table ' $this->_table '_log (
193                 shorturl char(4) not null,
194                 referrer varchar(255) null,
195                 occurrence datetime not null,
196                 ip ' 
. ($isMysql 'int(10) unsigned' 'varchar(16)') . ' null)';
197         if (
$isMysql) {
198             
$sql .= ' engine=archive';
199         }
200         
$this->db->query($sql);
201     }
202 }