B-Teck!

お仕事からゲームまで幅広く

【PHP】PHPの緩い比較とかの話

PHPの比較

PHPには、2つの変数が等しいことを比較する演算子=====で2つある。
==は、暗黙的に型を相互変換した後に行われる比較。 ===は、型も含めた比較となる。
PHP: 比較演算子 - Manual

2a問題

暗黙的な型変換による比較というのは、多言語経験者から見ると違和感の大きいものらしく、
2a問題」という名前で取り上げられることもある。
"2a"という文字列が、==で整数型の2と比較した際に等しくなってしまうというやつだ。

<?php
    $a = 2;
    $b = "2a";
    
    if ($a == $b) {
        echo "$a == $b is True";
    }
    
    if ($a === $b) {
        echo "$a === $b is True";
    }
    
    //$a == $b is Trueのみ出力される

==についての不思議な挙動は、下記でもっと深く見ることができる。

素晴らしき自動的な世界〜或いは「型のない」世界〜 - がるの健忘録
PHP 5.4.4から==の挙動が一段と難しくなりました - hnwの日記

その他の関数

じゃあ基本的に==は使わず、===を使おうという話なんだけど、
実はin_arrayとかsortとか、switchとかarray_searchとか…
PHP自体の組み込み関数には、デフォルトで==と同様の比較をしているものがある。
例に挙げた以外にも多分あるんだと思うんだけど、まとまって参照できるところがなくてわからない。
(誰か知ってたら教えてくれ)

in_arrayとかarray_search

こいつらには第三引数にstrictってのがあって、
これをtrueにすると型までみていい感じにしてくれる。
デフォルトだと緩い比較なのでガバガバ。

PHPのin_arrayは罠が多いので注意喚起が必要 - Qiita
PHP: in_array - Manual

sort

あとこいつも第二引数で比較方法を設定できていい感じ。
デフォルトだと(略)

設定値 説明
SORT_REGULAR 通常通りに項目を比較 (型は変更しません)
SORT_NUMERIC 数値的に項目を比較
SORT_STRING 文字列として項目を比較
SORT_LOCALE_STRING 現在のロケールに基づいて比較します。
比較に使うロケールは、setlocale() で変更できます。
SORT_NATURAL 要素の比較を文字列として行い、 natsort() と同様の「自然順」で比較します。
SORT_FLAG_CASE SORT_STRING や SORT_NATURAL と (ビット OR で) 組み合わせて使い、
文字列のソートで大文字小文字を区別しないようにします。

PHPのsort関数は相当おかしい - hnwの日記
PHP: sort - Manual

switch

構文的に厳密な比較に対応してない。
ハマりやすい。

<?php
$i = "0";
switch ($i) {
    case 0:
        echo "iは0に等しい";
        break;
    case 1:
        echo "iは1に等しい";
        break;
    case 2:
        echo "iは2に等しい";
        break;
    default:
       echo "iは0,1,2に等しくない";
}
// "iは0に等しい"が出力されちゃう

回避するにはこうする。

<?php
$i = "0";
switch (true) {
    case $i === 0:
        echo "iは0に等しい";
        break;
    case $i === 1:
        echo "iは1に等しい";
        break;
    case $i === 2:
        echo "iは2に等しい";
        break;
    default:
       echo "iは0,1,2に等しくない";
}
// "iは0,1,2に等しくない"が出力される

こうなるとif-elseでいいんじゃ?って気もしてくるけど、
switch使いたいときもあるから気をつけないといけない。

PHP: switch - Manual

2017年になってました

今更ですが新年のご挨拶です。

去年は母の急逝然り、人間関係然り、後悔を残すことの多い一年でした。
人間、いつ何があるのかわからないなぁという感じで、
やろうと思ってることができなくなってしまうなんて当たり前にあるんですよね。
自分と向き合う時間、他人と向き合う時間、それぞれを大事にして、
今年一年はなるべく後悔しない、前向きでやり残しのない一年にしたいです。

そういえば、去年末は月間アクセス数が1万の大台目前まできたみたいです。
ブログの性格上、固定の読者さんはあまりおらず、検索経由で来られる方がほとんどだと思いますが、
今年もまた一年のんびり書いていきますので、どうぞよろしくお願いしますね。

【VBA】パスワードのかかっていないExcelだけ開く

VBAからパスワードのかかったExcelファイルを開く際、普通にWorkbooks.Openするとパスワード入力ダイアログで止まってしまう。
この場合、パスワードを入力するかダイアログを閉じるまで実行中の処理が停止してしまう。
また、ダイアログを閉じた場合にはエラーが発生してしまう。

とりあえず下記方法で回避することができる

  • On Error Resume Nextでエラー発生時に後続の処理が実行されるようにする
  • Open時のパスワード引数に空文字「""」を指定する
  • Open後、Err.NumberがOpen失敗「1004」であるかを確認する
    ※ ループ等の場合、Err.Clearをしないとエラー情報が残ってしまうので注意

ただ少し問題があって、この方法では、パスワードのかかったExcel以外にも開けないExcelが存在したとき(編集中等)に同様に開けないファイルとして扱ってしまう。
どうするのがベストなんだろう。

Sub openExcel(ByVal filePath As String)
    Dim wkBook      As Workbook: Set wkBook = Nothing
    Dim xlApp       As Object: Set xlApp = CreateObject("Excel.Application")
    
'Errorを無視
On Error Resume Next

    'パスワードに空文字を入れることで、パスワード無しで開けるブックのみ開く
    Set wkBook = xlApp.Workbooks.Open(filePath, Password:="")
    'オープン失敗時はエラーメッセージ
    Select Case Err.Number
        Case 1004
            MsgBox "ワークブックにパスワードがかかっているためOpenできません。"
            Err.Clear
        Case 0
            MsgBox filePath & "をOpenしました。"
    End Select
    
    '後処理
    xlApp.Close
    Set xlApp = Nothing
End Sub