Home > PHP

PHP Archive

引数を名前で渡す

  • 2007-05-31 (木)
  • PHP
この記事の所要時間: 042

引数が多い時に困るのはみんな一緒ですね。

C++とかJavaで良く見るプログラミングスタイルだが、こうやってひたすら数字だけが並んでいるとやたらと読みにくい。三日後に見たらどのパラメーターが何なのかが思い出せなくて、コンストラクターのソースコードを参照せねばならない。

Life is beautiful: 「パラメーターのプロパティ渡し」でプログラムを読みやすくする、というアイデアはどうだろう

PHPのコンストラクタを名前付き引数で呼ぶアイデアはid:shimookaさんが書かれているので、ここでは通常のメソッドについて考えてみました。

いまいちなコードですが、引数省略時のデフォルト値、存在しない引数名が来た時・必須引数が無い場合に例外を投げるようにしてます。

<?php
class IllegalArgumentException extends Exception {}

class Hoge {
  protected function getParam(array &$params, $key, $isMust = false, $default = null) {
    if (array_key_exists($key, $params)) {
      $v = $params&#91;$key&#93;;
      unset($params&#91;$key&#93;);
      return $v;
    } else {
      if ($isMust) {
        throw new IllegalArgumentException();
      } else {
        return $default;
      }
    }
  }
}

class Foo extends Hoge {
  public function func1(array $params = array()) {
    $id = $this->getParam($params, 'id', true);
    $name = $this->getParam($params, 'name', false, 'NO NAME');
    if (count($params) > 0) {
      throw new IllegalArgumentException();
    }
                      
    printf("id=%s name=%s\n", $id, $name); 
  }
}
                
$obj = new Foo();
// id=1 name=hoge     
$obj->func1(array('name' => 'hoge', 'id' => 1));
// id=1 name=NO NAME
$obj->func1(array('id' => 1));
//  Uncaught exception 'IllegalArgumentException'
$obj->func1(); 
//  Uncaught exception 'IllegalArgumentException'
$obj->func1(array('name' => 'hoge', 'id' => 1, 'email' => 'hoge@example.com'));

引数定義をメソッド宣言とは別に持つのはいまいちですね、はい。

ちなみにPythonは言語としてこの機能をサポートしているようです(via: odz buffer)。あとVBも同じような機能があります(via: 位置と名前による引数渡し)。

PHPを今まさに使っている(学んでいる)人へ

この記事の所要時間: 49

PHPはとかく非難されやすい言語です。「WebのVB」と揶揄されるように他言語の利用者(特にLL系)からはあまり良い評判を見ません。

PHPを仕事で使ってる人やこれから学ぼうとしている人の中には

「ああ~PHPっていけてないんだ。」

と感じている人もいるでしょう。

「このままPHPを使っていて良いんだろうか。」

と不安に駆られる人もいるでしょう。

開発経験がある程度あって複数の言語が扱える方ならこのような批判を見ても、ご自身で判断が下せるかと思います。(なのでそのような方達はこのエントリの対象ではありません。)

しかしそうでない方、特に今まさにPHPを学んでいる方はWeb上で繰り広げられるPHP批判に不安になったのではないでしょうか。

趣味でやっている方なら「じゃあRubyにするか」と言語を変えることできますが、仕事や学校でPHPをやっているとそう簡単に言語を変えることはできません。このままじゃ「いけてないプログラマーになるんじゃないか・・・」と不安になったり。。。

ここで一言

PHPを使っても大丈夫なんです!!

理由を4つばかり。

PHPについて

まずPHP自身について。

PHPには豊富な関数があり様々な機能を利用することができます。またextensionによってC/C++で書かれたライブラリをPHP自体に組み込む事ができます。(ミサイルを飛ばしたりw)

また色々と批判の多いPHP言語仕様ですが、C/Javaに似た文法ですし、オブジェクト指向もいちおうサポートしています。ですのでPHPの後にJavaをやるという際にもわりとスムーズに移行することができます。

私自身VBAでオブジェクト指向の雰囲気を見て、PHP3で何となくオブジェクト指向っぽい事をやって、Javaで開発したという経験があります。特にPHPとJavaはextendsやthis、try/catchなど似た構文が使われており理解が早いと思います。

動作環境の安定性、豊富な実績

次に動作環境の安定性、豊富な実績です。

プログラミング言語は言語だけで成立するものではなく何らかの形で動作する環境が必要です。特にWebシステムの場合はApacheなどWebサーバとの連携が必須となります。Apache+PHP(mod_php)の組み合わせは様々なサーバで動作しており、多くのレンタルサーバではPHPが動作するようになっています。

さらにYahoo!やGREEなど数多くのサイトがPHPでWebシステムを稼働させています。

またLLではネックになりがちな実行速度についてもAPC/eAcceleratorといったコードキャッシュ・アクセラレータが存在します。これにより実行速度を向上させることができます。これらのツールは実験的なものではなく、実際に数万~数十万人のユーザをさばくような有名サイトで稼働しています。(PHP6ではAPCが正式に採用されるという話しもあります)

豊富なフレームワークやライブラリ

次は豊富なフレームワークやライブラリです。

PEARをはじめとした有用なライブラリ群が数多く公開されています。さらに昨今ではCakePHPやSymfony、Ethnaといったフレームワークもあります。これらを使うことにより効率的な開発を行うことができます。

どうせ使う言語は変わる

最後は使う言語はPHPだけではないという事です。

今はPHPをやらざるを得ない状況でも、開発を続けている限りそれが一生涯続くという事はまず無いと思います。いずれは他の言語も学ぶようになるので、それまではしっかりとPHPを習得しておいた方が良いです。PHPは良くも悪くもWebシステム開発では今や主流とも言える言語なので習得しておけば他言語を学ぶ際に基準となります。

PHPでWebシステムを開発していけばWebシステムを作る上で必要となるスキルは身に付きます。このスキルはPHP以外の言語に移行したところで無駄になるわけではありません。Webシステムを開発する上で必要な概念はどの言語を使っても同じです。(ここではWebシステムとしていますが、PHPではバッチ処理などWeb以外の箇所を開発することもできます。なので当然ながらそのスキルも生きてきます。)

もちろん私自身PHPに不満点はありますし、PHP以外に素晴らしい言語もあります。しかし一つでも自分が自由自在に書ける言語があるという事はシステム開発を行う上で大きな礎となります。

PHPを今まさに使っている方、学んでいる方、自信を持って頑張っていきましょう。

[2007/06/02:追記]
はてぶでid:shomaさんがPHPを使ってるサイトを紹介して頂けたので追記しておきます。(ありがとうございます。)

PHPを使ってる有名サイト:Yahoo!, GREE, digg, facebook, CNET, Wikipedia, goo, wordpress, 楽天

第24回PHP勉強会に参加してきました。

この記事の所要時間: 156

第24回PHP勉強会

events.php.gr.jp

第24回PHP勉強会に参加してきました。詳細なレポートはshimookaさんのところにあるので;-)、ざっくりと感想を。

