Tips and Tricks About Computers, Web Development, Linux, the Internet and the Like
PHP
This section contains PHP snippets and scripts
Reducing pageweight by compressing production css and js files
Sep 4th
I’ve been a little obsessed with improving the speed of web pages via minified javascript and css files. YUI’s team not only agrees with this, they recommend gzipping your minified js and css files. For a while I’ve been calling YUI Compressor inside my push to production scripts to do the deed. However, with this new mention of gzipping, I think might be exploring other options such as the method mentioned on the page which originally linked me to that awesome YUI writeup; minifying and gzipping javascript and css on the fly using php.
codepad.org – an online compiler/interpreter, and a simple collaboration tool.
May 27th
codepad.org is an online compiler/interpreter, and a simple collaboration tool.
Creating a simple news script that reads from an XML file
Mar 29th
I recently recoded a website for a friend because his markup was pretty ugly (thanks to WYSIWYG editors). While I was doing that, he mentioned he’d like to have an xml file he could edit to update his news. Simple enough I said and banged out the following which includes a little helpful function that wraps paragraphs in HTML p tags. My friend alpha helped me rewrite some ugly regex with a novel idea:
print join '', map {qq[
$_
]} split (/\r?\n/,$object->content)Of course, he’s a big perl coder so I had to create a php equivalent, nls2p.
Edit: Mookow mentioned he wanted to write links, so obviously a system would have to be put in place to support this. Using HTML tags in XML gets a little sloppy so I opted to use bbcode which is a very popular method to format text in web applications and very simple to add more filters.
The news post creator:
<?php
if (file_exists('news.xml')) {
$xml = simplexml_load_file('news.xml');
foreach ($xml->post as $post) {
echo "
<h4>".$post->title." - ".$post->date."</h4>
\n";
echo "
<div>".bbcode(nls2p($post->content))."</div>
\n";
echo "
--".$post->author."
\n";
}
} else {
echo "
Failed to open news source
";
}
The functions for formatting:
// Wrap paragraphs with
tags
function nls2p($str) {
$split_str = preg_split('#[\t]+?[\r\n]{1}#',$str);
foreach ($split_str as $line) {
$new_str = $new_str."
".preg_replace("/[\t\n]/","",$line)."
\n";
}
return $new_str;
}
// Parse bbcode as HTML
function bbcode($string) {
$search = array(
'@\[(?i)b\](.*?)\[/(?i)b\]@si',
'@\[(?i)i\](.*?)\[/(?i)i\]@si',
'@\[(?i)u\](.*?)\[/(?i)u\]@si',
'@\[(?i)img\](.*?)\[/(?i)img\]@si',
'@\[(?i)url=(.*?)\](.*?)\[/(?i)url\]@si',
'@\[(?i)code\](.*?)\[/(?i)code\]@si'
);
$replace = array(
'<b>\\1</b>',
'<i>\\1</i>',
'<u>\\1</u>',
'<img src="\\1">',
'<a href="\\1" target="_blank">\\2</a>',
'<code>\\1</code>'
);
return preg_replace($search , $replace, $string);
}
?>
The XML data file:
<?xml version='1.0' standalone='yes'?> <news> <post> <date>03-29-09</date> <author>Moo</author> <content>The site has been completely recoded by a friend of mine, -z- from NexuizNinjaz.com. He even added this fancy news script. Now, to update the news, I don't even have to touch index.php! Alright, I'm out for now. OMG A [b]NEW LINE[/b]! Now with [i]italics[/i] and [u]underlines[/u] and [url=http://www.nexuizninjaz.com]links[/url] [code]code blocks[/code] and images [img]http://www.xepic.net/pics/pic_091622001182920068.jpg[/img] </content> </post> <post> <date>03-29-09</date> <author>Moo</author> <content>Yeah, I removed the stupid gray background, because after a while it got boring. I changed the "Random Shiznit" column into something that could be a bit more productive. Once all of the other sites are up, I'll update the main page with the latest news from those. This main news column will be for the more intriguing news. I also decided to add titles to the news articles...Duuhhhhhhh OMG A NEW LINE! and again, LOL! </content> </post> <post> <date>03-25-09</date> <author>Moo</author> <content>I added a boring gray background to the main page, as you can see. The hover text-changing effect was removed because of its stupidity. A cool guy named Fabzor re-colored the header for me.</content> </post> <post> <date>03-23-09</date> <author>Moo</author> <content>Website updated, not much. Cool hover effects were added, but we still don't have much color in the page. Hopefully, I can find a good background and get this show on the road.</content> </post> <post> <date>03-22-09</date> <author>Moo</author> <content>Most of the links up top will not work for the time being, this site is just getting edited. Soon, we'll have a better webpage than this one, but for now, deal with this one!</content> </post> </news>
That’s pretty much it verbatim, including mookow’s news ^_^. Hope this is helpful to someone.
Geany, the Almost Notepad++, Improved gedit with a Few Extras That Make You Smile
Jul 21st
I was browsing around linuxappfinder.com today and came across Geany, which I’ve already gotten quite comfortable with.
Some features I found that make it similar to notepad++
- The hotkeys are very similar if not identical to notepad++ (gedit has different ones)
- It supports regex find/replace
- It supports ‘find in files’ – by using grep
- It’s a lightweight and efficient IDE
Some features Geany has that notepad++ doesn’t
- Status window with a history
- Scribble pad
- Built in terminal
- Color picker
- Support for projects
- Tight integration with my window manager
- The functions menu is expanded to contain all sorts of clever information that I can fold in a non buggy tree
Some features notepad++ has that geany doesn’t
- TextFX (neat macros that help you with string related pattern updates)
- GUI for changing theme colors
- Tabbed find/replace and find in all (nit pick)
Why I’m quickly choosing Geany over Notepad++
With tight integration with gnome (as opposed to notepad++ which I have to run through wine) and a package of dark themes which I was able to find quickly through the Geany’s FAQ.
I haven’t tried it on Windows yet but I’d imagine it’d run pretty well.. though you are missing out on a lot of things you can take advantage of in linux. If you’re a windows user and would like to give it a whirl, this will help you.
Feather.php – Making form to database interactions easier
Jan 31st
More often than not, when I’m doing form to database interactions, I use the same field names. This means there is some redundant information in my code, making my job more difficult. As such, I’ve started to create feather.php, a SQL statement constructor class that works off data sent by the $_POST or $_GET arrays.
It’s not ready for a public release as there are still some security issues to address (see TODO) but I wanted to get some thoughts, opinions and suggestions on this.
For those of you concerned about using the same field names as database columns, I plan to add associative arrays to allow you use different names.
i.e. something like this:
$field_names array('the_first_name','the_last_name','the_zipcode');
$column_names = array('first_name','last_name','zipcode');
Redirecting Specific IP's to Other Websites Using PHP
Jan 20th
Someone had requested a script that would redirect a list of ip’s to a random list of sites.
Here’s the snippet I came up with:
<?
// list of ip's you wish to redirect
$ips = array('127.0.0.1');
// Sites you wish to redirect to
$sites = array('http://yahoo.com/','http://www.aol.com');
foreach($ips as $ip) {
if ($ip==GetHostByName($REMOTE_ADDR)) {
header('Location: ' . $sites[rand(0,count($sites))]);
}
}
?>
XEPIC – Facebook Application
Dec 2nd
After going to GameDev ’07 at Harvard with James Lindsay on Saturday, I got some motivation and decided to get my feet wet with the facebook api. I have created my first application which pulls pictures from the website www.xepic.net and displays them in your profile like so:
If you’d like to try out my application, you can find it here.
PHP Functions: Strip Chars From A Filename and Cloud
Jun 8th
Someone overheard a friend and I chatting about a website and it’s tag cloud script. I mentioned that it’s pretty easy to create and that I had already created a php function. He asked me to dig it up, so I present to you a cloud script in it’s simplest form.
Cloud
<?php
$ceiling = 50;
class cloud {
function setTop($top) {
global $ceiling;
$ceiling = $top;
}
function sizeMe($name,$times) {
global $ceiling;
$times = ($times / $ceiling) * 100;
if ($times <=20)
$size = 1;
else if ($times>=11 && $times <=40)
$size = 2;
else if ($times>=21 && $times <=60)
$size = 3;
else if ($times>=31 && $times <=80)
$size = 4;
else
$size = 5;
// You should really replace this with a return $size, this is just for demonstration purposes
echo "<span class=\"a$size\"><a href=\"#\">$name</a></span> ";
}
}
?>
It’s pretty straight forward and easily modified. You setTop based on the number of entries in your database, then pass $name and the number of times it appears to the sizeMe function. If you have any questions or suggestions please let me know. I tried to keep this simple.
In this example I created classes, a1, a2, a3, a4 and a5 that I defined font-size for in a css file.
Strip Characters From A Filename
<?php
function renameStripChars($directory, $pattern, $replacement, $verbose = false) {
if($curdir = opendir($directory)) {
while($file = readdir($curdir)) {
if($file != '.' && $file != '..') {
$dstfile = $directory . '/' . preg_replace($pattern, $replacement, $file);
$srcfile = $directory . '/' . $file;
rename($srcfile, $dstfile);
if(is_dir($srcfile) && $verbose) {
renameStripChars($srcfile, $pattern, $replacement, $verbose);
}
}
}
closedir($curdir);
}
}
// An example of how I used it
renameStripChars('picsTest', '/[^\w\d\.-\s]+/', '', true);
renameStripChars('picsTest', '/[\s]+/', '_', true);
renameStripChars('picsTest', '/[-]+/', '-', true);
renameStripChars('picsTest', '/[_]+/', '_', true);
?>
I wrote this function because I had a boatload of images with ridiculous characters in them and I wanted them out before I uploaded them somewhere. Most frameworks have a similar function built in but if you’re looking for a quick fix, this is perfect for you.
Using PHP to sort Wallpapers by Dimension
May 14th
Once upon a time, I found myself in possession of a vast amount of wallpapers. I’m talking well over 10,000, all in one folder. That’s not to helpful since a lot of them were repeats in different dimensions. After realizing that I couldn’t just add a ‘sort by dimension’ field in explorer, I set about on a mission using a method I knew would work, PHP with the GD (Graphics Display) library.
The core of this script is based around the ability for GD to pull the dimensions of a picture quickly and efficiently. The goal is to get php to look in a directory and sort all their files by dimensions we define (this will help filter out any pictures in other dimensions present in the folder).
So the sorting cycle looks something like this:
$totalFiles=count($file);
for ($j=0; $j < $totalFiles; $j++)
{
$dimension=getimagesize("$dir$file[$j]");
for($k=0; $k < $totalFolders; $k++)
if($dimension[0] == $width[$k] && $dimension[1] == $height[$k])
{
movefile($dir, $file[$j], "$dir$width[$k]x$height[$k]");
$sorted++;
if ($sorted%50==0) { echo $sorted." files sorted | ".round(100*($sorted/$totalFiles),2)."%\n"; }
}
}
However, the folder is currently non-existent but we're not going to open up explorer and create them ourself, especially since we plan on having an array hold our dimension values.
Here's what I did:
$width = array(800,1024,1152,1280,1280,1280,1440,1600,1680,1920);
$height = array(600,768,864,800,960,1024,900,1200,1050,1200);
if (count($width) != count($height)) // Make sure the arrays are the same length
exit ("Non-matching array lengths: ".count($width)." widths with ".count($height)." heights.");
$totalFolders=count($width);
for($i=0; $i < $totalFolders; $i++) // Create directories based on the arrays if they haven't been created already
if (!file_exists("$dir$width[$i]x$height[$i]"))
{
mkdir("$dir$width[$i]x$height[$i]");
echo "Folder $width[$i]x$height[$i] created\n";
}
The rest of the code is all pretty straight forward. I added in some job statistics (percentages and amount of time to complete). I have to find my pictures of the output but I believe it went through all the wallpapers in ~110 seconds. Anyway, here's the script in it's entirety, I hope it helps :).
<?php
$dir = "pics/"; // The directory (trailing slash)
$dh = opendir($dir); // Open the directory
$m_time = explode(" ",microtime()); // These two lines get the start time
$starttime = $m_time[0] + $m_time[1];//
$width = array(800,1024,1152,1280,1280,1280,1440,1600,1680,1920);
$height = array(600,768,864,800,960,1024,900,1200,1050,1200);
if (count($width) != count($height)) // Make sure the arrays are the same length
exit ("Non-matching array lengths: ".count($width)." widths with ".count($height)." heights.");
$totalFolders=count($width);
for($i=0; $i < $totalFolders; $i++) // Create directories based on the arrays if they haven't been created already
if (!file_exists("$dir$width[$i]x$height[$i]"))
{
mkdir("$dir$width[$i]x$height[$i]");
echo "Folder $width[$i]x$height[$i] created\n";
}
// Create an array of the file names
while (false !== ($filename = readdir($dh))) {
if (!is_dir($dir.$filename)) $file[] = $filename;
}
if (!isset($file))
exit ("No files in this directory.");
$sorted=0;
$totalFiles=count($file);
for ($j=0; $j < $totalFiles; $j++)
{
$dimension=getimagesize("$dir$file[$j]");
for($k=0; $k < $totalFolders; $k++)
if($dimension[0] == $width[$k] && $dimension[1] == $height[$k])
{
movefile($dir, $file[$j], "$dir$width[$k]x$height[$k]");
$sorted++;
if ($sorted%50==0) { echo $sorted." files sorted | ".round(100*($sorted/$totalFiles),2)."%\n"; }
}
}
$m_time = explode(" ",microtime()); // These two lines get the end time
$endtime = $m_time[0] + $m_time[1]; //
echo "\n".$sorted." files have been sorted in ". round(($endtime - $starttime),3) ." seconds.\n";
echo "That's ".(1/(round(($endtime - $starttime),3)/$sorted))." files per second!";
function movefile($dir, $theFile, $newDir)
{
if (!copy($dir.$theFile, "$newDir/$theFile")) echo "failed to copy $theFile...\n";
else unlink($dir.$theFile);
}
?>
Note: This script should be called from the command line.
Using PHP to Make Excel Easier
May 12th
Recently, I was presented with the task of counting the number times filenames repeated in an excel document. Being a 16,000+ row file, with well over 500 unique filenames, there was no simple solution in excel.
Having some experience exporting excel documents using PHP, I knew importing them wouldn’t be too hard. A few google searches later and I came across Excel Reader which fit the bill perfectly.
After sorting my excel document by filename, I created a loop in PHP that would read the first filename, count the number of times it repeated until it changed, then append that data to a string that would be exported to a new excel document once it was done reading the data.
// Starting at 2 to skip the headers | +1 so it compares the last row
for ($i = 2; $i <= $data->sheets[0]['numRows']+1; $i++) {
if ($data->sheets[0]['cells'][$i][3]==$curFileName) {
$curFileCount++;
} else {
// Output the data line:
"Device","User","Filename","Filecount"
$stringData =
"\"".$data->sheets[0]['cells'][$i-1][1]."\",\"".$data->sheets[0]['cells'][$i
-1][2]."\",\"".$data->sheets[0]['cells'][$i-1][3]."\",\"".$curFileCount."\"\
n";
fwrite($fh, $stringData);
// Reset the count and update the curFileName
$curFileName = $data->sheets[0]['cells'][$i][3];
$curFileCount=1;
}
echo "Row: ".$i."\n";
}Of course… I didn’t have just one of these files but 6 and manually entering each filename would be a waste of my time. My software solution for this was to create an “Input” and “Output” folder and have PHP cycle through the “Input” folder.
// For every file in the dir "Input", generate a report
if ($handle = opendir('Input')) {
while (false !== ($orgFileName = readdir($handle))) {
if($orgFileName != '..' && $orgFileName != '.') {
// Echo the file we're reading, then read that file
echo $orgFileName."\n";
$data->read("Input/".$orgFileName);Eventually it all came together as such:
<?php
// Don't change any of this
require_once 'Excel/reader.php';
$data = new Spreadsheet_Excel_Reader();
$data->setOutputEncoding('CP1251');
// For every file in the dir "Input", generate a report
if ($handle = opendir('Input')) {
while (false !== ($orgFileName = readdir($handle))) {
if($orgFileName != '..' && $orgFileName != '.') {
// Echo the file we're reading
echo $orgFileName."\n";
$data->read("Input/".$orgFileName);
// Set the first filename to compare
$curFileName =
$data->sheets[0]['cells'][2][3]==$curFileName;
$curFileCount = 0;
// Create a file and open it for writing
$myFile =
"Output/File_Count-".str_replace(".xls",".csv",$orgFileName);
$fh = fopen($myFile, 'w') or die("can't open file");
// Starting at 2 to skip the headers | +1 so it
compares the last row
for ($i = 2; $i <= $data->sheets[0]['numRows']+1;
$i++) {
if
($data->sheets[0]['cells'][$i][3]==$curFileName) {
$curFileCount++;
} else {
// Output the data line:
"Device","User","Filename","Filecount"
$stringData =
"\"".$data->sheets[0]['cells'][$i-1][1]."\",\"".$data->sheets[0]['cells'][$i
-1][2]."\",\"".$data->sheets[0]['cells'][$i-1][3]."\",\"".$curFileCount."\"\
n";
fwrite($fh, $stringData);
// Reset the count and update the
curFileName
$curFileName =
$data->sheets[0]['cells'][$i][3];
$curFileCount=1;
}
echo "Row: ".$i."\n";
}
// Close file
fclose($fh);
}
}
}
?>My apologies for the less than perfect code, I was on a deadline and perfect code wasn’t a requirement :).
Here are some screenshots of the script in action.




