UserInformation   C
last analyzed

Complexity

Total Complexity 64

Size/Duplication

Total Lines 489
Duplicated Lines 8.18 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 64
lcom 1
cbo 4
dl 40
loc 489
rs 5.8364
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __toString() 0 4 1
C __get() 0 25 11
C __set() 0 27 11
A getNickname() 0 4 1
A getNicknameY() 0 4 1
A getSex() 0 4 1
A getBloodType() 0 4 1
A getBirthdateY() 0 4 1
A getBirthdateM() 0 4 1
A getBirthdateD() 0 4 1
A getAge() 0 4 1
A getConstellations() 0 4 1
A getPlace() 0 4 1
A setNickname() 0 6 1
A setNicknameY() 0 10 2
A setSex() 10 10 4
B setBloodType() 0 12 6
A setBirthdateY() 0 12 2
A setBirthdateM() 10 10 2
A setBirthdateD() 10 10 2
A setAge() 10 10 2
A setConstellations() 0 22 3
A setPlace() 0 9 3
A makeParameter() 0 10 3
A className() 0 4 1

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like UserInformation 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 UserInformation, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * 送信パラメータのうち、利用者に関するパラメータを管理するクラス
4
 * @author AIZAWA Hina <[email protected]>
5
 * @copyright 2015 by AIZAWA Hina <[email protected]>
6
 * @license https://github.com/fetus-hina/docomo-dialogue/blob/master/LICENSE MIT
7
 * @since 0.1.0
8
 */
9
10
namespace jp3cki\docomoDialogue;
11
12
/**
13
 * 送信パラメータのうち、利用者に関するパラメータを管理するクラス
14
 *
15
 * @property string $nickname       ニックネーム
16
 * @property string $nickname_y     ニックネームの読み
17
 * @property string $sex            性別
18
 * @property string $bloodtype      血液型
19
 * @property int    $birthdate_y    生年月日(年)
20
 * @property int    $birthdate_m    生年月日(月)
21
 * @property int    $birthdate_d    生年月日(日)
22
 * @property int    $age            年齢
23
 * @property string $constellations 星座
24
 * @property string $place          ユーザの場所
25
 *
26
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
27
 */
