Base62 Encode

If you have large integers and you want to shrink them down in size for whatever reason, you can use this code. Should be easy enough to extend if you want even higher bases (just add a few more chars and increase the base).

<?php

/**
 * Converts a base 10 number to any other base.
 *
 * @param int $val   Decimal number
 * @param int $base  Base to convert to. If null, will use strlen($chars) as base.
 * @param string $chars Characters used in base, arranged lowest to highest. Must be at least $base characters long.
 *
 * @return string    Number converted to specified base
 */

function base_encode($val, $base=62, $chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
    if(!isset($base)) $base = strlen($chars);
    $str = '';
    do {
        $m = bcmod($val, $base);
        $str = $chars[$m] . $str;
        $val = bcdiv(bcsub($val, $m), $base);
    } while(bccomp($val,0)>0);
    return $str;
}

/**
 * Convert a number from any base to base 10
 *
 * @param string $str   Number
 * @param int $base  Base of number. If null, will use strlen($chars) as base.
 * @param string $chars Characters use in base, arranged lowest to highest. Must be at least $base characters long.
 *
 * @return int    Number converted to base 10
 */

function base_decode($str, $base=62, $chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
    if(!isset($base)) $base = strlen($chars);
    $len = strlen($str);
    $val = 0;
    $arr = array_flip(str_split($chars));
    for($i = 0; $i < $len; ++$i) {
        $val = bcadd($val, bcmul($arr[$str[$i]], bcpow($base, $len-$i-1)));
    }
    return $val;
}

(code updated 26-Aug-2011 for arbitrary precision using BC Math)

Posted in

Easy Current Page Tab

So, you have a bunch of links along the top of your page, and you want to highlight the current one when you click on it. Easy enough to do if you copy and paste the entire navbar onto every page and add some CSS to the current tab, but that’s just bad practice! You want to throw the navbar in a layout, and still have it work.

Well, here’s an easy solution! Just drop this in your layout:

<ul id="navbar">
<?php
$pages = array(
    'url1' => 'linktext1',
    'url2' => 'linktext2',
    'url3' => 'linktext3'
);
foreach($pages as $url => $name) {
    if($url == $currentPage) echo '<li class="current">'.$name.'</li>';
    else echo '<li><a href="'.$url.'">'.$name.'</a></li>';
}
?>
</ul>

And make sure you set $currentPage properly. You could use $_SERVER[‘REQUEST_URI’] if your URLs are in the same format (leading and trailing slashes if necessary).

Update: Made a little wrapper function out of this too.

function list_links($links) {
    global $currentPage;
    foreach($links as $name => $url) {
        $class = strtolower($name);
        $class = preg_replace('`[^a-z\s]`', '', $class);
        $class = preg_replace('`\s+`', '-', $class);
        $class = trim($class, '-');
        if(ltrim($url,'/') == $currentPage) echo '<li class="',$class,'"><a href="',$url,'" class="selected">',$name,'</a></li>';
        else echo '<li class="',$class,'"><a href="',$url,'">',$name,'</a></li>';
    }
}

This one adds a class to each <li> too so you can style it in your CSS.

Human-readable file size in C

I did a quick search on Google and couldn’t find any code that did this in C/C++, so here’s my contribution for the day. Just remember the allocate enough space in the buffer — about 10 chars should be enough.

char* readable_fs(double size/*in bytes*/, char *buf) {
    int i = 0;
    const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
    while (size > 1024) {
        size /= 1024;
        i++;
    }
    sprintf(buf, "%.*f %s", i, size, units[i]);
    return buf;
}

// usage
struct stat info;
char buf[10];
lstat("somefile.txt", &info);
printf("File size: %s\n", readable_fs(info.st_size, buf));
Posted in

Extract (almost) any archive type

After getting tired of trying to remember “tar xvzf”, I wrote a little script for extracting almost any file type via the linux command-line. You can copy and paste this script into a text editor like gedit and save it to “/usr/local/bin” for convenience. I called mine “untar” since it doesn’t seem to be taken. Remember to `chmod +x ` so that you can execute it. You may need to `sudo apt-get install unrar` if you don’t already have it. I’m sure you can see how to add other file types from this example.

