弱小PGのブログ -2ページ目

Zend framework解読 vol.2.2 /Loader/Autoloader.php

■書いたのを間違えてC-x kしてしまいました。もう嫌だ。ぼくはVimer(?)になります。

色々と検証の結果、
フロントコントローラ(/public/index.php)から呼ばれてる
Zend/Application.phpが

Zend_Loader_Autoloader::getInstance()を

呼び出してる時点での
Zend/Loader/Autoloader.phpでは

・__construct
オートロードと、プロパティに値を突っ込むという2行だけど
妙に解釈が難しいメソッド

・getInstance
シングルトンパターンを生成するだけのシンプルなメソッド


がとりあえず呼ばれてる


キーというかトリガーになるメソッドっぽいのは
・autoload
・_autoload

autoloadでは
・getClassAutoloaders
こちゃこちゃしてる処理。

_autoloadでは
・getDefaultAutoloader
プロパティ _defaultAutoloaderを返すだけのメソッド

宣言時点でのプロパティの内容:
protected $_defaultAutoloader = array('Zend_Loader', 'loadClass');
↑Zend/Loader.phpのloadClassメソッド?(´・ω・`;)

・suppressNotFoundWarnings
引数が無しかNULLならプロパティsuppressNotFoundWarningsを返すだけ
引数があればその引数をbool型にして(0以外ならtrueにして)代入し、
オブジェクト自身を返すメソッド

宣言時点でのプロパティの内容:
protected $_suppressNotFoundWarnings = false;

が呼ばれる


■この中で中身を色々除いて解剖する必要があるのは

__constructと
autoloadと
_autoloadと
getClassAutoloadersか

////__construct()

/**
* Constructor
*
* Registers instance with spl_autoload stack
*
* @return void
*/
protected function __construct()
{
spl_autoload_register(array(__CLASS__, 'autoload'));
$this->_internalAutoloader = array($this, '_autoload');
}


//──────────────────────────────────
////__CLASS__はこの場合、どこのどのクラスから呼んでも
////このZend/Loader/Autoloader.phpクラスそのものしか指さないっぽい。
////動的にならないのにマジック定数にする意味はなんだろう。拡張性か。
//──────────────────────────────────


//──────────────────────────────────
////見た感じどの時点でautoloadクラスが呼ばれるか分からなかったので、
////実際にZend frameworkが動いてるWebアプリのAutoloader.phpにsyslogを
////仕掛けてみた。

////したら/public/index.phpが呼ばれてる時点で10回くらいautoloadと
////_autoloadを通っていた。

////色々調べていて分かったけれど
////たぶん、このAutoloader.phpが呼ばれた時点ではautoload()も_autoload()も
////実行されてない。(当たり前といえば当たり前だけど コンストラクタから
////呼ばれてへんし…)

////spl_autoload_registerは、引数に配列としてクラス名とメソッドを指定すると
////__autoload関数が呼ばれるタイミング(つまりrequireしていないクラスを
////インスタンス化しようとしたタイミング)でそのクラスのメソッドを呼び出す
////という仕組みにできるらしい

////重要なのは、ここでspl_autoload_registerにAutoloaderクラス自身が
////登録されてることで、今後requireしてないクラスを呼んだ時には
////このクラスのautoloadメソッドが呼ばれるようになってる
////syslogであの数autoloadが呼ばれていたのは、その数だけのrequireしていない
////クラスを呼んだから。というわけで 次はどんなクラスを呼んでるのか
////見てみることにした(´・ω・`)
//──────────────────────────────────

//──────────────────────────────────
↓autoload()を通った順番

Zend_Config_Ini
Zend_Application_Bootstrap_Bootstrap
Zend_Application_Bootstrap_BootstrapAbstract
Zend_Application_Bootstrap_Bootstrapper
Zend_Application_Bootstrap_ResourceBootstrapper
Zend_Application_Module_Autoloader
Zend_Loader_PluginLoader
Zend_Controller_Front
Zend_Registry

////_autoload()の中にも仕掛けてみたけど、倍になっただけでした
////Zend_Registryだけは1つ。ただし、もう1度ログを取ると
////タイムスタンプ同じものが2個に増えるというよくわからない現象が発生。。
////↑つまり、2回呼ばれてたことになってる。??

////今のところ、なぜ_autoloadメソッドを通ってるのかは不明。
////アンダーバーひとつ足りないし、足りてても実行はされないはずだし。
//──────────────────────────────────


>autoload()

//──────────────────────────────────
////このアプリケーションが実行されてる時はいつでも、
////(フロントコントローラで実行されるたび呼ばれるのだから)
////requireしていないクラスを読んだらこのメソッドが実行される(はず)。
////そういう意味では使用頻度高いクラスだからちょっとしっかり理解したい
////気がする
//──────────────────────────────────

