忍者ブログ

カウンター

プロモーション

カレンダー

02 2025/03 04
S M T W T F S
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

AntinomyMy の実験室

   私のWEBアプリ実験室です!

ブログ内検索

楽天でお買い物

twitter

最新トラックバック

最新コメント

忍者アナライズ

ウェザーニュース

バーコード

本を買う

アクセス解析

Google+

[PR]

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


No Image

$_SERVER['SCRIPT_FILENAME']は実行されたスクリプトの位置を返すのではない!

PHP の調べ物

ふと$_SERVER['SCRIPT_FILENAME']はPHP の説明によると
「現在実行されているスクリプトの絶対パス」と書いてあるので
読み込んでいるスクリプト内で使われているなら、
読込先のフルパスが帰ってくるのか?
疑問に思ったのでやってみた。

include()
include_once()
require()
require_once()

などで呼び出している中で$_SERVER['SCRIPT_FILENAME']を使ってみた。

以下、
--------------------------(読み込むスクリプト)--------------------------
script_name.php
------------------------------------------------------------------------------
<?php
function scriptArea(){
 return $_SERVER['SCRIPT_FILENAME'];
}
?>


--------------------------(直接始めに実行するスクリプト)--------------------------
script_area_test.php
------------------------------------------------------------------------------
<?php
include_once "script_name.php";

echo "includeエリア:" . scriptArea() . "</br>";

echo "そのままエリア:" . $_SERVER['SCRIPT_FILENAME'];

?>
 

--------------------------実行結果--------------------------
includeエリア:C:/hogehoge/script_test_area.php
そのままエリア:C:/hogehoge/script_test_area.php


--------------------------結論--------------------------
実行しているスクリプトの名前であって、
読込先のスクリプト内を表示することは出来ない。
あくまでサーバが最初に処理し始めたスクリプトの
名前を返す様だ。


セキュリティーからすれば外部からの見通しがきかなくなる点では安全かもしれないが、
読み込みスクリプトのパスを欲している場合に、この方法で取得出来ないということみたいだ。

それもそのはず、結果からみた予想ではあるが、処理開始始めに決定される変数が
$_SERVERなんだろうと思う。たかがそれだけの事だが、されどそれだけの事だった。

一応メモ書きであぁ~るっ。

拍手[0回]

PR

No Image

POST とPHP だけでリロード対策!JavaScript Cookie session GET データベース一切無し!

PHP の調べ物

PHP だけの言語を用いて、JavaScript Cookie session データベース使用無しのリロード対策作りました。
GET でアドレス後から付け加える方法も無しで、POST情報だけで実現しています。


やり方は、自分のページのAは描画をPHPで行う最に、自分のページA内に
任意のPOSTデータを含む情報を持つページAの形をした別物A’を読む、
そのA'はPOSTデータだけが違うという物。

つまり自分自身の分身A’を読み込んでPOSTデータ処理させる。


以下で説明するスクリプトの場合、初回と同様になる様に
空のデータを0扱いする様にしてあり、処理される。

私のブログにしては妙に短いのはお酒を・・・いるからですw(朝じゃん・・・・)

さてもう眠いですが、うれしさも何も無く酔い・・・いや、軽く頭が軽い状態で、
プログラムを組んでいた訳なんですが、そのソースを惜しげもなく(無様な汚いコードですw)
ここにメモ書きとして掲載しようじゃないか>w<;(早く寝たいですw)


さて、もとい。
ブラウザの違いとか検証は、いい加減にやってみました。(何度も言いますが眠いですw)
Internet Explorer 8 と、 Mozilla Firefox 3.6.17 そして、Safari 5.0.5(7533.21.1 ) ですが、
なぜか私の場合以下のファイル名がフルパスで
http://localhost/flipflop_reload_protect.php
なのですが、Safari だけは最後に / を追加した
http://localhost/flipflop_reload_protect.php/
とアドレス入力に入力しなければ初回の画面が見られませんでした。
Safari の意味不明な仕様に驚き、良いブラウザハックの拾い物をした!と思いつつ?
時間も時間なので次に進みたいと思いますw


