back

Emacs で PHPコードを編集するための設定と、PHPに関する学習メモ・雑記。


実行環境



php-modeの導入

今まで、php-mode.el version 1.2.0 をインストールしていたのだが、version 1.4.0 がリリースされたので導入した。make すると info ファイルが生成される。

;;; php-mode.el --- major mode for editing PHP code

;; Copyright (C) 1999, 2000, 2001, 2003, 2004 Turadg Aleahmad
;;               2008 Aaron S. Hawley

;; Maintainer: Aaron S. Hawley <ashawley at users.sourceforge.net>
;; Author: Turadg Aleahmad, 1999-2004
;; Keywords: php languages oop
;; Created: 1999-05-17
;; Modified: 2007-01-02
;; X-URL:   http://php-mode.sourceforge.net/

(defconst php-mode-version-number "1.4.0"
  "PHP Mode version number.")

プロジェクトページ: PHP mode for Emacs (sourceforge.net)

日本語訳マニュアル: PHP Mode マニュアル

以下の設定で、関数名補完を利用するためにはPHPマニュアルをダウンロードする必要がある。

PHPマニュアルのダウンロードページ: Download documentation

(require 'php-mode)

(eval-after-load 'php-mode
  '(progn
    ;; C-c RET: php-browse-manual
    (setq php-manual-url "http://www.php.net/manual/ja/")
    ;; C-c C-f: php-search-documentation
    (setq php-search-url "http://jp2.php.net/")
    ;; 補完のためのマニュアルのパス
    (setq php-manual-path "~/lib/php/html/")
    ;; M-TAB が有効にならないので以下の設定を追加
    (define-key php-mode-map "\C-\M-i" 'php-complete-function)
    ;; その他
    (define-key php-mode-map "\C-\M-a" 'php-beginning-of-defun)
    (define-key php-mode-map "\C-\M-e" 'php-end-of-defun)
    ))

php-mode は imenu の設定も行っているので、好みでメニューバーに追加する。

(add-hook 'php-mode-hook
          (lambda ()
            ;; imenu をメニューバーに追加
            (imenu-add-to-menubar "Imenu")
            ))

補完コマンドの改造

補完コマンドの php-complete-function を mcomplete.el と組み合わせて利用するため、インタフェースに completing-read を使うように改造した。以下のコードを .emacs にコピーする(completing-read による補完が好きな人向け)。download

(eval-after-load 'php-mode
  '(defun php-complete-function ()
    "Perform function completion on the text around point.
Completes to the set of names listed in the current tags table
and the standard php functions.
The string to complete is chosen in the same way as the default
for \\[find-tag] (which see)."
    (interactive)
    (let ((pattern (php-get-pattern))
          beg
          completion
          (php-functions (php-completion-table)))
      (if (or (= (preceding-char) ?\ )(not pattern))
          (setq pattern "" completion "")
        (search-backward pattern)
        (setq beg (point))
        (forward-char (length pattern))
        (setq completion (try-completion pattern php-functions nil)))
      (cond ((eq completion t))
            ((null completion)
             (message "Can't find completion for \"%s\"" pattern)
             (ding))
            ((not (string= pattern completion))
             (delete-region beg (point))
             (insert completion))
            (t
             (let ((end (point))
                   (sym (completing-read "Complete PHP symbol: "
                                         php-functions nil t pattern)))
               (when (not (equal sym ""))
                 (unless (string= pattern "") (delete-region beg end))
                 (insert sym))))))))

マニュアルの検索

php-mode はポイント位置の単語を拾って、PHPマニュアルを検索するコマンドが含まれている(php-search-documentation, コマンドキーは、C-c C-f)。しかし、ユーザーが任意の関数を入力して調べたいと思っても、残念ながらできない。そこで、補完のための関数を利用して、ユーザーが指定する関数をマニュアルから検索するコマンドを作った。補完機能を利用しているため、うろ覚えでも関数名を指定できるし、一覧から選択することも可能なので、この方が便利だと思う。

Screen shot

(defun php-completing-search-documentation ()
  "Search PHP documentation for the symbol user specified."
  (interactive)
  (let* ((default (current-word t))
         (pattern (completing-read
                   (format "PHP symbol {`%s'}: " (or default ""))
                   (php-completion-table))))
    (browse-url
     (concat php-search-url (if (string= pattern "") default pattern)))))

設定は、標準のキーバインドと差し替えて、次のようにすれば良いでしょう。

(define-key php-mode-map "\C-c\C-f" 'php-completing-search-documentation)

eldoc for php-mode

eldoc は、関数呼び出しの引数リストを自動的にエコーエリアに表示する簡易ヘルプ機能で、Emacs に標準で付属しているマイナーモード。バッファのメジャーモードがサポートしていれば M-x eldoc-mode で enable と disable をトグルできる。便利な機能なので、php-mode でも eldoc を使えるようにした。マニュアルから抽出した関数名と引数リストを辞書として自前で持っているだけなので、ユーザー定義関数の場合は表示できない。下の phpdoc.el をダウンロード後、ロードパスの通ったディレクトリにコピーして、以下の設定をすれば利用できる。

(require 'phpdoc)
(add-hook 'php-mode-hook (lambda () (eldoc-mode t)))

Download: phpdoc.el v 1.10

(次のスクリプトを使って英語版マニュアルから辞書を生成した: phpdoc.scm)

phpdoc.png

確かに eldoc が利用できると便利なのだけれど、補完で入力した後、開きカッコを入力しても肝心の関数の情報が表示されない。カーソルを動かすとか空白を入力したりすれば表示されるけれど、これではちょっと不便だ。私自身は、Emacs22 から追加された insert-pair というコマンドを使って閉じカッコも同時に入力しているので、次のような設定をすることにした。下の設定で、'(php-mode python-mode) として、python-mode でも試してみたところ具合がいい気がします。

(eval-after-load 'eldoc
  '(defadvice insert-pair (after for-eldoc-mode activate)
    "Force display eldoc-message."
    (and (local-variable-if-set-p 'eldoc-documentation-function)
         (memq major-mode '(php-mode))
         eldoc-mode
         (eldoc-message (funcall eldoc-documentation-function)))))

insert-pair は、次のような感じで設定すればよいです。

(setq parens-require-spaces nil)	; 開きカッコの前に空白を入れない
(define-key php-mode-map "(" 'insert-pair)

flymake を利用した構文チェック

Emacs 22 に標準で付属している flymake を利用すると、PHPコードのリアルタイム構文チェックを行なうことができる。

参考ページ

Download: flymake-php.el (上記ページを参考にファイル化したものです。著作権は上記ブログの管理者 Wersja polska 氏?にあると思われます。)

flymake-php.el をダウンロード後、ロードパスの通ったディレクトリにコピーして、以下の設定を行なう。

(require 'flymake-php)
(add-hook 'php-mode-hook 'flymake-php-load)

コードを書いている途中で、エラーがあると自動的にハイライトされる。また、C-c d (コマンド名は、flymake-display-err-menu-for-current-line)でエラー内容がポップアップメニューで表示されるので確認できる。必要に応じて以下のような設定をしておけば、エラー行と警告行の色を変更できる。

(eval-after-load 'flymake
  '(progn
    (set-face-background 'flymake-errline "red")
    (set-face-background 'flymake-warnline "blue")
    ))

なお、flymake には Infoマニュアルが付属している。

(info "(flymake)Top")

php-eval.el

ちょっとしたテストコードを書いて、結果を確認したいということがあって Emacs Lisp を作ってみた。emacs-lisp-mode で、式を eval すると簡単に評価値が得られて開発が楽だけれど、同じような感覚で PHPのコードを書ければなあという願望から考えたものです。つまりインタラクティブな開発環境が欲しいと(他のスクリプト言語、ruby, python, scheme, haskell...などでもインタラクティブに開発できる)。実は、PHPにもインタラクティブモードがあって、$ php -a で起動できる。これを利用した Emacs Lisp があるのかどうかわからないけれど、あれば使ってみたい。とりあえず間に合せで、php-eval.el でもそれなりに便利。

Download: php-eval.el

以下のコマンドは、shell-command-on-region を使ってコードをPHPに渡し、結果を *PHP-OUTPUT* バッファに出力します。

その他、次のコマンドがあります。

*PHP-OUTPUT* バッファでは、C-c C-b で browse-url-of-buffer を実行できます。また、defvar で宣言している変数はカスタマイズ可能です。

設定例:

(require 'php-mode)
(require 'php-eval)
(eval-after-load 'php-eval
  '(progn
    (define-key php-mode-map "\C-x\C-e" 'php-eval-delimiter)
    (define-key php-mode-map "\C-c\C-i" 'php-eval-insert-delimiter)
    (define-key php-mode-map "\C-c\C-x" 'php-eval-tag)
    (define-key php-mode-map "\C-c\C-e" 'php-eval-last-tag)
    (define-key php-mode-map "\C-c\C-r" 'php-eval-region)
    (define-key php-mode-map "\C-c\C-v" 'php-eval-display-buffer)
    ))

基本的な文法

PHPマニュアル

タグ、セミコロン

PHPは埋め込み型の言語で、ファイルを解析して開始タグを探し、見つかればコードを実行し、終了タグが見つかれば実行を終了する。それ以外の部分は、そのまま出力する。

PHP で利用できるタグは 4種類。このうち、

<?php ?>

<script language="php"> </script>

は、常に使用することができる。他は短縮型のタグとASPスタイルのタグで、php.ini で有効/無効を切り替える。

PHPは、C言語ライクな文法で、文の区切りにセミコロンが必要。しかし、終了タグにはセミコロンが含まれていると認識されるので、終了タグの直前の文では省略可能。ブロックの終了タグは、直後に改行がある場合それを含む。

ファイル終端では、終了タグを付けるのは任意。include() や require() を利用する場合は、終了タグを省略する方が無難。

コメント

C、C++、Unixシェル型のコメントをサポートしている。


PHPマニュアル

8種類の基本型をサポートし、ドキュメント上の擬似的な3種類の型がある。

基本型

※スカラーとは、配列などと違い、より小さな部分に'分割する'ことができない値のこと。

ドキュメント上の疑似的な型:

型と値を調べる

型変換

強制的に変換するには、キャストするか settype() 関数を使う。

boolean 型

TRUE または FALSE を表わす。boolean型に変換した場合 FALSE となるもの。

整数型

8 進数表記は数の前に 0 を付ける。16 進数表記は数の前に 0x を付ける。

整数のサイズはプラットフォーム依存。一般的には、32 ビット符号付整数の範囲。

<?php echo PHP_INT_MAX; ?>      // 2147483647

integer型の範囲外の数を指定した場合、また結果が integer型の範囲外の数となるような計算を行った場合 float型になる。

<?php var_dump(pow(2, 31) - 1); ?> // float(2147483647)
<?php var_dump(pow(2, 30)); ?>     // int(1073741824)

文字列型

PHPマニュアル

配列

PHP では添字配列と連想配列の間に違いはなく、配列型は 1 つだけで、整数または文字列のインデックスを使用することができる。

NULL

NULL は、変数が値を持たないことを意味する。


変数

PHPマニュアル

定数

PHPマニュアル

定数としてセットされているかどうかチェックするには、defined() 関数を使う。(変数について調べるには、isset() 関数、関数が定義されているかどうかは、function_exists() 関数を利用する。)

<?php var_dump(defined('PHP_INT_MAX')); ?> // bool(true)

定数の値を得るには、constant() 関数を使用する。

<?php echo constant('PHP_INT_MAX'); ?> // 2147483647

定義済みの定数の一覧は、get_defined_constants() 関数を使用する。

<?php print_r(get_defined_constants()); ?>

PHPマニュアル


演算子

PHPマニュアル

代数演算子

変数の値を負にする。(-)

<?php $a = 1; echo -$a; ?>      // -1

剰余(%) は、両オペランドの小数点以下を切り捨ててから処理される。

代入演算子

文字列の連結 (.=)

<?php
$a = 'Hello, ';
$a .= 'world!';
echo $a;                        // Hello, world!
?>

比較演算子

(===): 両オペランドが等しく、同じ型でである場合に真。

(!==): 両オペランドが等しくないか、同じ型でない場合に真。

文字列と整数との比較では、文字列は整数に変換される。数値形式の文字列どうしでは、整数として比較される(switch文にも適用されるルール)。

<?php
var_dump(0 == "a");             // 0 == 0 -> true
var_dump("1" == "01");          // 1 == 1 -> true
var_dump("1" == "1e0");         // 1 == 1 -> true
?>

エラー制御演算子

(@): 式の前に付けた場合、その式が生成するエラーメッセージが無視される。

<?php
$file = file('存在しないファイル');
echo 'hello';
?>

// 結果
PHP Warning:  file(存在しないファイル): failed to open stream: No such file or directory in - on line 2
<font color=ff0000>
Warning: file(存在しないファイル): failed to open stream: No such file or directory in - on line 2
</font>hello
<?php
$file = @file('存在しないファイル');
echo 'hello';
?>

// 結果
hello
<?php
$file = @file('存在しないファイル') or
    die ('オープンできません');
echo 'hello';
?>

// 結果
// die は、exit()と同等で現在のスクリプトを終了する。
オープンできません

実行演算子

(``): シェルのコマンドを実行できる。単に出力するだけでなく、変数に代入できる。shell_exec() 関数と等価。

<?php
$output = `date`;
echo $output;                   // 2008年  3月  6日 木曜日 20:12:43 JST
?>
<?php
$output = shell_exec('date');
echo $output;                   // 2008年  3月  6日 木曜日 20:14:54 JST
?>

配列演算子

型演算子

(instanceof): ある変数が特定のクラスのインスタンスであるかどうかを調べる。


制御構造

PHPマニュアル

foreach


関数

PHPマニュアル


練習

factorial (階乗)

// 再帰
<?php
function factorial($n) { return $n <= 0 ? 1 : $n * factorial($n - 1); }

var_dump(factorial(10));             // int(3628800)
var_dump(factorial(100));            // float(9.3326215443944E+157)
var_dump(factorial(170));            // float(7.257415615308E+306)
var_dump(factorial(171));            // float(INF)
?>
// while 1
<?php
function factorial($n) {
    $ans = 1;
    while ($n > 0) { $ans *= $n; --$n; }
    return $ans;
}
var_dump(factorial(10));        // int(3628800)
var_dump(factorial(100));       // float(9.3326215443944E+157)
var_dump(factorial(170));       // float(7.257415615308E+306)
var_dump(factorial(171));       // float(INF)
var_dump(factorial(1000));      // float(INF)
?>
// while 2
<?php
function factorial($n) {
    $ans = $n;
    while (--$n > 0) { $ans *= $n; }
    return $ans;
}
var_dump(factorial(10));        // int(3628800)
var_dump(factorial(100));       // float(9.3326215443944E+157)
var_dump(factorial(170));       // float(7.257415615308E+306)
var_dump(factorial(171));       // float(INF)
var_dump(factorial(1000));      // float(INF)
?>
// for
<?php
function factorial($n) {
    $ans = 1;
    for ( ; $n > 0; --$n) { $ans *= $n; }
    return $ans;
}
var_dump(factorial(10));        // int(3628800)
var_dump(factorial(100));       // float(9.3326215443944E+157)
var_dump(factorial(170));       // float(7.257415615308E+306)
var_dump(factorial(171));       // float(INF)
var_dump(factorial(1000));      // float(INF)
?>

πの近似

// John Wallis

<?php
function pi_product($a, $b) {
    $ans = 1;
    while ($a <= $b) {
        $ans *= (4 * (pow($a, 2) + $a)) /  pow($a * 2 + 1, 2);
        ++$a;
    }
    return $ans;
}
echo 4 * pi_product(1, 1000);   // 3.1423773650939
?>

product (高階関数)

高階関数 product を使って、factorial と pi_product を定義する。

<?php
// 高階関数
function product($term, $a, $next, $b) {
    $ans = 1;
    while ($a <= $b) {
        $ans *= $term($a);
        $a = $next($a);
    }
    return $ans;
}

// 階乗
function factorial($n) {
    return product(create_function('$x', 'return $x;'),
                   1,
                   create_function('$x', 'return $x + 1;'),
                   $n);
}

// πの近似
function pi_product($a, $b) {
    return product(
        create_function('$x', 'return (4 * (pow($x, 2) + $x)) /  pow($x * 2 + 1, 2);'),
        $a,
        create_function('$x', 'return $x + 1;'),
        $b);
}

echo factorial(10);             // 3628800
echo 4 * pi_product(1, 1000);   // 3.1423773650939
?>

sum_integers (aからbまでの整数の和)

// 再帰
<?php
function sum_integers($a, $b) {
    return $a > $b ? 0 : $a + sum_integers($a + 1, $b);
}
echo sum_integers(1,10);        // 55
echo sum_integers(1,0);         // 0
echo sum_integers(0,0);         // 0
?>
// for
<?php
function sum_integers($a, $b) {
    $ans = 0;
    for ( ; $a <= $b; ++$a) { $ans += $a; }
    return $ans;
}
echo sum_integers(1,10);        // 55
echo sum_integers(1,0);         // 0
echo sum_integers(0,0);         // 0
?>
// while
<?php
function sum_integers($a, $b) {
    $ans = 0;
    while ($a <= $b) { $ans += $a++; }
    return $ans;
}
echo sum_integers(1,10);        // 55
echo sum_integers(1,0);         // 0
echo sum_integers(0,0);         // 0
?>

sum_cube (aからbまでの整数の三乗の和)

// 匿名関数
// PHPでは、関数内関数といってもグローバルなので無名関数を使う
<?php
function sum_cube($a, $b) {
    $cube = create_function('$x', 'return $x * $x * $x;');
    $ans = 0;
    while ($a <= $b) { $ans += $cube($a++); }
    return $ans;
}
echo sum_cube(1,10);            // 3025
var_dump(isset($cube));         // bool(false)
?>

sum (高階関数)

高階関数 sum を使って、sum_integers と sum_cube を定義する

<?php
// 高階関数
function sum($term, $a, $next, $b) {
    $ans = 0;
    while ($a <= $b) {
        $ans += $term($a);
        $a = $next($a);
    }
    return $ans;
}

// 整数の和
function sum_integers($a, $b) {
    return sum(create_function('$x', 'return $x;'),
               $a,
               create_function('$x', 'return $x + 1;'),
               $b);
}

// 整数の三乗の和
function sum_cube($a, $b) {
    return sum(create_function('$x', 'return $x * $x * $x;'),
               $a,
               create_function('$x', 'return $x + 1;'),
               $b);
}

echo sum_integers(1,10);        // 55
echo sum_cube(1,10);            // 3025
?>

sigma (可変個の引数の総和)

PHPでは、次の3つの関数を利用すれば可変長引数を扱える。サンプルとして整数引数の総和を求める sigma() 関数を定義した。

int func_num_args(void);     // 関数に渡された引数の数を返す
mixed func_get_arg(int);     // 関数の引数リストから、指定した引数を返す
array func_get_args(void);   // 関数の引数リストを配列として返す
// func_get_args
<?php
function sigma() {
    $a = func_get_args();
    $ans = 0;
    foreach ($a as $x) { $ans += $x; }
    return $ans;
}
echo sigma(1,2,3,4,5);               // 15
echo sigma(1,2,3,4,5,6,7,8,9,10);    // 55
?>

// func_num_args, func_get_arg
<?php
function sigma() {
    $n = func_num_args();
    $ans = 0;
    for ($i = 0; $i < $n; ++$i) { $ans += func_get_arg($i); }
    return $ans;
}
echo sigma(1,2,3,4,5);               // 15
echo sigma(1,2,3,4,5,6,7,8,9,10);    // 55
?>

ライブラリ関数の array_sum() があるので、ループしなくてもよい。

<?php
function sigma() { return array_sum(func_get_args()); }
echo sigma(1,2,3,4,5);               // 15
echo sigma(1,2,3,4,5,6,7,8,9,10);    // 55
?>

高階関数の array_reduce() を使う手もある。

<?php
function sigma() {
    return array_reduce(
        func_get_args(),
        create_function('$k, $x', 'return $x + $k;'),
        0);
}
echo sigma(1,2,3,4,5);               // 15
echo sigma(1,2,3,4,5,6,7,8,9,10);    // 55
?>

Scheme なら fold や apply が使える。

(define (sigma . x) (fold + 0 x))
(define (sigma . x) (apply + x))

gosh> (sigma 1 2 3 4 5)
=>15
gosh> (sigma 1 2 3 4 5 6 7 8 9 10)
=>55

配列のコピー

PHPでは、配列をコピーするには単に代入すればよく、内部ノードまで再帰的にコピーされる。代入は、元の変数を新しい変数にコピーする(値による代入)ため、片方の変数に対する変更はもう片方に影響を与えない。この点、データ型が基本型と参照型に分かれ、配列は参照が代入される JavaScript とは異なる。

配列を受け取るユーザー定義関数では、値渡しがデフォルトなので、配列が変更されてもよい場合や配列を走査するだけのような目的なら、参照渡しにしないとリソースを無駄に消費してしまう。代入の場合も参照による代入が可能(PHPマニュアル)。

// JavaScriptの場合

a = [1,2,3];
b = a;                          // 代入ではコピーできない
a[0] = 100;
b                               // [100, 2, 3]
// PHPの場合
<?php
$a = array(1,2,3, array(array(4,5),6,7));

$b = $a;                             // 値による代入
$c = &$a;                            // 参照による代入

$a[1] = 100;                         // $aを変更
$a[3][0][1] = 200;                   // $aを変更

print_r($a);
print_r($b); // 内部ノードまでコピーされているので$aを変更しても影響がない
print_r($c); // $aが変更されれば影響を受ける
?>
// $a
Array
(
    [0] => 1
    [1] => 100
    [2] => 3
    [3] => Array
        (
            [0] => Array
                (
                    [0] => 4
                    [1] => 200
                )

            [1] => 6
            [2] => 7
        )

)
// $b
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => Array
        (
            [0] => Array
                (
                    [0] => 4
                    [1] => 5
                )

            [1] => 6
            [2] => 7
        )

)
// $c
Array
(
    [0] => 1
    [1] => 100
    [2] => 3
    [3] => Array
        (
            [0] => Array
                (
                    [0] => 4
                    [1] => 200
                )

            [1] => 6
            [2] => 7
        )

)

配列の参照渡し

参照渡しにする場合には、関数定義において & を仮引数の前に付ける(配列以外でも同じ)。

<?php
// array_foreach: 関数と配列を受け取り、配列の各要素に関数を適用する

function array_foreach($func, &$a) {
    for ($i = 0, $n = count($a); $i < $n; ++$i) {
        $func($a[$i]);
    }
}

$a = array(1,2,3);

// 配列を非破壊的に変更する関数を渡す
array_foreach(create_function('$x', 'echo ($x *= $x) . "\n";'), $a);
print_r($a);

// 配列を破壊的に変更する関数を渡す
array_foreach(create_function('&$x', 'echo ($x *= $x) . "\n";'), $a);
print_r($a);
?>
1
4
9
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
)
1
4
9
Array
(
    [0] => 1
    [1] => 4
    [2] => 9
)

上で定義した array_foreach() は、PHP4以降で利用できる array_walk() 関数を使えば自前で用意する必要はない。Scheme なら、map に対応する副作用目的の for-each を使う場面で、PHPの場合は array_walk() 関数を使う。結果は上と同じになる。

// 自前の array_foreach ではなく、array_walk を使う

<?php
$a = array(1,2,3);

// 配列の要素を非破壊的に変更する関数を渡す
array_walk($a, create_function('$x', 'echo ($x *= $x) . "\n";'));
print_r($a);

// 配列の要素を破壊的に変更する関数を渡す
array_walk($a, create_function('&$x', 'echo ($x *= $x) . "\n";'));
print_r($a);
?>

再帰版の array_walk_recursive() 関数もあるが、PHP5以降に限られる。自分の環境では利用できないので、再帰版の array_foreach_recursive() 関数を定義した。再帰呼び出ししているため、どの程度実用的かは不明。

// 再帰版 array_foreach
<?php
// array_foreach_recursive: 関数と配列を受け取り、配列の各要素に再帰的に関数を適用する

function array_foreach_recursive($f, &$a) {
    for ($i = 0, $n = count($a); $i < $n; ++$i) {
        is_array($a[$i])
            ? array_foreach_recursive($f, $a[$i])
            : $f($a[$i]);
    }
}

$a = array(1,2,3, array(array(4,5),6,7));

// 配列の要素を非破壊的に変更する関数を渡す
array_foreach_recursive(create_function('$x', 'echo ($x *= $x) . "\n";'), $a);
print_r($a);

// 配列の要素を破壊的に変更する関数を渡す
array_foreach_recursive(create_function('&$x', 'echo ($x *= $x) . "\n";'), $a);
print_r($a);
?>
1
4
9
16
25
36
49
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => Array
        (
            [0] => Array
                (
                    [0] => 4
                    [1] => 5
                )

            [1] => 6
            [2] => 7
        )

)
1
4
9
16
25
36
49
Array
(
    [0] => 1
    [1] => 4
    [2] => 9
    [3] => Array
        (
            [0] => Array
                (
                    [0] => 16
                    [1] => 25
                )

            [1] => 36
            [2] => 49
        )

)

ツリーのマップ

array_map() 関数は、ツリー構造の配列には適用できないので、ツリー構造の配列にも適用可能な tree_map 関数を定義した。(ただし、array_map() 関数のように複数の配列を引数に取ることはできない)。関数を適用するのみか、関数を適用した結果を集めるかの違いがあるだけで、array_foreach_recursive とほとんど同じコードになる。

<?php
// tree_map: ツリー構造の配列に関数を再帰的に適用し、関数が返す結果を集める

function tree_map($f, $a) {
    $ans = array();
    for ($i = 0, $n = count($a); $i < $n; ++$i) {
        is_array($a[$i])
            ? array_push($ans, tree_map($f, $a[$i]))
            : array_push($ans, $f($a[$i]));
    }
    return $ans;
}

$a = array(1,2,3);
print_r(tree_map(create_function('$x', 'return $x * $x;'), $a));

$a = array(1,2,3, array(array(4,5),6,7));
print_r(tree_map(create_function('$x', 'return $x * $x;'), $a));
?>
Array
(
    [0] => 1
    [1] => 4
    [2] => 9
)
Array
(
    [0] => 1
    [1] => 4
    [2] => 9
    [3] => Array
        (
            [0] => Array
                (
                    [0] => 16
                    [1] => 25
                )

            [1] => 36
            [2] => 49
        )

)

関数とグローバル変数

関数内部では、変数の有効範囲はその関数にローカルなので、次のケースではエラーになる。

<?php
$global_var = 1;
function test() {
    return $global_var;
}
echo test();           // PHP Notice:  Undefined variable:  global_var
?>

関数内部からグローバル変数にアクセスするには、次のように global宣言するか、$GLOBALSを使用する。

<?php
$a = 1;

function test1() {
    global $a;
    return $a;
}
function test2() {
    return $GLOBALS['a'];
}
function test3() {
    global $b;
    $GLOBALS['c'] = 3;
    $b = 2;
}

echo test1();                        // 1
echo test2();                        // 1
test3();
echo $b;                             // 2
echo $c;                             // 3
?>

静的(static)変数

static変数は、他の変数と同様に関数内のローカルスコープを持つが、関数が呼ばれたときに一度だけ初期化され、処理が関数の外に移っても値は保持される。

<?php
function static_test () {
    static $count = 10;                      // 定数式で初期化する
    return $count++;
}

$i = 5;
while ($i--) { echo static_test() . "\n"; }
?>
10
11
12
13
14

スーパーグローバル変数

スーパーグローバル変数は定義済みの変数(配列)なので、全ての定義済みの変数を配列で返す、get_defined_vars() 関数で調べることができる。

<?php
foreach (get_defined_vars() as $key => $value) {
    printf("%-20s =>%s\n", $key, gettype($value));
}
?>
argv                 => array
argc                 => integer
HTTP_POST_VARS       => array
_POST                => array
HTTP_GET_VARS        => array
_GET                 => array
HTTP_COOKIE_VARS     => array
_COOKIE              => array
HTTP_SERVER_VARS     => array
_SERVER              => array
HTTP_ENV_VARS        => array
_ENV                 => array
HTTP_POST_FILES      => array
_FILES               => array
_REQUEST             => array

printf() 関数は、フォーマットした文字列を出力する。sprintf() 関数は、フォーマットした文字列を返すのみで出力はしない。

int printf ( string $format [, mixed $args [, mixed $... ]] )
string sprintf ( string $format [, mixed $args [, mixed $... ]] )

特定のスーパーグローバル変数を参照する場合にも、foreach が使える。

<?php
foreach ($_ENV as $key => $value) { echo "<b>$key:</b> $value<br>"; }
?>

Last modified: Thu Mar 20 04:50:59 JST 2008
Copyright (C) 2008 Kazushi NODA All Right Reserved.

Valid HTML 4.01 Transitional Valid CSS