Completed
Push — master ( db5f77...4370c7 )
by Thomas
30s queued 10s
created

Namer::getAttributeName()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 5
c 1
b 0
f 1
dl 0
loc 11
ccs 4
cts 4
cp 1
rs 10
cc 3
nc 4
nop 3
crap 3
1
<?php
2
3
namespace ORM;
4
5
use ORM\Exception\InvalidConfiguration;
6
use ORM\Exception\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
 * @author  Thomas Flori <[email protected]>
16
 */
17
class Namer
18
{
19
    /** The template to use to calculate the table name.
20
     * @var string */
21
    protected $tableNameTemplate = '%short%';
22
23
    /** The naming scheme to use for table names.
24
     * @var string */
25
    protected $tableNameScheme = 'snake_lower';
26
27
    /** @var string[] */
28
    protected $tableNames = [];
29
30
    /** @var string[][] */
31
    protected $columnNames = [];
32
33
    /** The naming scheme to use for column names.
34
     * @var string */
35
    protected $columnNameScheme = 'snake_lower';
36
37
    /** The naming scheme used for method names.
38
     * @var string */
39
    protected $methodNameScheme = 'camelCase';
40
41
    /** The naming scheme used for attributes.
42
     * @var string */
43
    protected $attributeNameScheme = 'camelCase';
44
45
    /**
46 297
     * Namer constructor.
47
     *
48 297
     * @param array $options
49 4
     */
50
    public function __construct($options = [])
51 297
    {
52
        foreach ($options as $option => $value) {
53
            $this->setOption($option, $value);
54
        }
55
    }
56
57
    /**
58
     * Set $option to $value
59
     *
60 4
     * @param string $option
61
     * @param mixed  $value
62
     * @return static
63 4
     */
64 1
    public function setOption($option, $value)
65 1
    {
66
        switch ($option) {
67 3
            case EntityManager::OPT_TABLE_NAME_TEMPLATE:
68 1
                $this->tableNameTemplate = $value;
69 1
                break;
70
71 2
            case EntityManager::OPT_NAMING_SCHEME_TABLE:
72 1
                $this->tableNameScheme = $value;
73 1
                break;
74
75 1
            case EntityManager::OPT_NAMING_SCHEME_COLUMN:
76 1
                $this->columnNameScheme = $value;
77 1
                break;
78
79
            case EntityManager::OPT_NAMING_SCHEME_METHODS:
80 4
                $this->methodNameScheme = $value;
81
                break;
82
83
            case EntityManager::OPT_NAMING_SCHEME_ATTRIBUTE:
84
                $this->attributeNameScheme = $value;
85
                break;
86
        }
87
88
        return $this;
89
    }
90
91
    /**
92 147
     * Get the table name for $reflection
93
     *
94 147
     * @param string $class
95 147
     * @param string $template
96 2
     * @param string $namingScheme
97
     * @return string
98
     * @throws InvalidName
99 147
     */
100 4
    public function getTableName($class, $template = null, $namingScheme = null)
101
    {
102
        if (!isset($this->tableNames[$class])) {
103 147
            if ($template === null) {
104
                $template = $this->tableNameTemplate;
105 147
            }
106
107
            if ($namingScheme === null) {
108 147
                $namingScheme = $this->tableNameScheme;
109 147
            }
110 147
111
            $reflection = new ReflectionClass($class);
112 147
113
            $name = $this->substitute(
114
                $template,
115 146
                [
116 2
                    'short'     => $reflection->getShortName(),
117
                    'namespace' => explode('\\', $reflection->getNamespaceName()),
118
                    'name'      => preg_split('/[\\\\_]+/', $reflection->name),
119 144
                ],
120
                '_'
121
            );
122 143
123
            if (empty($name)) {
124
                throw new InvalidName('Table name can not be empty');
125
            }
126
127
            $this->tableNames[$class] = $this->forceNamingScheme($name, $namingScheme);
128
        }
129
130
        return $this->tableNames[$class];
131
    }
132
133
    /**
134 166
     * Get the column name with $namingScheme or default naming scheme
135
     *
136 166
     * @param string $class
137 166
     * @param string $attribute
138 2
     * @param string $prefix
139
     * @param string $namingScheme
140
     * @return string
141 166
     */
142
    public function getColumnName($class, $attribute, $prefix = null, $namingScheme = null)
143 166
    {
144 22
        if (!isset($this->columnNames[$class][$attribute])) {
145
            if ($namingScheme === null) {
146
                $namingScheme = $this->columnNameScheme;
147 166
            }
148
149
            $name = $this->forceNamingScheme($attribute, $namingScheme);
150 166
151
            if ($prefix !== null && strpos($name, $prefix) !== 0) {
152
                $name = $prefix . $name;
153
            }
154
155
            $this->columnNames[$class][$attribute] = $name;
156
        }
157
158
        return $this->columnNames[$class][$attribute];
159
    }
160 121
161
    /**
162 121
     * Get the method name with $namingScheme or default naming scheme
163 2
     *
164
     * @param string $name
165
     * @param string $namingScheme
166 121
     * @return string
167
     */
168
    public function getMethodName($name, $namingScheme = null)
169
    {
170
        if ($namingScheme === null) {
171
            $namingScheme = $this->methodNameScheme;
172
        }
173
174
        return $this->forceNamingScheme($name, $namingScheme);
175
    }
176
177
178
    /**
179 186
     * Get the attribute name with $namingScheme or default naming scheme
180
     *
181 186
     * @param string $name
182 186
     * @param string $namingScheme
183 186
     * @return string
184
     */
185 185
    public function getAttributeName($name, $prefix = null, $namingScheme = null)
186 1
    {
187
        if ($namingScheme === null) {
188
            $namingScheme = $this->attributeNameScheme;
189 185
        }
190 186
191
        if ($prefix !== null) {
192
            $name = preg_replace('~^' . preg_quote($prefix) . '~', '', $name);
193
        }
194
195
        return $this->forceNamingScheme($name, $namingScheme);
196
    }
197
198
    /**
199
     * Substitute a $template with $values
200
     *
201
     * $values is a key value pair array. The value should be a string or an array o
202
     *
203
     * @param string $template
204
     * @param array  $values
205
     * @param string $arrayGlue
206 255
     * @return string
207
     */
208 255
    public function substitute($template, $values = [], $arrayGlue = ', ')
209 255
    {
210 255
        return preg_replace_callback(
211 255
            '/%(.*?)%/',
212 24
            function ($match) use ($values, $arrayGlue) {
213 255
                // escape % with another % ( %% => % )
214
                if ($match[0] === '%%') {
215
                    return '%';
216
                }
217 255
218 23
                return $this->getValue(trim($match[1]), $values, $arrayGlue);
219 23
            },
220
            $template
221 232
        );
222 198
    }
223 198
224
    /**
225 149
     * Enforce $namingScheme to $name
226 4
     *
227 4
     * Supported naming schemes: snake_case, snake_lower, SNAKE_UPPER, Snake_Ucfirst, camelCase, StudlyCaps, lower
228
     * and UPPER.
229 145
     *
230 4
     * @param string $name The name of the var / column
231 4
     * @param string $namingScheme The naming scheme to use
232
     * @return string
233 141
     * @throws InvalidConfiguration
234 123
     */
235 123
    public function forceNamingScheme($name, $namingScheme)
236
    {
237 18
        $words = explode('_', preg_replace(
238 9
            '/([a-z0-9])([A-Z])/',
239 9
            '$1_$2',
240
            preg_replace_callback('/([a-z0-9])?([A-Z]+)([A-Z][a-z])/', function ($match) {
241 9
                return ($match[1] ? $match[1] . '_' : '') . $match[2] . '_' . $match[3];
242 4
            }, $name)
243 4
        ));
244
245 5
        switch ($namingScheme) {
246 4
            case 'snake_case':
247 4
                $newName = implode('_', $words);
248
                break;
249
250 1
            case 'snake_lower':
251
                $newName = implode('_', array_map('strtolower', $words));
252
                break;
253 254
254
            case 'SNAKE_UPPER':
255
                $newName = implode('_', array_map('strtoupper', $words));
256
                break;
257
258
            case 'Snake_Ucfirst':
259
                $newName = implode('_', array_map('ucfirst', $words));
260
                break;
261
262
            case 'camelCase':
263
                $newName = lcfirst(implode('', array_map('ucfirst', array_map('strtolower', $words))));
264
                break;
265 185
266
            case 'StudlyCaps':
267 185
                $newName = implode('', array_map('ucfirst', array_map('strtolower', $words)));
268 185
                break;
269 11
270 11
            case 'lower':
271
                $newName = implode('', array_map('strtolower', $words));
272 174
                break;
273
274
            case 'UPPER':
275
                $newName = implode('', array_map('strtoupper', $words));
276 185
                break;
277 1
278 1
            default:
279
                throw new InvalidConfiguration('Naming scheme ' . $namingScheme . ' unknown');
280
        }
281
282 184
        return $newName;
283 168
    }
284
285
    /**
286 16
     * Get the value for $attribute from $values using $arrayGlue
287
     *
288
     * @param string $attribute The key for $values
289
     * @param array $values
290
     * @param string $arrayGlue
291
     * @return string
292
     * @throws InvalidConfiguration
293
     */
294
    protected function getValue($attribute, $values, $arrayGlue)
295
    {
296
        $placeholder = '%' . $attribute . '%';
297 16
        if (preg_match('/\[(-?\d+\*?)\]$/', $attribute, $arrayAccessor)) {
298
            $attribute     = substr($attribute, 0, strpos($attribute, '['));
299 16
            $arrayAccessor = $arrayAccessor[1];
300 11
        } else {
301 11
            $arrayAccessor = '';
302
        }
303 11
304 2
        // throw when the variable is unknown
305
        if (!array_key_exists($attribute, $values)) {
306
            throw new InvalidConfiguration(
307 9
                'Template invalid: Placeholder ' . $placeholder . ' is not allowed'
308 9
            );
309
        }
310
311 14
        if (is_scalar($values[$attribute]) || is_null($values[$attribute])) {
312
            return (string) $values[$attribute];
313
        }
314
315
        return $this->arrayToString($values[$attribute], $arrayAccessor, $arrayGlue);
316
    }
317
318
    /**
319
     * Convert array to string using indexes defined by $accessor
320
     *
321
     * @param array  $array
322
     * @param string $accessor
323
     * @param string $glue
324
     * @return string
325
     */
326
    protected function arrayToString(array $array, $accessor, $glue)
327
    {
328
        if (isset($accessor[0])) {
329
            $from = $accessor[0] === '-' ?
330
                count($array) - abs($accessor) : (int) $accessor;
331
332
            if ($from >= count($array)) {
333
                return '';
334
            }
335
336
            $array = substr($accessor, -1) === '*' ?
337
                array_slice($array, $from) : [ $array[$from] ];
0 ignored issues
show
Bug introduced by
It seems like $from can also be of type double; however, parameter $offset of array_slice() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

337
                array_slice($array, /** @scrutinizer ignore-type */ $from) : [ $array[$from] ];
Loading history...
338
        }
339
340
        return implode($glue, $array);
341
    }
342
}
343