では本ソースです。



 

 

<?php
header('Content-type: text/html; charset=UTF-8');
echo '前のPOSTだよ!';
var_dump($_POST);
if(!$_POST['flipflopProtect'])
{
 echo '0の時だけ<br/><br/>';
 //POSTデータ
 $originalPOST = array(
  'flipflopProtect' =>  !(boolean)$_POST['flipflopProtect'],
  'count_submit' => 1 + (integer)$_POST['count_submit']
 );
 $mergePOST = array_merge($_POST,$originalPOST);
 
 
 $contentData = http_build_query($mergePOST, "", "&");
 
 //header
 $header = array(
     "Content-Type: application/x-www-form-urlencoded",
     "Content-Length: ".strlen($contentData)
 );
 
 $context = array(
     "http" => array(
         "method"  => "POST",
         "header"  => implode("\r\n", $header),
         "content" => $contentData
     )
 );
 
 $url = "http://localhost/flipflop_reload_protect.php";
 echo file_get_contents($url, false, stream_context_create($context));
 die();
}
?>

/* 本文(ホームページなど) */
うんたらうんたら・・・
<?php echo "<br/>\$_POST['flipflopProtect']がずっとブラウザ内では1なんですよ! 実際の値:" . $_POST['flipflopProtect'] ?>
<form id="reloadBlock" method="POST" action="<?php echo $_SERVER['REQUEST_URI'] ?>">
 <input type="hidden" name="flipflopProtect" value="0" />
 <input type="hidden" name="count_submit" value="<?php echo $_POST['count_submit']?>" />
 <input id="submitBottun" type="submit" value="<?php echo $_POST['flipflopProtect']==1?'プロテクト中':'何処かにバグ' ?>" />
</form>


 こんな感じでした。

簡単に説明すると
POSTデータの$_POST['flipflopProtect'] は、開始直後はstringでは''で、
これは数値式のinteger で表せば0です。
その0か''の時だけ、自分自身を参照しに行き、
その処理の時に$_POST['flipflopProtect'] を1にしてプロテクトをかけ、
リロード出来ない状態にして、リロードしても処理しない部分を作り上げています、
つまり自分自身にPOSTする処理を無くしています。

そんな感じで見栄えは一緒、でもPOSTデータの内容が違うと言うとてもとても危なげで
奇想天外な事をしています。

これは本当に苦肉の策みたいな物なので、
Webページとしての描画内容はPHPのそのリロードプロテクトの後に
書いている状態なので、その辺を一まとめにするかとか、
色々他のPHPスクリプトを付け加えて実践的にすると
どんな感じになるかとかまでは書いていません
(お酒飲んでいるから早くブログを書き終えたいですw朝ですしw)。

そんな感じです<_ _>


一応ですが書くとすると自身が表示させたい下部の
/* 本文(ホームページなど) */
と書いている部分以降に
if(!$_POST['flipflopProtect']){  ~~~~~  }
この ~~~~~ にリロード時に処理させたくない内容を入れればいいんです。

別に1ページのソース内に細切れに<?php そのソース  ?>でいいと思うんです。

そんな感じで今回のメモは終わりにしたいと思います。
お酒入っているので、見直しは後ほどにします。


お休みっ! ^v^ノシ


追伸:(結構ウェイト大きいです)
なんかやはり、自身を参照している部分が不可思議な表示です。
推測ではキャッシュしている部分とPHP内部で計算してしまう部分と両方を用いて
描画しているのではないかと思います。
以下の修正変更したソースのカウントが動いてしまうのがその良い例です。

A=自身 A’=自身を呼び出した物とします。

また実はAとA'の両方にPOSTメッセージは存在し、別々に処理されています。
この当りもややこしいです。
そしてA'内部からAのグローバル変数やAのPOSTデータは操作できません。

書籍などには無い、不思議なブラウザとサーバ処理(ApacheやPHPの挙動)が見え隠れします、
環境に依存する可能性もあるので気をつけてください。

とりあえず初回もカウントしない様にして、内部の時間も数値化してみました。
A'の描画が不思議であるのがわかると思います。




 <?php
