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.

Mark’s PHP Snippets

These are just a few PHP snippets/functions I have written over the years and have found to be quite useful.

mysql_connect('localhost','USERNAME','PASSWORD');
mysql_select_db('DATABASE');

This one really isn’t anything special, but it makes its way into every single one of my pages, so it’s worth mentioning in case you forget the syntax or are new to PHP. It simply connects to a mysql database.

function mysql_safe_string($value) {
    if(empty($value))           return 'NULL';
    elseif(is_string($value))   return '\''.mysql_real_escape_string(trim($value)).'\'';
    elseif(is_numeric($value))  return $value;
    elseif(is_array($value))    return implode(',',array_map('mysql_safe_string',$value));
    else                        return false;
}

function mysql_safe_query($format) {
    $args = array_slice(func_get_args(),1);
    $args = array_map('mysql_safe_string',$args);
    $query = vsprintf($format,$args);
    return mysql_query($query);
}

These are my favorite two functions that I can’t live without. They make writing mysql queries so much easier.

// compare something like this
mysql_query(sprintf('INSERT INTO users (name, age) VALUES (%s, %d)', mysql_real_escape_string($_POST['name']), (is_numeric($_POST['age'])?$_POST['age']:'NULL')));
// to this
mysql_safe_query('INSERT INTO users (name, age) VALUES (%s, %s)', $_POST['name'], $_POST['age']);
// it even handles arrays nicely
$arr = array(1,2,3,4,5);
mysql_safe_query('SELECT * FROM posts WHERE cat_id IN (%s) ORDER BY date DESC', $arr);

These next two are quite simple, but still very handy.

function redirect($uri) {
    header('location:'.$uri);
    exit;
}

function pr($arr) {
    echo '<pre>';
    print_r($arr);
    echo '</pre>';
}

pr() is great for debugging, and redirect() helps keep your code clean and meaningful.

This next function is still in its infancy, so it may be refined over time.

function smart_excerpt($text, $maxLen=500) {
    if(preg_match('`.*?<!--\s*more\s*-->`is', $text, $matches))
        return $matches[0];
    if(strlen($text) > $maxLen) {
        $minLen = round($maxLen/2);
        if(preg_match('`.{'.$minLen.','.$maxLen.'}[.!?]`s', $text, $matches))
            return rtrim($matches[0]);
        return rtrim(substr($text, 0, $maxLen),' \t\n\r\0\x0B.!?').'...';
    }
    return false;
}

It accepts a body of text, and searches for the text “<!–more–>”. If it finds it, it returns everything before that. Otherwise, if it doesn’t, and it’s longer than $maxLen, it trims the text down to the first punctuation character (.!?) before $maxLen. That way your text doesn’t get cropped mid-sentence/word. If it can’t find any punctuation (because you’re a terrible writer?), it will crop at exactly $maxLen. It returns false if the text doesn’t need cropping. This way you can decide if you want to print a “view more” link or not.

// example usage
if($excerpt = smart_excerpt($post['body']))
    echo nl2br($excerpt).' <a href="post_view.php?id='.$post['id'].'">More &raquo;</a>';
else
    echo nl2br($post['body']);

Well, that’s all for now. I’ll post up some more if I find any other tasty tidbits lying around.