/**
* Autoload a class
*
* @param string $class
* @return bool
*/
public static function autoload($class)
{
$self = self::getInstance();

//──────────────────────────────────
////色々勉強して、2度目以降getInstance()する時にはコンストラクタは動かない
////ってことは分かった。
//──────────────────────────────────

foreach ($self->getClassAutoloaders($class) as $autoloader) {

//──────────────────────────────────
////arrayを返すgetClassAutoloadersメソッドの返り値をイテレータで回す
//──────────────────────────────────


if ($autoloader instanceof Zend_Loader_Autoloader_Interface) {
if ($autoloader->autoload($class)) {
return true;

//──────────────────────────────────
////getClassAutoloadersの戻り値がZend_Loader_Autoloader_Interfaceの
////オブジェクトであった場合、そのクラスのautoloadメソッドに値を渡して
////真を返す。っていうかInterfaceクラスって…?
////つまり、Interfaceクラスの継承クラスだった場合ってことか。
//──────────────────────────────────

}
} elseif (is_array($autoloader)) {
if (call_user_func($autoloader, $class)) {
return true;

//──────────────────────────────────
////●call_user_func(callback,parameter);
////ここで指定したcallback関数にparameterを渡して実行させる
////
//──────────────────────────────────


}
} elseif (is_string($autoloader) || is_callable($autoloader)) {
if ($autoloader($class)) {
return true;
}
}
}

//──────────────────────────────────
////なんか少し読めてきた。これデバッグの練習になるかも
////これはgetClassAutoloaders()含め、
////クラスを念入りに調べて読み込むようにしてるだけの処理だ。。
////まぁクラスの名前からしてそうなんだろうけども
//──────────────────────────────────


return false;
}



/**
* Get autoloaders to use when matching class
*
* Determines if the class matches a registered namespace, and, if so,
* returns only the autoloaders for that namespace. Otherwise, it returns
* all non-namespaced autoloaders.
*
* @param string $class
* @return array Array of autoloaders to use
*/
public function getClassAutoloaders($class)
{
$namespace = false;
$autoloaders = array();


//──────────────────────────────────
////$namespaceは名前空間だろうけど、なぜfalseにしてるんだろう。初期化か。
//──────────────────────────────────

// Add concrete namespaced autoloaders
foreach (array_keys($this->_namespaceAutoloaders) as $ns) {

//──────────────────────────────────
////$_namespaceAutoloaders はarray();で初期化されてるプロパティ。
//──────────────────────────────────

if ('' == $ns) {
continue;

//──────────────────────────────────
////最初は空だろうから、ここを通る??と思ったら通らなかった。
////最初(public/index.phpにアクセスしただけの段階)では、
////このforeachと最後のifは通らない。
//──────────────────────────────────

}
if (0 === strpos($class, $ns)) {
$namespace = $ns;
$autoloaders = $autoloaders + $this->getNamespaceAutoloaders($ns);
break;
}
}

// Add internal namespaced autoloader
foreach ($this->getRegisteredNamespaces() as $ns) {
if (0 === strpos($class, $ns)) {
$namespace = $ns;
//──────────────────────────────────
////ここだけは通ってた。
////strpos(文字列、文字)文字列の中で、文字が最初に現れる位置を返す。
////
//──────────────────────────────────
$autoloaders[] = $this->_internalAutoloader;
break;
}
}

// Add non-namespaced autoloaders
$autoloaders = $autoloaders + $this->getNamespaceAutoloaders('');

// Add fallback autoloader
if (!$namespace && $this->isFallbackAutoloader()) {
$autoloaders[] = $this->_internalAutoloader;
}

return $autoloaders;
}

