投稿日:

「Effective JavaScript」かいつまみ -基礎編-

翔泳社の「Effective JavaScript」(David Herman著 / 吉川邦夫訳)の一章から、
重要そうな部分をピックアップして箇条書きで要約しました。
太字の部分は要約者の勝手な感想(?)です。
普段開発で使用しているPHPに関して主に追記しています。

第一章 JavaScriptに慣れ親しむ

項目2 JavaScriptの浮動小数点数を理解しよう

・JavaScriptでは、すべての数が倍精度浮動小数点数(いわゆるdouble)である。
・-9,007,199,254,740,992~9,007,199,254,740,992までの整数は全てdoubleで表現する事が可能。
・当然のように、浮動小数点数演算特有の問題が存在。

console.log(0.1 + 0.2); //0.30000000000000004

・少数を扱う際は一度整数にしてから演算して、結果を調整するほうがよさそう。

console.log((0.1 * 10 + 0.2 * 10)/10); //0.3

項目3 暗黙の型変換に注意しよう

・JavaScriptはほとんどの型エラーをスルーして実行してしまう。
・数値演算地の-、*、/、%は、計算を行う前に引数を数値に変換しようとする。
・ただ+だけは例外的な動きをする。
・以下例

console.log(3 + true); //4
console.log("2" + 3); //"23"

・真偽値の型強制。
・ifや||や&&のような演算子は、受け取った値を真偽値に型変換して解釈する。
・JavaScriptでは偽となる値は、false 0 -0 “” NaN null undefined の7つだけ。
・PHPの場合は、”0″ や [] もfalseになるので要注意。

項目5 型が異なるときに==を使わない

・==で値を比較する場合、項目3で既出の暗黙の強制によって両者が数値に変換されて比較される。
・===は型を考慮して比較。
・==のほうが便利な場合もあるのでついつい使ってしまう事もありますが、基本的には===で統一したほうがよさそう。
特にPHPの場合 var_dump(“hoge” == 0)  //結果:true などと直感に反するケースがある(最初が数字でない文字列は0に変換されるため)ので要注意。

項目7 文字列は16ビットの符号単位を並べたシーケンスとして考えよう

・Unicodeが16ビット内でおさまると考えられていた時代にJavaScriptは生まれたため、JavaScriptの文字列の要素は16ビットの符号単位である。(UTF-16形式)
・実際にはUnicodeには16ビットを超える文字も存在し(例えば辰吉じょう一郎のじょう・丈の右上に点)、それらは16ビットの要素2つで表現される。
・そのためUnicodeで16ビットを超える文字の場合は、length や charAt などで文字数と結果が一致しないことになる。

投稿日:

Imagickを利用するにあたって

PDFファイルの1ページ目をイメージファイルjpgに変換するサンプルです。
imagickのサーバーへのインストールは、このブログからは除きます。
当初

$im = new Imagick();
//画像を生成したいPDFを読み込む
$im->readImage('hoge.pdf');
//特定のPDFのページ 0が表紙
$im->setImageIndex(0);
//サムネイルサイズ 640pxに収める
$im->thumbnailImage(640, 640, true);
//シャープ
$im->sharpenImage(0, 1);
//生成
$im->writeImage('out.jpg');

$im->destroy();

を元に関数を作成したのですが、
この記述では、階層が深いPDFを指定した場合は、エラーになるという致命的な問題が
潜んでいます。
そこで、ソースを

$im = new Imagick();
//画像を生成したいPDFを読み込む
$image = file_get_contents('/var/www/xxxx/xxxx/hoge.pdf');
$im->readImageBlob($image);
//特定のPDFのページ 0が表紙
$im->setImageIndex(0);
//サムネイルサイズ 640pxに収める
$im->thumbnailImage(640, 640, true);
//シャープ
$im->sharpenImage(0, 1);
//生成
$im->writeImage('/var/www/xxxx/xxxx/out.jpg');

$im->destroy();

