Monday, July 30, 2007

Executing background processes from Windows

I needed to execute background processes in a windows environment. This was because I had extensive processing to do on a file after it was uploaded via a web page. Here's how I ended up doing it (courtesy http://www.somacon.com/p395.php):


$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("whatever_command.exe", 0, false);


From the site:
You can start the process using the Run method of the WScript.Shell object, which is built-in to Windows. By varying the second parameter, you can make the window hidden, visible, minimized, etc. By setting the third parameter to false, Run does not wait for the process to finish executing. This code only works on Windows.

There are a few more strategies on achieving this which are outlined on somacon.com, but this is the one I ended up using. So, check out the page on somacon.com for more info if you'd like. I'd also like to express my thanks for the great tip!

Thursday, July 26, 2007

LOGO


OK, I know I'm trying to set up a php blog, but I just ran across something that I remember fondly from my childhood. LOGO! I haven't seen this since first grade on our Apple computers in the computer lab. This was the first "programming language" I learned. Check out this site:

http://www.fragmentarisch.net/svg/drawingboard.php

Official LOGO isn't exactly like this, but this is really close and it runs in your browser!

ODBC Browser


I was working on a project that required gaining access to an ODBC source (mdb file) on a windows box via PHP. I wanted a quick and dirty way to access the data. I did a google search on php odbc browser, but I couldn't find a thing. So, I decided to write my own. This is a very basic browser, but it seems to do what I need it to do. The only thing you should have to change is the DSN name, username and password (if the user & pass are set). This works great for my MDB file and it should work okay w/ other ODBC sources. Enjoy!

<?
/*
* Program Comments:
* This is a basic...very basic ODBC browser written in PHP
*/
session_start();

// odbc connection parameters
$dsn = "dsn_name_here";
$user = ""; // username and password may or may not be needed depending on the database source
$pwd = "";

$max_saved_queries = 15;

// the saved_query session variable isn't set, go ahead and define it as an array
if (!isset($_SESSION['saved_queries']))
{
$_SESSION['saved_queries'] = array("----------");
}


// A query was requested to be executed...
// Save it to history if it's not the duplicate of a previous entry in the query history
// Also, move the currently executing query to the top of the query history list
$query = $_REQUEST['query'];

if (isset($_REQUEST['query']) && isset($_REQUEST['execute_query']) &&amp; $query != "----------" && $query != "")
{
array_unshift($_SESSION['saved_queries'], $query);

for ($i=1; $i<count($_SESSION['saved_queries'])-1; $i++)
{
$q = $_SESSION['saved_queries'][$i];

// we have a query match.
// delete the match because we've already put the query at the top with our array_unshift function above
if (strtolower($q) == strtolower($query))
{
array_splice($_SESSION['saved_queries'], $i, 1);
}
}

if (count($_SESSION['saved_queries']) > $max_saved_queries)
{
array_pop($_SESSION['saved_queries']);
}
}


// connect to the odbc dsn
$dbh = odbc_connect($dsn, $user, $pwd);
?>

<html>
<head>
<style type="text/css">