extensionがアツい!

最近はフレームワークに注力してたのであまり追いかけてなかったのですが、やはり動いているモノ(ミサイルw)を見ると引き込まれます。以前少しだけCを書いていた時期があるのでバイナリアン系のネタは聞いているだけで楽しいです。

既に完成しているライブラリをPHP化するなら、ネタも色々ありそうなので何かやってみたいですね。単純に「extension作れる=かっこいい」というのもありますし。;-)

携帯+PC連携

内容とは微妙に違いますが、前から思っていたのが「汎用テンプレートって無いのか?」ということ。PHPだろうがPerlだろうがPythonだろうがRubyだろうが・・・テンプレートエンジン使うんだったら同じ書式のテンプレートを使ったら良いのに。

開発者側はそれぞれ覚えれば良いですが、デザイン側にそれを押しつけることはできないので結局今いる環境から動けない、なんて人もいる気がします。(実際それで新しいものを取り入れるのを躊躇したり。)

たとえばSmartyで書いたテンプレートが他の言語でも使えるとかで良いわけです。(うーん作ってみたいな。。。)

見える化

まさに一見にしかずです。上手く説明できないですが、できたものを見るとなぜかわくわくします。自然と「おー」と声が出ます。何か上手く使ってみたいです。

ちょっと体調不良だったり、宿難民になったりでいろいろありましたが、参加して良かったです。