で動くようになりました。
しかし、透明の箇所が真っ黒になるという不具合がありなおかつ
複数ページあるPDFと1ページのみのPDFでは動きが違うという問題にも直面
この難題を解決するのに、3時間程度要しました。
解決済みのサンプルがこちらです。

        $im = new Imagick();
        //画像を生成したいPDFを読み込む
        $image = file_get_contents('/var/www/xxxx/xxxx/hoge.pdf');
        $im->readImageBlob($image);
        $totalPage = $im->getImageScene();

        //サムネイルサイズ 640pxに収める
        $im->thumbnailImage(640, 640, true);
        //シャープ
        $im->sharpenImage(0, 1);
        // バックグラウンドを白にする。
        $im->setImageBackgroundColor('white');
        //特定のPDFのページ 0が表紙
        if ($totalPage != 0) {  // 複数ページの場合
            $im->flattenImages()->setImageIndex(0);
        } else {    // 単一ページの場合
            $im = $im->flattenImages();
        }
        //生成
        $im->writeImage('/var/www/xxxx/xxxx/out.jpg');
        $im->destroy();

以上です。

投稿日:

公開フォルダーにないPDFファイルを表示する方法

PDFファイルアップロードの機能を作成する場合、公開フォルダでない場所にアップロードして、
そのPDFファイルをaタグで別タブに表示したい場合があります。
そこで、その場合のサンプルを記載します。

ファイル名やIPアドレスはご使用の環境にあわせてください。
main.php(表示したいファイルをフルパスでGETで渡す。)




download

表示したいフォルダのファイルをオープンしてPDFとして表示させるためのphpファイル
pdfdisp.php


以上です。試してみてください。

投稿日:

アウトオブ眼中

ボタンをクリックしたら隠しdivの中のフォームをコピーして、
通し番号を発行して表に追加する、みたいな処理をよく書きます。
通し番号の入る場所は仮にダミー文字列(今回はQQQQ)を置いておいて、ボタンが押されたときの処理でまとめて書き換える感じです

