I’m not doing .net for now — I’ve gotten interested in PHP, so I’m learning PHP while working on it. There are a shocking number of baffling things in the beginner stage; good thing I was mentally prepared. Last night I ran into a problem: in PHP, session_start() always threw an error:

Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at D:wwwrootEZineTest.php:1) in D:wwwrootEZineTest.php on line 1

I searched all over the web and found three explanations and fixes for it:

  1. There can be no other output before the session_start(); statement (this is mostly the case that reports: Cannot send session cache limiter headers already sent)
  2. The session save path isn’t set; you need to change session.save_path = “C:/phpsession” in php.ini [set the path after it yourself, and make sure it exists. The php.ini file is generally located in the system drive/Windows directory]
  3. Change session.auto_start = 0 in php.ini to session.auto_start = 1

I was so frustrated. Point one: my session_start(); statement is on the first line. Point two: it is set. Point three: I don’t want to set it to auto.

I was really annoyed. Since I was testing on the server machine at home, I suspected it was a config problem, so I compared its PHP.INI against the config at the data center and found nothing suspicious. In the end I grabbed another PHP file I’d written that used sessions and tested it — and it worked! That proved it wasn’t a config problem. Finally I went ahead and deleted the problem file down to a single line and tested it — it still errored. Heh heh, with the scope narrowed down to that, it suddenly hit me: could this be a file gene problem?! I checked, and sure enough — a UTF-8-encoded PHP source file can’t session_start(); an ANSI one can.

Heh, so the problem can be solved: Method 1. Encode all files as non-UTF8; Method 2. Make the PHP engine support UTF-8-encoded source files (the following material is quoted from: http://phorum.study-area.org/index.php/topic,36484.html)

****** Fully adopt UTF-8 across the site. ******

1. Use vi /etc/httpd/conf/httpd.conf to set the charset in Apache to: (remember to restart) AddDefaultCharset UTF-8

2. Use vi /etc/php.ini to set the charset in php to: (remember to restart) default_charset = “utf-8”

3. Use vi /etc/my.cnf to set the charset in MySQL to: (remember to restart) [mysqld] init_connect=’SET NAMES utf8’ default-character-set=utf8 [client] default-character-set = utf8

4. When creating the database, choose the charset: (remember to clear the DB cache) DROP DATABASE IF EXISTS `aa`; CREATE DATABASE `aa` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; USE `aa`; CREATE TABLE IF NOT EXISTS `aat` ( `id` char(1) NOT NULL default ‘1’, `myStr` varchar(200) default NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

5. Use UltraEdit (v11.20a) to convert all ANSI-format php files to UTF-8 format: File –> Conversions –> ASCII to UTF-8 (Unicoding Editing) (in UltraEdit press Advanced –> configuration –> File Handling –> Unicode/UTF-8 Detection –> check Auto detect utf-8 files). If needed, you can run Remove BOM.php. When you use WinXP’s Notepad to convert a php file from ANSI to UTF-8, the BOM at the head of the file causes layout problems, so it must be removed — running Remove BOM.php removes it automatically. Remove BOM.php can be downloaded from: http://www.hoyo.idv.tw/hoyoweb/document/view.php?sid=13&author=hoyo&status=view 6. In the php file you must add:

7. In the file that connects to the DB, you must add 3 lines of mysql_query for it to work: $host=”localhost”; $DBname=”aa”; $user= “root”; $passwd = “”; $link = mysql_connect($host,$user,$passwd) or die (“Fail”); $db = mysql_select_db($DBname, $link) or die (“Fail”); // Before you actually query the DB to fetch data, add the following 3 lines mysql_query(“SET NAMES ‘utf8’”); mysql_query(“SET CHARACTER_SET_CLIENT=utf8”); mysql_query(“SET CHARACTER_SET_RESULTS=utf8”); $sql = “select * from aat where crid=’1’”; $rows = mysql_query($sql);

8. In php files, note this if needed: [Optional] When using htmlentities and htmlspecialchars, do it like this: $chars = htmlentities($chars,ENT_QUOTES,”UTF-8”); $chars = htmlspecialchars($chars,ENT_QUOTES,”UTF-8”); And before displaying, use $chars = html_entity_decode($chars,ENT_QUOTES,”UTF8”); If you’ve used addslashes() or mysql_real_escape_string(), remember to use the following: $chars = stripslashes($chars); If needed, you can use the following function to convert between encodings: $chars = iconv(‘Big5’,’UTF-8’,$chars); //convert from Big5 to UTF-8

Postscript: If you use Notepad to edit a UTF-8 file, saving it automatically adds a BOM header, and this BOM header is 3 bytes of invisible characters that cause PHP to output content too early when executing. Solution: delete the BOM header — you can use the UE text editor to save as UTF-8 text without a BOM header, or you can create a PHP script specifically for stripping BOM headers, put it in the site root directory, and run it once:

if (isset($_GET[‘dir’])){ //config the basedir
$basedir=$_GET[‘dir’];
}else{
$basedir = ‘.’;
}

$auto = 1;

checkdir($basedir);

function checkdir($basedir){
if ($dh = opendir($basedir)) {
while (($file = readdir($dh)) !== false) {
if ($file != ‘.’ && $file != ‘..’){
if (!is_dir($basedir.”/“.$file)) {
echo “filename: $basedir/
$file “.checkBOM(“$basedir/$file”).”
“;
}else{
$dirname = $basedir.”/“.
$file;
checkdir($dirname);
}
}
}
closedir($dh);
}
}

function checkBOM ($filename) {
global $auto;
$contents = file_get_contents($filename);
$charset[1] = substr($contents, 0, 1);
$charset[2] = substr($contents, 1, 1);
$charset[3] = substr($contents, 2, 1);
if (ord($charset[1]) == 239 && ord($charset[2]) == 187 &&
ord($charset[3]) == 191) {
if ($auto == 1) {
$rest = substr($contents, 3);
rewrite ($filename, $rest);
return (“BOM found,
automatically removed.”);
} else {
return (“BOM found. “);
}
}
else return (“BOM Not Found.”);
}

function rewrite ($filename, $data) {
$filenum = fopen($filename, “w”);
flock($filenum, LOCK_EX);
fwrite($filenum, $data);
fclose($filenum);
}
?>