Simple encode / decode integer to small string

jkon 2 Tallied Votes 3K Views Share

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
// 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 638 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 for diafol
diafol

I like it. Very straightforward.

jkon 638 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 for diafol
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 developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.