WordPressのesc_urlでURLをエスケープ処理するときに必要な知識としてesc_urlのパラメータと簡単なesc_urlの使用例。そしてesc_urlが関数の内部でどのような処理をしているのかについても理解しておきたかったので、分かる範囲で記事にまとめました。
WordPressのesc_url
esc_urlはWordPressでhref属性やsrc属性に指定するURLを無害化することができる関数です。
<?php esc_url( $url, $protocols, $_context ); ?>
esc_urlのパラメータは3つです。
$url (string) 必須
パラメータ$urlには、無害化するURLを指定したり、URLの文字列を代入した変数をセットして使います。
以下のa要素はhref属性値にesc_urlを記述した使用例です。
<a href="<?php echo esc_url('https://example.com');?>">サンプルリンク</a>
esc_urlの第一引数に$urlパラメータとして指定したURLをWordPressがesc_urlの内部で無害化してくれます。
$protocols (array)
$protocolsはオプションで、受け入れ可能なプロトコルを配列で指定できます。
初期値はnull
そしてデフォルトのプロトコルはwp_allowed_protocols()の値を返すとWordPressのコードリファレンスに説明があったのでwp_allowed_protocols()のソースで配列を確認してみました。
$protocolsの配列は以下です。
$protocols = array( 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'irc6', 'ircs', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'sms', 'svn', 'tel', 'fax', 'xmpp', 'webcal', 'urn' );
これら以外のURLはesc_urlの第二引数をデフォルトで使用した場合、拒否されるのだと思われます。(認識違いがあったらすません)
以下の記述はWordPressでaタグのhref属性にesc_urlを記述して、第二引数の$protocolsにhttpsをセットした使用例です。$urlにはmailtoを記述しています。
<a href="<?php echo esc_url('mailto:info@example.com', array('https'));?>">サンプルリンク</a>
結果
<a href="">サンプルリンク</a>
esc_urlの$protocolsに受け入れ可能なプロトコルをhttpsで指定したので、第一引数で指定したmailtoは拒否され、href属性の値は空になるという結果です。
$_context (sring)
$_contextはオプションで、URLをどのように用いるかです。
デフォルトは ‘display’.です。
esc_urlでURLをエスケープ処理してみる
WordPressのesc_urlでURLをエスケープ処理してみることにします。
WordPressのhome_url()にesc_urlでエスケープ処理
<a href="<?php echo esc_url( home_url( '/' ) ); ?>">ホーム</a>
home_urlはトップページのURLを文字列で取得できますがエスケープ処理されていないので、echoするときにesc_urlでエスケープ処理してURLをリンクにします。
WordPressでURLをa要素にして返す関数を作った場合とかでもesc_urlは使えます。
<?php
function url_docking( $url_name ){
$link_change = '<a href="'.esc_url($url_name).'">リンクテキスト</a>';
return $link_change;
}
echo url_docking('https://"example.com');
?>
雑に作ったurl_docking関数ですが、echoしてる引数に記述したURLには、ダブルクォーテーションの入力ミスを意図的にしてあります。
それでもurl_docking関数の内部でesc_urlのエスケープ処理をしているのでreturnで返ってくるURLは不要なダブルクォーテーションが除去され問題なくリンクとして機能します。
このときesc_urlを使用していない場合はリンクが正しく飛ばなくなってしまいます。
esc_url関数の内部処理
esc_url関数の内部でWordPressがどのようにエスケープ処理しているのか興味があったのでPHPよく知らないながらも「このPHP関数がそうかな」と調べたことを書いておきます。
esc_urlのソース以外はメモ程度の内容です。
esc_urlのソース(長い)
function esc_url( $url, $protocols = null, $_context = 'display' ) {
$original_url = $url;
if ( '' === $url ) {
return $url;
}
$url = str_replace( ' ', '%20', ltrim( $url ) );
$url = preg_replace( '|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url );
if ( '' === $url ) {
return $url;
}
if ( 0 !== stripos( $url, 'mailto:' ) ) {
$strip = array( '%0d', '%0a', '%0D', '%0A' );
$url = _deep_replace( $strip, $url );
}
$url = str_replace( ';//', '://', $url );
/*
* If the URL doesn't appear to contain a scheme, we presume
* it needs http:// prepended (unless it's a relative link
* starting with /, # or ?, or a PHP file).
*/
if ( strpos( $url, ':' ) === false && ! in_array( $url[0], array( '/', '#', '?' ), true ) &&
! preg_match( '/^[a-z0-9-]+?\.php/i', $url ) ) {
$url = 'http://' . $url;
}
// Replace ampersands and single quotes only when displaying.
if ( 'display' === $_context ) {
$url = wp_kses_normalize_entities( $url );
$url = str_replace( '&', '&', $url );
$url = str_replace( "'", ''', $url );
}
if ( ( false !== strpos( $url, '[' ) ) || ( false !== strpos( $url, ']' ) ) ) {
$parsed = wp_parse_url( $url );
$front = '';
if ( isset( $parsed['scheme'] ) ) {
$front .= $parsed['scheme'] . '://';
} elseif ( '/' === $url[0] ) {
$front .= '//';
}
if ( isset( $parsed['user'] ) ) {
$front .= $parsed['user'];
}
if ( isset( $parsed['pass'] ) ) {
$front .= ':' . $parsed['pass'];
}
if ( isset( $parsed['user'] ) || isset( $parsed['pass'] ) ) {
$front .= '@';
}
if ( isset( $parsed['host'] ) ) {
$front .= $parsed['host'];
}
if ( isset( $parsed['port'] ) ) {
$front .= ':' . $parsed['port'];
}
$end_dirty = str_replace( $front, '', $url );
$end_clean = str_replace( array( '[', ']' ), array( '%5B', '%5D' ), $end_dirty );
$url = str_replace( $end_dirty, $end_clean, $url );
}
if ( '/' === $url[0] ) {
$good_protocol_url = $url;
} else {
if ( ! is_array( $protocols ) ) {
$protocols = wp_allowed_protocols();
}
$good_protocol_url = wp_kses_bad_protocol( $url, $protocols );
if ( strtolower( $good_protocol_url ) != strtolower( $url ) ) {
return '';
}
}
/**
* Filters a string cleaned and escaped for output as a URL.
*
* @since 2.3.0
*
* @param string $good_protocol_url The cleaned URL to be returned.
* @param string $original_url The URL prior to cleaning.
* @param string $_context If 'display', replace ampersands and single quotes only.
*/
return apply_filters( 'clean_url', $good_protocol_url, $original_url, $_context );
}
esc_url関数の内部処理:最初のほうの$urlでstr_replaceやpreg_replaceで置き換えしてる。はず…
str_replace ( 置き換える文字列 , 置換後の文字列 , 対象の文字列 [, int &$count ] )
mixed preg_replace (正規表現のパターン, 置換を行う文字列, 置換対象となる文字列,置換回数)
esc_url関数の内部処理でstrposあたりも気になったので調べてみた。
strposの関数のリファレンスを見ると、戻り値は「第1引数で指定した文字列の中で第2引数で指定した文字列が最初に現れる位置」を返すようで。
これが使われているesc_url関数の内部処理で目に止まったのがif strpos( $url, ‘:’ )の部分で$url = ‘http://’がesc_urlの第一引数と結合代入されて、ゴニョゴニョと処理するようで、WordPressのesc_urlは$urlで受け取ったURL文字列にプロトコルが含まれていない以下の場合
<?php echo esc_url('example.com');?>
自動で http:// が補完されます。
apply_filters( 'clean_url'、 string $ good_protocol_url、 string $ original_url、 string $ _context)
あとはクリーンアップされてエスケープされた文字列をフィルタリングして、URLとして出力されるのです。