1 <?php 
declare(strict_types
=1); 
   4  * This file is part of the Rapsys PackBundle package. 
   6  * (c) Raphaël Gertz <symfony@rapsys.eu> 
   8  * For the full copyright and license information, please view the LICENSE 
   9  * file that was distributed with this source code. 
  12 namespace Rapsys\PackBundle\Util
; 
  15  * Helps manage string conversions 
  18         //The secret parameter 
  27         //The alpha array key number 
  30         //The offset reduced from secret 
  34          * Construct slugger util 
  36          * @todo Add a command to generate alpha array or generate it on first run with cache storage ? 
  37          * @todo Use Cache like in calendar controller through FilesystemAdapter 
  39          * @param string $secret The secret string 
  41         public function __construct(string $secret) { 
  43                 $this->secret 
= $secret; 
  46                  * Pseudo-random alphabet 
  47                  * @xxx use array flip and keys to workaround php "smart" that cast range('0', '9') as int instead of string 
  48                  * @xxx The key count mismatch, count(alpha)>count(rev), resulted in a data corruption due to duplicate numeric values 
  49                  * @todosee required range by json_encode result and short input (0->255 ???) 
  51                 $this->alpha 
= array_keys(array_flip(array_merge( 
  78                 $this->count = count($rev = $this->rev = array_flip($this->alpha)); 
  81                 $split = str_split($this->secret); 
  84                 $this->offset = array_reduce($split, function ($res, $a) use ($rev) { return $res += $rev[$a]; }, count($split)) % $this->count; 
  88          * Flatten recursively an array 
  90          * @param array|string $data The data tree 
  91          * @param string|null $current The current prefix 
  92          * @param string $sep The key separator 
  93          * @param string $prefix The key prefix 
  94          * @param string $suffix The key suffix 
  95          * @return array The flattened data 
  97         public function flatten($data, ?string $current = null, string $sep = '.', string $prefix = '', string $suffix = ''): array { 
 101                 //Look for data array 
 102                 if (is_array($data)) { 
 103                         //Iteare on each pair 
 104                         foreach($data as $k => $v) { 
 105                                 //Merge flattened value in return array 
 106                                 $ret += $this->flatten($v, empty($current) ? $k : $current.$sep.$k, $sep, $prefix, $suffix); 
 110                         //Store data in flattened key 
 111                         $ret[$prefix.$current.$suffix] = $data; 
 119          * Crypt and base64uri encode string 
 121          * @param array|string $data The data string 
 122          * @return string The hashed data 
 124         public function hash(array|string $data): string { 
 126                 if (is_array($data)) { 
 128                         $data = json_encode($data); 
 132                 //XXX: we use hash_hmac with md5 hash 
 133                 //XXX: crypt was dropped because it provided identical signature for string starting with same pattern 
 134                 return str_replace(['+
','/'], ['-','_
'], base64_encode(hash_hmac('md5
', $data, $this->secret, true))); 
 138          * Serialize then short 
 140          * @param array $data The data array 
 141          * @return string The serialized and shorted data 
 143         public function serialize(array $data): string { 
 144                 //Return shorted serialized data 
 145                 //XXX: dropped serialize use to prevent short function from dropping utf-8 characters 
 146                 return $this->short(json_encode($data)); 
 152          * @param string $data The data string 
 153          * @return string The shorted data 
 155         public function short(string $data): string { 
 161                         //Iterate on each character 
 162                         foreach(str_split($data) as $k => $c) { 
 163                                 if (isset($this->rev[$c]) && isset($this->alpha[($this->rev[$c]+$this->offset)%$this->count])) { 
 164                                         //XXX: Remap char to an other one 
 165                                         $ret .= chr(($this->rev[$c] - $this->offset + $this->count) % $this->count); 
 167                                         throw new \RuntimeException(sprintf('Unable to retrieve character
: %c
', $c)); 
 173                 return str_replace(['+
','/'], ['-','_
'], base64_encode($ret)); 
 177          * Convert string to safe slug 
 179          * @param string $data The data string 
 180          * @return ?string The slugged data 
 182         function slug(?string $data): ?string { 
 184                 if ($data === null) { 
 189                 //Use Transliterator if available 
 190                 if (class_exists('Transliterator
')) { 
 191                         //Convert from any to latin, then to ascii and lowercase 
 192                         $trans = \Transliterator::create('Any
-Latin
; Latin
-ASCII
; Lower()'); 
 193                         //Replace every non alphanumeric character by dash then trim dash 
 194                         return trim(preg_replace('/[^a
-zA
-Z0
-9]+
/', '-', $trans->transliterate($data)), '-'); 
 197                 //Convert from utf-8 to ascii, replace quotes with space, remove non alphanumericseparator, replace separator with dash and trim dash 
 198                 return trim(preg_replace('/[\
/_
|+ -]+
/', '-', strtolower(preg_replace('/[^a
-zA
-Z0
-9\
/_
|+ -]/', '', str_replace(['\'
', '"'], ' ', iconv('UTF-8', 'ASCII//TRANSLIT', $data))))), '-'); 
 202          * Convert string to latin 
 204          * @param string $data The data string 
 205          * @return ?string The slugged data 
 207         function latin(?string $data): ?string { 
 209                 if ($data === null) { 
 214                 //Use Transliterator if available 
 215                 if (class_exists('Transliterator')) { 
 216                         //Convert from any to latin, then to ascii and lowercase 
 217                         $trans = \Transliterator::create('Any-Latin; Latin-ASCII'); 
 218                         //Replace every non alphanumeric character by dash then trim dash 
 219                         return trim($trans->transliterate($data)); 
 222                 //Convert from utf-8 to ascii 
 223                 return trim(iconv('UTF-8', 'ASCII//TRANSLIT', $data)); 
 227          * Unshort then unserialize 
 229          * @param string $data The data string 
 230          * @return array The unshorted and unserialized data 
 232         public function unserialize(string $data): array { 
 233                 //Return unshorted unserialized string 
 234                 return json_decode($this->unshort($data), true); 
 240          * @param string $data The data string 
 241          * @return string The unshorted data 
 243         public function unshort(string $data): string { 
 247                 //Iterate on each character 
 248                 foreach(str_split(base64_decode(str_replace(['-','_'], ['+','/'], $data))) as $c) { 
 249                         //XXX: Reverse map char to an other one 
 250                         $ret .= $this->alpha[(ord($c) + $this->offset) % $this->count];