WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru Платформа для конвертации и монетизации трафика

oEmbed

oEmbed — это открытый формат, созданный для упрощения встраивания содержимого одной веб-страницы в другую. В роли контента могут выступать: фотографии, видеоролики, ссылки и другие типы данных.

oEmbed контент — это видео, аудио, HTML и другие коды на вашем сайте, которые были встроены из другого сайта. Например, если в WP вставить ссылку на видео youtube она превратиться в iframe с видео роликом.

Как это работает

В основе работы класса WP_Embed{} лежит класс WP_oEmbed{} - именно он содержит список зарегистрированных oEmbed провайдеров и он занимается запросом (получением), обработкой Discovery ссылки в head части HTML.

На очень ранней стадии еще до хука mu_plugin_loaded инициализируется класс WP_Embed:

$GLOBALS['wp_embed'] = new WP_Embed();

В конструкторе WP_Embed::__construct() создаются следующие хуки:

public function __construct() {

	// Hack to get the [embed] shortcode to run before wpautop().
	add_filter( 'the_content', array( $this, 'run_shortcode' ), 8 );
	add_filter( 'widget_text_content', array( $this, 'run_shortcode' ), 8 );

	// Attempts to embed all URLs in a post.
	add_filter( 'the_content', array( $this, 'autoembed' ), 8 );
	add_filter( 'widget_text_content', array( $this, 'autoembed' ), 8 );

	// After a post is saved, cache oEmbed items via Ajax.
	add_action( 'edit_form_advanced', array( $this, 'maybe_run_ajax_cache' ) );
	add_action( 'edit_page_form', array( $this, 'maybe_run_ajax_cache' ) );

	// Shortcode placeholder for strip_shortcodes().
	add_shortcode( 'embed', '__return_false' );
}
меню

Обработка oEmbed URL на фронте

Контент записи или виджета text проверяется на наличие шоткода [embed] или на наличие URL на отдельной строке. Для этого, как видно из кода выше, на хуке the_content запускаются два метода:

  1. WP_Embed::run_shortcode( $post_content ) — обрабатывает шоткод [embed].
  2. WP_Embed::autoembed( $post_content ) — обрабатывает URL (на отдельной строке), как шорткод [embed].

    Далее каждый найденный URL (на отдельной строке или в шорткоде) передается в метод WP_Embed::shortcode( $attr, $url ). Далее:

    1. Проверяется внутренний обработчик | Embed

      URL передается в метод WP_Embed::get_embed_handler_html( $rawattr, $url ). Этот метод проверяет переданный URL, на внутренние обработчики, которые регистрируются функцией wp_embed_register_handler().

      1. Если обработчик найден, то URL передается в функцию-обработчик и результат возвращается. Работа метода WP_Embed::shortcode() на этом прекращается.
      2. Если обработчик не найден, то WP_Embed::shortcode() продолжает проверку oEmbed (внешних) обработчиков.
    2. Проверяется внешний обработчик | oEmbed

      1. Если для URL есть кэш, то парсинг прерывается - результат берется из кэша.
      2. Если кэша нет, то URL передается в функцию wp_oembed_get( $url, $attr ), затем в метод WP_oEmbed::get_provider(), далее с этим URL сравниваются все зарегистрированные провайдеры (WP_oEmbed::$providers) и если есть совпадение, делается запрос чтобы получить oEmbed вставку с другого сайта (за этот запрос отвечает класс WP_oEmbed). В конце, неважно какой результат, обработка URL кэшируется.

        При таком варианте кэш создается только при первом запросе (когда его нет) и он не обновляется. Обновление кэша происходит только при обновлении записи в админке.

меню

Обработка oEmbed URL в админке

Также oEmbed кэш создается и (важно) обновляется при обновлении записи на странице редактирования записи в админке. Делается это AJAX запросом. Создается такой AJAX запрос только в момент обновления записи в адмнике - см. WP_Embed::maybe_run_ajax_cache().

Для примера, рассмотрим как WordPress обрабатывает ссылки на другие сайты на WordPress, чтобы встроить контент другого сайта. Общий принцип всего этого описан выше, а ниже рассмотрим как это работает в админке.

Блочный редактор

При вставке ссылки в блочный редактор срабатывает js событие, отправляющее ajax запрос методом GET на роут домен/wp-json/oembed/1.0/proxy, который регистрирует метод WP_oEmbed_Controller::register_routes(). Пример отправляемых данных:

