1414namespace Guanguans \Notify \ZohoCliq ;
1515
1616use Guanguans \Notify \Foundation \Authenticators \NullAuthenticator ;
17+ use Guanguans \Notify \Foundation \Caches \FileCache ;
1718use Guanguans \Notify \Foundation \Client ;
1819use Guanguans \Notify \Foundation \Exceptions \RequestException ;
1920use Guanguans \Notify \ZohoCliq \Messages \AccessTokenMessage ;
2021use GuzzleHttp \Middleware ;
2122use Psr \Http \Message \RequestInterface ;
2223use Psr \Http \Message \ResponseInterface ;
24+ use Psr \SimpleCache \CacheInterface ;
2325
2426/**
27+ * @see https://github.com/w7corp/easywechat/blob/6.x/src/OfficialAccount/AccessToken.php
2528 * @see https://github.com/w7corp/easywechat/blob/6.x/src/OpenPlatform/ComponentAccessToken.php
29+ * @see https://github.com/w7corp/easywechat/blob/6.x/src/OpenWork/AuthorizerAccessToken.php
30+ * @see https://github.com/w7corp/easywechat/blob/6.x/src/Work/AccessToken.php
2631 *
2732 * ```
2833 * curl --location 'https://accounts.zoho.com/oauth/v2/token' \
4146 * --header 'Authorization: Zoho-oauthtoken 1000.80e9143983dcc190427cad7e8f029e25.cdc1c1043e5e7f0555fc14fc7faf3' \
4247 * ```
4348 */
44- class Authenticator extends NullAuthenticator
49+ class Authenticator extends NullAuthenticator implements \Stringable
4550{
46- private static ?string $ accessToken ;
51+ private CacheInterface $ cache ;
52+ private string $ cacheKey ;
4753 private Client $ client ;
4854
4955 public function __construct (
5056 private string $ clientId ,
5157 #[\SensitiveParameter]
5258 private string $ clientSecret ,
59+ ?CacheInterface $ cache = null ,
60+ ?string $ cacheKey = null ,
5361 ?Client $ client = null ,
5462 ) {
63+ $ this ->cache = $ cache ?? new FileCache ;
64+ $ this ->cacheKey = $ cacheKey ?? "zoho_cliq.access_token. $ clientId. $ clientSecret " ;
5565 $ this ->client = $ client ?? new Client ;
5666 }
5767
68+ /**
69+ * @throws \GuzzleHttp\Exception\GuzzleException
70+ * @throws \JsonException
71+ * @throws \Psr\SimpleCache\InvalidArgumentException
72+ * @throws \ReflectionException
73+ */
74+ public function __toString (): string
75+ {
76+ return $ this ->getToken ();
77+ }
78+
5879 public function applyToMiddleware (callable $ handler ): callable
5980 {
6081 return array_reduce (
@@ -68,11 +89,6 @@ public function applyToMiddleware(callable $handler): callable
6889 );
6990 }
7091
71- public static function flushAccessToken (): void
72- {
73- self ::$ accessToken = null ;
74- }
75-
7692 /**
7793 * @todo
7894 */
@@ -87,7 +103,7 @@ private function authenticate(callable $handler): callable
87103 fn (RequestInterface $ request ): RequestInterface => $ request ->withHeader (
88104 'Authorization ' ,
89105 // "Bearer xxx",
90- "Bearer {$ this ->getAccessToken ()}" ,
106+ "Bearer {$ this ->getToken ()}" ,
91107 ),
92108 )($ handler );
93109 }
@@ -100,8 +116,8 @@ function (int $retries, RequestInterface &$request, ?ResponseInterface $response
100116 return false ;
101117 }
102118
103- if ($ response ?->getStatusCode() === 401 ) {
104- $ request = $ request ->withHeader ('Authorization ' , "Bearer {$ this ->refreshAccessToken ()}" );
119+ if (401 === $ response ?->getStatusCode()) {
120+ $ request = $ request ->withHeader ('Authorization ' , "Bearer {$ this ->refreshToken ()}" );
105121
106122 return true ;
107123 }
@@ -111,39 +127,50 @@ function (int $retries, RequestInterface &$request, ?ResponseInterface $response
111127 )($ handler );
112128 }
113129
114- private function refreshAccessToken (): string
115- {
116- self ::flushAccessToken ();
117-
118- return $ this ->getAccessToken ();
119- }
120-
121130 /**
122- * Temporary memory cache.
123- *
124- * @todo psr cache
131+ * @throws \GuzzleHttp\Exception\GuzzleException
132+ * @throws \JsonException
133+ * @throws \Psr\SimpleCache\InvalidArgumentException
134+ * @throws \ReflectionException
125135 */
126- private function getAccessToken (): string
136+ private function getToken (): string
127137 {
128- return self ::$ accessToken ??= $ this ->fetchAccessToken ();
138+ if (($ token = $ this ->cache ->get ($ this ->cacheKey )) && \is_string ($ token )) {
139+ return $ token ;
140+ }
141+
142+ return $ this ->refreshToken ();
129143 }
130144
131145 /**
132146 * @throws \GuzzleHttp\Exception\GuzzleException
133147 * @throws \JsonException
148+ * @throws \Psr\SimpleCache\InvalidArgumentException
134149 * @throws \ReflectionException
150+ *
151+ * ```json
152+ * {
153+ * "access_token": "1000.86e0701b6f279bfad7b6a05352dc304d.3106ea5d20401799c010212da3da1",
154+ * "scope": "ZohoCliq.Webhooks.CREATE",
155+ * "api_domain": "https://www.zohoapis.com",
156+ * "token_type": "Bearer",
157+ * "expires_in": 3600
158+ * }
159+ * ```
135160 */
136- private function fetchAccessToken (): string
161+ private function refreshToken (): string
137162 {
138163 $ response = $ this ->client ->send (AccessTokenMessage::make ([
139164 'client_id ' => $ this ->clientId ,
140165 'client_secret ' => $ this ->clientSecret ,
141166 ]));
142167
143- if (!$ accessToken = $ response ->json ('access_token ' )) {
168+ if (!$ token = $ response ->json ('access_token ' )) {
144169 throw RequestException::create ($ response ->request (), $ response );
145170 }
146171
147- return $ accessToken ;
172+ $ this ->cache ->set ($ this ->cacheKey , $ token , abs ((int ) $ response ->json ('expires_in ' ) - 100 ));
173+
174+ return $ token ;
148175 }
149176}
0 commit comments