#!/bin/sh
me=`basename $0`
if [ $# -eq 0 ]; then
    echo "usage: $me <files...>"
fi
for file in $@; do
    bn=`basename $file`
    case $file in
        *.tar)
            tar xvf $file;;
        *.tar.gz|*.tgz|*.tar.Z|*.tar.z)
            tar xvzf $file;;
        *.tar.bz2)
            tar xvjf $file;;
        *.zip)
            unzip $file;;
        *.rar)
            unrar x $file;;
        *)
            echo "$me: $bn: unkown filetype";;
    esac
done

Draw IplImage

OpenCV stores images in a data structure called IplImage. They provide methods for rendering it to the screen, but if you want to use OpenGL instead (which should be faster and gives you more flexibility), I wrote the following code. It should be a little faster than loading the IplImage into a texture first, and then drawing it (if you have to do this every frame).

void DrawIplImage(IplImage *image, int x = 0, int y = 0, GLfloat xZoom = 1.0f, GLfloat yZoom = 1.0f)
{
    GLenum format;
        switch(image->nChannels) {
            case 1:
                format = GL_LUMINANCE;
                break;
            case 2:
                format = GL_LUMINANCE_ALPHA;
                break;
            case 3:
                format = GL_BGR;
                break;
            default:
                return;
        }
   
        glRasterPos2i(x, y);
        glPixelZoom(xZoom, yZoom);
    glDrawPixels(image->width, image->height, format, GL_UNSIGNED_BYTE, image->imageData);
}

It should be pretty straight forward to use. Handles IplImages with 1 or 3 layers. Should work with 2 layers too, but I haven’t needed or found an example of this.

Get frame count from AVI

Here’s a little code snippet I wrote to get the number of frames in an AVI video file.

int getFrameCount(const char *filename) {
    // only works on AVIs
    int frameCount;
    char size[4];
    ifstream fin(filename, ios::in|ios::binary);
    if(!fin) {
        cerr << "Could not open " << filename << endl;
        exit(EXIT_FAILURE);
    }
    fin.seekg(0x30, ios::beg); // number of frames is stored at this location
    fin.read(size, 4);
    frameCount = size[0] | size[1]<<8 | size[2]<<16 | size[3]<<24;
    fin.close();
    return frameCount;
}

Use it however you like. I’m using it because OpenCV’s cvGetCaptureProperty doesn’t seem to work.

Templating with PHP

There are two main ways to create templates with PHP.

Header/Footer Files

First, design a one-page static layout for your site. You should come up with something like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
    <title>Untitled</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <meta name="keywords" content="" />
    <meta name="description" content="" />
    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
    <link rel="stylesheet" type="text/css" href="/style.css" />
</head>
<body>
<div id="page-wrap">
    <ul id="navbar">
        <li><a href="first-link">First Page</a></li>
        <li><a href="second-link">Second Page</a></li>
    </ul>
    <div id="main-area">
        <a id="logo" href="/"></a>
        <div id="content-container">
            <div id="content-body">
                PAGE-CONTENT-HERE
            </div>
        </div>
    </div>
    <div id="footer">
        <p>yoursite.com &copy; <?=date('Y')?> | <a href="/privacy-policy">Privacy Policy</a></p>
    </div>
</div>
</body>
</html>

Now, take everything above “PAGE-CONTENT-HERE” and throw it in a file like “header.inc.php”. Take the bottom half and put it in “footer.inc.php”. Now for each page, you can do something like this:

<?php
include 'header.inc.php';
echo <<<HTML
<h2>Page Title</h2>

<p>Page content.</p>
HTML
;
include 'footer.inc.php';
?>

Now, if you ever want to change the appearance of your site, you just need to edit your header or footer files!

The advantage of this method is that you can put PHP scripts before you include the header, or make special pages different by using a different header/footer.

Include Method

For this method, you can use the same page layout as above (save it as index.php), but replace PAGE-CONTENT-HERE with something like this:

<?php
include $_GET['p'].'.php';
?>

And then make all your links like <a href="/?p=mypage">. You have to very careful if you use this method though, because malicious users can include whatever they want into your layout! Imagine what would happen if they went to “yoursite.com/?p=index” for example (you’d have a page inside a page inside a page…). Or ?p=http://malicioususer.com/evilscript.

The advantage of using this method is that you don’t have to include a header and footer in each one of your content pages, but you get less flexibility over the layout of your site for special pages.

The Hybrid Method

This method gives you the best of both worlds, although it is perhaps ever so slightly more processor intensive. For this method, you will need create a .htaccess file like this:

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* /?p=$0

Well, I guess you don’t really need to, but it will make your URLs prettier (no file extensions).

Then create an index.php file like this:

<?php
define('DS', DIRECTORY_SEPARATOR);
define('ROOT', dirname(__FILE__));
define('PAGES', ROOT.DS.'pages'.DS);
define('LAYOUTS', ROOT.DS.'layouts'.DS);
define('PHP', '.php');

$pageLayout = 'default';

if(empty($_GET['p'])) $page = 'home';
else $page = $_GET['p'];

if(file_exists(PAGES.$page.PHP)) {
    $pageTitle = ucwords($page);
} else {
    header("HTTP/1.0 404 Not Found");
    $page = '404';
    $pageTitle = '404 - Page Not Found';
}

ob_start();
include PAGES.$page.PHP;
$pageContent = ob_get_clean();
include LAYOUTS.$pageLayout.PHP;

You probably won’t need to edit that at all unless you want to change a few default settings. Then create two folders, “layouts” and “pages”.

In the layouts folder, create a default layout called “default.php”. Something like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
    <title><?=$pageTitle?> - Your Site</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <meta name="keywords" content="" />
    <meta name="description" content="" />
    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
    <link rel="stylesheet" type="text/css" href="/style.css" />
    <?=$pageScripts?>
</head>
<body>
<div id="page-wrap">
    <ul id="navbar">
        <li><a href="first-link">First Page</a></li>
        <li><a href="second-link">Second Page</a></li>
    </ul>
    <div id="main-area">
        <a id="logo" href="/"></a>
        <div id="content-container">
            <div id="content-body">
                <?=$pageContent?>
            </div>
        </div>
    </div>
    <div id="footer">
        <p>yoursite.com &copy; <?=date('Y')?> | <a href="/privacy-policy">Privacy Policy</a></p>
    </div>
</div>
</body>
</html>

Notice the usage of variables $pageContent, $pageScripts, and $pageTitle. You can set the $pageTitle and $pageScripts within the page. $pageContent is where your content will appear in the layout. There is also one more variable you can set, $pageLayout, which you can set to use a layout other than the default one.

Now your page URLs will look something like “yoursite.com/page” with no file extension. You shouldn’t have to worry too much about evil users including bad files because they can only include stuff within the “pages” folder. You can also still access all your other files directly, by using the full URL with extension.

Oh, also make sure you have a file called “404.php” within the “pages” folder, or bad things might happen 🙂 This is the file that will get called if no page could be found. It uses the layout too.

I think that’s everything… so happy templating!

Resize images using this PHP script

Always bugs me when people stretch their images out of proportion, there’s really no reason for it. Just use this simply function to nicely resize your images! If you want all your images the same size (like a square), I recommend “trim”. Otherwise use “squeeze”. If you can think of more appropriate fill names, let me know!

<?php
// @fill
// center       - Centers the image without any scaling.  May be smaller than dimensions, or cropped.
// stretch      - Stretches the image to fill dimensions.  May change image proportions.
// squeeze      - Scales the image, maintaining original proportions.  May not fill dimensions.
// trim         - Scales the image, maintaining original proportions, to fill dimensions.  Image may be centered and trimmed.
// trim_rand    - Scales the image, maintaining original proportions, to fill dimensions.  Trimmed randomly.

