lime icon

Phosphorus and Lime

A Developer's Broadsheet

This blog has been deprecated. Please visit my new blog at
PHP/MySQL: updating rows with ADOdb Lite
Two ways to create a ADODB Lite object. The second, using DSN (Data Source Name) allows you to set a flag that will count updated rows even when they aren't changed. (This is significant if you are a defensive programmer like me.)

Simple Instantiation
$DBSingle =& ADONewConnection($db_type);
$DBSingle->db_name = $db_name;
if ( !$DBSingle->Connect($db_server, $db_user, $db_pass, $db_name) ) trigger_error('unable to connect to database [' . $db_name . ']', E_USER_WARNING);

DSN Instantiation
$_pw = urlencode($db_pass);
$_flags = 2;
$_dsn = "mysql://$db_user:$_pw@$db_server/$db_name?clientflags=$_flags";
if ( !$DBSingle =& ADONewConnection($_dsn) ) trigger_error('unable to connect to database [' . $db_name . ']', E_USER_WARNING);

ADO Documentation: DSN Support

keywords: php, mysql, adodb, adodb_lite, update, count, dsn
PHP Greqo: Blogger Post Demo
I've updated the Greqo code to handle classic and beta accounts transparently -- well, almost. If you set up your script with the basic undifferentiated settings, like this, it will still work for either type of account. However, since it will try to authenticate using the Google Account ClientLogin interface as a first step, classic accounts will throw an error here. Nevertheless, as in the test script linked above, the request should still succeed.

To clarify how it works, consider the following code samples.

User Settings and Greqo Setup
// *** Google User Settings (keep safe!)
$_GOOGLE['user'] = 'jqblogger';
$_GOOGLE['pw'] = 'supersafe123';

// *** Blogger User Settings (for Blogger beta accounts, these will be the same)
$_BLOGGER['user'] = $_GOOGLE['user'];
$_BLOGGER['pw'] = $_GOOGLE['pw'];
$_BLOGGER['blog_id'] = '123456'; // if not sure, right click on link in your Blogger dashboard';

// *** Load Files
$_path_to_greqodir = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR;
require_once($_path_to_greqodir . '');

// Important! (this function will globalize user settings and is required)
greqo_blogger_set_user_data($_BLOGGER['user'], $_BLOGGER['pw'], $_BLOGGER['blog_id']);

Generic Blogger Post
note: this will work for either type of account but will throw an error for classic account when it tries to authenticate under the new Google Accounts API
// prepare request (this will throw an error with classic Blogger accounts but post should still succeed)
if ( !greqo_request_clientlogin_token($_GOOGLE['user'], $_GOOGLE['pw'], 'blogger', 0) ) trigger_error('unable to get token');
if ( !greqo_blogger_request_api_url(0) ) $_SESSION['GDATA']['BLOGGER']['api_url'] = '';

