| Total Complexity | 40 |
| Total Lines | 315 |
| Duplicated Lines | 0 % |
| Changes | 5 | ||
| Bugs | 2 | Features | 0 |
Complex classes like View often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use View, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 26 | class View |
||
| 27 | { |
||
| 28 | /** |
||
| 29 | * Views configuration |
||
| 30 | * |
||
| 31 | * @var array |
||
| 32 | */ |
||
| 33 | protected $config; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * @var RendererInterface |
||
| 37 | */ |
||
| 38 | private $adapter; |
||
| 39 | |||
| 40 | /** |
||
| 41 | * Liste des adapters pris en comptes |
||
| 42 | * |
||
| 43 | * @var array |
||
| 44 | */ |
||
| 45 | public static $validAdapters = [ |
||
| 46 | 'native' => NativeAdapter::class, |
||
| 47 | 'blade' => BladeAdapter::class, |
||
| 48 | 'latte' => LatteAdapter::class, |
||
| 49 | 'plates' => PlatesAdapter::class, |
||
| 50 | 'smarty' => SmartyAdapter::class, |
||
| 51 | 'twig' => TwigAdapter::class, |
||
| 52 | ]; |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Options de la vue |
||
| 56 | * |
||
| 57 | * @var array |
||
| 58 | */ |
||
| 59 | private $options = []; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * La vue à rendre |
||
| 63 | * |
||
| 64 | * @var string |
||
| 65 | */ |
||
| 66 | private $view; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * Données partagées à toutes les vues |
||
| 70 | * |
||
| 71 | * @var array |
||
| 72 | */ |
||
| 73 | private static $shared = []; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * Constructeur |
||
| 77 | */ |
||
| 78 | public function __construct() |
||
| 79 | { |
||
| 80 | $this->config = config('view'); |
||
|
|
|||
| 81 | static::share($this->config['shared']); |
||
| 82 | $this->setAdapter($this->config['active_adapter'] ?? 'native'); |
||
| 83 | } |
||
| 84 | |||
| 85 | public function __toString() |
||
| 86 | { |
||
| 87 | return $this->get(); |
||
| 88 | } |
||
| 89 | |||
| 90 | /** |
||
| 91 | * Defini les données partagées entre plusieurs vues |
||
| 92 | */ |
||
| 93 | public static function share(array|string|Closure $key, mixed $value = null): void |
||
| 94 | { |
||
| 95 | if ($key instanceof Closure) { |
||
| 96 | $key = Services::container()->call($key); |
||
| 97 | } |
||
| 98 | if (is_string($key)) { |
||
| 99 | $key = [$key => $value]; |
||
| 100 | } |
||
| 101 | |||
| 102 | static::$shared = array_merge(static::$shared, $key); |
||
| 103 | } |
||
| 104 | |||
| 105 | /** |
||
| 106 | * Recupere et retourne le code html de la vue créée |
||
| 107 | * |
||
| 108 | * @param bool|string $compress |
||
| 109 | */ |
||
| 110 | public function get($compress = 'auto'): string |
||
| 111 | { |
||
| 112 | $output = $this->adapter->render($this->view, $this->options); |
||
| 113 | $output = $this->decorate($output); |
||
| 114 | |||
| 115 | return $this->compressView($output, $compress); |
||
| 116 | } |
||
| 117 | |||
| 118 | /** |
||
| 119 | * Affiche la vue generee au navigateur |
||
| 120 | */ |
||
| 121 | public function render(): void |
||
| 122 | { |
||
| 123 | $compress = $this->config['compress_output'] ?? 'auto'; |
||
| 124 | |||
| 125 | echo $this->get($compress); |
||
| 126 | } |
||
| 127 | |||
| 128 | /** |
||
| 129 | * Modifier les options d'affichage |
||
| 130 | */ |
||
| 131 | public function setOptions(?array $options = []): self |
||
| 132 | { |
||
| 133 | $this->options = (array) $options; |
||
| 134 | |||
| 135 | return $this; |
||
| 136 | } |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Définir la vue à afficher |
||
| 140 | */ |
||
| 141 | public function display(string $view): self |
||
| 142 | { |
||
| 143 | $this->view = $view; |
||
| 144 | |||
| 145 | return $this; |
||
| 146 | } |
||
| 147 | |||
| 148 | /** |
||
| 149 | * Définit plusieurs éléments de données de vue à la fois. |
||
| 150 | */ |
||
| 151 | public function addData(array $data = [], ?string $context = null): self |
||
| 152 | { |
||
| 153 | unset($data['errors']); |
||
| 154 | |||
| 155 | $data = array_merge(static::$shared, $data); |
||
| 156 | |||
| 157 | $this->adapter->addData($data, $context); |
||
| 158 | |||
| 159 | if (! array_key_exists('errors', $this->getData())) { |
||
| 160 | $this->setValidationErrors(); |
||
| 161 | } |
||
| 162 | |||
| 163 | return $this; |
||
| 164 | } |
||
| 165 | |||
| 166 | /** |
||
| 167 | * Définit plusieurs éléments de données de vue à la fois. |
||
| 168 | */ |
||
| 169 | public function with(array|string $key, mixed $value = null, ?string $context = null): self |
||
| 170 | { |
||
| 171 | if (is_array($key)) { |
||
| 172 | $context = $value; |
||
| 173 | } else { |
||
| 174 | $key = [$key => $value]; |
||
| 175 | } |
||
| 176 | |||
| 177 | return $this->addData($key, $context); |
||
| 178 | } |
||
| 179 | |||
| 180 | /** |
||
| 181 | * Définit une seule donnée de vue. |
||
| 182 | * |
||
| 183 | * @param mixed|null $value |
||
| 184 | */ |
||
| 185 | public function setVar(string $name, $value = null, ?string $context = null): self |
||
| 186 | { |
||
| 187 | $this->adapter->setVar($name, $value, $context); |
||
| 188 | |||
| 189 | return $this; |
||
| 190 | } |
||
| 191 | |||
| 192 | /** |
||
| 193 | * Remplacer toutes les données de vue par de nouvelles données |
||
| 194 | */ |
||
| 195 | public function setData(array $data, ?string $context = null): self |
||
| 196 | { |
||
| 197 | unset($data['errors']); |
||
| 198 | |||
| 199 | $data = array_merge(static::$shared, $data); |
||
| 200 | |||
| 201 | $this->adapter->setData($data, $context); |
||
| 202 | |||
| 203 | if (! array_key_exists('errors', $this->getData())) { |
||
| 204 | $this->setValidationErrors(); |
||
| 205 | } |
||
| 206 | |||
| 207 | return $this; |
||
| 208 | } |
||
| 209 | |||
| 210 | /** |
||
| 211 | * Remplacer toutes les données de vue par de nouvelles données |
||
| 212 | */ |
||
| 213 | public function getData(): array |
||
| 214 | { |
||
| 215 | return $this->adapter->getData(); |
||
| 216 | } |
||
| 217 | |||
| 218 | /** |
||
| 219 | * Supprime toutes les données de vue du système. |
||
| 220 | */ |
||
| 221 | public function resetData(): self |
||
| 222 | { |
||
| 223 | $this->adapter->resetData(); |
||
| 224 | |||
| 225 | return $this; |
||
| 226 | } |
||
| 227 | |||
| 228 | /** |
||
| 229 | * Definit le layout a utiliser par les vues |
||
| 230 | */ |
||
| 231 | public function setLayout(string $layout): self |
||
| 232 | { |
||
| 233 | $this->adapter->setLayout($layout); |
||
| 234 | |||
| 235 | return $this; |
||
| 236 | } |
||
| 237 | |||
| 238 | /** |
||
| 239 | * Defini l'adapteur à utiliser |
||
| 240 | */ |
||
| 241 | public function setAdapter(string $adapter, array $config = []): self |
||
| 267 | } |
||
| 268 | |||
| 269 | /** |
||
| 270 | * Renvoie les données de performances qui ont pu être collectées |
||
| 271 | * lors de l'exécution. Utilisé principalement dans la barre d'outils de débogage. |
||
| 272 | */ |
||
| 273 | public function getPerformanceData(): array |
||
| 274 | { |
||
| 275 | return $this->adapter->getPerformanceData(); |
||
| 276 | } |
||
| 277 | |||
| 278 | /** |
||
| 279 | * Compresse le code html d'une vue |
||
| 280 | */ |
||
| 281 | protected function compressView(string $output, bool|callable|string $compress = 'auto'): string |
||
| 282 | { |
||
| 283 | $compress = $compress === 'auto' ? ($this->options['compress_output'] ?? 'auto') : $compress; |
||
| 284 | $compress = $compress === 'auto' ? ($this->config['compress_output'] ?? 'auto') : $compress; |
||
| 285 | |||
| 286 | if (is_callable($compress)) { |
||
| 287 | $compress = Services::container()->call($compress); |
||
| 288 | } |
||
| 289 | |||
| 290 | if ($compress === 'auto') { |
||
| 291 | $compress = is_online(); |
||
| 292 | } |
||
| 293 | |||
| 294 | return true === $compress ? trim(preg_replace('/\s+/', ' ', $output)) : $output; |
||
| 295 | } |
||
| 296 | |||
| 297 | /** |
||
| 298 | * Exécute la sortie générée via tous les décorateurs de vue déclarés. |
||
| 299 | */ |
||
| 300 | protected function decorate(string $output): string |
||
| 301 | { |
||
| 302 | foreach ($this->config['decorators'] as $decorator) { |
||
| 303 | if (!is_subclass_of($decorator, ViewDecoratorInterface::class)) { |
||
| 304 | throw ViewException::invalidDecorator($decorator); |
||
| 305 | } |
||
| 306 | |||
| 307 | $output = $decorator::decorate($output); |
||
| 308 | } |
||
| 309 | |||
| 310 | return $output; |
||
| 311 | } |
||
| 312 | |||
| 313 | /** |
||
| 314 | * Defini les erreurs de validation pour la vue |
||
| 315 | */ |
||
| 316 | private function setValidationErrors() |
||
| 341 | } |
||
| 342 | } |
||
| 343 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.