Test Failed
Branch feature-validator (6de9aa)
by Thomas
02:45
created

Namer   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 40
c 0
b 0
f 0
lcom 1
cbo 2
dl 0
loc 272
ccs 96
cts 96
cp 1
rs 8.2608

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
B setOption() 0 22 5
B getTableName() 0 28 5
B getColumnName() 0 18 5
A getMethodName() 0 8 2
A substitute() 0 15 2
C forceNamingScheme() 0 49 10
D getValue() 0 36 9

How to fix   Complexity   

Complex Class

Complex classes like Namer 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Namer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace ORM;
4
5
use ORM\Exceptions\InvalidConfiguration;
6
use ORM\Exceptions\InvalidName;
7
use ReflectionClass;
8
9
/**
10
 * Namer is for naming errors, columns, tables and methods
11
 *
12
 * Namer is an artificial word and is more a name giver. We just don't wanted to write so much.
13
 *
14
 * @package ORM
15
 */
16
class Namer
17
{
18
    /** The template to use to calculate the table name.
19
     * @var string */
20
    protected $tableNameTemplate = '%short%';
21
22
    /** The naming scheme to use for table names.
23
     * @var string */
24
    protected $tableNameScheme = 'snake_lower';
25
26
    /** @var string[] */
27
    protected $tableNames = [];
28
29
    /** @var string[][] */
30
    protected $columnNames = [];
31
32
    /** The naming scheme to use for column names.
33
     * @var string */
34
    protected $columnNameScheme = 'snake_lower';
35
36
    /** The naming scheme used for method names.
37
     * @var string */
38 265
    protected $methodNameScheme = 'camelCase';
39
40 265
    /**
41 4
     * Namer constructor.
42
     *
43 265
     * @param array $options
44
     */
45
    public function __construct($options = [])
46
    {
47
        foreach ($options as $option => $value) {
48
            $this->setOption($option, $value);
49
        }
50
    }
51
52 4
    /**
53
     * Set $option to $value
54
     *
55 4
     * @param string $option
56 1
     * @param mixed $value
57 1
     * @return self
58
     */
59 3
    public function setOption($option, $value)
60 1
    {
61 1
        switch ($option) {
62
            case EntityManager::OPT_TABLE_NAME_TEMPLATE:
63 2
                $this->tableNameTemplate = $value;
64 1
                break;
65 1
66
            case EntityManager::OPT_NAMING_SCHEME_TABLE:
67 1
                $this->tableNameScheme = $value;
68 1
                break;
69 1
70
            case EntityManager::OPT_NAMING_SCHEME_COLUMN:
71
                $this->columnNameScheme = $value;
72 4
                break;
73
74
            case EntityManager::OPT_NAMING_SCHEME_METHODS:
75
                $this->methodNameScheme = $value;
76
                break;
77
        }
78
79
        return $this;
80
    }
81
82
    /**
83 130
     * Get the table name for $reflection
84
     *
85 130
     * @param string $class
86 2
     * @param string $template
87
     * @param string $namingScheme
88
     * @return string
89 130
     * @throws InvalidName
90 4
     */
91
    public function getTableName($class, $template = null, $namingScheme = null)
92
    {
93 130
        if (!isset($this->tableNames[$class])) {
94 130
            if ($template === null) {
95 130
                $template = $this->tableNameTemplate;
96 130
            }
97 130
98
            if ($namingScheme === null) {
99 129
                $namingScheme = $this->tableNameScheme;
100
            }
101
102
            $reflection = new ReflectionClass($class);
103
104
            $name = $this->substitute($template, [
105
                    'short'     => $reflection->getShortName(),
106
                    'namespace' => explode('\\', $reflection->getNamespaceName()),
107
                    'name'      => preg_split('/[\\\\_]+/', $reflection->name),
108
                ], '_');
109 139
110
            if (empty($name)) {
111 139
                throw new InvalidName('Table name can not be empty');
112 2
            }
113
114
            $this->tableNames[$class] = $this->forceNamingScheme($name, $namingScheme);
115 139
        }
116
117
        return $this->tableNames[$class];
118
    }
119
120
    /**
121
     * Get the column name with $namingScheme or default naming scheme
122
     *
123
     * @param        $class
124
     * @param string $field
125 89
     * @param string $prefix
126
     * @param string $namingScheme
127 89
     * @return string
128 2
     */
129
    public function getColumnName($class, $field, $prefix = null, $namingScheme = null)
130
    {
131 89
        if (!isset($this->columnNames[$class][$field])) {
132
            if (!$namingScheme) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $namingScheme of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
133
                $namingScheme = $this->columnNameScheme;
134
            }
135
136
            $name = $this->forceNamingScheme($field, $namingScheme);
137
138
            if ($prefix !== null && strpos($name, $prefix) !== 0) {
139
                $name = $prefix . $name;
140
            }
141
142
            $this->columnNames[$class][$field] = $name;
143
        }
144 168
145
        return $this->columnNames[$class][$field];
146 168
    }
