Simple encode / decode integer to small string

jkon

Hello , today I was developing a short url system and I created this class to help me encode / decode integer (id) to small string. That way for example “Ac” means 3674 . Because I believe that it may be helpful to others too I share it here. Notice that in the constructing string each char can be present only once.

Example of usage:

    $numCode = new Model_Utils_Numcode("4fFiV8kRTvm5MPNDcyO1dg7lr20Qtn3X6pKLZUqaEsxCwubGYIzhSWJojHeA9B");      
    $num = 3674;
    $encoded = $numCode->encode($num);

    echo "<br/> $num encoded = $encoded";
    // 3674 encoded = Ac

    echo "<br/> $encoded decoded = ".$numCode->decode($encoded);
    // Ac decoded = 3674

Class:

class Model_Utils_Numcode
{
  private $nums;
  private $chars;
  private $numeral;

  /**
   * @param $string , String containing the chars that will be used for
   * the convension. !important each char can be present only once.
   */
  public function __construct($string)
  {
    $this->nums = str_split($string);
    $this->chars = array_flip($this->nums);
    if(count($this->nums) != count($this->chars))
    {
      throw new Exception("$string !important each char can be present only once.", 371);
    }
    $this->numeral = count($this->nums);
  }

  /**
   * Encodes a number to a string
   * @param int $int
   * @return string
   */
  public function encode($int)
  {
    if(!is_int($int))
    {
      throw new Exception("$int is not integer.", 372);
    }
    return $this->convension($int, $this->numeral);
  }

  /**
   * Decodes a string to a number
   * @param string $string
   * @return number
   */
  public function decode($string)
  {
    $num = 0;
    $m = 1;
    $parts = str_split($string);
    $parts = array_reverse($parts);
    foreach($parts as $part)
    {
      if(!isset($this->chars[$part]))
      {
        throw new Exception("$part is not defined.", 373);
      }
      $num =  $num + $this->chars[$part] * $m;
      $m = $m * $this->numeral;
    }
    return $num;
  }

  /**
   * @see http://www.cut-the-knot.org/recurrence/conversion.shtml
   */
  private function convension($M,$N)
  {
    if($M  < $N)
    {
      return $this->nums[$M];
    }
    else
    {
      return $this->convension($M / $N, $N) . $this->nums[bcmod($M , $N)];
    }
  }

}
cereal commented: great! +13
diafol commented: Thanks for sharing +0
2,668 Views
About the Author

When I was kid my father bought me an Amstrad 6128 (after playing a little I desided it to brake down a program to see what is inside it and re fix it – same logic I did with toy cars or anything that had the unfortunate lack to be in my hands), that was it programming gained me although I didn’t realized it. Sometimes it is like “chicken or the egg”, we love something and we are good at it, or we love it because we are good at it? Still I don’t know…

// Examle of usage:
		$numCode = new Model_Utils_Numcode("4fFiV8kRTvm5MPNDcyO1dg7lr20Qtn3X6pKLZUqaEsxCwubGYIzhSWJojHeA9B");      
        $num = 3674;
        $encoded = $numCode->encode($num);
        
        echo "<br/> $num encoded = $encoded";
        // 3674 encoded = Ac
        
        echo "<br/> $encoded decoded = ".$numCode->decode($encoded);
        // Ac decoded = 3674
		