header('Content-type: text/html; charset=UTF-8');
echo '前のPOSTだよ!'.time().'<br/>';
var_dump($_POST);
if($_POST['flipflopProtect'])
{
 echo '0の時だけ'.time().'<br/>' . $_POST['flipflopProtect'] . '<br/>';
 var_dump($_POST);
 //POSTデータ
 $originalPOST = array(
  'flipflopProtect' =>  !$_POST['flipflopProtect'],
  'count_submit' => 1 + (integer)$_POST['count_submit']
 );
 $mergePOST = array_merge($_POST,$originalPOST);
 
 
 $contentData = http_build_query($mergePOST, "", "&");
 
 //header
 $header = array(
     "Content-Type: application/x-www-form-urlencoded",
     "Content-Length: ".strlen($contentData)
 );
 
 $context = array(
     "http" => array(
         "method"  => "POST",
         "header"  => implode("\r\n", $header),
         "content" => $contentData
     )
 );
 
 $url = "http://localhost/flipflop_reload_protect.php";
 echo 'ここから自分を呼んだ後<br/>';
 echo file_get_contents($url, false, stream_context_create($context));
 die();
}else{
 echo '<br/>リロードブロックと初回だけのはずなのに!<br/><br/>';
}
?>

 

/* 本文(ホームページなど) */
うんたらうんたら・・・
<?php echo "<br/>\$_POST['flipflopProtect']がずっとブラウザA内では0か''なんですよ! 実際の値:[" . $_POST['flipflopProtect'] . "]" ?>
<form id="reloadBlock" method="POST" action="<?php echo $_SERVER['REQUEST_URI'] ?>">
 <input type="hidden" name="flipflopProtect" value="1" />
 <input type="hidden" name="count_submit" value="<?php echo $_POST['count_submit']?>" />
 <input id="submitBottun" type="submit" value="<?php echo $_POST['flipflopProtect']==1?'何処かにバグ':'プロテクト中' ?>" />
</form>
<?php echo var_dump($_POST) ?> 


 

'<br/>リロードブロックと初回だけのはずなのに!<br/><br/>' って所が、カウントした場合でも表示されています。
このIF文はどちらかしか動かないからIFなのですが・・・
IFでそうであった場合と、そうでなかった場合、両方の結論が出てしまう様に見える!


キャッシュやインスタンスなどの絡みと読み込みの絡み、AとA'の関係から、
結果的にブラウザの持つAの変数操作はされている様な挙動を
かろうじて実現出来ている様ですが、実際の絡みが不明な為、
よーく考えて使うべきです。

私はフォームメールに実装しようと思います。

A'内で処理する部分にブラウザへの描画関数は用いない方が無難だと思います。

それはキャッシュやインスタンス内の何かをAで妙な反映をさせない対策になると思います。
Aはブラウザで表示できる物全ての処理をし、
A'は間違っても表示処理を入れない方が良いと言うことです。


多分・・・・それが無難で一番良い方法になると思います^^;


以上長ーーーーーいメモでした<_ _>

拍手[2回]


No Image

PDO でDDL を使う。

PHP と SQL の調べ物

さて、長い何時もの様に前置きから、
読むのが面倒な人は<本題>から読んでね!
それでももっと面倒な人は<結論>だけ見てね!
では以下長々と・・・

プログラミング言語の多くのマニュアルを書いた人は
プログラミング言語を作った人本人であろうとなかろうと
どうも全部羅派し忘れている節がある。

私もそうだが作ることの方がメインで、説明をしたりするのがサブ的な役割に
なってしまうこともある、それはモチベーションや目的、立場によって変わるのだろう。

物事を追求しなければ物事が解決できないとして、
疑問を持って追求しているのは暇人だからではない、
性格もあるとして、それをしなければ先に進まないからだ。

そして案外疑問に思ってもやってみていないことってある。
しかしその疑問を解決する事が、プログラミング言語と言う道具を使う方法が
上手くなる方法であるのも現実として間違えなく、
それこそが私は「ノウハウ」と言うのだと思う。