147 168
148
    /**
149
     * Get the column name with $namingScheme or default naming scheme
150 167
     *
151 1
     * @param $name
152
     * @param null $namingScheme
153
     * @return string
154 167
     */
155 168
    public function getMethodName($name, $namingScheme = null)
156
    {
157
        if (!$namingScheme) {
158
            $namingScheme = $this->methodNameScheme;
159
        }
160
161
        return $this->forceNamingScheme($name, $namingScheme);
162
    }
163
164
    /**
165
     * Substitute a $template with $values
166
     *
167
     * $values is a key value pair array. The value should be a string or an array o
168
     *
169
     * @param string $template
170
     * @param array  $values
171 226
     * @param string $arrayGlue
172
     * @return string
173 226
     */
174 226
    public function substitute($template, $values = [], $arrayGlue = ', ')
175 226
    {
176 226
        return preg_replace_callback(
177 24
            '/%(.*?)%/',
178 226
            function ($match) use ($values, $arrayGlue) {
179
                // escape % with another % ( %% => % )
180
                if ($match[0] === '%%') {
181
                    return '%';
182 226
                }
183 25
184 25
                return $this->getValue(trim($match[1]), $values, $arrayGlue);
185
            },
186 201
            $template
187 168
        );
188 168
    }
189
190 117
    /**
191 4
     * Enforce $namingScheme to $name
192 4
     *
193
     * Supported naming schemes: snake_case, snake_lower, SNAKE_UPPER, Snake_Ucfirst, camelCase, StudlyCaps, lower
194 113
     * and UPPER.
195 4
     *
196 4
     * @param string $name         The name of the var / column
197
     * @param string $namingScheme The naming scheme to use
198 109
     * @return string
199 91
     * @throws InvalidConfiguration
200 91
     */
201
    public function forceNamingScheme($name, $namingScheme)
202 18
    {
203 9
        $words = explode('_', preg_replace(
204 9
            '/([a-z0-9])([A-Z])/',
205
            '$1_$2',
206 9
            preg_replace_callback('/([a-z0-9])?([A-Z]+)([A-Z][a-z])/', function ($d) {
207 4
                return ($d[1] ? $d[1] . '_' : '') . $d[2] . '_' . $d[3];
208 4
            }, $name)
209
        ));
210 5
211 4
        switch ($namingScheme) {
212 4
            case 'snake_case':
213
                $newName = implode('_', $words);
214
                break;
215 1
216
            case 'snake_lower':
217
                $newName = implode('_', array_map('strtolower', $words));
218 225
                break;
219
220
            case 'SNAKE_UPPER':
221 167
                $newName = implode('_', array_map('strtoupper', $words));
222
                break;
223 167
224 167
            case 'Snake_Ucfirst':
225 11
                $newName = implode('_', array_map('ucfirst', $words));
226 11
                break;
227
228
            case 'camelCase':
229
                $newName = lcfirst(implode('', array_map('ucfirst', array_map('strtolower', $words))));
230 167
                break;
231 1
232 1
            case 'StudlyCaps':
233
                $newName = implode('', array_map('ucfirst', array_map('strtolower', $words)));
234
                break;
235
236 166
            case 'lower':
237 150
                $newName = implode('', array_map('strtolower', $words));
238
                break;
239
240
            case 'UPPER':
241 16
                $newName = implode('', array_map('strtoupper', $words));
242
                break;
243 16
244 11
            default:
245 11
                throw new InvalidConfiguration('Naming scheme ' . $namingScheme . ' unknown');
246
        }
247 11
248 2
        return $newName;
249
    }
250
251 9
    protected function getValue($var, $values, $arrayGlue)
252 9
    {
253
        $placeholder = '%' . $var . '%';
254
        if (preg_match('/\[(-?\d+\*?)\]$/', $var, $arrayAccessor)) {
255 14
            $var = substr($var, 0, strpos($var, '['));
256
            $arrayAccessor = $arrayAccessor[1];
257
        }
258
259
        // throw when the variable is unknown
260
        if (!array_key_exists($var, $values)) {
261
            throw new InvalidConfiguration(
262
                'Template invalid: Placeholder ' . $placeholder . ' is not allowed'
263
            );
264
        }
265
266
        if (is_scalar($values[$var]) || is_null($values[$var])) {
267
            return (string)$values[$var];
268
        }
269
270
        // otherwise we assume it is an array
271
        $array = $values[$var];
272
273
        if (isset($arrayAccessor[0])) {
274
            $from = $arrayAccessor[0] === '-' ?
275
                count($array) - abs($arrayAccessor) : (int)$arrayAccessor;
276
277
            if ($from >= count($array)) {
278
                return '';
279
            }
280
281
            $array = substr($arrayAccessor, -1) === '*' ?
282
                array_slice($array, $from) : [$array[$from]];
283
        }
284
285
        return implode($arrayGlue, $array);
286
    }
287
}
288