何度か書いているうちにdata-属性に行番号を持たせることで、:lastから現在の最大行番号を把握し、
次の行番号を探る、なんてことをやるようになりました。
隠しフィールドよりずいぶんすっきり見えます。attrで持たせると文字列で保存されるので、取得時に0を引いて数値に解釈しなおすことをお忘れなく。 …【1】
追加するときだけじゃなくて削除にも役立ちます。$(“tr[data-no=’行番号’]”,”#fueruTable”).remove() で行ごと削れます。

ところで、これはこれで面倒なことが一つあって、これを実際submitしたとき。

QQQQはダミーフィールドなので使わないはずだし、何よりdataに関してforeachとか回すなんてことになると邪魔。
サーバー側にunset($_POST[‘data’][‘QQQQ’])を噛ませて長いことやってました。

ある時、formの閉じタグ位置を間違えて、隠しフィールドをformの外に追いやってしまったんです。
それでsubmitしたらどうなったかというと

QQQQがいない。
これはこれで何か問題がある気もするけれど、ただテンプレートとして置いておきたいだけで、そもそもsubmitする気のないフィールドは
そもそもform内に置かなければよかった!

考えてみれば当たり前のことなのに、長いこと気づかなかったのでついブログに書き出してしまいました。
submitしないフォームはformタグの外、アウトオブ眼中!他の事例でも使えたら使うつもりでいます。

投稿日:

phpexcelのEXCEL列表記のテーブル作成

phpExcelにて列を指定して値を表示する際に、
EXCEL列の配列になったものをインデックスで指定して表示できれば、
横に長い表を作成する際に役に立つと思いこのテーブルを作成しました。


// EXCELの横座標を設定する。
$col1 = array("","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
$col2 = array("","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
$col3 = array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
$col4 = array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
$colArr = array();
$idx = 0;
foreach ($col1 as $val1) {
    if ($val1 == "") {
        foreach ($col2 as $val2) {
            foreach ($col3 as $val3) {
                $colArr[$idx] = $val1 . $val2 . $val3;
                $idx++;
            }
        }
    } else {
        foreach ($col4 as $val2) {
            foreach ($col3 as $val3) {
                $colArr[$idx] = $val1 . $val2 . $val3;
                $idx++;
            }
        }
    }
}
$col = 0;
$row = 1;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // A1に「日付」表示
$col = 26;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // AA1に「日付」表示
$col = 702;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // AAA1に「日付」表示
$col = 1378;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // BAA1に「日付」表示
$col = 16383;
$sheet->setCellValue("{$colArr[$col]}{$row}", "日付");  // XFD1に「日付」表示 ここがEXCEL横の最大値です。

PHPEXCELにて、列表記をインデックスで扱いたい時の配列です。

投稿日:

JSONで画像データを送信する方法

JSONで単純にファイル名を渡しても、当然ファイルのデータは送ることができません。
そのような場合は、base64_encodeを使って送りましょう。
このサンプルでは、JAVAScriptの時点でPHPのbase64_encodeを使用してデータ化した文字列を送り
受けて側で、base64_decodeしてファイルへ保存で渡すことが可能です。
続きを読む JSONで画像データを送信する方法

投稿日:

Conohaのオブジェクトストレージを試す

システムによって写真を大量に上げられる事があり他のシステムへの影響が出てきたのもあり今回プログラムから上げる画像やPDF等のファイルをシステムが動いているサーバーからオブジェクトストレージサーバーに置くことになりました。
Conohaのオブジェクトストレージを試した際につまずいた事等を書いていこうと思います。
続きを読む Conohaのオブジェクトストレージを試す

投稿日:

array_filterの邪道?

まだまだ修行中です。最近は二度手間をできる限り抑えたいので
PHPのリファレンスを読み込んでみて、こんな関数既にあるんだなっていうのを見つけているところです。
しっかり覚えていなくても、頭の片隅に引っかかっていればいずれ使えるよな…という魂胆です。

そんな中でarray_filterの使い方がどうしても気になりました
http://php.net/manual/ja/function.array-filter.php

配列のキーや値を関数に通して、trueが返ってくるものだけを配列に詰め込んで返す、
使う関数は関数名を指定しても無名関数を渡してもいい、と。

ここで、手元のリファレンス書籍には「一次元配列のみに対応」と、
こちらのオンラインマニュアルには書いていない記述があったのがどうにも引っかかったんです。

「なぜ渡したらダメなのか、あるいはダメだったのか」

とりあえず、php5.4環境で渡してみてどうか調べてみました。

$order=array( 'Alice'=>array(
		'coffee'=>array(
			'milk'=>false,
			'sugar'=>true,
		),
	),
	'Bob'=>array(
		'coffee'=>array(
			'milk'=>true,
			'sugar'=>true,
		),
	),
	'Chalie'=>array(
		'coffee'=>array(
			'milk'=>true,
			'sugar'=>false,
		),
		'cake'=>array('chocolate')
	),
	'Deborah'=>array(
		'coffee'=>array(
			'milk'=>false,
			'sugar'=>false
		),
	),
	'Eve'=>array(
		'doughnut'=>array(
			'plain','strawberry'
		),
	),
	'Fred'=>null,
	'George'=>array(
		'coffee'=>array(
			'milk'=>true,
			'sugar'=>false
		)
	),
);

function order_filter($v){
	/* TODO:ここに検索条件を書く */
}

var_dump(array_filter($order,'order_filter'));

ここのorder_filter($v)をいくつか変えて出力を調べてみました。

//コーヒー注文
return isset($v['coffee']);

/* Output:
array (size=5)
  'Alice' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean true
  'Bob' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean true
  'Chalie' => 
    array (size=2)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean false
      'cake' => 
        array (size=1)
          0 => string 'chocolate' (length=9)
  'Deborah' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean false
  'George' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean false
*/
//砂糖有りコーヒーまたはコーヒー注文無し
return (isset($v['coffee']['sugar'])&&$v['coffee']['sugar'])||empty($v['coffee']);

/* Output:
Notice:Undefined index: coffee in (file) on line 43 //43行目=上の条件を書いた行

array (size=4)
  'Alice' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean true
  'Bob' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean true
  'Eve' => 
    array (size=1)
      'doughnut' => 
        array (size=2)
          0 => string 'plain' (length=5)
          1 => string 'strawberry' (length=10)
  'Fred' => null
*/
//砂糖有りコーヒーまたはコーヒー注文無し 改
return (isset($v['coffee']['sugar'])&&$v['coffee']['sugar'])||empty($v['coffee']);

/* Output:
array (size=4)
  'Alice' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean false
          'sugar' => boolean true
  'Bob' => 
    array (size=1)
      'coffee' => 
        array (size=2)
          'milk' => boolean true
          'sugar' => boolean true
  'Eve' => 
    array (size=1)
      'doughnut' => 
        array (size=2)
          0 => string 'plain' (length=5)
          1 => string 'strawberry' (length=10)
  'Fred' => null
*/

結論としては、「キーの存在」を追加して調べる必要がある(ない場合noticeが出る)…みたいなところです。
この試行だけでは結局「なぜ渡したらダメなのか」という理由はわかりませんでした。
もっと未定義みたいな挙動が出てくるかと思ったら、そういうわけでもなく。
あるいは、バージョンが上がったことで渡せるようになったとか、そういうことなのかもしれないです。
(手元の書籍は5.1ぐらいに対応するよう書いていた9年以上前のものだからあり得る話)

可読性で言えば使わずにforeachループ回すのと五分五分、ってところかなと思います。
array_filterが何をしている関数かピンとこないと読みづらい、みたいな。

他の関数についても、気になったら実験を進めます。
それがリファレンスを頭の片隅に置くために役立つ気がしています。

投稿日:

JQueryにてJSONをPHPに送り、さらにベーシック認証の指定のURLにPOSTしてJSON形式のデータを受け取る方法

長いタイトルになってしまいましたが、ようするにJSON形式なら受け渡しが楽になりますよということです。
まず、JQueryからPHPにJSON形式のデータを渡す方法(www/index.php)




    
    
    
    Ajax Sample


Ajax sample

JQueryからJSON形式のデータを受け取りPHPで連想配列にして他のURLにポストする方法(www/ajax_api.php)
ちなみに、ベーシック認証がない場合は、define(‘DEMO’, true);をdefine(‘DEMO’, false);へ

 "{$memberId}"
    );
    $data = http_build_query($data, "", "&");

    if (DEMO) {
        //header
        $header = array(
            'User-Agent: My User Agent 1.0',    //ユーザエージェントの指定
            'Authorization: Basic ' . base64_encode('account:password'),//ベーシック認証
            "Content-Type: application/x-www-form-urlencoded",
            "Content-Length: " . strlen($data)
        );
    } else {
        //header
        $header = array(
            "Content-Type: application/x-www-form-urlencoded",
            "Content-Length: " . strlen($data)
        );
    }

    $context = array(
        "http" => array(
            "method"  => "POST",
            "header"  => implode("\r\n", $header),
            "content" => $data
        )
    );
    $res = file_get_contents(SAMPLE_API, false, stream_context_create($context));
    return $res;
}
?>

POSTデータを頂き、情報(連想配列のID指定にしていますが、ご利用の際には、DBから取得する方法へもバージョンアップできます。)
を取得JSON形式の返信を頂く方法(wwwapi/index.php)

 array("id" => 10000,'name' => 'Name10000','tel' => "000100010001"),
	"9999" => array("id" => 9999,'name' => 'Name9999','tel' => "000900090009"),
	"9998" => array("id" => 9998,'name' => 'Name9998','tel' => "000900090008"),
	"9997" => array("id" => 9997,'name' => 'Name9997','tel' => "000900090007"),
	"9996" => array("id" => 9996,'name' => 'Name9996','tel' => "000900090006"),
	"9995" => array("id" => 9995,'name' => 'Name9995','tel' => "000900090005"),
	"9994" => array("id" => 9994,'name' => 'Name9994','tel' => "000900090004"),
	"9993" => array("id" => 9993,'name' => 'Name9993','tel' => "000900090003"),
	"9992" => array("id" => 9992,'name' => 'Name9992','tel' => "000900090002"),
	);