しっかり理解したいとは思ったけれど、
ちょっと煩雑すぎて挙動追うのが大変(´Д`;)他のクラスの挙動も分からないとちょっと
理解するのは厳しいかも。とにかくrequireしてないクラスに対して徹底的にオートロード仕掛けてるんだな
ということは覚えておいて、次へ行くべし

Zend frameworkの中身解読 vol.2.1 Zend/Application.php

自分のような弱小が読むにはちょっと敷居が高い気もするけど
読まなきゃ一生読めないままなので頑張ってみる

というか、モジュールにBootstrapを追加したときとか
requireしたクラスやライブラリはどのくらいの範囲で使えるのか
ブラックボックスなままだと色々困るので中身を拝見することにしたのです

少し調べると、インスタンス化の際にクラスに渡す引数は
コンストラクタで受け取れるらしい。それを前提に…

デフォルト値のままであれば
第1引数 APPLICATION_ENV には文字列 'production'が
第2引数には、/application/configs/application.iniへの絶対パスが格納されている。

全部を解読しようにも長すぎるので、今回はコンストラクタから連なる
初期処理のみ追いかけます。まだbootstrap()もrun()も実行されてない段階なので
これが終わったって、本当の地獄はこれからだ状態なんですけど


で、Zend/Application.phpの素人解読


////所有するプロパティ。英語を読むとそれぞれ、


protected $_autoloader;

//──────────────────────────────────
////オートローダーのための値 まんま
////@var Zend_Loader_Autoloader
//──────────────────────────────────

protected $_bootstrap;

//──────────────────────────────────
////ブートストラップ。としか書いてない。
////@var Zend_Application_Bootstrap_BootstrapAbstract
////↑ってことは抽象クラスのオブジェクトを持ってる。
//──────────────────────────────────

protected $_environment;

//──────────────────────────────────
////環境設定用?←コンストラクタで、第1引数を文字列にして突っ込んでる
////@var string
//──────────────────────────────────

protected $_optionKeys = array();

//──────────────────────────────────
////ローワーキャメル(小文字?)にするためのオプションキー。
////@var array
//──────────────────────────────────

protected $_options = array();

//──────────────────────────────────
////Zend_Applicationのためのオプションを持つ。
////@var array
//──────────────────────────────────

////プロパティここまで


public function __construct($environment, $options = null)
{

//──────────────────────────────────
////↑productionという文字列と、
////application.iniまでの絶対パスを受け取ってる。
//──────────────────────────────────

$this->_environment = (string) $environment;

//──────────────────────────────────
////productionを文字列指定した上でプロパティに入れる。
//──────────────────────────────────


require_once 'Zend/Loader/Autoloader.php';
$this->_autoloader = Zend_Loader_Autoloader::getInstance();

//──────────────────────────────────
////オートローダーオブジェクトをシングルトンで生成して
////プロパティに格納。
////このクラスのコンストラクタ、requireしてるファイルも
////追わないといけない。
//──────────────────────────────────

if (null !== $options) {
if (is_string($options)) {

//──────────────────────────────────
////$options(第1引数)に文字列が入っていれば、その場所の
////設定ファイルを読み込む(_loadConfigメソッドは
////下で追いかけます)
//──────────────────────────────────

$options = $this->_loadConfig($options);

} elseif ($options instanceof Zend_Config) {

//──────────────────────────────────
////$optionsにZend_Configオブジェクトが入っていたら
////Zend_Config::toArray()メソッドを実行して返り値を
////$optionに格納
////名前からして、設定ファイルを配列に格納してそうですが。
//──────────────────────────────────

$options = $options->toArray();

} elseif (!is_array($options)) {

//──────────────────────────────────
////オブジェクトでも文字列でもなければ例外を投げる
//──────────────────────────────────

throw new Zend_Application_Exception('Invalid options provided; must be location of config file, a config object, or an array');
}

$this->setOptions($options);

//──────────────────────────────────
////setOptionsメソッドを実行。

////■読み込んだクラス・メソッドを細かく追わなければ初期処理はこれで終了。

////ここでやってるっぽいことは、
////■引数として渡されたパスの設定ファイル(*.iniとは限らない)
////を読み込んで登録する(その際、Bootstrapの数・場所を
////設定ファイルを参考に登録しているっぽい…)
////■オートロードを実行している(?)←このクラスのコンストラクタによるが
////以上…かも
//──────────────────────────────────

}
}



//─────────────────────────────────
////ここから各クラス・メソッドを追ってみる
////まずメソッドから。それからZend_Autoloader・Zend_Config。
//─────────────────────────────────


protected function _loadConfig($file)
{

$environment = $this->getEnvironment();

//──────────────────────────────────
////このgetEnvironment()は、プロパティ$_environmentを返すだけのメソッド。
////わざわざメソッドを用意するのは、クラスの外から読めるようにするためだろうか?
//──────────────────────────────────

$suffix = pathinfo($file, PATHINFO_EXTENSION);

//──────────────────────────────────
////PATHINFO_EXTENSIONというのは、指定されたファイルの拡張子。
////(pathinfo()は、第2引数を指定しなければ絶対パス、
////ファイル名など4種類のファイル情報を受け取る)
////従って、デフォルトであれば$suffixには'ini'が入っているはず。
//──────────────────────────────────


$suffix = ($suffix === 'dist')
? pathinfo(basename($file, ".$suffix"), PATHINFO_EXTENSION)
: $suffix;


//──────────────────────────────────
////拡張子がdistであれば(distって馴染みない名前だけど、phpの設定ファイル?に
////あるらしい(?))もう一度取得し直してる どうして??
////●basename()→パスの最後にあるファイルの名前を返す
//──────────────────────────────────


//strtolower()←小文字にする
switch (strtolower($suffix)) {
case 'ini':
$config = new Zend_Config_Ini($file, $environment);
break;

//──────────────────────────────────
////デフォルトならここを通るはず。何気に$environmentも使ってる
//──────────────────────────────────


case 'xml':
$config = new Zend_Config_Xml($file, $environment);
break;

case 'json':
$config = new Zend_Config_Json($file, $environment);
break;

case 'yaml':
$config = new Zend_Config_Yaml($file, $environment);
break;

case 'php':
case 'inc':
$config = include $file;
if (!is_array($config)) {
throw new Zend_Application_Exception('Invalid configuration file provided; PHP file does not return array value');
}
return $config;
break;

//──────────────────────────────────
////↑このへんの挙動がいまいち不明だけれど、phpかincファイルに
////設定が記載されてる可能性を期待してるのだろうか。
//──────────────────────────────────

default:
throw new Zend_Application_Exception('Invalid configuration file provided; unknown config type');
}

return $config->toArray();

//──────────────────────────────────
////結局、↑メソッドの引数が設定ファイルであればZend_Configオブジェクトを返す。
//──────────────────────────────────

}
}

//─────────────────────────────────
////次はZend_Autoloader.php。だけど長すぎるのでいったん切ります
//─────────────────────────────────

Zend frameworkの中身解読 vol.1 /public/index.php

どうせ色々勉強するなら公開したほうがいいかなと思い。
ZF Toolを使ってZendアプリケーションを作る前提で、素人に毛が生えたようなのが
勉強しながら解読していきます。

ZFコマンドでプロジェクト作成したときに出来る、/public/index.phpの内容から。
弱小PGの解釈なんで、悪い意味で(・н・;)初心者の方に分かりやすい内容になるとおもいます。

//// ←4つついてるのが自分のコメントです

<?php

// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

//───────────────────────────────────
////●realpath() ../や./などのシンボリックリンクをすべて解決し、絶対パスを返す。
////●dirname() 親ディレクトリのパスを返す。
////_FILE_ ファイルのフルパスとファイル名。

////■つまり、サーバのルートディレクトリからドキュメントルート~/applicationディレクトリまでの絶対パスが
////定数APPLICATION_PATHに入る。
////フロントコントローラでこれをしてるってことは、undefineしたり
////上書きしない限り全コントローラでこの定数が使える(はず

////当初めんくらったのが、  ●● || ○○ という書き方。
//// || が or とニアイコールなのは知ってはいたのだけれど、新しすぎてびっくりした。これは、

////  (こちらが真であれば、→を無視してこちらを実行) || (←が偽であればこちらを実行)

////ということらしい。解説本などでは見たことがない。
//───────────────────────────────────


// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));


//───────────────────────────────────
////●getenv() 環境変数を取得する。環境変数というのは、phpinfo()で見られる情報のうち
////Environment以下の情報。試したら他のところは取れませんでした

////不思議なのは、最初のdefined('APPLICATION_ENV')が偽であれば
////getenv('APPLICATION_ENV')も必ず偽になるような気がするのだけれど、
////であれば三項演算子はなぜ用意してあるのだろう、とか。

////■デフォルトでは大半の場合、APPLICATION_ENVには'production'が入ると思う。
////この値は/application/configs/application.iniの[production]と同じなんだけれど、
////どう関わってるのかはまだ分かりません(´・ω;`)
////テストで動かしたい場合、あらかじめAPPLICATION_ENVにapplication.iniに記載したテスト用コンフィグの
////名称(エントリ?)を記載しておくなり、環境変数に記載しておくなりすれば動くのかなぁとか。
//───────────────────────────────────

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));

