lime icon

Phosphorus and Lime

A Developer's Broadsheet

This blog has been deprecated. Please visit my new blog at klenwell.com/press.
PHP: next version of form wizard
Had an idea in the shower today on how to restructure the form field constructors in the next version of my form wizard:

Currently, in MVC terms, model, controller, and viewer are all handled by the constructor function. The function can either immediately echo the html that's produced after the data is validated and formatted, or it can return it for later display. The default setting is not to echo and this is usually necessary, especially if I intend to do any further post-function validation or manipulation of the data before displaying the form field html.

Given this reality, it would probably make more sense for the constructor simply to represent the data dimension of the form field and then write another function, e.g. display_field($name) or display_cx($name), to display it.
CSS: latest MySpace profile stylesheet
the ipod style

<style type="text/css">

/* BODY, WRAPPER TABLE */
body, table
{
background-color:white;
border-style:none;
}

/* MAIN PROFILE TABLE */
table table
{
position:relative;
background-color:white;
border-style:none;
}

/* PROFILE BOXES */
table table table
{
background-color:d2e2f2;
border-style:solid; border-width:1px; border-color:3c4782;
}

table table table table
{
background-color:d2e2f2;
border-style:solid; border-width:1px; border-color:transparent;
}


td
{
background-color:transparent;
border-style:solid; border-width:1px; border-color:transparent;
}


/* ALL FONTS */
.nametext, .orangetext15, .whitetext12, .lightbluetext8, span.blacktext12, table table table table table strong, .btext, .redbtext, .redtext, table table table table td, table table table td, table table td, table table table table div, table font font, a.navbar:link, table table table td a:link, a.text, a.man:link, a.navbar:hover, table table table td a:hover, a.text:hover
{
background:d2e2f2;
color:3c4782;
font-family:Lucida Console,Monaco,monospace;
font-weight:normal; font-size:11px;
}

/* PROFILE NAME */
.nametext
{
font-size:2em;
}

/* COLUMN BOX HEADERS (LEFT, RIGHT) */
.whitetext12, .orangetext15
{
font-weight:bold;
}

/* DEGREES-OF-SEPARATION BOX HEADER? */
.blacktext12, .blacktext12 #_ctl0_PageContent__ctl0_UserNetwork1_ctrlMessage
{
font-size:1.2em;
font-weight:bold;
}

/* BLOG BOX HEADER */
.btext
{
font-weight:bold;
}

/* LINKS */
a.navbar:link, table table table td a:link, a.text, a.man:link
{
color:3c4782;
text-decoration:underline;
font-weight:normal;
}
a.navbar:hover, table table table td a:hover, a.text:hover
{
color:blue;
text-decoration:none;
font-weight:normal;
}
a.navbar:visited, table table table td a:visited, a.text:visited
{
color:95a0bf;
text-decoration:underline;
font-weight:normal;
}

</style>


As soon as I crack their davinci code, I'm sure they'll overhaul it and offer something more rational.
CSS: new form style sheet
Put this together for my form wizard. Didn't change much in the appearance, but attempted to consolidate and simplify as much as possible so changes from here on out can be easier, faster, and more dramatic. This serves as a kind of stable version:

/*

amvc_basic css pack

File: _forms.css
Last Update: Oct 2005
Author: Tom Atwell


NOTES:

basic form wizard DIV tree:

CSS mode:

- class = wizard_field_block
- id = {name} / - class = {STYLE} ('OK'/'EDIT')
- class = {type}
- <label>
- class = form_prompt
- class = form_input

TABLE mode:

- id = form_stage
- class = form_block
- <table> class = form_table
- <td> class = form_label_cell
- <td> class = form_input_cell
- <td> class = form_prompt_cell

******************************/


/* CSS FORM WIZARD LAYOUT
******************************/

/* BASIC LAYOUT */
#wizard {}
form
{
margin:0;
padding:0;
}
#stage_banner {}
#stage_form
{
padding:1em 0;
background:#f2f5f7;
border:1px solid #7595aa;
border-width: 0 2px;
}
#stage_buttons {}


/* LINKS */
#wizard a {}
#wizard a:hover {}
#wizard a:visited {}

/* form wizard field blocks */
.wizard_field_block {}

/* field style types */
.textfield, .menu, .radio, .checkbox, .CSZ
{
margin:0 0 1em;
padding:.2em;
clear:left;
border-bottom:1px solid #7595aa;
}
.radio_group .form_input {
clear:left;
display:block;
}
.menu_table table
{
width:100%;
margin:0 0 1em;
padding:.2em;
border-bottom:1px solid #7595aa;
}


/* field sub-blocks */
label
{
display:inline;
width: 33%;
padding-left:.2em;
margin-right:.5em;
float:left;
clear:left;
background:transparent;
border: 0px solid #7595aa;
}
.form_input
{
display:inline;
}
.form_prompt
{
display:block;
padding-left:0;
clear:left;
}


/* EXCEPTIONS: field sub-blocks */
.radio_group .form_input
{
clear:left;
display:block;
}