url: https://oddstyle.ru/instrukciya-po-rabote-s-wordpress-rukovodstvo-dlya-novichkov
_locale: user

Ответ генерирует метод WP_oEmbed_Controller::get_proxy_item():

{
	"version":"1.0",
	"provider_name":"Блог про WordPress",
	"provider_url":"https://oddstyle.ru",
	"author_name":"Дмитрий",
	"author_url":"https://oddstyle.ru/author/admin",
	"title":"Инструкция по работе с WordPress. Руководство для новичков",
	"type":"rich",
	"width":600,
	"height":338,
	"html":"
		<blockquote class=\"wp-embedded-content\" data-secret=\"X2b9CmjdiG\">
			<a href=\"https://oddstyle.ru/instrukciya-po-rabote-s-wordpress-rukovodstvo-dlya-novichkov\">Инструкция по работе с WordPress. Руководство для новичков</a>
		</blockquote>
		<iframe 
			class=\"wp-embedded-content\" 
			sandbox=\"allow-scripts\" 
			security=\"restricted\" 
			style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" 
			title=\"«Инструкция по работе с WordPress. Руководство для новичков» — Блог про WordPress\" 
			src=\"https://oddstyle.ru/instrukciya-po-rabote-s-wordpress-rukovodstvo-dlya-novichkov/embed#?secret=X2b9CmjdiG\" 
			data-secret=\"X2b9CmjdiG\" 
			width=\"600\" 
			height=\"338\" 
			frameborder=\"0\" 
			marginwidth=\"0\" 
			marginheight=\"0\" 
			scrolling=\"no\"
		></iframe>
	"
}

Данные кешируются в таблицу *_options.

Визуальный редактор

При использовании визуального редактора при вставки ссылки срабатывает JS событие, запускающее ajax запрос методом post на файл admin-ajax.php, где на хуке wp_ajax_parse_embed отрабатывает одноимённая функция wp_ajax_parse_embed().

Пример отправляемых данных:

post_ID: 31
type: embed
shortcode: [embed]https://oddstyle.ru/instrukciya-po-rabote-s-wordpress-rukovodstvo-dlya-novichkov[/embed]
maxwidth: 549
action: parse-embed

Содержимое поля shortcode передаётся в WP_Embed::run_shortcode(). Что происходит дальше читайте выше в секции "Как это работает".

Пример возвращаемых данных:

{
	"success":true,
	"data":{
		"body":"
			<blockquote class=\"wp-embedded-content\" data-secret=\"mf3DjZabsV\">
				<a href=\"https://oddstyle.ru/instrukciya-po-rabote-s-wordpress-rukovodstvo-dlya-novichkov\">Инструкция по работе с WordPress. Руководство для новичков</a>
			</blockquote>
			<iframe 
				class=\"wp-embedded-content\" 
				sandbox=\"allow-scripts\" 
				security=\"restricted\" 
				style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" 
				title=\"«Инструкция по работе с WordPress. Руководство для новичков» — Блог про WordPress\" 
				src=\"https://oddstyle.ru/instrukciya-po-rabote-s-wordpress-rukovodstvo-dlya-novichkov/embed#?secret=mf3DjZabsV\" 
				data-secret=\"mf3DjZabsV\" 
				width=\"549\" 
				height=\"309\" 
				frameborder=\"0\" 
				marginwidth=\"0\" 
				marginheight=\"0\" 
				scrolling=\"no\"
			></iframe>
		",
		"attr":{
			"width":549,
			"height":824
		},
		"head":"<script src=\"https://wp-test.ru/wp-includes/js/wp-embed.js\"></script>",
		"sandbox":true
	}
}

Данные кешируются в таблицу wp_postmeta или wp_posts.

меню

Кэширование oEmbed запроса

Для кэширования запросов могут быть использованы метаполя записи (поста) или таблица wp_posts.

Если шоткод или ссылка вызывается из контента поста, то кэш будет сохраняться в метаполя текущей записи.

Если текущий записи нет, то кэш будет сохраняться в таблицу wp_posts под типом записи oembed_cache.

Кэш актуален 1 день (86400 секунд). Это значение можно изменить через хук oembed_ttl.

ВАЖНО! Проверка времени жизни и обновление кэша происходит только при обновлении записи со страницы редактирования записи в админке. С фронта кэш не обновляется. Запуск обновления кэша в админке инициализируют эти хуки:

add_action( 'edit_form_advanced', array( $this, 'maybe_run_ajax_cache' ) );
add_action( 'edit_page_form', array( $this, 'maybe_run_ajax_cache' ) );
меню