一見オタク要素満載であるが、職人気質な人はオタク要素があるのか、
それともオタク要素がある人は職人気質なのか、結局極めることと興味を持つことは
紙一重であり、続けなければもったいない事だと思う。

たとえ周りが色を付け、理解しなくとも気にする事も余り無いのかもしれない。境遇にもよるが。

私の部屋に貼ってあるメモ書き、戒めと言うかモチベーションの向上の為に貼ってある言葉がある。

「考えていても
   やってみなければ
       実感できないよ」

まぁ、考えることの割合が多く、実際にやってみる事の方が後になる私にはぴったりな文句。
今回も思ったらすぐにしてみれば終わる話だったのだが、その悪い私らしい所が
結果的にこのブログと成っている。このブログの99%は迷い悩みから生まれた物ですw(当社比率100%)

で、何について疑問に思って、今回は何を述べたいのか?
以下本題に入ります。

<本題>
PHP でMySQL を用いる時に、PDO を使った場合、データベースの操作、DMLについては
簡単に説明が見つかる、しかしDDLについては説明がない。
PDO をPHP公式のマニュアル を見る限り、どう見ても初めからMySQL のルート権限で
MySQL に接続してデータベース本体を作ったり、権限を操作する関連の説明を簡単に見つけられない。

唯一見つけたのは、ログインした後でDDLを操作する説明が、
MySQL 関数 (PDO_MYSQL)
これを読んでいると、一度データベース選択と共に接続しDMLが操作できる状況になった上で、
DDLのクエリーを発行すれば良いと書いてある気がする。
しかしこれでは、落ち度があるのだ。

もしMySQL に操作できるデータベースが1つも無かった場合には、ログイン出来ない様に感じてしまう。
つまりPDOでは空っぽのMySQL にログインしてデータベースを名前を付けて1から構築して
いくスクリプトは書けないんじゃないか?と思わせてしまう。

それはこの公式マニュアルの何処に原因があるのか?

それはこの説明
PDO::__construct 接続、および接続の管理にあるパラメータdsn の説明不足に問題がある。


<結論>
PDO のデータベース接続インスタンスの生成方法で
一見DML用途にしか接続インスタンスを生成出来ない様に見えるが、
実際はdsnパラメータはPHP公式マニュアルにかかれていない省略形の形を用いると
DDLを初めから操作できる。

例えばPHP公式マニュアルなどや他のサイトには、
dsnパラメータはこの以下2つのどちらの書き方でも使えるが、
データベースサーバへ接続するときにデータベース名が省略していいかどうか書いていない。

dsnの例
'mysql:dbname=testdb;host=127.0.0.1'
'mysql:host=127.0.0.1;dbname=testdb'
である。

これは元々データCスに'testdb'が存在しているのが前提である。
しかし全く空な場合の使い方がかかれていない。

これを試しにdbname=testdb
なしに記述し、ルートの名前とパスワードでMySQLに接続してみた。
そして'CREATE DATABASE PDO_sample_db'というクエリーを発行して確認してみた。

そうするとDDLの操作がMySQL(データベースサーバ)に接続した直後から何も意識せずにDDLが使える。

つまり
上の例で言えば、dbname=testdbなしにした
'mysql:host=127.0.0.1;'
をdsnに指定すれば、データベースを指定しない形でログインできた。
勿論ルート権限のIDとパスワードで試した。

<?php

$user='rootxxx';
$pass='passxxx';
$dsn='mysql:host=localhost;';
//DDL操作を念頭に入れない場合はいきなり以下の様に前に用意したデータベースを指定すれば良い
//$dsn='mysql:host=localhost;dbname=test';

try {
    $dbh = new PDO($dsn, $user, $pass);
 
 //データベースを作る
 $dbh->query('CREATE DATABASE PDO_sample_db');
 
    foreach($dbh->query('show databases') as $row) {
        print_r($row);
  echo '<br>';
    }
    $dbh = null;
} catch (PDOException $e) {
    print "エラー!: " . $e->getMessage() . "<br/>";
    die();
}
?>
 


結果:
 ・・・略・・・
