February 28, 2009

これだから PHP は

PHPのsort関数は相当おかしい – hnwの日記

ま、PHP はテンプレート言語だからちゃんと sort できないケースがあっても別にいいのかな。
でも PHP がテンプレート言語だからといって言語仕様が腐っててもいい理由にはならないよな。
PHPer はそんなことあんまり気にしないのかな。
もしくはすでにこの問題は PHPer の間では常識なのかな。

追記:
array_unique関数がPHP5.2.9から後方互換性を失いました – hnwの日記
ネタが尽きない PHP w

October 28, 2008

PHP の foreach のポインタにまつわる変な挙動

PHP の foreach でポインタにまつわる変な挙動に遭遇しました。
PHP のバージョンは 5.2.4 です。

foreach で配列のポインタを使う

例えば以下のようなコード。

<?php
$array = array("a", "b", "c");

foreach ($array as $index => $item) {
    if ($index == 2) {
        // 3番目の要素だけ「X」に変える
        $item = 'X';
    }
}

var_dump($array);
?>

これの出力は以下のようになります。

array(3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
}

3番目の要素の値が「X」になってませんね。
これはコードを以下のように書き換えることで期待通りに動作させることができます。

<?php
$array = array("a", "b", "c");

foreach ($array as $index => &$item) {
    if ($index == 2) {
        // 3番目の要素だけ「X」に変える
        $item = 'X';
    }
}

var_dump($array);
?>

foreach の中で使用する変数である「$item」の頭に「&」を付けています。
こうすることで、変数「$item」が配列の各要素へのポインタになります。
これを実行すると以下のように出力されました。

array(3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "X"
}

期待通りですね。
よかったよかった。

って、これで終わりじゃないんです。

配列の挙動が。。。

次に、以下のようなプログラムを実行してみます。

<?php
$array = array("a", "b", "c");

foreach ($array as $index => &$item) {
    if ($index == 2) {
        // 3番目の要素だけ「X」に変える
        $item = 'X';
    }
}

//var_dump($array);

foreach ($array as $index => $item) {
    echo $item . "\n";
}
?>

さっきのプログラムの後ろに foeach で配列の要素を順番に出力する処理を付け足しているだけです。
これを実行すると以下が出力されました。

a
b
b

ん?、最後の要素は「X」のはずなのに「b」と出力されています。
どういうことでしょう?

いろいろ実験した結果、以下のコードでこのおかしな現象を再現できることがわかりました。

$lt;?php
$array = array("a", "b", "c");

foreach ($array as $index =$gt; &$item) {
}
var_dump($array);

foreach ($array as $index =$gt; $item) {
}
var_dump($array);
?$gt;

二つの foreach ループを回しているだけです。
一回目の foreach ではループ変数に要素へのポインタを指定しています。
これを実行すると以下のように出力されます。

array(3) {
  [0]=$gt;
  string(1) "a"
  [1]=$gt;
  string(1) "b"
  [2]=$gt;
  &string(1) "c"
}
array(3) {
  [0]=$gt;
  string(1) "a"
  [1]=$gt;
  string(1) "b"
  [2]=$gt;
  &string(1) "b"
}

二回目の foeach の後に配列の中身がおかしなことになってますね。
配列の最後の要素が「c」ではなく「b」になってます。
なんでこんなことになるんでしょうか?

ていうか、何もしない foreach 文を二つ書くだけで配列の挙動がおかしくなってしまうなんて素敵な言語ですね!

調べた

ちょっと調べてみたんですが、

PHP 5 以降、$value の前に & を付けることで、 容易に配列の要素の値を変更できるようになっています。 これにより、値をコピーするのではなく、 リファレンス が代入されます。
[略]
警告
foreach ループを終えた後でも、 $value は配列の最後の要素を参照したままとなります。 unset() でその参照を解除しておくようにしましょう。

PHP: foreach – Manual

ということらしいです。

