[ Tags :: PHP ]

PHPのshuffle()が連想配列では使えない件について

↓こんな連想配列があったとして、
$AmericanLeagueEastDivisionTeams = array(
    'BAL' => array('city' => 'Baltimore', 'name' => 'Orioles'),
    'BOS' => array('city' => 'Boston', 'name' => 'Red Sox'),
    'NYY' => array('city' => 'New York', 'name' => 'Yankees'),
    'TB' => array('city' => 'Tampa Bay', 'name' => 'Rays'),
    'TOR' => array('city' => 'Toronto', 'name' => 'Blue Jays'),
);

shuffle()してみると…
shuffle($AmericanLeagueEastDivisionTeams);
var_export($AmericanLeagueEastDivisionTeams);
↓結果(見やすいように整形済み)
array (
  0 => array ('city' => 'Boston', 'name' => 'Red Sox'),
  1 => array ('city' => 'Toronto', 'name' => 'Blue Jays'),
  2 => array ('city' => 'New York', 'name' => 'Yankees'),
  3 => array ('city' => 'Tampa Bay', 'name' => 'Rays'),
  4 => array ('city' => 'Baltimore', 'name' => 'Orioles')
)
はい。キーが破棄されて、数字キーに割り当て直されちゃいます。

じゃあ、array_rand()でランダム順のキーを取得しよう、と思った。
$len = count($AmericanLeagueEastDivisionTeams);
$abbrs = array_rand($AmericanLeagueEastDivisionTeams, $len);
var_export($abbrs);
↓結果
array (
  0 => 'BAL',
  1 => 'BOS',
  2 => 'NYY',
  3 => 'TB',
  4 => 'TOR',
)
よし、この「キーがランダム順に入った配列」を使おう。
…あれ?並べ変わってなくね?
たまたまかな(^o^;

再実行→やっぱりおんなじ→再々実行→やっぱりおんなじ→(無限ループ)
全件取得する場合は並べ換えて紅の豚?
マニュアルに書いといてよ。そんなこと。。

まあ、array_keys()を使ってそれをshuffle()すれば同じ事はできるか。
$abbrs = array_keys($AmericanLeagueEastDivisionTeams);
shuffle($abbrs);
var_export($abbrs);
↓結果
array (
  0 => 'NYY',
  1 => 'BOS',
  2 => 'TB',
  3 => 'BAL',
  4 => 'TOR',
)
当然ながらうまくいく。再実行すればまた並び変わる。

せっかくだから、関数化しておくか。
PHPオリジナルの命名則にあわせて、ashuffle()と名付ける(※)。
オリジナルのshuffle()が並べ替えが成功したかどうかを返しているので、とりあえずtrueを返すことにする。
function ashuffle(&$array) {
    $keys = array_keys($array);
    shuffle($keys);

    $res = array();
    for ($i = 0, $_l = count($keys); $i < $_l; $i++) {
        $res[$keys[$i]] = $array[$keys[$i]];
    }

    $array = $res;
    return true;
}

これで、
ashuffle($AmericanLeagueEastDivisionTeams);

$rank = 0;
foreach ($AmericanLeagueEastDivisionTeams as $abbr => $team) {
    printf("%d: %s %s (%s)¥n", ++$rank, $team['city'], $team['name'], $abbr);
}
↓結果

1: New York Yankees (NYY)
2: Baltimore Orioles (BAL)
3: Tampa Bay Rays (TB)
4: Boston Red Sox (BOS)
5: Toronto Blue Jays (TOR)
成功。


※:蛇足。
この記事を書く時に、そういえば、もうすでに誰かが作ってるよな。"ashuffle()"って名前で。
っと思ってググってみたら、どうやら無いみたい。。
ってことは、このページがひっかかるようになるんだ。ウシシ( ̄ー ̄)

PHP

— posted by たか at 12:12 am   pingTrackBack [0]

PHPSESSIDの無限ループ

Cookieが無効な環境のために、PHPのセッションID(デフォルトで"PHPSESSID")をURLに(クエリストリングとして)くっつけてリダイレクトする。
ということはモバイル向けサービスで良くあることだと思う。
んで、ローカルでテスト中、件の無限ループが発生した。
apacheのアクセスログを見ると、
hoge.php?foo=var
hoge.php?foo=var&amp;PHPSESSID=(SSID1)
hoge.php?foo=var&amp;amp;PHPSESSID=(SSID1)&amp;PHPSESSID=(SSID2)
...
hoge.php?foo=var&amp;amp;amp;amp;amp;amp;PHPSESSID=(SSID1)&amp;amp;amp;amp;amp;PHPSESSID=(SSID2)&amp;amp;amp;amp;PHPSESSID=(SSID3)&amp;amp;amp;PHPSESSID=(SSID4)&amp;amp;PHPSESSID=(SSID5)&amp;PHPSESSID=(SSID6)
...

すなわち、
hoge.php?foo=var
にアクセスがあって、セッションIDがついてないのでつけましょう。そしてリダイレクト。
hoge.php?foo=var&amp;PHPSESSID=(SSID1)
おやおや、セッションIDがついてないのでつけましょう。そしてリダイレクト。
hoge.php?foo=var&amp;amp;PHPSESSID=(SSID1)&amp;PHPSESSID=(SSID2)
っという具合に、無限に繰り返し、URL長エラーになるまで繰り返してた。っということ。

原因は、"&"であるべきセパレータが"&amp;"になっていること。
"PHPSESSID"のつもりが、"amp;PHPSESSID"になっちゃったわけだ。

さて、誰の仕業だろう?
プログラムで発行しているときにやっちまった?
Net_URLを使ってるから、この子のせい?
PHPが自動で付けたもの?
Apacheが変換した?

とまあ、色々調べてしまったよ。
結論を言うのは簡単だなぁ。。
その結論とは、
PHPの設定がヘンだった。それと、PHPの設定をNet_URLが忠実に読み込んでいたから。

Net_URL::getQueryString()で、クエリストリングのセパレータを、ini_get('arg_separator.output')で取ってきてます。

んで、オイラのWindowsなxampp環境では、php.iniファイルで下記の通り指定していた。
arg_separator.output = "&amp;"


むむむ。なんでこんなことしたんだっけ?
はい。これをコメントアウトして、サービスを再起動すると、…ちゃんと動いた。


以下、苦労した原因を箇条書きしておく。繰り返さないように(´・ω・`)
・デバッグ用にリダイレクトURLをHTML出力してみたわけだが、<textarea>タグ内ならば生のソースが表示されるハズ、と初歩的な勘違い
  → "&"と表示されて、うーん、ここまでは上手く行ってるのに…と誤解。実際は"&amp;"だったのに
・php.iniを書き換えたら即時適用されそうな気がしていた
  → apacheを最起動しないと読まないよ。Windowsの場合サービスの再起動が必要。
・php.iniを変更するなら、なぜそうしたかメモっておこうよ。。
・グーグル先生を過信し過ぎ(←あえて冗長表現してみた)。

PHP

— posted by たか at 03:27 pm   pingTrackBack [0]

勝手xlsでセル内改行

WebアプリでCSVを吐き出して、利用者様にはExcelで開いてもらう場合、おそらく巷で良く起こっていると思われる現象…
『電話番号の最初の"0"が表示されんぞゴルァLink

というわけで、CSV形式ではなく、Excelが直接読める形式にすることによって、Webアプリ側で書式設定を施す。という方法。

◎ヘッダーで、
Content-Type: application/vnd.ms-excel
Content-Disposition: attachment; filename="katte.xls"
◎ボディのHTMLヘッダで、
<style>
.string {mso-number-format:"¥¥@";}
</style>
◎ボディのHTMLボディでTABLE組んで、
<td style="string">09090909090</td>

というお話は今回のメインではないので、詳しく知りたい方は上記キーワードでググれかし。

で、上記方法で、セル内改行を行いたい場合に困った。
まずは、改行文字のみだと、その部分に空白が表示されるだけ。
というわけで、PHPでいうところのnl2br()で<br>タグに変換すると…
なんか、改行の数だけ行数が増えて、同行の改行なしのセルは勝手に結合されて、、、

グーグル先生に聞いてもご機嫌ナナメだし…
でも、ヒントは賜った。

Excelシート自体が検索に引っ掛かり、そのソースが表示された。
…実際に作ってソースを解析すればいいんだ。
さあ、Excelを起動、セル内改行を作って、「Webページとして保存」。テキストエディタで開く。
どんなスタイルを使っているんだ…?

紆余曲折を経て、
br {mso-data-placement:same-cell;}
brタグ全体に指定されてたのね…orz
セルに指定されたスタイルのどれが効いてるのか必死に探しちまったよ。
早く気付けよ、俺。

というわけで、さっきのスタイル指定を
<style>
br {mso-data-placement:same-cell;}
.string {mso-number-format:"¥¥@";}
</style>
な感じにしましょう。

PHP セル内改行 勝手xls csv

— posted by たか at 11:31 am   pingTrackBack [0]

拡張子htmlのままPHPとして動かす場合

拡張子がhtmlなPHPファイルをそのままPHPとして動かす場合、HTML先頭の宣言がPHPとして解釈され、結果エラーとなる。
PHP開始タグを「
よって、この『ショートオープンタグ』設定を無効化すればよい。
ただ、この設定を行うためのディレクティブ"short_open_tag"なのだが、『変更の可否』がPHP_INI_ALL、ただし、
PHP <= 4.0.0 では PHP_INI_ALL、PHP < 5.3.0 では PHP_INI_PERDIR。
だそうで、PHP5.3.0以降なんてまだあまりつかわれていないだろうから、
PHP_INI_PERDIRであって、つまり『php.iniかApacheのhttpd.confか.htaccessで設定する必要がある』
ので注意が必要。。
(ini_set()できません、ってこと)


追記。あとから読んで、はてな?と思ったので。
上記で、PHP5.3.0以降の場合が書いてないじゃん、って思ったけど、最初に
    『変更の可否』がPHP_INI_ALL
って書いてあって、んで、
    PHP < 5.3.0 では PHP_INI_PERDIR
だから、PHP5.3.0以降はPHP_INI_ALLってことみたい。


というわけで.htaccessの例。
AddType application/x-httpd-php .html
php_flag short_open_tag off

ちゃんと動くかは環境次第なので、あしからず。

PHP

— posted by たか at 08:41 pm   pingTrackBack [0]

Content-Lengthヘッダを仕込んだはずなのに…orz...な件

Content-Lengthヘッダを仕込んだはずなのに、例えばブラウザにファイルサイズが伝わらなくて、ダウンロード残り推定時間が表示されないとか、ともかくContent-Lengthが機能してない!っとなった場合。

Apacheがgzip圧縮してませんか?
つまり、Apacheのmod_deflateモジュールが動いてませんか?

これに該当する場合の対処法です。
アプリケーション側で設定するので、Webサーバの設定なんていじれないよ、って場合でもオーケイなはず。
もっとも、アプリケーション側からの設定が許可されてない場合も考えられるので、悪しからず。。

・PHPの場合
apache_setenv('no-gzip', '1');

Content-Length header が送信されない|PHP WalkerLink
Apache環境変数に"no-gzip"を立てておくと、mod_deflateモジュールが抑制されるらしい。
らしい、というのは、

Apache モジュール mod_deflateLink
この記事内に、使用例は書いてあるけど、
「Apache環境変数に"no-gzip"を立てておくと、mod_deflateモジュールが抑制される」
とはどこにも書いてません…。
書いてあるとしたらこのページだと思うのだが…。

まあ、実際のヘッダを見ると変わっていたから、作用しているのは確認できた。

差分をとってみると、下記ヘッダが変わっていた。
mod_deflateモジュールが有効な場合:
Vary に Accept-Encoding が追加。
Content-Encoding が gzip に。
Transfer-Encoding: chunked が追加。


・他言語の場合
「PHPの場合」などと書いてしまったので、これも必要なのだろうけど…
、だろうけど…それぞれの言語でApache環境変数を設定する方法を調べて下さい。。。。。
まあ、ここまででもヒントにはなるでしょう。と自己満足。
ブログなんて自己満足でやるもんだろーーー。
(他言語で実装する機会があったら、そして解決できたら書きまする)

PHP

— posted by たか at 08:34 pm   pingTrackBack [0]

T: Y: ALL: Online:
ThemeSwitch
Created in 0.1263 sec.
prev
2017.11
next
      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    
 
blogBar
Firefox3 Meter