PHP
Grav

Gravでリンクカードを作る

リンクカードプラグインが無いっぽい感じがしたので作りました。
とはいえShortcode Coreプラグインを利用してカスタムショートコードを作っただけですが。

LinkcardShortcode.php
<?php
namespace Grav\Plugin\Shortcodes;

use Thunder\Shortcode\Shortcode\ShortcodeInterface;
use Thunder\Shortcode\Shortcode\ProcessedShortcode;

class LinkcardShortcode extends Shortcode {
    public function init() {
        $this->shortcode->getHandlers()->add('site', function(ProcessedShortcode $sc) {
            $url = $sc->getParameter('url');
            $purl = parse_url($url);
            $img = $sc->getParameter('img');
            $content = $sc->getContent();
            $html = null;

            if($url){
                $data = $this->get_ogp($url,$img,$content);
                $img = $data['img'];
                $title = $data['title'];
                $desc = $data['desc'];

                $html = '<blockquote class="link panel panel-default"><a onclick="ga(\'send\',\'event\',\'BlogCard\',\'click\',location.href);" rel="noopener" target="_blank" href="'.$url.'">';
                $html .= '<img class="thumb pull-right" src="'.$img.'" alt="'.$title.'" /><span class="lead text-primary">'.$title.'</span>';
                $html .= '<p>'.$desc.'</p><footer><img class="favicon" src="//www.google.com/s2/favicons?domain_url='.urlencode($url).'" /> '.$purl['host'].'</footer><div class="clearfix"></div></a></blockquote>';
            }
            return $html;
        });
    }

    private function get_ogp($url,$img = null,$title = null){
        $context = stream_context_create(array(
        'http' => array('ignore_errors' => true)
        ));
        $html = file_get_contents($url, false, $context);
        $description = null;
        $thumb = null;

        if($html){
            preg_match('/<title>(.*)<\/title>/',$html,$mt);
            if($mt && $mt[1]){$title = $mt[1];}

            preg_match('/<meta property="og:image" content="http([^\n]*)"/',$html,$mi);
            if($mi && $mi[1]){$thumb = 'http'.$mi[1];}

            preg_match('/<meta name="description" content="([^\n]*)"/',$html,$md);
            if($md && $md[1]){$description = $md[1];}
            else{
                preg_match('/<meta property="og:description" content="([^\n]*)"/',$html,$md);
                if($md && $md[1]){$description = $md[1];}
            }
        }

        if($img){$thumb = $img;}
        elseif(!$thumb){$thumb = '/empty.gif';}
        $data = array(
            'title' => $title,
            'img' => $thumb,
            'desc' => mb_substr($description,0,200)
        );
        return $data;
    }
}

[site url="" img=""]Title[/site] みたいな感じで使えます。

OpenGraph.php(というかDOMDocument)がnamespace回りの何かにひっかかって使えなかったため、正規表現ゴリゴリになってしまいました。
empty.gif(画像が無かった時の代替)は各自ご用意ください。

file_get_contentで403等が返ってきた時の回避方法は此方を参考にさせていただきました。
https://blog.manabusakai.com/2013/02/file-get-contents-status-code/

OGPだけではなくTwitter card情報から取ってくるとか、細かいカスタマイズは各自でお願いします。

Gravのキャッシュシステムをよく解っていないので、先方に負荷がかかるようならまた修正します。

Gistにも置いておきました。
https://gist.github.com/yukari-n/2920b9ead12ba6082a4d69f7a1f5b171

※この記事は2018-05-19 09:00公開のブログ記事を移転してきたものです。