Browse Source

Code formatting

main
Cédric Belin 6 months ago
parent
commit
2496fa05ad
36 changed files with 1128 additions and 1121 deletions
  1. +6
    -3
      .editorconfig
  2. +1
    -1
      .github/workflows/build.yaml
  3. +5
    -5
      .vscode/settings.json
  4. +51
    -51
      composer.json
  5. +6
    -6
      doc/advanced/events.md
  6. +18
    -18
      doc/advanced/testing.md
  7. +14
    -14
      doc/features/comment_check.md
  8. +5
    -5
      doc/features/key_verification.md
  9. +14
    -14
      doc/features/submit_ham.md
  10. +14
    -14
      doc/features/submit_spam.md
  11. +3
    -3
      doc/index.md
  12. +12
    -12
      doc/installation.md
  13. +9
    -5
      etc/mkdocs.yaml
  14. +22
    -22
      etc/phpdoc.xml
  15. +2
    -2
      etc/phpstan.neon
  16. +13
    -13
      etc/phpunit.xml
  17. +35
    -35
      example/main.php
  18. +149
    -149
      src/Author.php
  19. +74
    -74
      src/Blog.php
  20. +6
    -6
      src/CheckResult.php
  21. +189
    -189
      src/Client.php
  22. +19
    -19
      src/ClientException.php
  23. +190
    -190
      src/Comment.php
  24. +14
    -14
      src/CommentType.php
  25. +16
    -16
      src/RequestEvent.php
  26. +18
    -18
      src/ResponseEvent.php
  27. +1
    -1
      src/version.g.php
  28. +39
    -39
      test/AuthorTest.php
  29. +38
    -38
      test/BlogTest.php
  30. +74
    -74
      test/ClientTest.php
  31. +53
    -53
      test/CommentTest.php
  32. +3
    -3
      tool/build.ps1
  33. +3
    -3
      tool/clean.ps1
  34. +1
    -1
      tool/doc.ps1
  35. +1
    -1
      tool/upgrade.ps1
  36. +10
    -10
      tool/watch.ps1

+ 6
- 3
.editorconfig View File

@@ -3,11 +3,14 @@ root = true

[*]
charset = utf-8
indent_size = 2
indent_style = space
indent_style = tab
insert_final_newline = true
tab_width = 2
trim_trailing_whitespace = true

[*.md]
indent_size = 4
trim_trailing_whitespace = false

[*.{yaml,yml}]
indent_size = 2
indent_style = space

+ 1
- 1
.github/workflows/build.yaml View File

@@ -3,7 +3,7 @@ on:
pull_request:
push:
schedule:
- cron: '0 0 1 * *'
- cron: "0 0 1 * *"
jobs:
test:
runs-on: ubuntu-latest


+ 5
- 5
.vscode/settings.json View File

@@ -1,7 +1,7 @@
{
"editor.insertSpaces": true,
"editor.tabSize": 2,
"files.encoding": "utf8",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true
"editor.insertSpaces": false,
"editor.tabSize": 2,
"files.encoding": "utf8",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true
}

+ 51
- 51
composer.json View File

@@ -1,53 +1,53 @@
{
"description": "Prevent comment spam using the Akismet service. Add Akismet to your applications so you don't have to worry about spam again.",
"homepage": "https://docs.belin.io/akismet.php",
"license": "MIT",
"name": "cedx/akismet",
"type": "library",
"version": "12.1.0",
"authors": [
{"email": "cedric@belin.io", "homepage": "https://belin.io", "name": "Cédric Belin"}
],
"autoload": {
"psr-4": {"Akismet\\": "src/"}
},
"autoload-dev": {
"psr-4": {"Akismet\\": "test/"}
},
"config": {
"optimize-autoloader": true
},
"funding": [
{"type": "patreon", "url": "https://www.patreon.com/cedx"}
],
"keywords": [
"akismet",
"comment",
"spam",
"validation"
],
"require": {
"php": ">=7.4.0",
"ext-date": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-pcre": "*",
"nyholm/psr7": "^1.2.1",
"psr/http-client": "^1.0.0",
"symfony/event-dispatcher": "^5.0.8",
"symfony/http-client": "^5.0.8"
},
"require-dev": {
"cedx/coveralls": "^13.0.0",
"phpstan/phpstan": "^0.12.25",
"phpunit/phpunit": "^9.1.4"
},
"scripts": {
"coverage": "coveralls var/coverage.xml",
"test": "phpunit --configuration=etc/phpunit.xml"
},
"support": {
"docs": "https://api.belin.io/akismet.php",
"issues": "https://git.belin.io/cedx/akismet.php/issues"
}
"description": "Prevent comment spam using the Akismet service. Add Akismet to your applications so you don't have to worry about spam again.",
"homepage": "https://docs.belin.io/akismet.php",
"license": "MIT",
"name": "cedx/akismet",
"type": "library",
"version": "12.1.0",
"authors": [
{"email": "cedric@belin.io", "homepage": "https://belin.io", "name": "Cédric Belin"}
],
"autoload": {
"psr-4": {"Akismet\\": "src/"}
},
"autoload-dev": {
"psr-4": {"Akismet\\": "test/"}
},
"config": {
"optimize-autoloader": true
},
"funding": [
{"type": "patreon", "url": "https://www.patreon.com/cedx"}
],
"keywords": [
"akismet",
"comment",
"spam",
"validation"
],
"require": {
"php": ">=7.4.0",
"ext-date": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-pcre": "*",
"nyholm/psr7": "^1.2.1",
"psr/http-client": "^1.0.0",
"symfony/event-dispatcher": "^5.0.8",
"symfony/http-client": "^5.0.8"
},
"require-dev": {
"cedx/coveralls": "^13.0.0",
"phpstan/phpstan": "^0.12.25",
"phpunit/phpunit": "^9.1.4"
},
"scripts": {
"coverage": "coveralls var/coverage.xml",
"test": "phpunit --configuration=etc/phpunit.xml"
},
"support": {
"docs": "https://api.belin.io/akismet.php",
"issues": "https://git.belin.io/cedx/akismet.php/issues"
}
}

+ 6
- 6
doc/advanced/events.md View File

