PHP, what inspires you?

I was wondering, what problems have you come across that have made you rush to your IDE of choice and code away?

I’m sure that I’m not the only one that needs a problem to solve before things become interesting. Given the nature of us forum folk, I would guess most of the long standing folk hang around, just waiting for a problem to solve.

Here’s mine tonight, I’ve spent a full night researching, learning, then solving this problem.

Sometimes it’s nice to be reminded why we stick at this stuff. :slight_smile:

So there I was, as usual, browsing Sitepoint and this feature really irked me. In conjunction with [URL=“http://www.sitepoint.com/forums/showthread.php?t=628752”]this thread, it seemed people were fine with a loop that constantly queried a database to see if a value already existed until they found one that didn’t. :nono:

It just screamed to breach DRY and I set about tonight finding a way to solve this.

In the end, I created a class to encode the id provided by the database after record insertion (always unique) to generate this unique value (al la Short URL services).

For completeness, here is class.


<?php
/**
 * @author Anthony Sterling
 */
class Base62
{
    /**
     * @desc    Holds the key used to encode and decode.
     *
     * @var        Array
     */
    protected static $aCharMap = array(
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
        'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
        'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
        'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
        'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
        'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
        'W', 'X', 'Y', 'Z', '0', '1', '2', '3',
        '4', '5', '6', '7', '8', '9'
    );

    /**
     * @desc    Encodes an integer to Base 62
     *
     * @param    Integer    $iInteger
     * @return    String
     */
    public static function Encode($iInteger)
    {
        $sString = '';
        while($iInteger > 0)
        {
            $sString .= (string)self::$aCharMap[$iInteger % 62];
            $iInteger = floor($iInteger / 62);
        }
        return $sString;
    }

    /**
     * @desc    Decodes a Base 62 encoded string
     *
     * @param    String    $sString
     * @return    Integer
     */
    public static function Decode($sString)
    {
        $aFlippedMap = array_flip(
            self::$aCharMap
        );
        $iInteger = 0;
        for($iCurrentPosition = 0; $iCurrentPosition < strlen($sString); $iCurrentPosition++)
        {
            $iInteger += $aFlippedMap[$sString[$iCurrentPosition]] * pow(62, $iCurrentPosition);
        }
        return $iInteger;
    }
}

$iInteger = 2147483647;
$sEncoded = Base62::Encode($iInteger);
$iDecoded = Base62::Decode($sEncoded);

header('Content-Type: text/plain');
printf(
    "
    Attempting to Encode:\	%d
    Encoded Value:\	\	%s
    Decoded Value:\	\	%s
    ",
    $iInteger,
    $sEncoded,
    $iDecoded
);
/*
    Attempting to Encode:    2147483647
    Encoded Value:            bLMuvc
    Decoded Value:            2147483647
*/
?>

So, have you had any moments like this? If so, what was the problem then approach/code did you apply to the problem in hand?

Thanks all,

Anthony.

Nice way to think outside the box! Although I never tried to set up a url shortener, I’ve already thought about it and I too was thinking the random code was stored in the database and that to generate a new one would always mean checking if it wasn’t already linked to an url in the database…
With your way, the autoincrement ID is enough. Do you think all services such as tinyurl, bit.ly and such are doing like you came up with?

Thanks James,

With regards to your comment about short url services, I would guess they are, looking at a few of their URLs would indicate so anyway. :slight_smile:

My thinking was the only true unique value we could rely on easily would be the id provided by the database, hence my ‘journey’.

Although, upon reflection, I wonder whether vowels should be removed from the Map. Otherwise, a record with an ID of 3164521 might be quite the disappointment for the visitor! :wink:

From my initial conceptions, indicated in this thread, to researching and working out a final solution felt great.

Which then led me to wonder, what problems have other coders come across that similarly inspired them to code a solution…

Great class. Don’t forget to add all URL safe characters to the $aCharMap, add these to get even shorter URLs:


-_.*|()!

Why don’t those URL shorteners just generate a GUID?

A 36-64 length string isn’t much of a shortener :smiley:

Thanks!

Feel free to abuse, maybe the addition of an abstract getMap() method? It would only take a little modification…

yeah, your base simply becomes count(self::getMap()). Should be pretty easy to extend to whatever chars you want. Love the code SilverBulletUK

Sure, something like the following would do it, although it does need tweaking I would say…

<?php
abstract class BaseConverter
{
    public function encode($iInteger)
    {
        $aMap = $this->getMap();
        $sString = '';
        while($iInteger > 0)
        {
            $sString .= (string)$aMap[$iInteger % count($aMap)];
            $iInteger = floor($iInteger / count($aMap));
        }
        return $sString;
    }
    
    public function decode($sString)
    {
        $aFlippedMap = array_flip(
            $this->getMap()
        );
        $iInteger = 0;
        for($iCurrentPosition = 0; $iCurrentPosition < strlen($sString); $iCurrentPosition++)
        {
            $iInteger += $aFlippedMap[$sString[$iCurrentPosition]] * pow(count($aFlippedMap), $iCurrentPosition);
        }
        return $iInteger;
    }
    
    abstract public function getMap();
}

class Base62Converter extends BaseConverter
{
    public function getMap()
    {
        return array(
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
            'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
            'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
            'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
            'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
            'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
            'W', 'X', 'Y', 'Z', '0', '1', '2', '3',
            '4', '5', '6', '7', '8', '9'
        );
    }
}
?>

Gee, thanks. :slight_smile:

I translated this class into Python and removed vowels from the encoding scheme to avoid inappropriate words.

from math import floor, pow

class BaseConverter:
	encoding_scheme =  'bcdfghjklmnpqrstvwxyzABCDFGHJKLMNPQRSTVWXYZ0123456789'
	base = 0
	
	def __init__(self):
		self.base = len(self.encoding_scheme)
		
	def encode(self, integer):
		if integer < 0:
			raise ValueError("Integer to encode must be positive")
		encoded_string = ''
		if integer == 0:
			return self.encoding_scheme[0]
		else:
			while (integer > 0):
				encoded_string = encoded_string + self.encoding_scheme[integer % self.base]
				integer = int(floor(integer / self.base))
		return encoded_string
	
	def decode(self, encoded_string):
		integer = 0
		encoded_string_len = len(encoded_string)
		
		for i in range(0, encoded_string_len):
			integer += self.encoding_scheme.index(encoded_string[i]) * pow(self.base, i)
		return int(integer)

			
if __name__ == '__main__':
	print 'Testing:'	
	base = BaseConverter()
	print '  encoding_scheme: ' + base.encoding_scheme
	print '  Original: ' + str(2147483647)
	print '  Encoded:  ' + base.encode(2147483647)
	print '  Decoded:  ' + str(base.decode('zjKlkh'))
	print '  Zero encode (print base.encode(0)): ' + base.encode(0) 
	print '  Minus encode (print base.encode(-1)): ' 
	try:
		print base.encode(-1)
	except ValueError as error:
			print '   ' + error.__str__()

Because GUID is too long to be used for something in need of shortening.