Author Archive

    はてなブックマーク - Apache POIをPHPで動かせるようにしてみた
    このエントリーをはてなブックマークに追加

    お久しぶりです。
    最近ホークスの秋季キャンプに行ってきた萩原です。

    今回Fusic勉強会 #1で発表しました。
    ご参加いただいた皆様ありがとうございました!
    まだ試行錯誤の段階ですがいい勉強会にしていけたらいいなと思います。

    発表内容の詳細は上のスライドを見ていただくとして、
    発表でも紹介したApache POIをPHPで動かしたものをご紹介します。

    Apache POIとは?

    poi.apache.org/

    Apache POI
    ExcelやWordなどのOfficeのファイルを書きだすJavaのライブラリです。
    ただまぁ公式を見てもらったらわかると思いますが、Excelだけやたら充実しまくってます。
    つか公式見てもWordとかPowerpointの使い方分かりませんし、検索してもあまり情報が見つかりません・・・。

    POIのExcelですが、値の埋め込みやセルのコピー、スタイルのセットなど
    通常Excelで行える操作は一通り行えます。
    (できることはPHPExcelと同等以上かと思います。)

    POIをPHPから動かしたい!

    そんなPOIをPHPから動かしたいと考えました。
    現在それとなくGithubで作成中です。

    やってることは

    ①:出力したい情報(埋め込む値やスタイルの情報)をまとめて、CSVに出力。
    ②:①のCSVファイルとテンプレートファイル、出力先のファイルを指定してexecでjavaを叩く。
    ③:出力されたExcelファイルをPHPで処理。

    といった流れになっています。

    こんな感じでExcelを出力できるようにしています。

    $ExcelExport = new PoiPHP();
    $ExcelExport->settings(array(
    	'poi_path' => dirname(__FILE__) . '/Vendor/poi/poi-3.9',
    	'opencsv_path' => dirname(__FILE__) . '/Vendor/poi/opencsv-2.3/deploy/opencsv-2.3.jar',
    	'tmp_csv_dir_path' => dirname(__FILE__) . '/export',
    ));
    $ExcelExport->addString(0,0,0,‘test');
     
    $readfile = dirname(__FILE__) . ‘template.xlsx';
    $outFile = dirname(__FILE__) .’export.xlsx';
     
    $ExcelExport->excelExport($readfile, $outFile);

    詳細な使い方はgithubのREADMEを参照してもらえたらわかると思います。

    POIを使うメリット

    とは言っても、正直色々設定が面倒なので
    通常帳票を出力するにあたってはPHPではreviserやPHPExcelで十分だと思います。

    POIを使うメリットとしては
    ①シートをまたいだ計算式を含むテンプレートに値を埋め込む時
    reviserだとPHPエラーで落ちます。
    PHPExcelだとうまくいきますが、あまり複雑だったりするとPHPエラーで落ちてしまったりするようです。(詳細不明)

    ②テンプレートにマクロがいて、そのマクロを出力後に使用したい時
    まだ、これを案件で使用してはいないですが、個人的にこれは熱いです。
    (reviserやPHPExcelではマクロが削除されたりPHPエラーで落ちたりします。)

    もし、reviserやPHPExcelで出力できなくて困っている方はPOIの使用を検討してみてください!
    poi-phpも値の挿入やスタイルのセットは問題なく動作すると思います。
    (画像の挿入などで現在苦戦中ですが・・・)

      はてなブックマーク - PHPmatsuriにいって来ました!(一人目?)
      このエントリーをはてなブックマークに追加

      多分久しぶりな登場の気がする萩原です。

      昨年に引き続きPHP Matsuriに参加させていただきました!
      (今回は福岡!)
      弊社からは何と8人も参加させて頂きました。
      全員LTで発表しましたので発表の最後の方にはまたFusicかとつぶやかれるくらいでした。

      一人目って書きましたが別に二人目とかタイトルが続くことは多分ないんじゃないかと思います。

      今回のPHP Matsuri

      今回は、本当にネタが思いつかずやることが決まったのは当日14時くらいでした。
      大して役に立たないものおもしろそうなものを作ろうということは何となく事前に決めていましたが。

      何とかネタは思いついたものの、時間内に出来るんだろうかとか思いながらバタバタ作っていたらなんだかんだで日付が変わったころには大体できてしまっていました。
      残り時間は書いたのもを見直したり、前でファミコンやってるのを眺めてたりホークスと横浜のトレードに衝撃を受けていたり(しかも今日公式に発表されたトレード、メンツが変わってたし)してました。

      今回やったこと

      今回はお絵かきのアプリを作りました。
      ただお絵かきするだけじゃつまらないので、
      お絵描きの履歴を取っておいて速度まで再現するものを
      CakePHPとJavasciptで作成しました。

      具体的に何をしたかというと
      ・canvasを利用して絵を描きつつ、mousemove中のマウスの座標や色、線の太さ、時刻を取得。
      ・絵を描き終わったタイミングでAjaxでデータを送信して保存。
      ・保存したデータを利用して、SetTimeoutを使ってcanvasで描写。

      なんてことをやってました。

      え?PHPがどこにあるのかって?
      saveとかfindとか使ったってば!
      PHPmatsuriの間一つもPHPに関してググらなかったけど!

      因みにmousemoveでとれる座標はすべて取得しているので1分くらい描き続けると保存のAjaxが30秒くらいかかります・・・。
      (まぁVM使って動かしてたんで実際にやればもっと速くなるとは思いますが・・・。)

      ちなみにちょっと公開する場所とかないのでお見せできないのが残念です。

      ついでに言うと発表はダダすべりしました・・・。

      感想

      ネタは2週間くらい前から考えてたんですが、思いついたのは当日。
      当日になって「エイヤ」でやっても意外となんとかなるものだなと思ってしまいました。

      発表は失敗しましたが(一説にはマイクが入ってなかったとかなんとか・・・)、
      作ったものを評価してくれたみたいで本とシャツをいただけました!
      ありがとうございます!

      前回は結構セッションをたくさん聞いたので、今回はコーディングをメインに参加しました。
      やっぱりあの雰囲気でコードを書くのはとても楽しかったです!

      何か次回は北海道とかいう宣言が何かなされていたので次回は北海道なのでしょう(笑)

      次回も楽しみにしておきます!
      後結局のところ私はFusicの変態枠なのでしょうか・・・

        はてなブックマーク - CakePHP2.0で現状の問題点などまとめました。
        このエントリーをはてなブックマークに追加

        そろそろ誕生日の萩原です。誕生日プレゼントください。去年も似たようなことを言ったような気がするのは気のせいです。

        最近、CakePHP2を色々触ってまして、色々困ったことがあったので、まとめておこうかなと思います。(CakePHP2.0.4現在)
        (下でCakePHP2.xなんか知りませんと言われてますが・・・)

        AppControllerをapp/Controller以外に置く場合の注意

        CakePHP2からは皆さんご存知の通り、AppController及び、AppModelが、app直下に設置が出来なくなりました。

        CakePHP2ではAppControllerはapp/Controller以下に設置をすること、と言うことになっています。

        しかし、個人的には出来れば、直接アクセスするコントローラとはAppControllerは分けたいところです。

        そこで、AppControllerをapp/Libに設置をしようと考えまして設置を行っていました。

        app/Lib以下にAppControllerを設置後は、AppControllerを継承するコントローラにはApp::usesの記述が必要になります。

        <?php
         
        App::uses(&#039;AppController&#039;, &#039;Lib&#039;);
        
        class PostsController extends AppController {
         
        }

        しかし、作業をしていく内に、たまにAppController内のアクションが読めないというエラーが出ることがありました。(debug2でも)

        元凶はapp/tmp/cache/persistent/cake_core_file_mapのキャッシュ(こいつがなかなかの曲者です・・・)だと言うことはすぐにわかったのですが、どうにも再現性も取れず、対策がなかなかつかめない状態でした。(1ヶ月くらい悩んでいました・・・)

        結局のところ現象としては

        (1)404ページ(missing controllerなど)にアクセス
        (2)CakeErrorControllerを見に行く
        (3)CakeErrorControllerがapp/Controller以下にセットしていない場合は、CakeコアのCakeErrorControllerを見に行く
        (4)当然、コアのコントローラにApp::usesの記述があるはずもなく、継承するAppControllerの場所としてApp/Controller以下を見に行く
        (5)ないので、CakeコアのAppControllerにアクセスしに行く
        (6)さらに、キャッシュにAppControllerの位置はCakeコアにある、と記述
        (7)その後、通常のシステムのページにアクセスしても、AppControllerとして、CakeコアのAppControllerを見に行く
        (8)App/Lib以下に設置したAppConttroller内のアクションが呼ばれない

        ということが起こっていました。

        解決策としては

        ①AppControllerを素直にApp/Controller内に設置する
        ②CakeErrorControllerをApp/Controller内にコピーしてApp::usesをセットする

        ①はまずまず問題ないく動作をするはずです。

        ②についてはCakeコアでAppControllerを継承しているものが、他には通常あまり使わないであろう、PagesControllerくらいだったので、こちらでもおそらく問題なく動作すると思います。

        (2011/12/15追記)

        LibにAppControllerを置くと言う件について、Libの中にControllerのディレクトリを作ってその中に置けばいいという指摘を頂きました。
        試してみると、app/Lib/Controller/AppController.phpという形で設置をしたら、確かに問題なくusesなしでもapp/Lib/Controller/AppController.phpを見に行ってくれました。
        これならCakeErrorを書き換える必要もなくなるので、これが一番良い解決法かもしれません。

        app以下のクラス名とPlugin以下のクラス名が同名だと誤動作する

        これは具体例を挙げた方が早いと思います。

        app/Controller/PostsController.php(①)と、app/Plugin/Post/Controller/PostsController.php(②)の二つの同名のコントローラがあるとします。

        初めに①にアクセスした後、②にアクセスをしようとすると、①にアクセスをしてしまうというものです。(逆もしかり)

        これが起きてしまう原因はapp/tmp/cache/persistent/cake_core_file_mapのキャッシュがプラグインを考慮していないためです。(またお前か・・・)

        実のところ、AdminPluginを作成してそこに管理画面を実装しようとしていたところでしたので、これはかなり致命的でした。

        解消方法自体はコアを触れば出来なくもないのですが、コアのアップデートなど一切できなくなるのでお勧めはしません。

        どうしても動作させたいのであれば、lib/Cake/Core/App.phpをプラグインを考慮するように直接編集すると、動作はします。

        まぁ、ただ当分はapp以下とPlugin以下で同名のクラスが入るような実装は避けた方が良いでしょう。

        (2011/12/16追記)

        CakePHPチームにチケットを投げてみましたが、同名のクラスは現在使えない、Cake3で計画中と言われました。

        PaginatorのorderにSQLが記述できない

        CakePHP2からPaginatorがコンポーネントに分離されたことは皆さんご存知かとは思いますが、CakePHP2では現在のところPaginatorでorderにSQLがそのまま記述が出来ません。

        例えば

        $this->Paginator->settings = 
            array(&#039;order&#039; => array(&#039;`hoge` is null ASC&#039;)):

        のようなソートを行いたい時に上記のorderが無視されます。(実際に必要な場面がありました・・・)

        これは、コアのPaginatorComponentを見ればわかるのですが、352行目~の記述で

        if ($object->hasField($field)) {
        	$order[$alias . &#039;.&#039; . $field] = $value;
        } elseif ($object->hasField($key, true)) {
        	$order[$field] = $value;
        } elseif (isset($object->{$alias}) && $object->{$alias}->hasField($field, true)) {
        	$order[$alias . &#039;.&#039; . $field] = $value;
        }

        とありまして、例外が全く、無視される形になっています。

        この記述自体は1.3にもあるのですが、1.3では最後にarray_mergeをしているので、SQLも取り込んでくれていたのですが、2.0では無視された状態です。

        どうしても特殊なorderをかけたい場合は現状では素書きのSQLやDBのviewなどで対応しておいた方が良いかもしれません。

        (今回私の場合は、元々orderをかけたかったテーブルがviewだったのでviewにソート用のカラムを泣く泣く追加しました・・・)

        (2011/12/16追記)

        CakePHPチームにチケットを投げてみたところ、orderを文字列で指定したら動くじゃん!って言われたので現在配列で指定したんだけど・・・と返しているところです。

        SSLページでのCookieのセキュア属性を外す方法

        SSLページがあるページとないページが混同する場合、Cookieのセキュア属性が付いていると、セッションを共有できないため、セキュア属性を外してあげる必要があります。

        1.3の場合は以下の対応が必要になります。

        app/config/core.php

        //	Configure::write(&#039;Session.save&#039;, &#039;php&#039;);
        	Configure::write(&#039;Session.save&#039;, &#039;session_custom&#039;);

        app/config/session_custom.php

        <?php
         
        if (empty($_SESSION)) {
            ini_set(&#039;session.use_trans_sid&#039;, 0);
            ini_set(&#039;session.name&#039;, Configure::read(&#039;Session.cookie&#039;));
            ini_set(&#039;session.cookie_lifetime&#039;, $this->cookieLifeTime);
            ini_set(&#039;session.cookie_path&#039;, $this->path);
        }
        ini_set(&#039;session.cookie_secure&#039;, 0);

        これは、コアのcake_session.phpの設定を強引に上書きをしています。

        CakePHP2ではcore.phpに以下の記述をすれば、対応できます。

        Configure::write(&#039;Session&#039;, array(
            &#039;defaults&#039; => &#039;php&#039;,
            &#039;ini&#039; => array(
                &#039;session.cookie_secure&#039; => 0
            )
        ));

        特に新しいファイルを作成する必要はなく、手軽です。

        CakePHP2.0ではセッションを共有するしないにかかわらず、SSLがあるページとないページが混同するサイトでは、セキュア属性は外した方が良い。

        これは、まずSSLのあるページ(①)でログインをした後、SSLのないページ(②)にアクセスし、その後、①のページにアクセスをすると、ログアウトしてしまうと言う現象のためです。(これは1.3では発生しません)

        詳しい原因についてはまだ追い切れていないのですが、①にアクセスした後、②にアクセスすると①と異なるセッションキーが振られます。(これは当然の話)

        その後①のページにアクセスすると、②で発行されたセッションキーに上書きされてしまうため、ログアウトをしてしまいます。

        以上の理由から、今のところ、SSLがあるページとないページが混同するサイトでは、セキュア属性は外して置いた方が良いと思います。

        (2011/12/16追記)

        CakePHPチームにチケットを投げてみたところ、上記の書き方でセキュア外せばいいじゃん!って言われました。

        最後に

        まだCakePHP2は駆け出しで、問題が色々と出てますが、触っている感じとしては1.3とそう大差なく使えています。そして早いです。

        きっとこれからどんどん良くなっていくと思うので皆さんどんどんさわりましょう!

        後、本当にfile_mapのキャッシュには要注意です!
        ああ・・・app.phpの750行目を消してfile_mapのキャッシュをなかったことにしたい・・・w