@@ -9,15 +9,15 @@ The `Akismet\Client` class, used to query the Akismet service, is an [EventDispa
### The `Client::eventRequest` event
Emitted every time a request is made to the remote service:

```php
``` php
<?php
use Akismet\{Blog, Client, RequestEvent};
use Nyholm\Psr7\{Uri};

function main(): void {
$client = new Client('123YourAPIKey', new Blog(new Uri('https://www.yourblog.com')));
$client = new Client("123YourAPIKey", new Blog(new Uri("https://www.yourblog.com")));
$client->addListener(Client::eventRequest, function(RequestEvent $event) {
echo 'Client request: ', $event->getRequest()->getUri();
echo "Client request: ", $event->getRequest()->getUri();
});
}
```
@@ -25,15 +25,15 @@ function main(): void {
### The `Client::eventResponse` event
Emitted every time a response is received from the remote service:

```php
``` php
<?php
use Akismet\{Blog, Client, ResponseEvent};
use Nyholm\Psr7\{Uri};

function main(): void {
$client = new Client('123YourAPIKey', new Blog(new Uri('https://www.yourblog.com')));
$client = new Client("123YourAPIKey", new Blog(new Uri("https://www.yourblog.com")));
$client->addListener(Client::eventResponse, function(ResponseEvent $event) {
echo 'Server response: ', $event->getResponse()->getStatusCode();
echo "Server response: ", $event->getResponse()->getStatusCode();
});
}
```

+ 18
- 18
doc/advanced/testing.md View File

@@ -6,20 +6,20 @@ Make a [comment check](../features/comment_check.md) API call with the `Author->

The Akismet API will always return a `CheckResult::isSpam` response to a valid request with one of those values. If you receive anything else, something is wrong in your client, data, or communications.

```php
``` php
<?php
use Akismet\{Author, Blog, Client, Comment};
use Nyholm\Psr7\{Uri};

function main(): void {
$author = (new Author('127.0.0.1', 'Mozilla/5.0'))->setName('viagra-test-123');
$comment = (new Comment($author))->setContent('A user comment');
$author = (new Author("127.0.0.1", "Mozilla/5.0"))->setName("viagra-test-123");
$comment = (new Comment($author))->setContent("A user comment");
$blog = new Blog(new Uri('https://www.yourblog.com'));
$client = new Client('123YourAPIKey', $blog);
$blog = new Blog(new Uri("https://www.yourblog.com"));
$client = new Client("123YourAPIKey", $blog);
$result = $client->checkComment($comment);
echo 'It should be "CheckResult::isSpam": ', $result;
echo "It should be `CheckResult::isSpam`: ", $result;
}
```

@@ -28,20 +28,20 @@ Make a [comment check](../features/comment_check.md) API call with the `Author->

The Akismet API will always return a `CheckResult::isHam` response. Any other response indicates a data or communication problem.

```php
``` php
<?php
use Akismet\{Author, Blog, Client, Comment};
use Nyholm\Psr7\{Uri};

function main(): void {
$author = (new Author('127.0.0.1', 'Mozilla/5.0'))->setRole('administrator');
$comment = (new Comment($author))->setContent('A user comment');
$author = (new Author("127.0.0.1", "Mozilla/5.0"))->setRole("administrator");
$comment = (new Comment($author))->setContent("A user comment");
$blog = new Blog(new Uri('https://www.yourblog.com'));
$client = new Client('123YourAPIKey', $blog);
$blog = new Blog(new Uri("https://www.yourblog.com"));
$client = new Client("123YourAPIKey", $blog);
$result = $client->checkComment($comment);
echo 'It should be "CheckResult::isHam": ', $result;
echo "It should be `CheckResult::isHam`: ", $result;
}
```

@@ -50,19 +50,19 @@ Enable the `Client->isTest()` option in your tests.

That will tell Akismet not to change its behaviour based on those API calls: they will have no training effect. That means your tests will be somewhat repeatable, in the sense that one test won't influence subsequent calls.

```php
``` php
<?php
use Akismet\{Author, Blog, Client, Comment};
use Nyholm\Psr7\{Uri};

function main(): void {
$author = new Author('127.0.0.1', 'Mozilla/5.0');
$comment = (new Comment($author))->setContent('A user comment');
$author = new Author("127.0.0.1", "Mozilla/5.0");
$comment = (new Comment($author))->setContent("A user comment");
$blog = new Blog(new Uri('https://www.yourblog.com'));
$client = (new Client('123YourAPIKey', $blog))->setTest(true);
$blog = new Blog(new Uri("https://www.yourblog.com"));
$client = (new Client("123YourAPIKey", $blog))->setTest(true);
echo 'It should not influence subsequent calls.';
echo "It should not influence subsequent calls.";
$client->checkComment($comment);
}
```

+ 14
- 14
doc/features/comment_check.md View File

@@ -27,34 +27,34 @@ The `Comment` providing the user message to be checked.
A `CheckResult` value indicating whether the given `Comment` is ham, spam or pervasive spam.

!!! tip
A comment classified as pervasive spam can be safely discarded.
A comment classified as pervasive spam can be safely discarded.

The method throws a `ClientException` when an error occurs.
The exception `getMessage()` usually includes some debug information, provided by the `X-akismet-debug-help` HTTP header, about what exactly was invalid about the call.

## Example

```php
``` php
<?php
use Akismet\{Author, Blog, CheckResult, Client, ClientException, Comment, CommentType};
use Nyholm\Psr7\{Uri};

function main(): void {
try {
$comment = (new Comment(new Author))
->setContent('A user comment')
->setType(CommentType::contactForm);
try {
$comment = (new Comment(new Author))
->setContent("A user comment")
->setType(CommentType::contactForm);

$blog = new Blog(new Uri('https://www.yourblog.com'));
$client = new Client('123YourAPIKey', $blog);
$blog = new Blog(new Uri("https://www.yourblog.com"));
$client = new Client("123YourAPIKey", $blog);

$result = $client->checkComment($comment);
echo $result == CheckResult::isHam ? 'The comment is ham.' : 'The comment is spam.';
}
$result = $client->checkComment($comment);
echo $result == CheckResult::isHam ? "The comment is ham." : "The comment is spam.";
}

catch (ClientException $e) {
echo 'An error occurred: ', $e->getMessage();
}
catch (ClientException $e) {
echo "An error occurred: ", $e->getMessage();
}
}
```



+ 5
- 5
doc/features/key_verification.md View File

@@ -27,22 +27,22 @@ The exception `getMessage()` usually includes some debug information, provided b

## Example

```php
``` php
<?php
use Akismet\{Blog, Client, ClientException};
use Nyholm\Psr7\{Uri};

function main(): void {
try {
$blog = new Blog(new Uri('https://www.yourblog.com'));
$client = new Client('123YourAPIKey', $blog);
$blog = new Blog(new Uri("https://www.yourblog.com"));
$client = new Client("123YourAPIKey", $blog);

$isValid = $client->verifyKey();
echo $isValid ? 'The API key is valid.' : 'The API key is invalid.';
echo $isValid ? "The API key is valid." : "The API key is invalid.";
}

catch (ClientException $e) {
echo 'An error occurred: ', $e->getMessage();
echo "An error occurred: ", $e->getMessage();
}
}
```

+ 14
- 14
doc/features/submit_ham.md View File

@@ -17,7 +17,7 @@ See the [Akismet API documentation](https://akismet.com/development/api/#submit-
The user `Comment` to be submitted, incorrectly classified as spam.

!!! tip
Ideally, it should be the same object as the one passed to the original [comment check](comment_check.md) API call.
Ideally, it should be the same object as the one passed to the original [comment check](comment_check.md) API call.

## Return value
None.
@@ -27,26 +27,26 @@ The exception `getMessage()` usually includes some debug information, provided b

## Example

```php
``` php
<?php
use Akismet\{Author, Blog, Client, ClientException, Comment};
use Nyholm\Psr7\{Uri};

function main(): void {
try {
$blog = new Blog(new Uri('https://www.yourblog.com'));
$client = new Client('123YourAPIKey', $blog);
try {
$blog = new Blog(new Uri("https://www.yourblog.com"));
$client = new Client("123YourAPIKey", $blog);

$comment = (new Comment(new Author))->setContent('A valid user comment (ham)');
$result = $client->checkComment($comment);
// Got `CheckResult::isSpam`, but `CheckResult::isHam` expected.
$comment = (new Comment(new Author))->setContent("A valid user comment (ham)");
$result = $client->checkComment($comment);
// Got `CheckResult::isSpam`, but `CheckResult::isHam` expected.

echo 'The comment was incorrectly classified as spam.';
$client->submitHam($comment);
}
echo "The comment was incorrectly classified as spam.";
$client->submitHam($comment);
}

catch (ClientException $e) {
echo 'An error occurred: ', $e->getMessage();
}
catch (ClientException $e) {
echo "An error occurred: ", $e->getMessage();
}
}
```

+ 14
- 14
doc/features/submit_spam.md View File

@@ -19,7 +19,7 @@ See the [Akismet API documentation](https://akismet.com/development/api/#submit-
The user `Comment` to be submitted, incorrectly classified as ham.

!!! tip
Ideally, it should be the same object as the one passed to the original [comment check](comment_check.md) API call.
Ideally, it should be the same object as the one passed to the original [comment check](comment_check.md) API call.

## Return value
None.
@@ -29,26 +29,26 @@ The exception `getMessage()` usually includes some debug information, provided b

## Example

```php
``` php
<?php
use Akismet\{Author, Blog, Client, ClientException, Comment};
use Nyholm\Psr7\{Uri};

function main(): void {
try {
$blog = new Blog(new Uri('https://www.yourblog.com'));
$client = new Client('123YourAPIKey', $blog);
try {
$blog = new Blog(new Uri("https://www.yourblog.com"));
$client = new Client("123YourAPIKey", $blog);

$comment = (new Comment(new Author))->setContent('An invalid user comment (spam)');
$result = $client->checkComment($comment);
// Got `CheckResult::isHam`, but `CheckResult::isSpam` expected.
$comment = (new Comment(new Author))->setContent("An invalid user comment (spam)");
$result = $client->checkComment($comment);
// Got `CheckResult::isHam`, but `CheckResult::isSpam` expected.

echo 'The comment was incorrectly classified as ham.';
$client->submitSpam($comment);
}
echo "The comment was incorrectly classified as ham.";
$client->submitSpam($comment);
}

catch (ClientException $e) {
echo 'An error occurred: ', $e->getMessage();
}
catch (ClientException $e) {
echo "An error occurred: ", $e->getMessage();
}
}
```

+ 3
- 3
doc/index.md View File

@@ -14,13 +14,13 @@ You first need to [sign up for a developer key](https://akismet.com/signup/?plan
This will give you access to the API and will allow Akismet to monitor its results to make sure things are running as smoothly as possible.

!!! warning
All Akismet endpoints require an API key. If you are not already registered,
[join the developer program](https://akismet.com/signup/?plan=developer).
All Akismet endpoints require an API key. If you are not already registered,
[join the developer program](https://akismet.com/signup/?plan=developer).

### Get the library
Install the latest version of **Akismet for PHP** with [Composer](https://getcomposer.org):

```shell
``` shell
composer require cedx/akismet
```



+ 12
- 12
doc/installation.md View File

@@ -6,7 +6,7 @@ and [Composer](https://getcomposer.org), the PHP package manager, up and running

You can verify if you're already good to go with the following commands:

```shell
``` shell
php --version
# PHP 7.4.5 (cli) (built: Apr 14 2020 16:17:19) ( NTS Visual C++ 2017 x64 )

@@ -15,30 +15,30 @@ composer --version
```

!!! info
If you plan to play with the package sources, you will also need the latest versions of
[PowerShell](https://docs.microsoft.com/en-us/powershell) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material).
If you plan to play with the package sources, you will also need the latest versions of
[PowerShell](https://docs.microsoft.com/en-us/powershell) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material).

## Installing with Composer package manager

### 1. Install it
From a command prompt, run:

```shell
``` shell
composer require cedx/akismet
```

### 2. Import it
Now in your [PHP](https://www.php.net) code, you can use:

```php
``` php
<?php
use Akismet\{
Author,
Blog,
CheckResult,
Client,
ClientException,
Comment,
CommentType
Author,
Blog,
CheckResult,
Client,
ClientException,
Comment,
CommentType
};
```

+ 9
- 5
etc/mkdocs.yaml View File

@@ -10,19 +10,23 @@ site_dir: ../www

repo_name: git.belin.io
repo_url: https://git.belin.io/cedx/akismet.php
edit_uri: ''
edit_uri: ""

copyright: Copyright &copy; 2016 - 2020 Cédric Belin
extra:
social:
- icon: fontawesome/solid/globe
link: 'https://belin.io'
link: "https://belin.io"
name: Belin.io
- icon: fontawesome/brands/github
link: 'https://github.com/cedx'
link: "https://github.com/cedx"
name: GitHub
- icon: fontawesome/brands/twitter
link: 'https://twitter.com/cedxbelin'
link: "https://twitter.com/cedxbelin"
name: Twitter
- icon: fontawesome/brands/linkedin
link: 'https://linkedin.com/in/cedxbelin'
link: "https://linkedin.com/in/cedxbelin"
name: LinkedIn

markdown_extensions:
- admonition


+ 22
- 22
etc/phpdoc.xml View File

@@ -1,25 +1,25 @@
<?xml version="1.0"?>
<phpdocumentor
configVersion="3"
xmlns="http://www.phpdoc.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://docs.phpdoc.org/latest/phpdoc.xsd">
<title>Akismet for PHP</title>
<paths>
<cache>../var/phpdoc</cache>
<output>../doc/api</output>
</paths>
<version number="12.1.0">
<api>
<markers>
<marker>TODO</marker>
</markers>
<source dsn="..">
<path>src</path>
</source>
<visibility>protected</visibility>
<visibility>public</visibility>
</api>
</version>
configVersion="3"
xmlns="http://www.phpdoc.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://docs.phpdoc.org/latest/phpdoc.xsd">
<title>Akismet for PHP</title>
<paths>
<cache>../var/phpdoc</cache>
<output>../doc/api</output>
</paths>
<version number="12.1.0">
<api>
<markers>
<marker>TODO</marker>
</markers>
<source dsn="..">
<path>src</path>
</source>
<visibility>protected</visibility>
<visibility>public</visibility>
</api>
</version>
</phpdocumentor>

+ 2
- 2
etc/phpstan.neon View File

@@ -1,3 +1,3 @@
parameters:
level: max
paths: [../src, ../test]
level: max
paths: [../src, ../test]

+ 13
- 13
etc/phpunit.xml View File

@@ -1,18 +1,18 @@
<?xml version="1.0"?>
<phpunit bootstrap="../vendor/autoload.php" cacheResult="false" testdox="true">
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">../src</directory>
</whitelist>
</filter>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">../src</directory>
</whitelist>
</filter>

<logging>
<log type="coverage-clover" target="../var/coverage.xml"/>
</logging>
<logging>
<log type="coverage-clover" target="../var/coverage.xml"/>
</logging>

<testsuites>
<testsuite name="all">
<directory suffix="Test.php">../test</directory>
</testsuite>
</testsuites>
<testsuites>
<testsuite name="all">
<directory suffix="Test.php">../test</directory>
</testsuite>
</testsuites>
</phpunit>

+ 35
- 35
example/main.php View File

@@ -5,39 +5,39 @@ use Nyholm\Psr7\{Uri};

/** Queries the Akismet service. */
function main(): void {
try {
$client = new Client('123YourAPIKey', (new Blog(new Uri('https://www.yourblog.com')))
->setCharset('UTF-8')
->setLanguages(['fr']));
// Key verification.
$isValid = $client->verifyKey();
echo $isValid ? 'The API key is valid' : 'The API key is invalid';
// Comment check.
$author = (new Author)
->setEmail('john.doe@domain.com')
->setName('John Doe')
->setRole('guest');
$comment = (new Comment($author))
->setContent('A user comment')
->setDate(new DateTimeImmutable)
->setType(CommentType::contactForm);
$result = $client->checkComment($comment);
echo $result == CheckResult::isHam ? 'The comment is ham' : 'The comment is spam';
// Submit spam / ham.
$client->submitSpam($comment);
echo 'Spam submitted';
$client->submitHam($comment);
echo 'Ham submitted';
}
catch (Throwable $e) {
echo 'An error occurred: ', $e->getMessage(), PHP_EOL;
if ($e instanceof ClientException) echo 'From: ', $e->getUri(), PHP_EOL;
}
try {
$client = new Client("123YourAPIKey", (new Blog(new Uri("https://www.yourblog.com")))
->setCharset("UTF-8")
->setLanguages(["fr"]));
// Key verification.
$isValid = $client->verifyKey();
echo $isValid ? "The API key is valid" : "The API key is invalid";
// Comment check.
$author = (new Author)
->setEmail("john.doe@domain.com")
->setName("John Doe")
->setRole("guest");
$comment = (new Comment($author))
->setContent("A user comment")
->setDate(new DateTimeImmutable)
->setType(CommentType::contactForm);
$result = $client->checkComment($comment);
echo $result == CheckResult::isHam ? "The comment is ham" : "The comment is spam";
// Submit spam / ham.
$client->submitSpam($comment);
echo "Spam submitted";
$client->submitHam($comment);
echo "Ham submitted";
}
catch (Throwable $e) {
echo "An error occurred: ", $e->getMessage(), PHP_EOL;
if ($e instanceof ClientException) echo "From: ", $e->getUri(), PHP_EOL;
}
}

+ 149
- 149
src/Author.php View File

@@ -7,153 +7,153 @@ use Psr\Http\Message\{UriInterface};
/** Represents the author of a comment. */
class Author implements \JsonSerializable {

/** @var string The author's mail address. */
private string $email = '';
/** @var string The author's IP address. */
private string $ipAddress;
/** @var string The author's name. */
private string $name = '';
/** @var string The author's role. */
private string $role = '';
/** @var UriInterface|null The URL of the author's website. */
private ?UriInterface $url = null;
/** @var string The author's user agent, that is the string identifying the Web browser used to submit comments. */
private string $userAgent;
/**
* Creates a new author.
* @param string|null $ipAddress The author's IP address. Defaults to `$_SERVER['REMOTE_ADDR']`.
* @param string|null $userAgent The author's user agent. Defaults to `$_SERVER['HTTP_USER_AGENT']`.
*/
function __construct(?string $ipAddress = null, ?string $userAgent = null) {
$this->ipAddress = $ipAddress ?? ($_SERVER['REMOTE_ADDR'] ?? '');
$this->userAgent = $userAgent ?? ($_SERVER['HTTP_USER_AGENT'] ?? '');
}
/**
* Creates a new author from the specified JSON object.
* @param object $map A JSON object representing an author.
* @return self The instance corresponding to the specified JSON object.
*/
static function fromJson(object $map): self {
$author = new self(
isset($map->user_ip) && is_string($map->user_ip) ? $map->user_ip : '',
isset($map->user_agent) && is_string($map->user_agent) ? $map->user_agent : ''
);
return $author
->setEmail(isset($map->comment_author_email) && is_string($map->comment_author_email) ? $map->comment_author_email : '')
->setName(isset($map->comment_author) && is_string($map->comment_author) ? $map->comment_author : '')
->setRole(isset($map->user_role) && is_string($map->user_role) ? $map->user_role : '')
->setUrl(isset($map->comment_author_url) && is_string($map->comment_author_url) ? new Uri($map->comment_author_url) : null);
}
/**
* Gets the author's mail address.
* @return string The author's mail address.
*/
function getEmail(): string {
return $this->email;
}
/**
* Gets the author's IP address.
* @return string The author's IP address.
*/
function getIPAddress(): string {
return $this->ipAddress;
}
/**
* Gets the author's name.
* @return string The author's name.
*/
function getName(): string {
return $this->name;
}
/**
* Gets the author's role.
* @return string The author's role.
*/
function getRole(): string {
return $this->role;
}
/**
* Gets the URL of the author's website.
* @return UriInterface|null The URL of the author's website.
*/
function getUrl(): ?UriInterface {
return $this->url;
}
/**
* Gets the author's user agent, that is the string identifying the Web browser used to submit comments.
* @return string The author's user agent.
*/
function getUserAgent(): string {
return $this->userAgent;
}
/**
* Converts this object to a map in JSON format.
* @return \stdClass The map in JSON format corresponding to this object.
*/
function jsonSerialize(): \stdClass {
$map = new \stdClass;
$map->user_agent = $this->getUserAgent();
$map->user_ip = $this->getIPAddress();
if (mb_strlen($name = $this->getName())) $map->comment_author = $name;
if (mb_strlen($email = $this->getEmail())) $map->comment_author_email = $email;
if ($url = $this->getUrl()) $map->comment_author_url = (string) $url;
if (mb_strlen($role = $this->getRole())) $map->user_role = $role;
return $map;
}
/**
* Sets the author's mail address. If you set it to `"akismet-guaranteed-spam@example.com"`, Akismet will always return `true`.
* @param string $value The new mail address.
* @return $this This instance.
*/
function setEmail(string $value): self {
$this->email = $value;
return $this;
}
/**
* Sets the author's name. If you set it to `"viagra-test-123"`, Akismet will always return `true`.
* @param string $value The new name.
* @return $this This instance.
*/
function setName(string $value): self {
$this->name = $value;
return $this;
}
/**
* Sets the author's role. If you set it to `"administrator"`, Akismet will always return `false`.
* @param string $value The new role.
* @return $this This instance.
*/
function setRole(string $value): self {
$this->role = $value;
return $this;
}
/**
* Sets the URL of the author's website.
* @param UriInterface|null $value The new website URL.
* @return $this This instance.
*/
function setUrl(?UriInterface $value): self {
$this->url = $value;
return $this;
}
/** @var string The author's mail address. */
private string $email = "";
/** @var string The author's IP address. */
private string $ipAddress;
/** @var string The author's name. */
private string $name = "";
/** @var string The author's role. */
private string $role = "";
/** @var UriInterface|null The URL of the author's website. */
private ?UriInterface $url = null;
/** @var string The author's user agent, that is the string identifying the Web browser used to submit comments. */
private string $userAgent;
/**
* Creates a new author.
* @param string|null $ipAddress The author's IP address. Defaults to `$_SERVER["REMOTE_ADDR"]`.
* @param string|null $userAgent The author's user agent. Defaults to `$_SERVER["HTTP_USER_AGENT"]`.
*/
function __construct(?string $ipAddress = null, ?string $userAgent = null) {
$this->ipAddress = $ipAddress ?? ($_SERVER["REMOTE_ADDR"] ?? "");
$this->userAgent = $userAgent ?? ($_SERVER["HTTP_USER_AGENT"] ?? "");
}
/**
* Creates a new author from the specified JSON object.
* @param object $map A JSON object representing an author.
* @return self The instance corresponding to the specified JSON object.
*/
static function fromJson(object $map): self {
$author = new self(
isset($map->user_ip) && is_string($map->user_ip) ? $map->user_ip : "",
isset($map->user_agent) && is_string($map->user_agent) ? $map->user_agent : ""
);
return $author
->setEmail(isset($map->comment_author_email) && is_string($map->comment_author_email) ? $map->comment_author_email : "")
->setName(isset($map->comment_author) && is_string($map->comment_author) ? $map->comment_author : "")
->setRole(isset($map->user_role) && is_string($map->user_role) ? $map->user_role : "")
->setUrl(isset($map->comment_author_url) && is_string($map->comment_author_url) ? new Uri($map->comment_author_url) : null);
}
/**
* Gets the author's mail address.
* @return string The author's mail address.
*/
function getEmail(): string {
return $this->email;
}
/**
* Gets the author's IP address.
* @return string The author's IP address.
*/
function getIPAddress(): string {
return $this->ipAddress;
}
/**
* Gets the author's name.
* @return string The author's name.
*/
function getName(): string {
return $this->name;
}
/**
* Gets the author's role.
* @return string The author's role.
*/
function getRole(): string {
return $this->role;
}
/**
* Gets the URL of the author's website.
* @return UriInterface|null The URL of the author's website.
*/
function getUrl(): ?UriInterface {
return $this->url;
}
/**
* Gets the author's user agent, that is the string identifying the Web browser used to submit comments.
* @return string The author's user agent.
*/
function getUserAgent(): string {
return $this->userAgent;
}
/**
* Converts this object to a map in JSON format.
* @return \stdClass The map in JSON format corresponding to this object.
*/
function jsonSerialize(): \stdClass {
$map = new \stdClass;
$map->user_agent = $this->getUserAgent();
$map->user_ip = $this->getIPAddress();
if (mb_strlen($name = $this->getName())) $map->comment_author = $name;
if (mb_strlen($email = $this->getEmail())) $map->comment_author_email = $email;
if ($url = $this->getUrl()) $map->comment_author_url = (string) $url;
if (mb_strlen($role = $this->getRole())) $map->user_role = $role;
return $map;
}
/**
* Sets the author's mail address. If you set it to `"akismet-guaranteed-spam@example.com"`, Akismet will always return `true`.
* @param string $value The new mail address.
* @return $this This instance.
*/
function setEmail(string $value): self {
$this->email = $value;
return $this;
}
/**
* Sets the author's name. If you set it to `"viagra-test-123"`, Akismet will always return `true`.
* @param string $value The new name.
* @return $this This instance.
*/
function setName(string $value): self {
$this->name = $value;
return $this;
}
/**
* Sets the author's role. If you set it to `"administrator"`, Akismet will always return `false`.
* @param string $value The new role.
* @return $this This instance.
*/
function setRole(string $value): self {
$this->role = $value;
return $this;
}
/**
* Sets the URL of the author's website.
* @param UriInterface|null $value The new website URL.
* @return $this This instance.
*/
function setUrl(?UriInterface $value): self {
$this->url = $value;
return $this;
}
}

+ 74
- 74
src/Blog.php View File

@@ -7,88 +7,88 @@ use Psr\Http\Message\{UriInterface};
/** Represents the front page or home URL transmitted when making requests. */
class Blog implements \JsonSerializable {

/** @var string The character encoding for the values included in comments. */
private string $charset = '';
/** @var string The character encoding for the values included in comments. */
private string $charset = "";

/** @var \ArrayObject<int, string> The languages in use on the blog or site, in ISO 639-1 format. */
private \ArrayObject $languages;
/** @var \ArrayObject<int, string> The languages in use on the blog or site, in ISO 639-1 format. */
private \ArrayObject $languages;

/** @var UriInterface|null The blog or site URL. */
private ?UriInterface $url;
/** @var UriInterface|null The blog or site URL. */
private ?UriInterface $url;

/**
* Creates a new blog.
* @param UriInterface|null $url The blog or site URL.
*/
function __construct(?UriInterface $url) {
$this->languages = new \ArrayObject;
$this->url = $url;
}
/**
* Creates a new blog.
* @param UriInterface|null $url The blog or site URL.
*/
function __construct(?UriInterface $url) {
$this->languages = new \ArrayObject;
$this->url = $url;
}

/**
* Creates a new blog from the specified JSON object.
* @param object $map A JSON object representing a blog.
* @return self The instance corresponding to the specified JSON object.
*/
static function fromJson(object $map): self {
return (new self(isset($map->blog) && is_string($map->blog) ? new Uri($map->blog) : null))
->setCharset(isset($map->blog_charset) && is_string($map->blog_charset) ? $map->blog_charset : '')
->setLanguages(isset($map->blog_lang) && is_string($map->blog_lang) ? array_map('trim', explode(',', $map->blog_lang)) : []);
}
/**
* Creates a new blog from the specified JSON object.
* @param object $map A JSON object representing a blog.
* @return self The instance corresponding to the specified JSON object.
*/
static function fromJson(object $map): self {
return (new self(isset($map->blog) && is_string($map->blog) ? new Uri($map->blog) : null))
->setCharset(isset($map->blog_charset) && is_string($map->blog_charset) ? $map->blog_charset : "")
->setLanguages(isset($map->blog_lang) && is_string($map->blog_lang) ? array_map("trim", explode(",", $map->blog_lang)) : []);
}

/**
* Gets the character encoding for the values included in comments.
* @return string The character encoding for the values included in comments.
*/
function getCharset(): string {
return $this->charset;
}
/**
* Gets the character encoding for the values included in comments.
* @return string The character encoding for the values included in comments.
*/
function getCharset(): string {
return $this->charset;
}

/**
* Gets the languages in use on the blog or site, in ISO 639-1 format.
* @return \ArrayObject<int, string> The languages in use on the blog or site.
*/
function getLanguages(): \ArrayObject {
return $this->languages;
}
/**
* Gets the languages in use on the blog or site, in ISO 639-1 format.
* @return \ArrayObject<int, string> The languages in use on the blog or site.
*/
function getLanguages(): \ArrayObject {
return $this->languages;
}

/**
* Gets the blog or site URL.
* @return UriInterface|null The blog or site URL.
*/
function getUrl(): ?UriInterface {
return $this->url;
}
/**
* Gets the blog or site URL.
* @return UriInterface|null The blog or site URL.
*/
function getUrl(): ?UriInterface {
return $this->url;
}

/**
* Converts this object to a map in JSON format.
* @return \stdClass The map in JSON format corresponding to this object.
*/
function jsonSerialize(): \stdClass {
$map = new \stdClass;
$map->blog = (string) $this->getUrl();
if (mb_strlen($charset = $this->getCharset())) $map->blog_charset = $charset;
if (count($languages = $this->getLanguages())) $map->blog_lang = implode(',', (array) $languages);
return $map;
}
/**
* Converts this object to a map in JSON format.
* @return \stdClass The map in JSON format corresponding to this object.
*/
function jsonSerialize(): \stdClass {
$map = new \stdClass;
$map->blog = (string) $this->getUrl();
if (mb_strlen($charset = $this->getCharset())) $map->blog_charset = $charset;
if (count($languages = $this->getLanguages())) $map->blog_lang = implode(",", (array) $languages);
return $map;
}

/**
* Sets the character encoding for the values included in comments.
* @param string $value The new character encoding.
* @return $this This instance.
*/
function setCharset(string $value): self {
$this->charset = $value;
return $this;
}
/**
* Sets the character encoding for the values included in comments.
* @param string $value The new character encoding.
* @return $this This instance.
*/
function setCharset(string $value): self {
$this->charset = $value;
return $this;
}

/**
* Sets the languages in use on the blog or site, in ISO 639-1 format.
* @param array<int, string> $values The languages in use on the blog or site.
* @return $this This instance.
*/
function setLanguages(array $values): self {
$this->getLanguages()->exchangeArray($values);
return $this;
}
/**
* Sets the languages in use on the blog or site, in ISO 639-1 format.
* @param array<int, string> $values The languages in use on the blog or site.
* @return $this This instance.
*/
function setLanguages(array $values): self {
$this->getLanguages()->exchangeArray($values);
return $this;
}
}

+ 6
- 6
src/CheckResult.php View File

@@ -4,12 +4,12 @@ namespace Akismet;
/** Specifies the result of a comment check. */
abstract class CheckResult {

/** @var string The comment is not a spam (i.e. a ham). */
const isHam = 'isHam';
/** @var string The comment is not a spam (i.e. a ham). */
const isHam = "isHam";

/** @var string The comment is a pervasive spam (i.e. it can be safely discarded). */
const isPervasiveSpam = 'isPervasiveSpam';
/** @var string The comment is a pervasive spam (i.e. it can be safely discarded). */
const isPervasiveSpam = "isPervasiveSpam";

/** @var string The comment is a spam. */
const isSpam = 'isSpam';
/** @var string The comment is a spam. */
const isSpam = "isSpam";
}

+ 189
- 189
src/Client.php View File

@@ -8,193 +8,193 @@ use Symfony\Component\HttpClient\{Psr18Client};
/** Submits comments to the [Akismet](https://akismet.com) service. */
class Client extends EventDispatcher {

/** @var string An event that is triggered when a request is made to the remote service. */
const eventRequest = RequestEvent::class;
/** @var string An event that is triggered when a response is received from the remote service. */
const eventResponse = ResponseEvent::class;
/** @var string The Akismet API key. */
private string $apiKey;
/** @var Blog The front page or home URL of the instance making requests. */
private Blog $blog;
/** @var UriInterface The URL of the API end point. */
private UriInterface $endPoint;
/** @var Psr18Client The HTTP client. */
private Psr18Client $http;
/** @var bool Value indicating whether the client operates in test mode. */
private bool $isTest = false;
/** @var string The user agent string to use when making requests. */
private string $userAgent;
/**
* Creates a new client.
* @param string $apiKey The Akismet API key.
* @param Blog $blog The front page or home URL of the instance making requests.
*/
function __construct(string $apiKey, Blog $blog) {
assert(mb_strlen($apiKey) > 0);
parent::__construct();
$this->apiKey = $apiKey;
$this->blog = $blog;
$this->http = new Psr18Client;
$this->endPoint = $this->http->createUri('https://rest.akismet.com/1.1/');
$phpVersion = implode('.', [PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION]);
$pkgVersion = require __DIR__.'/version.g.php';
$this->userAgent = "PHP/$phpVersion | Akismet/$pkgVersion";
}
/**
* Checks the specified comment against the service database, and returns a value indicating whether it is spam.
* @param Comment $comment The comment to be checked.
* @return string A `CheckResult` value indicating whether the specified comment is spam.
*/
function checkComment(Comment $comment): string {
$apiUrl = $this->getEndPoint();
$endPoint = $this->http->createUri("{$apiUrl->getScheme()}://{$this->getApiKey()}.{$apiUrl->getAuthority()}{$apiUrl->getPath()}comment-check");
$response = $this->fetch($endPoint, get_object_vars($comment->jsonSerialize()));
if (((string) $response->getBody()) == 'false') return CheckResult::isHam;
return $response->getHeaderLine('X-akismet-pro-tip') == 'discard' ? CheckResult::isPervasiveSpam : CheckResult::isSpam;
}
/**
* Gets the Akismet API key.
* @return string The Akismet API key.
*/
function getApiKey(): string {
return $this->apiKey;
}
/**
* Gets the front page or home URL of the instance making requests.
* @return Blog The front page or home URL.
*/
function getBlog(): Blog {
return $this->blog;
}
/**
* Gets the URL of the API end point.
* @return UriInterface The URL of the API end point.
*/
function getEndPoint(): UriInterface {
return $this->endPoint;
}
/**
* Gets the user agent string to use when making requests.
* If possible, the user agent string should always have the following format: `Application Name/Version | Plugin Name/Version`.
* @return string The user agent string to use when making requests.
*/
function getUserAgent(): string {
return $this->userAgent;
}
/**
* Gets a value indicating whether the client operates in test mode.
* @return bool `true` if the client operates in test mode, otherwise `false`.
*/
function isTest(): bool {
return $this->isTest;
}
/**
* Sets the URL of the API end point.
* @param UriInterface $value The new URL of the API end point.
* @return $this This instance.
*/
function setEndPoint(UriInterface $value): self {
$this->endPoint = $value->withUserInfo('');
return $this;
}
/**
* Sets a value indicating whether the client operates in test mode.
* You can use it when submitting test queries to Akismet.
* @param bool $value `true` to enable the test mode, otherwise `false`.
* @return $this This instance.
*/
function setTest(bool $value): self {
$this->isTest = $value;
return $this;
}
/**
* Sets the user agent string to use when making requests.
* @param string $value The new user agent.
* @return $this This instance.
*/
function setUserAgent(string $value): self {
assert(mb_strlen($value) > 0);
$this->userAgent = $value;
return $this;
}
/**
* Submits the specified comment that was incorrectly marked as spam but should not have been.
* @param Comment $comment The comment to be submitted.
*/
function submitHam(Comment $comment): void {
$apiUrl = $this->getEndPoint();
$endPoint = $this->http->createUri("{$apiUrl->getScheme()}://{$this->getApiKey()}.{$apiUrl->getAuthority()}{$apiUrl->getPath()}submit-ham");
$this->fetch($endPoint, get_object_vars($comment->jsonSerialize()));
}
/**
* Submits the specified comment that was not marked as spam but should have been.
* @param Comment $comment The comment to be submitted.
*/
function submitSpam(Comment $comment): void {
$apiUrl = $this->getEndPoint();
$endPoint = $this->http->createUri("{$apiUrl->getScheme()}://{$this->getApiKey()}.{$apiUrl->getAuthority()}{$apiUrl->getPath()}submit-spam");
$this->fetch($endPoint, get_object_vars($comment->jsonSerialize()));
}
/**
* Checks the API key against the service database, and returns a value indicating whether it is valid.
* @return bool `true` if the specified API key is valid, otherwise `false`.
*/
function verifyKey(): bool {
$apiUrl = $this->getEndPoint();
$response = $this->fetch($apiUrl->withPath("{$apiUrl->getPath()}verify-key"), ['key' => $this->getApiKey()]);
return ((string) $response->getBody()) == 'valid';
}
/**
* Queries the service by posting the specified fields to a given end point, and returns the response as a string.
* @param UriInterface $endPoint The URL of the end point to query.
* @param array<string, string> $fields The fields describing the query body.
* @return ResponseInterface The server response.
* @throws ClientException An error occurred while querying the end point.
*/
private function fetch(UriInterface $endPoint, array $fields = []): ResponseInterface {
$bodyFields = array_merge(get_object_vars($this->getBlog()->jsonSerialize()), $fields);
if ($this->isTest()) $bodyFields['is_test'] = '1';
try {
$request = $this->http->createRequest('POST', $endPoint)
->withBody($this->http->createStream(http_build_query($bodyFields, '', '&', PHP_QUERY_RFC1738)))
->withHeader('User-Agent', $this->getUserAgent());
$this->dispatch(new RequestEvent($request));
$response = $this->http->sendRequest($request);
$this->dispatch(new ResponseEvent($response, $request));
if ($response->hasHeader('X-akismet-debug-help')) throw new ClientException($response->getHeaderLine('X-akismet-debug-help'), $endPoint);
return $response;
}
catch (\Throwable $e) {
if ($e instanceof ClientException) throw $e;
throw new ClientException($e->getMessage(), $endPoint, $e);
}
}
/** @var string An event that is triggered when a request is made to the remote service. */
const eventRequest = RequestEvent::class;
/** @var string An event that is triggered when a response is received from the remote service. */
const eventResponse = ResponseEvent::class;
/** @var string The Akismet API key. */
private string $apiKey;
/** @var Blog The front page or home URL of the instance making requests. */
private Blog $blog;
/** @var UriInterface The URL of the API end point. */
private UriInterface $endPoint;
/** @var Psr18Client The HTTP client. */
private Psr18Client $http;
/** @var bool Value indicating whether the client operates in test mode. */
private bool $isTest = false;
/** @var string The user agent string to use when making requests. */
private string $userAgent;
/**
* Creates a new client.
* @param string $apiKey The Akismet API key.
* @param Blog $blog The front page or home URL of the instance making requests.
*/
function __construct(string $apiKey, Blog $blog) {
assert(mb_strlen($apiKey) > 0);
parent::__construct();
$this->apiKey = $apiKey;
$this->blog = $blog;
$this->http = new Psr18Client;
$this->endPoint = $this->http->createUri("https://rest.akismet.com/1.1/");
$phpVersion = implode(".", [PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION]);
$pkgVersion = require __DIR__."/version.g.php";
$this->userAgent = "PHP/$phpVersion | Akismet/$pkgVersion";
}
/**
* Checks the specified comment against the service database, and returns a value indicating whether it is spam.
* @param Comment $comment The comment to be checked.
* @return string A `CheckResult` value indicating whether the specified comment is spam.
*/
function checkComment(Comment $comment): string {
$apiUrl = $this->getEndPoint();
$endPoint = $this->http->createUri("{$apiUrl->getScheme()}://{$this->getApiKey()}.{$apiUrl->getAuthority()}{$apiUrl->getPath()}comment-check");
$response = $this->fetch($endPoint, get_object_vars($comment->jsonSerialize()));
if (((string) $response->getBody()) == "false") return CheckResult::isHam;
return $response->getHeaderLine("X-akismet-pro-tip") == "discard" ? CheckResult::isPervasiveSpam : CheckResult::isSpam;
}
/**
* Gets the Akismet API key.
* @return string The Akismet API key.
*/
function getApiKey(): string {
return $this->apiKey;
}
/**
* Gets the front page or home URL of the instance making requests.
* @return Blog The front page or home URL.
*/
function getBlog(): Blog {
return $this->blog;
}
/**
* Gets the URL of the API end point.
* @return UriInterface The URL of the API end point.
*/
function getEndPoint(): UriInterface {
return $this->endPoint;
}
/**
* Gets the user agent string to use when making requests.
* If possible, the user agent string should always have the following format: `Application Name/Version | Plugin Name/Version`.
* @return string The user agent string to use when making requests.
*/
function getUserAgent(): string {
return $this->userAgent;
}
/**
* Gets a value indicating whether the client operates in test mode.
* @return bool `true` if the client operates in test mode, otherwise `false`.
*/
function isTest(): bool {
return $this->isTest;
}
/**
* Sets the URL of the API end point.
* @param UriInterface $value The new URL of the API end point.
* @return $this This instance.
*/
function setEndPoint(UriInterface $value): self {
$this->endPoint = $value->withUserInfo("");
return $this;
}
/**
* Sets a value indicating whether the client operates in test mode.
* You can use it when submitting test queries to Akismet.
* @param bool $value `true` to enable the test mode, otherwise `false`.
* @return $this This instance.
*/
function setTest(bool $value): self {
$this->isTest = $value;
return $this;
}
/**
* Sets the user agent string to use when making requests.
* @param string $value The new user agent.
* @return $this This instance.
*/
function setUserAgent(string $value): self {
assert(mb_strlen($value) > 0);
$this->userAgent = $value;
return $this;
}
/**
* Submits the specified comment that was incorrectly marked as spam but should not have been.
* @param Comment $comment The comment to be submitted.
*/
function submitHam(Comment $comment): void {
$apiUrl = $this->getEndPoint();
$endPoint = $this->http->createUri("{$apiUrl->getScheme()}://{$this->getApiKey()}.{$apiUrl->getAuthority()}{$apiUrl->getPath()}submit-ham");
$this->fetch($endPoint, get_object_vars($comment->jsonSerialize()));
}
/**
* Submits the specified comment that was not marked as spam but should have been.
* @param Comment $comment The comment to be submitted.
*/
function submitSpam(Comment $comment): void {
$apiUrl = $this->getEndPoint();
$endPoint = $this->http->createUri("{$apiUrl->getScheme()}://{$this->getApiKey()}.{$apiUrl->getAuthority()}{$apiUrl->getPath()}submit-spam");
$this->fetch($endPoint, get_object_vars($comment->jsonSerialize()));
}
/**
* Checks the API key against the service database, and returns a value indicating whether it is valid.
* @return bool `true` if the specified API key is valid, otherwise `false`.
*/
function verifyKey(): bool {
$apiUrl = $this->getEndPoint();
$response = $this->fetch($apiUrl->withPath("{$apiUrl->getPath()}verify-key"), ["key" => $this->getApiKey()]);
return ((string) $response->getBody()) == "valid";
}
/**
* Queries the service by posting the specified fields to a given end point, and returns the response as a string.
* @param UriInterface $endPoint The URL of the end point to query.
* @param array<string, string> $fields The fields describing the query body.
* @return ResponseInterface The server response.
* @throws ClientException An error occurred while querying the end point.
*/
private function fetch(UriInterface $endPoint, array $fields = []): ResponseInterface {
$bodyFields = array_merge(get_object_vars($this->getBlog()->jsonSerialize()), $fields);
if ($this->isTest()) $bodyFields["is_test"] = "1";
try {
$request = $this->http->createRequest("POST", $endPoint)
->withBody($this->http->createStream(http_build_query($bodyFields, "", "&", PHP_QUERY_RFC1738)))
->withHeader("User-Agent", $this->getUserAgent());
$this->dispatch(new RequestEvent($request));
$response = $this->http->sendRequest($request);
$this->dispatch(new ResponseEvent($response, $request));
if ($response->hasHeader("X-akismet-debug-help")) throw new ClientException($response->getHeaderLine("X-akismet-debug-help"), $endPoint);
return $response;
}
catch (\Throwable $e) {
if ($e instanceof ClientException) throw $e;
throw new ClientException($e->getMessage(), $endPoint, $e);
}
}
}

+ 19
- 19
src/ClientException.php View File

@@ -6,25 +6,25 @@ use Psr\Http\Message\{UriInterface};
/** An exception caused by an error in a `Client` request. */
class ClientException extends \RuntimeException {

/** @var UriInterface|null The URL of the HTTP request or response that failed. */
private ?UriInterface $uri;
/** @var UriInterface|null The URL of the HTTP request or response that failed. */
private ?UriInterface $uri;

/**
* Creates a new client exception.
* @param string $message A message describing the error.
* @param UriInterface|null $uri The URL of the HTTP request or response that failed.
* @param \Throwable|null $previous The previous exception used for the exception chaining.
*/
function __construct(string $message, ?UriInterface $uri = null, ?\Throwable $previous = null) {
parent::__construct($message, 0, $previous);
$this->uri = $uri;
}
/**
* Creates a new client exception.
* @param string $message A message describing the error.
* @param UriInterface|null $uri The URL of the HTTP request or response that failed.
* @param \Throwable|null $previous The previous exception used for the exception chaining.
*/
function __construct(string $message, ?UriInterface $uri = null, ?\Throwable $previous = null) {
parent::__construct($message, 0, $previous);
$this->uri = $uri;
}

/**
* Gets the URL of the HTTP request or response that failed.
* @return UriInterface|null The URL of the HTTP request or response that failed.
*/
function getUri(): ?UriInterface {
return $this->uri;
}
/**
* Gets the URL of the HTTP request or response that failed.
* @return UriInterface|null The URL of the HTTP request or response that failed.
*/
function getUri(): ?UriInterface {
return $this->uri;
}
}

+ 190
- 190
src/Comment.php View File

@@ -7,203 +7,203 @@ use Psr\Http\Message\{UriInterface};
/** Represents a comment submitted by an author. */
class Comment implements \JsonSerializable {

/** @var Author|null The comment's author. */
private ?Author $author;
/** @var Author|null The comment's author. */
private ?Author $author;

/** @var string The comment's content. */
private string $content = '';
/** @var string The comment's content. */
private string $content = "";

/** @var \DateTimeInterface|null The UTC timestamp of the creation of the comment. */
private ?\DateTimeInterface $date = null;
/** @var \DateTimeInterface|null The UTC timestamp of the creation of the comment. */
private ?\DateTimeInterface $date = null;

/** @var UriInterface|null The permanent location of the entry the comment is submitted to. */
private ?UriInterface $permalink = null;
/** @var UriInterface|null The permanent location of the entry the comment is submitted to. */
private ?UriInterface $permalink = null;

/** @var \DateTimeInterface|null The UTC timestamp of the publication time for the post, page or thread on which the comment was posted. */
private ?\DateTimeInterface $postModified = null;
/** @var \DateTimeInterface|null The UTC timestamp of the publication time for the post, page or thread on which the comment was posted. */
private ?\DateTimeInterface $postModified = null;

/** @var string A string describing why the content is being rechecked. */
private string $recheckReason = '';
/** @var string A string describing why the content is being rechecked. */
private string $recheckReason = "";

/** @var UriInterface|null The URL of the webpage that linked to the entry being requested. */
private ?UriInterface $referrer = null;
/** @var UriInterface|null The URL of the webpage that linked to the entry being requested. */
private ?UriInterface $referrer = null;

/** @var string The comment's type. This string value specifies a `CommentType` constant or a made up value like `"registration"`. */
private string $type = '';
/** @var string The comment's type. This string value specifies a `CommentType` constant or a made up value like `"registration"`. */
private string $type = "";

/**
* Creates a new comment.
* @param Author|null $author The comment's author.
/**
* Creates a new comment.
* @param Author|null $author The comment's author.
n */
function __construct(?Author $author) {
$this->author = $author;
}
/**
* Creates a new comment from the specified JSON object.
* @param object $map A JSON object representing a comment.
* @return self The instance corresponding to the specified JSON object.
*/
static function fromJson(object $map): self {
$keys = array_keys(get_object_vars($map));
$hasAuthor = count(array_filter($keys, fn($key) => (bool) preg_match('/^(comment_author|user)/', $key))) > 0;
return (new self($hasAuthor ? Author::fromJson($map) : null))
->setContent(isset($map->comment_content) && is_string($map->comment_content) ? $map->comment_content : '')
->setDate(isset($map->comment_date_gmt) && is_string($map->comment_date_gmt) ? new \DateTimeImmutable($map->comment_date_gmt) : null)
->setPermalink(isset($map->permalink) && is_string($map->permalink) ? new Uri($map->permalink) : null)
->setPostModified(isset($map->comment_post_modified_gmt) && is_string($map->comment_post_modified_gmt) ? new \DateTimeImmutable($map->comment_post_modified_gmt) : null)
->setRecheckReason(isset($map->recheck_reason) && is_string($map->recheck_reason) ? $map->recheck_reason : '')
->setReferrer(isset($map->referrer) && is_string($map->referrer) ? new Uri($map->referrer) : null)
->setType(isset($map->comment_type) && is_string($map->comment_type) ? $map->comment_type : '');
}
/**
* Gets the comment's author.
* @return Author|null The comment's author.
*/
function getAuthor(): ?Author {
return $this->author;
}
/**
* Gets the comment's content.
* @return string The comment's content.
*/
function getContent(): string {
return $this->content;
}
/**
* Gets the UTC timestamp of the creation of the comment.
* @return \DateTimeInterface|null The UTC timestamp of the creation of the comment.
*/
function getDate(): ?\DateTimeInterface {
return $this->date;
}
/**
* Gets the permanent location of the entry the comment is submitted to.
* @return UriInterface|null The permanent location of the entry the comment is submitted to.
*/
function getPermalink(): ?UriInterface {
return $this->permalink;
}
/**
* Gets the UTC timestamp of the publication time for the post, page or thread on which the comment was posted.
* @return \DateTimeInterface|null The UTC timestamp of the publication time for the post, page or thread on which the comment was posted.
*/
function getPostModified(): ?\DateTimeInterface {
return $this->postModified;
}
/**
* Gets the string describing why the content is being rechecked.
* @return string A string describing why the content is being rechecked.
*/
function getRecheckReason(): string {
return $this->recheckReason;
}
/**
* Gets the URL of the webpage that linked to the entry being requested.
* @return UriInterface|null The URL of the webpage that linked to the entry being requested.
*/
function getReferrer(): ?UriInterface {
return $this->referrer;
}
/**
* Gets the comment's type. This string value specifies a `CommentType` constant or a made up value like `"registration"`.
* @return string The comment's type.
*/
function getType(): string {
return $this->type;
}
/**
* Converts this object to a map in JSON format.
* @return \stdClass The map in JSON format corresponding to this object.
*/
function jsonSerialize(): \stdClass {
$map = ($author = $this->getAuthor()) ? $author->jsonSerialize() : new \stdClass;
if (mb_strlen($content = $this->getContent())) $map->comment_content = $content;
if ($date = $this->getDate()) $map->comment_date_gmt = $date->format('c');
if ($postModified = $this->getPostModified()) $map->comment_post_modified_gmt = $postModified->format('c');
if (mb_strlen($type = $this->getType())) $map->comment_type = $type;
if ($permalink = $this->getPermalink()) $map->permalink = (string) $permalink;
if (mb_strlen($recheckReason = $this->getRecheckReason())) $map->recheck_reason = $recheckReason;
if ($referrer = $this->getReferrer()) $map->referrer = (string) $referrer;
return $map;
}
/**
* Sets the comment's content.
* @param string $value The new content.
* @return $this This instance.
*/
function setContent(string $value): self {
$this->content = $value;
return $this;
}
/**
* Sets the UTC timestamp of the creation of the comment.
* @param \DateTimeInterface|null $value The new UTC timestamp of the creation of the comment.
* @return $this This instance.
*/
function setDate(?\DateTimeInterface $value): self {
$this->date = $value;
return $this;
}
/**
* Sets the permanent location of the entry the comment is submitted to.
* @param UriInterface|null $value The new permanent location of the entry.
* @return $this This instance.
*/
function setPermalink(?UriInterface $value): self {
$this->permalink = $value;
return $this;
}
/**
* Sets the UTC timestamp of the publication time for the post, page or thread on which the comment was posted.
* @param \DateTimeInterface|null $value The new UTC timestamp of the publication time.
* @return $this This instance.
*/
function setPostModified(?\DateTimeInterface $value): self {
$this->postModified = $value;
return $this;
}
/**
* Sets the string describing why the content is being rechecked.
* @param string $value A string describing why the content is being rechecked.
* @return $this This instance.
*/
function setRecheckReason(string $value): self {
$this->recheckReason = $value;
return $this;
}
/**
* Sets the URL of the webpage that linked to the entry being requested.
* @param UriInterface|null $value The new URL of the webpage that linked to the entry.
* @return $this This instance.
*/
function setReferrer(?UriInterface $value): self {
$this->referrer = $value;
return $this;
}
/**
* Sets the comment's type.
* @param string $value The new type.
* @return $this This instance.
*/
function setType(string $value): self {
$this->type = $value;
return $this;
}
function __construct(?Author $author) {
$this->author = $author;
}
/**
* Creates a new comment from the specified JSON object.
* @param object $map A JSON object representing a comment.
* @return self The instance corresponding to the specified JSON object.
*/
static function fromJson(object $map): self {
$keys = array_keys(get_object_vars($map));
$hasAuthor = count(array_filter($keys, fn($key) => (bool) preg_match('/^(comment_author|user)/', $key))) > 0;
return (new self($hasAuthor ? Author::fromJson($map) : null))
->setContent(isset($map->comment_content) && is_string($map->comment_content) ? $map->comment_content : "")
->setDate(isset($map->comment_date_gmt) && is_string($map->comment_date_gmt) ? new \DateTimeImmutable($map->comment_date_gmt) : null)
->setPermalink(isset($map->permalink) && is_string($map->permalink) ? new Uri($map->permalink) : null)
->setPostModified(isset($map->comment_post_modified_gmt) && is_string($map->comment_post_modified_gmt) ? new \DateTimeImmutable($map->comment_post_modified_gmt) : null)
->setRecheckReason(isset($map->recheck_reason) && is_string($map->recheck_reason) ? $map->recheck_reason : "")
->setReferrer(isset($map->referrer) && is_string($map->referrer) ? new Uri($map->referrer) : null)
->setType(isset($map->comment_type) && is_string($map->comment_type) ? $map->comment_type : "");
}
/**
* Gets the comment's author.
* @return Author|null The comment's author.
*/
function getAuthor(): ?Author {
return $this->author;
}
/**
* Gets the comment's content.
* @return string The comment's content.
*/
function getContent(): string {
return $this->content;
}
/**
* Gets the UTC timestamp of the creation of the comment.
* @return \DateTimeInterface|null The UTC timestamp of the creation of the comment.
*/
function getDate(): ?\DateTimeInterface {
return $this->date;
}
/**
* Gets the permanent location of the entry the comment is submitted to.
* @return UriInterface|null The permanent location of the entry the comment is submitted to.
*/
function getPermalink(): ?UriInterface {
return $this->permalink;
}
/**
* Gets the UTC timestamp of the publication time for the post, page or thread on which the comment was posted.
* @return \DateTimeInterface|null The UTC timestamp of the publication time for the post, page or thread on which the comment was posted.
*/
function getPostModified(): ?\DateTimeInterface {
return $this->postModified;
}
/**
* Gets the string describing why the content is being rechecked.
* @return string A string describing why the content is being rechecked.
*/
function getRecheckReason(): string {
return $this->recheckReason;
}
/**
* Gets the URL of the webpage that linked to the entry being requested.
* @return UriInterface|null The URL of the webpage that linked to the entry being requested.
*/
function getReferrer(): ?UriInterface {
return $this->referrer;
}
/**
* Gets the comment's type. This string value specifies a `CommentType` constant or a made up value like `"registration"`.
* @return string The comment's type.
*/
function getType(): string {
return $this->type;
}
/**
* Converts this object to a map in JSON format.
* @return \stdClass The map in JSON format corresponding to this object.
*/
function jsonSerialize(): \stdClass {
$map = ($author = $this->getAuthor()) ? $author->jsonSerialize() : new \stdClass;
if (mb_strlen($content = $this->getContent())) $map->comment_content = $content;