| 1 |  |  | <?php declare(strict_types=1); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | /* | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  * This file is part of Flight Routing. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  |  * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  |  * PHP version 8.0 and above required | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  |  * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  |  * @author    Divine Niiquaye Ibok <[email protected]> | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  |  * @copyright 2019 Divine Niiquaye Ibok (https://divinenii.com/) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  |  * @license   https://opensource.org/licenses/BSD-3-Clause License | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  |  * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  |  * For the full copyright and license information, please view the LICENSE | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  |  * file that was distributed with this source code. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  |  */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | namespace Flight\Routing\Traits; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  | use Flight\Routing\Handlers\ResourceHandler; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  | use Flight\Routing\RouteCollection; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  | /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |  * A default cache implementation for route match. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  |  * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  |  * @author Divine Niiquaye Ibok <[email protected]> | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  |  */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  | trait CacheTrait | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  | { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  |     private ?string $cache = null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  |      * @param string $path file path to store compiled routes | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 32 |  |  |      */ | 
            
                                                                        
                            
            
                                    
            
            
                | 33 | 2 |  |     public function setCache(string $path): void | 
            
                                                                        
                            
            
                                    
            
            
                | 34 |  |  |     { | 
            
                                                                        
                            
            
                                    
            
            
                | 35 | 2 |  |         $this->cache = $path; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |      * A well php value formatter, better than (var_export). | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 | 3 |  |     public static function export(mixed $value, string $indent = ''): string | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |         switch (true) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 | 3 |  |             case [] === $value: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 | 1 |  |                 return '[]'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 | 3 |  |             case \is_array($value): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 | 3 |  |                 if (!($t = \count($value, \COUNT_RECURSIVE) > 15) && \array_is_list($value)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 | 3 |  |                     \array_walk($value, function ($v) use (&$t, $value) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 | 3 |  |                         if (\count($value) > 1 && \is_string($v) && \strlen($v) > 100) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 | 2 |  |                             $t = true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  |                         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |                     }); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 | 3 |  |                 $j = -1; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 | 3 |  |                 $code = $t ? "[\n" : '['; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 | 3 |  |                 $subIndent = $t ? $indent.'  ' : $indent = ''; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 | 3 |  |                 foreach ($value as $k => $v) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 | 3 |  |                     $code .= $subIndent; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 | 3 |  |                     if (!\is_int($k) || $k !== ++$j) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 | 3 |  |                         $code .= self::export($k, $subIndent).' => '; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |                     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 | 3 |  |                     $code .= self::export($v, $subIndent).($t ? ",\n" : ', '); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 | 3 |  |                 return \rtrim($code, ', ').$indent.']'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 | 3 |  |             case (\is_string($value) && (':' === $value[0] && ':' === $value[-1])): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 | 2 |  |                 return \substr($value, 1, -1); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 | 3 |  |             case $value instanceof ResourceHandler: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 | 1 |  |                 return $value::class.'('.self::export($value(''), $indent).')'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 | 3 |  |             case $value instanceof \stdClass: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 | 1 |  |                 return '(object) '.self::export((array) $value, $indent); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 | 3 |  |             case $value instanceof RouteCollection: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 | 1 |  |                 return $value::class.'::__set_state('.self::export([ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 | 1 |  |                     'routes' => $value->getRoutes(), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 | 1 |  |                     'defaultIndex' => $value->count() - 1, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |                     'sorted' => true, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |                 ], $indent).')'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 | 3 |  |             case \is_object($value): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 | 1 |  |                 if (\method_exists($value, '__set_state')) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 | 1 |  |                     return $value::class.'::__set_state('.self::export( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 | 1 |  |                         \array_merge(...\array_map(function (\ReflectionProperty $v) use ($value): array { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 | 1 |  |                             $v->setAccessible(true); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 | 1 |  |                             return [$v->getName() => $v->getValue($value)]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 | 1 |  |                         }, (new \ReflectionObject($value))->getProperties())) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |                     ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 | 1 |  |                 return 'unserialize(\''.\serialize($value).'\')'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 | 3 |  |         return \var_export($value, true); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 | 4 |  |     protected function doCache(): RouteCollection | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 | 4 |  |         if (\is_array($a = @include $this->cache)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 | 3 |  |             $this->optimized = $a; | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 | 3 |  |             return $this->optimized[2] ??= $this->collection ?? new RouteCollection(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 | 2 |  |         if (\is_callable($collection = $this->collection ?? new RouteCollection())) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 | 1 |  |             $collection($collection = new RouteCollection()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 | 1 |  |             $collection->sort(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 | 1 |  |             $doCache = true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 | 2 |  |         if (!\is_dir($directory = \dirname($this->cache))) { | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 | 2 |  |             @\mkdir($directory, 0775, true); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |         try { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 | 2 |  |             return $collection; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |         } finally { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 | 2 |  |             $dumpData = $this->buildCache($collection, $doCache ?? false); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 | 2 |  |             \file_put_contents($this->cache, "<?php // auto generated: AVOID MODIFYING\n\nreturn ".$dumpData.";\n"); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 | 2 |  |             if (\function_exists('opcache_invalidate') && \filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 | 2 |  |                 @\opcache_invalidate($this->cache, true); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                            
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 | 2 |  |             $this->optimized = require $this->cache; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 | 2 |  |     protected function buildCache(RouteCollection $collection, bool $doCache): string | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 | 2 |  |         $dynamicRoutes = $dynamicVar = $staticRoutes = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 | 2 |  |         $dynamicString = ":static function (string \$path): ?array {\n      "; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 | 2 |  |         foreach ($collection->getRoutes() as $i => $route) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 | 2 |  |             $trimmed = \preg_replace('/\W$/', '', $path = '/'.\ltrim($route['path'], '/')); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 | 2 |  |             if (\in_array($prefix = '/'.\ltrim($route['prefix'] ?? '/', '/') ?? '/', [$trimmed, $path], true)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 | 2 |  |                 $staticRoutes[$trimmed ?: '/'][] = $i; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 | 2 |  |                 continue; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 | 2 |  |             [$path, $var] = $this->getCompiler()->compile($path, $route['placeholders'] ?? []); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 | 2 |  |             $path = \str_replace('\/', '/', \substr($path, 1 + \strpos($path, '^'), -(\strlen($path) - \strrpos($path, '$')))); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 | 2 |  |             if (($l = \array_key_last($dynamicRoutes)) && !\in_array($l, ['/', $prefix], true)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 | 2 |  |                 for ($o = 0, $new = ''; $o < \strlen($prefix); ++$o) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 | 2 |  |                     if ($prefix[$o] !== ($l[$o] ?? null)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 | 2 |  |                         break; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |                     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 | 2 |  |                     $new .= $l[$o]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 | 2 |  |                 if ($new && '/' !== $new) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 | 2 |  |                     if ($l !== $new) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 | 2 |  |                         $dynamicRoutes[$new] = $dynamicRoutes[$l]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 | 2 |  |                         unset($dynamicRoutes[$l]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 |  |  |                     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 | 2 |  |                     $prefix = $new; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 162 | 2 |  |             $dynamicRoutes[$prefix][] = \preg_replace('#\?(?|P<\w+>|<\w+>|\'\w+\')#', '', $path)."(*:{$i})"; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 163 | 2 |  |             $dynamicVar[$i] = $var; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 164 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 165 | 2 |  |         \ksort($staticRoutes, \SORT_NATURAL); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 166 | 2 |  |         \uksort($dynamicRoutes, fn (string $a, string $b): int => \in_array('/', [$a, $b], true) ? \strcmp($b, $a) : \strcmp($a, $b)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 167 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 168 | 2 |  |         foreach ($dynamicRoutes as $offset => $paths) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 169 | 2 |  |             $numParts = \max(1, \round(($c = \count($paths)) / 30)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 170 | 2 |  |             $prefix = '/'.\ltrim($offset, '/'); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 171 | 2 |  |             $indent = '      '; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 172 | 2 |  |             $chunks = self::export(\array_map(fn (array $p): string => "~^(?|".implode('|', $p).")$~", \array_chunk($paths, (int) \ceil($c / $numParts))), $indent); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 173 |  |  |             $dynamicString .= <<<PHP | 
            
                                                                                                            
                            
            
                                    
            
            
                | 174 |  |  |             if (\str_starts_with(\$path, '$prefix')) { foreach($chunks as \$p) { if (!\preg_match(\$p, \$path, \$m)) continue; return [\$m]; }} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 175 |  |  |                   else | 
            
                                                                                                            
                            
            
                                    
            
            
                | 176 |  |  |             PHP; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 177 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 178 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 179 | 2 |  |         if (\str_ends_with($dynamicString, 'else')) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 180 | 2 |  |             $dynamicString = \substr($dynamicString, 0, -4); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 181 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 182 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 183 | 2 |  |         return self::export([$staticRoutes, [$dynamicString."return null;\n    }:", $dynamicVar], $doCache ? $collection : null]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 184 |  |  |     } | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 185 |  |  | } | 
            
                                                        
            
                                    
            
            
                | 186 |  |  |  |