oEmbed в WordPress
oEmbed — это открытый формат, созданный для упрощения встраивания содержимого одной веб-страницы в другую. В роли контента могут выступать: фотографии, видеоролики, ссылки и другие типы контента.
oEmbed контент — это видео, аудио, HTML и другие коды на вашем сайте, которые были встроены из другого сайта. Например, если в WP вставить ссылку на видео youtube она превратиться в iframe с видео роликом.
Читайте также: Шорткоды в WordPress.
Как это работает
В основе работы класса WP_Embed{} лежит класс WP_oEmbed{} - именно он содержит список зарегистрированных oEmbed провайдеров и он занимается запросом (получением), обработкой Discovery ссылки в head части HTML.
На очень ранней стадии еще до хука mu_plugin_loaded инициализируется класс WP_Embed:
$GLOBALS['wp_embed'] = new WP_Embed();
В конструкторе WP_Embed::__construct() создаются следующие хуки:
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 ); // Shortcode placeholder for strip_shortcodes(). add_shortcode( 'embed', '__return_false' ); // 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' ) ); }
Обработка oEmbed URL на фронте
Контент записи или виджета text проверяется на наличие шоткода [embed]
или на наличие URL на отдельной строке. Для этого, как видно из кода выше, на хуке the_content запускаются два метода:
-
WP_Embed::run_shortcode( $post_content ) — обрабатывает шоткод
[embed]
. -
WP_Embed::autoembed( $post_content ) — обрабатывает URL (на отдельной строке), как шорткод
[embed]
.Далее каждый найденный URL (на отдельной строке или в шорткоде) передается в метод WP_Embed::shortcode( $attr, $url ). Далее:
-
Проверяется внутренний обработчик | Embed
URL передается в метод WP_Embed::get_embed_handler_html( $rawattr, $url ). Этот метод проверяет переданный URL, на внутренние обработчики, которые регистрируются функцией wp_embed_register_handler().
- Если обработчик найден, то URL передается в функцию-обработчик и результат возвращается. Работа метода WP_Embed::shortcode() на этом прекращается.
- Если обработчик не найден, то WP_Embed::shortcode() продолжает проверку oEmbed (внешних) обработчиков.
-
Проверяется внешний обработчик | oEmbed
- Если для URL есть кэш, то парсинг прерывается - результат берется из кэша.
-
Если кэша нет, то 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().
- wp_ajax_oembed_cache()
- WP_Embed::cache_oembed( $post_id )
- Далее, обработка идет по описанный выше схеме, контент поста передается в методы: WP_Embed::run_shortcode( $post_content ) и WP_Embed::autoembed( $post_content ).
- WP_Embed::cache_oembed( $post_id )
Для примера, рассмотрим как 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' ) );
Если по какой-то причине не удалось получить HTML встраивания, например вернулся 404 ответ или другой отличный от 200, то в кэш вместо html, будет добавлен маркер {{unknown}}
.
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 записи. Сделать это можно двумя способами:
- Добавить URL на отдельную строку.
- Вставить URL в шоткод
[embed]
. Например[embed]http://dom.com/adress[/embed]
.
WordPress автоматически обработает указанный URL (определит является ли тот oEmbed провайдером), встроит данные и создаст кэш запроса, чтобы не делать этот запрос каждый раз при загрузке страницы.
Встраивания представляют из себя краткую версию страницы. Например если добавить /embed
в конец URL любой записи, то мы попадем на страницу встраивания (которая должна появится на другом сайте при встраивании). Вот пример такой страницы: https://wp-kama.ru/handbook/codex/oembed/embed.
Пример того, как выглядит встраивание:
oEmbed в WordPress
Как изменить HTML код встраивания?
Для этого нужно создать в теме файл embed.php
- подробнее про иерархию шаблона.
Если такого файла в теме нет, то за код встраивания отвечают файлы:
В каждом из этих файлов есть хуки, которые можно использовать для изменения отдельных частей HTML страницы встраивания.
Функции oEmbed
- get_post_embed_url() — Получает URL, который нужно использовать в iframe для встраивания указанной записи на другом сайте (oEmbed формат).
- get_post_embed_html() — Получает готовый HTML код oEmbed встраивания указанной записи. Предполагается использовать этот код для встраивания записи на другом ресурсе.
- is_embed() — Проверят является ли запрос запросом на страницу встраивания записи (embed).
- wp_embed_register_handler() — Регистрирует Embed обработчик. Это обработчик, который превращает ссылку в контенте в HTML код.
- wp_oembed_get() — Встраивает объект по УРЛ. Пытается получить HTML код из переданного УРЛ на основе поддерживаемых oEmbed WordPress.
- wp_oembed_add_provider() — Добавляет oEmbed провайдера. Это URL который будет парсится в контенте для вывода HTML кода с другого сайта.
- wp_oembed_remove_provider() — Removes an oEmbed provider.
См. Полный список Embed функций.
Хуки oEmbed
- embed_cache_oembed_types - позволяет изменить типы записей для которых нужно обрабатывать oembed ссылки (шоткоды).
- oembed_ttl — позволяет изменить TTL (time to live) время жизни кэша.
- embed_oembed_html — позволяет изменить уже закэшированный HTML.
- oembed_dataparse — Позволяет изменить контент (HTML) создаваемый при встраивании URL, поддерживаемых oEmbed.
- embed_oembed_discover — Позволяет указать нужно ли переходить по URL и искать
<link>
тег встраивания на удаленном сайте.
См. Полный список хуков.
Поддерживаемые провайдеры
Ниже список 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.com | 3.4.0 | |
instagram.com | 3.5.0 | |
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.com | 4.4.0 | |
Speaker Deck | speakerdeck.com | 4.4.0 |
twitter.com/timelines | 4.5.0 | |
twitter.com/moments | 4.5.0 | |
facebook.com | 4.7.0 | |
twitter.com/user | 4.7.0 | |
twitter.com/likes | 4.7.0 | |
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 на такое не рассчитан. Но благодаря хукам, можно это сделать.
Для этого используем хук pre_oembed_result из метода 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; }
oEmbed для произвольного текста
Build In PostЕсли нам нужно обработать шорткод [embed]
или авто-встраивание ссылки в тексте. То этот текст нужно будет обработать отдельно. Базовая обработка с помощью функций do_shortcodes() или apply_shortcodes() oEmbed не включает. И по умолчанию такая обработка делается только для хука the_content
.
Соответственно у нас есть 2 варианта:
Вариант 1
Простой, но может не подходить из-за излишней нагруженности хука the_content - на нём обычно висит куча всего, что может оказаться лишним.
$text = ' Some text to check custom shortcode adding. [embed]https://my-youtube.com/watch?v=lWzMBLoLIAc[/embed] https://my-youtube.com/watch?v=uDQwKtkXV-0 '; $text = apply_filters( 'the_content', $text ); echo $text;
Вариант 2: Точечный подход
Делаем с текстом только то что нам нужно:
$text = ' Some text to check custom shortcode adding. [embed]https://my-youtube.com/watch?v=lWzMBLoLIAc[/embed] https://my-youtube.com/watch?v=uDQwKtkXV-0 '; $text = $GLOBALS['wp_embed']->run_shortcode( $text ); // shortcode $text = $GLOBALS['wp_embed']->autoembed( $text ); // oEmbed URLs //$text = apply_shortcodes( $text ); $text = wpautop( $text ); echo $text;
oEmbed в комментариях WordPress
Build In PostКод ниже позволяет использовать oEmbed в комментариях WordPress.
Правка для работы с AJAX:
Отключение oEmbed
Читайте в отдельной статье