Array ( [Database] => pdo_sample_db [0] => pdo_sample_db ) 
 ・・・略・・・

しっかりDDLが動いていました。
同様にクエリーを'DROP DATABASE PDO_sample_db'にして試しても、
しっかり消える挙動が成立しました。

私がなぜDDLに拘るかと言うと、わざわざ他の環境にデータベースを操作するPHPスクリプトや
環境ごと移管しようとした時に、データベースを再び作る煩わしさから逃れる為などがあります。

他の人にスクリプトごと差し上げるとしたら、データベースの構築設定無しでは動かないのに、
他の人にわざわざDDLのコマンドを教えたりしてデータベースを構築させるなんて言うほど手間な事が
無いと思うからです。

何かの理由で1から構築したり復旧する場合も同様だと思います。
ただ簡単にDROP DATABASE でデータベースが消えてしまうので、消す場合には
幾つかの注意と警告を促し、誤ってデータベースを消してしまわないスクリプトにしておくべきだと
思います。

※※※ それとレンタルサーバはデータベースの数の制限があるレンタルサーバがある気がします。 ※※※
なのでその辺もしっかり調べておかなければ、レンタルサーバを提供している側が意図しない数の
データベースをユーザーが扱っているのを知ったら問題が発生する恐れもあります。
その辺も調べておく必要があると思います。

それ以前に、レンタルサーバでデータベース数に制限がある所では、
ルート権限のIDとパスワードを教えてくれないかもしれませんが。
それでも'CREATE DATABASEができなくてもDROP DATABASE
はデータベースを作成した後の話なので、出来てしまう気がします。
その辺もきをつけなければいけないと思います。

とりあえず、PDOでもDDLが使えた! わーい。
あとGRANTの設定で、
$setUser_php_to_php_sample_db =<<<DDL
 GRANT ALL PRIVILEGES ON php_sample_db.*
 TO php@xxx IDENTIFIED BY 'passwordxxx';
DDL;

このクエリーを発行して権限も設定できるかどうか試してみるべきだと思いますが、
これは後回しにしようと思います、多分できるでしょう。
またこれもレンタルサーバなどの設定によっては、下手な設定にしない方が
良いかもしれません。

拍手[0回]


No Image

mb_internal_encoding 関数(mbstring.internal_encoding 設定) とstrlen 関数,mb_strlen 関数の不思議な関係

PHP の調べ物

さてさて、毎回Web上にある設定の変更できないレンタルのサーバの環境とローカル環境の違いを把握して・・・どうのこうの・・・
みたいなややこしいことをしてきた結果、当たり前であろう事により一段と近づく結論が浮かんだ。

それは設定の修正が効かない側のサーバ(レンタルサーバ)の環境に極力ローカルの開発環境も整えること。

という結論が出た、とても当たり前と言えば当たり前だ・・・。

文字化け以外にも、文字カウントを失敗するないていうことが、
デフォルトの 内部文字エンコーディング ->mbstring.internal_encoding (php.ini内)

関数で指定した内部文字エンコーディング ->mb_internal_encoding 関数
によって起こる事が判明した。

まず、この結論にたどり着くまでに気がついた事柄を述べる。
<まずテストする為のソースコード>
<?php
$string = '内部文字エンコーディング環境のTESTです!'.mt_rand(0,9).mt_rand(0,9).mt_rand(0,9);

// 文字の長さの検出の色々な仕方
var_dump(strlen($string));     //内部文字エンコーディングが違う環境に依存しない(バイト数)
var_dump(mb_strlen($string));    //内部文字エンコーディングが違う環境に確実に左右される
var_dump(mb_strlen($string ,'UTF-8'));  
//内部文字エンコーディングが違う環境に依存しにくい
?>

これを参考に以下、1. 2. 3. を見てもらえば判ると思う。

1.文字数をカウントする計算式に狂いが生じる。
内部エンコーディング:ISO-8859-1

内部エンコーディング:UTF-8
では部分的に違う出力結果になる。

内部エンコーディング:UTF-8 の場合、上から答えが int 61、 int 25、 int 25 となる。
内部エンコーディング:ISO-8859-1の場合、上から答えが int 61、 int 61、 int 25 となる。

2.カウントしたい文字列が1文字が1バイト以上の文字である場合には、
 文字数カウントに使えない関数がある。

つまりこの例のスクリプトの場合は、
var_dump(mb_strlen($string));    //内部文字エンコーディングが違う環境に依存しない
がバイト数でしか文字列がカウントできないです。

そしてまた、文字としてカウントできるとしても、正しい文字エンコーディングを指定していない場合、
存在しない文字を他の文字に読み違えたりして間違って文字を認識したりした結果、
文字数が変わってしまうのが、
var_dump(mb_strlen($string));    //内部文字エンコーディングが違う環境に確実に左右される
です。

また、内部エンコーディングを指定して文字数をカウントさせているので、一番文字数のカウント間違えが
少ないと思われる、
var_dump(mb_strlen($string ,'UTF-8'));  //内部文字エンコーディングが違う環境に依存しにくい
ですが、
依存しにくいと書いているのは、保存形式によって保存された文字が
本当に指定したコードで文字を間違えて文字化けなどをせずに解釈できるのかどうかの
テストは今回行っていないからです。

3.サーバの環境によってデフォルトの設定があるので、
 文字数カウントの関数の引数が任意であっても指定した方が環境が変わっても、
 スクリプトが同様に動く可能性が高くなる。

さて、ではサーバ側が例えばレンタルサーバであって、
php.ini 内のmbstring.internal_encoding の値がない(no value)場合なども考えられる。
その場合には、私のテスト環境のWindows とレンタルLinuxサーバ ではどちらもISO-8859-1 になってしまった。

phpinfo 関数で調べると、ISO-8859-1 になっている箇所が
iconv.input_encoding, iconv.internal_encoding, iconv.output_encoding
だけであったが、この設定以外にISO-8859-1 が使われている場所が
内部的にあるのかどうか?までは調べていない。

1つの環境内でphp.ini 内でデフォルトの内部エンコーディングが変化することなどは
開発環境では望ましくないが、他のサーバの設定も把握してスクリプトを書くのならば、
やはり環境を一致させた状態、つまりphp.ini を一緒にして完全に作るか、
それかスクリプト内部で

// 内部文字エンコーディングをUTF-8に設定
mb_internal_encoding('UTF-8');

とかするしかないと思う。

しかし確実なのは両方の対策をとっているのが望ましい気がした。



最後にphp はHTML やXHTML に埋め込み使うのであるが、
ヘッダーを出力できるという利点がある。

ヘッダーを解釈するのはWebサーバ上のApache などがMIME を解決して
クライアントのブラウザの挙動に繋げているのは前回説明したが、
それ以外にもブラウザの挙動に直接つながっているのだと分かる指定の仕方があった。

//ブラウザがこのスクリプトの出力の文字コードを割り出す為のヘッダー
header('Content-type: text/html; charset=UTF-8');

これを組み込んでおくことと、組み込まずにおくことでは
文字が化けるか化けないかが変わるのである。

ブラウザで確認したのは、IEであるが、
自動でUTF-8 になっているか、それとも今回のスクリプトソースをUTF-8で保存し
header('Content-type: text/html; charset=UTF-8');
をつけず、表示しようとすれば、環境にもよる(php.iniの設定に依存すると思う)が
シフトJISとなってしまって文字化けしてしまうことがある。

しかし不思議なことに、ブラウザのソースを見ても
<html>
<head>
ココ
</head>
などという場所に文字コードの指定や書いた形跡は無い。
それなのに文字コードは間違えがない設定になって表示されるのである。

また、文字コードを間違えない状況になった後でも、
ブラウザの自動選択を指定するとなぜか文字化けを起こすと思う。
これはブラウザが送ったヘッダーで表示の仕方が指定されていたが、
ブラウザの持つ文字の自動認識の方法を使って文字列を再検出した為だろうと
予測される。

これはブラウザの更新(F5キー)でヘッダー関数ででUTF-8 を指定しているのであれば
再び元に戻るのでとても面白い仕組み的な側面が見えると思う。