foreach でループ変数にポインタを使った場合、foreach を抜けた後に unset() を呼ぶ必要があるんですねぇ。
(foreach 側で自動でやっておいてくれたらいいのに。。)
というわけで、さっきのコードを以下のように変えてみました。

$lt;?php
$array = array("a", "b", "c");

foreach ($array as $index =$gt; &$item) {
}
var_dump($array);

// ポインタをリセット
unset($item);

foreach ($array as $index =$gt; $item) {
}
var_dump($array);
?$gt;

一回目の foreach の後に「unset($item);」を追加しています。
出力は以下のようになりました。

array(3) {
  [0]=$gt;
  string(1) "a"
  [1]=$gt;
  string(1) "b"
  [2]=$gt;
  &string(1) "c"
}
array(3) {
  [0]=$gt;
  string(1) "a"
  [1]=$gt;
  string(1) "b"
  [2]=$gt;
  &string(1) "c"
}

おお、ちゃんと期待通りの値が出力されました!

でもまだ納得いかないんですよねぇ。
ポインタをリセットしないと二回目の foreach で配列がおかしくなるっていうのが解せないです。
なんでだろ。

PHP ってほんと難しい言語ですね。
こういうところが PHP の魅力なのかもしれませんね(違っ)。

追記
こちらの解説が詳しいです。
foreachの$valueを参照で受けると思わぬバグを引き起こす – ぱせらんメモ
そういうことだったんですね。

September 30, 2008

Piki

前に php で超簡易 wiki みたいなものを作ったんですけど(Piki – 1ファイルで Wiki っぽいもの)、それにちょこっとだけ修正を加えて github に置きました。

以下になります。

http://github.com/jugyo/piki/tree/master

また、デモページを作りましたのでよかったらどうぞ。

http://jugyoo.org/piki/

追記:
今回の修正で、全ての HTML タグをエスケープして出力するようにしたんだけど、さすがに不便かな。

September 26, 2008

結局一番面倒くさいのって

ていうかさあ、PHP 自体が言ってしまえばテンプレート言語なわけでしょ。
だから Smarty とか使ったら PHP の埋め込み記法(<?php … ?>)とか意味無くなっちゃうよね。
ていうか、ソース書くときにいつも「<?php」から書き始めないといけないとかダサすぎる。
と言いつつ、最近は PHP で作ることにそれなりの意義を見出してたりするんだよね。
僕にとって面倒くさいのは HTML をカリカリ書くことなわけで、PHP のウザさはそれと比べるともう全然大したことないんですよ。
最近そう思うようになってきた。
ていうか、結局一番面倒くさいのって Web アプリケーションを作ることそのものなんですよね(言っちゃった)。
ま、嫌いじゃないけど。

September 25, 2008

一種類のプログラミング言語しか知らない人は

「それ」を理解するというのは、「それ」と「それ」以外のものとの差異を認識できてるようになって初めて可能になるんじゃないかな。
一種類のプログラミング言語しか知らない人はその言語を本当に理解しているとは言い難いと思うのです。
例えば、PHP はどこがダメなのかということを理解するためには PHP 以外の言語を知る必要がある。
そうすることで逆に PHP の良いところが今まで以上に見えてくるかもしれない。

なんてね。

September 12, 2008

Piki – 1ファイルで Wiki っぽいもの

1ファイルで Wiki っぽいものを作ろうと思い立って、作りました。
これを使えばいわゆるホームページみたいなのが比較的簡単に作れると思います。

「Piki」とか命名してみましたが、検索するとすでに同じ名前のものがあるようですね。
本当にありがとうございました。

PHP5 で動かすことを想定しています。
SQLite3 を使用します。
Wiki 記法とかじゃなく、普通の HTML で書きます。
編集画面には Basic 認証をかけて誰でも編集したりできないようにしています。

あんまりテストしてません。

更新履歴

  • 2008.09.12: タイトルも編集できるようにしました

