Completed
Pull Request — master (#2282)
by ྅༻ Ǭɀħ
01:46
created

functions.php ➔ yourls_edit_link()   B

Complexity

Conditions 9
Paths 13

Size

Total Lines 56
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 37
nc 13
nop 4
dl 0
loc 56
rs 7.1584
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * YOURLS
4
 * Function library
5
 */
6
7
/**
8
 * Determine the allowed character set in short URLs
9
 *
10
 */
11
function yourls_get_shorturl_charset() {
12
	static $charset = null;
13
	if( $charset !== null )
14
		return $charset;
15
16
    if( defined('YOURLS_URL_CONVERT') && in_array( YOURLS_URL_CONVERT, array( 62, 64 ) ) ) {
17
        $charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
18
    } else {
19
        // defined to 36, or wrongly defined
20
        $charset = '0123456789abcdefghijklmnopqrstuvwxyz';
21
    }
22
23
	$charset = yourls_apply_filter( 'get_shorturl_charset', $charset );
24
	return $charset;
25
}
26
27
/**
28
 * Make an optimized regexp pattern from a string of characters
29
 *
30
 */
31
function yourls_make_regexp_pattern( $string ) {
32
	$pattern = preg_quote( $string, '@' ); // add @ as an escaped character because @ is used as the regexp delimiter in yourls-loader.php
33
	// Simple benchmarks show that regexp with smarter sequences (0-9, a-z, A-Z...) are not faster or slower than 0123456789 etc...
34
	return $pattern;
35
}
36
37
/**
38
 * Is a URL a short URL? Accept either 'http://sho.rt/abc' or 'abc'
39
 *
40
 */
41
function yourls_is_shorturl( $shorturl ) {
42
	// TODO: make sure this function evolves with the feature set.
43
44
	$is_short = false;
45
46
	// Is $shorturl a URL (http://sho.rt/abc) or a keyword (abc) ?
47
	if( yourls_get_protocol( $shorturl ) ) {
48
		$keyword = yourls_get_relative_url( $shorturl );
49
	} else {
50
		$keyword = $shorturl;
51
	}
52
53
	// Check if it's a valid && used keyword
54
	if( $keyword && $keyword == yourls_sanitize_string( $keyword ) && yourls_keyword_is_taken( $keyword ) ) {
55
		$is_short = true;
56
	}
57
58
	return yourls_apply_filter( 'is_shorturl', $is_short, $shorturl );
59
}
60
61
/**
62
 * Check to see if a given keyword is reserved (ie reserved URL or an existing page). Returns bool
63
 *
64
 */
65
function yourls_keyword_is_reserved( $keyword ) {
66
	global $yourls_reserved_URL;
67
	$keyword = yourls_sanitize_keyword( $keyword );
68
	$reserved = false;
69
70
	if ( in_array( $keyword, $yourls_reserved_URL)
71
		or file_exists( YOURLS_ABSPATH ."/pages/$keyword.php" )
72
		or is_dir( YOURLS_ABSPATH ."/$keyword" )
73
	)
74
		$reserved = true;
75
76
	return yourls_apply_filter( 'keyword_is_reserved', $reserved, $keyword );
77
}
78
79
/**
80
 * Function: Get client IP Address. Returns a DB safe string.
81
 *
82
 */
83
function yourls_get_IP() {
84
	$ip = '';
85
86
	// Precedence: if set, X-Forwarded-For > HTTP_X_FORWARDED_FOR > HTTP_CLIENT_IP > HTTP_VIA > REMOTE_ADDR
87
	$headers = array( 'X-Forwarded-For', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_VIA', 'REMOTE_ADDR' );
88
	foreach( $headers as $header ) {
89
		if ( !empty( $_SERVER[ $header ] ) ) {
90
			$ip = $_SERVER[ $header ];
91
			break;
92
		}
93
	}
94
95
	// headers can contain multiple IPs (X-Forwarded-For = client, proxy1, proxy2). Take first one.
96
	if ( strpos( $ip, ',' ) !== false )
97
		$ip = substr( $ip, 0, strpos( $ip, ',' ) );
98
99
	return yourls_apply_filter( 'get_IP', yourls_sanitize_ip( $ip ) );
100
}
101
102
/**
103
 * Get next id a new link will have if no custom keyword provided
104
 *
105
 */
106
function yourls_get_next_decimal() {
107
	return yourls_apply_filter( 'get_next_decimal', (int)yourls_get_option( 'next_id' ) );
108
}
109
110
/**
111
 * Update id for next link with no custom keyword
112
 *
113
 */
114
function yourls_update_next_decimal( $int = '' ) {
115
	$int = ( $int == '' ) ? yourls_get_next_decimal() + 1 : (int)$int ;
116
	$update = yourls_update_option( 'next_id', $int );
117
	yourls_do_action( 'update_next_decimal', $int, $update );
118
	return $update;
119
}
120
121
/**
122
 * Delete a link in the DB
123
 *
124
 */
125 View Code Duplication
function yourls_delete_link_by_keyword( $keyword ) {
1 ignored issue
show
Duplication introduced by LeoColomb
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
126
	// Allow plugins to short-circuit the whole function
127
	$pre = yourls_apply_filter( 'shunt_delete_link_by_keyword', null, $keyword );
128
	if ( null !== $pre )
129
		return $pre;
130
131
	global $ydb;
132
133
	$table = YOURLS_DB_TABLE_URL;
134
    $keyword = yourls_sanitize_string($keyword);
135
    $delete = $ydb->fetchAffected("DELETE FROM `$table` WHERE `keyword` = :keyword", array('keyword' => $keyword));
136
	yourls_do_action( 'delete_link', $keyword, $delete );
137
	return $delete;
138
}
139
140
/**
141
 * SQL query to insert a new link in the DB. Returns boolean for success or failure of the inserting
142
 *
143
 */
144
function yourls_insert_link_in_db( $url, $keyword, $title = '' ) {
145
	global $ydb;
146
147
    $url       = yourls_sanitize_url($url);
148
    $keyword   = yourls_sanitize_keyword($keyword);
149
    $title     = yourls_sanitize_title($title);
150
    $timestamp = date('Y-m-d H:i:s');
151
    $ip        = yourls_get_IP();
152
153
    $table = YOURLS_DB_TABLE_URL;
154
    $binds = array(
155
        'keyword'   => $keyword,
156
        'url'       => $url,
157
        'title'     => $title,
158
        'timestamp' => $timestamp,
159
        'ip'        => $ip,
160
    );
161
    $insert = $ydb->fetchAffected("INSERT INTO `$table` (`keyword`, `url`, `title`, `timestamp`, `ip`, `clicks`) VALUES(:keyword, :url, :title, :timestamp, :ip, 0);", $binds);
162
163
	yourls_do_action( 'insert_link', (bool)$insert, $url, $keyword, $title, $timestamp, $ip );
164
165
	return (bool)$insert;
166
}
167
168
/**
169
 * Check if a long URL already exists in the DB. Return NULL (doesn't exist) or an object with URL informations.
170
 *
171
 * @since 1.5.1
172
 * @param  string $url  URL to check if already shortened
173
 * @return mixed        NULL if does not already exist in DB, or object with URL information as properties (eg keyword, url, title, ...)
174
 */
175 View Code Duplication
function yourls_url_exists( $url ) {
1 ignored issue
show
Duplication introduced by LeoColomb
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
	// Allow plugins to short-circuit the whole function
177
	$pre = yourls_apply_filter( 'shunt_url_exists', false, $url );
178
	if ( false !== $pre )
179
		return $pre;
180
181
	global $ydb;
182
	$table = YOURLS_DB_TABLE_URL;
183
    $url   = yourls_sanitize_url($url);
184
	$url_exists = $ydb->fetchObject("SELECT * FROM `$table` WHERE `url` = :url", array('url'=>$url));
185
186
    if ($url_exists === false) {
187
        $url_exists = NULL;
188
    }
189
190
	return yourls_apply_filter( 'url_exists', $url_exists, $url );
191
}
192
193
/**
194
 * Add a new link in the DB, either with custom keyword, or find one
195
 *
196
 */
197
function yourls_add_new_link( $url, $keyword = '', $title = '' ) {
198
	// Allow plugins to short-circuit the whole function
199
	$pre = yourls_apply_filter( 'shunt_add_new_link', false, $url, $keyword, $title );
200
	if ( false !== $pre )
201
		return $pre;
202
203
	$url = yourls_encodeURI( $url );
204
	$url = yourls_sanitize_url( $url );
205
	if ( !$url || $url == 'http://' || $url == 'https://' ) {
206
		$return['status']    = 'fail';
0 ignored issues
show
Coding Style Comprehensibility introduced by LeoColomb
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
207
		$return['code']      = 'error:nourl';
208
		$return['message']   = yourls__( 'Missing or malformed URL' );
209
		$return['errorCode'] = '400';
210
		return yourls_apply_filter( 'add_new_link_fail_nourl', $return, $url, $keyword, $title );
211
	}
212
213
	// Prevent DB flood
214
	$ip = yourls_get_IP();
215
	yourls_check_IP_flood( $ip );
216
217
	// Prevent internal redirection loops: cannot shorten a shortened URL
218
	if( yourls_get_relative_url( $url ) ) {
219
		if( yourls_is_shorturl( $url ) ) {
220
			$return['status']    = 'fail';
0 ignored issues
show
Coding Style Comprehensibility introduced by LeoColomb
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
221
			$return['code']      = 'error:noloop';
222
			$return['message']   = yourls__( 'URL is a short URL' );
223
			$return['errorCode'] = '400';
224
			return yourls_apply_filter( 'add_new_link_fail_noloop', $return, $url, $keyword, $title );
225
		}
226
	}
227
228
	yourls_do_action( 'pre_add_new_link', $url, $keyword, $title );
229
230
	$strip_url = stripslashes( $url );
231
	$return = array();
232
233
	// duplicates allowed or new URL => store it
234
	if( yourls_allow_duplicate_longurls() || !( $url_exists = yourls_url_exists( $url ) ) ) {
235
236
		if( isset( $title ) && !empty( $title ) ) {
237
			$title = yourls_sanitize_title( $title );
238
		} else {
239
			$title = yourls_get_remote_title( $url );
240
		}
241
		$title = yourls_apply_filter( 'add_new_title', $title, $url, $keyword );
242
243
		// Custom keyword provided
244
		if ( $keyword ) {
245
246
			yourls_do_action( 'add_new_link_custom_keyword', $url, $keyword, $title );
247
248
			$keyword = yourls_sanitize_string( $keyword );
249
			$keyword = yourls_apply_filter( 'custom_keyword', $keyword, $url, $title );
250 View Code Duplication
			if ( !yourls_keyword_is_free( $keyword ) ) {
0 ignored issues
show
Duplication introduced by LeoColomb
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
251
				// This shorturl either reserved or taken already
252
				$return['status']  = 'fail';
253
				$return['code']    = 'error:keyword';
254
				$return['message'] = yourls_s( 'Short URL %s already exists in database or is reserved', $keyword );
255
			} else {
256
				// all clear, store !
257
				yourls_insert_link_in_db( $url, $keyword, $title );
258
				$return['url']      = array('keyword' => $keyword, 'url' => $strip_url, 'title' => $title, 'date' => date('Y-m-d H:i:s'), 'ip' => $ip );
259
				$return['status']   = 'success';
260
				$return['message']  = /* //translators: eg "http://someurl/ added to DB" */ yourls_s( '%s added to database', yourls_trim_long_string( $strip_url ) );
261
				$return['title']    = $title;
262
				$return['html']     = yourls_table_add_row( $keyword, $url, $title, $ip, 0, time() );
263
				$return['shorturl'] = YOURLS_SITE .'/'. $keyword;
264
			}
265
266
		// Create random keyword
267
		} else {
268
269
			yourls_do_action( 'add_new_link_create_keyword', $url, $keyword, $title );
270
271
			$timestamp = date( 'Y-m-d H:i:s' );
272
			$id = yourls_get_next_decimal();
273
			$ok = false;
274
			do {
275
				$keyword = yourls_int2string( $id );
276
				$keyword = yourls_apply_filter( 'random_keyword', $keyword, $url, $title );
277 View Code Duplication
				if ( yourls_keyword_is_free($keyword) ) {
0 ignored issues
show
Duplication introduced by Raldenhoven
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
278
					if (yourls_insert_link_in_db( $url, $keyword, $title )){
279
						// everything ok, populate needed vars
280
						$return['url']      = array('keyword' => $keyword, 'url' => $strip_url, 'title' => $title, 'date' => $timestamp, 'ip' => $ip );
281
						$return['status']   = 'success';
282
						$return['message']  = /* //translators: eg "http://someurl/ added to DB" */ yourls_s( '%s added to database', yourls_trim_long_string( $strip_url ) );
283
						$return['title']    = $title;
284
						$return['html']     = yourls_table_add_row( $keyword, $url, $title, $ip, 0, time() );
285
						$return['shorturl'] = YOURLS_SITE .'/'. $keyword;
286
					} else {
287
						// database error, couldnt store result
288
						$return['status']   = 'fail';
289
						$return['code']     = 'error:db';
290
						$return['message']  = yourls_s( 'Error saving url to database' );
291
					}
292
					$ok = true;
293
				}
294
				$id++;
295
			} while ( !$ok );
296
			@yourls_update_next_decimal( $id );
0 ignored issues
show
Security Best Practice introduced by LeoColomb
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
297
		}
298
299
	// URL was already stored
300
	} else {
301
302
		yourls_do_action( 'add_new_link_already_stored', $url, $keyword, $title );
303
304
		$return['status']   = 'fail';
305
		$return['code']     = 'error:url';
306
		$return['url']      = array( 'keyword' => $url_exists->keyword, 'url' => $strip_url, 'title' => $url_exists->title, 'date' => $url_exists->timestamp, 'ip' => $url_exists->ip, 'clicks' => $url_exists->clicks );
307
		$return['message']  = /* //translators: eg "http://someurl/ already exists" */ yourls_s( '%s already exists in database', yourls_trim_long_string( $strip_url ) );
308
		$return['title']    = $url_exists->title;
309
		$return['shorturl'] = YOURLS_SITE .'/'. $url_exists->keyword;
310
	}
311
312
	yourls_do_action( 'post_add_new_link', $url, $keyword, $title );
313
314
	$return['statusCode'] = 200; // regardless of result, this is still a valid request
315
	return yourls_apply_filter( 'add_new_link', $return, $url, $keyword, $title );
316
}
317
318
319
/**
320
 * Edit a link
321
 *
322
 */
323
function yourls_edit_link( $url, $keyword, $newkeyword='', $title='' ) {
324
	// Allow plugins to short-circuit the whole function
325
	$pre = yourls_apply_filter( 'shunt_edit_link', null, $keyword, $url, $keyword, $newkeyword, $title );
326
	if ( null !== $pre )
327
		return $pre;
328
329
	global $ydb;
330
331
	$table = YOURLS_DB_TABLE_URL;
332
	$url = yourls_sanitize_url($url);
333
	$keyword = yourls_sanitize_string($keyword);
334
	$title = yourls_sanitize_title($title);
335
	$newkeyword = yourls_sanitize_string($newkeyword);
336
	$strip_url = stripslashes( $url );
337
	$strip_title = stripslashes( $title );
338
339
    $old_url = $ydb->fetchValue("SELECT `url` FROM `$table` WHERE `keyword` = :keyword", array('keyword' => $keyword));
340
341
	// Check if new URL is not here already
342
	if ( $old_url != $url && !yourls_allow_duplicate_longurls() ) {
343
		$new_url_already_there = intval($ydb->fetchValue("SELECT COUNT(keyword) FROM `$table` WHERE `url` = :url;", array('url' => $url)));
344
	} else {
345
		$new_url_already_there = false;
346
	}
347
348
	// Check if the new keyword is not here already
349
	if ( $newkeyword != $keyword ) {
350
		$keyword_is_ok = yourls_keyword_is_free( $newkeyword );
351
	} else {
352
		$keyword_is_ok = true;
353
	}
354
355
	yourls_do_action( 'pre_edit_link', $url, $keyword, $newkeyword, $new_url_already_there, $keyword_is_ok );
356
357
	// All clear, update
358
	if ( ( !$new_url_already_there || yourls_allow_duplicate_longurls() ) && $keyword_is_ok ) {
0 ignored issues
show
Bug Best Practice introduced by LeoColomb
The expression $new_url_already_there of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
359
            $sql   = "UPDATE `$table` SET `url` = :url, `keyword` = :newkeyword, `title` = :title WHERE `keyword` = :keyword";
360
            $binds = array('url' => $url, 'newkeyword' => $newkeyword, 'title' => $title, 'keyword' => $keyword);
361
			$update_url = $ydb->fetchAffected($sql, $binds);
362
		if( $update_url ) {
363
			$return['url']     = array( 'keyword' => $newkeyword, 'shorturl' => YOURLS_SITE.'/'.$newkeyword, 'url' => $strip_url, 'display_url' => yourls_trim_long_string( $strip_url ), 'title' => $strip_title, 'display_title' => yourls_trim_long_string( $strip_title ) );
0 ignored issues
show
Coding Style Comprehensibility introduced by LeoColomb
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
364
			$return['status']  = 'success';
365
			$return['message'] = yourls__( 'Link updated in database' );
366
		} else {
367
			$return['status']  = 'fail';
0 ignored issues
show
Coding Style Comprehensibility introduced by LeoColomb
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
368
			$return['message'] = /* //translators: "Error updating http://someurl/ (Shorturl: http://sho.rt/blah)" */ yourls_s( 'Error updating %s (Short URL: %s)', yourls_trim_long_string( $strip_url ), $keyword ) ;
369
		}
370
371
	// Nope
372
	} else {
373
		$return['status']  = 'fail';
0 ignored issues
show
Coding Style Comprehensibility introduced by LeoColomb
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
374
		$return['message'] = yourls__( 'URL or keyword already exists in database' );
375
	}
376
377
	return yourls_apply_filter( 'edit_link', $return, $url, $keyword, $newkeyword, $title, $new_url_already_there, $keyword_is_ok );
378
}
379
380
/**
381
 * Update a title link (no checks for duplicates etc..)
382
 *
383
 */
384 View Code Duplication
function yourls_edit_link_title( $keyword, $title ) {
0 ignored issues
show
Duplication introduced by LeoColomb
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
385
	// Allow plugins to short-circuit the whole function
386
	$pre = yourls_apply_filter( 'shunt_edit_link_title', null, $keyword, $title );
387
	if ( null !== $pre )
388
		return $pre;
389
390
	global $ydb;
391
392
	$keyword = yourls_sanitize_keyword( $keyword );
393
	$title = yourls_sanitize_title( $title );
394
395
	$table = YOURLS_DB_TABLE_URL;
396
	$update = $ydb->fetchAffected("UPDATE `$table` SET `title` = :title WHERE `keyword` = :keyword;", array('title' => $title, 'keyword' => $keyword));
397
398
	return $update;
399
}
400
401
402
/**
403
 * Check if keyword id is free (ie not already taken, and not reserved). Return bool.
404
 *
405
 */
406
function yourls_keyword_is_free( $keyword ) {
407
	$free = true;
408
	if ( yourls_keyword_is_reserved( $keyword ) or yourls_keyword_is_taken( $keyword ) )
409
		$free = false;
410
411
	return yourls_apply_filter( 'keyword_is_free', $free, $keyword );
412
}
413
414
/**
415
 * Check if a keyword is taken (ie there is already a short URL with this id). Return bool.
416
 *
417
 */
418 View Code Duplication
function yourls_keyword_is_taken( $keyword ) {
0 ignored issues
show
Duplication introduced by LeoColomb
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
419
420
	// Allow plugins to short-circuit the whole function
421
	$pre = yourls_apply_filter( 'shunt_keyword_is_taken', false, $keyword );
422
	if ( false !== $pre )
423
		return $pre;
424
425
	global $ydb;
426
    $keyword = yourls_sanitize_keyword($keyword);
427
	$taken = false;
428
	$table = YOURLS_DB_TABLE_URL;
429
430
	$already_exists = $ydb->fetchValue("SELECT COUNT(`keyword`) FROM `$table` WHERE `keyword` = :keyword;", array('keyword' => $keyword));
431
	if ( $already_exists )
432
		$taken = true;
433
434
	return yourls_apply_filter( 'keyword_is_taken', $taken, $keyword );
435
}
436
437
/**
438
 * Return XML output.
439
 *
440
 */
441
function yourls_xml_encode( $array ) {
442
	require_once( YOURLS_INC.'/functions-xml.php' );
443
	$converter= new yourls_array2xml;
444
	return $converter->array2xml( $array );
445
}
446
447
/**
448
 * Return array of all information associated with keyword. Returns false if keyword not found. Set optional $use_cache to false to force fetching from DB
449
 *
450
 * @since 1.4
451
 * @param  string $keyword    Short URL keyword
452
 * @param  bool   $use_cache  Default true, set to false to force fetching from DB
453
 * @return false|object       false if not found, object with URL properties if found
454
 */
455
function yourls_get_keyword_infos( $keyword, $use_cache = true ) {
456
	global $ydb;
457
	$keyword = yourls_sanitize_string( $keyword );
458
459
	yourls_do_action( 'pre_get_keyword', $keyword, $use_cache );
460
461
	if( $ydb->has_infos($keyword) && $use_cache === true ) {
462
		return yourls_apply_filter( 'get_keyword_infos', $ydb->get_infos($keyword), $keyword );
463
	}
464
465
	yourls_do_action( 'get_keyword_not_cached', $keyword );
466
467
	$table = YOURLS_DB_TABLE_URL;
468
	$infos = $ydb->fetchObject("SELECT * FROM `$table` WHERE `keyword` = :keyword", array('keyword' => $keyword));
469
470
	if( $infos ) {
471
		$infos = (array)$infos;
472
		$ydb->set_infos($keyword, $infos);
473
	} else {
474
        // is NULL if not found
475
        $infos = false;
476
		$ydb->set_infos($keyword, false);
477
	}
478
479
	return yourls_apply_filter( 'get_keyword_infos', $infos, $keyword );
480
}
481
482
/**
483
 * Return (string) selected information associated with a keyword. Optional $notfound = string default message if nothing found
484
 *
485
 */
486
function yourls_get_keyword_info( $keyword, $field, $notfound = false ) {
487
488
	// Allow plugins to short-circuit the whole function
489
	$pre = yourls_apply_filter( 'shunt_get_keyword_info', false, $keyword, $field, $notfound );
490
	if ( false !== $pre )
491
		return $pre;
492
493
	$keyword = yourls_sanitize_string( $keyword );
494
	$infos = yourls_get_keyword_infos( $keyword );
495
496
	$return = $notfound;
497
	if ( isset( $infos[ $field ] ) && $infos[ $field ] !== false )
498
		$return = $infos[ $field ];
499
500
	return yourls_apply_filter( 'get_keyword_info', $return, $keyword, $field, $notfound );
501
}
502
503
/**
504
 * Return title associated with keyword. Optional $notfound = string default message if nothing found
505
 *
506
 */
507
function yourls_get_keyword_title( $keyword, $notfound = false ) {
508
	return yourls_get_keyword_info( $keyword, 'title', $notfound );
509
}
510
511
/**
512
 * Return long URL associated with keyword. Optional $notfound = string default message if nothing found
513
 *
514
 */
515
function yourls_get_keyword_longurl( $keyword, $notfound = false ) {
516
	return yourls_get_keyword_info( $keyword, 'url', $notfound );
517
}
518
519
/**
520
 * Return number of clicks on a keyword. Optional $notfound = string default message if nothing found
521
 *
522
 */
523
function yourls_get_keyword_clicks( $keyword, $notfound = false ) {
524
	return yourls_get_keyword_info( $keyword, 'clicks', $notfound );
525
}
526
527
/**
528
 * Return IP that added a keyword. Optional $notfound = string default message if nothing found
529
 *
530
 */
531
function yourls_get_keyword_IP( $keyword, $notfound = false ) {
532
	return yourls_get_keyword_info( $keyword, 'ip', $notfound );
533
}
534
535
/**
536
 * Return timestamp associated with a keyword. Optional $notfound = string default message if nothing found
537
 *
538
 */
539
function yourls_get_keyword_timestamp( $keyword, $notfound = false ) {
540
	return yourls_get_keyword_info( $keyword, 'timestamp', $notfound );
541
}
542
543
/**
544
 * Update click count on a short URL. Return 0/1 for error/success.
545
 *
546
 */
547
function yourls_update_clicks( $keyword, $clicks = false ) {
548
	// Allow plugins to short-circuit the whole function
549
	$pre = yourls_apply_filter( 'shunt_update_clicks', false, $keyword, $clicks );
550
	if ( false !== $pre )
551
		return $pre;
552
553
	global $ydb;
554
	$keyword = yourls_sanitize_string( $keyword );
555
	$table = YOURLS_DB_TABLE_URL;
556
	if ( $clicks !== false && is_int( $clicks ) && $clicks >= 0 )
557
		$update = $ydb->fetchAffected( "UPDATE `$table` SET `clicks` = :clicks WHERE `keyword` = :keyword", array('clicks' => $clicks, 'keyword' => $keyword) );
558
	else
559
		$update = $ydb->fetchAffected( "UPDATE `$table` SET `clicks` = clicks + 1 WHERE `keyword` = :keyword", array('keyword' => $keyword) );
560
561
	yourls_do_action( 'update_clicks', $keyword, $update, $clicks );
562
	return $update;
563
}
564
565
/**
566
 * Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return
567
 *
568
 */
569
function yourls_get_stats( $filter = 'top', $limit = 10, $start = 0 ) {
570
	global $ydb;
571
572
	switch( $filter ) {
573
		case 'bottom':
574
			$sort_by    = 'clicks';
575
			$sort_order = 'asc';
576
			break;
577
		case 'last':
578
			$sort_by    = 'timestamp';
579
			$sort_order = 'desc';
580
			break;
581
		case 'rand':
582
		case 'random':
583
			$sort_by    = 'RAND()';
584
			$sort_order = '';
585
			break;
586
		case 'top':
587
		default:
588
			$sort_by    = 'clicks';
589
			$sort_order = 'desc';
590
			break;
591
	}
592
593
	// Fetch links
594
	$limit = intval( $limit );
595
	$start = intval( $start );
596
	if ( $limit > 0 ) {
597
598
		$table_url = YOURLS_DB_TABLE_URL;
599
		$results = $ydb->fetchObjects( "SELECT * FROM `$table_url` WHERE 1=1 ORDER BY `$sort_by` $sort_order LIMIT $start, $limit;" );
600
601
		$return = array();
602
		$i = 1;
603
604
		foreach ( (array)$results as $res ) {
605
			$return['links']['link_'.$i++] = array(
606
				'shorturl' => YOURLS_SITE .'/'. $res->keyword,
607
				'url'      => $res->url,
608
				'title'    => $res->title,
609
				'timestamp'=> $res->timestamp,
610
				'ip'       => $res->ip,
611
				'clicks'   => $res->clicks,
612
			);
613
		}
614
	}
615
616
	$return['stats'] = yourls_get_db_stats();
0 ignored issues
show
Bug introduced by LeoColomb
The variable $return does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
617
618
	$return['statusCode'] = 200;
619
620
	return yourls_apply_filter( 'get_stats', $return, $filter, $limit, $start );
621
}
622
623
/**
624
 * Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return
625
 *
626
 */
627
function yourls_get_link_stats( $shorturl ) {
628
	global $ydb;
629
630
	$table_url = YOURLS_DB_TABLE_URL;
631
	$shorturl  = yourls_sanitize_keyword( $shorturl );
632
633
    $res = $ydb->fetchObject("SELECT * FROM `$table_url` WHERE `keyword` = :keyword", array('keyword' => $shorturl));
634
	$return = array();
1 ignored issue
show
Unused Code introduced by LeoColomb
$return is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
635
636
	if( !$res ) {
637
		// non existent link
638
		$return = array(
639
			'statusCode' => 404,
640
			'message'    => 'Error: short URL not found',
641
		);
642
	} else {
643
		$return = array(
644
			'statusCode' => 200,
645
			'message'    => 'success',
646
			'link'       => array(
647
				'shorturl' => YOURLS_SITE .'/'. $res->keyword,
648
				'url'      => $res->url,
649
				'title'    => $res->title,
650
				'timestamp'=> $res->timestamp,
651
				'ip'       => $res->ip,
652
				'clicks'   => $res->clicks,
653
			)
654
		);
655
	}
656
657
	return yourls_apply_filter( 'get_link_stats', $return, $shorturl );
658
}
659
660
/**
661
 * Get total number of URLs and sum of clicks. Input: optional "AND WHERE" clause. Returns array
662
 *
663
 * The $where parameter will contain additional SQL arguments:
664
 *   $where['sql'] will concatenate SQL clauses: $where['sql'] = ' AND something = :value AND otherthing < :othervalue';
665
 *   $where['binds'] will hold the (name => value) placeholder pairs: $where['binds'] = array('value' => $value, 'othervalue' => $value2)
666
 *
667
 * @param  $where array  See comment above
668
 * @return array
669
 */
670
function yourls_get_db_stats( $where = array('sql' => '', 'binds' => array()) ) {
671
	global $ydb;
672
	$table_url = YOURLS_DB_TABLE_URL;
673
674
	$totals = $ydb->fetchObject( "SELECT COUNT(keyword) as count, SUM(clicks) as sum FROM `$table_url` WHERE 1=1 " . $where['sql'] , $where['binds'] );
675
	$return = array( 'total_links' => $totals->count, 'total_clicks' => $totals->sum );
676
677
	return yourls_apply_filter( 'get_db_stats', $return, $where );
678
}
679
680
/**
681
 * Get number of SQL queries performed
682
 *
683
 */
684
function yourls_get_num_queries() {
685
	global $ydb;
686
687
	return yourls_apply_filter( 'get_num_queries', $ydb->get_num_queries() );
688
}
689
690
/**
691
 * Returns a sanitized a user agent string. Given what I found on http://www.user-agents.org/ it should be OK.
692
 *
693
 */
694
function yourls_get_user_agent() {
695
	if ( !isset( $_SERVER['HTTP_USER_AGENT'] ) )
696
		return '-';
697
698
	$ua = strip_tags( html_entity_decode( $_SERVER['HTTP_USER_AGENT'] ));
699
	$ua = preg_replace('![^0-9a-zA-Z\':., /{}\(\)\[\]\[email protected]&\!\?;_\-=~\*\#]!', '', $ua );
700
701
	return yourls_apply_filter( 'get_user_agent', substr( $ua, 0, 254 ) );
702
}
703
704
/**
705
 * Redirect to another page
706
 *
707
 */
708
function yourls_redirect( $location, $code = 301 ) {
709
	yourls_do_action( 'pre_redirect', $location, $code );
710
	$location = yourls_apply_filter( 'redirect_location', $location, $code );
711
	$code     = yourls_apply_filter( 'redirect_code', $code, $location );
712
	// Redirect, either properly if possible, or via Javascript otherwise
713
	if( !headers_sent() ) {
714
		yourls_status_header( $code );
715
		header( "Location: $location" );
716
	} else {
717
		yourls_redirect_javascript( $location );
718
	}
719
	die();
720
}
721
722
/**
723
 * Set HTTP status header
724
 *
725
 * @since 1.4
726
 * @param int $code  status header code
727
 * @return bool      whether header was sent
728
 */
729
function yourls_status_header( $code = 200 ) {
730
	yourls_do_action( 'status_header', $code );
731
732
	if( headers_sent() )
733
		return false;
734
735
	$protocol = $_SERVER['SERVER_PROTOCOL'];
736
	if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )
737
		$protocol = 'HTTP/1.0';
738
739
	$code = intval( $code );
740
	$desc = yourls_get_HTTP_status( $code );
741
742
	@header ("$protocol $code $desc"); // This causes problems on IIS and some FastCGI setups
0 ignored issues
show
Security Best Practice introduced by LeoColomb
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
743
744
    return true;
745
}
746
747
/**
748
 * Redirect to another page using Javascript. Set optional (bool)$dontwait to false to force manual redirection (make sure a message has been read by user)
749
 *
750
 */
751
function yourls_redirect_javascript( $location, $dontwait = true ) {
752
	yourls_do_action( 'pre_redirect_javascript', $location, $dontwait );
753
	$location = yourls_apply_filter( 'redirect_javascript', $location, $dontwait );
754
	if( $dontwait ) {
755
		$message = yourls_s( 'if you are not redirected after 10 seconds, please <a href="%s">click here</a>', $location );
756
		echo <<<REDIR
757
		<script type="text/javascript">
758
		window.location="$location";
759
		</script>
760
		<small>($message)</small>
761
REDIR;
762
	} else {
763
		echo '<p>' . yourls_s( 'Please <a href="%s">click here</a>', $location ) . '</p>';
764
	}
765
	yourls_do_action( 'post_redirect_javascript', $location );
766
}
767
768
/**
769
 * Return a HTTP status code
770
 *
771
 */
772
function yourls_get_HTTP_status( $code ) {
773
	$code = intval( $code );
774
	$headers_desc = array(
775
		100 => 'Continue',
776
		101 => 'Switching Protocols',
777
		102 => 'Processing',
778
779
		200 => 'OK',
780
		201 => 'Created',
781
		202 => 'Accepted',
782
		203 => 'Non-Authoritative Information',
783
		204 => 'No Content',
784
		205 => 'Reset Content',
785
		206 => 'Partial Content',
786
		207 => 'Multi-Status',
787
		226 => 'IM Used',
788
789
		300 => 'Multiple Choices',
790
		301 => 'Moved Permanently',
791
		302 => 'Found',
792
		303 => 'See Other',
793
		304 => 'Not Modified',
794
		305 => 'Use Proxy',
795
		306 => 'Reserved',
796
		307 => 'Temporary Redirect',
797
798
		400 => 'Bad Request',
799
		401 => 'Unauthorized',
800
		402 => 'Payment Required',
801
		403 => 'Forbidden',
802
		404 => 'Not Found',
803
		405 => 'Method Not Allowed',
804
		406 => 'Not Acceptable',
805
		407 => 'Proxy Authentication Required',
806
		408 => 'Request Timeout',
807
		409 => 'Conflict',
808
		410 => 'Gone',
809
		411 => 'Length Required',
810
		412 => 'Precondition Failed',
811
		413 => 'Request Entity Too Large',
812
		414 => 'Request-URI Too Long',
813
		415 => 'Unsupported Media Type',
814
		416 => 'Requested Range Not Satisfiable',
815
		417 => 'Expectation Failed',
816
		422 => 'Unprocessable Entity',
817
		423 => 'Locked',
818
		424 => 'Failed Dependency',
819
		426 => 'Upgrade Required',
820
821
		500 => 'Internal Server Error',
822
		501 => 'Not Implemented',
823
		502 => 'Bad Gateway',
824
		503 => 'Service Unavailable',
825
		504 => 'Gateway Timeout',
826
		505 => 'HTTP Version Not Supported',
827
		506 => 'Variant Also Negotiates',
828
		507 => 'Insufficient Storage',
829
		510 => 'Not Extended'
830
	);
831
832
	if ( isset( $headers_desc[$code] ) )
833
		return $headers_desc[$code];
834
	else
835
		return '';
836
}
837
838
/**
839
 * Log a redirect (for stats)
840
 *
841
 * This function does not check for the existence of a valid keyword, in order to save a query. Make sure the keyword
842
 * exists before calling it.
843
 *
844
 * @since 1.4
845
 * @param string $keyword short URL keyword
846
 * @return mixed Result of the INSERT query (1 on success)
847
 */
848
function yourls_log_redirect( $keyword ) {
849
	// Allow plugins to short-circuit the whole function
850
	$pre = yourls_apply_filter( 'shunt_log_redirect', false, $keyword );
851
	if ( false !== $pre )
852
		return $pre;
853
854
	if ( !yourls_do_log_redirect() )
855
		return true;
856
857
	global $ydb;
858
	$table = YOURLS_DB_TABLE_LOG;
859
    $ip = yourls_get_IP();
860
    $binds = array(
861
        'now' => date( 'Y-m-d H:i:s' ),
862
        'keyword'  => yourls_sanitize_string($keyword),
863
        'referrer' => isset($_SERVER['HTTP_REFERER']) ? yourls_sanitize_url_safe($_SERVER['HTTP_REFERER']) : 'direct',
864
        'ua'       => yourls_get_user_agent(),
865
        'ip'       => $ip,
866
        'location' => yourls_geo_ip_to_countrycode($ip),
867
    );
868
869
    return $ydb->fetchAffected("INSERT INTO `$table` (click_time, shorturl, referrer, user_agent, ip_address, country_code) VALUES (:now, :keyword, :referrer, :ua, :ip, :location)", $binds );
870
}
871
872
/**
873
 * Check if we want to not log redirects (for stats)
874
 *
875
 */
876
function yourls_do_log_redirect() {
877
	return ( !defined( 'YOURLS_NOSTATS' ) || YOURLS_NOSTATS != true );
0 ignored issues
show
Coding Style Best Practice introduced by LeoColomb
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
878
}
879
880
/**
881
 * Converts an IP to a 2 letter country code, using GeoIP database if available in includes/geo/
882
 *
883
 * @since 1.4
884
 * @param string $ip IP or, if empty string, will be current user IP
885
 * @param string $defaut Default string to return if IP doesn't resolve to a country (malformed, private IP...)
0 ignored issues
show
Documentation introduced by ozh
There is no parameter named $defaut. Did you maybe mean $default?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
886
 * @return string 2 letter country code (eg 'US') or $default
887
 */
888
function yourls_geo_ip_to_countrycode( $ip = '', $default = '' ) {
889
	// Allow plugins to short-circuit the Geo IP API
890
	$location = yourls_apply_filter( 'shunt_geo_ip_to_countrycode', false, $ip, $default ); // at this point $ip can be '', check if your plugin hooks in here
891
	if ( false !== $location )
892
		return $location;
893
894
	if ( $ip == '' )
895
		$ip = yourls_get_IP();
896
897
    // Allow plugins to stick to YOURLS internals but provide another DB
898
    $db = yourls_apply_filter('geo_ip_path_to_db', YOURLS_INC.'/geo/GeoLite2-Country.mmdb');
899
    if (!is_readable($db)) {
900
        return $default;
901
    }
902
903
    $reader = new \GeoIp2\Database\Reader($db);
904
    try {
905
        $record = $reader->country($ip);
906
        $location = $record->country->isoCode; // eg 'US'
907
    } catch (\Exception $e) {
908
        /*
909
        Unused for now, Exception and $e->getMessage() can be one of :
910
911
        - Exception: \GeoIp2\Exception\AddressNotFoundException
912
          When: valid IP not found
913
          Error message: "The address 10.0.0.30 is not in the database"
914
915
        - Exception: \InvalidArgumentException
916
          When: IP is not valid, or DB not readable
917
          Error message: "The value "10.0.0.300" is not a valid IP address", "The file "/path/to/GeoLite2-Country.mmdb" does not exist or is not readable"
918
919
        - Exception: \MaxMind\Db\Reader\InvalidDatabaseException
920
          When: DB is readable but is corrupt or invalid
921
          Error message: "The MaxMind DB file's search tree is corrupt"
922
923
        - or obviously \Exception for any other error (?)
924
        */
925
        $location = $default;
926
    }
927
928
    return yourls_apply_filter( 'geo_ip_to_countrycode', $location, $ip, $default );
929
}
930
931
/**
932
 * Converts a 2 letter country code to long name (ie AU -> Australia)
933
 *
934
 * This associative array is the one used by MaxMind internal functions, it may differ from other lists (eg "A1" does not universally stand for "Anon proxy")
935
 *
936
 * @since 1.4
937
 * @param string $code 2 letter country code, eg 'FR'
938
 * @return string Country long name (eg 'France') or an empty string if not found
939
 */
940
function yourls_geo_countrycode_to_countryname( $code ) {
941
	// Allow plugins to short-circuit the function
942
	$country = yourls_apply_filter( 'shunt_geo_countrycode_to_countryname', false, $code );
943
	if ( false !== $country )
944
		return $country;
945
946
    // Weeeeeeeeeeee
947
    $countries = array(
948
        'A1' => 'Anonymous Proxy', 'A2' => 'Satellite Provider', 'AD' => 'Andorra', 'AE' => 'United Arab Emirates', 'AF' => 'Afghanistan',
949
        'AG' => 'Antigua and Barbuda', 'AI' => 'Anguilla', 'AL' => 'Albania', 'AM' => 'Armenia', 'AO' => 'Angola',
950
        'AP' => 'Asia/Pacific Region', 'AQ' => 'Antarctica', 'AR' => 'Argentina', 'AS' => 'American Samoa', 'AT' => 'Austria',
951
        'AU' => 'Australia', 'AW' => 'Aruba', 'AX' => 'Aland Islands', 'AZ' => 'Azerbaijan', 'BA' => 'Bosnia and Herzegovina',
952
        'BB' => 'Barbados', 'BD' => 'Bangladesh', 'BE' => 'Belgium', 'BF' => 'Burkina Faso', 'BG' => 'Bulgaria',
953
        'BH' => 'Bahrain', 'BI' => 'Burundi', 'BJ' => 'Benin', 'BL' => 'Saint Barthelemy', 'BM' => 'Bermuda',
954
        'BN' => 'Brunei Darussalam', 'BO' => 'Bolivia', 'BQ' => 'Bonaire, Saint Eustatius and Saba', 'BR' => 'Brazil', 'BS' => 'Bahamas',
955
        'BT' => 'Bhutan', 'BV' => 'Bouvet Island', 'BW' => 'Botswana', 'BY' => 'Belarus', 'BZ' => 'Belize',
956
        'CA' => 'Canada', 'CC' => 'Cocos (Keeling) Islands', 'CD' => 'Congo, The Democratic Republic of the', 'CF' => 'Central African Republic', 'CG' => 'Congo',
957
        'CH' => 'Switzerland', 'CI' => 'Cote D\'Ivoire', 'CK' => 'Cook Islands', 'CL' => 'Chile', 'CM' => 'Cameroon',
958
        'CN' => 'China', 'CO' => 'Colombia', 'CR' => 'Costa Rica', 'CU' => 'Cuba', 'CV' => 'Cape Verde',
959
        'CW' => 'Curacao', 'CX' => 'Christmas Island', 'CY' => 'Cyprus', 'CZ' => 'Czech Republic', 'DE' => 'Germany',
960
        'DJ' => 'Djibouti', 'DK' => 'Denmark', 'DM' => 'Dominica', 'DO' => 'Dominican Republic', 'DZ' => 'Algeria',
961
        'EC' => 'Ecuador', 'EE' => 'Estonia', 'EG' => 'Egypt', 'EH' => 'Western Sahara', 'ER' => 'Eritrea',
962
        'ES' => 'Spain', 'ET' => 'Ethiopia', 'EU' => 'Europe', 'FI' => 'Finland', 'FJ' => 'Fiji',
963
        'FK' => 'Falkland Islands (Malvinas)', 'FM' => 'Micronesia, Federated States of', 'FO' => 'Faroe Islands', 'FR' => 'France', 'GA' => 'Gabon',
964
        'GB' => 'United Kingdom', 'GD' => 'Grenada', 'GE' => 'Georgia', 'GF' => 'French Guiana', 'GG' => 'Guernsey',
965
        'GH' => 'Ghana', 'GI' => 'Gibraltar', 'GL' => 'Greenland', 'GM' => 'Gambia', 'GN' => 'Guinea',
966
        'GP' => 'Guadeloupe', 'GQ' => 'Equatorial Guinea', 'GR' => 'Greece', 'GS' => 'South Georgia and the South Sandwich Islands', 'GT' => 'Guatemala',
967
        'GU' => 'Guam', 'GW' => 'Guinea-Bissau', 'GY' => 'Guyana', 'HK' => 'Hong Kong', 'HM' => 'Heard Island and McDonald Islands',
968
        'HN' => 'Honduras', 'HR' => 'Croatia', 'HT' => 'Haiti', 'HU' => 'Hungary', 'ID' => 'Indonesia',
969
        'IE' => 'Ireland', 'IL' => 'Israel', 'IM' => 'Isle of Man', 'IN' => 'India', 'IO' => 'British Indian Ocean Territory',
970
        'IQ' => 'Iraq', 'IR' => 'Iran, Islamic Republic of', 'IS' => 'Iceland', 'IT' => 'Italy', 'JE' => 'Jersey',
971
        'JM' => 'Jamaica', 'JO' => 'Jordan', 'JP' => 'Japan', 'KE' => 'Kenya', 'KG' => 'Kyrgyzstan',
972
        'KH' => 'Cambodia', 'KI' => 'Kiribati', 'KM' => 'Comoros', 'KN' => 'Saint Kitts and Nevis', 'KP' => 'Korea, Democratic People\'s Republic of',
973
        'KR' => 'Korea, Republic of', 'KW' => 'Kuwait', 'KY' => 'Cayman Islands', 'KZ' => 'Kazakhstan', 'LA' => 'Lao People\'s Democratic Republic',
974
        'LB' => 'Lebanon', 'LC' => 'Saint Lucia', 'LI' => 'Liechtenstein', 'LK' => 'Sri Lanka', 'LR' => 'Liberia',
975
        'LS' => 'Lesotho', 'LT' => 'Lithuania', 'LU' => 'Luxembourg', 'LV' => 'Latvia', 'LY' => 'Libya',
976
        'MA' => 'Morocco', 'MC' => 'Monaco', 'MD' => 'Moldova, Republic of', 'ME' => 'Montenegro', 'MF' => 'Saint Martin',
977
        'MG' => 'Madagascar', 'MH' => 'Marshall Islands', 'MK' => 'Macedonia', 'ML' => 'Mali', 'MM' => 'Myanmar',
978
        'MN' => 'Mongolia', 'MO' => 'Macau', 'MP' => 'Northern Mariana Islands', 'MQ' => 'Martinique', 'MR' => 'Mauritania',
979
        'MS' => 'Montserrat', 'MT' => 'Malta', 'MU' => 'Mauritius', 'MV' => 'Maldives', 'MW' => 'Malawi',
980
        'MX' => 'Mexico', 'MY' => 'Malaysia', 'MZ' => 'Mozambique', 'NA' => 'Namibia', 'NC' => 'New Caledonia',
981
        'NE' => 'Niger', 'NF' => 'Norfolk Island', 'NG' => 'Nigeria', 'NI' => 'Nicaragua', 'NL' => 'Netherlands',
982
        'NO' => 'Norway', 'NP' => 'Nepal', 'NR' => 'Nauru', 'NU' => 'Niue', 'NZ' => 'New Zealand',
983
        'O1' => 'Other', 'OM' => 'Oman', 'PA' => 'Panama', 'PE' => 'Peru', 'PF' => 'French Polynesia',
984
        'PG' => 'Papua New Guinea', 'PH' => 'Philippines', 'PK' => 'Pakistan', 'PL' => 'Poland', 'PM' => 'Saint Pierre and Miquelon',
985
        'PN' => 'Pitcairn Islands', 'PR' => 'Puerto Rico', 'PS' => 'Palestinian Territory', 'PT' => 'Portugal', 'PW' => 'Palau',
986
        'PY' => 'Paraguay', 'QA' => 'Qatar', 'RE' => 'Reunion', 'RO' => 'Romania', 'RS' => 'Serbia',
987
        'RU' => 'Russian Federation', 'RW' => 'Rwanda', 'SA' => 'Saudi Arabia', 'SB' => 'Solomon Islands', 'SC' => 'Seychelles',
988
        'SD' => 'Sudan', 'SE' => 'Sweden', 'SG' => 'Singapore', 'SH' => 'Saint Helena', 'SI' => 'Slovenia',
989
        'SJ' => 'Svalbard and Jan Mayen', 'SK' => 'Slovakia', 'SL' => 'Sierra Leone', 'SM' => 'San Marino', 'SN' => 'Senegal',
990
        'SO' => 'Somalia', 'SR' => 'Suriname', 'SS' => 'South Sudan', 'ST' => 'Sao Tome and Principe', 'SV' => 'El Salvador',
991
        'SX' => 'Sint Maarten (Dutch part)', 'SY' => 'Syrian Arab Republic', 'SZ' => 'Swaziland', 'TC' => 'Turks and Caicos Islands', 'TD' => 'Chad',
992
        'TF' => 'French Southern Territories', 'TG' => 'Togo', 'TH' => 'Thailand', 'TJ' => 'Tajikistan', 'TK' => 'Tokelau',
993
        'TL' => 'Timor-Leste', 'TM' => 'Turkmenistan', 'TN' => 'Tunisia', 'TO' => 'Tonga', 'TR' => 'Turkey',
994
        'TT' => 'Trinidad and Tobago', 'TV' => 'Tuvalu', 'TW' => 'Taiwan', 'TZ' => 'Tanzania, United Republic of', 'UA' => 'Ukraine',
995
        'UG' => 'Uganda', 'UM' => 'United States Minor Outlying Islands', 'US' => 'United States', 'UY' => 'Uruguay', 'UZ' => 'Uzbekistan',
996
        'VA' => 'Holy See (Vatican City State)', 'VC' => 'Saint Vincent and the Grenadines', 'VE' => 'Venezuela', 'VG' => 'Virgin Islands, British', 'VI' => 'Virgin Islands, U.S.',
997
        'VN' => 'Vietnam', 'VU' => 'Vanuatu', 'WF' => 'Wallis and Futuna', 'WS' => 'Samoa', 'YE' => 'Yemen',
998
        'YT' => 'Mayotte', 'ZA' => 'South Africa', 'ZM' => 'Zambia', 'ZW' => 'Zimbabwe',
999
    );
1000
1001
    $code = strtoupper($code);
1002
    if(array_key_exists($code, $countries)) {
1003
        $name = $countries[$code];
1004
    } else {
1005
        $name = '';
1006
    }
1007
1008
    return yourls_apply_filter( 'geo_countrycode_to_countryname', $name );
1009
}
1010
1011
/**
1012
 * Return flag URL from 2 letter country code
1013
 *
1014
 */
1015
function yourls_geo_get_flag( $code ) {