88use  Psr \Log \LoggerInterface ;
99use  Symfony \Component \Cache \Adapter \AbstractAdapter ;
1010use  Symfony \Component \Cache \Adapter \FilesystemAdapter ;
11+ use  Symfony \Component \Cache \Adapter \MemcachedAdapter ;
1112use  Symfony \Component \Cache \PruneableInterface ;
1213
1314/** 
@@ -243,7 +244,7 @@ private function saveRemediations(array $decisions): bool
243244            }
244245        }
245246
246-         return  $ this  ->adapter -> commit ();
247+         return  $ this  ->commit ();
247248    }
248249
249250    private  function  removeRemediations (array  $ decisions ): bool 
@@ -268,7 +269,7 @@ private function removeRemediations(array $decisions): bool
268269            }
269270        }
270271
271-         return  $ this  ->adapter -> commit ();
272+         return  $ this  ->commit ();
272273    }
273274
274275    /** 
@@ -291,17 +292,22 @@ private function saveRemediationsForIp(array $decisions, string $ip): string
291292            $ remediation  = $ this  ->formatRemediationFromDecision (null );
292293            $ remediationResult  = $ this  ->addRemediationToCacheItem ($ ip , $ remediation [0 ], $ remediation [1 ], $ remediation [2 ]);
293294        }
294-         $ this  ->adapter -> commit ();
295+         $ this  ->commit ();
295296
296297        return  $ remediationResult ;
297298    }
298299
299300    public  function  clear (): bool 
300301    {
301-         $ cleared  = $ this  ->adapter ->clear ();
302+         $ this  ->setCustomErrorHandler ();
303+         try  {
304+             $ cleared  = $ this  ->adapter ->clear ();
305+         } finally  {
306+             $ this  ->unsetCustomErrorHandler ();
307+         }
302308        $ this  ->warmedUp  = false ;
303309        $ this  ->defferUpdateCacheConfig (['warmed_up '  => $ this  ->warmedUp ]);
304-         $ this  ->adapter -> commit ();
310+         $ this  ->commit ();
305311        $ this  ->logger ->info ('' , ['type '  => 'CACHE_CLEARED ' ]);
306312
307313        return  $ cleared ;
@@ -328,7 +334,7 @@ public function warmUp(): int
328334        if  ($ newDecisions ) {
329335            $ this  ->warmedUp  = $ this  ->saveRemediations ($ newDecisions );
330336            $ this  ->defferUpdateCacheConfig (['warmed_up '  => $ this  ->warmedUp ]);
331-             $ this  ->adapter -> commit ();
337+             $ this  ->commit ();
332338            if  (!$ this  ->warmedUp ) {
333339                throw  new  BouncerException ('Unable to warm the cache up ' );
334340            }
@@ -338,7 +344,7 @@ public function warmUp(): int
338344        // Store the fact that the cache has been warmed up. 
339345        $ this  ->defferUpdateCacheConfig (['warmed_up '  => true ]);
340346
341-         $ this  ->adapter -> commit ();
347+         $ this  ->commit ();
342348        $ this  ->logger ->info ('' , ['type '  => 'CACHE_WARMED_UP ' , 'added_decisions '  => $ nbNew ]);
343349
344350        return  $ nbNew ;
@@ -447,6 +453,9 @@ public function get(string $ip): string
447453        return  $ remediation ;
448454    }
449455
456+     /** 
457+      * Prune the cache (only when using PHP File System cache). 
458+      */ 
450459    public  function  prune (): bool 
451460    {
452461        if  ($ this  ->adapter  instanceof  PruneableInterface) {
@@ -458,4 +467,59 @@ public function prune(): bool
458467
459468        throw  new  BouncerException ('Cache Adapter ' .\get_class ($ this  ->adapter ).' is not prunable. ' );
460469    }
470+ 
471+     /** 
472+      * When Memcached connection fail, it throw an unhandled warning. 
473+      * To catch this warning as a clean execption we have to temporarily change the error handler. 
474+      */ 
475+     private  function  setCustomErrorHandler (): void 
476+     {
477+         if  ($ this  ->adapter  instanceof  MemcachedAdapter) {
478+             set_error_handler (function  () {
479+                 throw  new  BouncerException ('Error when connecting to Memcached. Please fix the Memcached DSN or select another cache technology. ' );
480+             });
481+         }
482+     }
483+ 
484+     /** 
485+      * When the selected cache adapter is MemcachedAdapter, revert to the previous error handler. 
486+      * */ 
487+     private  function  unsetCustomErrorHandler (): void 
488+     {
489+         if  ($ this  ->adapter  instanceof  MemcachedAdapter) {
490+             restore_error_handler ();
491+         }
492+     }
493+ 
494+     /** 
495+      * Wrap the cacheAdapter to catch warnings. 
496+      * 
497+      * @throws BouncerException if the connection was not successful 
498+      * */ 
499+     private  function  commit (): bool 
500+     {
501+         $ this  ->setCustomErrorHandler ();
502+         try  {
503+             $ result  = $ this  ->adapter ->commit ();
504+         } finally  {
505+             $ this  ->unsetCustomErrorHandler ();
506+         }
507+ 
508+         return  $ result ;
509+     }
510+ 
511+     /** 
512+      * Test the connection to the cache system (Redis or Memcached). 
513+      * 
514+      * @throws BouncerException if the connection was not successful 
515+      * */ 
516+     public  function  testConnection (): void 
517+     {
518+         $ this  ->setCustomErrorHandler ();
519+         try  {
520+             $ this  ->adapter ->getItem ('  ' );
521+         } finally  {
522+             $ this  ->unsetCustomErrorHandler ();
523+         }
524+     }
461525}
0 commit comments