Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.51% covered (success)
75.51%
37 / 49
84.62% covered (success)
84.62%
11 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
Define
75.51% covered (success)
75.51%
37 / 49
84.62% covered (success)
84.62%
11 / 13
37.71
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 noKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 key
57.69% covered (warning)
57.69%
15 / 26
0.00% covered (danger)
0.00%
0 / 1
10.71
 value
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 arrayDefine
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 isIndexArrayDefine
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isAssocArrayDefine
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isExchange
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 isKeyExchange
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 isValueExchange
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 nestedTypeDefineExists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isAllowKeyAction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 defineConflict
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * @license MIT
5 * @author hazuki3417<hazuki3417@gmail.com>
6 * @copyright 2022 hazuki3417 all rights reserved.
7 */
8
9namespace Selen\Schema\Exchange;
10
11use Selen\Schema\Exchange\Define\Key;
12
13class Define
14{
15    public const KEY_ACTION_NONE   = 'none';
16    public const KEY_ACTION_ADD    = 'add';
17    public const KEY_ACTION_REMOVE = 'remove';
18    public const KEY_ACTION_RENAME = 'rename';
19    public const KEY_ACTIONS       = [
20        self::KEY_ACTION_NONE,
21        self::KEY_ACTION_ADD,
22        self::KEY_ACTION_REMOVE,
23        self::KEY_ACTION_RENAME,
24    ];
25
26    /** @var Key */
27    public $key;
28
29    /** @var KeyExchangeInterface|callable|null */
30    public $keyExchangeExecute;
31
32    /** @var ValueExchangeInterface|callable|null */
33    public $valueExchangeExecute;
34
35    /** @var ArrayDefine|null */
36    public $arrayDefine;
37
38    /** @var bool */
39    private $haveCalledValue = false;
40
41    /** @var bool */
42    private $haveCalledArrayDefine = false;
43
44    /**
45     * インスタンスを生成します
46     *
47     * @return Define
48     */
49    private function __construct(Key $key)
50    {
51        $this->key = $key;
52    }
53
54    /**
55     * 添字配列(index)の定義を生成します
56     */
57    public static function noKey(): Define
58    {
59        // TODO: noKeyが指定されたときのkeyActionの処理を実装する
60        return new self(new Key(null));
61    }
62
63    /**
64     * 連想配列(assoc)の定義を生成します
65     *
66     * @param KeyExchangeInterface|callable|null $execute
67     *
68     * @throws \InvalidArgumentException 引数の型が不正なときに発生します
69     * @throws \ValueError               引数の値が不正なときに発生します
70     */
71    public static function key(string|int $name, string $action = self::KEY_ACTION_NONE, $execute = null): Define
72    {
73        $format = 'Invalid %s %s. expected %s %s.';
74
75        // if ($name === null) {
76        //     $allowType = ['integer', 'string'];
77        //     $mes       = \sprintf($format, '$name', 'type', 'type', \implode(', ', $allowType));
78        //     throw new \InvalidArgumentException($mes);
79        // }
80
81        if (!self::isAllowKeyAction($action)) {
82            $mes = \sprintf($format, '$action', 'value', 'value', \implode(', ', self::KEY_ACTIONS));
83            throw new \ValueError($mes);
84        }
85
86        /** @var bool[] */
87        $allowTypeList = [
88            \is_null($execute),
89            \is_callable($execute),
90            ($execute instanceof KeyExchangeInterface),
91        ];
92
93        if (!\in_array(true, $allowTypeList, true)) {
94            $allowType = [null, 'callable', KeyExchangeInterface::class];
95            $mes       = \sprintf($format, '$execute', 'type', 'type', \implode(', ', $allowType));
96            throw new \InvalidArgumentException($mes);
97        }
98
99        $key = new Key($name);
100
101        switch (true) {
102            case $action === self::KEY_ACTION_ADD:
103                $key = $key->enableAdd();
104                break;
105            case $action === self::KEY_ACTION_REMOVE:
106                $key = $key->enableRemove();
107                break;
108            case $action === self::KEY_ACTION_RENAME:
109                $key = $key->enableRename();
110                break;
111            default:
112                break;
113        }
114        $self                     = new self($key);
115        $self->keyExchangeExecute = $execute;
116
117        return $self;
118    }
119
120    /**
121     * @throws \LogicException メソッドの呼び出し順が不正なときに発生します
122     */
123    public function value(ValueExchangeInterface|callable|null $execute = null): Define
124    {
125        if ($this->defineConflict()) {
126            throw new \LogicException('Invalid method call. cannot call value method after arrayDefine.');
127        }
128
129        $this->haveCalledValue      = true;
130        $this->valueExchangeExecute = $execute;
131        return $this;
132    }
133
134    /**
135     * @throws \LogicException メソッドの呼び出し順が不正なときに発生します
136     *
137     * @param Define $define 定義を指定します
138     */
139    public function arrayDefine(Define ...$define): Define
140    {
141        if ($this->defineConflict()) {
142            throw new \LogicException('Invalid method call. cannot call arrayDefine method after value.');
143        }
144
145        // NOTE: 引数の指定がない場合はそのまま通す(エラーにしない)
146
147        $this->haveCalledArrayDefine = true;
148        $this->arrayDefine           = new ArrayDefine(...$define);
149
150        return $this;
151    }
152
153    /**
154     * IndexArray(keyなし)か確認します
155     *
156     * @return bool IndexArrayの場合はtrueを、それ以外の場合はfalseを返します
157     */
158    public function isIndexArrayDefine(): bool
159    {
160        return $this->key->getName() === null;
161    }
162
163    /**
164     * AssocArray(keyあり)か確認します
165     *
166     * @return bool AssocArrayの場合はtrueを、それ以外の場合はfalseを返します
167     */
168    public function isAssocArrayDefine(): bool
169    {
170        return $this->key->getName() !== null;
171    }
172
173    /**
174     * 変換処理を実行するか判定します。
175     *
176     * @return bool 変換する場合はtrueを、それ以外の場合はfalseを返します
177     */
178    public function isExchange(): bool
179    {
180        return $this->isKeyExchange() || $this->isValueExchange();
181    }
182
183    /**
184     * keyの変換処理を実行するか判定します。
185     *
186     * @return bool 変換する場合はtrueを、それ以外の場合はfalseを返します
187     */
188    public function isKeyExchange(): bool
189    {
190        return
191            $this->key->isAddKey()
192            || $this->key->isRemoveKey()
193            || $this->key->isRenameKey();
194    }
195
196    /**
197     * valueの変換処理を実行するか判定します。
198     *
199     * @return bool 変換する場合はtrueを、それ以外の場合はfalseを返します
200     */
201    public function isValueExchange(): bool
202    {
203        // NOTE: defineConflictでチェックしているので、片方のみの判定でも良い
204        return $this->valueExchangeExecute !== null && $this->arrayDefine === null;
205    }
206
207    /**
208     * ネストされた定義が存在するか確認します
209     *
210     * @return bool 存在する場合はtrueを、それ以外の場合はfalseを返します
211     */
212    public function nestedTypeDefineExists(): bool
213    {
214        // NOTE: defineConflictでチェックしているので、片方のみの判定でも良い
215        return $this->valueExchangeExecute === null && $this->arrayDefine !== null;
216    }
217
218    private static function isAllowKeyAction(string $name): bool
219    {
220        return \in_array($name, self::KEY_ACTIONS, true);
221    }
222
223    /**
224     * 定義呼び出しが競合しているか確認します
225     *
226     * @return bool 競合する場合はtrueを、それ以外の場合はfalseを返します
227     */
228    private function defineConflict()
229    {
230        return $this->haveCalledValue || $this->haveCalledArrayDefine;
231    }
232}