使い方

ダウンロードしたファイルの名前を index.php に変えてブラウザからアクセスします。
最初にアクセスしたときに DB の作成と初期化が行われます。
追記: スクリプトを配置したディレクトリに書き込み権限を与えておく必要があります。
追記: 配置するサイトの URL に合わせて $base_url を修正しておく必要があります。
Basic 認証のユーザーIDとパスワードは初期状態で「admin」と「password」になっているので、適宜変更してください(ソースコードの3行目と4行目のところ)。

ライセンス

BSDライセンス で

スクリーンショット

piki1

piki2

ソースコード

download

Gist 使うとソース貼り付けるの楽でいいですね。

July 25, 2008

CodeIgniter のモデル

CodeIgniter のビューの続き。
モデルについて。

モデルクラスは application/models/ フォルダに以下に置く。
モデルの定義は以下のような感じ。

ファイル: application/models/blog_model.php

<?php
class Blog_model extends Model {
  ...
}
?>

モデルを使用するには以下のようにコントローラで「$this->load->model(’Blog_model’);
」とか明示的に書く必要がある。

ファイル: application/controllers/blog.php

<?php
class Blog extends Controller {

  function index()
  {
    $this->load->model('Blog_model');
    ...
  }
  ...
}
?>

(上の例は微妙かも。コンストラクタに書いとけばいい気も。)

こうすると「$this->Blog_model」ってな感じでモデルにアクセスできるようになる。
でもこれだけじゃまだDBには繋がらない。
DBに接続するには以下のファイルを編集して「自動接続」を有効にする必要がある。

ファイル: application/config/autoload.php

$autoload['libraries'] = array('database');

↑「$autoload['libraries']」に「’database’」を追加してる。

あと、DB に接続するための設定も当然必要で、それは以下のファイルに記述する。

application/config/database.php

デフォルトでMySQLの設定が書いてあったので今回はMySQLを使うことに。
(SQLiteでやりたかったけど、そのための情報をすぐに見つけられなかったのでやめた。)

とりあえずMySQLに以下のようなテーブルを作った。

CREATE TABLE `ci`.`blogs` (
  `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  `title` VARCHAR( 255 ) NOT NULL ,
  `content` TEXT NOT NULL
) ENGINE = InnoDB

テストデータ:

INSERT INTO `ci`.`blogs` (
  `title` ,
  `content`
)
VALUES (
'テスト', 'テストですよー'
);

んで、コントローラを以下のようにすると画面にテーブルの内容が出てくる。

ファイル: application/controllers/blog.php

<?php
class Blog extends Controller {

  function index()
  {
    $this->load->model('Blog_model');

    $blogs = $this->Blog_model->db->get('blogs', 10)->result();

    var_dump($blogs);
  }
}
?>

スクリーンショット:

やったー。

ま、でも、var_dumpしただけで喜んでちゃいけない。
DB周りをもうちょっと突っ込んで調べないと。

参考:
CodeIgniter ユーザガイド 日本語版 : モデル
(前回と前々回の記事は若干古いドキュメントを参照していた。。)

ていうか、最近のフレームワークってどれもRailsっぽく見えてしまうなぁ。

CodeIgniter はドキュメントが丁寧に書かれていて(かつ日本語訳もされていて)非常に助かるなぁ。
ドキュメント重要ですねー。

追記:
えーっと、実は上記のモデル(Blog_model)の使用例はあんまり適切じゃないんですよねー。
なんでかっていうと、本来 blog テーブルからデータを取ってくる云々っていうのは Blog_model の内部でやるべきなんです。
それをコントローラー側でやってて、なんかモデルの意味無い!みたいな感じに上記の例ではなってます。
というわけでもうちょっと勉強します。

追記:
よくよく考えると、上の例の blogs テーブルとか blog_model とかっていうネーミングは変だ。
entries とか entry_model だよね普通。