[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/laminas/laminas-zendframework-bridge/src/ -> ConfigPostProcessor.php (source)

   1  <?php
   2  
   3  /**
   4   * @see       https://github.com/laminas/laminas-zendframework-bridge for the canonical source repository
   5   * @copyright https://github.com/laminas/laminas-zendframework-bridge/blob/master/COPYRIGHT.md
   6   * @license   https://github.com/laminas/laminas-zendframework-bridge/blob/master/LICENSE.md New BSD License
   7   */
   8  
   9  namespace Laminas\ZendFrameworkBridge;
  10  
  11  use function array_intersect_key;
  12  use function array_key_exists;
  13  use function array_pop;
  14  use function array_push;
  15  use function count;
  16  use function in_array;
  17  use function is_array;
  18  use function is_callable;
  19  use function is_int;
  20  use function is_string;
  21  
  22  class ConfigPostProcessor
  23  {
  24      /** @internal */
  25      const SERVICE_MANAGER_KEYS_OF_INTEREST = [
  26          'aliases'    => true,
  27          'factories'  => true,
  28          'invokables' => true,
  29          'services'   => true,
  30      ];
  31  
  32      /** @var array String keys => string values */
  33      private $exactReplacements = [
  34          'zend-expressive' => 'mezzio',
  35          'zf-apigility'    => 'api-tools',
  36      ];
  37  
  38      /** @var Replacements */
  39      private $replacements;
  40  
  41      /** @var callable[] */
  42      private $rulesets;
  43  
  44      public function __construct()
  45      {
  46          $this->replacements = new Replacements();
  47  
  48          /* Define the rulesets for replacements.
  49           *
  50           * Each ruleset has the following signature:
  51           *
  52           * @param mixed $value
  53           * @param string[] $keys Full nested key hierarchy leading to the value
  54           * @return null|callable
  55           *
  56           * If no match is made, a null is returned, allowing it to fallback to
  57           * the next ruleset in the list. If a match is made, a callback is returned,
  58           * and that will be used to perform the replacement on the value.
  59           *
  60           * The callback should have the following signature:
  61           *
  62           * @param mixed $value
  63           * @param string[] $keys
  64           * @return mixed The transformed value
  65           */
  66          $this->rulesets = [
  67              // Exact values
  68              function ($value) {
  69                  return is_string($value) && isset($this->exactReplacements[$value])
  70                      ? [$this, 'replaceExactValue']
  71                      : null;
  72              },
  73  
  74              // Router (MVC applications)
  75              // We do not want to rewrite these.
  76              function ($value, array $keys) {
  77                  $key = array_pop($keys);
  78                  // Only worried about a top-level "router" key.
  79                  return $key === 'router' && count($keys) === 0 && is_array($value)
  80                      ? [$this, 'noopReplacement']
  81                      : null;
  82              },
  83  
  84              // service- and pluginmanager handling
  85              function ($value) {
  86                  return is_array($value) && array_intersect_key(self::SERVICE_MANAGER_KEYS_OF_INTEREST, $value) !== []
  87                      ? [$this, 'replaceDependencyConfiguration']
  88                      : null;
  89              },
  90  
  91              // Array values
  92              function ($value, array $keys) {
  93                  return 0 !== count($keys) && is_array($value)
  94                      ? [$this, '__invoke']
  95                      : null;
  96              },
  97          ];
  98      }
  99  
 100      /**
 101       * @param string[] $keys Hierarchy of keys, for determining location in
 102       *     nested configuration.
 103       * @return array
 104       */
 105      public function __invoke(array $config, array $keys = [])
 106      {
 107          $rewritten = [];
 108  
 109          foreach ($config as $key => $value) {
 110              // Determine new key from replacements
 111              $newKey = is_string($key) ? $this->replace($key, $keys) : $key;
 112  
 113              // Keep original values with original key, if the key has changed, but only at the top-level.
 114              if (empty($keys) && $newKey !== $key) {
 115                  $rewritten[$key] = $value;
 116              }
 117  
 118              // Perform value replacements, if any
 119              $newValue = $this->replace($value, $keys, $newKey);
 120  
 121              // Key does not already exist and/or is not an array value
 122              if (! array_key_exists($newKey, $rewritten) || ! is_array($rewritten[$newKey])) {
 123                  // Do not overwrite existing values with null values
 124                  $rewritten[$newKey] = array_key_exists($newKey, $rewritten) && null === $newValue
 125                      ? $rewritten[$newKey]
 126                      : $newValue;
 127                  continue;
 128              }
 129  
 130              // New value is null; nothing to do.
 131              if (null === $newValue) {
 132                  continue;
 133              }
 134  
 135              // Key already exists as an array value, but $value is not an array
 136              if (! is_array($newValue)) {
 137                  $rewritten[$newKey][] = $newValue;
 138                  continue;
 139              }
 140  
 141              // Key already exists as an array value, and $value is also an array
 142              $rewritten[$newKey] = static::merge($rewritten[$newKey], $newValue);
 143          }
 144  
 145          return $rewritten;
 146      }
 147  
 148      /**
 149       * Perform substitutions as needed on an individual value.
 150       *
 151       * The $key is provided to allow fine-grained selection of rewrite rules.
 152       *
 153       * @param mixed $value
 154       * @param string[] $keys Key hierarchy
 155       * @param null|int|string $key
 156       * @return mixed
 157       */
 158      private function replace($value, array $keys, $key = null)
 159      {
 160          // Add new key to the list of keys.
 161          // We do not need to remove it later, as we are working on a copy of the array.
 162          array_push($keys, $key);
 163  
 164          // Identify rewrite strategy and perform replacements
 165          $rewriteRule = $this->replacementRuleMatch($value, $keys);
 166          return $rewriteRule($value, $keys);
 167      }
 168  
 169      /**
 170       * Merge two arrays together.
 171       *
 172       * If an integer key exists in both arrays, the value from the second array
 173       * will be appended to the first array. If both values are arrays, they are
 174       * merged together, else the value of the second array overwrites the one
 175       * of the first array.
 176       *
 177       * Based on zend-stdlib Zend\Stdlib\ArrayUtils::merge
 178       * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
 179       *
 180       * @return array
 181       */
 182      public static function merge(array $a, array $b)
 183      {
 184          foreach ($b as $key => $value) {
 185              if (! isset($a[$key]) && ! array_key_exists($key, $a)) {
 186                  $a[$key] = $value;
 187                  continue;
 188              }
 189  
 190              if (null === $value && array_key_exists($key, $a)) {
 191                  // Leave as-is if value from $b is null
 192                  continue;
 193              }
 194  
 195              if (is_int($key)) {
 196                  $a[] = $value;
 197                  continue;
 198              }
 199  
 200              if (is_array($value) && is_array($a[$key])) {
 201                  $a[$key] = static::merge($a[$key], $value);
 202                  continue;
 203              }
 204  
 205              $a[$key] = $value;
 206          }
 207  
 208          return $a;
 209      }
 210  
 211      /**
 212       * @param mixed $value
 213       * @param null|int|string $key
 214       * @return callable Callable to invoke with value
 215       */
 216      private function replacementRuleMatch($value, $key = null)
 217      {
 218          foreach ($this->rulesets as $ruleset) {
 219              $result = $ruleset($value, $key);
 220              if (is_callable($result)) {
 221                  return $result;
 222              }
 223          }
 224          return [$this, 'fallbackReplacement'];
 225      }
 226  
 227      /**
 228       * Replace a value using the translation table, if the value is a string.
 229       *
 230       * @param mixed $value
 231       * @return mixed
 232       */
 233      private function fallbackReplacement($value)
 234      {
 235          return is_string($value)
 236              ? $this->replacements->replace($value)
 237              : $value;
 238      }
 239  
 240      /**
 241       * Replace a value matched exactly.
 242       *
 243       * @param mixed $value
 244       * @return mixed
 245       */
 246      private function replaceExactValue($value)
 247      {
 248          return $this->exactReplacements[$value];
 249      }
 250  
 251      private function replaceDependencyConfiguration(array $config)
 252      {
 253          $aliases = isset($config['aliases']) && is_array($config['aliases'])
 254              ? $this->replaceDependencyAliases($config['aliases'])
 255              : [];
 256  
 257          if ($aliases) {
 258              $config['aliases'] = $aliases;
 259          }
 260  
 261          $config = $this->replaceDependencyInvokables($config);
 262          $config = $this->replaceDependencyFactories($config);
 263          $config = $this->replaceDependencyServices($config);
 264  
 265          $keys = self::SERVICE_MANAGER_KEYS_OF_INTEREST;
 266          foreach ($config as $key => $data) {
 267              if (isset($keys[$key])) {
 268                  continue;
 269              }
 270  
 271              $config[$key] = is_array($data) ? $this->__invoke($data, [$key]) :  $data;
 272          }
 273  
 274          return $config;
 275      }
 276  
 277      /**
 278       * Rewrite dependency aliases array
 279       *
 280       * In this case, we want to keep the alias as-is, but rewrite the target.
 281       *
 282       * We need also provide an additional alias if the alias key is a legacy class.
 283       *
 284       * @return array
 285       */
 286      private function replaceDependencyAliases(array $aliases)
 287      {
 288          foreach ($aliases as $alias => $target) {
 289              if (! is_string($alias) || ! is_string($target)) {
 290                  continue;
 291              }
 292  
 293              $newTarget = $this->replacements->replace($target);
 294              $newAlias  = $this->replacements->replace($alias);
 295  
 296              $notIn = [$newTarget];
 297              $name  = $newTarget;
 298              while (isset($aliases[$name])) {
 299                  $notIn[] = $aliases[$name];
 300                  $name    = $aliases[$name];
 301              }
 302  
 303              if ($newAlias === $alias && ! in_array($alias, $notIn, true)) {
 304                  $aliases[$alias] = $newTarget;
 305                  continue;
 306              }
 307  
 308              if (isset($aliases[$newAlias])) {
 309                  continue;
 310              }
 311  
 312              if (! in_array($newAlias, $notIn, true)) {
 313                  $aliases[$alias]    = $newAlias;
 314                  $aliases[$newAlias] = $newTarget;
 315              }
 316          }
 317  
 318          return $aliases;
 319      }
 320  
 321      /**
 322       * Rewrite dependency invokables array
 323       *
 324       * In this case, we want to keep the alias as-is, but rewrite the target.
 325       *
 326       * We need also provide an additional alias if invokable is defined with
 327       * an alias which is a legacy class.
 328       *
 329       * @return array
 330       */
 331      private function replaceDependencyInvokables(array $config)
 332      {
 333          if (empty($config['invokables']) || ! is_array($config['invokables'])) {
 334              return $config;
 335          }
 336  
 337          foreach ($config['invokables'] as $alias => $target) {
 338              if (! is_string($alias)) {
 339                  continue;
 340              }
 341  
 342              $newTarget = $this->replacements->replace($target);
 343              $newAlias  = $this->replacements->replace($alias);
 344  
 345              if ($alias === $target || isset($config['aliases'][$newAlias])) {
 346                  $config['invokables'][$alias] = $newTarget;
 347                  continue;
 348              }
 349  
 350              $config['invokables'][$newAlias] = $newTarget;
 351  
 352              if ($newAlias === $alias) {
 353                  continue;
 354              }
 355  
 356              $config['aliases'][$alias] = $newAlias;
 357  
 358              unset($config['invokables'][$alias]);
 359          }
 360  
 361          return $config;
 362      }
 363  
 364      /**
 365       * @param mixed $value
 366       * @return mixed Returns $value verbatim.
 367       */
 368      private function noopReplacement($value)
 369      {
 370          return $value;
 371      }
 372  
 373      private function replaceDependencyFactories(array $config)
 374      {
 375          if (empty($config['factories']) || ! is_array($config['factories'])) {
 376              return $config;
 377          }
 378  
 379          foreach ($config['factories'] as $service => $factory) {
 380              if (! is_string($service)) {
 381                  continue;
 382              }
 383  
 384              $replacedService = $this->replacements->replace($service);
 385              $factory         = is_string($factory) ? $this->replacements->replace($factory) : $factory;
 386              $config['factories'][$replacedService] = $factory;
 387  
 388              if ($replacedService === $service) {
 389                  continue;
 390              }
 391  
 392              unset($config['factories'][$service]);
 393              if (isset($config['aliases'][$service])) {
 394                  continue;
 395              }
 396  
 397              $config['aliases'][$service] = $replacedService;
 398          }
 399  
 400          return $config;
 401      }
 402  
 403      private function replaceDependencyServices(array $config)
 404      {
 405          if (empty($config['services']) || ! is_array($config['services'])) {
 406              return $config;
 407          }
 408  
 409          foreach ($config['services'] as $service => $serviceInstance) {
 410              if (! is_string($service)) {
 411                  continue;
 412              }
 413  
 414              $replacedService = $this->replacements->replace($service);
 415              $serviceInstance = is_array($serviceInstance) ? $this->__invoke($serviceInstance) : $serviceInstance;
 416  
 417              $config['services'][$replacedService] = $serviceInstance;
 418  
 419              if ($service === $replacedService) {
 420                  continue;
 421              }
 422  
 423              unset($config['services'][$service]);
 424  
 425              if (isset($config['aliases'][$service])) {
 426                  continue;
 427              }
 428  
 429              $config['aliases'][$service] = $replacedService;
 430          }
 431  
 432          return $config;
 433      }
 434  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer