Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Readability Laravel Package

j0k3r/php-readability

PHP library that extracts the main readable content and title from web pages. Based on an improved full-text-rss Readability fork, it offers namespaced code, simple tests, optional Tidy HTML cleanup, and PSR-3 logging for debugging parsing issues.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. This package is a pure, self-contained PHP library: it takes an HTML string plus a base URL, returns a DOMElement for title and content, and nothing else. That is a clean seam for a Laravel application — no service container assumptions, no HTTP client opinion, no storage opinion. It slots in as a single stateless-in-practice step inside a content pipeline you already own (fetch → extract → sanitize → persist → render). The Psr\Log\LoggerInterface injection point means extraction attempts can be traced through the same Monolog channel as the rest of the app, which is the difference between a debuggable extraction feature and a black box.

The Apache-2.0 license is the friendliest possible outcome for a commercial product: permissive, explicit patent grant, no copyleft contamination of your application code. The project is a re-namespaced fork of a fork (fivefilters → full-text-rss/Dither → j0k3r), which matters less legally than it does epistemically: the algorithm lineage is Mozilla Readability, synced at 2.0.0, and the maintainers have been explicitly backporting Mozilla behavior. Algorithmic parity with the browser's reader mode is a real, defensible product property.

Integration feasibility. Low. Composer install, one class, roughly six lines of glue. PHP >= 7.4 with CI coverage through 8.4 fits any modern Laravel release. The only non-PHP dependency is the optional Tidy extension, which is a Dockerfile line item, not an architecture decision. The 2.0.9 change making loadHtml() publicly callable independently of init() is quietly important for TPMs: it enables a two-phase workflow where you load/validate the document once and then retry extraction with different options, instead of paying full parse cost per attempt.

Technical risk.

Risk Severity Note
Maintainer concentration High Effectively one primary maintainer plus two regular contributors. 184 stars, 0 dependents — the ecosystem has not stress-tested this at scale.
Code comprehensibility Medium-High The README says the code "is still really hard to understand / read." That is an honest vendor admission and predicts expensive debugging.
Encoding correctness Medium Multiple releases (1.2.13, 2.0.7, 1.3.1) are charset-decoding and tidy_repair_string encoding fixes. Non-UTF-8 and mis-declared pages will be your top bug class.
Extraction completeness Medium init() returns false on failure by design. There is no confidence score, no partial result, no diagnostic payload beyond logs.
Output safety High if mishandled It strips <script>/<style> via clean(), but it is a readability extractor, not a sanitizer. Extracted HTML retains attributes and markup. It must be followed by a real sanitizer (HTMLPurifier, symfony/html-sanitizer, or mews/purifier) before it touches a browser.
Resource exhaustion Medium Full-document DOM construction on attacker- or user-supplied HTML. No size cap, no timeout, no depth guard. You must enforce these upstream.

Key questions for the team.

  1. What is the measurable success criterion — extraction success rate on a labeled corpus of your actual target domains, or just "it looks fine"? Pick a number (< 5% empty extraction on top 200 URLs) before committing.
  2. Do we run Tidy in production? It improves malformed-HTML handling but adds a system package and historically introduced encoding bugs. Decide and document; do not leave it to whichever base image happens to include it.
  3. Is this a user-facing product feature (reader mode) or an internal enrichment step (search indexing, summarization input)? The risk calculus differs enormously.
  4. Who owns the fork if upstream goes quiet? Name a person, not a team.
  5. What is the fallback when init() returns false — raw HTML, truncated text, or a "view original" link? The UX contract matters more than the library.

Integration Approach

Stack fit. Native Laravel fit, with three thin wrappers you should build rather than skip:

  • A ReadabilityExtractor service (singleton, but instantiated per document — the object is stateful across loadHtml/init) behind an interface, so swapping to a Node sidecar running mozilla/readability later is a config change, not a refactor.
  • A queued job (ExtractArticleContent) rather than inline execution. Extraction is CPU-bound and unpredictable; it does not belong in a request/response cycle.
  • A ContentExtraction value object returning title, sanitized HTML, plain text, word count, and a status enum — never let DOMElement leak into your domain layer.

Suggested pipeline: HTTP fetch (Laravel HTTP client, strict timeouts) → charset normalization → size guard (reject > ~5 MB) → Readability → sanitizer → persistence → cache.

Migration path. Assume you are replacing one of: (a) a paid extraction API (Mercury/Postlight, Diffbot), (b) a Node microservice, or (c) nothing — regex or naive strip_tags. In all three cases the migration is additive, not destructive: run the new extractor in shadow mode against a sample of live URLs, store both outputs, and diff. Only cut over when the success rate and output fidelity are demonstrably at or above the incumbent. Cost savings from killing a per-call API is the most likely business case and is easy to quantify.