// Class:
   class Model_Utils_Numcode
    {
      private $nums;
      private $chars;
      private $numeral;
    
      /**
       * @param $string , String containing the chars that will be used for
       * the convension. !important each char can be present only once.
       */
      public function __construct($string)
      {
        $this->nums = str_split($string);
        $this->chars = array_flip($this->nums);
        if(count($this->nums) != count($this->chars))
        {
          throw new Exception("$string !important each char can be present only once.", 371);
        }
        $this->numeral = count($this->nums);
      }
    
      /**
       * Encodes a number to a string
       * @param int $int
       * @return string
       */
      public function encode($int)
      {
        if(!is_int($int))
        {
          throw new Exception("$int is not integer.", 372);
        }
        return $this->convension($int, $this->numeral);
      }
    
      /**
       * Decodes a string to a number
       * @param string $string
       * @return number
       */
      public function decode($string)
      {
        $num = 0;
        $m = 1;
        $parts = str_split($string);
        $parts = array_reverse($parts);
        foreach($parts as $part)
        {
          if(!isset($this->chars[$part]))
          {
            throw new Exception("$part is not defined.", 373);
          }
          $num =  $num + $this->chars[$part] * $m;
          $m = $m * $this->numeral;
        }
        return $num;
      }
    
      /**
       * @see http://www.cut-the-knot.org/recurrence/conversion.shtml
       */
      private function convension($M,$N)
      {
        if($M  < $N)
        {
          return $this->nums[$M];
        }
        else
        {
          return $this->convension($M / $N, $N) . $this->nums[bcmod($M , $N)];
        }
      }
    
    }
cereal 1,524 Nearly a Senior Poster Featured Poster

Thanks for sharing! I tested your class and I like it, consider to add a passphrase to allow different results, it could be helpful.

You may want to check Hashids, it's very similar to your concept: http://hashids.org/

jkon 535 Posting Whiz in Training Featured Poster

Thank you cereal , the demand was to create a url shortening system with no consideration on exposed url security (it is just redirecting) . In that matter I could pass the reference id as it was. But of course if this goes large the url's would look ugly and not 'short'. Creating this helped me in that sense and it has a bit of complexity that the chars used for the numeral system , and the numeral system each-self are defined in the construction argument.

Of course this is not a security system since id's , to more complex things than just redirecting , has no meaning to be shared in a url (internal system entities should not be exposed in any sense). Even in this simple redirection I believe that “Numcode” hides the internal system id and would require a lot of effort for someone to understand it, gaining from that just to redirect to the next page.

Member Avatar
diafol

I like it. Very straightforward.

jkon 535 Posting Whiz in Training Featured Poster

The project I was working went live this week , making me to realize that is_int don't work with strings that are integers , in the encode method. I am posting once again the class Numcode alternating is_int($int) with preg_match('/^\d+$/', $int)

<?php
class Model_Utils_Numcode
{
  private $nums;
  private $chars;
  private $numeral;

  /**
   * @param $string , String containing the chars that will be used for
   * the conversion. !important each char can be present only once.
   */
  public function __construct($string)
  {
    $this->nums = str_split($string);
    $this->chars = array_flip($this->nums);
    if(count($this->nums) != count($this->chars))
    {
      throw new Exception("$string !important each char can be present only once.", 371);
    }
    $this->numeral = count($this->nums);
  }

  /**
   * Encodes a number to a string
   * @param int $int
   * @return string
   */
  public function encode($int)
  {
    if(!preg_match('/^\d+$/', $int))
    {
      throw new Exception("$int is not integer.", 372);
    }
    return $this->convension($int, $this->numeral);
  }

  /**
   * Decodes a string to a number
   * @param string $string
   * @return number
   */
  public function decode($string)
  {
    $num = 0;
    $m = 1;
    $parts = str_split($string);
    $parts = array_reverse($parts);
    foreach($parts as $part)
    {
      if(!isset($this->chars[$part]))
      {
        throw new Exception("$part is not defined.", 373);
      }
      $num =  $num + $this->chars[$part] * $m;
      $m = $m * $this->numeral;
    }
    return $num;
  }

  /**
   * @see http://www.cut-the-knot.org/recurrence/conversion.shtml
   */
  private function convension($M,$N)
  {
    if($M  < $N)
    {
      return $this->nums[$M];
    }
    else
    {
      return $this->convension($M / $N, $N) . $this->nums[bcmod($M , $N)];
    }
  }

}
?>
Member Avatar
diafol

is_int is a problem with strings. I use the validate functions for data from forms.

if($var = filter_var($intString, FILTER_VALIDATE_INT))...
cereal commented: me too! +13
Be a part of the DaniWeb community

We're a friendly, industry-focused community of 1.21 million developers, IT pros, digital marketers, and technology enthusiasts learning and sharing knowledge.