freema/perspective-api-bundle
Symfony bundle for Google's Perspective API that scores text for toxicity and abusive language. Gives moderators a simple service to check comments and user-generated content, with configurable thresholds for filtering or flagging harmful posts in your app.
This package wraps Google's Perspective API (comment toxicity scoring) for PHP. Despite the -bundle name it targets Symfony's DI container, but the core service is plain PHP, so in Laravel you consume it as a small adapter rather than a full framework integration.
Minimal path:
Resources/config (or equivalent) service definition before writing anything — this tells you exactly which config keys map to which constructor arguments, and that's where the 1.2 feature lives.TOXICITY and log the result. Do not start by wiring it into moderation actions; start by observing score distributions on your real comment corpus.AppServiceProvider using a small config file (config/perspective.php) fed by env vars.As of 1.2, the service accepts custom HTTP client options. This is the headline change for Laravel users: you can now pass timeout, connect_timeout, proxy, and retry-related transport options through to the underlying HTTP client instead of being stuck with library defaults. Check the constructor signature after upgrading to confirm the exact argument name/shape — the release notes only say "custom HTTP client options," not the precise key.
Wrap, don't scatter. Create one thin ToxicityScorer (or similar) application service that owns the package service. Every caller goes through it, so when Google adds attributes or you swap to a queue, you change one file.
Score asynchronously by default. Perspective calls are network round-trips of a few hundred ms. On user-generated content paths, dispatch a job (ScoreComment, queued) and store scores on the model. Only call synchronously for pre-publish blocking flows, and then only with a short timeout.
Threshold, don't boolean. Perspective returns per-attribute probabilities. Persist the raw scores (TOXICITY, SEVERE_TOXICITY, IDENTITY_ATTACK, INSULT, PROFANITY, THREAT) in a JSON column and apply thresholds in your own code. Thresholds tuned per-surface (public comments vs. DMs vs. usernames) beat one global number, and storing raw scores lets you re-tune retroactively without re-calling the API.
Batch where possible. Prefer sending multiple comments per request when the package/API surface allows it; it directly reduces quota consumption.
Fail open with a marker. In a job, catch transport errors and record scored_at = null, score_error = .... Never let a moderation API outage silently 500 a comment submission. In synchronous blocking flows, decide explicitly whether unknown = allow or unknown = hold.
Use the new HTTP client options deliberately. Configure them in config/perspective.php and pass them through:
timeout ~2–3s for synchronous paths (Google's P99 can exceed this under load),connect_timeout ~1s,Cache aggressively. The same text (normalized: trimmed, lowercased where appropriate) yields the same score. A cache keyed on a hash of the text plus the requested attributes will absorb a large share of repeat content — spam, copypasta, and retried jobs especially.
Keep the key server-side. Put it in .env / a secret manager, exclude it from any client bundle, and never log request bodies containing it.
-bundle naming is a trap. Docs and examples are Symfony-flavored (services.yaml, autowiring, bundle registration). Translate, don't copy. In Laravel you instantiate the service manually or via a bound factory.TOXICITY score on a non-English comment means anything.TOXICITY is the composite, the others are components. Don't sum them and don't double-count — use TOXICITY for gating and the components for explanation/UI reasons.composer.json and re-read the constructor on every minor bump — this package's config surface is small and moving.How can I help you explore Laravel packages today?