The online racing simulator
PHP >= 5.3 - LfsString Class (LFS string conversion)
I have been working with dawesdust_12 on a new PHP LfsString class to replace the old codepage and color code converter functions I posted here.

The class uses iconv for character conversion. We found that this is the fastest conversion method available and will be supported by many PHP installs.
I suppose support for other conversion libraries could be added, but maybe it's not needed.

This class hopefully does everything right. I've tested it rigorously, but who knows some bugs may still exist.

I have also benchmarked every change i made and have tried many methods of conversion, to make sure everything is as fast as can be.
Can you find more optimisations?

Here is the class. The interface describes which methods you can use :
<?php

interface iLfsString
{
// Converts everything into a UTF-8 html string.
// Assumes the raw LFS string is UTF-8. If it isn't, you have to indicate it.
public static function convert($s, $fromCp = 'UTF-8');

// Converts everything into a UTF-8 (html) string, but strips colors.
// Assumes the raw LFS string is UTF-8. If it isn't, you have to indicate it.
public static function convertWithoutColor($s, $fromCp = 'UTF-8');

// Convert only color codes into html.
// Assumes a raw LFS string.
public static function convertColor($s);

// Strip color codes.
// Assumes a raw LFS string.
public static function stripColor($s);

// Prepare a raw LFS host name with or without color codes, for linkage.
// Assumes the raw hostname is UTF-8. If it isn't, you have to indicate it.
// Output is a cp1252 rawurlencoded string.
public static function encodeLfsUrl($s, $fromCp = 'UTF-8');
}

class LfsString implements iLfsString
{
public static function convert($s, $fromCp = 'UTF-8')
{
// Conversion only works if the raw string is in CP1252
if ($fromCp != 'CP1252')
$s = iconv($fromCp, 'CP1252', $s);

return str_replace('^', '^',
self::writeColor(
self::convertLfsSpecialChars(
self::codepageConvert(
str_replace('^^', '^', $s)))));
}

public static function convertWithoutColor($s, $fromCp = 'UTF-8')
{
// Conversion only works if the raw string is in CP1252
if ($fromCp != 'CP1252')
$s = iconv($fromCp, 'CP1252', $s);

return str_replace('^', '^',
self::unWriteColor(
self::convertLfsSpecialChars(
self::codepageConvert(
str_replace('^^', '^', $s)))));
}

public static function convertColor($s)
{
return str_replace('^', '^^',
self::writeColor(
str_replace('^^', '^', $s)));
}

public static function stripColor($s)
{
return str_replace('^', '^^',
self::unWriteColor(
str_replace('^^', '^', $s)));
}

public static function encodeLfsUrl($s, $fromCp = 'UTF-8')
{
// LFS expects the host name in cp1252
if ($fromCp != 'CP1252')
$s = iconv($fromCp, 'CP1252', $s);

return rawurlencode($s);
}

// Private parts
private static $colorCodes = array(
'000', // 0
'F00', // 1
'0F0', // 2
'FF0', // 3
'00F', // 4
'F0F', // 5
'0FF', // 6
'FFF' // 7
);

private static $codepages = array(
'L' => 'CP1252', // Latin 1
'G' => 'CP1253', // Greek
'C' => 'CP1251', // Cyrillic
'E' => 'CP1250', // Central Europe
'T' => 'CP1254', // Turkish
'B' => 'CP1257', // Baltic
'J' => 'CP932', // Japanese
'S' => 'CP936', // Simplified Chinese
'K' => 'CP949', // Korean
'H' => 'CP950' // Traditional Chinese
);

private static $specialPtrn = array('&', '<', '>', '^h', '^d', '^s', '^c', '^a', '^q', '^t', '^l', '^r', '^v');
private static $specialRepl = array('&', '^l', '^r', '#' , '\\', '/' , ':' , '*' , '?' , '"' , '<' , '>' , '|');
private static $specialReplHtml = array('&', '^l', '^r', '#', '\\', '/', ':', '*', '?' , '"', '<', '>', '|');

private static $found = false; // static temp var
private static function writeColor($s)
{
self::$found = false;
$replaced = preg_replace_callback(
'#\^([0-9])#',
function (array $matches)
{
if ($matches[1] < 8)
{
$return = (self::$found ? '</span>' : '').'<span style="color:#'.self::$colorCodes[$matches[1]].';">';
self::$found = true;
}
else
{
$return = self::$found ? '</span>' : '';
self::$found = false;
}
return $return;
},
$s);
return self::$found ? $replaced.'</span>' : $replaced;
}

private static function unWriteColor($s)
{
return preg_replace('#\^[0-9]#', '', $s);
}

private static function codepageConvert($s)
{
$parts = preg_split('#\^([LGCETBJSKH])#', $s, -1, PREG_SPLIT_DELIM_CAPTURE);
array_unshift($parts, 'L');

$p = 0;
$s = '';
while (isset($parts[$p]))
{
$s .= iconv(self::$codepages[$parts[$p]], 'UTF-8', $parts[$p+1]);
$p += 2;
}

return $s;
}

private static function convertLfsSpecialChars($s)
{
return isset($_SERVER['SERVER_NAME']) ?
str_replace(self::$specialPtrn, self::$specialReplHtml, $s) :
str_replace(self::$specialPtrn, self::$specialRepl, $s);
}
}

