Skip to content

Commit

Permalink
Merge a22283c into 3d1b1b3
Browse files Browse the repository at this point in the history
  • Loading branch information
weierophinney committed Nov 5, 2019
2 parents 3d1b1b3 + a22283c commit ac763d5
Show file tree
Hide file tree
Showing 32 changed files with 1,606 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.phpunit.result.cache
/composer.lock
/docs/html/
/vendor/
Expand Down
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ env:

matrix:
include:
- php: 5.3
dist: precise
- php: 5.4
- php: 5.5
- php: 5.6
- php: 7.0
- php: 7.1
Expand Down
16 changes: 10 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
"forum": "https://discourse.laminas.dev/"
},
"require": {
"php": "^5.3 || ^7.0"
"php": "^5.6 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.1"
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1"
},
"autoload": {
"files": [
Expand All @@ -34,15 +34,19 @@
"test/classes.php"
],
"psr-4": {
"LaminasTest\\ZendFrameworkBridge\\": "test//",
"Apigility\\": "test//TestAsset//Apigility//",
"Expressive\\": "test//TestAsset//Expressive//",
"Laminas\\": "test//TestAsset//Laminas//"
"LaminasTest\\ZendFrameworkBridge\\": "test/",
"LaminasTest\\ZendFrameworkBridge\\TestAsset\\": "test/TestAsset/classes/",
"Apigility\\": "test/TestAsset/Apigility/",
"Expressive\\": "test/TestAsset/Expressive/",
"Laminas\\": "test/TestAsset/Laminas/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
},
"laminas": {
"module": "Laminas\\ZendFrameworkBridge"
}
},
"config": {
Expand Down
379 changes: 379 additions & 0 deletions config/replacements.php

Large diffs are not rendered by default.

265 changes: 265 additions & 0 deletions src/ConfigPostProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
<?php
/**
* @see https://github.com/laminas/laminas-zendframework-bridge for the canonical source repository
* @copyright https://github.com/laminas/laminas-zendframework-bridge/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-zendframework-bridge/blob/master/LICENSE.md New BSD License
*/

namespace Laminas\ZendFrameworkBridge;

class ConfigPostProcessor
{
/** @var array String keys => string values */
private $exactReplacements = [
'zend-expressive' => 'expressive',
'zf-apigility' => 'apigility',
];

/** @var Replacements */
private $replacements;

/** @var callable[] */
private $rulesets;

public function __construct()
{
$this->replacements = new Replacements();

/* Define the rulesets for replacements.
*
* Each ruleset has the following signature:
*
* @param mixed $value
* @param string[] $keys Full nested key hierarchy leading to the value
* @return null|callable
*
* If no match is made, a null is returned, allowing it to fallback to
* the next ruleset in the list. If a match is made, a callback is returned,
* and that will be used to perform the replacement on the value.
*
* The callback should have the following signature:
*
* @param mixed $value
* @param string[] $keys
* @return mixed The transformed value
*/
$this->rulesets = [
// Exact values
function ($value) {
return is_string($value) && isset($this->exactReplacements[$value])
? [$this, 'replaceExactValue']
: null;
},

// Router (MVC applications)
// We do not want to rewrite these.
function ($value, array $keys) {
$key = array_pop($keys);
// Only worried about a top-level "router" key.
return $key === 'router' && count($keys) === 0 && is_array($value)
? [$this, 'noopReplacement']
: null;
},

// Aliases and invokables
function ($value, array $keys) {
static $keysOfInterest;

$keysOfInterest = $keysOfInterest ?: ['aliases', 'invokables'];
$key = array_pop($keys);

return in_array($key, $keysOfInterest, true) && is_array($value)
? [$this, 'replaceDependencyAliases']
: null;
},

// Array values
function ($value, array $keys) {
return 0 !== count($keys) && is_array($value)
? [$this, '__invoke']
: null;
},
];
}

/**
* @param string[] $keys Hierarchy of keys, for determining location in
* nested configuration.
* @return array
*/
public function __invoke(array $config, $keys = [])
{
$rewritten = [];

foreach ($config as $key => $value) {
// Determine new key from replacements
$newKey = is_string($key) ? $this->replace($key, $keys) : $key;

// Keep original values with original key, if the key has changed, but only at the top-level.
if (empty($keys) && $newKey !== $key) {
$rewritten[$key] = $value;
}

// Perform value replacements, if any
$newValue = $this->replace($value, $keys, $newKey);

// Key does not already exist and/or is not an array value
if (! array_key_exists($newKey, $rewritten) || ! is_array($rewritten[$newKey])) {
// Do not overwrite existing values with null values
$rewritten[$newKey] = array_key_exists($newKey, $rewritten) && null === $newValue
? $rewritten[$newKey]
: $newValue;
continue;
}

// New value is null; nothing to do.
if (null === $newValue) {
continue;
}

// Key already exists as an array value, but $value is not an array
if (! is_array($newValue)) {
$rewritten[$newKey][] = $newValue;
continue;
}

// Key already exists as an array value, and $value is also an array
$rewritten[$newKey] = static::merge($rewritten[$newKey], $newValue);
}

return $rewritten;
}

/**
* Perform substitutions as needed on an individual value.
*
* The $key is provided to allow fine-grained selection of rewrite rules.
*
* @param mixed $value
* @param string[] $keys Key hierarchy
* @param null|int|string $key
* @return mixed
*/
private function replace($value, array $keys, $key = null)
{
// Add new key to the list of keys.
// We do not need to remove it later, as we are working on a copy of the array.
array_push($keys, $key);

// Identify rewrite strategy and perform replacements
$rewriteRule = $this->replacementRuleMatch($value, $keys);
return $rewriteRule($value, $keys);
}

/**
* Merge two arrays together.
*
* If an integer key exists in both arrays, the value from the second array
* will be appended to the first array. If both values are arrays, they are
* merged together, else the value of the second array overwrites the one
* of the first array.
*
* Based on zend-stdlib Zend\Stdlib\ArrayUtils::merge
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
*
* @param array $a
* @param array $b
* @return array
*/
public static function merge(array $a, array $b)
{
foreach ($b as $key => $value) {
if (! isset($a[$key]) && ! array_key_exists($key, $a)) {
$a[$key] = $value;
continue;
}

if (null === $value && array_key_exists($key, $a)) {
// Leave as-is if value from $b is null
continue;
}

if (is_int($key)) {
$a[] = $value;
continue;
}

if (is_array($value) && is_array($a[$key])) {
$a[$key] = static::merge($a[$key], $value);
continue;
}

$a[$key] = $value;
}

return $a;
}

/**
* @param mixed $value
* @param null|int|string $key
* @return callable Callable to invoke with value
*/
private function replacementRuleMatch($value, $key = null)
{
foreach ($this->rulesets as $ruleset) {
$result = $ruleset($value, $key);
if (is_callable($result)) {
return $result;
}
}
return [$this, 'fallbackReplacement'];
}

/**
* Replace a value using the translation table, if the value is a string.
*
* @param mixed $value
* @return mixed
*/
private function fallbackReplacement($value)
{
return is_string($value)
? $this->replacements->replace($value)
: $value;
}

/**
* Replace a value matched exactly.
*
* @param mixed $value
* @return mixed
*/
private function replaceExactValue($value)
{
return $this->exactReplacements[$value];
}

/**
* Rewrite dependency aliases array
*
* In this case, we want to keep the alias as-is, but rewrite the target.
*
* This same logic can be used for invokables, which are essentially just
* an alias map.
*
* @param array $value
* @return array
*/
private function replaceDependencyAliases(array $aliases)
{
foreach ($aliases as $alias => $target) {
$aliases[$alias] = $this->replacements->replace($target);
}
return $aliases;
}

/**
* @param mixed $value
* @return mixed Returns $value verbatim.
*/
private function noopReplacement($value)
{
return $value;
}
}
53 changes: 53 additions & 0 deletions src/Module.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
/**
* @see https://github.com/laminas/laminas-zendframework-bridge for the canonical source repository
* @copyright https://github.com/laminas/laminas-zendframework-bridge/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-zendframework-bridge/blob/master/LICENSE.md New BSD License
*/

namespace Laminas\ZendFrameworkBridge;

use Laminas\ModuleManager\Listener\ConfigMergerInterface;
use Laminas\ModuleManager\ModuleEvent;
use Laminas\ModuleManager\ModuleManager;

class Module
{
/**
* Initialize the module.
*
* Type-hinting deliberately omitted to allow unit testing
* without dependencies on packages that do not exist yet.
*
* @param ModuleManager $moduleManager
*/
public function init($moduleManager)
{
$moduleManager
->getEventManager()
->attach('mergeConfig', [$this, 'onMergeConfig']);
}

/**
* Perform substitutions in the merged configuration.
*
* Rewrites keys and values matching known ZF classes, namespaces, and
* configuration keys to their Laminas equivalents.
*
* Type-hinting deliberately omitted to allow unit testing
* without dependencies on packages that do not exist yet.
*
* @param ModuleEvent $moduleEvent
*/
public function onMergeConfig($event)
{
/** @var ConfigMergerInterface */
$configMerger = $event->getConfigListener();
$processor = new ConfigPostProcessor();
$configMerger->setMergedConfig(
$processor(
$configMerger->getMergedConfig($returnAsObject = false)
)
);
}
}

0 comments on commit ac763d5

Please sign in to comment.