oEmbed Безопасность

Встраивание чужого кода на свой сайт открывает возможность XSS атаки. Так, например, полученный код может содержать хакерский код, который может получить куки вашего сайта да и вообще, может делать кучу всего.

Чтобы обезопасить себя от подобного рода дыр в безопасности, WordPress выводит встраивание в теге <iframe>. А также очищает полученный код iframe от нежелательных атрибутов. В частности код встраивания допускает следующие HTML теги, которые проверяются функцией wp_kses():

$allowed_html = array(
	'a'          => array(
		'href' => true,
	),
	'blockquote' => array(),
	'iframe'     => array(
		'src'          => true,
		'width'        => true,
		'height'       => true,
		'frameborder'  => true,
		'marginwidth'  => true,
		'marginheight' => true,
		'scrolling'    => true,
		'title'        => true,
	),
);

$html = wp_kses( $html, $allowed_html );

Как мы видим код встраивания может содержать только три тега и ограниченное число параметров для этих тегов.

Смотрите код функции wp_filter_oembed_result(), которая по умолчанию повешена на хук oembed_dataparse и применяется абсолютно ко всем полученным кодам для встраивания.

меню

WordPress как поставщик oEmbed

C версии 4.4. WordPress сам стал поставщиком/провайдером oEmbed формата. Теперь, записи одного сайта WP можно встроить в другой сайт.

Чтобы это сделать, нужно в контент поста добавить URL записи. Сделать это можно двумя способами:

  1. Добавить URL на отдельную строку.
  2. Вставить URL в шоткод [embed]. Например [embed]http://dom.com/adress[/embed].

WordPress автоматически обработает указанный URL (определит является ли тот oEmbed провайдером), встроит данные и создаст кэш запроса, чтобы не делать этот запрос каждый раз при загрузке страницы.

Встраивания представляют из себя краткую версию страницы. Например если добавить /embed в конец URL любой записи, то мы попадем на страницу встраивания (которая должна появится на другом сайте при встраивании). Вот пример такой страницы: https://wp-kama.ru/handbook/wordpress/oembed/embed.

Пример того, как выглядит встраивание:

Инструкция по работе с WordPress. Руководство для новичков
меню

Как изменить HTML код встраивания?

Для этого нужно создать в теме файл embed.php

Если такого файла в теме нет, то за код встраивания отвечают файлы:

В каждом из этих файлов есть хуки, которые можно использовать для изменения отдельных частей HTML страницы встраивания.

меню

oEmbed Функции и Хуки

Функции

Хуки

Это скромный список из важных хуков. Полный список смотрите здесь.

  • embed_cache_oembed_types - позволяет изменить типы записей для которых нужно обрабатывать oembed ссылки (шоткоды).
  • oembed_ttl — позволяет изменить TTL (time to live) время жизни кэша.
  • embed_oembed_html — позволяет изменить уже закэшированный HTML.
  • oembed_dataparse — Позволяет изменить контент (HTML) создаваемый при встраивании URL, поддерживаемых oEmbed.
меню

Поддерживаемые провайдеры

Ниже список oEmbed провайдеров, которых WordPress поддерживает из коробки. Дополнительные провайдеры можно добавить в этот список с помощью функции wp_oembed_add_provider().

