Skip to content
This repository has been archived by the owner on Nov 20, 2018. It is now read-only.

PSR-6 (Cache) integration #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.php_cs
composer.lock
vendor
3 changes: 1 addition & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 TJ
Copyright (c) 2016 CMTT (https://cmtt.ru)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

8 changes: 5 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
}
],
"require": {
"php": ">=5.4.0",
"maknz/slack": "~1.7",
"php": ">=5.4.0",
"psr/cache": "~1.0",
"psr/log": "~1.0"
},
"suggest": {
"monolog/monolog": "Monolog sends your logs to files, sockets, inboxes, databases and various web services"
"monolog/monolog": "Monolog sends your logs to files, sockets, inboxes, databases and various web services",
"tedivm/stash": "Stash is a PHP caching library with a support of PSR-6, Memcached, Redis, APC and other cache drivers"
},
"autoload": {
"psr-4": {"TJ\\": "src/"}
}
}
}
9 changes: 5 additions & 4 deletions example.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@

// Uncomment if you want to use Memcached for antiflood protection
/*
$memcached = new Memcached();
$memcached->addServer('localhost', '11211');
$driver = new Stash\Driver\Memcache(['servers' => ['127.0.0.1', '11211']]);

$newrphus->setMemcached($memcached);
$pool = new Stash\Pool($driver);

$newrphus->setCache($pool);
*/

// You can add multiple additional fields to message (optional)
Expand All @@ -33,4 +34,4 @@
// And customize Slack message text (optional)
$newrphus->setMessageText("New misprint: {$_POST['misprintText']}");

$result = $newrphus->report($_POST['misprintText'], $_POST['misprintUrl']);
$result = $newrphus->report($_POST['misprintText'], $_POST['misprintUrl']);
164 changes: 114 additions & 50 deletions src/Newrphus.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use Exception;
use Maknz\Slack\Client as Slack;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerInterface;

/**
Expand All @@ -21,7 +22,7 @@ class Newrphus
* How many misprints will be accepted from one IP address
* per 10 minutes before it will be banned
*
* @var integer
* @var int
*/
public $attemptsThreshold = 10;

Expand Down Expand Up @@ -66,10 +67,19 @@ class Newrphus
/**
* Memcached instance
*
* @deprecated
*
* @var Memcached
*/
protected $memcached;

/**
* PSR-6 compatible cache pool
*
* @var CacheItemPoolInterface
*/
protected $cache;