$memberId = $_POST['member_id'];

if(empty($memberArray[$memberId])) {
	echo json_encode(array());
}
echo json_encode($memberArray[$memberId]);
?>

※jquery-1.11.2.min.jsは、特にふれていませんが、実行するには必要になります、ご注意ください。

投稿日:

本日は曇天なり

前回の僕のブログが(フランス革命歴)時報だったから、電話番号似ているつながりと言うわけでもないですが、今回は天気予報を取ってくるという話があったので、そのときにやったことをメモしておきます。

天気予報情報はここ。OpenWeatherMap.orgを使いました。
https://openweathermap.org/

いろいろ英語で書いてあって苦手な人は大変ですが、とりあえず必要なことは

  • Sign Upから登録
  • API keysから文字列を持ってくる

だいたいこんなところです。Subscribeは今のところ必要ない感じです。(月額ですし)

こちら側でやる必要があることはだいたい、

  • 持ってきた文字列を含むHTTPリクエストをURLに投げる
  • JSONを受け取る

あとはお好きなように使う、といったところです。

/**
* 定数、配列定義
*/

//アクセス用APIキー
define('API_KEY','/*ここに取得したAPIキー*/');
//アクセス用URL
define('API_URL','http://api.openweathermap.org/data/2.5/forecast?units=metric&id=1853908&APPID='.API_KEY);//大阪のcity idを使用

