Akismet APIを利用して EC-CUBE 公式プラグインの 商品レビュー管理プラグインのコメントスパム対策をする方法です。
Symfonyのドキュメントを参考に作っているので最初に Symfony ドキュメントの API でスパム対策をする をご確認ください。
以下、サンプルコードです。
Akismet に登録する
akismet.com の無料アカウントに登録して Akismet APIキーを取得してください。
Symfony HTTPClient コンポーネントをインストール
API の呼び出しをするために、Symfony の HTTPClient コンポーネントを使用します。
composer req http-client
スパムチェッカークラスを作成
app/Customize/Service 以下に、SpamChecker クラスを作成します。
<?php
namespace Customize\Service;
use Plugin\ProductReview4\Entity\ProductReview;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class SpamChecker
{
/**
* @var HttpClientInterface
*/
private $client;
/**
* @var string
*/
private $endpoint;
public function __construct(HttpClientInterface $client, string $akismetKey)
{
$this->client = $client;
$this->endpoint = sprintf('https://%s.rest.akismet.com/1.1/comment-check', $akismetKey);
}
/**
* @param ProductReview $review
* @param array $context
* @return int Spam score: 0: not spam, 1: maybe spam, 2: blatant spam
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
*/
public function getSpamScore(ProductReview $review, array $context): int
{
$response = $this->client->request('POST', $this->endpoint, [
'body' => array_merge($context, [
'blog' => 'https://a-zumi.net',
'comment_type' => 'comment',
'comment_author' => $review->getReviewerName(),
'comment_content' => $review->getComment(),
'blog_lang' => 'ja',
'blog_charset' => 'UTF-8',
'is_test' => true // trueにするとテストモード。本番環境では外す
]),
]);
$headers = $response->getHeaders();
if ('discard' === ($headers['x-akismet-pro-tip'][0] ?? '')) {
return 2;
}
$content = $response->getContent();
if (isset($headers['x-akismet-debug-help'][0])) {
throw new \RuntimeException(sprintf('Unable to check for spam: %s (%s).', $content, $headers['x-akismet-debug-help'][0]));
}
return 'true' === $content ? 1 : 0;
}
}
getSpamScore()
メソッドは API 呼び出しのレスポンスに応じて3つの値を返します。
2
: コメントが露骨なスパム1
: コメントがスパムの可能性がある0
: コメントがスパムではない
SpamChecker クラスを登録する
app/Customize/Resource/config 内に、以下の内容のservices.yamlを作成します。
services:
Customize\Service\SpamChecker:
arguments:
- '@http_client'
- "%env(AKISMET_KEY)%"
そして、.env にAkismet APIキーをセットしてください。
AKISMET_KEY=abcdefg
コメントがスパムかチェックする
ProductReviewTypeを拡張してコメントがスパムか判定する処理を追加します。
app/Customize/Form/Extension 以下に、ProductReviewTypeExtension を作成します。
<?php
namespace Customize\Form\Extension;
use Customize\Service\SpamChecker;
use Plugin\ProductReview4\Entity\ProductReview;
use Plugin\ProductReview4\Form\Type\ProductReviewType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\RequestStack;
class ProductReviewTypeExtension extends AbstractTypeExtension
{
/**
* @var SpamChecker
*/
private $spamChecker;
/**
* @var RequestStack
*/
private $requestStack;
public function __construct(SpamChecker $spamChecker, RequestStack $requestStack)
{
$this->spamChecker = $spamChecker;
$this->requestStack = $requestStack;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($request = $this->requestStack->getMasterRequest()) {
$builder
->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) use ($request) {
/** @var ProductReview $data */
$data = $event->getData();
$context = [
'user_ip' => $request->getClientIp(),
'user_agent' => $request->headers->get('user-agent'),
'referrer' => $request->headers->get('referer'),
'permalink' => $request->getUri()
];
if (2 === $this->spamChecker->getSpamScore($data, $context)) {
throw new \RuntimeException('Blatant spam, go away!');
}
});
}
}
public static function getExtendedTypes(): iterable
{
yield ProductReviewType::class;
}
}
以上で完成です。