狠狠撸

狠狠撸Share a Scribd company logo
5分で分かるかもしれない
名前空間とオートロード

 第1回 関西PHP初心者勉強会

  2011年8月27日(土)

  スーパーグローバル世代のPHPer
        k-holy
名前空間とは
「ソースコード上で冗長な命名規則を用いなくても名前の衝突が
起こらないようにし、しかもそれを容易に記述できるようにす
る」(Wikipediaより引用)ための概念

PHP的には、名前の衝突を避けるために使われてきたPEAR命名規
則、「HTTP_Request2_Adapter_Socket」のような、アン
ダースコア区切りの長いクラス名を整理し、適切に階層管理する
ための機能

PHP5.3.0から利用可能
名前空間を宣言する
namespace宣言は必ずコードの先頭に記述すること。
(コメント、改行、スペースは宣言の前に書いてもよい)
名前空間は「」(バックスラッシュ)区切りで階層を構成できる。
<?php
namespace HolyExample; //名前空間 ”HolyExample” を宣言


※1ファイル内では1つの名前空間で完結させることを推奨
名前空間を宣言する
namespace宣言は必ずコードの先頭に記述すること。
(コメント、改行、スペースは宣言の前に書いてもよい)
名前空間は「」(バックスラッシュ)区切りで階層を構成できる。
<?php
namespace HolyExample; //名前空間 ”HolyExample” を宣言

ちなみに、誤っていわゆるBOM付UTF-8でファイルを保存したところ、
こんなエラーで怒られてしまいました。

Fatal error:    Namespace declaration statement has to
be the very first statement in the script

※BOM付きUTF-8には気をつけて!
名前空間でのクラス定義
名前空間で定義されるクラス/関数/定数は、先頭に名前空間を
付加したものとして解釈される。

namespace HolyExample;//名前空間”HolyExample”を宣言

class Foo { //Fooクラスを定義
    public static function getClass() {
        return __CLASS__;//クラス名を返す
    }
}
echo Foo::getClass();//HolyExampleFoo
名前空間とグローバル空間
PHPの定義済みクラス/関数/定数は、グローバル空間に配置さ
れる。名前空間でグローバル空間のクラスを使用する場合は、先
頭に名前空間セパレータを付ける必要がある。
関数や定数は、名前が被らない場合に限り従来通りの記述で構わ
ない。逆に言うと、実は同名で定義すれば上書き可能(一部の定数を除く)

namespace HolyExample;

class Exception extends Exception{}
$ns_ex = new Exception();//HolyExampleException
$global_ex = new Exception();//Exception
PHP_VERSION;//付けても
PHP_VERSION;//付けなくても同じ
別の名前空間のクラスを使う
別の名前空間に定義されたクラスを利用する場合、以下いずれか
の方法で記述する。

●   完全修飾名で記述
●   use 文でインポートしたクラス名で記述
●   use 文 + as キーワードで定義したエイリアスで記述
完全修飾名
名前空間セパレータを含む識別子のうち、先頭に名前空間セパ
レータを付けて指定したものを 完全修飾名 と呼ぶ。

HolyExample名前空間で、ZendFramework2の
ZendOAuthConsumer クラスを使う場合の例

●   完全修飾名
    namespace HolyExample;
    //ZendOAuthConsumerクラスを完全修飾名で指定
    $consumer = new ZendOAuthConsumer();

※ファイルシステムでにおける絶対パスのようなもの
インポートとエイリアス
●   インポート
    namespace HolyExample;
    use ZendOAuthConsumer;
    //インポートしたクラス名 Consumer でアクセスできる
    $consumer = new Consumer();

●   エイリアス
    namespace HolyExample;
    use ZendOAuthConsumer as OAuthConsumer;
    //定義したエイリアス OAuthConsumer でアクセスできる
    $consumer = new OAuthConsumer();

※適切なクラス名を付けていれば基本的にはインポートのみで良いはず
更に詳しく知りたい方は
  PHP Manualで
    FAQもあります
オートロードとは
クラスを利用する際、自動的にそのクラスが定義されているファ
イルを読み込む機能。(require_onceが不要に)
spl_autoload_register()関数で、オートローダのコール
バックを登録することで実装できる。

spl_autoload_register(function ($className) {
    //「_」をディレクトリ区切り文字に置換してinclude
    return (bool)@include str_replace('_',
        DIRECTORY_SEPARATOR, $className) . '.php';
}, true, true);

無名関数のこんな実装でも一応動くけど、これでは名前空間に対応でき
ていないので…
PSR-0という規格
オートローダの相互運用性を確保するために、クラスの完全修飾
名とファイル名の命名規則として提案されている規格

PSR-0 Final Proposal
 - PHP Standards Working Group – 2009-11-14
http://bit.ly/8UPl4A

※Symfony, Doctrine, ZendFramework, Lithium等の主要
なPHP5.3以降専用フレームワークが対応

