Posts Tagged ‘php’

Create Your Own URL Shortener

Sunday, February 13th, 2011

I don’t really pay attention to twitter that often,but I did notice more and more people are starting to use personalized url shorteners. There’s a lot of free services out there you can use, but if you have somewhere you can host a simple php script, why not make your own?

I ended up buying iamj.us/tyn, and that’s what I’m going to set this up on. If I wanted to make things shorter, I could take off the /tyn but then I dont think it’d make as much sense. http://iamj.us/tyn11l redirects back to this page, for example. If you need help picking out a short domain name, try out domai.nr.

To create my own shortener, I decided just to use php’s base_convert function which will convert to and from bases 2-36. For a personal url shortener, you shouldn’t need more than base 36. I did end up having to write a base 62 converter class for sh0tz so that I can keep urls short, but that’s another post another time.

Create the Database

mysql> create database iamjurl;
mysql> grant all privileges on iamjurl.* to 'dbuser'@'localhost' identified by 'password';
mysql> flush privileges;
mysql> CREATE TABLE `urls` (
    ->   `id` int(10) NOT NULL AUTO_INCREMENT,
    ->   `url` varchar(1024) NOT NULL,
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Super simple. One column for the url id, and one column for the url. You could add more fields for tracking views and that sort of thing if you wanted.

Write the PHP Guts
First, we need the ability to insert a new url into the database and echo out the short url.

if (isset($_GET['new'])) {
        //Somebody called /url.php?new to create a new short url
        if (isset($_GET['url'])) {
                //If url= isnt set to anything, then we don't really care since theres no link to add
                $dbcon = mysql_connect($db['host'],$db['user'],$db['pass']);
                if (!$dbcon) die ('Error connecting to db: ' . mysql_error());
                mysql_select_db($db['name'],$dbcon);
                $query = 'INSERT INTO urls (url) VALUES ("' . mysql_real_escape_string($_GET['url']) . '");';
                $result = mysql_query($query, $dbcon);
                if (!$result) die ('Invalid query: ' . mysql_error());
                //the new url's added to the database, now we get the auto_increment id and base_convert it to base36
                $shorturl = base_convert(mysql_insert_id($dbcon), 10, 36);
                //and echo it out to the browser
                echo "{$baseurl}{$shorturl} \n";
                mysql_close($dbcon);
        }
}

That should be fairly self-explanatory with the comments in there. So now you can go to http://iamj.us/url.php?new&url=http://google.com and it will add a new row to the urls table with http://google.com in it. It’ll also echo https://iamj.us/tyn1o or something similar to the screen. Whatever the baseurl is plus the base36 id of that row.

Now, if we actually want to be able to go to that link using a short url?

if (isset($_GET['go'])) {
        //somebody got redirected either with .htaccess or went directly to url.php?go=
        $dbcon = mysql_connect($db['host'],$db['user'],$db['pass']);
        if (!$dbcon) die ('Error connecting to db: ' . mysql_error());
        mysql_select_db($db['name'],$dbcon);
        //take go= and convert it back to base10 to match the mysql row id
        $shortid = base_convert(mysql_real_escape_string($_GET['go']),36,10);
        $query = 'SELECT url FROM urls WHERE id="' . $shortid . '";';
        $result = mysql_query($query, $dbcon);
        if (!$result) die ('Invalid query: ' . mysql_error());
        //While testing this, it's easier to echo $query out and make sure you're getting the right url
        //returned from mysql
        //      echo $query;
        //      echo mysql_result($result,0);
        //if we're not testing, then just redirect the user to the url we received from mysql
        header("Location: " . mysql_result($result,0));
}

Again, this should be self-explanatory. Go to /url.php?go=o and it converts the letter o to base10 which turns into 24, and then redirects the browser to the url stored in mysql with the id of 24. If you wanted to track views or other statistics, this would be the spot to do so(before redirecting.)

We don’t actually want to use /url.php?go=. We want to use(in my case) /tynXXXX. We do this with a simple mod_rewrite rule. This is what my .htaccess looks like:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(tyn)(.*)$ /url.php?go=$2 [NC,L]

The RewriteRule line would need changed if you’re not going to use a prefix to the shorturl(/xxx instead of /tynXXX). ^(.*) should be sufficient, and change $2 to $1.

Security
As it is right now, there’s not much security to this script. You can rename url.php to something like ASDF089234fasdf.php if you want to use security through obscurity. With an .htaccess, noone will know the actual filename of this script, and if you use the bookmarklet below you won’t have to remember it either.
If you plan on letting other people use it, adding checks for duplicate urls and reusing the same id would be a good idea. I’m already using mysql_real_escape_string but it wouldn’t hurt to sanitize the input even further.

Each time a row gets added, the id is only incremented by 1. Meaning it’d be easy for someone to get one url and then start going forward or backward down your list of urls just by subtracting/adding 1(in base36) to the url. To prevent this, try multiplying $shorturl by 303 or some other large odd number. Run this code to see what I mean:

<?php
/**
* basetest.php - Outputs a table showing how multiplying a number before base_converting it helps with obscurity.
* Author: Justyn Shull <justyn@justynshull.com>
*/
$magicnum = 303;
$i=1;
echo "<table><tr><td>id</td><td>Normal</td><td>x303</td></tr>";
while ($i<=50)
{
        echo "<tr><td>$i</td>";
        echo "<td>" . base_convert($i,10,36) . "</td>";
        echo "<td>" . base_convert($i*$magicnum,10,36) . "</td></tr>\n";
        $i++;
}
echo "</table>";
?>

See how the ‘normal’ column is easy to decipher, but the ‘x303′ column is a little more random?

Bonus!
Adding an actual gui to this script would make things awesome, right? Because you don’t want to urlencode URLs on your own and type in a long url everytime you want to make a short url. I’d recommend displaying an input box if url isn’t set and having the form method set to get.

Or you can do what I did, and just create a simple bookmarklet like this:

javascript:void(location.href='http://iamj.us/url.php?new&url='+escape(location.href))

Result
Go to http://iamj.us/url.php to see what my end result looks like for now. It’s essentially the same code in this post plus a few extras.

du2.php

Wednesday, August 18th, 2010

This is based off of du.php, and is basically the same thing except it lets you click on folders to navigate through the directory structure and see how much space each directory is taking up.

It uses php’s shell_exec function to call the du utility on the directories it’s in. So if your host doesn’t allow you to use shell_exec, this isn’t going to work for you. If they do allow shell_exec, but do not allow ssh access then this is perfect.

You can view it here as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<HTML><HEAD><TITLE>Disk Usage Report</TITLE></HEAD><BODY>
<?php
///////////////////////////////////////////////////////////
//Disk Usage script v2                       ///
//du2.php                          ///
/////////////                         ///
//Author:  Justyn Shull                    ///
//E-mail:  justyn@justynshull.com           ///
/////////////                      ///
//based off my old du.php script:         ///
//This lists the directories in $dir with the   ///
//space they take up.  Not sorted, but allows  ///
//you to click on a directory to navigate     ///
////////////////////////////////////////////////
 
/////TODO: Fix it so you don't end up with the full path you've taken
//        eg:   du2.php?dir=../mail/../html/
 
$time1 = microtime(True);
chdir(getenv('DOCUMENT_ROOT'));
chdir("..");
$homedir = shell_exec('pwd');
echo "Home: $homedir";
if ($_GET["dir"] == "")
    //$dir = getenv('DOCUMENT_ROOT')."/.."; // *should* be the users's home
    $dir = ".";
else
    $dir = $_GET["dir"];
 
echo escapeshellarg($dir)."<br />";
chdir($dir);
echo shell_exec("pwd")."<br />";
$homeindir = strpos(shell_exec("pwd"),$homedir);
//if ($homeindir === false) die ("Invalid dir");  //hopefully this will keep us from leaving $home
 
exec('ls -la',$dirs,$rc);
if ($rc != 0) die("Error listing files");
$numdir = count($dirs) - 1;
for ($x = 1;$x <= $numdir; $x++){
    $type = substr($dirs[$x],0,1);
    if ($type == "d") {
    $lsdirs[] = substr(strrchr($dirs[$x]," "),1);
    }
}
$numdir = count($lsdirs) - 1;
 
// Print usage now
echo "<br />";
for ($x = 0; $x <= $numdir; $x++) {
    if ($x == 1) {
        echo "<a href=du2.php?dir=$dir/$lsdirs[$x]>";
        echo "<img src=\"/icons/folder.gif\" border=0></a>    ..<br />";
    } else {
    unset($du);
    exec("du -hs ".escapeshellarg($lsdirs[$x]),$du,$rc);
    if ($rc != 0) die("Error on ".$x.": ".escapeshellarg($lsdirs[$x]));
    echo "<a href=du2.php?dir=$dir/$lsdirs[$x]>";
    echo "<img src=\"/icons/folder.gif\" border=0></a>".$du[0]."<br />";
    }
}
 
 
$time2 = microtime(True); 
$dutime = ($time2 - $time1); 
echo '<p>Page generated in <b>'.$dutime.'</b> seconds</p>';
?>
 
</BODY></HTML>