44
55namespace Boson \Component \Uri \Component ;
66
7+ use Boson \Component \Uri \Exception \InvalidHostArgumentException ;
8+ use Boson \Component \Uri \Exception \InvalidPasswordArgumentException ;
9+ use Boson \Component \Uri \Exception \InvalidPortArgumentException ;
10+ use Boson \Component \Uri \Exception \InvalidUserArgumentException ;
711use Boson \Contracts \Uri \Component \AuthorityInterface ;
812use Boson \Contracts \Uri \Component \UserInfoInterface ;
913
10- final class Authority implements AuthorityInterface
14+ /**
15+ * @phpstan-sealed MutableAuthority
16+ *
17+ * @phpstan-consistent-constructor
18+ */
19+ class Authority implements AuthorityInterface
1120{
1221 /**
1322 * Gets the user component of the URI.
1423 *
1524 * @var non-empty-string|null
1625 */
1726 public ?string $ user {
18- get => $ this ->userInfo ?->user ;
27+ get => $ this ->userInfo ?->username ;
1928 }
2029
2130 /**
@@ -27,34 +36,127 @@ final class Authority implements AuthorityInterface
2736 get => $ this ->userInfo ?->password;
2837 }
2938
39+ public protected(set) string $ host ;
40+
41+ public protected(set) ?int $ port ;
42+
43+ /**
44+ * Gets the userinfo URI component with a specific {@see UserInfo}
45+ * implementation.
46+ *
47+ * @var UserInfo|null
48+ */
49+ public protected(set) ?UserInfoInterface $ userInfo ;
50+
51+ /**
52+ * @param \Stringable|non-empty-string $host
53+ * @param int<0, max>|null $port
54+ *
55+ * @throws InvalidHostArgumentException if an invalid authority host is provided
56+ * @throws InvalidUserArgumentException if an invalid user info's username is provided
57+ * @throws InvalidPasswordArgumentException if an invalid user info's password is provided
58+ */
3059 public function __construct (
31- /**
32- * @var non-empty-string
33- */
34- public readonly string $ host ,
35- /**
36- * @var int<0, 65535>|null
37- */
38- public readonly ?int $ port = null ,
39- public readonly ?UserInfoInterface $ userInfo = null ,
40- ) {}
41-
42- public function equals (mixed $ other ): bool
60+ \Stringable |string $ host ,
61+ ?int $ port = null ,
62+ ?UserInfoInterface $ info = null ,
63+ ) {
64+ $ this ->host = $ this ->formatHost ($ host );
65+ $ this ->port = $ this ->formatPort ($ port );
66+ $ this ->userInfo = $ this ->formatUserInfo ($ info );
67+ }
68+
69+ /**
70+ * Returns an authority instance from another one
71+ *
72+ * @api
73+ *
74+ * @throws InvalidHostArgumentException if an invalid authority host is provided
75+ * @throws InvalidUserArgumentException if an invalid user info's username is provided
76+ * @throws InvalidPasswordArgumentException if an invalid user info's password is provided
77+ */
78+ final public static function from (AuthorityInterface $ authority ): static
79+ {
80+ return new static (
81+ host: $ authority ->host ,
82+ port: $ authority ->port ,
83+ info: $ authority ->userInfo ,
84+ );
85+ }
86+
87+ /**
88+ * Returns an authority instance from another one
89+ *
90+ * @api
91+ *
92+ * @throws InvalidHostArgumentException if an invalid authority host is provided
93+ * @throws InvalidUserArgumentException if an invalid user info's username is provided
94+ * @throws InvalidPasswordArgumentException if an invalid user info's password is provided
95+ */
96+ final public static function tryFrom (?AuthorityInterface $ authority ): ?static
97+ {
98+ if ($ authority === null ) {
99+ return null ;
100+ }
101+
102+ return static ::from ($ authority );
103+ }
104+
105+ /**
106+ * @return non-empty-string
107+ * @throws InvalidHostArgumentException if an invalid authority host is provided
108+ */
109+ protected function formatHost (\Stringable |string $ host ): string
110+ {
111+ if ($ host instanceof \Stringable) {
112+ try {
113+ $ host = (string ) $ host ;
114+ /** @phpstan-ignore-next-line : This is not a dead catch */
115+ } catch (\Throwable $ e ) {
116+ throw InvalidHostArgumentException::becauseStringableErrorOccurs ($ e );
117+ }
118+ }
119+
120+ if ($ host === '' ) {
121+ throw InvalidHostArgumentException::becauseComponentIsEmpty ();
122+ }
123+
124+ return $ host ;
125+ }
126+
127+ /**
128+ * @return int<0, max>|null
129+ */
130+ protected function formatPort (?int $ port ): ?int
131+ {
132+ return $ port ;
133+ }
134+
135+ /**
136+ * @throws InvalidUserArgumentException if an invalid user info's username is provided
137+ * @throws InvalidPasswordArgumentException if an invalid user info's password is provided
138+ */
139+ protected function formatUserInfo (?UserInfoInterface $ info ): ?UserInfo
140+ {
141+ return UserInfo::tryFrom ($ info );
142+ }
143+
144+ final public function equals (mixed $ other ): bool
43145 {
44146 return $ other === $ this
45- || ($ other instanceof self
147+ || ($ other instanceof AuthorityInterface
46148 && $ this ->host === $ other ->host
47149 && $ this ->port === $ other ->port
48150 && ($ other ->userInfo === $ this ->userInfo
49151 || $ other ->userInfo ?->equals($ this ->userInfo ) === true ));
50152 }
51153
52- public function toString (): string
154+ final public function toString (): string
53155 {
54156 return (string ) $ this ;
55157 }
56158
57- public function __toString (): string
159+ final public function __toString (): string
58160 {
59161 $ result = $ this ->host ;
60162
0 commit comments