1+ <?php
2+ /**
3+ *
4+ * This file is part of phpFastCache.
5+ *
6+ * @license MIT License (MIT)
7+ *
8+ * For full copyright and license information, please see the docs/CREDITS.txt file.
9+ *
10+ * @author Khoa Bui (khoaofgod) <[email protected] > http://www.phpfastcache.com 11+ * @author Georges.L (Geolim4) <[email protected] > 12+ *
13+ */
14+
15+ namespace phpFastCache \Drivers \Couchdb ;
16+
17+ use Doctrine \CouchDB \CouchDBClient as CouchdbClient ;
18+ use Doctrine \CouchDB \CouchDBException ;
19+ use phpFastCache \Core \Pool \DriverBaseTrait ;
20+ use phpFastCache \Core \Pool \ExtendedCacheItemPoolInterface ;
21+ use phpFastCache \Entities \driverStatistic ;
22+ use phpFastCache \Exceptions \phpFastCacheDriverCheckException ;
23+ use phpFastCache \Exceptions \phpFastCacheDriverException ;
24+ use phpFastCache \Exceptions \phpFastCacheInvalidArgumentException ;
25+ use Psr \Cache \CacheItemInterface ;
26+
27+ /**
28+ * Class Driver
29+ * @package phpFastCache\Drivers
30+ * @property CouchdbClient $instance Instance of driver service
31+ */
32+ class Driver implements ExtendedCacheItemPoolInterface
33+ {
34+ use DriverBaseTrait;
35+
36+ /**
37+ * Driver constructor.
38+ * @param array $config
39+ * @throws phpFastCacheDriverException
40+ */
41+ public function __construct (array $ config = [])
42+ {
43+ $ this ->setup ($ config );
44+
45+ if (!$ this ->driverCheck ()) {
46+ throw new phpFastCacheDriverCheckException (sprintf (self ::DRIVER_CHECK_FAILURE , $ this ->getDriverName ()));
47+ } else {
48+ $ this ->driverConnect ();
49+ }
50+ }
51+
52+ /**
53+ * @return bool
54+ */
55+ public function driverCheck ()
56+ {
57+ return class_exists ('Doctrine\CouchDB\CouchDBClient ' );
58+ }
59+
60+ /**
61+ * @param \Psr\Cache\CacheItemInterface $item
62+ * @return mixed
63+ * @throws phpFastCacheDriverException
64+ * @throws phpFastCacheInvalidArgumentException
65+ */
66+ protected function driverWrite (CacheItemInterface $ item )
67+ {
68+ /**
69+ * Check for Cross-Driver type confusion
70+ */
71+ if ($ item instanceof Item) {
72+ try {
73+ $ this ->instance ->putDocument (['data ' => $ this ->encode ($ this ->driverPreWrap ($ item ))], $ item ->getEncodedKey (), $ this ->getLatestDocumentRevision ($ item ->getEncodedKey ()));
74+ }catch (CouchDBException $ e ){
75+ throw new phpFastCacheDriverException ('Got error while trying to upsert a document: ' . $ e ->getMessage (), null , $ e );
76+ }
77+ return true ;
78+ } else {
79+ throw new phpFastCacheInvalidArgumentException ('Cross-Driver type confusion detected ' );
80+ }
81+ }
82+
83+ /**
84+ * @param \Psr\Cache\CacheItemInterface $item
85+ * @return mixed
86+ * @throws phpFastCacheDriverException
87+ */
88+ protected function driverRead (CacheItemInterface $ item )
89+ {
90+ try {
91+ $ response = $ this ->instance ->findDocument ($ item ->getEncodedKey ());
92+ }catch (CouchDBException $ e ){
93+ throw new phpFastCacheDriverException ('Got error while trying to get a document: ' . $ e ->getMessage (), null , $ e );
94+ }
95+
96+ if ($ response ->status === 404 || empty ($ response ->body ['data ' ])){
97+ return null ;
98+ }else if ($ response ->status === 200 ){
99+ return $ this ->decode ($ response ->body ['data ' ]);
100+ }else {
101+ throw new phpFastCacheDriverException ('Got unexpected HTTP status: ' . $ response ->status );
102+ }
103+ }
104+
105+ /**
106+ * @param \Psr\Cache\CacheItemInterface $item
107+ * @return bool
108+ * @throws phpFastCacheDriverException
109+ * @throws phpFastCacheInvalidArgumentException
110+ */
111+ protected function driverDelete (CacheItemInterface $ item )
112+ {
113+ /**
114+ * Check for Cross-Driver type confusion
115+ */
116+ if ($ item instanceof Item) {
117+ try {
118+ $ this ->instance ->deleteDocument ($ item ->getEncodedKey (), $ this ->getLatestDocumentRevision ($ item ->getEncodedKey ()));
119+ }catch (CouchDBException $ e ){
120+ throw new phpFastCacheDriverException ('Got error while trying to delete a document: ' . $ e ->getMessage (), null , $ e );
121+ }
122+ return true ;
123+ } else {
124+ throw new phpFastCacheInvalidArgumentException ('Cross-Driver type confusion detected ' );
125+ }
126+ }
127+
128+ /**
129+ * @return bool
130+ * @throws phpFastCacheDriverException
131+ */
132+ protected function driverClear ()
133+ {
134+ try {
135+ $ this ->instance ->deleteDatabase ($ this ->getDatabaseName ());
136+ $ this ->createDatabase ();
137+ }catch (CouchDBException $ e ){
138+ throw new phpFastCacheDriverException ('Got error while trying to delete and recreate the database: ' . $ e ->getMessage (), null , $ e );
139+ }
140+
141+
142+ return true ;
143+ }
144+
145+ /**
146+ * @return bool
147+ */
148+ protected function driverConnect ()
149+ {
150+ if ($ this ->instance instanceof CouchdbClient) {
151+ throw new \LogicException ('Already connected to Couchdb server ' );
152+ } else {
153+ $ host = isset ($ this ->config [ 'host ' ]) ? $ this ->config [ 'host ' ] : '127.0.0.1 ' ;
154+ $ ssl = isset ($ this ->config [ 'ssl ' ]) ? $ this ->config [ 'ssl ' ] : false ;
155+ $ port = isset ($ this ->config [ 'port ' ]) ? $ this ->config [ 'port ' ] : 5984 ;
156+ $ path = isset ($ this ->config [ 'path ' ]) ? $ this ->config [ 'path ' ] : '/ ' ;
157+ $ username = isset ($ this ->config [ 'username ' ]) ? $ this ->config [ 'username ' ] : '' ;
158+ $ password = isset ($ this ->config [ 'password ' ]) ? $ this ->config [ 'password ' ] : '' ;
159+ $ timeout = isset ($ this ->config [ 'timeout ' ]) ? $ this ->config [ 'timeout ' ] : 10 ;
160+
161+ $ url = ($ ssl ? 'https:// ' : 'http:// ' );
162+ if ($ username )
163+ {
164+ $ url .= "{$ username }" ;
165+ if ($ password )
166+ {
167+ $ url .= ": {$ password }" ;
168+ }
169+ $ url .= '@ ' ;
170+ }
171+ $ url .= $ host ;
172+ $ url .= ": {$ port }" ;
173+ $ url .= $ path ;
174+
175+ $ this ->instance = CouchDBClient::create ([
176+ 'dbname ' => $ this ->getDatabaseName (),
177+ 'url ' => $ url ,
178+ 'timeout ' => $ timeout
179+ ]);
180+
181+ $ this ->createDatabase ();
182+ }
183+
184+ return true ;
185+ }
186+
187+ /**
188+ * @return string|null
189+ */
190+ protected function getLatestDocumentRevision ($ docId )
191+ {
192+ $ path = '/ ' . $ this ->getDatabaseName () . '/ ' . urlencode ($ docId );
193+
194+ $ response = $ this ->instance ->getHttpClient ()->request (
195+ 'GET ' ,// At this moment HEAD requests are not working: https://github.com/doctrine/couchdb-client/issues/72
196+ $ path ,
197+ null ,
198+ false
199+ );
200+ if (!empty ($ response ->headers ['etag ' ])){
201+ return trim ($ response ->headers ['etag ' ], " ' \"\t\n\r\0\x0B" );
202+ }else {
203+ return null ;
204+ }
205+ }
206+
207+ /**
208+ * @return string
209+ */
210+ protected function getDatabaseName ()
211+ {
212+ return isset ($ this ->config [ 'database ' ]) ? $ this ->config [ 'database ' ] : 'phpfastcache ' ;
213+ }
214+
215+ /**
216+ * @return void
217+ */
218+ protected function createDatabase ()
219+ {
220+ if (!in_array ($ this ->instance ->getDatabase (), $ this ->instance ->getAllDatabases (), true )){
221+ $ this ->instance ->createDatabase ($ this ->instance ->getDatabase ());
222+ }
223+ }
224+
225+ /********************
226+ *
227+ * PSR-6 Extended Methods
228+ *
229+ *******************/
230+
231+ /**
232+ * @return driverStatistic
233+ */
234+ public function getStats ()
235+ {
236+ $ info = $ this ->instance ->getDatabaseInfo ();
237+
238+ return (new driverStatistic ())
239+ ->setSize ($ info ['sizes ' ]['active ' ])
240+ ->setRawData ($ info )
241+ ->setData (implode (', ' , array_keys ($ this ->itemInstances )))
242+ ->setInfo ('Couchdb version ' . $ this ->instance ->getVersion () . "\n For more information see RawData. " );
243+ }
244+ }
0 commit comments