Completed
Pull Request — master (#41)
by Thomas
03:37
created

Namer::setOption()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

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