会社から言われたわけでなく、自分でわざわざこういった勉強会に参加される方は当たり前ですが学習意欲の高い方ばかりなわけです。そんな方々と自分の興味ある分野で、ああだこうだとお話できたら、楽しくないわけがない!!

日頃、開発は一人でやっているのでとても良い刺激を受けました。また近いうちに参加したいと思うのでよろしくお願いします。:-)

# Twitterで実況してましたが誰も見てない気がしてやめました:-p
# せっかくなんでいちおうログ出しときます。Twitter検索 : @phper

第24回PHP勉強会に参加してきます。

この記事の所要時間: 014

今からPHP勉強会に参加してきます。
shimookaさんがblogで実況されるとの事なので、私はTwitterで実況してみようと思います。

Twitterのアカウントはshin1x1です。

では参加される皆さんよろしくお願いします。

CakePHP Controller#Object()が外部から呼べる

この記事の所要時間: 055

以前あったControllerのメソッドが外部から呼べてしまう問題ですが、フレームワークが修正されて問題は解決したと思っていました。

しかし、よくよく見てみるとObject()だけはまだ呼べる状態になっています。

下のようなコントローラに[http://example.com/hoge/Object]でアクセスすると”**construct”が2回表示されます。

[app/controller/hoge_controller.php]

<?php
class HogeController extends AppController {
  var $uses = array();

  function __construct() {
    parent::__construct();
    var_dump("**construct");
  }
}
?>

コンストラクタを継承してHogeControllerでややこしい事をしなければ、まあ2回呼ばれても良いかもしれませんが、やはり外から呼べる状態は気持ち悪いです。

こういうのを考えるとURLからコントローラとアクションが自動的にマッピングされるより、routes.phpできっちりマッピングしておいた方が安全かなあと思ったりします。(まあ利便性とのバランスですが)

いちおうチケットは出しておいたので修正されるのを待ちたいと思います。

クエリ文字列のセッションIDはクッキーには出力されない

  • 2007-05-10 (木)
  • PHP
この記事の所要時間: 145

セッション固定化(Session fixation)を調べている時に気づいたのですが、PHPではクエリ文字列内のセッションIDはクッキーとしては出力されないんですね。

良くセッション固定化で例に挙げられるが下のような例です。

  1. 攻撃者がセッションID付きURL[http://example.com/?PHPSESSID=abcd]をユーザAに踏ませる
  2. ユーザAのセッションが[セッションID=abcd]で生成される。
  3. ユーザAがログインする。
  4. 攻撃者が[セッションID=abcd]でアクセスするとユーザAがログイン済みの状態になっており、セッションを乗っ取る事ができる。

これ自体はもちろんそうなのですが、PHPではクエリ文字列に含まれるセッションIDでセッションは生成されますが、クッキーにそのセッションIDは出力されません。(PHP5.2.1のext/session/session.cを見るとそんな感じの実装です。)

2.の状態ではサーバ上に[セッションID=abcd]のセッションは存在しますが、そのセッションIDはクライアント側には知らされません。

3.でログイン情報をクライアントからサーバにPOSTで送信しますが、クライアントは2.でセッションIDの通知を受けていないので、セッションIDは送信しません。なのでサーバ上では新規セッションとして新しいセッションIDが生成されます。

その後、4.のように攻撃者が[セッションID=abcd]でアクセスしても、ログイン前のセッション(空のセッション)を参照するだけなので、当然ながら要ログインな画面へはアクセスできません。

つまり単純にセッションID付きURLを踏ませても、認証セッションを乗っ取るのは難しいのかなと。(かといってセッション固定化対策が不要なわけでは無いのであしからず。)

※これはセッションIDの受け渡しにクッキーを使っている場合の話です。携帯のようにクエリ文字列でセッションIDの受け渡しを行う場合は、例の手順でセッションを乗っ取ることができます。

Twitter検索がRSS対応になりました

この記事の所要時間: 037

既にお気付きかと思いますが、Twitter検索がRSS対応になりました。

キーワード検索の検索結果をお好きなRSSリーダーで見ることができます。

同じキーワードを定期的にチェックしたい方は活用してみてください。

↓のような使い方が面白いかも。

  • 自分のユーザ名で検索。
    =>自分宛に書いた投稿をチェック
  • 気になるキーワードで検索。
    =>キーワードに合致する投稿をチェック
  • でも検索結果が0。
    =>いつか誰かが書くかもしれないので、いちおうRSS購読(by @jazzanovaさん)

RSS対応の背中を押して下さった@nitoyonさん、@otuneさんありがとうございました。

CakePHP モデルのvalidates()に注意

この記事の所要時間: 138

Model#validates()ですが、「ちょっとどうなの?」な仕様になってます。

このメソッドはModel#save()内で自動的に呼ばれるのか、コントローラのアクションメソッド内で呼ばれるのが一般的な使い方だと思います。

問題が起こるのは後者の方で、空の配列をvalidates()に渡すと、Userl#$validateで何を定義していてもtrueが返ってきます。

下のソースならUserモデルの$validateに入力チェックをいれておけば「ng」が表示されそうですが、実はUser#$validateに関わらず「ok」が表示されてしまいます。

<?php
class FooController extends AppController
{
    var $uses = array('User');

    function blank()
    {
        $this->data = array();
        if ($this->User->validates($this->data)) {
            var_dump('ok');
        } else {
            var_dump('ng');
        }

        exit;
    }
}
?>

原因はModel#invalidFields()の以下の箇所にあります。(invalidFields()はvalidates()から呼ばれます)

[cake/libs/model/model_php4.php]

<?php

		foreach($this->validate as $field_name => $validator) {
			if (isset($data[$field_name]) && !preg_match($validator, $data[$field_name])) {
				$this->invalidate($field_name);
			}
		}

?>

$dataはvalidates()に渡される連想配列なのですが、$dataに$validateのキーが存在しない場合はpreg_match自体が処理されず、invalidate()にはなりません。

なのでコントローラからvalidates()を呼ぶ時は、$dataにModel#$validateのキーに存在するかを確認する必要があります。

ちなみにModel#save()ではvalidates()の後の処理でfalseが返るようになっています。ただこれも$dataが空の配列の場合の話で、Model#$validateにキーは無い(入力チェックはしない)が、テーブルにカラムがあるキーのみの配列なら通ってしまいます。

例えば下のソースなら「ok」が表示されます。

<?php
    function blank()
    {
        // User#$validate に id が無い場合
        $this->data = array('id' => '');
        if ($this->User->save($this->data)) {
            var_dump('ok');
        } else {
            var_dump('ng');
        }

        exit;
    }
}
?>

やはりこちらも入力チェックするキーが$dataに存在するかを確認するしか無いようです。

CakePHP $_GET/$_POSTの値はどこに?

この記事の所要時間: 348

PHPのスーパーグローバルの値をどのように参照すれば良いかまとめてみました。

Controller#dataやアクションメソッド引数のようにフレームワークで想定された使い方をしている分には特に問題無いのですが、ちょっと他のことをやろうとすると、どこに値が格納されているか分からず困った事がありました。

# もちろん$_GET/$_POSTを使えば値は取れますが、せっかくのフレームワークなのでなるべくその中で値を使いたいものです。

1. $_GET

$_GETの値はController#params[‘url’]に格納されます。
ちなみに$_GET[‘url’]はURLルーティング(リクエストURIからコントローラ・モデル等を決定)で、$_GET[‘file’]は[app/webroot/js/vendors.php]で参照されています。

[http://example.com/foo/index/?id=1&code=abcd&offset=10]でアクセス

<?php
class FooController extends AppController {
  function index() {
    // $this->params['url']['id'] => 1
    // $this->params['url']['code'] => 'abcd'
    // $this->params['url']['offet'] => 10
  }
}
?>

2. $_POST

$_POSTの値はController#params[‘form’]に格納されます。
なお中でも$_POST[‘data’]は特別で、Controller#data/Controller#params[‘data’]にも格納されます。

[id=1&name=abcd&comment=Hello!!&data[Foo][name]=Bar]をPOST

<?php
class FooController extends AppController {
  function index() {
    // $this->params['form']['id'] => 1
    // $this->params['form']['name'] => 'abcd'
    // $this->params['form']['comment'] => 'Hello!!'

    // 以下3つは同じ
    // $this->params['form']['data']['Foo']['name'] => 'Bar'
    // $this->params['data']['Foo']['name'] => 'Bar'
    // $this->data['Foo']['name'] => 'Bar'
  }
}
?>

3. $_COOKIE

$_COOKIEの値を参照する方法は用意されていません。もし$_COOKIEの値を使用する場合は直接参照するしかないようです。
ちなみにフレームワーク側はCakeSessionクラスがこの値を参照しています。

4. $_FILES

$_FILESはおおよそ$_POSTと同様にキーによって格納される場所が異なります。

まず$_FILES[‘data’]以外の箇所についてですが、これは$_POSTと同じようにController#params[‘form’]に格納されます。

つぎに$_FILES[‘data’]ですが、こちらは$_POST[‘data’]と同じようにController#data/Controller#params[‘data’]に格納されます。ここでは$_FILES[‘data’]がそのまま格納されるわけではなく、下のソースのように[$model][$field][$key]の連想配列に値を格納されます。

[cake/dispatcher.php:340]

		if (isset($_FILES['data'])) {
			foreach ($_FILES['data'] as $key => $data) {

				foreach ($data as $model => $fields) {

					foreach ($fields as $field => $value) {
						$params['data'][$model][$field][$key] = $value;
					}
				}
			}
		}

$_POST[‘data’]の場合と異なり、$_FILES[‘data’]の値はController#params[‘form’]には格納されません。

なお、$_FILESの格納処理は$_POSTの値が格納された後に行われます。つまり$_POSTと$_FILESが同じキーを持つ値は$_FILESの値で上書きされてしまいますのでご注意を。

5. $_ENV/$_SERVER

$_ENV/$_SERVERの値は[cake/basics.php]にあるenv()関数で参照できます。
どちらの変数にも同じキーで値がある場合は、$_SERVERの値が優先され、次に$_ENVの値になります。双方に該当するキーが無い場合はgetenv()の値が返ります。(全てのキーが無ければnullが返ります。)

<?php
class FooController extends AppController {
  function index() {
    $_SERVER&#91;'foo'&#93; = 'server';
    $_ENV&#91;'foo'&#93; = 'env';
    putenv('foo=getenv');
    // server を出力
    var_dump(env('foo'));

    unset($_SERVER&#91;'foo'&#93;); 
    // env を出力
    var_dump(env('foo'));

    unset($_ENV&#91;'foo'&#93;); 
    // foo を出力
    var_dump(env('foo'));
  }
}
?>

6. $_REQUEST/$_GLOBALS

$_REQUEST/$_REQUESTの値は特に参照する方法は用意されていません。フレームワークでも参照している箇所は見当たりませんでした。

Twitterで暇つぶし

この記事の所要時間: 040

またまたTwitterネタ。

Twitter 検索某ネタのおかげで好評でした(@ieiriさん、ありがとうございました)ので、調子に乗ってこんなのも作ってみました。

Twitter で暇つぶし : みんな何してる?
twitter_viewer_qr.jpg

携帯でTwitterのステータス(投稿)を見るだけのサイトです。

外出している時に携帯でTwitterを見ていると意外と更新されずに何度もリロードする事ありません?

Friendsをバンバン増やせば良いのでしょうけどあまり増やしすぎると今度PCで見るときがつらかったり。

知らない人のステータスをただ見るだけなのですが意外と面白かったりします。

登録等は一切不要なので「Twitterって聞くけど、何?」な方も一度どうぞ。

ホーム > PHP

検索
フィード
メタ情報

Return to page top