最後に神経質に直したスクリプトの例を載せておこうと思う。

<?php
//ブラウザがこのスクリプトの出力の文字コードを割り出す為のヘッダー
header('Content-type: text/html; charset=UTF-8');

// 内部文字エンコーディングをUTF-8に設定
mb_internal_encoding('UTF-8');


$string
= '内部文字エンコーディング環境のTESTです!'.mt_rand(0,9).mt_rand(0,9).mt_rand(0,9);

// 文字の長さの検出の色々な仕方
var_dump(strlen($string));     //内部文字エンコーディングが違う環境に依存しない
var_dump(mb_strlen($string));    //内部文字エンコーディングが違う環境に確実に左右される
var_dump(mb_strlen($string ,'UTF-8'));  
//内部文字エンコーディングが違う環境に依存しにくい
?>



こんなこんな感じだろうと思う。


そして余談だが、UTF-8 でソースを保管する場合に、
もしソース中で文字を自身が指定したフォントを用いた画像で出力する為の、
imagefttext imagettftext 関数で画像を使う場合、
保存形式をUTF-8(BOMあり)ではなくUTF-8(BOMなし)にして保存しなければ、
画像データの先頭まで、ファイル形式の構造情報が3バイト付加されてしまうので、
画像データとして用いることができなくなるので注意が必要です。
UTF-16だと2バイトのBOMが付き、それは意味があるそうですが、
UTF-8でのBOMは実際には無意味なものらしいです。
詳しくは、BOM をどうぞ。

また画像を出力するスクリプトに
//ブラウザがこのスクリプトの出力の文字コードを割り出す為のヘッダー
header('Content-type: text/html; charset=UTF-8');
という内容は必要ないです。


ちょっと独り言:
当たり前って難しい・・・当たり前になるって難しい・・・当たり前に成る頃には忘れていたり、大したことがないこと・・・でも大切・・・

以上長いまとめのメモでした。

拍手[0回]


No Image

PHP GD ライブラリ で、Apache TypesConfig の落とし穴

Apache と PHP の調べ物

 さぁ、今回は3日で通算8時間ほどハマったPHP の GD ライブラリ を使おうとした時の
事柄を書いて置こう。

 結果から言うと、ブラウザが識別できるヘッダー情報を付加して送るPHPの関数である
header 関数は、header 関数でMIME を設定しただけではブラウザ 側がしっかり認識できない
と言うことです。

 すぐに対象方法が知りたい場合はかなり読み飛ばして<対処方法>を読めば早いです。

 またそれ以前にPHP のGD ライブラリで画像を吐き出させようとheader 関数を呼ぶと
その時点でheader が既に呼び出されてエラーを出してしまう場合は、
php.ini の設定でアウトプットのバッファー設定がオフだと何かPHP関連のファイルを
ブラウザに出力する記述があった場合に即座にheader をPHP が吐き出してしまう様です。

 なのでoutput_buffering = off

に成っているのならば、

output_buffering = 4096

などに変更しましょう、それでなければほとんど、header 関数自体が使えないと
思った方が良いかもしれません。

 header 関数でMIME を設定しただけではブラウザ 側がしっかり認識できない方の理由はどうも、
ブラウザが直接ヘッダーを確認する前に、どうもWebサーバ が間に入り、
その情報を操作してしまっているみたいです。

 私の場合はXAMPP などは使わず、WAMP 環境を個別にインストールと設定を行い、
環境を整えてきました。
 もしかすると私が使ったことが無いXAMPP などを使っている人は、
設定がもうされていたりして困らない事なのかもしれないですが、そこは未確認です。


--- 知ってたら飛ばしていい所 ---
 ま、でも知らない人にまずGD ライブラリ とは何か?
と言うと、PHP スクリプトを用いて文字や図形をWBMP やGIF 、PNG、JPEG などの画像情報を
作り、それをWeb サーバ上の溶媒に保存したり、Webサーバ に保存しないでメモリ上にある
画像情報 と、ブラウザ がどんな情報か解釈できるヘッダー情報 を追加して、
それをApache などのWebサーバ を通してブラウザに送り、
ブラウザ側に表示できるという一風変わった、しかし優れた物である。

