Skip to content
Oct 12 11

IE innerHTML CSS bug

by Jonathon

Today I encountered a bug that affects every version of Internet Explorer from IE6 all the way through IE9. Here’s what happens.

The Bug

  1. Set a Javascript string variable containing CSS styles (<style> or <link>) followed by some HTML content, such as a <p> tag (in my application this happened via JSONP).
  2. Add a <div> node to the DOM
  3. Insert the content from #1 into the .innerHTML property of the <div>
  4. The content gets inserted, devoid of CSS styles.
<!-- these styles are ignored when inserted via innerHTML -->
<style>
  p { color: blue; }
</style>
<p>Hello, how are you!</p>

The Solution

The simple solution was to put the other markup before the <style> or <link> tag. Bizarre.

<p>Hello, how are you!</p>
<!-- these styles are applied when inserted via innerHTML -->
<style>
 p { color: blue; }
</style>

Because IE needs something that affects the layout before it will apply CSS styles in an innerHTML property, these will not work as style triggers:

  • <!-- a comment -->
  • <p></p> (or any other empty block-level HTML tag)
Oct 8 11

Writing MySQL schema migrations: best practices

by Jonathon

Building MySQL web applications with a team of developers will inevitably present the challenge of database schema changes. Any good web developer understands the importance of keeping all code under version control, but how many follow the same principle for the database?

What follows are some best practices that I have learned over the past several years from the school of hard knocks. read more…

Oct 6 11

Detecting mobile browsers via Javascript

by Jonathon

Sometimes you want to do some sort of special processing in Javascript on mobile browsers, or vice versa. Here’s a quickie Javascript way to do that:

function is_mobile() {
	var agents = ['android', 'webos', 'iphone', 'ipad', 'blackberry'];
	for(i in agents) {
		if(navigator.userAgent.match('/'+agents[i]+'/i')) {
			return true;
		}
	}
	return false;
}

This version is fast and easy to extend to detect additional browsers by changing the agents array. Here’s a much more thorough version, modified from detectmobilebrowsers.com:

function is_mobile() {
	return /android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4));
}

Since this is a single gigantic regex, I suspect that it would run more slowly than the previous version, and is also more challenging to modify.

Instead of browser user-agent sniffing, you might want to consider using browser feature detection instead to tailor your application to the capabilities of the browser, rather than relying on the userAgent string (which can be customized in most browsers).

Enjoy!

Oct 4 11

Enabling a Dell Wireless 1370 network adapter (BCM4328) on Ubuntu Linux

by Jonathon

After three recent virus infections on Windows XP and Windows 7 (including at least one rootkit infection), I turned to Ubuntu Linux as a safer operating system. Two of the PCs were blessed with Atheros-based wireless network adapters, which are well-supported on Linux. The other laptop, a Dell Inspiron 2200, is blessed with one of those infamous Broadcom chipsets.

Supposedly, the BCM4328 (rev 02) wireless chipset is supported on Linux, but as of Ubuntu Desktop 11.04 and Linux Mint 11, it doesn’t work reliably. So I turned to the old tried-and-true ndiswrapper to run the BCM4328 Windows driver under Linux. read more…

Sep 30 11

http_build_query(): surprise!

by Jonathon

Did you know that PHP’s http_build_query() drops any key from your query array if the value is NULL? I wonder how many subtle API client bugs are caused by this behavior.

The workaround is easy, just set values that should be intentionally empty to boolean FALSE, integer 0, or an empty string.

Sep 30 11

Master-Master MySQL Replication…that hurts less

by Jonathon

If you have ever touched a MySQL slave, you know that they can and do frequently halt. While sync problems can be caused by many things—network outages, schema changes, etc—one of the most common problems in a dual-master setup is primary key collision.

Primary Key Collision

…happens when records are added on two different servers to the same table and get the same AUTO_INCREMENT value. Fortunately, there is a trivially easy way to prevent this from happening.

auto-increment-increment=N

Adding this to your my.cnf or my.ini file will make AUTO_INCREMENT increment by N rather than by 1. N is the number of replicated servers that are masters.

Combine this setting with…

auto-increment-offset=N

…to ensure that each replicated master uses unique AUTO_INCREMENT values. N should match the server-id setting.

With these two settings in place, your primary keys will never collide. read more…

Jun 11 11

Heartbeat logging while consuming Twitter streams using Phirehose

by Jonathon

Phirehose is an awesomely useful Twitter Streaming API client library, written in PHP by Fenn Bailey.

Heartbeat logging is something that I originally added for Rainmaker, and I finally got around to contributing those modifications, which you can see here on GitHub.