28
class UserInformation
29
{
30
    /** 男性 */
31
    const SEX_MALE      = '男';
32
    /** 女性 */
33
    const SEX_FEMALE    = '女';
34
    /** 不明/その他の性 */
35
    const SEX_OTHERS    = null;
36
37
    /** 血液型: A */
38
    const BLOOD_TYPE_A  = 'A';
39
    /** 血液型: B */
40
    const BLOOD_TYPE_B  = 'B';
41
    /** 血液型: O */
42
    const BLOOD_TYPE_O  = 'O';
43
    /** 血液型: AB */
44
    const BLOOD_TYPE_AB = 'AB';
45
    /** 血液型: 不明 */
46
    const BLOOD_TYPE_UNKNOWN = null;
47
48
    /** 星座: 牡羊座 */
49
    const CONSTELLATION_ARIES       = '牡羊座';
50
    /** 星座: 牡牛座 */
51
    const CONSTELLATION_TAURUS      = '牡牛座';
52
    /** 星座: 双子座 */
53
    const CONSTELLATION_GEMINI      = '双子座';
54
    /** 星座: 蟹座 */
55
    const CONSTELLATION_CANCER      = '蟹座';
56
    /** 星座: 獅子座 */
57
    const CONSTELLATION_LEO         = '獅子座';
58
    /** 星座: 乙女座 */
59
    const CONSTELLATION_VIRGO       = '乙女座';
60
    /** 星座: 天秤座 */
61
    const CONSTELLATION_LIBRA       = '天秤座';
62
    /** 星座: 蠍座 */
63
    const CONSTELLATION_SCORPIUS    = '蠍座';
64
    /** 星座: 射手座 */
65
    const CONSTELLATION_SAGITTARIUS = '射手座';
66
    /** 星座: 山羊座 */
67
    const CONSTELLATION_CAPRICONUS  = '山羊座';
68
    /** 星座: 水瓶座 */
69
    const CONSTELLATION_AQUARIUS    = '水瓶座';
70
    /** 星座: 魚座 */
71
    const CONSTELLATION_PISCES      = '魚座';
72
73
    /** @internal 送信パラメータ */
74
    private $parameters = [
75
        'nickname'          => null,                        // ユーザのニックネーム
76
        'nickname_y'        => null,                        // ユーザのニックネームの読み(カナ)
77
        'sex'               => self::SEX_OTHERS,            // ユーザの性別
78
        'bloodtype'         => self::BLOOD_TYPE_UNKNOWN,    // ユーザの血液型
79
        'birthdateY'        => null,                        // ユーザの誕生日(Y)
80
        'birthdateM'        => null,                        // ユーザの誕生日(M)
81
        'birthdateD'        => null,                        // ユーザの誕生日(D)
82
        'age'               => null,                        // ユーザの年齢
83
        'constellations'    => null,                        // ユーザの星座
84
        'place'             => null,                        // ユーザの場所
85
    ];
86
87
    /**
88
     * @internal 場所情報として有効な地点の一覧
89
     * @see setPlace()
90
     * @see getPlace()
91
     */
92
    private static $validPlaces = [
93
        '稚内', '旭川', '留萌', '網走', '北見', '紋別', '根室', '釧路', '帯広', '室蘭',
94
        '浦河', '札幌', '岩見沢', '倶知安', '函館', '江差', '青森', '弘前', '深浦',
95
        'むつ', '八戸', '秋田', '横手', '鷹巣', '盛岡', '二戸', '一関', '宮古',
96
        '大船渡', '山形', '米沢', '酒田', '新庄', '仙台', '古川', '石巻', '白石',
97
        '福島', '郡山', '白河', '小名浜', '相馬', '若松', '田島', '宇都宮', '大田原',
98
        '水戸', '土浦', '前橋', 'みなかみ', 'さいたま', '熊谷', '秩父', '東京', '大島',
99
        '八丈島', '父島', '千葉', '銚子', '館山', '横浜', '小田原', '甲府', '河口湖',
100
        '長野', '松本', '諏訪', '軽井沢', '飯田', '静岡', '網代', '石廊崎', '三島',
101
        '浜松', '御前崎', '新潟', '津川', '長岡', '湯沢', '高田', '相川', '富山',
102
        '伏木', '岐阜', '高山', '名古屋', '豊橋', '福井', '大野', '敦賀', '金沢',
103
        '輪島', '大津', '彦根', '津', '上野', '四日市', '尾鷲', '京都', '舞鶴', '奈良',
104
        '風屋', '和歌山', '潮岬', '大阪', '神戸', '姫路', '洲本', '豊岡', '鳥取',
105
        '米子', '岡山', '津山', '松江', '浜田', '西郷', '広島', '呉', '福山', '庄原',
106
        '下関', '山口', '柳井', '萩', '高松', '徳島', '池田', '日和佐', '松山',
107
        '新居浜', '宇和島', '高知', '室戸岬', '清水', '福岡', '八幡', '飯塚', '久留米',
108
        '佐賀', '伊万里', '長崎', '佐世保', '厳原', '福江', '大分', '中津', '日田',
109
        '佐伯', '熊本', '阿蘇乙姫', '牛深', '人吉', '宮崎', '油津', '延岡', '都城',
110
        '高千穂', '鹿児島', '阿久根', '枕崎', '鹿屋', '種子島', '名瀬', '沖永良部',
111
        '那覇', '名護', '久米島', '南大東島', '宮古島', '石垣島','与那国島', '海外'
112
    ];
113
114
    /**
115
     * マジックメソッド __toString
116
     *
117
     * デバッグ等で表示する際に中身がわかるように JSON で返すだけで、
118
     * 表現そのものに意味はないし依存してはならない
119
     *
120
     * @return string
121
     */
122
    public function __toString()
123
    {
124
        return json_encode($this->makeParameter(), JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
125
    }
126
127
    /**
128
     * マジックメソッド __get
129
     *
130
     * @param   string  $key    プロパティ取得用のキー
131
     * @return  string          キーに対応する値
132
     *
133
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
134
     */
135
    public function __get($key)
136
    {
137
        switch ($key) {
138
            case 'nickname':
139
                return $this->getNickname();
140
            case 'nickname_y':
141
                return $this->getNicknameY();
142
            case 'sex':
143
                return $this->getSex();
144
            case 'bloodtype':
145
                return $this->getBloodType();
146
            case 'birthdate_y':
147
                return $this->getBirthdateY();
148
            case 'birthdate_m':
149
                return $this->getBirthdateM();
150
            case 'birthdate_d':
151
                return $this->getBirthdateD();
152
            case 'age':
153
                return $this->getAge();
154
            case 'constellations':
155
                return $this->getConstellations();
156
            case 'place':
157
                return $this->getPlace();
158
        }
159
    }
160
161
    /**
162
     * マジックメソッド __set
163
     *
164
     * @param   string  $key    プロパティ設定用のキー
165
     * @param   string  $value  設定する値
166
     *
167
     * @throws  InvalidArgumentException    対応するキーが存在しない時
168
     * @throws  DomainError                 設定する値が異常な時
169
     *
170
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
171
     */
172
    public function __set($key, $value)
173
    {
174
        switch ($key) {
175
            case 'nickname':
176
                return $this->setNickname($value);
177
            case 'nickname_y':
178
                return $this->setNicknameY($value);
179
            case 'sex':
180
                return $this->setSex($value);
181
            case 'bloodtype':
182
                return $this->setBloodType($value);
183
            case 'birthdate_y':
184
                return $this->setBirthdateY($value);
185
            case 'birthdate_m':
186
                return $this->setBirthdateM($value);
187
            case 'birthdate_d':
188
                return $this->setBirthdateD($value);
189
            case 'age':
190
                return $this->setAge($value);
191
            case 'constellations':
192
                return $this->setConstellations($value);
193
            case 'place':
194
                return $this->setPlace($value);
195
            default:
196
                throw new InvalidArgumentException("Unknown key {$key}");
197
        }
198
    }
199
200
    /**
201
     * ユーザのニックネームを取得
202
     *
203
     * @return string
204
     */
205
    public function getNickname()
206
    {
207
        return $this->parameters['nickname'];
208
    }
209
210
    /**
211
     * ユーザのニックネーム(読み)を取得
212
     *
213
     * @return string
214
     */
215
    public function getNicknameY()
216
    {
217
        return $this->parameters['nickname_y'];
218
    }
219
220
    /**
221
     * ユーザの性別を取得
222
     *
223
     * @return string
224
     */
225
    public function getSex()
226
    {
227
        return $this->parameters['sex'];
228
    }
229
230
    /**
231
     * ユーザの血液型を取得
232
     *
233
     * @return string
234
     */
235
    public function getBloodType()
236
    {
237
        return $this->parameters['bloodtype'];
238
    }
239
240
    /**
241
     * ユーザの生年月日(年)を取得
242
     *
243
     * @return string
244
     */
245
    public function getBirthdateY()
246
    {
247
        return $this->parameters['birthdateY'];
248
    }
249
250
    /**
251
     * ユーザの生年月日(月)を取得
252
     *
253
     * @return string
254
     */
255
    public function getBirthdateM()
256
    {
257
        return $this->parameters['birthdateM'];
258
    }
259
260
    /**
261
     * ユーザの生年月日(日)を取得
262
     *
263
     * @return string
264
     */
265
    public function getBirthdateD()
266
    {
267
        return $this->parameters['birthdateD'];
268
    }
269
270
    /**
271
     * ユーザの年齢を取得
272
     *
273
     * @return string
274
     */
275
    public function getAge()
276
    {
277
        return $this->parameters['age'];
278
    }
279
280
    /**
281
     * ユーザの星座を取得
282
     *
283
     * @return string
284
     */
285
    public function getConstellations()
286
    {
287
        return $this->parameters['constellations'];
288
    }
289
290
    /**
291
     * ユーザの場所を取得
292
     *
293
     * @return string
294
     */
295
    public function getPlace()
296
    {
297
        return $this->parameters['place'];
298
    }
299
300
    /**
301
     * ユーザのニックネームを設定する
302
     *
303
     * @param   string  $value              ニックネーム(最大10文字)
304
     * @return  self
305
     * @throws  DomainError
306
     */
307
    public function setNickname($value)
308
    {
309
        validators\Text::validate($value, 10, 'nickname too long');
310
        $this->parameters['nickname'] = mb_substr($value, 0, 10, 'UTF-8');
311
        return $this;
312
    }
313
314
    /**
315
     * ユーザのニックネーム(読み)を設定する
316
     *
317
     * @param   string  $value          ニックネームの読み(最大20文字)
318
     * @return  self
319
     * @throws  DomainError
320
     */
321
    public function setNicknameY($value)
322
    {
323
        validators\Text::validate($value, 20, 'nickname(yomi) too long');
324
        $value = mb_substr($value, 0, 20, 'UTF-8');
325
        if (!preg_match('/^[゠-ヿ]+$/u', $value)) {
326
            throw new DomainError('nickname(yomi) accept only katakana');
327
        }
328
        $this->parameters['nickname_y'] = $value;
329
        return $this;
330
    }
331
332
    /**
333
     * ユーザの性別を設定する
334
     *
335
     * @param   string  $value          ユーザの性別    SEX_MALE | SEX_FEMALE | SEX_OTHERS
336
     * @return  self
337
     * @throws  DomainError
338
     */
339 View Code Duplication
    public function setSex($value)
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...
340
    {
341
        if ($value !== self::SEX_MALE &&
342
           $value !== self::SEX_FEMALE &&
343
           $value !== self::SEX_OTHERS) {
344
            throw new DomainError("Invalid sex");
345
        }
346
        $this->parameters['sex'] = $value;
347
        return $this;
348
    }
349
350
    /**
351
     * ユーザの血液型を設定する
352
     *
353
     * @param   string  $value          ユーザの血液型    BLOOD_TYPE_*
354
     * @return  self
355
     * @throws  DomainError
356
     */
357
    public function setBloodType($value)
358
    {
359
        if ($value !== self::BLOOD_TYPE_A &&
360
           $value !== self::BLOOD_TYPE_B &&
361
           $value !== self::BLOOD_TYPE_O &&
362
           $value !== self::BLOOD_TYPE_AB &&
363
           $value !== self::BLOOD_TYPE_UNKNOWN) {
364
            throw new DomainError("Invalid bloodtype");
365
        }
366
        $this->parameters['bloodtype'] = $value;
367
        return $this;
368
    }
369
370
    /**
371
     * ユーザの生年月日(年)を設定する
372
     *
373
     * @param   int     $value          ユーザの生年月日(年)
374
     * @return  self
375
     * @throws  DomainError
376
     */
377
    public function setBirthdateY($value)
378
    {
379
        if ($value === null) {
380
            $this->parameters['birthdateY'] = null;
381
        } else {
382
            $datetime = new \DateTime('now');
383
            $max = (int)$datetime->format('Y');
384
            validators\Number::validate($value, 1, $max, 'Invalid birthdateY');
385
            $this->parameters['birthdateY'] = min($max, max(1, (int)$value));
386
        }
387
        return $this;
388
    }
389
390
    /**
391
     * ユーザの生年月日(月)を設定する
392
     *
393
     * @param   int     $value          ユーザの生年月日(月)
394
     * @return  self
395
     * @throws  DomainError
396
     */
397 View Code Duplication
    public function setBirthdateM($value)
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...
398
    {
399
        if ($value === null) {
400
            $this->parameters['birthdateM'] = null;
401
        } else {
402
            validators\Number::validate($value, 1, 12, 'Invalid birthdateM');
403
            $this->parameters['birthdateM'] = min(12, max(1, (int)$value));
404
        }
405
        return $this;
406
    }
407
408
    /**
409
     * ユーザの生年月日(日)を設定する
410
     *
411
     * @param   int     $value          ユーザの生年月日(日)
412
     * @return  self
413
     * @throws  DomainError
414
     */
415 View Code Duplication
    public function setBirthdateD($value)
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...
416
    {
417
        if ($value === null) {
418
            $this->parameters['birthdateD'] = null;
419
        } else {
420
            validators\Number::validate($value, 1, 31, 'Invalid birthdateD');
421
            $this->parameters['birthdateD'] = min(31, max(1, (int)$value));
422
        }
423
        return $this;
424
    }
425
426
    /**
427
     * ユーザの年齢を設定する
428
     *
429
     * @param   int     $value          ユーザの年齢
430
     * @return  self
431
     * @throws  DomainError
432
     */
433 View Code Duplication
    public function setAge($value)
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...
434
    {
435
        if ($value === null) {
436
            $this->parameters['age'] = null;
437
        } else {
438
            validators\Number::validate($value, 0, null, 'Invalid age');
439
            $this->parameters['age'] = max(0, (int)$value);
440
        }
441
        return $this;
442
    }
443
444
    /**
445
     * ユーザの星座を設定する
446
     *
447
     * @param   string  $value          ユーザの星座    CONSTELLATION_*
448
     * @return  self
449
     * @throws  DomainError
450
     */
451
    public function setConstellations($value)
452
    {
453
        if ($value !== null && !in_array($value, [
454
                self::CONSTELLATION_ARIES,
455
                self::CONSTELLATION_TAURUS,
456
                self::CONSTELLATION_GEMINI,
457
                self::CONSTELLATION_CANCER,
458
                self::CONSTELLATION_LEO,
459
                self::CONSTELLATION_VIRGO,
460
                self::CONSTELLATION_LIBRA,
461
                self::CONSTELLATION_SCORPIUS,
462
                self::CONSTELLATION_SAGITTARIUS,
463
                self::CONSTELLATION_CAPRICONUS,
464
                self::CONSTELLATION_AQUARIUS,
465
                self::CONSTELLATION_PISCES,
466
            ])
467
        ) {
468
            throw new DomainError('Invalid constellations');
469
        }
470
        $this->parameters['constellations'] = $value;
471
        return $this;
472
    }
473
474
    /**
475
     * ユーザの場所を設定する
476
     *
477
     * @param   string  $value          ユーザの場所    one of $validPlaces
478
     * @return  self
479
     * @throws  DomainError
480
     */
481
    public function setPlace($value)
482
    {
483
        if ($value !== null &&
484
           !in_array($value, self::$validPlaces, true)) {
485
            throw new DomainError('Invalid place');
486
        }
487
        $this->parameters['place'] = $value;
488
        return $this;
489
    }
490
491
    /**
492
     * 送信用のパラメータ配列を作成して取得する
493
     *
494
     * @return  array
495
     */
496
    public function makeParameter()
497
    {
498
        $ret = [];
499
        foreach ($this->parameters as $k => $v) {
500
            if ($v !== null) {
501
                $ret[$k] = $v;
502
            }
503
        }
504
        return $ret;
505
    }
506
507
    /**
508
     * クラス名(FQCN)を取得
509
     *
510
     * return string
511
     */
512
    public static function className()
513
    {
514
        return get_called_class();
515
    }
516
}
517