Helper
in package
埋め込み対象 URL から OGP / oEmbed のメタ情報を取得する。
ユニット・ブロックエディタなど複数の入口から同じ経路で取得できるようサービスにしている。 取得結果の保存先(カラム構成)は呼び出し側の責務で、ここでは扱わない。
どのサービスの埋め込みを解決できるかは ProviderRegistry が持ち、本クラスは 「キャッシュ」「サニタイズ」「表示のための整形」という、プロバイダーに依らない部分だけを担う。
Table of Contents
Constants
- PREVIEW_HTML_MAX_BYTES = 102400
- プレビュー用に再サニタイズを受け付ける HTML の上限(バイト)
- FALLBACK_EMBED_HEIGHT = 152
- 比率を計算できない iframe に与える高さ(px)
Properties
- $cacheAdapter : AdapterInterface|null
- フィールドキャッシュのアダプタ
- $previewScriptPolicy : ScriptPolicy|null
- 編集画面のプレビューで実行してよい外部 script の許可ホスト
- $providerRegistry : ProviderRegistry|null
- 埋め込みを解決するプロバイダーのレジストリ
- $sanitizer : callable(string): string|null
- 埋め込み HTML のサニタイザ
Methods
- __construct() : mixed
- fallbackIframeTitle() : string
- iframe に補完する title 属性値を、oEmbed のタイトル → 提供元名 → 汎用ラベルの優先順で決める。
- fetch() : EmbedData
- URL からメタ情報を取得する
- fetchWithoutCache() : EmbedData
- キャッシュを使わずに取得する
- fetchWithRetryOnEmptyCache() : EmbedData
- URL からメタ情報を取得する。キャッシュヒットが「前回取得失敗(空)」だった場合に限り、 キャッシュを無視して再取得する。
- forget() : void
- 指定 URL のキャッシュを破棄する
- isResolvableUrl() : bool
- 埋め込みの取得対象として受け付ける URL か判定する
- normalizeIframes() : string
- サニタイズ済みの iframe を、表示・アクセシビリティの両面で確定した形へ整える。
- sanitizeEmbedHtml() : string
- 埋め込み HTML を「保存してよい形」に確定させる。
- sanitizeForPreview() : string
- 保存済みの埋め込み HTML を、編集画面のプレビューで実行してよい形に確定させる。
- resolve() : EmbedData
- 外部から取得する
- applyResponsiveStyle() : bool
- iframe の固定寸法をアスペクト比へ移し替える。
- cache() : AdapterInterface
- フィールドキャッシュのアダプタを取得する
- cacheKey() : string
- ensureIframeLoading() : bool
- iframe に loading="lazy" が無ければ補完する。
- ensureIframeTitle() : bool
- iframe に非空 title が無ければ補完する。
- fallbackHeight() : bool
- 比率へ変換できない iframe の高さを、潰れない値へ退避させる。
- finalize() : EmbedData
- プロバイダーが返した結果を、保存してよい形へ確定させる。
- hasMeaningfulEmbedTag() : bool
- サニタイズ後の HTML に、埋め込みとして意味のある要素が残っているか判定する。
- previewScriptPolicy() : ScriptPolicy
- プレビューで実行してよい外部 script の許可ホストを取得する
- providers() : ProviderRegistry
- 埋め込みプロバイダーのレジストリを取得する
- purify() : string
- 外部から取得した埋め込み HTML をサニタイズする
- removeScriptsNotAllowedBy() : string
- サニタイズ済みの HTML から、src が許可ホストに一致しない script 要素を取り除く。
- removeSrclessScriptTags() : string
- src を持たない script 要素を取り除く。
- resolveAndCache() : EmbedData
- 取得してキャッシュへ保存する
Constants
PREVIEW_HTML_MAX_BYTES
プレビュー用に再サニタイズを受け付ける HTML の上限(バイト)
public
mixed
PREVIEW_HTML_MAX_BYTES
= 102400
oEmbed が返す埋め込み HTML は大きなもの(インライン SVG を含む Instagram 等)でも 数十 KB に収まる。それを大きく超える入力は埋め込みとして不自然なため、 HtmlPurifier に渡す前に断り、サニタイズのコストを入力の大きさで膨らませない。
FALLBACK_EMBED_HEIGHT
比率を計算できない iframe に与える高さ(px)
private
mixed
FALLBACK_EMBED_HEIGHT
= 152
width="100%" / height="100%" のような相対指定を返すプロバイダ(Spotify 等の音声系)向け。 この種の埋め込みは高さが固定のプレイヤー UI であることが多く、実測でおおむね収まる値。
Properties
$cacheAdapter
フィールドキャッシュのアダプタ
private
AdapterInterface|null
$cacheAdapter
$previewScriptPolicy
編集画面のプレビューで実行してよい外部 script の許可ホスト
private
ScriptPolicy|null
$previewScriptPolicy
$providerRegistry
埋め込みを解決するプロバイダーのレジストリ
private
ProviderRegistry|null
$providerRegistry
$sanitizer
埋め込み HTML のサニタイザ
private
callable(string): string|null
$sanitizer
Methods
__construct()
public
__construct([callable(string): string|null $sanitizer = null ][, AdapterInterface|null $cacheAdapter = null ][, ProviderRegistry|null $providerRegistry = null ][, ScriptPolicy|null $previewScriptPolicy = null ]) : mixed
Parameters
- $sanitizer : callable(string): string|null = null
-
埋め込み HTML のサニタイザ。 省略時は HtmlPurifier サービス(config の
html_purifier_iframe_allowed_hostsに従う)。 テストからグローバル状態に触れずに差し替えられるよう引数で受け取る。 - $cacheAdapter : AdapterInterface|null = null
-
フィールドキャッシュのアダプタ。 省略時は
Cache::field()(ファサード)。$sanitizer と同じ理由でテストから注入できるようにする。 - $providerRegistry : ProviderRegistry|null = null
-
埋め込みプロバイダーのレジストリ。 省略時はコンテナの
embed.providers。上と同じ理由でテストから注入できるようにする。 - $previewScriptPolicy : ScriptPolicy|null = null
-
プレビューで実行してよい外部 script の許可ホスト。 省略時は config の
block_editor_embed_preview_script_allowed_hosts(とextends_側)。 上と同じ理由でテストから注入できるようにする。
fallbackIframeTitle()
iframe に補完する title 属性値を、oEmbed のタイトル → 提供元名 → 汎用ラベルの優先順で決める。
public
static fallbackIframeTitle(string $title, string $providerName) : string
Why not 提供元名だけを入れないか: title="YouTube" では「YouTube という名前のフレーム」
までしか伝わらず、そこに何が埋め込まれているのかが支援技術の利用者に届かない。img の alt と
同じ発想で、種別を表す語を添えて読み上げが文として成立する形にする。
Why not private: 保存時(normalizeIframes)と、保存済み HTML の出力時 (EmbedFunctionTagHandler)の両方が同じ文言・ 同じ優先順を使う必要があるため、文言の単一の情報源としてここに公開する。
Parameters
- $title : string
-
oEmbed から取得したタイトル
- $providerName : string
-
oEmbed から取得したプロバイダ名
Return values
stringfetch()
URL からメタ情報を取得する
public
fetch(string $url) : EmbedData
同じ URL の再取得を避けるためフィールドキャッシュを経由する。取得に失敗した場合も 空の結果をキャッシュして、保存のたびに毎回外部へ取りに行かないようにする。
Parameters
- $url : string
-
取得対象の URL
Return values
EmbedData —取得できなかった項目は空文字
fetchWithoutCache()
キャッシュを使わずに取得する
public
fetchWithoutCache(string $url) : EmbedData
Parameters
- $url : string
Return values
EmbedDatafetchWithRetryOnEmptyCache()
URL からメタ情報を取得する。キャッシュヒットが「前回取得失敗(空)」だった場合に限り、 キャッシュを無視して再取得する。
public
fetchWithRetryOnEmptyCache(string $url) : EmbedData
fetch() の 24 時間キャッシュは巡回的な保存(オートセーブ等)でのサーバ負荷対策として 妥当だが、ユーザーがブロックエディタで URL を明示的に再確定した操作(一時的な障害からの 回復を期待している)にまで適用すると、確定し直しても 24 時間失敗し続けてしまう。 非空のキャッシュ(取得成功)はそのまま尊重し、毎回律儀に再取得はしない。
Parameters
- $url : string
-
取得対象の URL
Return values
EmbedData —取得できなかった項目は空文字
forget()
指定 URL のキャッシュを破棄する
public
forget(string $url) : void
Parameters
- $url : string
isResolvableUrl()
埋め込みの取得対象として受け付ける URL か判定する
public
isResolvableUrl(string $url) : bool
編集画面の取得(ACMS_GET_Admin_BlockEditor_EmbedJson)と入稿用タグの解決 (BlockEditor\Authoring\AuthoringConverter)で同じ基準を使うため、ここに置く。
Why not: REGEX_VALID_URL だけで済ませないのは、この定数が ftp スキームも許すため。 埋め込みで扱うのは http(s) だけに限る(SSRF 対策の本体は SsrfGuardedClient が担う)。
Parameters
- $url : string
Return values
boolnormalizeIframes()
サニタイズ済みの iframe を、表示・アクセシビリティの両面で確定した形へ整える。
public
normalizeIframes(string $html, string $title, string $providerName) : string
- 固定寸法(width="560" height="315" 等)をアスペクト比へ変換し、コンテナ幅に追従させる。 oEmbed が返す固定ピクセルのままでは、表示サイズ(data-width)を広げても提供元の実寸のままになり、 CSS の max-width では縮小しかできない(拡大されない)。
- 非空の title が無ければ oEmbed のタイトル・プロバイダ名の順で補完する(WCAG 2.4.1 / H64: フレームには判別可能なタイトルが必要。プロバイダが title を省略することが多いため)。
- loading="lazy" を補完し、ビューポートに入るまで外部の埋め込みを取得しないようにする。
サニタイズはしない。信頼できない HTML は sanitizeEmbedHtml() を通すこと (sanitizeEmbedHtml() は最後にこのメソッドを呼ぶ)。
Why not private: 保存時と、保存済み HTML の出力時 (EmbedFunctionTagHandler)で同じ正規化を 重複実装せず、変換規則の単一の情報源をここに保つため。
Parameters
- $html : string
-
サニタイズ済みの埋め込み HTML
- $title : string
-
oEmbed から取得したタイトル
- $providerName : string
-
oEmbed から取得したプロバイダ名
Return values
stringsanitizeEmbedHtml()
埋め込み HTML を「保存してよい形」に確定させる。
public
sanitizeEmbedHtml(string $html[, string $title = '' ][, string $providerName = '' ]) : string
埋め込み先の許可はここで独自に持たず、iframe のホワイトリスト
(config の html_purifier_iframe_allowed_hosts)を単一の情報源として HtmlPurifier に委ねる。
許可外ホストは src ごと落ちるため、実体の無くなった HTML は空文字に倒して
呼び出し側が OGP カード / 通常リンクへ退避できるようにする。
取得時(エディターへ返す時点)と出力時(公開側の描画時)の両方で同じ規則を通す。 出力時の再サニタイズは EmbedFunctionTagHandler::render() が担う。
Why not 出力時だけに寄せないか: 編集中のプレビューや保存 HTML に、許可外の HTML が
一時的にでも載らないようにするため。ブロックエディターの保存 HTML は PHP 側で
サニタイズされず(Services/Unit/Models/BlockEditor.php)、エディター上の表示は
取得結果そのものなので、取得時にも確定させておく。
Why not 取得時だけで済ませないか: 保存 HTML は API・インポート・ソース編集等からも
書き換えられ、取得時の確定を経ていない値が混ざりうるため、出力時にも同じ規則を通す。
Parameters
- $html : string
-
oEmbed 等から得た生の埋め込み HTML
- $title : string = ''
-
oEmbed から取得したタイトル。iframe に title が無いときの補完に使う
- $providerName : string = ''
-
oEmbed から取得したプロバイダ名。title が空のときの次点の補完に使う
Return values
string —採用できる HTML。採用できない場合は空文字
sanitizeForPreview()
保存済みの埋め込み HTML を、編集画面のプレビューで実行してよい形に確定させる。
public
sanitizeForPreview(string $html[, string $title = '' ][, string $providerName = '' ]) : string
規則は公開側の出力(EmbedFunctionTagHandler::render())
と同じ sanitizeEmbedHtml() を通したうえで、さらに外部 script をプレビュー用の許可ホスト
(block_editor_embed_preview_script_allowed_hosts)に一致するものだけに絞る。プレビューで動くのは
公開側の許可ホストとプレビュー用の許可ホストの積集合だけになる。外部サービスへの再取得はしない。
Why not 公開側の許可ホストをそのまま使わないか: 公開側にはタグマネージャー・解析・MA 系の script も並ぶ。投稿者が自分のコンテナ ID を指定した script を埋め込むと、管理者がプレビューを 開いた時点で管理画面のオリジンで任意の JS を動かせてしまうため、埋め込みウィジェットに限る。
Why not プレビューを保存済みの HTML のまま描画しないか: プレビューは提供元のウィジェットを 動かすため管理画面と同じオリジンで描画する。保存済みの値は貼り付け・改ざんで任意の HTML になりうるので、サーバーで確定させた結果だけを描画させる。
Parameters
- $html : string
-
保存済みの埋め込み HTML
- $title : string = ''
-
iframe に title が無いときの補完に使うタイトル
- $providerName : string = ''
-
title が空のときの次点の補完に使うプロバイダ名
Return values
string —プレビューに使える HTML。上限超過・採用できない場合は空文字
resolve()
外部から取得する
protected
resolve(string $url) : EmbedData
Why not: 拡張ポイント(extendsEmbedUnit 等)をここで発火させないのは、フックが 「埋め込みユニットの」拡張として名付けられており、ブロックエディタなど別の入口から 呼ばれたときにも発火してしまうため。発火はユニットモデル側に置く。
Why not private: fetch() / fetchWithRetryOnEmptyCache() のキャッシュ判定ロジックを 実ネットワーク接続(SsrfGuardedClient 経由)無しでテストするため、protected にして テストダブル(無名サブクラス)から差し替えられるようにする。
Parameters
- $url : string
Return values
EmbedDataapplyResponsiveStyle()
iframe の固定寸法をアスペクト比へ移し替える。
private
applyResponsiveStyle(DOMElement $iframe) : bool
Parameters
- $iframe : DOMElement
Return values
bool —変換したら true(比率を計算できない場合は触らず false)
cache()
フィールドキャッシュのアダプタを取得する
private
cache() : AdapterInterface
Return values
AdapterInterfacecacheKey()
private
cacheKey(string $url) : string
Parameters
- $url : string
Return values
stringensureIframeLoading()
iframe に loading="lazy" が無ければ補完する。
private
ensureIframeLoading(DOMElement $iframe) : bool
Why not 寸法が確定した iframe だけに限らないか: WordPress は width / height が両方 揃った iframe にしか lazy を付けない(寸法不明のまま遅延させると、ビューポートに入った 瞬間にレイアウトシフトが起きるため)。当サービスは applyResponsiveStyle が aspect-ratio かフォールバックの height で必ず領域を確保するので、その前提を常に満たす。
Why not ファーストビューを除外しないか: WordPress は LCP 悪化を避けるため本文先頭の 要素を除外するが、iframe は LCP の候補要素ではなく(iframe 内の画像は親フレームの LCP に 数えられない)、また展開時に本文中の位置が分からないため、一律で付与する。
Parameters
- $iframe : DOMElement
Return values
bool —補完したら true(プロバイダ指定の loading を尊重した場合は false)
ensureIframeTitle()
iframe に非空 title が無ければ補完する。
private
ensureIframeTitle(DOMElement $iframe, string $title, string $providerName) : bool
プロバイダが title を省略した iframe は、スクリーンリーダー利用者に 「フレーム、名前なし」としか伝わらない(WCAG 2.4.1 達成方法 H64)。
Parameters
- $iframe : DOMElement
- $title : string
-
oEmbed から取得したタイトル
- $providerName : string
-
oEmbed から取得したプロバイダ名
Return values
bool —補完したら true(既存の title を尊重した場合は false)
fallbackHeight()
比率へ変換できない iframe の高さを、潰れない値へ退避させる。
private
fallbackHeight(DOMElement $iframe) : bool
iframe { width: 100% } を当てる CSS 下では、height="100%" のような相対指定は
高さ auto の親に対して 0 になる。style に固定高さが既に書かれている場合は
提供元の意図を尊重して触らない。
Parameters
- $iframe : DOMElement
Return values
bool —変更したら true
finalize()
プロバイダーが返した結果を、保存してよい形へ確定させる。
private
finalize(EmbedData $data) : EmbedData
HTML のサニタイズを必ずここで通すことで、プロバイダーはサニタイズを迂回できない。
拡張アプリが埋め込み先のホストを許可するには、プロバイダーの登録とは別に
extends_html_purifier_iframe_allowed_hosts 等へホストを足す必要がある
(「誰が解決するか」と「何を出力してよいか」を別の権限として分ける)。
Parameters
- $data : EmbedData
-
プロバイダーが返した生の結果
Return values
EmbedDatahasMeaningfulEmbedTag()
サニタイズ後の HTML に、埋め込みとして意味のある要素が残っているか判定する。
private
hasMeaningfulEmbedTag(string $html) : bool
iframe / script は src を失うと埋め込みとして機能しないので「残っている」とみなさない。 script を含めるのは、Threads や Bluesky のように blockquote の中身が空で、ウィジェットの script が後から描画する形式があるため(strip_tags だけでは「空」と判定されてしまう)。
Parameters
- $html : string
Return values
boolpreviewScriptPolicy()
プレビューで実行してよい外部 script の許可ホストを取得する
private
previewScriptPolicy() : ScriptPolicy
extends_block_editor_embed_preview_script_allowed_hosts(拡張ポイント)をコア既定値の後ろに
連結する。ScriptPolicy::fromConfig() と同じく、コア既定値を残したまま追加できるようにするため。
Return values
ScriptPolicyproviders()
埋め込みプロバイダーのレジストリを取得する
private
providers() : ProviderRegistry
Return values
ProviderRegistrypurify()
外部から取得した埋め込み HTML をサニタイズする
private
purify(string $html) : string
サニタイズの設定・キャッシュ管理は HtmlPurifier サービスに委ねる。編集者入力向けの
clean() ではなく cleanUntrusted()(HTML.Trusted / CSS.Trusted を落とし、許可要素を
絞ったプリセット)を通すことで、oEmbed 応答による UI 覆い被せを防ぐ。
Why not 自前で HTMLPurifier を組み立てないか: iframe の許可ホスト(IframePolicy)・ キャッシュディレクトリ・定義 ID をここで組み立てると、HtmlPurifier 側の設定変更に 追従が必要になり、キャッシュクリアの入口も増える(用途ごとに呼び出し元へ クリア処理を足すことになり漏れる)。設定を持つのは HtmlPurifier サービスだけに保つ。
Parameters
- $html : string
Return values
stringremoveScriptsNotAllowedBy()
サニタイズ済みの HTML から、src が許可ホストに一致しない script 要素を取り除く。
private
removeScriptsNotAllowedBy(string $html, ScriptPolicy $policy) : string
Parameters
- $html : string
-
サニタイズ済みの HTML
- $policy : ScriptPolicy
-
残してよい script の許可ホスト
Return values
stringremoveSrclessScriptTags()
src を持たない script 要素を取り除く。
private
removeSrclessScriptTags(string $html) : string
サニタイズ後の script は「許可ホストの src を持つ外部 script」だけが意味を持つ。
許可ホスト外の src は属性ごと、インライン JS は中身ごと落ちて、空の <script></script> が
残るため、埋め込みとして機能しないそれらをここで掃除する。
Why not サニタイズ前に全ての script を落とさないか: 以前はそうしていたが、X / Instagram /
Threads のように blockquote + script 形式でしか埋め込みを返さないプロバイダーに対応できない。
script の可否は許可ホストの whitelist(html_purifier_script_allowed_hosts)へ委ね、
ここでは「サニタイズを通り抜けたが実体の無いもの」だけを落とす。
Parameters
- $html : string
Return values
stringresolveAndCache()
取得してキャッシュへ保存する
private
resolveAndCache(string $url, AdapterInterface $cache, string $cacheKey) : EmbedData
Parameters
- $url : string
- $cache : AdapterInterface
- $cacheKey : string