Provider Flavor Since
Dailymotion dailymotion.com 2.9.0
Flickr flickr.com 2.9.0
Scribd scribd.com 2.9.0
Vimeo vimeo.com 2.9.0
WordPress.tv wordpress.tv 2.9.0
YouTube youtube.com/watch 2.9.0
Crowdsignal polldaddy.com 3.0.0
SmugMug smugmug.com 3.0.0
YouTube youtu.be 3.0.0
Twitter twitter.com 3.4.0
Instagram instagram.com 3.5.0
Instagram instagr.am 3.5.0
Slideshare slideshare.net 3.5.0
SoundCloud soundcloud.com 3.5.0
Dailymotion dai.ly 3.6.0
Flickr flic.kr 3.6.0
Spotify spotify.com 3.6.0
Imgur imgur.com 3.9.0
Meetup.com meetup.com 3.9.0
Meetup.com meetu.ps 3.9.0
Animoto animoto.com 4.0.0
Animoto video214.com 4.0.0
Issuu issuu.com 4.0.0
Mixcloud mixcloud.com 4.0.0
Crowdsignal poll.fm 4.0.0
TED ted.com 4.0.0
YouTube youtube.com/playlist 4.0.0
Tumblr tumblr.com 4.2.0
Kickstarter kickstarter.com 4.2.0
Kickstarter kck.st 4.2.0
Cloudup cloudup.com 4.3.0
ReverbNation reverbnation.com 4.4.0
VideoPress videopress.com 4.4.0
Reddit reddit.com 4.4.0
Speaker Deck speakerdeck.com 4.4.0
Twitter twitter.com/timelines 4.5.0
Twitter twitter.com/moments 4.5.0
Facebook facebook.com 4.7.0
Twitter twitter.com/user 4.7.0
Twitter twitter.com/likes 4.7.0
Twitter twitter.com/lists 4.7.0
Screencast screencast.com 4.8.0
Amazon amazon.com (com.mx, com.br, ca) 4.9.0
Amazon amazon.de (fr, it, es, in, nl, ru, co.uk) 4.9.0
Amazon amazon.co.jp (com.au) 4.9.0
Amazon amazon.cn 4.9.0
Amazon a.co 4.9.0
Amazon amzn.to (eu, in, asia) 4.9.0
Amazon z.cn 4.9.0
Someecards someecards.com 4.9.0
Someecards some.ly 4.9.0
Crowdsignal survey.fm 5.1.0
Instagram TV instagram.com 5.1.0
Instagram TV instagr.am 5.1.0
TikTok tiktok.com 5.4.0
меню

Удаление просроченного oEmbed Кэша

Для очистки Базы Данных на больших сайтах возможно есть смысл периодически запускать такую функцию, чтобы удалять просроченный кэш.

/**
 * Remove expired oEmbed Cache.
 * 
 * @param int $ttl The cache lifetime in seconds.
 * 
 */
function kama_delete_expired_oembed_cache( $ttl = MONTH_IN_SECONDS ){
	global $wpdb;

	// META
	$query_data = $wpdb->get_results(
		"SELECT * FROM $wpdb->postmeta WHERE meta_key LIKE '_oembed_time_%' ORDER BY meta_value+0 DESC" 
	);

	$res = [];
	foreach( $query_data as $data ){

		$post = get_post( $data->post_id );
		$info = date( 'd-m-Y', $data->meta_value ) ." - $post->ID: $post->post_title";

		if( time() > $data->meta_value + $ttl ){

			$oembed_meta_key = str_replace( '_oembed_time_', '_oembed_', $data->meta_key );

			delete_post_meta( $data->post_id, $data->meta_key );
			delete_post_meta( $data->post_id, $oembed_meta_key );

			$res['DELETED'][] = $info;
		}
		else {
			$res['NOT DELETED'][] = $info;
		}

	}

	// POSTS
	$min_allowed_date = date( 'Y-m-d H:i:59', time() - $ttl );

	$posts = $wpdb->get_results(
		"SELECT * FROM $wpdb->posts WHERE post_type = 'oembed_cache' AND post_modified_gmt < '$min_allowed_date' 
		ORDER BY post_modified_gmt DESC" 
	);

	foreach( $posts as $post ){
		$res['DELETED POSTS'][] = $post->post_modified_gmt;

		wp_delete_post( $post->ID, 'force_delete' );
	}

	return $res;

}

$res = kama_delete_expired_oembed_cache();
print_r( $res );
меню

Внутренний embed обработчик с кэшированием

Для начала замечу, что это хак - WP на такое не рассчитан. Но благодаря хукам, можно это сделать.

Для этого используем хук в методе WP_oEmbed::get_html():

// ...
$pre = apply_filters( 'pre_oembed_result', null, $url, $args );

if ( null !== $pre ) {
	return $pre;
}
// ...

Нужно зарегистрировать внутренний обработчик через функцию wp_embed_register_handler(). Но в функции обработчике вернуть false и перенести функцию обработчик в упомянутый хук pre_oembed_result.

Т.е. код будет примерно такой:

add_action( 'init', 'myembed_provider_register' );
add_filter( 'pre_oembed_result', 'myembed_provider_handler', 10, 3 );

function myembed_provider_register(){
	wp_embed_register_handler(
		'myembed',
		'~https://foo\.bar\.com/(\w+)~i',
		'__return_false'
	);
}

function myembed_provider_handler( $null, $url, $args ){

	$html = '{{unknown}}';

	// обрабатываем $url делаем HTTP запрос. См. WP HTTP API

	// Возвращаем iframe или HTML код

	return $html;
}
Комментариев нет
    Войти