#author("2022-04-23T16:27:32+09:00","","") * URLカスタマイズの仕組み [#t65b6b10] - ページ: [[BugTrack]] - 投稿者: [[umorigu]] - 優先順位: 低 - 状態: 完了 - カテゴリー: 本体新機能 - 投稿日: 2021-11-27 (土) 18:12:19 - バージョン: 1.5.3 - リリース予定バージョン: 1.5.4 #contents ** メッセージ [#p2d503a3] PukiWiki の定番のカスタマイズとして、ページURLを変えるものがある。これを本体書き換え無しで行うための仕組みを導入する。 URL (path, query 部分) の例 「階層1/名前」というページの場合 - /?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D (標準) -- 最近のブラウザではアドレス欄でURL decodeされて 「/?階層1/名前」という表示になることが多い - /%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D (Path (サーバー上のファイル) として扱う) - /%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D.html (さらに拡張子を.html にする) - /?&a66dc2fa8e (短縮URLプラグイン [[official:Plugins/s.inc.php]] での例示) -- 短縮URLは通常URLにリダイレクトするパターンと、直接表示するパターンの2つ - /?a66dc2fa8e (短縮URLプラグイン [[official:Plugins/s.inc.php]] 利用) -- 短縮部分に "?" をつけない形 (通常ページ名と区別がつかないため、解決するための仕様決めが必要) - /a66dc2fa8e (短縮URLをさらにpath風に見せる) - /123.html (pgid プラグイン利用?) カスタマイズ例: | カスタマイズ | 説明 | ページURL例 | アドレス欄表示 | 備考 |h | (1) 標準 | '?' の後にページ名 | https://pukiwiki.osdn.jp/_samples/1/?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D | /_samples/1/?階層1/名前 | | | (2) 末尾 .html | '?' の後にページ名。末尾が .html | https://pukiwiki.osdn.jp/_samples/2/?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D.html | /_samples/2/?階層1/名前.html | | | (3) path風に変更 | '?' がなく、サーバー上のファイル名のような見た目 | https://pukiwiki.osdn.jp/_samples/3/%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D | /_samples/3/階層1/名前 | | | (6) 短縮URLベース | 長いURLのページを短縮URLプラグインによる短いURLで表現 | https://pukiwiki.osdn.jp/_samples/6/?&a66dc2fa8e | /_samples/6/?&a66dc2fa8e | | | (7) 短縮URLリダイレクト | 短縮URLプラグインにより、短縮URL→実URLにリダイレクト | https://pukiwiki.osdn.jp/_samples/7/?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D &br; (リダイレクト前 短縮URL) https://pukiwiki.osdn.jp/_samples/7/?&a66dc2fa8e | /_samples/7/?階層1/名前 | | | (8) 短縮URL優先 | 短縮URLプラグインにより、ページ名URL→短縮URLにリダイレクト | https://pukiwiki.osdn.jp/_samples/8/?&a66dc2fa8e &br; (リダイレクト前 通常URL) https://pukiwiki.osdn.jp/_samples/8/?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D | /_samples/8/?&a66dc2fa8e | | | (9) 短縮URLベース2 | 長いURLのページを短縮URLプラグインによる短いURLで表現&br;(「英数小文字10文字」はすべて短縮URLとみなす) | https://pukiwiki.osdn.jp/_samples/9/?a66dc2fa8e | /_samples/9/?a66dc2fa8e | | ** 実動作サンプル [#samples] *** (1) 通常 [#nf0c7789] - https://pukiwiki.osdn.jp/_samples/1/ -- https://pukiwiki.osdn.jp/_samples/1/?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D --- アドレス欄 「/?階層1/名前」 *** (2) 末尾 (ページ名).html [#d77d4243] - https://pukiwiki.osdn.jp/_samples/2/ -- https://pukiwiki.osdn.jp/_samples/2/?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D.html --- アドレス欄 「/?階層1/名前.html」 pukiwiki.ini.php // Page-URI mapping handler ( See https:// pukiwiki.osdn.jp/?PukiWiki/PageURI ) class HtmlSuffixPageURIHandler extends PukiWikiStandardPageURIHandler { function get_page_uri_virtual_query($page) { return '?' . pagename_urlencode($page) . '.html'; } function get_page_from_query_string($query_string) { $param1st = preg_replace("#^([^&]*)&.*$#", "$1", $query_string); if ($param1st == '') { return null; // default page } if (strpos($param1st, '=') !== FALSE) { // Found '/?key=value' (NG chars) return FALSE; // Error page } $base = preg_replace('#\.html$#', '', $param1st); if ($param1st === $base) { return FALSE; // Error } $page = urldecode($base); $page2 = input_filter($page); if ($page !== $page2) { return FALSE; // Error page } return $page2; } } $page_uri_handler = new HtmlSuffixPageURIHandler(); *** (3) ページ名を path に見せる [#u315499d] ページ名の前に '?' がつかず、ページがそのままサーバー上のファイルのように見えるURL - https://pukiwiki.osdn.jp/_samples/3/ -- https://pukiwiki.osdn.jp/_samples/3/%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D --- アドレス欄 「/階層1/名前」 pukiwiki.ini.php: image ディレクトリ define('IMAGE_DIR', '/_samples/3/image/'); トップページの絶対URL $script = 'https:// pukiwiki.osdn.jp/_samples/3/'; パスのカスタマイズ ('?' をつけない) 各ページで相対パスでなくルート相対パスを利用するために pkwk_base_uri_type_stack_push(PKWK_URI_ROOT); を実行しておく。 // Page-URI mapping handler ( See https:// pukiwiki.osdn.jp/?PukiWiki/PageURI ) class VirtualPathPageURIHandler extends PukiWikiStandardPageURIHandler { function get_page_uri_virtual_query($page) { return pagename_urlencode($page); } } pkwk_base_uri_type_stack_push(PKWK_URI_ROOT); $page_uri_handler = new VirtualPathPageURIHandler(); .htaccess このカスタマイズはPHPだけでは実現できず、Webサーバー側でパスをクエリ文字列に変換する必要がある。 Apacheでの指定は以下のようになる。 RewriteEngine On RewriteCond %{REQUEST_URI} !(^/_samples/3/$) RewriteCond %{REQUEST_URI} !(^/_samples/3/image/) RewriteCond %{REQUEST_URI} !(^/_samples/3/skin/) RewriteCond %{REQUEST_FILENAME} !(\.php$) RewriteRule ^(.+)$ /_samples/3/?$1 [L] 意味: path が /, /image/..., /skin/... 以外の場合は /?{path} というリクエストに変換する。 この設定の場合、"image/" と "skin/" で始まるページ名、末尾が ".php" となるページ名は作成できない。 /skin/pukiwiki.skin.php: スキンファイルもいくつか、参照ファイルをルート相対パスで指定する必要がある。 (ここでは一部のみ抜粋) <link rel="stylesheet" type="text/css" href="/_samples/3/<?php echo SKIN_DIR ?>pukiwiki.css" /> <script type="text/javascript" src="/_samples/3/skin/main.js" defer></script> <script type="text/javascript" src="/_samples/3/skin/search2.js" defer></script> <a href="<?php echo $link['top'] ?>"><img id="logo" src="/_samples/3/<?php echo IMAGE_DIR . $image['logo'] ?>" width="80" height="80" alt="[PukiWiki]" title="[PukiWiki]" /></a> echo '<a href="' . $link[$key] . '">' . '<img src="/_samples/3/' . IMAGE_DIR . $image[$key] . '" width="' . $x . '" height="' . $y . '" ' . 'alt="' . $lang[$key] . '" title="' . $lang[$key] . '" />' . '</a>'; /pukiwiki.ini.php: Base URLを固定で指定する。 $script = 'https://pukiwiki.osdn.jp/_sample/3/' *** (6) 短縮URLプラグイン ([[s.inc.php>official:Plugins/s.inc.php]]) による常時短縮URLのカスタマイズ [#dee2c813] - https://pukiwiki.osdn.jp/_samples/6/ -- https://pukiwiki.osdn.jp/_samples/6/?&a66dc2fa8e --- (リダイレクト無し) --- アドレス欄 「/?&a66dc2fa8e」 pukiwiki.ini.php: // Page-URI mapping handler ( See https:// pukiwiki.osdn.jp/?PukiWiki/PageURI ) class ShortUrlPageURIHandler extends PukiWikiStandardPageURIHandler { function get_page_uri_virtual_query($page) { exist_plugin('s'); // Load s.inc.php $page_id = plugin_s_get_page_id($page); if ($page_id) { return '?&' . $page_id; } return '?' . pagename_urlencode($page); } function get_page_from_query_string($query_string) { exist_plugin('s'); // Load s.inc.php $m = array(); if (preg_match('#^\&([0-9a-f]{10})$#', $query_string, $m)) { $page = plugin_s_get_page_from_page_id($m[1]); return $page; } return parent::get_page_from_query_string($query_string); } } $page_uri_handler = new ShortUrlPageURIHandler(); 注意: ページURLを取得するのにファイル生成またはファイル存在チェックを伴うため、ページ数が数万となるような大規模サイトでは一覧などでパフォーマンスの低下が発生します。 *** (7) 短縮URLプラグイン ([[s.inc.php>official:Plugins/s.inc.php]]) によるリダイレクトのカスタマイズ [#b55ec583] 主に通常ページ名URLを使い、短縮URLでのリンクも可能にする設定。 (短縮URLプラグインの元々の動作) - https://pukiwiki.osdn.jp/_samples/7/ -- https://pukiwiki.osdn.jp/_samples/7/?&a66dc2fa8e --- https://pukiwiki.osdn.jp/_samples/7/?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D にリダイレクト --- アドレス欄 「/?階層1/名前」 pukiwiki.ini.php: // Page-URI mapping handler ( See https:// pukiwiki.osdn.jp/?PukiWiki/PageURI ) class ShortUrlRedirectPageURIHandler extends PukiWikiStandardPageURIHandler { function filter_raw_query_string($query_string) { $m = array(); if (preg_match('#^\&([0-9a-f]{10})$#', $query_string, $m)) { return 'cmd=s&k=' . $m[1]; } return $query_string; } } $page_uri_handler = new ShortUrlRedirectPageURIHandler(); タイトル下に以下のコードを挿入する。 ([[official:Plugins/s.inc.php]] より) <?php if ($is_page) { ?> <span class="small"> <?php exist_plugin('s'); echo plugin_s_convert_get_short_link(); ?> </span> <?php } ?> *** (8) 短縮URLプラグイン ([[s.inc.php>official:Plugins/s.inc.php]]) 短縮URL優先利用カスタマイズ [#t0659104] - https://pukiwiki.osdn.jp/_samples/8/ -- https://pukiwiki.osdn.jp/_samples/8/?&a66dc2fa8e --- そのまま表示 -- https://pukiwiki.osdn.jp/_samples/8/?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D --- https://pukiwiki.osdn.jp/_samples/8/?&a66dc2fa8e にリダイレクト --- アドレス欄 「/?&a66dc2fa8e」 pukiwiki.ini.php: // Page-URI mapping handler ( See https:// pukiwiki.osdn.jp/?PukiWiki/PageURI ) class ShortUrlPrimaryPageURIHandler extends PukiWikiStandardPageURIHandler { function get_page_uri_virtual_query($page) { exist_plugin('s'); // Load s.inc.php $page_id = plugin_s_get_page_id($page); if ($page_id) { return '?&' . $page_id; } return '?' . pagename_urlencode($page); } function get_page_from_query_string($query_string) { exist_plugin('s'); // Load s.inc.php $m = array(); if (preg_match('#^\&([0-9a-f]{10})$#', $query_string, $m)) { $page = plugin_s_get_page_from_page_id($m[1]); return $page; } $is_get = $_SERVER['REQUEST_METHOD'] === 'GET'; if ($is_get) { if (strpos('&', $query_string) !== NULL) { $dec_page = urldecode($query_string); $page_id = plugin_s_get_page_id($dec_page); if ($page_id) { $short_url = get_page_uri($dec_page, PKWK_URI_ROOT); header("HTTP/1.1 302 Found"); header("Location: $short_url"); exit; } } } return parent::get_page_from_query_string($query_string); } } $page_uri_handler = new ShortUrlPrimaryPageURIHandler(); *** (9) 短縮URLプラグイン ([[s.inc.php>official:Plugins/s.inc.php]]) による常時短縮URL パターン2 [#j4da4be5] - https://pukiwiki.osdn.jp/_samples/9/ -- https://pukiwiki.osdn.jp/_samples/9/?a66dc2fa8e --- (リダイレクト無し) --- アドレス欄 「/?a66dc2fa8e」 "?" の後に "&" 等の記号なしに短縮URL用の page_id を続ける形。 URLはシンプルになるが、指定されているのがページ名なのか短縮URLのpage_idなのかを機械的に判定できない。 例: "/?aaaaaaaaaa" というURLの場合に「"aaaaaaaa"というページ名」なのか「"aaaaaaaaaa"という短縮URLのpage_id」なのかわからない。どちらの可能性もある。 このカスタマイズ例では、英数小文字が10文字続いた場合には常に短縮URLのpage_idとして扱う。 「英数小文字10文字」はすべて短縮URLとみなし、この形の名前のページの作成は不可とする。 pukiwiki.ini.php: class ShortUrl2PageURIHandler extends PukiWikiStandardPageURIHandler { function get_page_uri_virtual_query($page) { exist_plugin('s'); // Load s.inc.php $page_id = plugin_s_get_page_id($page); if ($page_id) { return '?' . $page_id; } return '?' . pagename_urlencode($page); } function get_page_from_query_string($query_string) { exist_plugin('s'); // Load s.inc.php $m = array(); if (preg_match('#^([0-9a-z]{10})$#', $query_string, $m)) { $page = plugin_s_get_page_from_page_id($m[1]); if ($page) { return $page; } else { // Not found http_response_code(404); die_message('Not found'); exit; } } return parent::get_page_from_query_string($query_string); } } $page_uri_handler = new ShortUrl2PageURIHandler(); ** URLカスタマイズの2パターン [#f92aa1c1] 表示に関わる - (a) PHPのみで実現できるもの -- "?" を使って、PHPファイルとクエリパラメータを分けているもの - (b) Apache/nginxなど、WebサーバーでのURLマッピング操作が必要なもの -- "?" を使わず、PHPファイルのURLと本来クエリパラメータとして渡す部分を両方 path として見せているもの ** 参照 [#q540935c] - [[BugTrack/2213]] get_page_uri($page) 関数 - [[official:Plugins/s.inc.php]] ** 対象 [#d873298b] - ページの表示 (readプラグインのaction) - read以外のactionのURLは対象外 (標準のまま) ** 設計 [#f5b72059] - 各ページのURLを作成しているのは get_page_uri($page) である。get_page_uri() 内の1か所を変更するだけですべてのURLが書き換わる (のが理想) - 各種プラグインも get_page_uri($page) によって各ページのURLを生成すること -- get_page_uri() ができたのが PukiWiki 1.5.2 (2019年3月リリース) なのでこれ以前に作られたプラグインはそもそも対応していない クラス class PukiWikiStandardPageURIHandler { // QueryStringの変換 function filter_raw_query_string($query_string); // ページ名からURIのページ表現部分を生成 function get_page_uri_fragment($page); // クエリストリングからページ名を取得 function get_page_from_query_string($query_string); } lib/init.php:397 // cmdもpluginも指定されていない場合は、QUERY_STRINGをページ名かInterWikiNameであるとみなす if (! isset($vars['cmd']) && ! isset($vars['plugin'])) { $get['cmd'] = $post['cmd'] = $vars['cmd'] = 'read'; $arg = preg_replace("#^([^&]*)&.*$#", "$1", $arg); if ($arg == '') $arg = $defaultpage; if (strpos($arg, '=') !== false) $arg = $defaultpage; // Found '/?key=value' $arg = urldecode($arg); $arg = strip_bracket($arg); $arg = input_filter($arg); $get['page'] = $post['page'] = $vars['page'] = $arg; } ** 対応していないプラグインへの対応 [#unsuppoeted_plugins] 各ページへのリンクURL取得手段として PukiWiki 1.5.2 から導入された get_page_uri($page) を利用していないプラグインはカスタムURL対応になりません。 ページURLに get_page_uri($page) を利用するよう書き換えてください。 ** カスタマイズ内容 [#hc888a0e] (URL生成) 各ページのURL は get_base_uri() + 「ページを表すURL片」で表現される。 (例: "?FrontPage", "?BugTrack/2525") ページ名を引数にしてこの「ページを表すURL片」の生成する関数を登録できるようにする。 (ページの表示) 「ページを表すURL片」を受け取って、表示(または別のプラグインアクション)を行う関数を登録できるようにする。 ** 既存ロジック不明なところ [#ze114a2e] make_link.php クラス Link_interwikiname - function set() $url = get_interwiki_url($name, $this->param); $this->url = ($url === FALSE) ? get_base_uri() . '?' . pagename_urlencode('[[' . $name . ':' . $this->param . ']]') : htmlsc($url); ? に続けて [[ name : param ]] を設定しているがこの記述で read しようとするとエラーになる。どういう条件でここに入るのか不明 -------- - 試作してみました。まだ取り込まず、試行錯誤の段階です -- [[umorigu]] &new{2021-11-28 (日) 20:22:55}; - 対応しました。 1.5.4 のリリースに含めます。 commit:1dd1bb561c, commit:fc0f35942a, commit:0ca033f598 -- [[umorigu]] &new{2021-12-05 (日) 18:53:20}; - URLの https://pukiwiki.osdn.jp/_samples/7/?%E9%9A%8E%E5%B1%A41/%E5%90%8D%E5%89%8D よりも /?&a66dc2fa8e の方をcanonicalにして、通常URLを短縮URLの方へリダイレクトするというオプションは作れませんでしょうか? -- [[⁇]] &new{2021-12-27 (月) 10:29:56}; -- 作れます。少しお待ちを -- [[umorigu]] &new{2022-01-10 (月) 18:40:46}; -- この先日話していた「通常URLを短縮URLの方へリダイレクトするというオプション」は使用可能になりそうでしょうか? -- [[?]] &new{2022-03-31 (木) 20:54:59}; -- 「(8) 短縮URLプラグイン ([[s.inc.php>official:Plugins/s.inc.php]]) 短縮URL優先利用カスタマイズ」を追加してみました。これでどうでしょうか? -- [[umorigu]] &new{2022-04-02 (土) 01:52:53}; - 窓の杜では 本体へ手を加えることなく実現 なんて紹介されちゃったけど、編集合戦で混乱してますね。。。 -- [[_]] &new{2022-04-03 (日) 12:19:17}; -- 窓の杜の記事は https://forest.watch.impress.co.jp/docs/news/1399456.html ですよね。このBugTrackが混乱、ってことですか?「本体へ手を加えることなく実現」はできているつもりです。pukiwiki.ini.php (PukiWikiの設定ファイル) だけの変更ですべてコントロールできるので。この記事はかなり開発者(私umorigu)の意図に近い書き方になっていました。(どこにも書いてないのにすごい) -- [[umorigu]] &new{2022-04-03 (日) 16:09:52}; #comment