<?php
/**
* Created by PhpStorm.
* User: bumz
* Date: 7/8/14
* Time: 2:49 PM
*/
namespace Sindrive\IpsetBundle\Listener;
use Sindrive\IpsetBundle\Event\BanEvent;
use Sindrive\IpsetBundle\Service\Ban;
use Sindrive\MemcachedBundle\Services\SindriveMemcached;
class BanListener
{
/**
* @var string
*/
private $banKey;
/**
* @var \Sindrive\IpsetBundle\Service\Ban
*/
private $banService;
/**
* @var \Memcached|SindriveMemcached
*/
private $memcached;
/**
* @param SindriveMemcached $memcached
* @param \Sindrive\IpsetBundle\Service\Ban $banService
* @param string $banKey
*/
public function __construct(SindriveMemcached $memcached, Ban $banService, string $banKey = 'sindrive-ips-ban-listener')
{
$this->memcached = $memcached;
$this->banService = $banService;
$this->banKey = $banKey;
}
public function banByCount(BanEvent $event): void
{
$banKey = sprintf('%s-%s-%s', $this->banKey, 'count', $event->getIp());
$result = $this->memcached->increment($banKey);
if (false === $result && $this->memcached->getResultCode() == \Memcached::RES_NOTFOUND) {
$this->memcached->add($banKey, 1, time() + 30 * 24 * 60 * 60);
$result = 1;
}
if ($result > $event->getCount()) {
$this->banService->add($event->getIp());
}
}
public function banByTimeSpanCount(BanEvent $event): bool
{
$banKey = sprintf('%s-%s-%s', $this->banKey, 'count-time', $event->getIp());
do {
$resultStore = $this->memcached->getCas($banKey);
$result = $resultStore['value'];
$cas = $resultStore['cas'];
$currentTime = time();
$time = $currentTime + $event->getTimeOffset();
if ($this->memcached->getResultCode() == \Memcached::RES_NOTFOUND) {
$result = array($event->getTimeOffset() => array($time));
$this->memcached->add($banKey, $result, $time);
} elseif ($this->memcached->getResultCode() == \Memcached::RES_SUCCESS) {
if (isset($result[$event->getTimeOffset()])) {
$result[$event->getTimeOffset()] = array_filter($result[$event->getTimeOffset()], function($item) use ($currentTime) {
return $item > $currentTime;
});
// need a clean indexed array
$result[$event->getTimeOffset()] = array_values($result[$event->getTimeOffset()]);
}
$result[$event->getTimeOffset()][] = $time;
$this->memcached->cas($cas, $banKey, $result, $time);
} else {
return false;
}
} while ($this->memcached->getResultCode() != \Memcached::RES_SUCCESS);
if (count($result[$event->getTimeOffset()]) > $event->getCount()) {
$this->banService->add($event->getIp());
}
return true;
}
public function banImmediately(BanEvent $event): void
{
$this->banService->add($event->getIp());
}
}