Download the PHP package rasuvaeff/yii3-seo without Composer
On this page you can find all versions of the php package rasuvaeff/yii3-seo. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download rasuvaeff/yii3-seo
More information about rasuvaeff/yii3-seo
Files in rasuvaeff/yii3-seo
Package yii3-seo
Short Description Next.js-inspired, Yii3-native typed SEO metadata: title templates, OpenGraph, Twitter cards, hreflang, canonical URLs, robots, JSON-LD and XML sitemaps
License BSD-3-Clause
Homepage https://github.com/rasuvaeff/yii3-seo
Informations about the package yii3-seo
rasuvaeff/yii3-seo
Next.js-inspired, Yii3-native typed SEO metadata. Describe a page with one
declarative Metadata object — title templates, OpenGraph, Twitter cards,
hreflang, canonical URL, robots directives, icons, verification and JSON-LD —
and a single MetadataDefaults instance supplies site-wide values. Tags land
in <head> automatically via WebViewRenderer. XML sitemaps are generated from
the same configured origin.
Using an AI coding assistant? llms.txt has a compact API reference ready to paste into context. Projects using the llm/skills Composer plugin also get this package's agent skill synced into
.agents/skills/automatically on install.
Requirements
- PHP 8.3+,
ext-filter,ext-mbstring,ext-xmlwriter yiisoft/html^3.13 || ^4.0yiisoft/view^12.0yiisoft/yii-view-renderer^7.4psr/http-message,psr/http-server-handler,psr/http-server-middleware(self-canonical middleware)psr/http-factory(sitemap responses)
Installation
Concept
The API brings the declarative style of the Next.js Metadata API to Yii3:
| Next.js | yii3-seo |
|---|---|
export const metadata = { ... } (page) |
new Metadata(...) dispatched per request |
layout metadata (defaults) |
MetadataDefaults in DI params |
title.template / default / absolute |
Title::template() / Title::absolute() |
alternates.canonical / languages |
Alternates |
openGraph / twitter |
OpenGraph + OgImage / TwitterCard |
metadataBase |
MetadataDefaults(metadataBase: ...) |
Defaults are merged with the page metadata: the title template wraps the page
title, OpenGraph/Twitter inherit unset fields, and relative URLs are resolved
against metadataBase.
Unlike Next.js, nested OpenGraph/Twitter values are merged field by field and social title, description and image fallbacks are enabled by default. These Yii3-native rules reduce repetition while explicit page values always win.
Quickstart
Two configuration edits, one dispatch and two lines in the layout.
1. Site-wide defaults — config/common/params.php
2. Add the injection to the view renderer — config/common/di.php
3. Describe the page — in an action
That is the whole integration. A request for
https://example.com/products/awesome?utm_source=mail renders:
og:title/og:description fell back to the page title and description,
twitter:* fell back to OpenGraph, the canonical URL came from the request path
with the tracking parameter stripped, and the layout only had to print
<title> and the JSON-LD block (step 4).
What the package wires for you
| Element | Mechanism |
|---|---|
| Meta and link tags | SeoInjection through WebViewRenderer injection interfaces (step 2) |
SeoMetadataEvent handler |
config/events-web.php, auto-registered through yiisoft/config — no events-web.php entry in the application |
$seo layout parameter |
LayoutParametersInjectionInterface, so the layout needs no object injection |
| Reset between requests | config/di.php wires SeoInjection::reset(), so nothing leaks in RoadRunner or similar runtimes |
HttpApplicationRunner reads the events-web group with RecursiveMerge, so
these listeners merge with the application's own and with other packages'
listeners for the same event instead of colliding.
Upgrading from 1.0.x: remove the SeoMetadataEvent entry from the
application's own config/common/events-web.php. Merged listeners stack rather
than deduplicate, so keeping it invokes the handler twice per dispatch. That is
harmless today — setMetadata() re-resolves the same input — but it is dead
configuration.
4. Print title and JSON-LD — in the layout
WebViewRenderer has injection interfaces for meta and link tags only, so these
two elements are rendered from the automatically injected $seo parameter:
Public API
Metadata
Immutable declarative object (all fields optional). A string title is
normalized to Title::of().
| Field | Type | Renders |
|---|---|---|
title |
string\|Title |
<title> (template applied) |
description |
string |
<meta name="description"> |
keywords |
list<string> |
<meta name="keywords"> |
authors |
list<Author> |
<meta name="author"> + <link rel="author"> |
applicationName, generator, creator, publisher |
string |
matching <meta name> |
themeColor, colorScheme |
string |
theme-color, color-scheme |
robots |
Robots |
<meta name="robots"> / googlebot |
alternates |
Alternates |
canonical + hreflang links |
openGraph |
OpenGraph |
og:* |
twitter |
TwitterCard |
twitter:* |
icons |
Icons |
<link rel="icon"> etc. |
manifest |
string |
<link rel="manifest"> |
verification |
Verification |
verification <meta> |
jsonLd |
list<JsonLd> |
<script type="application/ld+json"> |
other |
list<MetaTag> |
custom <meta> |
MetadataDefaults
Site-wide defaults: metadataBase, title (template/default),
applicationName, generator, themeColor, colorScheme, robots,
openGraph, twitter, icons, verification, jsonLd, other. Provide via
the rasuvaeff/yii3-seo → defaults parameter.
MetadataResolver + ResolvedMetadata
MetadataResolver is the single source of truth for defaults, title templates
and social fallback rules. It returns an immutable ResolvedMetadata that can
be inspected independently of Yii rendering:
Configured relative crawler-facing URLs remain relative in
ResolvedMetadata; rendering resolves them against metadataBase. Normally
applications obtain the current result through
SeoInjection::getResolvedMetadata().
ResolvedMetadata::toArray() exports the same result as a normalized array for
JSON APIs, SPA payloads, preview tooling and debugging:
| Rule | Behavior |
|---|---|
| Crawler-facing URLs | Canonical, hreflang, og:url and images are resolved against metadataBase, exactly as rendering resolves them |
| Icons and manifest | Exported as configured, matching the rendered <link> tags |
Relative URL without metadataBase |
Throws InvalidArgumentException, the same failure rendering would produce |
| Empty values | null values and empty collections are omitted; title is always present |
| Defaults | openGraph.type defaults to website and twitter.card to summary_large_image, as in the rendered head |
MetadataValidator
MetadataValidator inspects a ResolvedMetadata and reports typed issues. It
never throws and never modifies metadata, so it is safe to run in a development
toolbar, a preview page or a CI check:
Each MetadataIssue carries a MetadataIssueSeverity (Error or Warning), a
stable machine-readable code for filtering, and a human-readable message.
| Code | Severity | Reported when |
|---|---|---|
title.missing |
Error | The resolved title is empty |
title.too_long |
Warning | The title exceeds 60 characters |
description.missing |
Warning | No meta description is set |
description.too_short |
Warning | The description is shorter than 50 characters |
description.too_long |
Warning | The description exceeds 160 characters |
canonical.missing |
Warning | No canonical URL is set |
canonical.og_url_mismatch |
Error | The canonical URL and og:url resolve to different URLs |
url.unresolvable |
Error | A crawler-facing URL is invalid or relative without a metadataBase |
image.missing |
Warning | Neither an Open Graph nor a Twitter image is set |
image.alt_missing |
Warning | An Open Graph image has no alt text |
image.dimensions_missing |
Warning | An Open Graph image has no width and height |
robots.conflicting |
Error | robots/googlebot carry contradictory directives such as index and noindex |
other.duplicate |
Warning | The same custom meta tag is declared twice |
Lengths are advisory: they never make the result invalid.
Title
| Factory | Use |
|---|---|
Title::of('Home') |
page title, template applied |
Title::absolute('Home') |
page title, template bypassed |
Title::template('%s | Acme', default: 'Acme') |
defaults: template + fallback |
Alternates
Locales match /^(?:[a-z]{2}(?:-[A-Z]{2})?|x-default)$/.
SelfCanonical + SelfCanonicalMiddleware
Opt-in: derive the canonical URL of pages that do not declare one from
metadataBase plus the current request path.
A request for https://example.com/products/1?page=2&utm_source=mail then
renders <link rel="canonical" href="https://example.com/products/1?page=2">.
| Rule | Behavior |
|---|---|
| Request authority | Ignored. Only the path and query are read, so a request on an alternative host, port or scheme still yields the configured site's URL |
| Query parameters | Dropped by default. SelfCanonical::keepingQuery(...) keeps an explicit allow-list |
| Parameter order | The allow-list order, not the request order, so ?sort=a&page=1 and ?page=1&sort=a produce the same canonical URL |
| Array-valued parameters | Never enter a canonical URL (?tag[]=a is dropped even if tag is allow-listed) |
Explicit Alternates::canonical |
Always wins; hreflang languages declared by the page are preserved |
metadataBase |
Required — MetadataDefaults throws when selfCanonical is set without it |
| Without the middleware | No request path is recorded and the policy does nothing |
SelfCanonicalMiddleware calls SeoInjection::setRequestPath(); it works
regardless of whether the action sets page metadata before or after it.
OpenGraph + OgImage
TwitterCard
Robots
| Factory / method | Directive |
|---|---|
Robots::index() |
index, follow |
Robots::noindex() / nofollow() / none() / noarchive() |
matching directives |
new Robots(['noindex', 'nosnippet']) |
custom combination |
->withNoSnippet() / ->withNoImageIndex() |
append directive |
->withMaxSnippet(-1) / ->withMaxImagePreview('large') / ->withMaxVideoPreview(30) |
Google max-* |
->withGoogleBot('noindex', ...) |
separate <meta name="googlebot"> |
Icons / Icon, Verification, Author
MetaTag
| Factory | Attribute |
|---|---|
MetaTag::name(name, content) |
name="..." |
MetaTag::property(property, content) |
property="..." |
MetaTag::httpEquiv(httpEquiv, content) |
http-equiv="..." |
JsonLd
Renders as <script type="application/ld+json"> with JSON_HEX_TAG to prevent
</script> injection.
SeoInjection
Singleton registered in DI. Implements LayoutParametersInjectionInterface,
MetaTagsInjectionInterface and LinkTagsInjectionInterface. The package DI
config also registers a service reset hook, so stale per-request metadata is
cleared between requests in reusable runtimes.
| Method | Description |
|---|---|
setMetadata(Metadata) |
Set metadata for the current request |
clear() |
Reset (useful in tests) |
getLayoutParameters(): array |
Exposes the injection to layouts as $seo |
getResolvedMetadata(): ResolvedMetadata |
Fully merged logical metadata |
getTitle(): string |
Resolved title for <title> |
getMetaTags(): list<Meta> |
Called by WebViewRenderer |
getLinkTags(): array<Link> |
Called by WebViewRenderer |
getJsonLdHtml(): string |
Rendered JSON-LD <script> blocks |
Sitemaps
The sitemap side of the package is independent of the head metadata: it needs
nothing but a source of URLs and the same metadataBase. The package never
crawls the site — the application always says what belongs in the sitemap.
SitemapUrl
| Argument | Type | Notes |
|---|---|---|
loc |
string |
Required. Absolute, or relative and resolved against metadataBase |
lastModified |
?DateTimeImmutable |
Rendered as <lastmod> in W3C datetime format |
changeFrequency |
?ChangeFrequency |
Always…Never; a hint crawlers may ignore |
priority |
?float |
0.0–1.0, rendered with one decimal |
images |
SitemapImage[] |
<image:image> entries |
alternates |
array<string, string> |
locale => url, rendered as <xhtml:link rel="alternate"> |
Locales use the same rules as Alternates: de, de-DE or x-default.
SitemapProviderInterface
Yield instead of returning an array: every consumer below pulls one URL at a time, so a provider backed by a database cursor never materialises its result set.
Sitemap and SitemapIndex
Both implement SitemapDocumentInterface and emit the document as chunks:
A document built from a Generator can be emitted once; pass an array to make
it re-renderable. SitemapIndex takes SitemapIndexEntry objects (loc plus an
optional lastModified) and produces a <sitemapindex>.
SitemapFileExporter
Writes a URL stream to disk, enforcing both protocol limits — 50 000 URLs and 50 MiB uncompressed — by measuring each rendered entry before appending it:
The layout depends only on the URL stream:
| URLs | Files written |
|---|---|
| Fit into one file | sitemap.xml — a plain <urlset> |
| Do not fit | sitemap-1.xml … sitemap-N.xml plus a sitemap.xml index |
export() returns the written file paths in write order. Nothing is deleted:
chunk files left by an earlier, larger export stay on disk and the fresh index
never references them. A single URL that cannot fit into the byte limit throws
InvalidArgumentException rather than producing an over-limit file — the chunk
opened at that moment is left on disk without its closing tag, so treat a failed
export as one to re-run, not as a partial result to publish.
publicPath must match where the written files are actually served from:
exporting into /var/www/public/sitemaps while leaving publicPath at /
produces an index pointing at https://example.com/sitemap-1.xml, which 404s.
Nothing can check this for you — the exporter never sees the URL the directory
is published under.
SitemapFileExporter is wired in DI and inherits metadataBase from the
configured MetadataDefaults; publicPath comes from the
rasuvaeff/yii3-seo → sitemap → publicPath parameter.
SitemapResponseFactory
Serves a document from a route. The body is a php://temp stream, so a 50 MiB
sitemap does not cost 50 MiB of PHP memory:
It needs a PSR-17 ResponseFactoryInterface and StreamFactoryInterface, both
of which a Yii3 application already has in its container.
robots.txt
RobotsTxt is a typed document, not a template: every user agent and path is
validated, and a value carrying a control character is rejected rather than
written, so configuration can never forge an extra Disallow: / line.
| Rule | Detail |
|---|---|
| Paths | Must start with / or * |
| Group without rules | Renders the empty Disallow: line — the protocol's "nothing is restricted" |
| Sitemap URLs | Resolved against metadataBase, like every other crawler-facing URL |
| Order | User-agent, Allow, Disallow, Crawl-delay; sitemaps last |
Serving it
RobotsTxtAction is a PSR-15 handler; both dependencies come from the
container, so the route is the whole integration:
The document it serves is bound in DI from parameters:
The package never inspects the environment. When indexable is false the
bound document is RobotsTxt::disallowAll() — every crawler blocked, no sitemap
advertised — regardless of what robots contains. A wrong guess here would
either de-index production or expose staging, so the decision is always the
application's, stated explicitly. RobotsTxtResponseFactory is available
directly if a route needs to build the document per request.
X-Robots-Tag
Responses without a <head> — generated PDFs, images, exports — carry the same
policy as a header. Robots::toHeaderValues() returns one value per header
line:
Security
- Crawler-facing URLs (canonical, hreflang,
og:image,og:url,twitter:image, and every sitemap URL) are resolved againstmetadataBase; absolute URLs are validated withFILTER_VALIDATE_URL. A relative URL with no base throwsInvalidArgumentException. - HTML escaping is handled by
Yiisoft\Html— no raw string concatenation. - Sitemap XML escaping is handled by
XMLWriter; only the fixed document header and footer are literals, and they carry no input. robots.txtvalues are rejected when they contain a control character, so configuration cannot forge an extra directive line. Whether a site may be indexed is never guessed from the environment — the application passes it in.- JSON-LD uses
JSON_HEX_TAGto prevent</script>injection.
Examples
See examples/ for runnable scripts and a Yii3 integration sketch:
examples/yii3-app.php. Sitemap generation and file
export: examples/sitemap.php; crawl policy:
examples/robots-txt.php.
See ROADMAP.md for planned curated structured-data builders.
Development
License
BSD-3-Clause. See LICENSE.md.
All versions of yii3-seo with dependencies
ext-filter Version *
ext-mbstring Version *
ext-xmlwriter Version *
psr/http-factory Version ^1.1
psr/http-message Version ^1.1 || ^2.0
psr/http-server-handler Version ^1.0
psr/http-server-middleware Version ^1.0
yiisoft/html Version ^3.13 || ^4.0
yiisoft/yii-view-renderer Version ^7.4