日本だんでぃ協会
Japan Dandy Association

中の人プロフィール
だんでぃ
web拍手作った人
プログラマーでミュージシャンだと言い張って聞かない43才

おこんてんつ
  トップ
プログラミング (12)
デカ文字ジェネレータ
  音楽 (7)
  ポーカー (1)
  雑記 (1)

リンク
ろじっくぱらだいす
老舗のプログラム系テキストサイト。web拍手を思い付くきっかけをいただいたサイトです。ワタナベさんラヴ!

■テキストエリアに入力したjavascriptと、PHPのnl2br()の相性問題【解決編】

2017/01/12 00:36 作成
では、解決編です!

前回、テキストエリアに入力した、
テストです。
ウェーイ。
<script>
  console.log("テストですよ");
</script>
こんな感じの文章を、PHPのnl2br()で出力すると
テストです。<br />
ウェーイ。<br />
<script><br />
  console.log("テストですよ");<br />
</script><br />
こういうふうにHTMLが生成されてしまい、javascriptが期待通りに動かない。という話をしました。

PHPのコード的には
$body = nl2br( $body );
こんな感じでシンプルに処理しているのですが、もうちょっと処理を工夫する必要がありそうです。

では、どういうふうに処理をしたら良いのか考えてみましょう。

やりたいことは、
・改行コード「\r\n」を自動的に<br />タグに変換したい。
・でも、javascriptも正常に動作させたい。
の2点です。

ようは、<script>~</script>内の改行を<br />タグに変換させないようにすれば良いわけですね。

PHPに、一発で期待通りの処理をしてくれる関数は無いかなと調べてみたのですが、
どうも無さそうなのでPHPのコードを改造してみましょう。

入力した文章の全文を対象として、
「<script>~</script>の内側の改行はそのままだけど、<script>~</script>の外の改行は<br />タグに変換する」
という処理を書こうとすると、たぶん正規表現を使ってややこしい処理をしないといけないと思うので、
もっと簡単に、
「基本的に、改行は<br />タグに変換するが、<script>が出現したら</script>で閉じられるまで改行コードはそのままにする」
という考え方で処理をしてみましょう。

この、「どういう考え方で処理をするか」というのを「アルゴリズム」と言い、
より適切なアルゴリズムを考え出すことができるかどうかがプログラマの腕の見せ所です。

んで、できあがったコードは以下のようになります。
$splitBody = split( "\r\n", $bodyFromDB );
$disableBRbyScriptTag = 0;
foreach( (array)$splitBody as $key => $value ){
  if( strpos( $value, '<script' )!==false ){
    $disableBRbyScriptTag = 1;
  }
  if( strpos( $value, '</script' )!==false ){
    $disableBRbyScriptTag = 0;
  }
  $body .= $value;
  if( !$disableBRbyScriptTag ){
    $body .= "<br />\r\n";
  }else{
    $body .= "\r\n";
  }
}

ざーっと解説すると、
まず文章を改行「\r\n」で分割し、それを順に1行ずつ処理していきます。
でもって、$disableBRbyScriptTagという変数を初期値0で用意しておき、
処理している行に「<script」が出現したら$disableBRbyScriptTagを1に、
「</script」が出現したら$disableBRbyScriptTagを0にします。
んで、最終的に$disableBRbyScriptTagが0なら<br />を挿入し、1なら何もしない。という処理になっています。

これで最初の例文を処理すると、
テストです。<br />
ウェーイ。<br />
<script>
  console.log("テストですよ");
</script><br />
こんな感じになり、概ね上手く行っていることが分かります。

が、最後の行で</script>の後ろに<br />タグが付いちゃってるのが気になりますね。
さっきの処理だと、</script>が現れたら$disableBRbyScriptTagを0にして、
その後に<br />を付けるかどうかの判定をしているので、確かにその通り処理されています。

このままだと、webサイトの記事内にjavascriptを記述した直後に余計な改行が挟まれてしまい、
あまり理想的とは言えません。

なので、先ほどの処理に、
・</script>が出現したら、その次の行から<br />タグを付ける。
というふうに処理を書き換えてみましょう。

できあがったコードはこちらです。
$splitBody = split( "\r\n", $bodyFromDB );
$disableBRbyScriptTag = 0;
foreach( (array)$splitBody as $key => $value ){
  if( strpos( $value, '<script' )!==false ){
    $disableBRbyScriptTag = 1;
  }
  $body .= $value;
  if( strpos( $value, '</script' )!==false ){
    $disableBRbyScriptTag = 0;
    $body .= "\r\n";
    continue;
  }
  if( !$disableBRbyScriptTag ){
    $body .= "<br />\r\n";
  }else{
    $body .= "\r\n";
  }
}
こんな感じで、</script>が出現したら$disableBRbyScriptTagを0にして、即座にcontinueで次のループに処理をスキップさせてやります。

すると、
テストです。<br />
ウェーイ。<br />
<script>
  console.log("テストですよ");
</script>
うまくいきました。

これで、このサイトの記事内にjavascriptを記述しづらかった問題が解決できました。
期待通りに動作するコードが書けると達成感がありますね。プログラミングの醍醐味です。

ちなみに、今回のコードは
・同じ行に「<script>」と「</script>」の両方が出現したときに誤動作をする可能性
がありますが、そんなふうに文章を書くことは多分ないですし、
基本的にこの機能を使うのは僕だけなので、気をつけて使えば良いだけのことなので、
今回はこれで良しとします。

次回は、
・記事内にHTMLのソースコードを書きたいときにHTML特殊文字のエスケープがめんどくさい
という問題について取り組んでみたいと思います。

お楽しみに~♪



前の記事 解決させたいこのサイトのシステムの問題点
次の記事 機能しているHTMLタグと、機能せずにそのまま表示されるHTMLタグを記事内で混在させる
2024 日本だんでぃ協会 All Rights Reserved.