// post
if ( $POST_DATA = greqo_blogger_post_entry($test_post_body, $test_post_title)
echo '<p style="color:green;">post succeeded</p>';
$_response = print_r($POST_DATA,1);
echo "<pre>{$_response}</pre>";
echo "<p><a href=\"{$POST_DATA['self_link']}\">click to see post</a></p>";
echo '<p style="color:red;">post failed</p>';

Now, if you know you are dealing with a classic (non-beta) account and wished to avoid the error warnings, remove the Google authentication part and just manually set the Blogger api url:

Classic Blogger Post
// prepare request (removed for Classic Blogger -- uncomment for new beta)
#if ( !greqo_request_clientlogin_token($_GOOGLE['user'], $_GOOGLE['pw'], 'blogger', 0) ) trigger_error('unable to get token');
#if ( !greqo_blogger_request_api_url(0) ) $_SESSION['GDATA']['BLOGGER']['api_url'] = '';

// manually set Blogger api url
$_SESSION['GDATA']['BLOGGER']['api_url'] = '';

// post
if ( $POST_DATA = greqo_blogger_post_entry($test_post_body, $test_post_title)
echo '<p style="color:green;">post succeeded</p>';
$_response = print_r($POST_DATA,1);
echo "<pre>{$_response}</pre>";
echo "<p><a href=\"{$POST_DATA['self_link']}\">click to see post</a></p>";
echo '<p style="color:red;">post failed</p>';

Greqo Google Code Home
PHP: Greqo Google Data API
A couple weeks ago, Google announced the release of a new Blogger Data API for PHP: Ngeblog. Ngeblog uses the Zend Framework's HTTP Client to authorize Google accounts -- and therein lies its one fatal shortcoming, at least for me. The Zend Framework requires PHP 5 -- and most my hosts are still on PHP 4.4.

So I've developed an alternative that uses the PEAR HTTP Client and Magpie XML classes. It's called Greqo (Google REQuest Object -- though it's not really an object-oriented architecture.) I've created a Google Project for it and uploaded it there. Right now, it can authenticate a Google account (using the ClientLogin interface), retrieve a Blogger feed, and post a new entry to a Blogger blog. I'll add functions to edit and delete posts as I have time.

It's simple to load. Just start a session and call the greqo_driver. An example:


// *** Load Files
$_parent_dir = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR;
require_once($_parent_dir . '');


A full demo is available in the Google Code repository:
wikka comment spam
Noticed I was getting an unusual number of hits all of the sudden on my mushpup wikka wiki. At first, I thought: "At last, people are finally catching on."

Then I looked where they were coming from. Primarily, Google. But they weren't searching for "mushpup" or "passwords". Instead, it was more like, "nubile rapidshare" and "Aneta TeenPinkVideo". Hmmmm... I don't remember posting anything about pink videos.

Well, I did a Google search on these terms, found the mushpup listing and figured it out: wikka comment spam.

Googled for a shotgun solution. Didn't really find one but came up with one pretty quickly.

Three steps:

1. Backup your database
Now as good as time as any. Especially if you screw up one of the next two steps.

2. Change wikka.config.php settings
The key ones default_write_acl and default_comment_acl. Set to the following:

'default_write_acl' => '+',
'default_comment_acl' => '+',

This will limit page editing and commenting privileges to registered users.

These are the global settings. Access can also be limited on a per-page basis by the through the admin web interface.

info on other settings here:

3. Delete any existing spam comments
This is pretty easy since they're all in a simple table named something like wikka_comments.

phpMyAdmin makes it simple to delete the offenders. Or you could use a statement like this:

DELETE FROM `wikka_comments` WHERE user=""

( was the actual ip adding most the spam.)

keywords: wiki, wikka, spam, comments, comment spam, mysql, phpmyadmin
This is one of those things I keep having to look up. It involves the old target attribute you use in <a> links to open the link in a new window. This attribute is deprecated in XHTML strict and HTML 4.0 -- or so I hear.

Here, in any event, is the workaround:

onclick=",'_blank');return false;"

So this:

<a href="" target="_blank"></a>

Would become this:

<a href="" rel="external" onclick=",'_blank');return false;"></a>

(The rel attribute is not necessary but can be used for javascript functions to the same effect.)


keywords: link, links, anchor, blank, _blank, target, xhtml
PHP: filepath to URL
I've been wrestling with some complication with sessions and domains names recently, so this thread on comp.lang.php caught my attention. It says basically, if you're not careful, you could have a user on your site lose their session if they were to somehow move from say to, your user will lose their session.

I offer my solution to this problem here (I think this is the same concept used by most popular applications such as Joomla). The functions below may also be useful. The first has evolved over a long period of time. It hasn't failed me yet, though I don't know that it is the optimal solution to this particular challenge.

// filepath_to_url
function amvc_filepath_to_url($filepath)
// *** DATA

// internal
$_slash = '/';
$_url_rel = '';

// paths
$PATH = array();
$PATH['file'] = amvc_normalize_path($filepath, $_slash);
$PATH['www'] = 'http://' . $_SERVER['HTTP_HOST'] . $_slash;

// directory arrays
$_DIRS = array();
$_DIRS['filepath'] = explode($_slash, $PATH['file']);
$_DIRS['doc_root'] = explode($_slash, amvc_normalize_path($_SERVER['DOCUMENT_ROOT'],$_slash));
$_DIRS['diff'] = '';

// return
$url = '';


// * DEBUG

// path is url
if ( preg_match('%^(http)s?://%i', $PATH['file']) ) return $PATH['file'];

// compute path differences
$_DIRS['diff'] = array_values(array_diff($_DIRS['filepath'], $_DIRS['doc_root']));


// shift empty cells
if ( empty($_DIRS['diff'][0]) ) array_shift($_DIRS['diff']);

// get URL relative path
$_url_rel = implode('/', $_DIRS['diff']);

// build URL
$url = $PATH['www'] . $_url_rel;

// *** RETURN

return $url;

// amvc_normalize_path
function amvc_normalize_path($path, $slash)
// *** DATA

// internal

// return var
$new_path = '';


// replace forward slashes
$new_path = str_replace('/', $slash, $path);

// replace backslashes
$new_path = str_replace('\\', $slash, $new_path);

// *** RETURN

return $new_path;