function resize($src_filename, $dst_filename, $dst_width, $dst_height, $fill='squeeze', $quality=80, $png_filters=PNG_NO_FILTER)
{
    if(!file_exists($src_filename)) {
        //throw new Exception("File does not exist: $src_filename");
        return false;
    }
    if(empty($dst_filename)) {
        $dst_filename = $src_filename;
    }
    if($dst_width <= 0) {
        //throw new Exception("Width must be positive: $dst_width");
        return false;
    }
    if($dst_height <= 0) {
        //throw new Exception("Height must be positive: $dst_height");
        return false;
    }
    $src_ext = substr($src_filename,strrpos($src_filename,'.')+1);
    switch(strtolower($src_ext)) {
        case 'gif':
            $src_image = imagecreatefromgif($src_filename);
            break;
        case 'jpe':
        case 'jpeg':
        case 'jpg':
            $src_image = imagecreatefromjpeg($src_filename);
            break;
        case 'png':
            $src_image = imagecreatefrompng($src_filename);
            break;
        default:
            //throw new Exception("Invalid source file extension: $src_ext");
            return false;
    }
    $src_width = imagesx($src_image);
    $src_height = imagesy($src_image);
    switch(strtolower(trim($fill))) {
        case 'center':
            $src_x = round($src_width/2-$dst_width/2);
            $src_y = round($src_height/2-$dst_height/2);
            if($src_x < 0) {
                $dst_width = $src_width;
                $src_x = 0;
            }
            if($src_y < 0) {
                $dst_height = $src_height;
                $src_y = 0;
            }
            $dst_image = imagecreatetruecolor($dst_width, $dst_height);
            imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, $dst_width, $dst_height, $dst_width, $dst_height);
            break;
        case 'stretch':
            $dst_image = imagecreatetruecolor($dst_width, $dst_height);
            imagecopyresampled($dst_image, $src_image, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
            break;
        case 'crop':
        case 'crop_center':
        case 'trim_center':
        case 'trim':
            $src_ratio = $src_width/$src_height;
            $dst_ratio = $dst_width/$dst_height;
            if($src_ratio < $dst_ratio) // trim top and bottom
            {
                $ratio = $src_width/$dst_width;
                $crop_height = $dst_height*$ratio;
                $src_y = round(($src_height-$crop_height)/2);
                $crop_width = $src_width;
                $src_x = 0;
            }
            else // trim left and right
            {
                $ratio = $src_height/$dst_height;
                $crop_width = $dst_width*$ratio;
                $src_x = round(($src_width-$crop_width)/2);
                $crop_height = $src_height;
                $src_y = 0;
            }
            $dst_image = imagecreatetruecolor($dst_width, $dst_height);
            imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, $dst_width, $dst_height, $crop_width, $crop_height);
            break;
        case 'crop_rand':
        case 'trim_rand':
            $src_ratio = $src_width/$src_height;
            $dst_ratio = $dst_width/$dst_height;
            if($src_ratio < $dst_ratio) // trim top and bottom
            {
                $ratio = $src_width/$dst_width;
                $crop_height = $dst_height*$ratio;
                $src_y = rand(0,$src_height-$crop_height);
                $crop_width = $src_width;
                $src_x = 0;
            }
            else // trim left and right
            {
                $ratio = $src_height/$dst_height;
                $crop_width = $dst_width*$ratio;
                $src_x = rand(0,$src_width-$crop_width);
                $crop_height = $src_height;
                $src_y = 0;
            }
            $dst_image = imagecreatetruecolor($dst_width, $dst_height);
            imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, $dst_width, $dst_height, $crop_width, $crop_height);
            break;
        case 'squeeze':
        case 'stretch_prop':
        case 'fit':
            $ratio = max($src_width/$dst_width, $src_height/$dst_height);
            if($ratio < 1) $ratio = 1; // do not enlarge
            $dst_width = round($src_width/$ratio);
            $dst_height = round($src_height/$ratio);
            $dst_image = imagecreatetruecolor($dst_width, $dst_height);
            imagecopyresampled($dst_image, $src_image, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
            break;
        default:
            //throw new Exception("Unrecognized fill type: $fill");
            return false;
    }
    $dst_ext = substr($dst_filename,strrpos($dst_filename,'.')+1);
    if(empty($dst_ext)) {
        $dst_ext = $src_ext;
        $dst_filename .= ".$src_ext";
    }
    switch(strtolower($dst_ext)) {
        case 'gif':
            return imagegif($dst_image, $dst_filename);
        case 'jpe':
        case 'jpeg':
        case 'jpg':
            return imagejpeg($dst_image, $dst_filename, $quality);
        case 'png':
            return imagepng($dst_image, $dst_filename, $quality, $png_filters);
        default:
            //throw new Exception('Invalid destination file extension: $dst_ext');
            return false;
    }
}

?>

Use .htaccess to hide file extensions

Create a file at the root of your web server called “.htaccess”. Put the following code inside:

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule .* $0.php

This will make it so you can access all your PHP pages without having to type the “.php” extension the URL or your <a href>s. i.e., you can access “yoursite.com/about.php” just by going to “yoursite.com/about”. The actual file names should still have the .php extension though. You can do this with whatever file type you like too.

cPanel Quick Config – Quick Tip

Maybe I’m slow, but I just discovered “php.ini QuickConfig” in cPanel 11. It probably exists in prior versions too. It was hidden under “Software / Services”. Most of the default settings should be fine, but you might want to disable register_globals if it isn’t disabled already. This will prevent malicious visitors from setting your PHP variables themselves by attaching arguments to the URL. Just make sure you always use $_GET and $_POST where ever it’s necessary.