この仕様に従って作成しておけば、おおむね問題なし
PSR-0の仕様
重要なのはこの3点
1.必ずトップレベルに「ベンダー名」を持たせ、完全修飾名を
  「<ベンダー名>(<名前空間>)*<クラス名>」とする
2.名前空間セパレータは DIRECTORY_SEPARATOR に変換される
3.クラス名に含まれるアンダースコアは
  DIRECTORY_SEPARATOR に変換される

ZendOAuthConsumer クラス(ベンダー名:Zend)
→/project/lib/vendor/Zend/OAuth/Consumer.php

※実装例の SplClassLoader はgistに掲載されている
https://gist.github.com/221634
+αのオートローダ仕様
PSR-0準拠は最低条件として、こんな仕様も追加しました。
ベンダー名ごとに検索対象ディレクトリおよびファイル拡張子を
指定可能とする

●   架空のクラス Smorty の例
    HolyLoader::getInstance()
    ->set('Smorty',//ベンダー名
        '/project/lib/vendor/Smorty/libs',//ディレクトリ
        '.class.php')//拡張子
    ->enableAutoload();
    $smorty = new Smorty();//Smorty
    →/project/lib/vendor/Smorty/libs/Smorty.class.php
stream_resolve_include_path
             オートロードが呼ばれると、
             コールバックオートローダの引
             数にクラス名が完全修飾名で渡
             されるので、PSR-0の仕様に
             従ってファイルパスに変換し、
             インクルードパスを考慮しつつ
             ファイルを検索する必要があり
             ます。
             そこで役に立ったのが、この関
             数
             これ使えば、もう @include とか
             書かなくてもいい
             ※PHP5.3.2以降
オートロード                 こんな時に呼ばれる
namespace HolyExample;

//クラスのインスタンスを生成
$foo = new Foo(); //HolyExampleFoo

//スタティックメソッドをコール
$foo = Foo::getClass();

//クラス定数を記述
Foo::CONSTANT;

//クラス/インタフェース定義で extends, implements
class RuntimeException
  extends RuntimeException //RuntimeException
  implements Exception {} //HolyExampleException
オートロード                 こんな時に呼ばれる
namespace HolyExample;

//スタティックメソッドのコールバックを呼ぶ
call_user_func(array('HolyExampleFoo',
    'getClass'));

//class_exists(), interface_exists()で第2引数を指定しな
い、またはtrueを指定
if (class_exists('HolyExampleFoo'))

※動的にクラス名を指定する際は完全修飾名で書かないとダメ
オートロード                  こんな時には呼ばれない
namespace HolyExample;

//タイプヒンティング
public function setFoo(Foo $foo)//HolyExampleFoo

//instanceof演算子
if ($foo instanceof Foo)//HolyExampleFoo

//try-catch節
try {
} catch (Exception $e) {//HolyExampleException
}
オートロード                  こんな時には呼ばれない
namespace HolyExample;

//is_a(), is_subclass_of()
if (is_a($foo, 'HolyExampleFoo'))

//スタティックメソッドのコールバックをis_callable()
if (is_callable(array('HolyExampleFoo',
    'getClass'))//普通にis_callable()のエラーにはなる

//class_exists(), interface_exists()で第2引数にfalseを指
定
if (class_exists('HolyExampleFoo', false))

※動的にクラス名を指定する際は完全修飾名で書かないとダメ
オートロードが呼ばれないと…
try {
    //UnknownExceptionがスローされるかもしれない処理
} catch (UnkomanException $e) {//タイプミスしてる
    mail('dev@example.com', 'Unknown error',
        $e->getMessage());
}

例外がスローされた時にメールで通知するつもりが…


Fatal error:    Uncaught exception
'HolyExampleUnknownException' with message...

※そもそもこういう場当たり的な例外処理は避けた方が無難ですが
オートロードスタック
spl_autoload_register()で複数のオートローダを登録した
場合、オートロードが呼ばれたら、対象のクラスが定義された
ファイルを読み込むまで、登録された順番で実行される。
spl_autoload_register()の第3引数にTRUEを指定すると、
スタックの末尾ではなく先頭に追加できるので、複数のオート
ローダを利用する場合は優先順位で使い分けが可能。


spl_autoload_functions()でオートロードスタックの配列を
取得できるので、オートロードの処理で何かがおかしい時はこれ
で確認してみてください。
まとめ
●   名前空間は普通に使うだけなら難しくない
●   BOM付きUTF-8の罠には要注意
●   名前空間/クラス名とファイルパスの対応はPSR-0を参考に
●   try-catchやinstanceofで例外を扱う時はオートロードが
    呼ばれないのでtypoに気をつけて



拙作オートローダのコードはgistに掲載しています。
https://gist.github.com/1127033

ありがとうございました。

More Related Content

5分で分かる名前空间とオートロード