Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
49 / 49
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
DiItem
100.00% covered (success)
100.00%
49 / 49
100.00% covered (success)
100.00%
8 / 8
32
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
11
 hasSingletonValue
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setSingletonValue
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 getSingletonValue
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 class
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 value
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 factory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 disposeImpl
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
8
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\Core\DI;
6
7use TypeError;
8use PeServer\Core\DisposerBase;
9use PeServer\Core\IDisposable;
10use PeServer\Core\Text;
11use PeServer\Core\Throws\ArgumentException;
12use PeServer\Core\Throws\InvalidOperationException;
13use PeServer\Core\Throws\NotImplementedException;
14use PeServer\Core\Throws\NotSupportedException;
15use PeServer\Core\TypeUtility;
16
17/**
18 * DI登録値。
19 *
20 * シングルトンデータは内包する。
21 */
22class DiItem extends DisposerBase
23{
24    #region define
25
26    /**
27     * [ライフサイクル] 毎回作る。
28     */
29    public const LIFECYCLE_TRANSIENT = 0;
30    /**
31     * [ライフサイクル] シングルトン。
32     */
33    public const LIFECYCLE_SINGLETON = 1;
34
35    /**
36     * [登録種別] 型。
37     */
38    public const TYPE_TYPE = 0;
39    /**
40     * [登録種別] 値。
41     */
42    public const TYPE_VALUE = 1;
43    /**
44     * [登録種別] 生成処理。
45     */
46    public const TYPE_FACTORY = 2;
47
48    #endregion
49
50    #region variable
51
52    /**
53     * シングルトンデータを持つか。
54     *
55     * @var bool
56     */
57    private bool $hasSingletonValue = false;
58    /**
59     * シングルトンデータ。
60     *
61     * @var mixed
62     */
63    private mixed $singletonValue = null;
64
65    #endregion
66
67    /**
68     * 生成。
69     *
70     * @param self::LIFECYCLE_* $lifecycle ライフサイクル。
71     * @param self::TYPE_* $type 登録種別。
72     * @param class-string|mixed|callable(IDiContainer, DiItem[]):mixed $data 登録データ。
73     * @param bool $nonDisposal IDisposable 対象にするか。
74     */
75    public function __construct(
76        public int $lifecycle,
77        public int $type,
78        public mixed $data,
79        public bool $nonDisposal = false
80    ) {
81        if ($lifecycle !== self::LIFECYCLE_TRANSIENT && $lifecycle !== self::LIFECYCLE_SINGLETON) { //@phpstan-ignore-line self::LIFECYCLE_*
82            throw new ArgumentException('$lifecycle: ' . $lifecycle);
83        }
84
85        switch ($type) {
86            case self::TYPE_TYPE:
87                if (!is_string($data)) {
88                    throw new TypeError('$data: ' . TypeUtility::getType($data));
89                }
90                if (Text::isNullOrWhiteSpace($data)) {
91                    throw new ArgumentException('$data: empty');
92                }
93                break;
94
95            case self::TYPE_VALUE:
96                if ($lifecycle != self::LIFECYCLE_SINGLETON) {
97                    throw new ArgumentException('$lifecycle: self::LIFECYCLE_SINGLETON');
98                }
99                $this->hasSingletonValue = true;
100                $this->singletonValue = $data;
101                break;
102
103            case self::TYPE_FACTORY:
104                if (!is_callable($data)) {
105                    throw new TypeError('$data: ' . TypeUtility::getType($data));
106                }
107                break;
108
109            default:
110                throw new NotSupportedException('$type: ' . $type);
111        }
112    }
113
114    #region function
115
116    /**
117     * シングルトンデータを保持しているか。
118     *
119     * @return bool
120     */
121    public function hasSingletonValue(): bool
122    {
123        if ($this->lifecycle != self::LIFECYCLE_SINGLETON) {
124            return false;
125        }
126
127        return $this->hasSingletonValue;
128    }
129
130
131    /**
132     * シングルトンデータを設定。
133     *
134     * DIコンテナ側で処理する想定。
135     *
136     * @param mixed $value
137     * @throws NotSupportedException
138     * @throws InvalidOperationException
139     */
140    public function setSingletonValue(mixed $value): void
141    {
142        if ($this->lifecycle != self::LIFECYCLE_SINGLETON) {
143            throw new NotSupportedException();
144        }
145        if ($this->hasSingletonValue) {
146            throw new InvalidOperationException();
147        }
148
149        if ($this->type === self::TYPE_TYPE) {
150            if (!is_a($value, (string)$this->data)) {
151                throw new TypeError(TypeUtility::getType($value) . ' - ' . $this->data);
152            }
153        }
154
155        $this->singletonValue = $value;
156        $this->hasSingletonValue = true;
157    }
158
159    /**
160     * シングルトンデータを設定。
161     *
162     * @return mixed
163     * @throws NotSupportedException
164     * @throws InvalidOperationException
165     */
166    public function getSingletonValue(): mixed
167    {
168        if ($this->lifecycle != self::LIFECYCLE_SINGLETON) {
169            throw new NotSupportedException();
170        }
171        if (!$this->hasSingletonValue) {
172            throw new InvalidOperationException();
173        }
174
175        return $this->singletonValue;
176    }
177
178    /**
179     * 型: クラスとして生成。
180     *
181     * @template T
182     * @param class-string $className
183     * @phpstan-param class-string<T> $className
184     * @param self::LIFECYCLE_* $lifecycle
185     * @return self
186     */
187    public static function class(string $className, int $lifecycle = self::LIFECYCLE_TRANSIENT): self
188    {
189        return new self($lifecycle, self::TYPE_TYPE, $className);
190    }
191
192    /**
193     * 値として生成。
194     *
195     * @template T
196     * @param mixed $data
197     * @phpstan-param T $data
198     * @return self
199     */
200    public static function value(mixed $data): self
201    {
202        return new self(self::LIFECYCLE_SINGLETON, self::TYPE_VALUE, $data);
203    }
204
205    /**
206     * 生成処理として生成。
207     *
208     * @template T
209     * @param callable $factory
210     * @phpstan-param callable(IDiContainer,DiItem[]):T $factory
211     * @param self::LIFECYCLE_* $lifecycle
212     * @return self
213     */
214    public static function factory(callable $factory, int $lifecycle = self::LIFECYCLE_TRANSIENT): self
215    {
216        return new self($lifecycle, self::TYPE_FACTORY, $factory);
217    }
218
219    #endregion
220
221    #region DisposerBase
222
223    protected function disposeImpl(): void
224    {
225        if ($this->nonDisposal) {
226            return;
227        }
228
229        /** @var IDisposable|null */
230        $disposer = null;
231
232        if ($this->type === DiItem::TYPE_VALUE) {
233            if ($this->data instanceof IDisposable) {
234                $disposer = $this->data;
235            }
236        }
237        if ($this->lifecycle === DiItem::LIFECYCLE_SINGLETON) {
238            if ($this->hasSingletonValue()) {
239                $value = $this->getSingletonValue();
240                if ($value instanceof IDisposable) {
241                    $disposer = $value;
242                }
243            }
244        }
245
246        if ($disposer !== null) {
247            $disposer->dispose();
248        }
249    }
250
251    #endregion
252}