システムによって写真を大量に上げられる事があり他のシステムへの影響が出てきたのもあり今回プログラムから上げる画像やPDF等のファイルをシステムが動いているサーバーからオブジェクトストレージサーバーに置くことになりました。
Conohaのオブジェクトストレージを試した際につまずいた事等を書いていこうと思います。
続きを読む Conohaのオブジェクトストレージを試す
カテゴリー: PHP
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回までしか呼べないとかあったりしますけど、とりあえず試す分には十分でしょう。天気予報が必要になったら、こういう方法に頼れる、と言う話でした。
使いどころは知らない
フランス革命歴CCXXIV年フリミエール(3月)25日(五曜日)3時81分。
このブログを書き始めた日本標準時を(いくつかの前提をつけて)フランス革命歴に計算しなおしてみました。
ええ、今年9月に入社して3か月半、このブログのためにネタを探した結果がこのざまです。
そもそも、そんな計算をしようと考えたのはphpにjdtofrenchなる関数が存在しているのを知ったからです。
ユリウス積算日をフランス革命歴に変換するという関数で…
http://php.net/manual/ja/function.jdtofrench.php
フランス革命歴が実際に使用された、わずか14年足らずの間は
この関数でフランス革命歴に変換できるそうなのです。
世間には西暦だけじゃなくて、皇紀だったり、他の紀元年も存在すれば、
和暦でもあえて今年を昭和から数えて91年と言ってみたり、ときとして”あえて使われていない暦年で言及する”
なんていうネタがありますけれど、さすがにjdtofrenchではそのネタに使えない。
たった14年足らずしか使われていなかったから、関数もその範囲で計算を打ち切ってます。
(当然といえば当然)
だから、自分で現在時刻をフランス革命歴に換算するのを組んでみたというわけです。
フランス革命歴は…詳しいことはご自分で調べてください。とりあえず、要点は
- 1792年9月22日(秋分)が紀元
- 30日12か月+年末に追加日5~6日
- 曜日は日付の下一桁(10日周期) 追加日は曜日無し
- うるう年に明確なルールはなかった(年始を秋分に合わせようとしていたらしい)
- 1日=10時間, 1時間=100分 1分=100秒
jdtofrenchが1806年以降に対応していないのは、フランス革命歴が実際に使われていた時期だけに対応するほか、
XX年以降のうるう年が決まっていないから、ということによるものだそうです。
なので、今回換算するにあたって、グレゴリオ暦の秋分を常に年始に合わせることにしました。
おおむね、グレゴリオ暦と同じ周期でうるう年が出る計算になります。
秋分の日の計算はこちらを使わせていただきました。
http://php.o0o0.jp/article/php-equinox_day
//グレゴリオ暦$year年秋分の日00:00:00のタイムスタンプ function autumn_equinox($year){ $aeqday = floor(23.2488 + 0.242194 * ($year - 1980)) - floor(($year - 1980)/4); $aeqdate=mktime(0,0,0,9,$aeqday,$year); return $aeqdate; } $nowdate=strtotime("today"); $nowdatetime=strtotime("now"); //現在日時から日付(00:00:00)を引いて今日の経過秒数を求める 【1】 $nowclock=$nowdatetime-$nowdate; //グレゴリオ暦1970年秋分=革命歴CDXXIX年ヴェンデミエール1日 $year = date('Y',time()); $fy=$year-1970+179; //今年の秋分 $aeqdate = autumn_equinox($year); //秋分前なら前年、秋分以降なら今年の秋分の日が革命歴の年始 if($nowdate<$aeqdate){ $fy--; $ffirst=autumn_equinox($year-1); }else{ $ffirst=$aeqdate; } //革命歴年始からの日数 $ftdays=($nowdate-$ffirst)/(60*60*24); //革命歴は30日x12か月+うるう日5~6日、よって30で割れば解決 $fm=floor($ftdays/30)+1; $fd=($ftdays%30)+1; //曜日は10日周期 $dn=$fd%10; //1日=革命歴10時間=革命歴1000分=革命歴100000秒 //革命歴1秒=1/100000日=グレゴリオ暦0.864秒 //革命歴経過秒数を求める $ftsecond=floor($nowclock/0.864); //革命歴10000秒ごとに革命歴1時間【2】 $fhour=floor($ftsecond/10000); //革命歴1時~10時になるように調整 if($fhour<1) $fhour=10; //革命歴1時間未満の革命歴100秒ごとに革命歴1分 $fminute=floor(($ftsecond%10000)/100); //革命歴1分未満の革命歴秒を求める $fsecond=$ftsecond%100;
あとは$fy
から$fsecond
を適当に整形する感じです。
【1】において、strtotime(“now”)が現在時刻、strtotime(“today”)が今日の00:00:00を意味しているから、
単純に引いてその日の経過秒数を使えるようにするというひと手間が要りました。
フランス革命歴下では、1秒の長さすら変化するゆえの策でした。
タイムスタンプが累計秒数だからできる荒業、でしょうか。これで、
【2】の剰余演算は%演算子を使う方向に統一しました。(fmodで計算する方法もあった。どちらが良かったのだろう)
暦年換算は、1秒の長さすら変わる暦年でもなければこれほど大掛かりなものにはならないかとは思います。
逆に、これほど大掛かりなものをわざわざ作ってみたことで、タイムスタンプは結局”ただの秒数”だと
再発見できたわけですし…
現在時刻をフランス革命歴に変換できる、という奇特なネタを身に着けることができた、というわけです。
現在時刻、(フランス革命歴)4時23分!
便利に使えるdate関数
「10日後の日付を求めたい」「来月の最後の日付を求めたい」「日付の曜日を求めたい」・・
PHPを実務で使い始めて1年の間、date関数に馴染んでなかった僕としては、こんなことですごく困ったものでした。
今日の日付の取得はさすがにわかったのですが、そこから10日後を求めようとすると、月によって次月になる日が変わる。結構書かないといけないな。。と思っていたら先輩から「date使ったら一発ですよ」と言われ、そんな簡単にできてしまうのか、、とびっくりしました。
今回はそんなときに使った便利なdate関数について、忘備録としてまとめておきたいと思います。
date('Y-m-d H:i:s') //現在時刻 date('Y-m-d', strtotime("tomorrow")) // 明日 date('Y-m-d', strtotime("+ 10 day")) // 10日後 date('Y-m-d', strtotime("+ 2 weeks")) // 2週間後 date('Y-m-d', strtotime("- 1 month")) // 一ヶ月前 date('Y-m-d', strtotime("first day of - 1 month")) // 先月の最初の日 date('Y-m-d', strtotime("last day of next month")) // 来月の最後の日 date('Y-m-d H:i:s', strtotime("yesterday 15:00:00")) //昨日の15時0分0秒 date("Y-m-d",strtotime("last Sunday")) // 一番最近の日曜日
ここで勉強になったことを2点書いておきます。
last day of の指定は非常に便利で、strtotime(“2013-08-31 +1 month”)だと、2013/10/01になってしまうが、strtotime( “last day of 2013-08 +1 month” )と書くと2013/09/30と次の月の最終日になるみたいです。
date('Y-m-d', strtotime("2013-08-31 +1 month")) // 結果⇒2013/10/01 date('Y-m-d', strtotime("last day of 2013-08 +1 month")) // 結果⇒2013/09/30
strtotimeに「tomorrow」をしてした場合は明日の0時0分0秒に、「+ 1 day」を指定した場合は、明日のちょうど24時間後の時間になるみたいです。strtotime(“tomorrow”)=strtotime(“+ 1 day”)ではないので注意とのことでした。
date('Y-m-d H:i:s', strtotime("tomorrow")) // 明日の0時0分0秒 date('Y-m-d H:i:s', strtotime("+ 1 day")) // 24時間後の日時
カレンダーの表示サンプル
ページいっぱいにカレンダーを表示するサンプルを作成しました。
参考になればご活用ください。
わたしの環境では、bootstrap.cssや社内で整備したcssを使っているのですが、
本サンプルでは、それらcssをこのサンプル内に記述しているため、すこし長くなっていますがお許しください。
サンプルの内容
初回表示は、今日の日付を元に今月のカレンダーを表示します。前月、次月は背景をグレーに、今日は背景を黄色にしています。
年、月を選択して、確認を押下すると、その月のカレンダーを表示します。
表示している内容は、スケジュールの件数とスケジュール内容(これはサンプルなので、使用される場合は、ここに表示したい
内容を設定してください)になります。
下記サンプル「index.html」ソースです。
2015,2016=>2016,2017=>2017); $monthArr = array(1=>1,2=>2,3=>3,4=>4,5=>5,6=>6,7=>7,8=>8,9=>9,10=>10,11=>11,12=>12); ?>calender
EC-CUBEでカートに商品を入れすぎるとエラー発生
EC-CUBE2.13(たぶん2系全て?)で商品を100個とか沢山カートに入れるとなぜかエラーが出てしまいます。
MySQLで、dtb_session.sess_dataにカートの内容を保持しており、このフィールドはTEXT型になっているんですが、調べてみるとどうもこのTEXT型ではサイズ(容量)が足りないようです。
結局、TEXT型を使ってたのをMEDIUMTEXT型に変更したらいけたのですが、まあ、通常のBtoC取引では100種類の商品も買うこと想定してなかったんでしょうね。
今回調べたTEXT型について以下にまとめます。
TEXT型には4つのタイプTINYTEXT、TEXT、MEDIUMTEXT、LONGTEXTがあります。
・TINYTEXT 最長255 バイト
・TEXT 最長65,535 バイト(64KB)
・MEDIUMTEXT 最長16,777,215 バイト(約1.6MB)
・LONGTEXT 最長4,294,967,295 バイト(約4.3GB)
カートのセッション情報を保存する、といった最長サイズで引っかかりそうなフィールドは、MEDIUMTEXTにしておいた方がよさそうですね。
PHPのfloor関数で痛い目に合う
PHPのfloor関数で痛い目に合ったのでメモしておきます。
お客さんから連絡がありシステムででの計算の結果がおかしいとのことで調べるとfloor関数を使った後に結果がずれてきていました。
何でやと調べると意外と有名な話だった・・・
浮動小数点の精度が微妙と本家で警告が出ている。http://php.net/manual/ja/language.types.float.php
echo floor((0.1+0.7)*10);を実行すると8になってほしいのに7になってしまうというお話。
一番簡単な対策としてecho floor((string)(0.1+0.7)*10);として回避しておきました。
DBで使うビューについて
最近仕事で、データベース上にビューを作る機会が何度か出てきました。最初は使い方も使う意味もわからなかったのですが、何度か教えてもらっているうちに、すごく便利なものだということがわかってきました。そのビューの使い方等、この機会に復習がてら自分なりにまとめておきたいと思います。
実際に表示したい項目がDBテーブルに存在しない場合は、新たに項目を設けてビューを作っておくと実際にDBテーブル上の項目と同じように使えるので、その値を表示させたり、あるいはその項目でソートさせたりすることもできるので非常に便利です。また、リアルタイムでビューが更新されるので、誕生日から現在の年齢を求めるときなど動的な値を算出するときにも便利です。
続きを読む DBで使うビューについて