?>

Quote from Requirements :Requirements

You will need nothing if the system you are using is one of the recent POSIX-compliant systems because standard C libraries that are supplied in them must provide iconv facility. Otherwise, you have to get the » libiconv library installed in your system.

Quote from Installation :Installation

This extension is enabled by default, although it may be disabled by compiling with --without-iconv.

After reading these two things, I feel a lot better about it requiring iconv.
Quote from Dygear :After reading these two things, I feel a lot better about it requiring iconv.

There are exceptions. For instance, (If I understood Vic correctly), he had to go into his package manager and install it separately. A lot of distributions that come with PHP in their Repo don't bundle any extensions, and instead have them compiled separately (installed as php-iconv for instance), which requires it to be installed. I didn't check my PHP on my server, but as mine is compiled from source manually, it should have iconv. (Double checked, it does)
Perhaps Vic can correct/corroborate my explanation, but he did have some hoops before iconv would work.
Correct. On FreeBSD there are two parts for PHP in the ports collection : the core port and the extensions port. The core is completely vanilla PHP. And when you want extra functionality, you select the modules in the configuration screen of the extensions port, like PDO, Iconv, etc.
I'm pretty sure though that iconv is selectd by default in the extensions port. Except on this test site I unselected every module and am only adding one at a time whenever I need them. Keeps things a bit cleaner ..
So the use of iconv isn't so bad me thinks. And the use of iconv really made a huge difference in speed.
Quote from Victor :Correct. On FreeBSD there are two parts for PHP in the ports collection : the core port and the extensions port. The core is completely vanilla PHP. And when you want extra functionality, you select the modules in the configuration screen of the extensions port, like PDO, Iconv, etc.
I'm pretty sure though that iconv is selectd by default in the extensions port. Except on this test site I unselected every module and am only adding one at a time whenever I need them. Keeps things a bit cleaner ..
So the use of iconv isn't so bad me thinks. And the use of iconv really made a huge difference in speed.

Ubuntu and CentOS (Fedora) is the same as well. I just prefer to use a version compiled by me (I swear I'm not a linux nerd )
I know we are gettting slightly off topic, but I just wanted to say that I also compile from source and never had any issues. I run through these commands every time I update.

wget http://php.net/distributions/php-5.4.14.tar.bz2
tar xvjf php-5.4.14.tar.bz2
cd php-5.4.14
./configure --enable-fpm --with-mcrypt --with-zlib --enable-mbstring --with-openssl --with-gd --with-jpeg-dir=/usr/lib --with-png-dir=/usr/lib --enable-gd-native-ttf --with-curl --enable-ftp --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd
make && make test && make install
service php-fpm restart

This always seems to work better, faster then just adding modules at run time.
Quote from Dygear :I know we are gettting slightly off topic, but I just wanted to say that I also compile from source and never had any issues. I run through these commands every time I update.

wget http://php.net/distributions/php-5.4.14.tar.bz2
tar xvjf php-5.4.14.tar.bz2
cd php-5.4.14
./configure --enable-fpm --with-mcrypt --with-zlib --enable-mbstring --with-openssl --with-gd --with-jpeg-dir=/usr/lib --with-png-dir=/usr/lib --enable-gd-native-ttf --with-curl --enable-ftp --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd
make && make test && make install
service php-fpm restart

This always seems to work better, faster then just adding modules at run time.

Grabbed from my PHPInfo: A bit more basic.

'./configure' '--with-gd' '--with-mysqli' '--with-mcrypt' '--with-curl' '--enable-mbstring' '--with-jpeg-dir=/usr' '--with-mysql' '--with-openssl' '--with-zlib'


FGED GREDG RDFGDR GSFDG