respectify / respectify-php
PHP library for the Respectify API
Requires
- php: >=7.4
- guzzlehttp/psr7: ^2.7
- psr/http-message: ^1.0
- react/event-loop: ^1.1
- react/http: ^1.10
- react/promise: ^3.2
Requires (Dev)
- mockery/mockery: ^1.6
- phpunit/phpunit: ^11.4
- twig/twig: ^3.14.1
- vlucas/phpdotenv: ^5.6
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-14 11:24:44 UTC
README
PHP library for the Respectify API. Respectify aims to improve internet discussions: encourage healthy, understanding discussion in comments and replies.
Getting started
This contains the Respectify client, which is currently available only as an asychronous API using ReactPHP.
Check out src/RespectifyClientAsync.php. For info on the REST API that it wraps, have a look here.
If you are migrating from Google's Perspective API, use the dedicated Perspective compatibility sub-client:
$client->perspective()->analyzeComment([ 'comment' => ['text' => 'You clearly did not read the article.'], 'requestedAttributes' => [ 'TOXICITY' => new stdClass(), 'INSULT' => new stdClass(), ], ])->then(function ($result) { echo $result->attributeScores['TOXICITY']->summaryScore->value . "\n"; }); $client->run();
Comment health and moderation tolerances
Comment health is made of ten items: nine categories of problem (threat, identity_attack, sexual_content,
harassment, insult, profanity, dismissive_tone, logical_fallacy, low_effort) and quality. Each item has a
level: off, permissive, relaxed, standard (the default), strict or zero_tolerance. Nothing is saved on the API key: pass a
ModerationTolerances as the last argument of evaluateComment() or megacall() on every call. Items you do not name
are standard; only the items you name are sent.
Each item that is not off gets a score from 1 to 5. For a problem category, 5 means nothing was found; otherwise it
is the worst finding (minor 4, clear 3, severe 2, extreme 1). For quality, it is the quality score. Each level sets the lowest
score accepted:
| Level | Problem category | Quality |
|---|---|---|
off |
not scored, not coached, not counted | not scored |
permissive |
2 (filters only the very worst: minor, clear and severe findings accepted) | 2 |
relaxed |
3 (minor and clear findings accepted) | 2 |
standard |
4 (minor findings accepted) | 3 |
strict |
5 (no findings accepted) | 4 |
zero_tolerance |
5 (no findings accepted, and any hint gets a full assessment) | 5 |
Quality has only four distinct minimums, so permissive and relaxed share 2 there.
threat, identity_attack, sexual_content and harassment are floored: they can be made stricter but never
permissive, relaxed or off, and accept no finding at any level. ModerationTolerances rejects a floor violation (and any unknown
item or level) with a BadRequestException when it is constructed, so an invalid request is never sent.
HealthItem::allowedLevels() lists the levels an item accepts, for building a settings UI.
Use CommentScore::$exceedsTolerance to decide whether to act on a comment: it is true when any item scores below its
minimum, or the comment is severely toxic. The response also has:
healthItems: each item that is not off, by name, as aHealthItemScorewithscore,minimumandisAcceptable. The raw quality score is$score->healthItems['quality']->score.healthScore: comment health, 1.0 to 5.0 (the geometric mean of the quality score and the worst problem item's score).nullwhen every item is off.overallScore: comment health rounded to an integer, 1 to 5.
Older servers send neither healthItems nor healthScore; they read as [] and null. Findings an item accepts are
removed from the response. toxicityScore is not adjusted for tolerances. Each objectionable or negative tone phrase
also has a category and a severity (1 minor, 2 clear, 3 severe, 4 extreme), and each logical fallacy has a
severity. Severe is a slur or dehumanising language, a threat, sexual harassment, urging self-harm or sustained
abuse; extreme is the same at full force, and no level accepts it.
use Respectify\Moderation\HealthItem; use Respectify\Moderation\ModerationCategory; use Respectify\Moderation\ModerationTolerances; use Respectify\Moderation\ToleranceLevel; $tolerances = new ModerationTolerances([ ModerationCategory::PROFANITY => ToleranceLevel::RELAXED, ModerationCategory::LOGICAL_FALLACY => ToleranceLevel::OFF, ModerationCategory::THREAT => ToleranceLevel::ZERO_TOLERANCE, HealthItem::QUALITY => ToleranceLevel::STRICT, ]); $client->evaluateComment($articleId, $comment, null, $tolerances)->then(function ($score) { if ($score->exceedsTolerance) { foreach ($score->healthItems as $item => $result) { if (!$result->isAcceptable) { echo $item . ': ' . $result->score . ' (needs ' . $result->minimum . ")\n"; } } } }); $client->run();
Spam: spam, suspected spam, or neither
checkSpam() and megacall() (with the 'spam' service, as $result->spamCheck) return a SpamDetectionResult
with one of three outcomes:
isSpamtrue: spam. Delete or trash the comment.suspectedSpamtrue (andisSpamfalse): it looks like spam but is not certain. Do not delete it: send it back to the commenter withsuggestion(for example "This looks like spam. How about removing the link?"), the way you would a comment that needs revising for comment health. A real person will fix it; a spammer won't bother.- Neither: not spam. Publish it.
suggestion is empty unless suspectedSpam is true. A suspected comment is not spam, so in megacall() the other
checks still run for it and are returned as usual. Older servers send neither field; they read as false and ''.
$client->checkSpam($comment, $articleId)->then(function ($spam) { if ($spam->isSpam) { // delete it } elseif ($spam->suspectedSpam) { // send it back to the commenter, showing $spam->suggestion } else { // publish it } }); $client->run();
Errors and logging
API failures reject the promise with a RespectifyException subclass (BadRequestException, UnauthorizedException,
PaymentRequiredException, ServerException, JsonDecodingException, and so on).
getMessage() is safe to log. When the library builds a message from an API response, the message carries only the
kind of error, the HTTP status, the server's error title, and the server's error description when that description is
one of Respectify's known fixed strings, for example Unauthorized (HTTP 401): Authentication failed - Invalid user ID or API key.. It never contains a raw response body, an excerpt of JSON that failed to decode, or anything the server
echoed back from the request. That matters because responses can contain the user's comment text.
If you need the full detail while debugging, getResponseData() returns the parsed error JSON and getResponseBody()
returns the raw body. Both may contain user content: do not log them, store them, or show them to other users.
Developing
To be able to make changes:
$ brew install php $ brew install composer
If you want to run unit tests:
$ composer require --dev phpunit/phpunit mockery/mockery vlucas/phpdotenv
and then to run tests:
$ vendor/bin/phpunit --bootstrap vendor/autoload.php tests
Tests are run against mocks, but there is value in running against the real API. Do do this create a tests/.venv file, with content:
USE_REAL_API=true RESPECTIFY_EMAIL=you@example.com RESPECTIFY_API_KEY=your-api-key-here REAL_ARTICLE_ID=an-existing-article-id-here
using real Respectify credentials.
Docs
Docs are generated in Markdown format for Docusaurus via the phpdocumentor-markdown-customised phpdoc theme. It is MIT-licensed. Please see the readme in that folder for more information.
You'll need twig and php-documentor installed (install the dev dependencies.)
If you have a phpdoc command available (eg, set up as a Docker container):
$ alias phpdoc="docker run --rm -v \"$(pwd)\":/data phpdoc/phpdoc:3"
then you can general doc like so:
# Run phpDocumentor with --template argument pointed to markdown template $ phpdoc --directory=src --target=docs --template="phpdocumentor-markdown-customised/themes/markdown" --title="Respectify PHP Library" -c phpdoc.xml