/**
* Logger instance
*
Expand All @@ -80,8 +90,9 @@ class Newrphus
/**
* Slack options setter
*
* @param array $slackSettings e.g. [ 'endpoint' => 'https://hook.slack.com/...', 'channel' => '#misprints' ]
* @return TJ\Newrphus
* @param array $slackSettings e.g. [ 'endpoint' => 'https://hook.slack.com/...', 'channel' => '#misprints' ]
*
* @return Newrphus
*/
public function setSlackSettings($slackSettings)
{
Expand All @@ -93,8 +104,9 @@ public function setSlackSettings($slackSettings)
/**
* PSR-3 compatible logger setter
*
* @param LoggerInterface $logger
* @return TJ\Newrphus
* @param LoggerInterface $logger
*
* @return Newrphus
*/
public function setLogger(LoggerInterface $logger)
{
Expand All @@ -106,8 +118,11 @@ public function setLogger(LoggerInterface $logger)
/**
* Memcached setter
*
* @param Memcached $memcached
* @return TJ\Newrphus
* @deprecated
*
* @param Memcached $memcached
*
* @return Newrphus
*/
public function setMemcached($memcached)
{
Expand All @@ -117,29 +132,39 @@ public function setMemcached($memcached)
}

/**
* Add field to Slack message
* Cache setter
*
* @param CacheItemPoolInterface $cache
*
* @param string $title
* @param string $value
* @param boolean $short
* @return TJ\Newrphus
* @return Newrphus
*/
public function addField($title, $value, $short = false)
public function setCache($cache)
{
array_push($this->fields, [
'title' => $title,
'value' => $value,
'short' => (bool) $short
]);
$this->cache = $cache;

return $this;
}

/**
* Custom notification text setter
* Slack mesage text setter
*
* @param string $notificationText
* @return TJ\Newrphus
* @param string $messageText
*
* @return Newrphus
*/
public function setMessageText($messageText)
{
$this->messageText = $messageText;

return $this;
}

/**
* Custom Slack notification text setter
*
* @param string $notificationText
*
* @return Newrphus
*/
public function setNotificationText($notificationText)
{
Expand All @@ -149,24 +174,32 @@ public function setNotificationText($notificationText)
}

/**
* Slack mesage text setter
* Add custom field to Slack message
*
* @param string $messageText
* @return TJ\Newrphus
* @param string $title
* @param string $value
* @param bool $short
*
* @return Newrphus
*/
public function setMessageText($messageText)
public function addField($title, $value, $short = false)
{
$this->messageText = $messageText;
array_push($this->fields, [
'title' => $title,
'value' => $value,
'short' => (bool) $short
]);

return $this;
}

/**
* Report about misprint
*
* @param string misprintText
* @param string misprintUrl
* @return boolean
* @param string misprintText Used for antflood protection and as a fallback if message doesn't provided
* @param string misprintUrl URL where misprint was found
*
* @return bool
*/
public function report($misprintText, $misprintUrl = null)
{
Expand All @@ -186,45 +219,75 @@ public function report($misprintText, $misprintUrl = null)
/**
* Flood protection with Memcached
*
* @param string $misprintHash
* @param string $misprintHash
*
* @throws Exception if report is flood-positive
* @return boolean
*
* @return bool
*/
protected function floodProtect($misprintHash)
{
if (!$this->memcached) {
if (!$this->memcached && !$this->cache) {
return false;
}

$ip = $this->getIP();

if ($ip !== false) {
$mcIpHash = 'newrphus:byIP:' . md5($ip);
$attemptsCount = $this->memcached->get($mcIpHash);
if ($this->memcached->getResultCode() === 0) {
if ($attemptsCount > $this->attemptsThreshold) {
$ipHash = 'newrphus/byIP/' . md5($ip);
$textHash = 'newrphus/byText/' . $misprintHash;

if ($this->cache) {
$ipItem = $this->cache->getItem($ipHash);
$textItem = $this->cache->getItem($textHash);

$attemptsCount = $ipItem->get();
if ($attemptsCount !== null && (int) $attemptsCount > $this->attemptsThreshold) {
throw new Exception("Too many attempts", 429);
} else {
$ipItem->lock();

if ($ipItem->isMiss()) {
$ipItem->expiresAfter(300);
}

$this->cache->save($ipItem->set((int) $attemptsCount + 1));
}

if ($textItem->isHit()) {
throw new Exception("This misprint already was sent", 202);
} else {
$this->cache->save($textItem->expiresAfter(300)->set(true));
}
$this->memcached->increment($mcIpHash);
} else {
$this->memcached->set($mcIpHash, 1, 300);
}
}
$attemptsCount = $this->memcached->get($ipHash);

$mcTextHash = 'newrphus:byText:' . $misprintHash;
$this->memcached->get($mcTextHash);
if ($this->memcached->getResultCode() === 0) {
throw new Exception("This misprint already was sent", 202);
}
if ($this->memcached->getResultCode() === 0) {
if ($attemptsCount > $this->attemptsThreshold) {
throw new Exception("Too many attempts", 429);
}

$this->memcached->set($mcTextHash, true, 300);
$this->memcached->increment($ipHash);
} else {
$this->memcached->set($ipHash, 1, 300);
}

$this->memcached->get($textHash);
if ($this->memcached->getResultCode() === 0) {
throw new Exception("This misprint already was sent", 202);
}

$this->memcached->set($textHash, true, 300);
}
}

return true;
}

/**
* Get user IP address
*
* @return string|boolean
* @return string|bool
*/
protected function getIP()
{
Expand All @@ -242,9 +305,10 @@ protected function getIP()
/**
* Send misprint report to Slack
*
* @param string $misprintText
* @param string $misprintUrl
* @return boolean
* @param string $misprintText
* @param string $misprintUrl
*
* @return bool
*/
protected function sendToSlack($misprintText, $misprintUrl)
{
Expand Down