/* buttons */
.form_button, .wizard_button
{
clear:left;
}

/******************************/



/* Table-Structured Layout
******************************/

.form_table
{
width:100%;
}

.form_label_cell
{
width:33%;
background:white;
}

/*****************************/


/* Prompt Styles
******************************/
.EDIT, .form_prompt_cell
{
color:red;
font-weight:bold;
}
.EDIT label
{
padding:.1em;
border: 0px solid #7595aa;
}
.EDIT table
{
border: thin solid red;
}
/*****************************/


/* REVIEW STAGE
******************************/

#review_form
{
margin:1em auto;
}
#review_form table
{
width:90%;
text-align:left;
margin:0 auto;
}
table.review_table
{
font-family:Courier New,Courier,monospace;
margin:1em auto;
width:95%;
margin:0 auto;
border:medium solid #708090;
border-width:0 1px 0 0;
background:white;
padding:0 0 0 2px;
}
.review_table td, .review_table th
{
padding:0.5em;
border:thin solid #708090;
border-width:0 0 2px;
}
.review_table th
{
background-color:#708090;
color:#f7f8f9;
padding:2px .4em .4em;
border:thin solid #f7f8f9;
border-width:0 1px 0;
text-align:center;
}
.review_table .label
{
background-color:#f7f8f9;
}
.review_table .input
{
border-right:1px solid #708090;
padding:.4em .4em .4em 2em;
}
.review_table p
{
margin:0 0 1em;
}
.empty_value
{
color:#d9dde1;
margin:0 auto;
}

/*****************************/


/* Edit Field Page
******************************/

#edit_page
{
width:500px;
margin:1em auto;
padding:1em;
background:white;
border:thin solid #746058;
border-width:0 2px;
}
#edit_page p
{
font-size:medium;
font-family:arial,sans-serif;
}
#edit_box
{
text-align:left;
padding:1em;
}

/*****************************/

 
Google Base: We control all your base
Apparently, Google's new service has eBay and Craiglist shaking in their boots. I confess, I'd take a little satisfaction in seeing Google eat Craiglist lunch, if only as a kind of poetic justice.

Disclosure: a few months ago, I approached Craiglist with an that would have dramatically diversified their operation. They wouldn't even hear it. On the one hand, I can't really blame them -- I'm sure they get approached with a lot of crackpot ideas (when they're not being posted on their bulletin boards.) They seem content to ride their charmingly clunky content management system into the ground. But they're going to be desperate for crackpot ideas once Google moves in on their racket.
MySQL: Collaborative Filtering
Someday I'll have a use for collaborative filtering. Not today, but some day. When I do, this will be very helpful:

SELECT t2.col_item,Count(t2.col_item) AS popularity FROM table AS t1
LEFT JOIN table AS t2 ON t1.col_arbiter = t2.col_arbiter
WHERE t1.col_item = 'this_item'
AND t2.col_item <> 'this_item'
GROUP BY t2.col_item
ORDER BY Count(t2.col_item) DESC
LIMIT 5


So what this means:

First, the idea is that you're trying to find the "this other item" in phrase "People who liked this item also liked this other item," a la Amazon.

What the different variables mean:

table - MySQL table containing items and their arbiters

t1 - temporary table containing every item in table

t2 - temp table containing every association between items (common denom: col_arbiter) in table

col_item - items that are being associated

t1.col_item - in temp table 1, column holding items such as "this item" in phrase above

t2.col_item - in temp table 2, column holding "this other item" in phrase above

col_arbiter - (from L. arbiter elegantiarum), column for people whose taste is being queried


Have not tested it myself yet.

source: Ian B on comp.lang.php
HTML: pragma no-cache
Still working on the reload issue for the blogfessional. The JS reload script didn't seem to solve the problem. I had the feeling that the pragma header might help and this seems to have done the trick:

<META Http-Equiv="Cache-Control" Content="no-cache" />
<META Http-Equiv="Pragma" Content="no-cache" />
<META Http-Equiv="Expires" Content="0" />


But I'm still not perfectly clear on why both tags are needed -- think I read somewhere it has something to do with browser inconsistencies -- and I'm not patient enough right now to learn this inside and out.
Tiny Disks, Tiny URLs, and other Tiny Stuff
Still trying to wrap my head around this. Sounds simple enough. Take an address like this:

http://phosphorusandlime.blogspot.com/

and turn it into something like this:

http://tinyurl.com/cbmkx

The TinyDisk stuff sounds fascinating, but that's the part I don't quite understand yet.