Compatibility. PHP 7.4–8.4 verified by CI. psr/log 1.x/2.x/3.x all accepted, which means no conflict with Monolog 2 or 3. Parser is selectable (libxml default vs masterminds/html5); note that the HTML5 parser moved from the abandoned electrolinux/php-html5lib to masterminds/html5 in 1.2.5, so older lockfiles may need attention. Two live major lines (1.x and 2.x) receive parallel fixes — pin to ^2.0 and do not straddle.

Sequencing.

  1. Week 1 — Spike. CLI proof of concept against 50 representative URLs. Record success rate, wall time, peak memory, and the top failure signature. Kills or greenlights the initiative cheaply.
  2. Week 1–2 — Guardrails. Size limits, timeouts, sanitizer, SSRF protection on the fetch (block private IP ranges), structured logging with the PSR-3 hook.
  3. Week 2–3 — Shadow mode. Queue job writing to a side table; compare against incumbent. This is where you discover the encoding tail.
  4. Week 3–4 — Feature flag. 5% → 25% → 100%, with a "view original" escape hatch always present in the UI.
  5. Post-launch — Regression corpus. Snapshot the extraction output for your top 100 URLs; run it in CI. Extraction quality silently degrades as sites redesign, and this package will not tell you.

Operational Impact

Maintenance. Low code churn, but the dependency is a quality dependency, not a stability dependency: you are relying on someone else keeping the algorithm current as publishers change their markup. Release cadence is healthy (2.0.x maintained in parallel with 1.x, Dependabot enabled, PHPStan, php-cs-fixer, Rector, code coverage published), which is a good maintenance signal. The counterweight is the bus factor and 0 dependents — you are an early adopter, not a follower. Budget roughly one engineer-day per quarter for dependency bumps and extraction-quality triage, plus a standing contingency: fork to an internal namespace if upstream stalls. That fork is cheap precisely because the surface area is one class plus a handful of DOM helpers.

Support. Support burden will not come from crashes; it will come from "this article came out wrong" tickets. Mitigations: always log the source URL and a hash of the input HTML with the extraction result; keep the "original page" link permanently; expose a per-URL internal debug command that re-runs extraction with a Monolog DEBUG handler attached. Without these, every bad extraction becomes an unbounded investigation.

Scaling. Embarrassingly parallel and horizontally trivial — no shared state, no external I/O. Cost is CPU and memory per document: DOM construction dominates, and pathological pages (huge comment threads, deeply nested tables) can spike memory well past the input size. Size-cap at ingress and give queue workers generous memory_limit with a per-job guard. The real scaling lever is caching: extracted content is a pure function of (HTML, URL, options), so cache on a content hash and you convert a compute problem into a lookup. At meaningful volume, extraction cost becomes noise relative to the fetch.

Failure modes, ranked by likelihood.

  1. Silent empty extraction — init() returns false, job succeeds, user sees a blank reader view. Treat false as a typed failure, not a boolean, and have an explicit fallback UI.
  2. Mojibake — mangled accented characters from mis-declared charsets. Highest-frequency historical bug family; test with Latin-1, Shift-JIS, and meta[charset]-after-title pages.
  3. Partial extraction — the extractor returns the first paragraph or truncates mid-list on atypical layouts. Not detectable without a length heuristic; alert when extracted word count is < 20% of a naive text-length estimate.
  4. Tidy absence in production — works in dev, degrades on malformed HTML in prod, or vice versa. Pin the environment explicitly.
  5. Self-inflicted XSS — shipping unsanitized extracted HTML. This is your failure mode, not the package's. Make the sanitizer non-optional in the service, not a caller responsibility.
  6. Resource exhaustion — oversized or adversarial HTML in a queue worker. Cap size, cap depth, cap time.

Ramp-up. A competent Laravel engineer ships a working integration in half a day from the README alone. Understanding why an extraction failed takes considerably longer, because the internals are dense and undocumented beyond the usage snippet. Plan for one engineer to own this end-to-end — including the regression corpus and the fork contingency — rather than distributing it across a team where nobody accumulates the debugging intuition. The package's genuine strength is that its API is so small that the ramp-up cost is concentrated and bounded; its genuine weakness is that when it misbehaves, the code itself offers little help, so your observability has to compensate.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
encore2/laravel-admin2
cboxdk/laravel-console-kit
shapecode/doctrine-dbal-datetimeutc
gilads-otiannoh254/inertia-protocol
dakiquang/laravel-oauth-refresh-lock
woda/laravel-worktrees
dalefranks/ramasite
faghy/laracashbook
roundly-consulting/google-places-for-laravel
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
adachsoft/cli-process-tester
adachsoft/changelog-linter
adachsoft/collection
adachsoft/php-code-style
adachsoft/api-version-bundle
cgv/localized-resource