多分、よく使われていると思われる場面というのは、
新規にフリーメールなどや何処かの会員に登録する時に、
不正にアカウントを取得されない様に画像に数字や文字が書かれ、
それを誤り無く入力しなければいけない場面で使われていると思う。

 それでは簡単な例を載せようと思いましたが、
基本的にGD ライブラリのImage 関数の例にある事柄をしようとすると
それが出来ないので、その例はそのImage 関数のマニュアルに書いてあるので、
割愛し、重要なheader 関数の事柄だけを書いてみます。


imagegif 関数の場合
   header('Content-Type: image/gif');

imagepng 関数の場合
  header('Content-Type: image/png');

imagejpeg 関数の場合
  header('Content-Type: image/jpeg');

ここで定義しているのでブラウザ側はこれで動く様に思える、
しかし実は
Apache でMIME のタイプが指定されていなければ、ブラウザで判断出来ない様だ。

それが分かったのは、レンタルサーバに上げると自身の書いたPHPスクリプトは動く、
phpinfo 関数を使ってWindows 環境とLinux 環境の違いはあれ、ほぼ一緒にしているのに
サーバに上げた方は正常に画像がブラウザで表示されるのに、ローカル側では
ブラウザで表示されない、そこで気がついたのだった。

<対処方法>
 ここで重要な対象方法ですが、とても簡単です。
Apache の設定ファイルにPHP で自分で使いたいMIME タイプを
「httpd.conf」の中にAddType でMIME を個別に指定するか
それとも、実はApache を普通にインストールすると付随している
設定ファイル(mime.types)を読み込む記述をすると、
MIME タイプの設定が完了します。(勿論Apache は再起動しなければいけません)

このどちらかの方法でMIME タイプを読み込めると言うことです。

もし、「mime.types」のファイルを使わないならば、
の方法のApache の設定ファイルの「httpd.conf」の中に

AddType image/jpeg .jpeg .jpg .jpe

と書けばいいのです。

設定ファイル「mime.types」は、中の定義で基本的なMIME タイプの設定がかなりの量が成されています、
これを基本ベースとして足らないMIME の付け加えや修正を行えば便利だと思います。

の「mime.types」を読み込む設定を「httpd.conf」の中に書くには、

TypesConfig conf/mime.types

と1行書くだけです。
これだけの事が分かるまでに私はPHPの挙動の方を疑って結構悩みました。

さて、「mime.types」のファイルで例えば
JPG ですが、どの様に定義されているかを見ると、

image/jpeg     jpeg jpg jpe

と書いてありました。

このどちらかを書けば、情報は送信されていてサイズもあるのに表示されないという
悩ましい事態から抜け出すことが出来ると思います。

最後に注意点というか論点ですが、「mime.types」のファイルを用いる場合に、
自身が使いたいヘッダー情報があるかどうかを確認した方が良いかもしれません。
使いたいMIME タイプが定義してあるかどうかの設定なのですから。

- - - - - -
追伸:
もしもPNGファイルの画像を出力するスクリプトをPHPで書くならば、
そのスクリプトの保存形式をUTF-8の場合は必ずBOM無しにしなければいけません。

imagefttext imagettftext 関数で画像を使う場合、
保存形式をUTF-8(BOMあり)ではなくUTF-8(BOMなし)にして保存しなければ、
画像データの先頭まで、ファイル形式の構造情報が3バイト付加されてしまうので、
画像データとして用いることができなくなるので注意が必要です。
れこは関数が画像をファイルとして出力する機能が衝突するからでしょうか?
不可思議な仕様ですが、PHPの画像生成関数は現在BOMに依存している様です

UTF-16だと2バイトのBOMが付き、それは意味があるそうですが、
UTF-8でのBOMは実際には無意味なものらしいです。
詳しくは、BOM をどうぞ。


長い個人的なメモなのか説明なのか毎回分かりませんが、以上ですw

拍手[1回]


[PR]