A tracking link from xents.com
(doesn't appear to work)
Gallery: Invisible Franchises
The first gallery link:



A little design exercise to fill the dead hours of the afternoon at the office.
JS: reload page once
Needed to reload a page once and only once a couple seconds after someone sees it. Why? For this site. In order for someone to see their own confession displayed, the page must be reloaded after he or she has posted it. I came up with this:

// reload_once()
function reload_once()
{
var this_url = location.href;

if ( this_url.indexOf("?reloaded") == -1 )
{
// window.location.replace(this_url + "?reload")
document.write('<meta http-equiv="refresh" content="3;url=' + this_url + '?reloaded">');
}
}

reload_once();


Seems to do the trick and rather efficient. Inspired by this.
PHP: mysql_is_unique
Given table, column name, and value (with option database name argument) will tell you whether that value is currently in the database table. Useful for user management systems.

/* Fx mysql_is_unique
******************************/
function mysql_is_unique($table, $col_name, $value, $db_name='default')
{
// *** DATA

# internal
$SQL['select_unique'] = '';


// *** MANIPULATE

// default db
if ( $db_name == 'default' )
{
if ( defined('DB_NAME') )
{
$db_name = DB_NAME;
}
else
{
trigger_error('database not defined', E_USER_WARNING);
return FALSE;
}
}

// sanity checks

# column name
if ( empty($col_name) )
{
trigger_error("empty column name", E_USER_WARNING);
return FALSE;
}

# value
if ( empty($value) )
{
trigger_error("empty value", E_USER_WARNING);
return FALSE;
}

# table
if ( !mysql_is_table($table, $db_name) )
{
trigger_error("table [$table] not found", E_USER_ERROR);
return FALSE;
}

// open DB
$link = open_db($db_name);

// sanitize input
$value = mysql_sanitize_input($value, $link);

// mysql query
$SQL['select_unique'] = "SELECT `$col_name` FROM `$table` WHERE `$col_name` = '$value'";

// result
$_sqlr = mysql_query($SQL['select_unique'])
OR trigger_error('MySQL error number '.mysql_errno().': '.mysql_error());

// close db
close_db($link);


// *** RETURN

# is not unique
if ( mysql_num_rows($_sqlr) > 0 )
{
return FALSE;
}
# is unique
else
{
return TRUE;
}

} # end Fx
/******************************/

 
PHP: mysql_insert_array
This function inserts an associative array (e.g. $ARRAY = array("first_name" => $_first_name, "last_name" => $_last_name);) in table where array keys match the database column names.

/* Fx mysql_insert_array
*****************************/
function mysql_insert_array($ARRAY, $table, $db_name='')
{
// *** DATA

# internal
$_insert_line = '';
$_values_line = '';

# flags
$_FLAG['is_col'] = FALSE;

# return
$id = FALSE;


// *** MANIPULATE

// default db
if ( $db_name == 'default' )
{
if ( defined('DB_NAME') )
{
$db_name = DB_NAME;
}
else
{
trigger_error('database not defined', E_USER_WARNING);
return FALSE;
}
}

// open DB
$link = open_db($db_name)
OR trigger_error("unable to open db [$db_name]", E_USER_WARNING);

// get table column names

# query
$SQL['show_cols'] = "SHOW COLUMNS FROM $table";
$_sqlr = mysql_query($SQL['show_cols']);

# query failed
if (!$_sqlr)
{
close_db($link);
trigger_error("Unable to check columns for table [$table]", E_USER_WARNING);
return FALSE;
}

# match column
if (mysql_num_rows($_sqlr) > 0)
{
$_i = 0;
while ($_COL_HEADERS = mysql_fetch_assoc($_sqlr))
{
$COLUMN[$_i] = $_COL_HEADERS['Field'];
$_i++;
}
}

// construct query string

# reset ARRAY
reset($ARRAY);

# cycle through array
foreach ( $ARRAY as $key => $value ) {

# reset flag
$_FLAG['is_col'] = FALSE;

# check ARRAY keys against table column names [$COLUMN]
for( $i = 0; $i < count($COLUMN); $i++ )
{
if ( $key == $COLUMN[$i] )
{
$_FLAG['is_col'] = TRUE;
break;
}
}

# if key is column, insert value
if ($_FLAG['is_col'])
{

// *** SANITIZE DATA HERE (see http://www.php.net/manual/en/function.mysql-real-escape-string.php)
$value = mysql_sanitize_input($value, $link);

$_insert_line .= "`$key`, ";
$_values_line .= "'$value', ";
}
}
## end FOREACH loop

# assemble query

# trim insert superfluous comma from query strings
$_insert_line = substr($_insert_line, 0, strrpos($_insert_line, ','));
$_values_line = substr($_values_line, 0, strrpos($_values_line, ','));

# SQL query
$SQL['insert_arrays'] = <<<SQL
INSERT INTO `$table` ( $_insert_line )
VALUES ( $_values_line )
SQL;

// run query
$_sqlr = mysql_query($SQL['insert_arrays'])
OR trigger_error('MySQL error number '.mysql_errno().': '.mysql_error());

// verify INSERT
if ( mysql_affected_rows($link) > 0 )
{
$id = mysql_insert_id();
}
else
{
$id = FALSE;
}


// *** RETURN

// close db
close_db($link);

// return result
return $id;

} /* end Fx */
/****************************/


The function mysql_sanitize_input($value, $link) is little more than a wrapper for the mysql function mysql_real_escape_string ( string unescaped_string [, resource link_identifier] ).
MySpace Samy Worm
Ended up just now on an article about the worm that ate myspace this week or last week:

Web 2.0 worm downs MySpace

The article includes a link to the authors explanation -- and presentation -- of the exploit code itself, which makes interesting reading, if for no other reason than the ingenuity with which he was able to craft it. (Only a college student or someone working for a company about to go bankrupt would have the time to do this.) Here is the code (linebreaks were an important part of it):

Code removed because it was screwing up my layout -- to view code, click link above


The author assures us that it is now harmless on MySpace. But where else might it -- or, rather, the concept -- end up wrecking havoc?
Internet: BLACKOUT!!!
Working on my computer last night -- right in the middle of uploading some files when everything stopped. Well, not everything. Google still worked. But the site I was trying to get to -- kaput! I thought maybe I had been banned or something. Turned out, another internet outage. Details here:

Tier-1 ISPs Dying
[slashdot]

Discovered these link from the thread -- just what I was looking for last night:

Internet Health Report


Internet Traffic Report
PHP: simple blurber
A quick and dirty way to create a blurb for a text string:

$_blurb_length = 5;

if ( str_word_count($text) > $_blurb_length && preg_match('%(\w+\s){'.$_blurb_length.'}%i', $text, $_BLURB) )
{
$blurb = rtrim($_BLURB[0]) . '...';
}
else
{
$blurb = $text;
}
Baseball Cards
I have always loved the 1977 Topps baseball cards. Not sure exactly why. Probably ill-advised for a self-respecting designer to acknowledge the least influence of baseball cards. But it's always been a dream to own a near-mint condition set of these cards.

The Reggie Jackson card of that year, in particular, was always a mystery to me. Walter Pater wrote of the strange smile of the Mona Lisa. I was bewitched by that weird green of his helmet:



The mystery may in part be explained by this:



Reggie went to the Yankees in 1977 and the card was quickly switched just before its release. (Man, those old Oriole helmets were amazing!)

After the 1977 Topps, my next favorite baseball card year is '79. This was the year I started collecting baseball cards -- 18¢ a pack, if I remember correctly. It was the first set I collected in whole -- all by buying packs. You could get a box of cards for $5. What joy. Love the Reggie card this year, too:



The 1980 one's not bad either.

In truth, not a huge Reggie fan, (though I was thrilled when he signed with the Angels.) But he had some great baseball cards.
Blogger: Spam
Noticed Blogger is requiring CAPTCHA verification on posts for one of my blogs now. This concerned me a bit, as it could affect some creative projects I've been working on, like the following:

http://tgasandbox.blogspot.com/

Finally found an explanation for the new obstacle:

http://buzz.blogger.com/2005/10/on-spam.html

As I figured, spam-related. I notice I don't get the CAPTCHA field here when trying to post. The blog I'm getting it on is a new one, so maybe it's a sort of probationary thing for new blogs.

Blogger has started compiling a list of deleted spam blogs:

http://buzz.blogger.com/deleted-subdomains-2005-10-17.txt

Interesting reading.
unix timestamp
Haven't quite got to the point where I can convert unix timestamps to human being time in my head, so this site sometimes comes in handy.
Server: .htaccess files
Found this link helpful, especially for shared-hosting environments that use these files.
HTML: Yet Another Page-Centering Scheme
I find it absolutely ridiculous that there is not a simple way to vertically and horizontally center a page with style sheets. Something in the way of this:

body
{
margin:0px; padding:0px;
}
#page_content
{
width:750px;
margin:10px auto;
height:100%;
vertical-align:middle;
}


I used to just do it with a table, but that seems to be generally regarded as amateurish or old-fashioned or something among the pundits and doesn't reliably work with validating doctype declarations.

The following is my latest effort as lightweight, valid, two-axis centered page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en">
<head>
<title>Centered Page Template</title>
<style type="text/css">
<!--
html
{
height:100%;
}
body
{
margin:0px; padding:0px;
height:100%;
}
#centering_table
{
margin:0px; padding:0px;
width:100%;
height:100%;
border:none;
}
#centering_table td
{
position:relative;
width:100%;
height:100%;
vertical-align:middle;
text-align:center;
}
#page_content
{
margin:0px auto;
}
-->
</style>
</head>

<body>
<table id="centering_table"><tr><td>
<div id="page_content">
content here
</div>
</td></tr></table>

</body>
</html>


It uses a table, but only the one css-styling, and passes as valid XHTML.
CSS: background (with image)
Another formula I'm always forgetting:

background: black url('http://www.blogblog.com/rounders4/icon_arrow.gif') no-repeat top right;


Example:

PHP: strip_tags() test
A simple test for running strip_tags(). I wanted to test it with an allowed tags variable set using a heredoc:

$IR_CONFIG['form_pack']['allowed_tags'] = <<<TAGS
<div><span><pre><br><br />
<a><h1><h2><h3><h4><h5><h6><p>
<ul><li><ol>
<b><em><i><strong><u>
<table><tr><td><th>
<img><blockquote>
TAGS;


Code:

/* TEST
*******************************/

$string = <<<TEST
<div>
<script>this is a script</script>
<b>this is allowed</b>
<font>this is not allowed</font>
</div>
TEST;

$allowed_tags = $IR_CONFIG['form_pack']['allowed_tags'];
$clean = strip_tags($string, $allowed_tags);
echo htmlspecialchars($string) . '<br>';
echo htmlspecialchars($clean);

/******************************/


 
Research: iframe
Contemplating some creative uses of the iframe tag. Some sites explaining exactly what it is:

IFRAME - Inline Frame [htmlhelp.com]

The iframe tag [w3schools.com]

Remote Scripting with IFRAME [developer.apple.com]

Iframe SSI Script 2 [dynamicdrive.com]
HTML: refresh and reload
I always blank on these:

<!-- refresh page every 10 mins -->
<meta http-equiv="refresh" content="600">


// PHP variable assignment
$_HTML['head']['refresh'] = '<meta http-equiv="refresh" content="600">';


<!-- redirect page after 3 seconds -->
<meta http-equiv="refresh" content="3;url=http://phosphorusandlime.blogspot.com/">


Now if I can only remember where to find this post.
Project: Triple Threat Dancewear
This is the third version of the catalogue site I designed for Triple Threat Dancewear, but the first one that's dynamic in nature. I'll be redoing the home page within the next few days. But the heart of it is done. The major new features are:

1. browse by style page

2. browse by item

Not tied into a SQL database yet. That will the next step. Rather, the data is stored in multidimensional arrays, which is not a problem for a project of this size and rather easier to handle while scripting.
XML: Google Groups Atom XML Converted to Array
Google Group ATOM feed: http://groups.google.com/group/comp.lang.php

Array produced by my PHP XML parser:

Array
(
[FEED] => Array
(
[VERSION] => 0.3
[XMLNS] => http://purl.org/atom/ns#
[TITLE] => Array
(
[MODE] => escaped
[TYPE] => text/html
[DATA] => comp.lang.php Google Group
)

[TAGLINE] => Array
(
[MODE] => escaped
[TYPE] => text/html
[DATA] =>
PHP, server side scripting.

)

[LINK] => Array
(
[HREF] => http://groups.google.com/group/comp.lang.php
[REL] => alternate
[TITLE] => comp.lang.php
[TYPE] => text/html
)

[MODIFIED] => Array
(
[DATA] =>
2005-10-06T01:17:30Z

)

[GENERATOR] => Array
(
[URL] => http://groups.google.com
[VERSION] => 1.99
[DATA] => Google Groups
)

[INFO] => Array
(
[MODE] => xml
[TYPE] => text/html
[DIV] => Array
(
[XMLNS] => http://www.w3.org/1999/xhtml
[DATA] => This is an Atom formatted XML
site feed. It is intended to be viewed in a Newsreader or syndicated to
another site. Please visit the for more info.
[A] => Array
(
[HREF] => http://help.blogger.com/bin/answer.py?answer=697
[DATA] => Blogger Knowledge Base
)

)

)

[ENTRY] => Array
(
[0] => Array
(
[AUTHOR] => Array
(
[NAME] => Array
(
)

[EMAIL] => Array
(
[DATA] => kurt.krueckeb...@gmail.com
)

)

[ISSUED] => Array
(
[DATA] =>
2005-10-06T00:00:07Z

)

[MODIFIED] => Array
(
[DATA] =>
2005-10-06T00:00:07Z

)

[ID] => Array
(
[DATA] => http://groups.google.com/group/
)

[LINK] => Array
(
[HREF] => http://groups.google.com/group/comp.lang.php/
[REL] => alternate
[TITLE] => exec("ls *.*") gives unexpected output
[TYPE] => text/html
)

[TITLE] => Array
(
[MODE] => escaped
[TYPE] => text/html
[DATA] => exec("ls *.*") gives unexpected output
)

[SUMMARY] => Array
(
[MODE] => escaped
[TYPE] => text/html
[XML:SPACE] => preserve
[DATA] =>
The second line of this script
<?php
// current directory
echo getcwd() . "<br />";
print ( exec("ls *.*") );
?>
should display the names of the four files (it does in an ssh session)
which are located in public_html, but instead it displays just one
filename, that of this script.
I don't understand why?

)

)

)

)

)


Note: Google's feed will give you like 60 entries!
XML: Sample Atom Feed
Can never find this when I need it:

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

<title>Example Feed</title>
<link href="http://example.org/"/>
<updated>2003-12-13T18:30:02Z</updated>
<author>
<name>John Doe</name>
</author>
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>

<entry>
<title>Atom-Powered Robots Run Amok</title>
<link href="http://example.org/2003/12/13/atom03"/>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2003-12-13T18:30:02Z</updated>
<summary>Some text.</summary>
</entry>

</feed>


Must remember to look here.
PHP: Error Handling
An old error handling function that I am retiring. I have a more advanced SESSION handler that can track errors between page redirects that I use now.

/* Fx E_handler
******************************/
function E_handler($e_num, $e_msg, $filename, $linenum, $vars) {

// *** DATA

// global
global $E_TYPE, $_E_version, $E_local_mail, $E_local_log;

// internal variables
$_E_info = '';
$_bug_trace = '';

// timestamp for the error entry
$_E_stamp = date("Y-m-d H:i:s (T)");

// output styling

# console
$_console_style = 'position:relative;'
. 'padding:0; margin:2em 0 1em; text-align:left;'
. 'background-color:#683431; border:thin solid red; border-width:1px 0 10px; '
. 'color:#00ff00; font-family:sans-serif; font-size:11px;';

# titlebar
$_titlebar_style = 'position:relative; background:red; padding:.2em; '
. 'border:thin solid white; border-width:1px 0 0;';

# h2
$h2_style = 'color:white; font-family:sans-serif; font-size:12px; margin:.1em;';

# screen
$_screen_style = 'position:relative;'
. 'background:none; padding:6px 0 0;'
. 'border:thin solid red; border-width:0 4px; '
. 'text-align:left; font-family:Courier New,monospace; font-size:11px; font-weight:bold;';

# h3
$h3_style = 'padding:.2em .5em; margin:10px 0 0; '
. 'color:white; font-family:sans-serif; font-size:11px; '
. 'background:red;';

# trace box
$_trace_style = 'position:relative; height:125; overflow:auto; padding:0 .5em;'
. 'color:#dbe3e8; background:#4f6a7b; '
. 'text-align:left; font-size:10px;';


// E_STRICT: PHP5 only
if ( defined('E_STRICT') ) { $errortype[E_STRICT] = "Runtime Notice"; }


// *** MANIPULATE

// suppress array index warnings (http://www.php.net/manual/en/function.error-reporting.php)
if ( $e_num == E_NOTICE && substr($e_msg, 0, 17) == "Undefined index: " ) { return; }

// error_reporting off -> suppress
if ( error_reporting() == 0 ) { return; }

// backtrace
$BUG = debug_backtrace();
$_file = $BUG[1]['file'];
$_line = $BUG[1]['line'];
# $_bug_trace = print_r($BUG, TRUE);

// filter ehandler
$_steps = count($BUG) + 1;
foreach ( $BUG as $_fx_step ) {
$_steps--;
if ( $_fx_step['function'] == 'E_handler' ) {
continue;
}
$_bug_trace .= '<div style="border-bottom:thin solid #dbe3e8; padding:0 .5em .5em;">'
. "<h3 style=\"margin:0; text-decoration:underline;\">Function Step #{$_steps} -> {$_fx_step['function']}()</h3>"
. htmlspecialchars(print_r($_fx_step, TRUE)) . "</div>\n";
}


// html encode
# $_bug_trace = htmlspecialchars($_bug_trace);

// build error message
$_E_info = <<<EINFO

<errortype><b>$E_TYPE[$e_num]</b></errortype> ->
<errormsg style="font-style:italic; font-family:Times New Roman,serif;">$e_msg</errormsg>
(<errornum>Error #$e_num</errornum>)<br />
File: <scriptname>$filename</scriptname><br />
Line: <scriptlinenum>$linenum</scriptlinenum><br />
<datetime>$_E_stamp</datetime><br />

EINFO;

// construct trace box (display only in local or alpha versions)
$_trace_box = <<<TRACE

<h3 style="$h3_style">&raquo; debugger trace</h3>
<div id="trace_box" style="$_trace_style">
<pre>$_bug_trace</pre>
</div>

TRACE;

if ( VERSION != 'local' && VERSION != 'alpha' ) {
$_trace_box = '<!-- suppressed -->';
}

// generate output
$output = <<<ERR


<!-- ERROR CONSOLE -->
<div id="eh_console" class="error_report" style="$_console_style">

<!-- TITLEBAR -->
<div id="eh_titlebar" style="$_titlebar_style">
<h2 style="$h2_style">Error &raquo; {$E_TYPE[$e_num]}</h2>
</div>

<!-- REPORT SCREEN -->
<div id="eh_screen" style="$_screen_style">
<div id="e_info" style="padding-left:1em;">$_E_info</div>

<!-- TRACE BOX -->
$_trace_box

</div>
<!-- end SCREEN -->

</div>
<!-- end ERROR -->


ERR;

// vary output according to version

# local -> echo[, log, mail]
if ( VERSION == 'local' ) {
echo $output;
if ( $E_local_mail ) { E_mail(); }
if ( $E_local_log ) { E_log(); }
if ($e_num == E_USER_ERROR) {
die("\n<div id=\"fatal_error\"><b>FATAL ERROR at $_file: $_line</b><br />\n In the event of live application, you would be redirected to an error page</div>");
}
}

# alpha -> echo, log
elseif ( VERSION == 'alpha' ) {
echo $output;
E_log();
if ($e_num == E_USER_ERROR) {
die("\n<div id=\"fatal_error\"><b>FATAL ERROR at $_file: $_line</b><br />\n In the event of live application, you would be redirected to an error page</div>");
}
}

# beta -> echo, log, mail
elseif ( VERSION == 'beta' ) {
echo $output;
E_log();
E_mail();
if ($e_num == E_USER_ERROR) {
die("\n<div id=\"fatal_error\"><b>FATAL ERROR at $_file: $_line</b><br />\n In the event of live application, you would be redirected to an error page</div>");
}
}
# live -> log, mail fatal, redirect
else {
E_log($output, 3, E_LOG);
if ( ($e_num == E_USER_ERROR) || ($e_num == E_USER_WARNING) ) {
E_mail(SP_CONTACT, 'Critical User Error', $output);
E_redirect();
}
}


/* alpha version */
if ( $ep_version == 'alpha' ) {

}
/* beta/test_live version */
elseif ( $ep_version == 'beta' || $ep_version == 'test_live' ) {
$vartrace = "<div style=\"$trace_style\"><pre><h2>DEBUG TRACE</h2>$bug_trace</pre></div>";
echo "\n\n<!-- ERROR -->\n<div id=\"errorentry\"".$err_style.">\n".$err.$vartrace."</div>\n<!-- end ERROR -->\n\n";
}


} /* end Fx */
/*****************************/


 
Testing Blogger ATOM API
This is a test of the ATOM API:


$fp = fsockopen('ssl://www.blogger.com', 443);
if($fp)
{
fputs($fp, $data);

}
PHP: Post to Blogger
I was planning to knock this as a morning exercise off before breakfast. It's ended up being an all day order and I'm no closer to my goal than when I started.

I thought I could just use this:

PEAR::Services::Blogger PHP Client

But when I try to use it according to their example, I keep getting this error:

301:
Moved Permanently
The document has moved here.


The only problem is creating an SSL connection, which requires either cURL or rebuilding PHP with the proper extension. Ugh.
PHP: MySQL query routine
I've been using MySQL queries in PHP for quite a while now, but it still feels messy and like I'm doing it for the first time every time I have to add one to my code. I have a partner now who's taking over most the database management, but I still have to use them now and again. So I finally sat down and put together a little template:

// OPEN DB
open_db(DB_NAME);

// DB : get TIMESTAMP of last MOD REPORT

# SQL query
$SQL['select'] = <<<SQL1
SELECT `ID`, `timestamp`
FROM $table
ORDER BY `timestamp` DESC
SQL1;

# run query
$_sqlr = mysql_query($SQL['select']) or trigger_error('MySQL error number '.mysql_errno().': '.mysql_error(), E_USER_WARNING);

# fetch results
if ( mysql_num_rows($_sqlr) > 0 )
{
while ( $_ROW = mysql_fetch_assoc($_sqlr) )
{
$RESULT[] = $_ROW;
}
}
else
{
trigger_error('no records found', E_USER_NOTICE);
}

# free results
if ( is_resource($_sqlr) )
{
mysql_free_result($_sqlr);
}


Here's a skeleton of the routine:

// PHP-MySQL routine

# open DB

# SQL query

# run query

# fetch results

# free results


Thus far, it's helped immensely. I'll post my open_db() function when I have a chance.
Blogger API
I was just looking at my Blogger API for PHP and discovered, to my horror, that it is an old RPC-based class I adapted from somewhere. I was right to suppose that their ATOM protocol must be fairly well developed and found on the Blogger developer page a ready-made PHP class. Apparently, it's been around since June. They even have a wiki that lays out its usage all nice-and-easy, like I like. I'm going to start playing with this over the weekend.
CSS: csszengarden.com
If you have designs on the csszengarden site, like me, the following will help:

/* SCHEMA */

/* *** PAGE BLOCKS *** */

/* basic elements */
div { border:1px dashed black; }
body { background#666666:; }
a, a:link, a:hover, a:visited, a:active { color:yellow; font-weight:bold; }

/* container */
#container { background:#999999; }

/* intro */
#intro { background:maroon; }
#pageHeader { background:silver; }
#quickSummary { background:#ff0000; }
#preamble { background:#ff0066; }

/* supportingText */
#supportingText { background:#000066; }
#participation { background:#000099; }
#benefits { background:#0000cc; }
#requirements { background:#0000ff; }
#footer { background:#6699cc; }

/* linkList */
#linkList { background:#006600; }
#linkList2 { background:#009900; }
#lselect { background:#00cc00; }
#larchive { background:#99cc00; }
#lresources { background:#ccff00; }

/* extraDivs */
#extraDiv1 { background:red; }
#extraDiv2 { background:orange; }
#extraDiv3 { background:yellow; }
#extraDiv4 { background:green; }
#extraDiv5 { background:blue; }
#extraDiv6 { background:indigo; }


Just put it at the end of your style sheet as you work on it.
PHP: HTML Form Arrays
I've done this before... but I forgot how. So here's a reminder I hope will stick with me. To process a form array for something like, say, a checklist, give all the form elements the same name with brackets attached, something like so:

<input type="checkbox" name="CHECKBOX[]" value="1" />
<input type="checkbox" name="CHECKBOX[]" value="2" />
<input type="checkbox" name="CHECKBOX[]" value="3" />
<input type="checkbox" name="CHECKBOX[]" value="4" />


Then, when the form is submitted, to process the array, you can use the $_POST global, something like this:

$CHECKBOX = array();
$CHECKBOX = $_POST['CHECKBOX'];
foreach ( $CHECKBOX as $_value )
{
// do something
}


I'll work up a working example using javascript or something when I have a chance.
OSS: DB Designer 4
Just came across a recommendation for this. Don't really need an ERD program at present, but I'm always on the lookout for good open source coding or design tools.
MediaWiki: Changing the Navigation Side Panel
I've had to learn this at least twice now, so I figure I may as well make myself a note. To change it:

1. Go to Special:Allmessages page
2. Find link in name column you wish to change (e.g. MediaWiki:Portal)
3. Edit page and enter new label you'd like for this link (e.g. Archives)


Although the link will be relabled, it will still point to the original page (in this case, MediaWiki:Portal.) To have the link actually link to the new page, click the -url link on the Special:Allmessages page (e.g. portal-url) and edit that page, much like you edited the lable page, by entering the name of the page you wish to have it redirected to.

You can also have the old page redirect to the new one. To do this, go to that page and enter the following line at the top:

#REDIRECT [[New Page Name Here]]


But easier just to change the -url namespace itself.

For more info on redirection, see Wikipedia:Redirects
JS: blogger archive link
The following script adds a link to the current archives page for your blogger blog without requiring you to list out all the archived months. As currently configured, it required monthly archives.

Put this in <HEAD> tag:

<script type="text/javascript">

// fx blogger_last_archive()
function blogger_last_archive() {

// DATA
var arch_root="http://phosphorusandlime.blogspot.com/";
var obj_DATE = new Date();
var arch_year= obj_DATE.getFullYear();
var arch_month= obj_DATE.getMonth() + 1;
var arch_tail="_01_phosphorusandlime_archive.html";

// MANIPULATE

// uncomment if you prefer to get archive for last month
// arch_month--;

// control for year turnover
if ( arch_month == 0 )
{
arch_month = 12;
}

// convert to two-digit field
if ( arch_month > 0 && arch_month <= 9 )
{
var s_arch_month = String(arch_month);
arch_month = "0" + s_arch_month;
}

// RETURN
document.write('<a href="' + arch_root + arch_year + '_' + arch_month + arch_tail + '">archives</a>' );
}
// end blogger_last_archive()

</script>


Put this in <BODY> tag where you wish to have link appear:

<script type="text/javascript">blogger_last_archive();</script>


See archives link in column to left for result.
CSS: border-collapse:collapse
Finally looked up the CSS equivalent for the "cellspacing" attribute for the <TABLE> tag:

table.no_cellspacing
{
border-collapse:collapse;
}
PHP and MySQL: stored procedures and heredocs
Played uninspired poker again tonight, so before bed trying to wash the bad taste out of my mouth with a little light reading on stored procedures in MySQL with PHP:

http://forums.mysql.com/read.php?98,19146,19393

Doesn't look promising.

Lately, I've been using heredocs myself. Haven't had any problems thus far. You could create a file to serve as a repository of stored queries like the following:

$SQL['join1'] = <<<SQLJOIN
SELECT $_requested_fields
FROM $_table1
LEFT JOIN $_table2 ON {$_table1}.{$_foreign_key}
WHERE {$_table1}.{$_primary_key} = {$_table2}.{$_foreign_key}
SQLJOIN;


Not sure what the performance implications are exactly.
PHP: wizard driver
The template I use for my wizard drivers:

<?php

/*
Flame Wizard Driver

File: _driver.inc.php
Last Update: Nov 2005
Author: Tom Atwell (klenwell@gmail.com)

SECTIONS:

1. Configurations
2. Stages
3. Review Arrays
4. DB Packages
5. Other Wizard Arrays

*/


/* CONFIGURATIONS *******************************/

// META

# name (inactive)
$wizard_name = 'name';

# directory for STAGES include files
$wizard_dir = 'dir';

// CONTROL ARRAYS

$STAGES = array();
$REVIEW = array();
$DB = array();
$TABLE = array();

// DATABASE

# DATABASE
$DB['name'] = 'db_name';

# TABLES
$TABLE = array
(
# ID/KEY => NAME
'ref' => 'table_name',
);

# WRITE ARRAYS
$DB_WRITE['table_name'] = array();

/*______________________________________________*/


/* STAGES ***************************************/

# to add a stage, paste line: $STAGES[] = 'FILE_NAME';

$STAGES[] = 'next_stage';


/*______________________________________________*/


/* REVIEW ARRAYS ********************************/

$REVIEW['group'] = array
(
'field_name'
);

/*______________________________________________*/


/* DATABASE ARRAYS ******************************/

// INSERT/UPDATE

# table_name
$DB_WRITE['table_name'] = array
(
'field_col1',
'field_col2'
);

/*______________________________________________*/


/* AUXILIARY ARRAYS *****************************/


$MENU['name'] = array
(
'item label' => 'value'
);

/*______________________________________________*/

?>
Unauthorized Access
I'm sorry. You do not have access to that page. If you believe you have been redirected to this page in error, please contact Tom at klenwell@gmail.com.