GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

PlaceholdersRepository   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 181
Duplicated Lines 7.73 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 4
dl 14
loc 181
rs 9
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getTags() 0 4 1
A loadConfigFiles() 0 9 2
A getConfig() 7 7 2
A getFilePath() 7 7 2
A getEnvironment() 0 4 1
A setEnvironment() 0 4 1
A getScenarioTags() 0 3 1
C replaceRecursively() 0 51 10
A getReplacement() 0 4 1
B recursiveReplacementSearch() 0 20 7
A getSectionPlaceholders() 0 12 3
A setReplacement() 0 10 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
namespace Ciandt\Behat\PlaceholdersExtension\Config;
3
4
use Symfony\Component\Yaml\Yaml;
5
use Ciandt\Behat\PlaceholdersExtension\Utils\PlaceholderUtils;
6
use Ciandt\Behat\PlaceholdersExtension\PlaceholderContainer\PlaceholderContainer;
7
use Ciandt\Behat\PlaceholdersExtension\Exception\MissingSectionException;
8
use Ciandt\Behat\PlaceholdersExtension\Exception\UndefinedPlaceholderException;
9
use Exception;
10
11
/**
12
 * Description of ConfigsRepository
13
 *
14
 * @author bwowk
15
 */
16
class PlaceholdersRepository
17
{
18
    const PLACEHOLDER_REGEX = '/\${(?P<placeholder>[a-zA-Z0-9_-]+)}/';
19
    
20
    private $configs;
21
    
22
    private $beforeScenarioSubscriber;
23
    
24
    private $runtimePlaceholders = array();
25
26
    /**
27
     * @var string
28
     */
29
    private $environment;
30
31
    public function __construct($configs_mapping, $beforeScenarioSubscriber)
32
    {
33
        $this->configs = $this->loadConfigFiles($configs_mapping);
34
        $this->beforeScenarioSubscriber = $beforeScenarioSubscriber;
35
    }
36
37
    /**
38
     *
39
     * @return string[]
40
     * @todo read configs and also bring alternative @config:section tags
41
     */
42
    public function getTags()
43
    {
44
        return array_keys($this->configs);
45
    }
46
47
    /**
48
     * reads the YAML placeholder definitions and returns an associative array
49
     *
50
     * @param type $config_files
0 ignored issues
show
Bug introduced by
There is no parameter named $config_files. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
51
     * @todo use %paths.base% value
52
     * @return array
53
     */
54
    private function loadConfigFiles($configs_mapping)
55
    {
56
        $placeholder_maps = array();
57
        foreach ($configs_mapping as $tag => $file_path) {
58
            $placeholder_maps[$tag]['config'] = Yaml::parse(file_get_contents($file_path));
59
            $placeholder_maps[$tag]['path'] = $file_path;
60
        }
61
        return $placeholder_maps;
62
    }
63
64 View Code Duplication
    private function getConfig($key)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
65
    {
66
        if (key_exists($key, $this->configs)) {
67
            return $this->configs[$key]['config'];
68
        }
69
        return null;
70
    }
71
72 View Code Duplication
    private function getFilePath($key)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
73
    {
74
         if (key_exists($key, $this->configs)) {
75
            return $this->configs[$key]['path'];
76
        }
77
        return null;
78
    }
79
       
80
    public function getEnvironment()
81
    {
82
        return $this->environment;
83
    }
84
85
    public function setEnvironment($environment)
86
    {
87
        $this->environment = $environment;
88
    }
89
    
90
    private function getScenarioTags(){
91
        return $this->beforeScenarioSubscriber->getScenarioTags();
92
    }
93
94
    private function replaceRecursively($placeholder, $replaced = array())
95
    {
96
        // if the current placeholder was already replaced before, this is a cyclic dependecy
97
        if (in_array($placeholder, $replaced)){
98
            $tree = implode('>', $replaced);
99
            throw new Exception("Cyclic placeholder dependecy detected. Trying to replace $placeholder again when already replaced: $tree");
100
        }
101
102
        $tags = $this->getScenarioTags();
103
104
        //@todo abort if there's no config tag
105
        $configTag = PlaceholderUtils::getConfigTag($tags);
106
        $configKey = PlaceholderUtils::getConfigKey($configTag);
107
108
        $variantTags = PlaceholderUtils::filterVariantTags($tags, false);
109
        $variant = end($variantTags);
110
        $environment = $this->getEnvironment();
111
112
        $keys = array('$' . $variant, '$' . $environment, $placeholder);
113
114
        // First try to find it in the runtime placeholders
115
        $replacement = $this->recursiveReplacementSearch($keys, $this->runtimePlaceholders);
116
117
118
        // Then look in the placeholder files
119
        if ($replacement === null && $configTag !== false ) {
120
            $section = PlaceholderUtils::getSectionKey($configTag);
121
            $placeholders = $this->getSectionPlaceholders($configKey, $section);
122
            $replacement = $this->recursiveReplacementSearch($keys, $placeholders);
123
        }
124
125
        if ($replacement === null && $configTag !== false){
126
            $configPath = $this->getFilePath($configKey);
127
            $treePosition = "$configPath>$section>placeholders";
0 ignored issues
show
Bug introduced by
The variable $section does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
128
            throw new UndefinedPlaceholderException("No $placeholder replacement was defined on runtime or on $treePosition>$placeholder for variant $variant and environment $environment");
129
        } elseif ($replacement === null && $configTag === false) {
130
            throw new UndefinedPlaceholderException("No $placeholder replacement was defined on runtime, and this scenario is not linked with any replacements file");
131
        }
132
133
        //if the replaced string doesn't have placeholders itself, return it right away
134
        if (preg_match_all(self::PLACEHOLDER_REGEX, $replacement, $matches, PREG_SET_ORDER) == 0) {
135
            return $replacement;
136
        }
137
138
        //if it does have, replace them before returning
139
        foreach ($matches as $match) {
0 ignored issues
show
Bug introduced by
The expression $matches of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
140
            $replaced[] = $placeholder;
141
            $replacement = str_replace('${' . $match['placeholder'] . '}', $this->replaceRecursively($match['placeholder'], $replaced), $replacement);
142
        }
143
        return $replacement;
144
    }
145
146
    public function getReplacement($placeholder)
147
    {
148
        return $this->replaceRecursively($placeholder);
149
    }
150
    
151
    private function recursiveReplacementSearch($keys, $values)
152
    {
153
        if (empty($keys) || is_scalar($values)) {
154
            return $values;
155
        }
156
        $key = array_pop($keys);
157
        if (key_exists($key, $values)) {
158
            $specificValue = $this->recursiveReplacementSearch($keys, $values[$key]);
159
            if ($specificValue) {
160
                return $specificValue;
161
            }
162
        } if (key_exists('$default', $values)) {
163
            $defaultValue = $this->recursiveReplacementSearch($keys, $values['$default']);
164
            if ($defaultValue) {
165
                return $defaultValue;
166
            }
167
        }
168
        
169
        return null;
170
    }
171
    
172
    private function getSectionPlaceholders($configKey, $section)
173
    {
174
        $config = $this->getConfig($configKey);
175
        if (!isset($config) || !key_exists($section, $config)) {
176
            throw new MissingSectionException(
177
                $configKey,
178
                $this->getFilePath($configKey),
179
                $section
180
            );
181
        }
182
        return $config[$section]['placeholders'];
183
    }
184
    
185
    public function setReplacement($key, $value, $environment = '$default', $variant = '$default')
186
    {
187
        if (!isset($this->runtimePlaceholders[$key])) {
188
            $this->runtimePlaceholders[$key] = array();
189
        }
190
        if (!isset($this->runtimePlaceholders[$key][$environment])) {
191
            $this->runtimePlaceholders[$key][$environment] = array();
192
        }
193
        $this->runtimePlaceholders[$key][$environment][$variant] = $value;
194
    }
195
196
}
197