Remove usage old model from Throttling
[MAILPOET-3032]
This commit is contained in:
@ -4,6 +4,7 @@ namespace MailPoet\Subscribers;
|
||||
|
||||
use MailPoet\Doctrine\Repository;
|
||||
use MailPoet\Entities\SubscriberIPEntity;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* @extends Repository<SubscriberIPEntity>
|
||||
@ -12,4 +13,39 @@ class SubscriberIPsRepository extends Repository {
|
||||
protected function getEntityClassName() {
|
||||
return SubscriberIPEntity::class;
|
||||
}
|
||||
|
||||
public function findOneByIPAndCreatedAtAfterTimeInSeconds(string $ip, int $seconds): ?SubscriberIPEntity {
|
||||
return $this->entityManager->createQueryBuilder()
|
||||
->select('sip')
|
||||
->from(SubscriberIPEntity::class, 'sip')
|
||||
->where('sip.ip = :ip')
|
||||
->andWhere('sip.createdAt >= :timeThreshold')
|
||||
->setParameter('ip', $ip)
|
||||
->setParameter('timeThreshold', (new Carbon())->subSeconds($seconds))
|
||||
->setMaxResults(1)
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
}
|
||||
|
||||
public function getCountByIPAndCreatedAtAfterTimeInSeconds(string $ip, int $seconds): int {
|
||||
return $this->entityManager->createQueryBuilder()
|
||||
->select('COUNT(sip)')
|
||||
->from(SubscriberIPEntity::class, 'sip')
|
||||
->where('sip.ip = :ip')
|
||||
->andWhere('sip.createdAt >= :timeThreshold')
|
||||
->setParameter('ip', $ip)
|
||||
->setParameter('timeThreshold', (new Carbon())->subSeconds($seconds))
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function deleteCreatedAtBeforeTimeInSeconds(int $seconds): int {
|
||||
return (int)$this->entityManager->createQueryBuilder()
|
||||
->delete()
|
||||
->from(SubscriberIPEntity::class, 'sip')
|
||||
->where('sip.createdAt < :timeThreshold')
|
||||
->setParameter('timeThreshold', (new Carbon())->subSeconds($seconds))
|
||||
->getQuery()
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
@ -2,42 +2,37 @@
|
||||
|
||||
namespace MailPoet\Subscription;
|
||||
|
||||
use MailPoet\Models\SubscriberIP;
|
||||
use MailPoet\Entities\SubscriberIPEntity;
|
||||
use MailPoet\Subscribers\SubscriberIPsRepository;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Throttling {
|
||||
/** @var SubscriberIPsRepository */
|
||||
private $subscriberIPsRepository;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(WPFunctions $wp) {
|
||||
public function __construct(SubscriberIPsRepository $subscriberIPsRepository, WPFunctions $wp) {
|
||||
$this->wp = $wp;
|
||||
$this->subscriberIPsRepository = $subscriberIPsRepository;
|
||||
}
|
||||
|
||||
public function throttle() {
|
||||
$subscriptionLimitEnabled = $this->wp->applyFilters('mailpoet_subscription_limit_enabled', true);
|
||||
|
||||
$subscriptionLimitWindow = $this->wp->applyFilters('mailpoet_subscription_limit_window', DAY_IN_SECONDS);
|
||||
$subscriptionLimitBase = $this->wp->applyFilters('mailpoet_subscription_limit_base', MINUTE_IN_SECONDS);
|
||||
$subscriptionLimitWindow = (int)$this->wp->applyFilters('mailpoet_subscription_limit_window', DAY_IN_SECONDS);
|
||||
$subscriptionLimitBase = (int)$this->wp->applyFilters('mailpoet_subscription_limit_base', MINUTE_IN_SECONDS);
|
||||
|
||||
$subscriberIp = Helpers::getIP();
|
||||
|
||||
if ($subscriptionLimitEnabled && !$this->wp->isUserLoggedIn()) {
|
||||
if (!empty($subscriberIp)) {
|
||||
$subscriptionCount = SubscriberIP::where('ip', $subscriberIp)
|
||||
->whereRaw(
|
||||
'(`created_at` >= NOW() - INTERVAL ? SECOND)',
|
||||
[(int)$subscriptionLimitWindow]
|
||||
)->count();
|
||||
|
||||
$subscriptionCount = $this->subscriberIPsRepository->getCountByIPAndCreatedAtAfterTimeInSeconds($subscriberIp, $subscriptionLimitWindow);
|
||||
if ($subscriptionCount > 0) {
|
||||
$timeout = $subscriptionLimitBase * pow(2, $subscriptionCount - 1);
|
||||
$existingUser = SubscriberIP::where('ip', $subscriberIp)
|
||||
->whereRaw(
|
||||
'(`created_at` >= NOW() - INTERVAL ? SECOND)',
|
||||
[(int)$timeout]
|
||||
)->findOne();
|
||||
|
||||
$existingUser = $this->subscriberIPsRepository->findOneByIPAndCreatedAtAfterTimeInSeconds($subscriberIp, $timeout);
|
||||
if (!empty($existingUser)) {
|
||||
return $timeout;
|
||||
}
|
||||
@ -45,21 +40,23 @@ class Throttling {
|
||||
}
|
||||
}
|
||||
|
||||
$ip = SubscriberIP::create();
|
||||
$ip->ip = $subscriberIp;
|
||||
$ip->save();
|
||||
if ($subscriberIp !== null) {
|
||||
$ip = new SubscriberIPEntity($subscriberIp);
|
||||
$existingIp = $this->subscriberIPsRepository->findOneBy(['ip' => $ip->getIP(), 'createdAt' => $ip->getCreatedAt()]);
|
||||
if (!$existingIp) {
|
||||
$this->subscriberIPsRepository->persist($ip);
|
||||
$this->subscriberIPsRepository->flush();
|
||||
}
|
||||
}
|
||||
|
||||
$this->purge();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function purge() {
|
||||
public function purge(): void {
|
||||
$interval = $this->wp->applyFilters('mailpoet_subscription_purge_window', MONTH_IN_SECONDS);
|
||||
return SubscriberIP::whereRaw(
|
||||
'(`created_at` < NOW() - INTERVAL ? SECOND)',
|
||||
[$interval]
|
||||
)->deleteMany();
|
||||
$this->subscriberIPsRepository->deleteCreatedAtBeforeTimeInSeconds($interval);
|
||||
}
|
||||
|
||||
public function secondsToTimeString($seconds): string {
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
namespace MailPoet\Subscription;
|
||||
|
||||
use MailPoet\Models\SubscriberIP;
|
||||
use MailPoet\Entities\SubscriberIPEntity;
|
||||
use MailPoet\Subscribers\SubscriberIPsRepository;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
|
||||
@ -10,9 +11,13 @@ class ThrottlingTest extends \MailPoetTest {
|
||||
/** @var Throttling */
|
||||
private $throttling;
|
||||
|
||||
/** @var SubscriberIPsRepository */
|
||||
private $subscriberIPsRepository;
|
||||
|
||||
protected function _before() {
|
||||
parent::_before();
|
||||
$this->throttling = $this->diContainer->get(Throttling::class);
|
||||
$this->subscriberIPsRepository = $this->diContainer->get(SubscriberIPsRepository::class);
|
||||
}
|
||||
|
||||
public function testItProgressivelyThrottlesSubscriptions() {
|
||||
@ -20,10 +25,7 @@ class ThrottlingTest extends \MailPoetTest {
|
||||
expect($this->throttling->throttle())->equals(false);
|
||||
expect($this->throttling->throttle())->equals(60);
|
||||
for ($i = 1; $i <= 10; $i++) {
|
||||
$ip = SubscriberIP::create();
|
||||
$ip->ip = '127.0.0.1';
|
||||
$ip->createdAt = Carbon::now()->subMinutes($i);
|
||||
$ip->save();
|
||||
$this->createSubscriberIP('127.0.0.1', Carbon::now()->subMinutes($i));
|
||||
}
|
||||
expect($this->throttling->throttle())->equals(MINUTE_IN_SECONDS * pow(2, 10));
|
||||
}
|
||||
@ -49,18 +51,12 @@ class ThrottlingTest extends \MailPoetTest {
|
||||
}
|
||||
|
||||
public function testItPurgesOldSubscriberIps() {
|
||||
$ip = SubscriberIP::create();
|
||||
$ip->ip = '127.0.0.1';
|
||||
$ip->save();
|
||||
$this->createSubscriberIP('127.0.0.1', Carbon::now());
|
||||
$this->createSubscriberIP('127.0.0.1', Carbon::now()->subDays(30)->subSeconds(1));
|
||||
|
||||
$ip2 = SubscriberIP::create();
|
||||
$ip2->ip = '127.0.0.1';
|
||||
$ip2->createdAt = Carbon::now()->subDays(30)->subSeconds(1);
|
||||
$ip2->save();
|
||||
|
||||
expect(SubscriberIP::count())->equals(2);
|
||||
expect($this->subscriberIPsRepository->countBy([]))->equals(2);
|
||||
$this->throttling->throttle();
|
||||
expect(SubscriberIP::count())->equals(1);
|
||||
expect($this->subscriberIPsRepository->countBy([]))->equals(1);
|
||||
}
|
||||
|
||||
public function testItConvertsSecondsToTimeString() {
|
||||
@ -73,7 +69,15 @@ class ThrottlingTest extends \MailPoetTest {
|
||||
expect($this->throttling->secondsToTimeString(59))->equals('59 seconds');
|
||||
}
|
||||
|
||||
private function createSubscriberIP(string $ip, Carbon $createdAt): SubscriberIPEntity {
|
||||
$subscriberIP = new SubscriberIPEntity($ip);
|
||||
$subscriberIP->setCreatedAt($createdAt);
|
||||
$this->entityManager->persist($subscriberIP);
|
||||
$this->entityManager->flush();
|
||||
return $subscriberIP;
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
SubscriberIP::deleteMany();
|
||||
$this->truncateEntity(SubscriberIPEntity::class);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user