//───────────────────────────────────
////●set_include_path() そのスクリプト内でインクルードパスを書き換える。
////●implode(文字列、配列) 配列要素を文字列によって連結する
////●PATH_SEPARATOR Windowsであれば;、UNIX系OSであれば:を返す定数。インクルードパス用のセパレータ。
////●get_include_path() 現在のインクルードパスを取得する。

////■サーバールートからアプリケーション内の/libraryまでの絶対パスを
////インクルードパスに一時的に登録する=requireの際にパスを記述する手間を省く。
////という動作をしてるものと思います。
//───────────────────────────────────

/** Zend_Application */
require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap()
->run();

//───────────────────────────────────
////Zend/Application.phpは よくは知らないけどZend frameworkの中核をなす
////クラス(?)
////弱小PGだけに、クラスが引数をとってインスタンス化されるとき
////それはコンストラクタにいくのか?ということすら知りません。
////ディスパッチループしているらしきこのあたりの動作を知るために
////Application.phpをがんばって読解したい。。Bootstrap.phpもよくわからない。

////ここで分かるのは、どうやら'production'を格納したAPPLICATION_ENVと
//// /configs/application.iniへの絶対パスをZend_Applicationへの引数に指定してるので、
////これらの設定を参考にApplicationを動かすのだろうなってことくらい。
////見たまんまですけど、、
//───────────────────────────────────