]>
Raphaël G. Git Repositories - packbundle/blob - Util/SluggerUtil.php
   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  * Manages string conversions 
  21         protected array $alpha; 
  29          * The alpha array key number 
  34          * The offset reduced from secret 
  36         protected int $offset; 
  39          * Construct slugger util 
  41          * Run "bin/console rapsyspack:range" to generate RAPSYSPACK_RANGE="ayl[...]z9w" range in .env.local 
  43          * @todo Use Cache like in calendar controller through FilesystemAdapter ? 
  45          * @param string $secret The secret string 
  47         public function __construct(protected string $secret) { 
  49                 if (empty($range = $_ENV['RAPSYSPACK_RANGE']) || $range === 'Ch4ng3m3!') { 
  50                         //Protect member variable setup 
  55                  * Get pseuto-random alphabet by splitting range string 
  56                  * TODO: see required range by json_encode result and short input (0->255 ???) 
  57                  * XXX: The key count mismatch, count(alpha)>count(rev), resulted in a data corruption due to duplicate numeric values 
  59                 $this->alpha 
= str_split($range); 
  62                 $this->count 
= count($rev = $this->rev 
= array_flip($this->alpha
)); 
  65                 $split = str_split($this->secret
); 
  68                 //TODO: protect undefined index ? 
  69                 $this->offset 
= array_reduce($split, function ($res, $a) use ($rev) { return $res +
= $rev
[$a
]; }, count($split)) % 
$this->count
; 
  73          * Flatten recursively an array 
  75          * @param array|string $data The data tree 
  76          * @param string|null $current The current prefix 
  77          * @param string $sep The key separator 
  78          * @param string $prefix The key prefix 
  79          * @param string $suffix The key suffix 
  80          * @return array The flattened data 
  82         public function flatten($data, ?string $current = null, string $sep = '.', string $prefix = '', string $suffix = ''): array { 
  87                 if (is_array($data)) { 
  89                         foreach($data as $k => $v) { 
  90                                 //Merge flattened value in return array 
  91                                 $ret +
= $this->flatten($v, empty($current) ? $k : $current.$sep.$k, $sep, $prefix, $suffix); 
  95                         //Store data in flattened key 
  96                         $ret[$prefix.$current.$suffix] = $data; 
 104          * Crypt and base64uri encode string 
 106          * @param array|string $data The data string 
 107          * @return string The hashed data 
 109         public function hash(array|string $data): string { 
 111                 if (is_array($data)) { 
 113                         $data = json_encode($data); 
 117                 //XXX: we use hash_hmac with md5 hash 
 118                 //XXX: crypt was dropped because it provided identical signature for string starting with same pattern 
 119                 return str_replace(['+','/'], ['-','_'], base64_encode(hash_hmac('md5', $data, $this->secret
, true))); 
 123          * Serialize then short 
 125          * @param array $data The data array 
 126          * @return string The serialized and shorted data 
 128         public function serialize(array $data): string { 
 129                 //Return shorted serialized data 
 130                 //XXX: dropped serialize use to prevent short function from dropping utf-8 characters 
 131                 return $this->short(json_encode($data)); 
 137          * @param string $data The data string 
 138          * @return string The shorted data 
 140         public function short(string $data): string { 
 146                         //Iterate on each character 
 147                         foreach(str_split($data) as $k => $c) { 
 148                                 if (isset($this->rev
[$c]) && isset($this->alpha
[($this->rev
[$c]+
$this->offset
)%
$this->count
])) { 
 149                                         //XXX: Remap char to an other one 
 150                                         $ret .= chr(($this->rev
[$c] - $this->offset + 
$this->count
) % 
$this->count
); 
 152                                         throw new \
RuntimeException(sprintf('Unable to retrieve character: %c', $c)); 
 158                 return str_replace(['+','/','='], ['-','_',''], base64_encode($ret)); 
 162          * Convert string to safe slug 
 164          * @param string $data The data string 
 165          * @param string $separator The separator string  
 166          * @return ?string The slugged data 
 168         function slug(?string $data, string $separator = '-'): ?string { 
 170                 if ($data === null) { 
 175                 //Use Transliterator if available 
 176                 if (class_exists('Transliterator')) { 
 177                         //Convert from any to latin, then to ascii and lowercase 
 178                         $trans = \Transliterator
::create('Any-Latin; Latin-ASCII; Lower()'); 
 179                         //Replace every non alphanumeric character by dash then trim dash 
 180                         return trim(preg_replace('/[^a-zA-Z0-9]+/', $separator, $trans->transliterate($data)), $separator); 
 183                 //Convert from utf-8 to ascii, replace quotes with space, remove non alphanumericseparator, replace separator with dash and trim dash 
 184                 return trim(preg_replace('/[\/_|+ -]+/', $separator, strtolower(preg_replace('/[^a-zA-Z0-9\/_|+ -]/', '', str_replace(['\'', '"'], ' ', iconv('UTF-8', 'ASCII//TRANSLIT', $data))))), $separator); 
 188          * Convert string to latin 
 190          * @param string $data The data string 
 191          * @return ?string The slugged data 
 193         function latin(?string $data): ?string { 
 195                 if ($data === null) { 
 200                 //Use Transliterator if available 
 201                 if (class_exists('Transliterator')) { 
 202                         //Convert from any to latin, then to ascii and lowercase 
 203                         $trans = \Transliterator
::create('Any-Latin; Latin-ASCII'); 
 204                         //Replace every non alphanumeric character by dash then trim dash 
 205                         return trim($trans->transliterate($data)); 
 208                 //Convert from utf-8 to ascii 
 209                 return trim(iconv('UTF-8', 'ASCII//TRANSLIT', $data)); 
 213          * Unshort then unserialize 
 215          * @param string $data The data string 
 216          * @return array The unshorted and unserialized data 
 218         public function unserialize(string $data): array { 
 219                 //Return unshorted unserialized string 
 220                 return json_decode($this->unshort($data), true); 
 226          * @param string $data The data string 
 227          * @return string The unshorted data 
 229         public function unshort(string $data): string { 
 233                 //Iterate on each character 
 234                 foreach(str_split(base64_decode(str_replace(['-','_'], ['+','/'], $data))) as $c) { 
 235                         //XXX: Reverse map char to an other one 
 236                         $ret .= $this->alpha
[(ord($c) + 
$this->offset
) % 
$this->count
];