body { font-family: arial; }
.tbl { font-weight: bold; padding-top:10px; }
.columns { display:none; }
.resultClass0 { background-color:#F5F5F5; padding-left:8px; }
.resultClass1 { background-color:#E0E0E0; padding-left:8px; }
</style>


<script language="javaScript">
var tableArray = new Array();

function showHide()
{
var selectBox = document.getElementById('tables');
var selectedTable = selectBox[selectBox.selectedIndex].value;

document.getElementById('columns').innerHTML = tableArray[selectedTable];
}


function showSavedQuery()
{
var selectBox = document.getElementById('saved_queries');
var query = selectBox[selectBox.selectedIndex].value;

document.getElementById('query').value = query;
}
</script>

</head>
<body>

<b><i><big>ODBC Browser</big></i></b><br><br>


<?
$table_result = odbc_tables($dbh);
$table_name = array();
$table_type = array();

while (odbc_fetch_row($table_result))
{
$tn = odbc_result($table_result,"TABLE_NAME");
$tt = odbc_result($table_result,"TABLE_TYPE");

$table_name[] = $tn;
$table_type[] = $tt;
?>

<script language="javascript">
tableArray['<?=$tn?>'] = "";
<?
$column_result = odbc_columns($dbh, $dsn, "", $tn);
while (odbc_fetch_row($column_result))
{
?>
tableArray['<?=$tn?>'] += '<small><?=odbc_result($column_result, "COLUMN_NAME")?></small><br>';
<?
}
?>
</script>

<?
}
?>


<table border=0>
<tr>
<td valign="top">

<b>Tables</b><br>
<select name="tables" id="tables" size="8" onChange="showHide()">

<br>

<?
$table_result = odbc_tables($dbh);
foreach ($table_name as $key => $tn)
{
?>
<option value="<?=$tn?>"><?=$tn?> - <?=$table_type[$key]?></option>
<?
}
?>

</select>

</td>
<td valign="top" style="padding-left:15px;">

<b>Columns</b>
<div id="columns" style="width:500;height:140;overflow:auto;border:1px;border-style: solid;"></div>

</td>
</tr>
</table>


<hr>


<table border=0>
<tr>
<td>


<form method="post" action="mdb_table_data.php">
<table border=0 cellspacing=0 cellpadding=0>
<tr>
<td>
<b>Execute Query:</b><br>
<textarea name="query" id="query" cols="60" rows="8"><?=$query?></textarea><br>
<input type="submit" name="execute_query" value="Execute">
</td>
</tr>
</table>
</form>


</td>

<td valign="top" style="padding-left:15px;">

<b>Saved Queries:</b><br>

<select name="saved_queries" id="saved_queries" size="8" onChange="showSavedQuery()">

<?
foreach ($_SESSION['saved_queries'] as $saved_query)
{
$short_display = substr($saved_query, 0, 40)
?>
<option value="<?=$saved_query?>"><?=$short_display?></option>
<?
}
?>

</select>

</td>

</tr>

</table>


<hr>


<table border="0" cellspacing="0" cellpadding="0">
<?
// execute the query here
if (isset($_REQUEST['query']) && isset($_REQUEST['execute_query']) &&amp; $query != "----------" && $query != "")
{
$res = odbc_prepare($dbh, $query);
odbc_execute($res);

$total_num_of_rows = 0;
$row_num = 0;
$max_rows = 20;


while ($row = odbc_fetch_array($res))
{
$row_num++;
$total_num_of_rows++;
$class_num = $row_num % 2;

if ($row_num == 1)
{
?>
<tr>
<?
foreach ($row as $col_name => $value)
{
?>
<th valign="top" style="padding-top:10px;padding-left:8px;"><?=$col_name?></th>
<?
}
?>
</tr>
<?
}

if ($row_num >= $max_rows)
{
$row_num = 0;
}


?>
<tr>
<?
foreach ($row as $col_name => $value)
{
?>
<td valign="top" class="resultClass<?=$class_num?>">
<?=$value?>
</td>
<?
}
?>
</tr>
<?

}


?>
</table>

<?=$total_num_of_rows?> row(s) matched your query
<?
}
?>
</table>


</body>
</html>

Monday, July 23, 2007

Preventing form spam

I was having problems with form spam on many of the web sites that I've created. I did some research and came across a web site which helps ALOT with preventing form spam. In fact, I haven't had any form spam from the sites that I've enabled this service on. The site is at http://www.projecthoneypot.org.

This site offers the following services
  • Blacklisting lookup service (via dns query) which allows you to look up an IP address and get spam threat scores back from their database. The API is available at http://www.projecthoneypot.org/httpbl_api.php.
  • Set up your own honey pot for each site. I think this is fine since spammers will visit your site anyway. So, you might as well divert them from the contact page (where your form or e-mail addresses are located) and give them a fake e-mail address that will be logged by project honey pot. This page has an FAQ about installing a honey pot on your site.
  • There's more, but the ones above are the ones I use the most.

Thursday, July 12, 2007

PHP function to make a file size human-readable

Problem: You're working in PHP and you have a file size in bytes. You'd like to make it easier to read.

Answer: This quick function I created. It accepts the byte count and returns a human-readable file size string:
// make file size human-readable
function byte_size($bytes)
{
switch(1)
{
case ($bytes < pow(2, 10)): // bytes
$size = number_format($bytes) . ' B';
break;
case ($bytes < pow(2, 20)):
$size = number_format($bytes / pow(2, 10)) . ' kB'; // kilobytes
break;
case ($bytes < pow(2, 30)):
$size = number_format($bytes / pow(2, 20)) . ' MB'; // megabytes
break;
case ($bytes < pow(2,40)):
$size = number_format($bytes / pow(2,30)) . ' GB'; // gigabytes
break;
case ($bytes < pow(2,50)):
$size = number_format($bytes / pow(2,40)) . ' TB'; // terabytes
break;
case ($bytes < pow(2,60)):
$size = number_format($byes / pow(2,50)) . ' PB'; // pedabytes
break;
default: // a famous man once said, "a PC will never need more than 640 kB of RAM"
$size = number_format($bytes) . ' B';
}

return $size;
}

Tuesday, July 10, 2007

PHP Recursive Function to create a directory

Apparently, in PHP versions less than 5.0, you can't recursively create a directory. Here's a little function I wrote to allow this...works on *nix systems:


<?
// set the number of recursions, so we don't get out of control w/ infinite recursions
// like when we don't have file system access to create directories
// or when we try to create a directory w/ incorrect characters in it...like an asterisk (for example)
$num_recursions = 0;

// recursive function to create a directory
function mkdir_recursive($dir)
{
global $num_recursions;
$num_recursions++;

echo "attempting to create $dir\n";
$dir_res = @mkdir($dir);

if ($num_recursions > 100)
{
echo "Cannot create $dir...num_recursions in mkdir_recursive = $num_recursions\n";
exit;
}

if ($dir_res === false)
{
$next_attempt_dir = substr($dir, 0, strrpos(substr($dir, 0, -1), '/'));
echo "dir creation failed...Now attempting to create $next_attempt_dir\n";
mkdir_recursive($next_attempt_dir);

echo "trying again to create $dir\n";
$dir_res = mkdir($dir);
}
else // dir creation successful. Reset the global recursion counter.
{
$num_recursions = 0;
}

}
?>

Friday, May 25, 2007

Simple PHP directory listing

I needed a SIMPLE directory listing script written in PHP for a large number of files I had to import to a web site. I found a lot of fancy css-styled file viewers, but I just wanted a simple one that would generate html and links for me. So, I wrote one. Here it is:



<html>
<body>

<?
$dir = "."; // current directory (from file system point of view)

// figure out link path for files
$php_self = $_SERVER['PHP_SELF'];
$web_path = substr($php_self, 0, strrpos($php_self, "/")+1);


// Open the directory, and proceed to read its contents
if (is_dir($dir))
{
if ($dh = opendir($dir))
{
while (($file = readdir($dh)) !== false)
{

if ($file == "." || $file == "..")
{
continue;
}

$file_array[] = $file;
}
closedir($dh);
}
}

sort($file_array);
?>


<h2><?=$web_path?></h2>
<ul>

<?
foreach ($file_array AS $file)
{
?>
<li><a href='<?=$web_path?><?=rawurlencode($file)?>'><?=$file?></a>
<?
}
?>

</ul>

</body>
</html>>


Friday, May 04, 2007

Comparing mysql databases

I was recently working on a project between two servers (dev and live). The goal was to compare the structures of the two databases so I could merge the changes I made on dev and import them to live. I had a problem though...the mysql versions between the two were slightly different and so were the phpmyadmin versions and the mysqldump versions. I tried dumps using all three tools. However because of version differences, I could not get a good comparison between the two databases. I had no way to easily compare the two! So, I decided to use my programming legs and write a php script. It's a really simple script, but got the job done. I was then able to use winmerge (http://winmerge.org/) and easily tell what the differences between the two were. I called the php script database_describe.php. Keep in mind that you need command-line privileges on your system for this script to work:


$db_host = "127.0.0.1";
$db_name = "db_name";
$db_user = "mysql_username";
$db_pass = "mysql_password";

$link = mysql_connect ($db_host, $db_user, $db_pass) or die ('Cannot Connect to mysql db!');
mysql_select_db ($db_name);


$query = "SHOW TABLES";
$table_result = mysql_query($query);
while ($table_row = mysql_fetch_assoc($table_result))
{
$table_name = $table_row['Tables_in_'.$db_name];

$query = "DESCRIBE ".$table_name;

echo $table_name."\n";
echo "----------------------\n";

$describe_result = mysql_query($query);
while ($describe_row = mysql_fetch_assoc($describe_result))
{
echo $describe_row['Field']."\t".$describe_row['Type']."\n";
}

echo "\n\n";
}
?>


To run this program, I simply typed the following on the command line:
php database_describe.php > outputfile.txt

outputfileand then used my favorite sftp transfer program (filezilla or winscp) to download the dump that was made into outputfile.txt and repeated the step on the other system.

I know this is an absolute kludge that has to be done from the command line. Perhaps I'll write a web interface to do the same thing someday.