Why log heartbeats in Phirehose?

  • To gain assurance that Phirehose is still alive, and actually functioning. In our case, missing tweets means lost money and unhappy clients. We needed to monitor this very closely.
  • To enable automatically detecting connection drops and rewinding the count parameter to pick up those tweets, or backfilling them in using the Twitter Search API.
  • To collect usage data for reporting purposes.

Usage

To use this, simply declare a heartbeat(array $data) method in your Phirehose child class. read more…

May 26 11

Solved: “Access Denied” errors when calling signtool.exe from PHP

by Jonathon

SIGHntool, why must you give me such grief?

I have spent the last 8 hours trying to figure out why Microsoft’s signtool.exe code signing utility refuses to work when called from PHP’s system() or shell_exec() functions on my WAMP server:

C:\build> "C:\Program Files\InstallMate 7\Tools\signtool.exe" sign /v /f codesignedcert.pfx Setup.exe 2>&1

The following certificate was selected:
    Issued to: <redacted>.
    Issued by: UTN-USERFirst-Object
    Expires:   5/12/2012 6:59:59 PM
    SHA1 hash: <redacted>

Done Adding Additional Store

Attempting to sign: C:\build\Setup.exe

Number of files successfully Signed: 0
Number of warnings: 0
Number of errors: 1

SignTool Error: ISignedCode::Sign returned error: 0x80090010

	Access denied.

SignTool Error: An error occurred while attempting to sign: C:\build\Setup.exe

Note: the 2>&1 at the end of the signtool call is essential if you want to capture error messages which are emitted to STDERR instead of STDOUT. Yes, I lost an hour or two just on that.

Dead ends

  • Windows 7 apparently sets the read-only attribute on all files, and it isn’t easy to turn that attribute off. But since other file operations worked from PHP, this wasn’t the issue.
  • Prefacing the signtool call with CMD /C didn’t help.
  • Setting full control file permissions on the C:\build folder for Guest, SYSTEM, and any other user account I could think of didn’t help either.
  • Wrapping signtool in a batch file was an exercise in futility.

The maddeningly frustrating thing was that signtool worked great when called from the command line — just not from PHP!

An aha! moment

The issue turned out to be pretty stupid, as they usually do. I merely had to change the account that Apache was running as to that of a normal user, instead of the default local system account.

services.msc - changing the Apache user

May 5 11

Uploading large files: covering all the bases

by Jonathon

When uploading a file to a PHP script on an Apache web server, there are several configuration options that if improperly set can get in the way. I just encountered yet another one of these, and decided to catalog them here.

Size, Time, and Memory

There are three types of limits that affect file uploads, and the weakest link in the chain is your effective limit.

If your size limit is set to 3gb, but your time limit does not allow for the time required to upload that much data, you’ll still be unable to upload those large files. Likewise, the ability to upload does no good if you do not have enough memory to process the file that was uploaded.

Assumptions

This post assumes an 8MB upload limit (8mb x 1024kb x 1024 bytes = 8388608). You will want to adjust this number up or down according to your needs.

Oddly, although 8mb is the default value for PHP’s upload_max_filesize setting, some of the other default settings are much lower (2mb, or in some cases, only 100k).

PHP limits

upload_max_filesize = 8388608
post_max_size = 8388608
max_input_time = 60

Depending on what you’re doing with the uploaded files, you may also need to increase your memory limit:

memory_limit = 64MB

Apache limits

If LimitRequestBody is set to something non-zero, you may need to increase its value in your Apache httpd.conf file or .htaccess file:

LimitRequestBody 8388608

If you are using mod_fcgid (required to run the latest PHP 5.3 VC9 NTS build for Windows), then you need to set the value of FcgidMaxRequestLen, which defaults to 100k if it is not set. (Note that some systems may put mod_fcgid settings in a file separate from the main httpd.conf file).

FcgidMaxRequestLen 8388608
FcgidIOTimeout 60

Happy uploading!

Apr 28 11

A plugged-in-and-giving-back mentality

by Jonathon

While working on the Chrome version of a browser extension for a client of my company, I subscribed to the chromium-extensions Google group. I did this both for my own edification as well as for others’ benefit.

Every now and then, a message comes through that relates to my personal experience or knowledge which has not yet been answered, and I will answer it.

When you contribute to an open-source community, it does two things: it builds your reputation within the community, and it increases the likelihood of getting quality help yourself when you need it. There’s also the bonus of promoting your company if you use an email signature. This has SEO value since most of the time these messages wind up in search engines.

Besides chromium-extensions, I am also subscribed to phirehose-users (my participation here earned commit access as an official contributor), twitter-development-talk, and some local PHP user groups lists.

I wish this kind of plugged-in-and-giving-back mentality were more prevalent. If you use an open-source project or technology, subscribe to the mailing list (or forum), and don’t just lurk. Answer questions, and where appropriate, share your code or your experiences.