You are here

php

Using the hook_user for user validation such as preventing an email for a username

I run a Drupal 6 site that gets a lot of registrations every day and sometimes people register with their email address as their username. The problem is that the username is displayed anytime that user posts content on the website so we try to discourage this. I finally got fed up and just wrote a bit of code that uses hook_user and would go into your custom module. It looks something like this:


function custommodule_user($type, &$edit, &$account, $category = NULL) {
  if ($type == 'validate') {
    $regexp = '/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/';
    if (preg_match($regexp, $edit['name'])) {
      form_set_error('name', "Do not enter an email address 
        in the username field as it is publicly viewable.");
    }
  }
}

Replace custommodule with the name of your module. The rest of the code simply looks to see if the validate operation is being performed, checks the name form field and does a simple preg_match to see if it looks like an email.

Parse emails and insert into a MySQL Database using PHP

I had a problem where I needed to parse emails and insert them into a MySQL database in real time as they are received. This is a relatively easy problem to figure out, but I wanted to outline the steps I took to tackle this in case anyone else wanted simple, step by step instructions on how to solve this problem.

First of all, this solution uses Linux, specifically I like to use Ubuntu. I also used Postfix, an open source mail server. Finally, the mail script is based on Eric London's mail parsing script so he did all the mail heavy lifting. I simply added some database stuff to insert it into a MySQL table using PDO. Props to Eric for a great script that simplified this task for me.

To start, you will need a linux server. I use Amazon EC2 instances in the cloud so I just fire up one of alestic's AMIs and bam, server. I make sure the security preferences allow incoming on port 25 (SMTP). (Note: if you are having issues, it could be a security / firewall issue.) For testing purposes I use the micro AMI because they are inexpensive.

Once I have SSH-ed into the box, I run a few commands to make sure everything is up to date and that I have all the tools and software I need installed.


apt-get update && apt-get upgrade -y
tasksel install lamp-server
apt-get install postfix php5-cli

You don't actually need Apache for what we are doing here but I am installing the entire lamp stack for ease and simplicity and I will be using it later.

Let's create our database called email and the mail table where we will store the incoming email:


mysql -p
create database email
CREATE TABLE `mail` (
`mid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created` int(10) unsigned NOT NULL DEFAULT '0',
`body` longtext,
`return_path` varchar(255) DEFAULT NULL,
`x_original_to` varchar(255) DEFAULT NULL,
`delivered_to` varchar(255) DEFAULT NULL,
`mailto` varchar(255) DEFAULT NULL,
`subject` varchar(255) DEFAULT NULL,
`message_id` varchar(255) CHARACTER SET latin1 DEFAULT NULL,
`maildate` int(11) DEFAULT NULL,
`mailfrom` varchar(255) DEFAULT NULL,
`added` tinyint(3) unsigned NOT NULL,
`content_transfer_encoding` varchar(255) DEFAULT NULL,
PRIMARY KEY (`mid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='incoming mail';

Its important that the default charset is UTF8 because this can be a problem if you are in the UK or other countries where character set issues can happen.

Now we are going to make a folder in our home directory and put our mail script in there:


mkdir /home/default/scripts

I have attached the mail.php script file to this post so grab it and put it in your scripts directory that you created above. (Its named mail.php_.txt, you you will need to rename it to mail.php.)

You will need to edit the configuration variables at the top of the file to connect to the database properly and send emails to your email address if there are errors. Another thing to keep in mind is that the script uses PDO so your version of PHP needs to support that (PHP 5.1 or greater).

Now we edit our Postfix aliases file to pipe mail from a specific account to a script that we will be added later:


nano /etc/aliases

Add a line at the bottom of the file:


import: "|php -q /home/default/scripts/mail.php"

Import is the name of the account so if you want the beginning of the email address (before the @) to be something different, change this.

Finally, you are going to need to setup some DNS entries to route mail to your linux server: an A and MX record. There are lots of resources on how to do this on the Internet. Lets say the email address you were using for this was import@server.domain.com, you would need two records to make this work:

server.domain.com. IN A 50.50.50.50
server.domain.com. IN MX 10 server.domain.com.

If you don't know what any of this means, hopefully you have access to a web interface that can set this up for you.

Assuming everything went well, you should be able to send email to your linux server and it will be inserting the mail into your database table. Enjoy!

Cron, PHP and Drupal development (using the cron hook)

A while back I talked about doing development using the cron hook and how it was not very intuitive to use because if you have an error, it never gets displayed.

There is a handy module called Elysia Cron that allows you to have granular control over your cron jobs and how they are run. You can run functions in your module by clicking on them or set them to run at whatever intervals you want. All Drupal websites that have any sort of custom cron jobs need this module. Unfortunately, there is only a Drupal 6.x stable release right now.

So when you are creating a cron hook in your module, simply create a custom function that does whatever task you need to get done. You can then run this function manually in Elysia Cron to debug and make sure it produces the desired results. And then when its ready for prime time, you can call your custom function in Elysia Cron's configuration, or just add your custom function call in the cron hook function of your module and you are ready to rock.

Dealing with different character sets

I've been working on a project using MySQL, PHP and Querypath, an XML / HTML document parser where I need to store content retrieved from webpages into MySQL tables. I'm using the standard cURL to retrieve the pages, Querypath to parse them and then insert data into various tables in a MySQL database.

Everything worked great when dealing with US and Canadian websites. Then I started trying to do similar stuff on some UK websites and weird, random issues starting popping up that I couldn't really explain. One problem was that I didn't have the character set for my MySQL tables set to UTF-8 so that was an easy fix. But some pages were still parsing correctly and others weren't for no apparent reason.

After smashing my head against the wall for a few hours, it popped into my head that it could be a character set problem even though because I never had this problem with the US websites. I simply changed the options on my Querypath object as follows:

$qp = htmlqp($html, NULL, array('convert_to_encoding' => 'utf-8'));

The weird thing about this is that Querypath should be able to detect the character set because its even set in the document but maybe not. Anyways, this fix solved my problems.

Removing querystring variables from a URL in PHP

Recently I was trying to remove some variables / keys from a URL in PHP. I wanted a simple function that I could pass a URL and an array of keys to and that would return a modified URL without the keys I specified to have removed. The resulting solution turned out not as simple as I had hoped, but here it is anyway:

function remove_querystring_vars($url, $keys) {

$url = parse_url($url);

parse_str($url['query'], $query);

foreach ($keys as $key) {
unset($query[$key]);
}

$url['query'] = http_build_query($query);

$value = glue_url($url);

return $value;

}

// $parsed is a parse_url() resulting array
function glue_url($parsed) {

if (! is_array($parsed)) return false;

if (isset($parsed['scheme'])) {
$sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://');
$uri = $parsed['scheme'] . $sep;
} else {
$uri = '';
}

if (isset($parsed['pass'])) {
$uri .= "$parsed[user]:$parsed[pass]@";
} elseif (isset($parsed['user'])) {
$uri .= "$parsed[user]@";
}

if (isset($parsed['host'])) $uri .= $parsed['host'];
if (isset($parsed['port'])) $uri .= ":$parsed[port]";
if (isset($parsed['path'])) $uri .= $parsed['path'];
if (isset($parsed['query'])) $uri .= "?$parsed[query]";
if (isset($parsed['fragment'])) $uri .= "#$parsed[fragment]";

return $uri;
}

?>

Basically, its two functions. The first function called remove_querystring_vars expects the url and a simple array of keys to be passed to it. The second function glue_url is required as a helper function to re-assemble the url after its dismantled. Unfortunately, there isn't an opposite function to parse_url built into the PHP library so this function is required.

Here is a sample usage:

<?php

$keys = array("foo", "foo2");
$url = "http://www.myurl.com/somepage.php?key=value&foo=bar&foo2=bar2&key2=value2";
print remove_querystring_vars($url, $keys);

?>

This should return:


http://www.myurl.com/somepage.php?key=value&key2=value2