makeURL と アンカー# params ? について
Posted: 2014年2月15日(土) 22:09
いつもお世話になっております。
古いMODXサイトをデザインごと一新する作業をしていて気になる問題を発見しました。
以前はTinyMCE内でのリンクやウェブリンクでのURL指定時、アンカーやパラメーターを使用したい場合にドキュメントIDの数字のみでは無く、
[~99~]#anchor
といった形の指定が可能で、結果的には[~99~]の部分が正しくURLになり、末尾に#anchorがついていました。
個人的にアンカーを使う事がないので、いつのバージョンから駄目になったのか不明なのですが、1.0.12Jのソースをながめていますと、前述のような書き方をしても makeUrl 内の処理で一旦数値のみにする部分があり、この部分で上記の書き方は破壊されて最終的には正しいURLになりません。 (カッコ左側だけ外れて 99~]#anchor というような形になってしまいます)
以前は可能だったので古いバージョンで動いているサイトには前述の記述方法を使っている箇所が沢山あるケースもありますし、今後もこのような書き方をしたいという需要もあると思います。
こちらでザックリながめて、いつものようになるべく現ソースをいじらない方法で調整してみました。makeURL には引数としてargsを渡す部分があって、この場合の処理はちゃんとされるのかもしれません(が、この引数を使っている例が見つからなかった)。
今回の調整では…
※1.0.12j に先日のrelativeの件を手修正しているの1.0.12r1のコードと一部差異があるかもしれません。
※一部配列がNULLになったり""になったりみっともないのですが、オリジナルをなるべく触らない方針と実害無いと言うことでそのままにしてあります。
※処理の妥当性、他への影響、洩れてるURL指定パターンなどがあるようにも思えます…。
※コード内部を検証できていませんが、前述の記述で問題なく[+url+] [+symlink+] で取り出せる ditto は makeURL を使わず、古くから基本が変らないオリジナルコードで処理しているんじゃないでしょうか。
記述の自由度が徒になって、想定すべきパターンが沢山あり、リファクタリング時にはその辺に注意する必要もありそうです。1場合によっては ditto を参考にして、すりあわせる方法もあるような気がします。
private function _getReferenceListing()
{
$referenceListing = array();
$rs = $this->db->select('id,content', '[+prefix+]site_content', "type='reference'");
$rows = $this->db->makeArray($rs);
foreach($rows as $row)
{
extract($row);
$content = trim($content);
$querysaved = preg_split('/(\?|#)/', $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);// ?,&,#等のパラメータ前後を分けて保存
$content = $querysaved[0]; //パラメータ以外をオリジナルソースと同じ所に保存
if((strpos($content,'[')!==false || strpos($content,'{')!==false) && strpos($content,'[~')===false)
{
$content = $this->parseDocumentSource($content);
}
elseif(strpos($content,'[~')===0)
{
$content = substr($content,2,-2);
if(strpos($content,'[')!==false || strpos($content,'{')!==false)
{
$content = $this->parseDocumentSource($content);
}
}
$referenceListing[$id.'_param'] = implode('', array_slice($querysaved,1));//$querysaved からパラメータを取戻して引き渡せる形で保存。
$referenceListing[$id] = $content;
}
$this->referenceListing = $referenceListing;
return $referenceListing;
}
function makeUrl($id, $alias= '', $args= '', $scheme= 'abs')
{
if($id==0) return $this->config['site_url'];
$makeurl= '';
$f_url_prefix = $this->config['friendly_url_prefix'];
$f_url_suffix = $this->config['friendly_url_suffix'];
if (!preg_match('@^[0-9]+$@',$id))
{
$this->messageQuit("'{$id}' is not numeric and may not be passed to makeUrl()");
}
if(!isset($this->referenceListing)) $this->_getReferenceListing();
if(isset($this->referenceListing[$id]))
{
if(preg_match('/^[0-9]+$/',$this->referenceListing[$id]))
{
$id = $this->referenceListing[$id];
}
else return $this->referenceListing[$id];
}
if(isset($this->referenceListing[$id.'_param'])) $param = $this->referenceListing[$id.'_param'];// param取り出し
if ($this->config['friendly_urls'] == 1)
{
$alPath = '';
if(empty($alias))
{
$alias = $id;
if ($this->config['friendly_alias_urls'] == 1)
{
if(!$this->aliasListing) $this->setAliasListing();
$al= $this->aliasListing[$id];
$alPath = ($al && !empty($al['path'])) ? $al['path'] . '/' : '';
if(!empty($alPath))
{
$_ = explode('/', $alPath);
foreach($_ as $i=>$v)
{
$_[$i] = urlencode($v);
}
$alPath = join('/', $_);
}
if ($al && $al['alias'])
{
if($this->config['xhtml_urls']==='1') $alias = urlencode($al['alias']);
else $alias = $al['alias'];
}
}
}
if(strpos($alias, '.') !== false && $this->config['suffix_mode']==='1')
{
$f_url_suffix = '';
}
elseif($al['isfolder']==='1' && $this->config['make_folders']==='1' && $id != $this->config['site_start'])
{
$f_url_suffix = '/';
}
$makeurl = $alPath . $f_url_prefix . $alias . $f_url_suffix;
}
else $makeurl= "index.php?id={$id}";
$makeurl .= $param; //組み立て後のpathにparamを付加
$site_url = $this->config['site_url'];
$base_url = $this->config['base_url'];
switch($scheme)
{
case 'full':
$site_url = $this->config['site_url'];
$base_url = '';
break;
case 'http':
case '0':
if(strpos($site_url,'http://')!==0)
$site_url = 'http' . substr($site_url,strpos($site_url,':'));
$base_url = '';
break;
case 'https':
case '1':
if(strpos($site_url,'https://')!==0)
$site_url = 'https' . substr($site_url,strpos($site_url,':'));
$base_url = '';
break;
case 'absolute':
case 'abs':
$site_url = '';
$base_url = $this->config['base_url'];
break;
case 'relative':
case '-1':
$site_url = '';
$base_url = '';
break;
default:
$site_url = '';
$base_url = $this->config['base_url'];
}
$url = "{$site_url}{$base_url}{$makeurl}";
if($args!=='')
{
$args = ltrim($args,'?&');
if(strpos($url,'?')===false) $url .= "?{$args}";
else $url .= "&{$args}";
}
if($this->config['xhtml_urls']) $url = preg_replace("/&(?!amp;)/",'&', $url);
$rs = $this->invokeEvent('OnMakeUrl',
array(
"id" => $id,
"alias" => $alias,
"args" => $args,
"scheme"=> $scheme,
"url" => $url
)
);
if (!empty($rs))
{
$url = end($rs);
}
return $url;
}
古いMODXサイトをデザインごと一新する作業をしていて気になる問題を発見しました。
以前はTinyMCE内でのリンクやウェブリンクでのURL指定時、アンカーやパラメーターを使用したい場合にドキュメントIDの数字のみでは無く、
[~99~]#anchor
といった形の指定が可能で、結果的には[~99~]の部分が正しくURLになり、末尾に#anchorがついていました。
個人的にアンカーを使う事がないので、いつのバージョンから駄目になったのか不明なのですが、1.0.12Jのソースをながめていますと、前述のような書き方をしても makeUrl 内の処理で一旦数値のみにする部分があり、この部分で上記の書き方は破壊されて最終的には正しいURLになりません。 (カッコ左側だけ外れて 99~]#anchor というような形になってしまいます)
以前は可能だったので古いバージョンで動いているサイトには前述の記述方法を使っている箇所が沢山あるケースもありますし、今後もこのような書き方をしたいという需要もあると思います。
こちらでザックリながめて、いつものようになるべく現ソースをいじらない方法で調整してみました。makeURL には引数としてargsを渡す部分があって、この場合の処理はちゃんとされるのかもしれません(が、この引数を使っている例が見つからなかった)。
今回の調整では…
- argsの処理とバッティングして2重にargsがついてしまう可能性がある
- 力尽きて rewriteURL 他関連しそうな部分をチェックしていない
- #? 以降だけを考慮しているが 他に考えるべき文字がある気がする
※1.0.12j に先日のrelativeの件を手修正しているの1.0.12r1のコードと一部差異があるかもしれません。
※一部配列がNULLになったり""になったりみっともないのですが、オリジナルをなるべく触らない方針と実害無いと言うことでそのままにしてあります。
※処理の妥当性、他への影響、洩れてるURL指定パターンなどがあるようにも思えます…。
※コード内部を検証できていませんが、前述の記述で問題なく[+url+] [+symlink+] で取り出せる ditto は makeURL を使わず、古くから基本が変らないオリジナルコードで処理しているんじゃないでしょうか。
記述の自由度が徒になって、想定すべきパターンが沢山あり、リファクタリング時にはその辺に注意する必要もありそうです。1場合によっては ditto を参考にして、すりあわせる方法もあるような気がします。
private function _getReferenceListing()
{
$referenceListing = array();
$rs = $this->db->select('id,content', '[+prefix+]site_content', "type='reference'");
$rows = $this->db->makeArray($rs);
foreach($rows as $row)
{
extract($row);
$content = trim($content);
$querysaved = preg_split('/(\?|#)/', $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);// ?,&,#等のパラメータ前後を分けて保存
$content = $querysaved[0]; //パラメータ以外をオリジナルソースと同じ所に保存
if((strpos($content,'[')!==false || strpos($content,'{')!==false) && strpos($content,'[~')===false)
{
$content = $this->parseDocumentSource($content);
}
elseif(strpos($content,'[~')===0)
{
$content = substr($content,2,-2);
if(strpos($content,'[')!==false || strpos($content,'{')!==false)
{
$content = $this->parseDocumentSource($content);
}
}
$referenceListing[$id.'_param'] = implode('', array_slice($querysaved,1));//$querysaved からパラメータを取戻して引き渡せる形で保存。
$referenceListing[$id] = $content;
}
$this->referenceListing = $referenceListing;
return $referenceListing;
}
function makeUrl($id, $alias= '', $args= '', $scheme= 'abs')
{
if($id==0) return $this->config['site_url'];
$makeurl= '';
$f_url_prefix = $this->config['friendly_url_prefix'];
$f_url_suffix = $this->config['friendly_url_suffix'];
if (!preg_match('@^[0-9]+$@',$id))
{
$this->messageQuit("'{$id}' is not numeric and may not be passed to makeUrl()");
}
if(!isset($this->referenceListing)) $this->_getReferenceListing();
if(isset($this->referenceListing[$id]))
{
if(preg_match('/^[0-9]+$/',$this->referenceListing[$id]))
{
$id = $this->referenceListing[$id];
}
else return $this->referenceListing[$id];
}
if(isset($this->referenceListing[$id.'_param'])) $param = $this->referenceListing[$id.'_param'];// param取り出し
if ($this->config['friendly_urls'] == 1)
{
$alPath = '';
if(empty($alias))
{
$alias = $id;
if ($this->config['friendly_alias_urls'] == 1)
{
if(!$this->aliasListing) $this->setAliasListing();
$al= $this->aliasListing[$id];
$alPath = ($al && !empty($al['path'])) ? $al['path'] . '/' : '';
if(!empty($alPath))
{
$_ = explode('/', $alPath);
foreach($_ as $i=>$v)
{
$_[$i] = urlencode($v);
}
$alPath = join('/', $_);
}
if ($al && $al['alias'])
{
if($this->config['xhtml_urls']==='1') $alias = urlencode($al['alias']);
else $alias = $al['alias'];
}
}
}
if(strpos($alias, '.') !== false && $this->config['suffix_mode']==='1')
{
$f_url_suffix = '';
}
elseif($al['isfolder']==='1' && $this->config['make_folders']==='1' && $id != $this->config['site_start'])
{
$f_url_suffix = '/';
}
$makeurl = $alPath . $f_url_prefix . $alias . $f_url_suffix;
}
else $makeurl= "index.php?id={$id}";
$makeurl .= $param; //組み立て後のpathにparamを付加
$site_url = $this->config['site_url'];
$base_url = $this->config['base_url'];
switch($scheme)
{
case 'full':
$site_url = $this->config['site_url'];
$base_url = '';
break;
case 'http':
case '0':
if(strpos($site_url,'http://')!==0)
$site_url = 'http' . substr($site_url,strpos($site_url,':'));
$base_url = '';
break;
case 'https':
case '1':
if(strpos($site_url,'https://')!==0)
$site_url = 'https' . substr($site_url,strpos($site_url,':'));
$base_url = '';
break;
case 'absolute':
case 'abs':
$site_url = '';
$base_url = $this->config['base_url'];
break;
case 'relative':
case '-1':
$site_url = '';
$base_url = '';
break;
default:
$site_url = '';
$base_url = $this->config['base_url'];
}
$url = "{$site_url}{$base_url}{$makeurl}";
if($args!=='')
{
$args = ltrim($args,'?&');
if(strpos($url,'?')===false) $url .= "?{$args}";
else $url .= "&{$args}";
}
if($this->config['xhtml_urls']) $url = preg_replace("/&(?!amp;)/",'&', $url);
$rs = $this->invokeEvent('OnMakeUrl',
array(
"id" => $id,
"alias" => $alias,
"args" => $args,
"scheme"=> $scheme,
"url" => $url
)
);
if (!empty($rs))
{
$url = end($rs);
}
return $url;
}