/* HTTPリクエストを投げる感じのクラスHttpRequestも用意しておく */

/*ここから取得*/
$http = new HttpRequest(API_URL,HttpRequest::REQ_HTTP_GET);
$code = $http->sendRequest();

if($code!=200){
    echo 'Error:データの取得に失敗した';
    exit;
}

$rawWeather=$http->getResponseBody();

ここで、$rawWeatherの中身はこんな感じ

{
     "cod":"200",
     "message":0.021,
     "cnt":39,
     "list":[
         {
             "dt":1491793200,
             "main":{
                 "temp":12.43,
                 "temp_min":8.35,
                 "temp_max":12.43,
                 "pressure":892.44,
                 "sea_level":1032.22,
                 "grnd_level":892.44,
                 "humidity":78,
                 "temp_kf":4.08
             },
             "weather":[{
                 "id":500,
                 "main":"Rain",
                 "description":"light rain",
                 "icon":"10d"
             }],
             "clouds":{"all":68},
             "wind":{"speed":1.06,"deg":217.002},
             "rain":{"3h":0.155},
             "snow":{},
             "sys":{"pod":"d"},
             "dt_txt":"2017-04-10 03:00:00"
          },
          { ...以下、3時間ごと5日分の予報... }
     ]
     "city":{
         "id":1853908,
         "name":"Osaka",
         "coord":{"lat":35.95,"lon":137.2667},
         "country":"JP"
     }
 }

天気の内容はlist[n].weather.mainに、気温や気圧はlist[n].mainに出ています。list[n].dt_txtは世界標準時なので、日本時間では4月10日正午の天気ってことになります。URLにunits=metricをつけることで、気温の単位は摂氏として送られてきます。リクエスト文字列をいじると華氏にもできたりするそうです。
月額のないAPIだと1分間に60回までしか呼べないとかあったりしますけど、とりあえず試す分には十分でしょう。天気予報が必要になったら、こういう方法に頼れる、と言う話でした。