| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 | 
							- <?php
 
- /*
 
-  * This file is part of Hashids.
 
-  *
 
-  * (c) Ivan Akimov <ivan@barreleye.com>
 
-  *
 
-  * For the full copyright and license information, please view the LICENSE
 
-  * file that was distributed with this source code.
 
-  */
 
- namespace Hashids;
 
- use Hashids\Math\Bc;
 
- use Hashids\Math\Gmp;
 
- use Hashids\Math\MathInterface;
 
- use RuntimeException;
 
- /**
 
-  * This is the hashids class.
 
-  *
 
-  * @author Ivan Akimov <ivan@barreleye.com>
 
-  * @author Vincent Klaiber <hello@doubledip.se>
 
-  * @author Johnson Page <jwpage@gmail.com>
 
-  */
 
- class Hashids implements HashidsInterface
 
- {
 
-     /**
 
-      * The seps divider.
 
-      *
 
-      * @var float
 
-      */
 
-     const SEP_DIV = 3.5;
 
-     /**
 
-      * The guard divider.
 
-      *
 
-      * @var float
 
-      */
 
-     const GUARD_DIV = 12;
 
-     /**
 
-      * The alphabet string.
 
-      *
 
-      * @var string
 
-      */
 
-     protected $alphabet;
 
-     /**
 
-      * Shuffled alphabets, referenced by alphabet and salt.
 
-      *
 
-      * @var array
 
-      */
 
-     protected $shuffledAlphabets;
 
-     /**
 
-      * The seps string.
 
-      *
 
-      * @var string
 
-      */
 
-     protected $seps = 'cfhistuCFHISTU';
 
-     /**
 
-      * The guards string.
 
-      *
 
-      * @var string
 
-      */
 
-     protected $guards;
 
-     /**
 
-      * The minimum hash length.
 
-      *
 
-      * @var int
 
-      */
 
-     protected $minHashLength;
 
-     /**
 
-      * The salt string.
 
-      *
 
-      * @var string
 
-      */
 
-     protected $salt;
 
-     /**
 
-      * The math class.
 
-      *
 
-      * @var \Hashids\Math\MathInterface
 
-      */
 
-     protected $math;
 
-     /**
 
-      * Create a new hashids instance.
 
-      *
 
-      * @param string $salt
 
-      * @param int $minHashLength
 
-      * @param string $alphabet
 
-      *
 
-      * @throws \Hashids\HashidsException
 
-      */
 
-     public function __construct($salt = '', $minHashLength = 0, $alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890')
 
-     {
 
-         $this->salt = \mb_convert_encoding($salt, 'UTF-8', \mb_detect_encoding($salt));
 
-         $this->minHashLength = $minHashLength;
 
-         $alphabet = \mb_convert_encoding($alphabet, 'UTF-8', \mb_detect_encoding($alphabet));
 
-         $this->alphabet = \implode('', \array_unique($this->multiByteSplit($alphabet)));
 
-         $this->math = $this->getMathExtension();
 
-         if (\mb_strlen($this->alphabet) < 16) {
 
-             throw new HashidsException('Alphabet must contain at least 16 unique characters.');
 
-         }
 
-         if (false !== \mb_strpos($this->alphabet, ' ')) {
 
-             throw new HashidsException('Alphabet can\'t contain spaces.');
 
-         }
 
-         $alphabetArray = $this->multiByteSplit($this->alphabet);
 
-         $sepsArray = $this->multiByteSplit($this->seps);
 
-         $this->seps = \implode('', \array_intersect($sepsArray, $alphabetArray));
 
-         $this->alphabet = \implode('', \array_diff($alphabetArray, $sepsArray));
 
-         $this->seps = $this->shuffle($this->seps, $this->salt);
 
-         if (!$this->seps || (\mb_strlen($this->alphabet) / \mb_strlen($this->seps)) > self::SEP_DIV) {
 
-             $sepsLength = (int) \ceil(\mb_strlen($this->alphabet) / self::SEP_DIV);
 
-             if ($sepsLength > \mb_strlen($this->seps)) {
 
-                 $diff = $sepsLength - \mb_strlen($this->seps);
 
-                 $this->seps .= \mb_substr($this->alphabet, 0, $diff);
 
-                 $this->alphabet = \mb_substr($this->alphabet, $diff);
 
-             }
 
-         }
 
-         $this->alphabet = $this->shuffle($this->alphabet, $this->salt);
 
-         $guardCount = (int) \ceil(\mb_strlen($this->alphabet) / self::GUARD_DIV);
 
-         if (\mb_strlen($this->alphabet) < 3) {
 
-             $this->guards = \mb_substr($this->seps, 0, $guardCount);
 
-             $this->seps = \mb_substr($this->seps, $guardCount);
 
-         } else {
 
-             $this->guards = \mb_substr($this->alphabet, 0, $guardCount);
 
-             $this->alphabet = \mb_substr($this->alphabet, $guardCount);
 
-         }
 
-     }
 
-     /**
 
-      * Encode parameters to generate a hash.
 
-      *
 
-      * @param mixed $numbers
 
-      *
 
-      * @return string
 
-      */
 
-     public function encode(...$numbers): string
 
-     {
 
-         $ret = '';
 
-         if (1 === \count($numbers) && \is_array($numbers[0])) {
 
-             $numbers = $numbers[0];
 
-         }
 
-         if (!$numbers) {
 
-             return $ret;
 
-         }
 
-         foreach ($numbers as $number) {
 
-             $isNumber = \ctype_digit((string) $number);
 
-             if (!$isNumber) {
 
-                 return $ret;
 
-             }
 
-         }
 
-         $alphabet = $this->alphabet;
 
-         $numbersSize = \count($numbers);
 
-         $numbersHashInt = 0;
 
-         foreach ($numbers as $i => $number) {
 
-             $numbersHashInt += $this->math->intval($this->math->mod($number, $i + 100));
 
-         }
 
-         $lottery = $ret = \mb_substr($alphabet, $numbersHashInt % \mb_strlen($alphabet), 1);
 
-         foreach ($numbers as $i => $number) {
 
-             $alphabet = $this->shuffle($alphabet, \mb_substr($lottery.$this->salt.$alphabet, 0, \mb_strlen($alphabet)));
 
-             $ret .= $last = $this->hash($number, $alphabet);
 
-             if ($i + 1 < $numbersSize) {
 
-                 $number %= (\mb_ord($last, 'UTF-8') + $i);
 
-                 $sepsIndex = $this->math->intval($this->math->mod($number, \mb_strlen($this->seps)));
 
-                 $ret .= \mb_substr($this->seps, $sepsIndex, 1);
 
-             }
 
-         }
 
-         if (\mb_strlen($ret) < $this->minHashLength) {
 
-             $guardIndex = ($numbersHashInt + \mb_ord(\mb_substr($ret, 0, 1), 'UTF-8')) % \mb_strlen($this->guards);
 
-             $guard = \mb_substr($this->guards, $guardIndex, 1);
 
-             $ret = $guard.$ret;
 
-             if (\mb_strlen($ret) < $this->minHashLength) {
 
-                 $guardIndex = ($numbersHashInt + \mb_ord(\mb_substr($ret, 2, 1), 'UTF-8')) % \mb_strlen($this->guards);
 
-                 $guard = \mb_substr($this->guards, $guardIndex, 1);
 
-                 $ret .= $guard;
 
-             }
 
-         }
 
-         $halfLength = (int) (\mb_strlen($alphabet) / 2);
 
-         while (\mb_strlen($ret) < $this->minHashLength) {
 
-             $alphabet = $this->shuffle($alphabet, $alphabet);
 
-             $ret = \mb_substr($alphabet, $halfLength).$ret.\mb_substr($alphabet, 0, $halfLength);
 
-             $excess = \mb_strlen($ret) - $this->minHashLength;
 
-             if ($excess > 0) {
 
-                 $ret = \mb_substr($ret, (int) ($excess / 2), $this->minHashLength);
 
-             }
 
-         }
 
-         return $ret;
 
-     }
 
-     /**
 
-      * Decode a hash to the original parameter values.
 
-      *
 
-      * @param string $hash
 
-      *
 
-      * @return array
 
-      */
 
-     public function decode($hash): array
 
-     {
 
-         $ret = [];
 
-         if (!\is_string($hash) || !($hash = \trim($hash))) {
 
-             return $ret;
 
-         }
 
-         $alphabet = $this->alphabet;
 
-         $hashBreakdown = \str_replace($this->multiByteSplit($this->guards), ' ', $hash);
 
-         $hashArray = \explode(' ', $hashBreakdown);
 
-         $i = 3 === \count($hashArray) || 2 === \count($hashArray) ? 1 : 0;
 
-         $hashBreakdown = $hashArray[$i];
 
-         if ('' !== $hashBreakdown) {
 
-             $lottery = \mb_substr($hashBreakdown, 0, 1);
 
-             $hashBreakdown = \mb_substr($hashBreakdown, 1);
 
-             $hashBreakdown = \str_replace($this->multiByteSplit($this->seps), ' ', $hashBreakdown);
 
-             $hashArray = \explode(' ', $hashBreakdown);
 
-             foreach ($hashArray as $subHash) {
 
-                 $alphabet = $this->shuffle($alphabet, \mb_substr($lottery.$this->salt.$alphabet, 0, \mb_strlen($alphabet)));
 
-                 $result = $this->unhash($subHash, $alphabet);
 
-                 if ($this->math->greaterThan($result, PHP_INT_MAX)) {
 
-                     $ret[] = $this->math->strval($result);
 
-                 } else {
 
-                     $ret[] = $this->math->intval($result);
 
-                 }
 
-             }
 
-             if ($this->encode($ret) !== $hash) {
 
-                 $ret = [];
 
-             }
 
-         }
 
-         return $ret;
 
-     }
 
-     /**
 
-      * Encode hexadecimal values and generate a hash string.
 
-      *
 
-      * @param string $str
 
-      *
 
-      * @return string
 
-      */
 
-     public function encodeHex($str): string
 
-     {
 
-         if (!\ctype_xdigit((string) $str)) {
 
-             return '';
 
-         }
 
-         $numbers = \trim(chunk_split($str, 12, ' '));
 
-         $numbers = \explode(' ', $numbers);
 
-         foreach ($numbers as $i => $number) {
 
-             $numbers[$i] = \hexdec('1'.$number);
 
-         }
 
-         return $this->encode(...$numbers);
 
-     }
 
-     /**
 
-      * Decode a hexadecimal hash.
 
-      *
 
-      * @param string $hash
 
-      *
 
-      * @return string
 
-      */
 
-     public function decodeHex($hash): string
 
-     {
 
-         $ret = '';
 
-         $numbers = $this->decode($hash);
 
-         foreach ($numbers as $i => $number) {
 
-             $ret .= \mb_substr(dechex($number), 1);
 
-         }
 
-         return $ret;
 
-     }
 
-     /**
 
-      * Shuffle alphabet by given salt.
 
-      *
 
-      * @param string $alphabet
 
-      * @param string $salt
 
-      *
 
-      * @return string
 
-      */
 
-     protected function shuffle($alphabet, $salt): string
 
-     {
 
-         $key = $alphabet.' '.$salt;
 
-         if (isset($this->shuffledAlphabets[$key])) {
 
-             return $this->shuffledAlphabets[$key];
 
-         }
 
-         $saltLength = \mb_strlen($salt);
 
-         $saltArray = $this->multiByteSplit($salt);
 
-         if (!$saltLength) {
 
-             return $alphabet;
 
-         }
 
-         $alphabetArray = $this->multiByteSplit($alphabet);
 
-         for ($i = \mb_strlen($alphabet) - 1, $v = 0, $p = 0; $i > 0; $i--, $v++) {
 
-             $v %= $saltLength;
 
-             $p += $int = \mb_ord($saltArray[$v], 'UTF-8');
 
-             $j = ($int + $v + $p) % $i;
 
-             $temp = $alphabetArray[$j];
 
-             $alphabetArray[$j] = $alphabetArray[$i];
 
-             $alphabetArray[$i] = $temp;
 
-         }
 
-         $alphabet = \implode('', $alphabetArray);
 
-         $this->shuffledAlphabets[$key] = $alphabet;
 
-         return $alphabet;
 
-     }
 
-     /**
 
-      * Hash given input value.
 
-      *
 
-      * @param string $input
 
-      * @param string $alphabet
 
-      *
 
-      * @return string
 
-      */
 
-     protected function hash($input, $alphabet): string
 
-     {
 
-         $hash = '';
 
-         $alphabetLength = \mb_strlen($alphabet);
 
-         do {
 
-             $hash = \mb_substr($alphabet, $this->math->intval($this->math->mod($input, $alphabetLength)), 1).$hash;
 
-             $input = $this->math->divide($input, $alphabetLength);
 
-         } while ($this->math->greaterThan($input, 0));
 
-         return $hash;
 
-     }
 
-     /**
 
-      * Unhash given input value.
 
-      *
 
-      * @param string $input
 
-      * @param string $alphabet
 
-      *
 
-      * @return int
 
-      */
 
-     protected function unhash($input, $alphabet)
 
-     {
 
-         $number = 0;
 
-         $inputLength = \mb_strlen($input);
 
-         if ($inputLength && $alphabet) {
 
-             $alphabetLength = \mb_strlen($alphabet);
 
-             $inputChars = $this->multiByteSplit($input);
 
-             foreach ($inputChars as $char) {
 
-                 $position = \mb_strpos($alphabet, $char);
 
-                 $number = $this->math->multiply($number, $alphabetLength);
 
-                 $number = $this->math->add($number, $position);
 
-             }
 
-         }
 
-         return $number;
 
-     }
 
-     /**
 
-      * Get BC Math or GMP extension.
 
-      *
 
-      * @codeCoverageIgnore
 
-      *
 
-      * @throws \RuntimeException
 
-      *
 
-      * @return \Hashids\Math\MathInterface
 
-      */
 
-     protected function getMathExtension(): MathInterface
 
-     {
 
-         if (\extension_loaded('gmp')) {
 
-             return new Gmp();
 
-         }
 
-         if (\extension_loaded('bcmath')) {
 
-             return new Bc();
 
-         }
 
-         throw new RuntimeException('Missing BC Math or GMP extension.');
 
-     }
 
-     /**
 
-      * Replace simple use of $this->multiByteSplit with multi byte string.
 
-      *
 
-      * @param $string
 
-      *
 
-      * @return array|string[]
 
-      */
 
-     protected function multiByteSplit($string): array
 
-     {
 
-         return \preg_split('/(?!^)(?=.)/u', $string) ?: [];
 
-     }
 
- }
 
 
  |