@blog.justoneplanet.info

日々勉強

ZendFramework Zend_Uri

以下のようにすると文字列がURIとして妥当かどうか検証できる。

<?php
require_once 'Zend/Uri.php';

$uri = 'http://example.org';
if(Zend_Uri::check($uri)){
    //this is valid as a uri
}
else{
    //this is not valid as a uri
}

各メソッドについて

bool Zend_Uri::check(string $url)
与えられた文字列がURIとして適当か判定する。

ZendFramework Zend_Validate(バリデート)

■バリデート

IPアドレスかどうか判定するには以下のようにする。

<?php
require_once 'Zend/Validate/Ip.php';

$validator = new Zend_Validate_Ip();
if($validator->isValid('192.168.1.1')){
    //this is an IP address.
}
else{
    //this is not an IP address.
}

指定された範囲にあるかどうかを判定するには以下のようにする。

<?php
require_once 'Zend/Validate/Between.php';

$validator = new Zend_Validate_Between(10, 20);
if($validator->isValid(15)){
    //this is in the range.
}
else{
    //this is not in the range.
}

■メッセージの設定と取得

以下のようにバリデータオブジェクトにメッセージを付加することができる。

<?php
require_once 'Zend/Validate/Between.php';

$validator = new Zend_Validate_Between(10, 20);
$valiidator->setMessage('%value% is not in the range.');
if($validator->isValid(35)){
    //this is in the range.
}
else{
    $msg = $validator->getMessages();
    echo $msg[0];//35 is not in the range.
}

■バリデータチェーン

以下のようにして複数のバリデートを1つに扱うことができる。

<?php
require_once 'Zend/Validate.php';
require_once 'Zend/Validate/NotEmpty.php';
require_once 'Zend/Validate/Alpha.php';
require_once 'Zend/Validate/StringLength.php';

$validator_emp = new Zend_Validate_NotEmpty();
$validator_alp = new Zend_Validate_Alpha();
$validator_str = new Zend_Validate_StringLength(4, 10);

$validator_emp->setMessage('アカウント名が入力されていません。');
$validator_alp->setMessage('アカウント名は半角英数字のみで入力してください。');
$validator_str->setMessage('アカウント名は4~10文字で入力してください。');

$validChain = new Zend_Validate();
$validChain->addValidator($validator_emp, true)->addValidator($validator_alp)->addValidator($validator_str);

if(!$validChain->isValid($name)){
    $messages = $validChain->getMessages();
    foreach($messages as $msg){
        echo $msg;
    }
}

各コンポーネントについて

Zend_Validate_Alnum([bool $allowWhiteSpace = false])
英数字のみで構成されているか判定する。引数がtrueのときは空白を許可する。
Zend_Validate_Apha([bool $allowWhiteSpace = false])
アルファベットのみで構成されているか判定する。引数がtrueのときは空白を許可する。
Zend_Validate_Between(int $min, int $max[, bool $inclusive = true])
指定された範囲内かどうか判定する。第三引数がfalseのとき、境界値は含まない。
Zend_Validate_Ccnum()
クレジット番号として適当かどうか判定する。
Zend_Validate_Date([string $format = ‘YYYY-MM-DD'[, string $locate]])
日付として正しい形式か判定する。
Zend_Validate_Int()
整数型か判定する。
Zend_Validate_Float()
浮動小数点型か判定する。
Zend_Validate_InArray(mixed $value, array $haystack[, bool $strict = false])
設定した配列の中に指定のキーが存在するか判定する。第三引数がtrueのとき型の一致も判定する。
Zend_Validate_EmailAddress([$allow[, $validateMx[, $hostnameValidator]]])
メールアドレスとして適当か判定する。標準ではDNSのホスト名として正しいアドレスであるかを調べる。
Zend_Validate_HostName([$allow[, $validateIdn[, $validateTld[, $ipValidator]]]])
ホスト名として適当か判定する。標準ではDNSホスト名として有効かのみ調べる。
Zend_Validate_Ip()
IPアドレスとして適当か判定する。
Zend_Validate_Regex(string $pattern)
設定した正規表現にマッチするか判定する。

クレジット番号のような判定には便利だがPHPの関数のエイリアスのようなものはいかがであろう。。。

各メソッドについて

Zend_Validate_[TYPE]::isValid(mixed $subject)
引数が適合しているか判定する。
Zend_Validate_[TYPE]::setMesssage(string $msg[, $error_code])
メッセージを設定する。
Zend_Validate_[TYPE]::getMesssages()
メッセージを取得する。
Zend_Validate::addValid(object $validator[, bool $stop = false])
Zend_Validateオブジェクトにバリデータを付加する。第二引数でtrueを指定すると適合しなかった場合に残りの処理を行わない。

Zend Frameworkのrequire_onceがJavaのimportみたいで気持ちいい。

ZendFramework Zend_Feed(フィード)

以下のようにフィードを取得することができる。

<?php
require_once 'Zend/Feed.php';

try{
    $feed = Zend_Feed::import('http://example.org/rss.xml');
}
catch(Zend_Feed_Exception $e){
    die('eror');
}
if(get_class($feed) === 'Zend_Feed_Rss'){
    $channel = array(
        'site_title' => $feed->title(),
        'site_url' => $feed->title(),
        'site_description' => $feed->description()
    );
    $entries = array();
    foreach($feed as $item){
        $entries[] = array(
            'article_title' => $item->title(),
            'article_category' => $item->category(),
            'article_author' => $item->creator(),
            'article_url' => $item->link(),
            'article_content' => $item->description(),
            'article_update' => $item->pubDate()
        );
    }
}
else{
    $channel = array(
        'site_title' => $feed->title(),
        'site_url' => $feed->link('alternate'),
        'site_description' => $feed->tagline()
    );
    $entries = array();
    foreach($feed as $item){
        $entries[] = array(
            'article_title' => $item->title(),
            'article_category' => $item->subject(),
            'article_author' => $item->author->name(),
            'article_url' => $item->link('alternate'),
            'article_content' => $item->content(),
            'article_update' => $item->modified()
        );
    }
}

ZendFramework Zend_Debug, Zend_Log, Zend_Exception(デバッグ、ログ、例外)

■Zend_Debug

require_once 'Zend/Debug.php';

$str = 'this is a sample!'
Zend_Debug::dump($str);

■Zend_Log

長期にわたって監視したい場合に有効である。

require_once 'Zend/Log.php';
require_once 'Zend/Log/Writer/Stream.php';

$logger = new Zend_Log();
$writer = new Zend_Log_Writer_Stream('/path/to/log.txt');
$logger->addWriter($writer);

DBにログを書き込みたい場合は以下のようにする。

require_once 'Zend/Log.php';
require_once 'Zend/Log/Writer/Db.php';
require_once 'Zend/Db.php';

$dbh = Zend_Db::factory(
    'Pdo_Mysql',
    array(
        'host' => 'localhost',
        'username' => 'user',
        'password' => 'pass',
        'dbname' => 'dbname'
    )
);
$dbh->query("SET NAMES 'utf8'");

$logger = new Zend_Log();
$writer = new Zend_Log_Writer_DB(
    $dbh,
    'table_log',
    array(
        'level' => 'priority',
        'priority' => 'priorityName',
        'msg' => 'message',
        'register_date' => 'timestamp'
    )
);
$logger->addWriter($writer);

上述の場合、既にDBにtable_logテーブルが存在し、カラム(level,priority,msg,register_date)が作られている前提である。

実用例

以下のようにすると警告以上のメッセージしか記録されない。

require_once 'Zend/Log.php';
require_once 'Zend/Log/Writer/Stream.php';

$logger = new Zend_Log();
$writer = new Zend_Log_Writer_Stream('/path/to/log.txt');
$logger->addWriter($writer);
$writer->addFilter(Zend_Log::ERR);

$logger->log('システム使用不可' , Zend_Log::EMERG);
$logger->log('至急対応が必要' , Zend_Log::ALER);
$logger->log('危機的な状況' , Zend_Log::CRIT);
$logger->log('エラー' , Zend_Log::ERR);
$logger->log('警告' , Zend_Log::WARN);
$logger->log('注意' , Zend_Log::NOTICE);
$logger->log('情報' , Zend_Log::INFO);
$logger->log('デバッグメッセージ' , Zend_Log::DEBUG);

■Zend_Exception

ZendFrameworkでは全ての例外がZend_Exceptionを継承している。

<?php
require_once 'Zend/Db.php';
try{
    $dbh = Zend_Db::factory(
        'Pdo_Mysql',
        array(
            'host' => 'localhost',
            'username' => 'name',
            'password' => 'pass',
            'dbname' => 'dbname'
        )
    );
}
catch(Zend_Db_Adapter_Exeption $e){
    //failed to connect because of invalid user or db switch in the off position
}
catch(Zend_Exception $e){
    //failed to load the class file
}

各メソッドについて

Zend_Debug::dump(mixed $variable[, string $caption[, bool $isDisplayed]])
第一引数の内容を第二引数のキャプションをつけて表示する。第三引数がfalseの場合は値を文字列として返します。
Zend_Log::__construct(void)
ログオブジェクトを生成する。
Zend_Log_Writer_Stream::__construct(string $path_to_txt);
第一引数にファイルのパスを指定し書込オブジェクトを生成する。
Zend_Log_Writer_DB::__construct(resource $dbh, string $table_name, array $dbcolumn_for_log);
第一引数にDBハンドラを、第二引数にテーブル名を、第三引数にテーブルのカラムをキーにした連想配列を指定し書込オブジェクトを生成する。
Zend_Log::addWriter($writer);
ログオブジェクトに引数の書込オブジェクトを付加する。
Zend_Log_Writer_Stream::addFilter(int $error_level)
第一引数で指定したレベルでフィルタリングする。

ZendFramework Zend_Config(設定情報)

symfonyのymlは個人的にはどうも…という事でZend_Configを触ってみる。

■iniファイルのconfig

;config.ini
;preference information

[global]
domain_name = example.org
ip_address = 192.168.2.2
root_path = /home/admin

[database]
database.type = pdo_mysql
database.host = localhost
database.username = name
database.password = pass
database.dbname = dbname

プログラム中での使用

<?php
require_once 'Zend/Config/Ini.php';

$config = new Zend_Config_Ini('/path/to/config.ini', 'global');
$config->domain_name;// example.org
$config->root_path;// /home/admin
$config->database->username// null

各メソッドについて

Zend_Config_Ini Zend_Config_Ini(string $path_to_config, array $sections[,array $mode])
第一引数で指定したiniファイルを読み込んで第二引数のセクションのみインスタンスに格納する。第二引数にnullを設定すると全てのセクションを読み込む。セクションが1つのときは文字列で指定することも可能である。また第三引数では書き込みを許可することもできる。

ZendFramework Zend_Acl(アクセスコントロール)

■Zend_Acl

リソース
アクセス制御対象オブジェクト
ロール
リソースにアクセスを要求するオブジェクト

ロールの定義

require_once 'Zend/Acl.php';
require_once 'Zend/Acl/Role.php';

$acl = new Zend_Acl();
$acl->addRole(new Zend_Acl_Role('guest'));
$acl->addRole(new Zend_Acl_Role('member'), 'guest');
$acl->addRole(new Zend_Acl_Role('admin'), 'member');

アクセス制御の定義

以下のようにしてロールにアクセス制御

$acl->allow('guest', null, 'view');
$acl->allow('member', null, 'entry');
$acl->allow('admin', null, array('edit', 'delete'));

アクセス制御

if($acl->isAllowed('guest', null, 'view')){
    //code
}
else{
    //code
}
if($acl->isAllowed('member', null, 'delete')){
    //code
}
else{
    //code
}

実例

require_once 'Zend/Acl.php';
require_once 'Zend/Acl/Role.php';
require_once 'Zend/Acl/Resource.php';

$acl = new Zend_Acl();

$acl->addRole(new Zend_Acl_Role('guest'));
$acl->addRole(new Zend_Acl_Role('member'), 'guest');
$acl->addRole(new Zend_Acl_Role('admin'), 'member');

$acl->add(new Zend_Acl_Resource('myPage'));

$acl->allow('guest', null, 'view');
$acl->allow('member', null, 'entry');
$acl->allow('admin', null, array('edit', 'delete'));

$acl->deny('guest', 'myPage', 'view');
$acl->allow('member', 'myPage', 'view');

if($acl->isAllowed('guest', 'myPage', 'view')){
    //allow
}
else{
    //deny
}
if($acl->isAllowed('member', 'myPage', 'view')){
    //allow
}
else{
    //deny
}

各メソッドについて

Zend_Acl::addRole(new Zend_Acl_Role(string $rolename)[, string $inheritance])
第一引数で指定したロール名でロールを定義する。第二引数があった場合はそのロールを継承する。
Zend_Acl::allow(string $rolename, string $resource, string $restrict)
第一引数のロールに対して第三引数のアクセス制御を定義する。
bool Zend_Acl::isAllowed(string $rolename, string $resourcename, string $restrict)
第一引数のロールに対して第三引数のアクセス制御名でアクセス許可があるかをbool値で返す。

ZendFramework Zend_Auth(認証)

以下のようにするとZend_Authコンポーネントを使った認証が可能である。

<?php
require_once 'Zend/Db.php';
require_once 'Zend/Auth/Adapter/DbTable.php';

$params = array(
    'host' => 'localhost',
    'username' => 'name',
    'password' => 'pass',
    'dbname' => 'dbname',
);
$dbh = Zend_Db::factory('Pdo_Mysql', $params);

$auth = new Zend_Auth_Adapter_DbTable($dbh, 'user', 'id', 'password');
$auth->setIdentity($userid_from_user_input);
$auth->setCredential($password_from_user_input);
if($auth->isValid()){
    //succeed;
    //$auth->getResultRowObject();
}
else{
    //failed;
}

Zend_Authではファイルシステムを利用した認証や、Basic認証、ダイジェスト認証などを行う事ができる。

■Basic認証

本題とは関係ないが備忘録として書き留めておく。

パスワードファイルの生成

以下のようにしてパスワードファイルを生成する。

sudo htpasswd -c /etc/httpd/.htpasswd admin
ユーザの追加
sudo htpasswd /etc/httpd/.htpasswd taro

.htaccessの記述

AuthUserFile /etc/httpd/.htpasswd
AuthGroupFile /dev/null
AuthName "Input ID and Password, Now!"
AuthType Basic
require valid-user

CONSTRAINT句

以下のようなSQLを実行すると、commentテーブルのカラムtask_idのレコードには、taskテーブルのカラムidに存在するものしか存在できない。この参照整合性制約機能は多用すると少々問題が起こることもある。

CREATE TABLE IF NOT EXISTS `comment` (
    `id` int(11) NOT NULL auto_increment,
    `task_id` int(11) default NULL,
    `description` text,
    `created_at` datetime default NULL,
    PRIMARY KEY  (`id`),
    KEY `comment_FI_1` (`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `task` (
    `id` int(11) NOT NULL auto_increment,
    `title` varchar(255) default NULL,
    `description` text,
    `status_id` int(11) default NULL,
    `created_at` datetime default NULL,
    `updated_at` datetime default NULL,
    PRIMARY KEY  (`id`),
    KEY `task_FI_1` (`status_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
ALTER TABLE `comment`
    ADD CONSTRAINT `comment_FK_1` FOREIGN KEY (`task_id`) REFERENCES `task` (`id`);

■メリットとデメリット

メリット

  • 誤ったデータをテーブルに含めないようにできる

デメリット

  • データ移行時に整合性をチェックするため時間がかかる
  • テーブル設計が複雑になる(運用上の整合性の考慮)

■代替案

アプリケーション側での制御。

JavaScript Complex Class(複素数のクラス)

以下のように複素数のクラスを定義することができる。

function Complex(real, imaginary){
    this.x = real;
    this.y = imaginary;
}
Complex.prototype.magnitude = function(){
    return Math.sqrt(this.x * this.x + this.y * this.y);
}
Complex.prototype.negative = function(){
    return new Complex(-this.x, -this.y);
}
Complex.prototype.add = function(cmp){
    return new Complex(this.x + cmp.x, this.y + cmp.y);
}
Complex.prototype.multiply = function(cmp){
    return new Complex(this.x * cmp.x - (this.y * that.y), this.x * cmp.y + this.y + cmp.x);
}
Complex.prototype.toString = function(){
    return '{' + this.x + ', ' + this.y + '}';
}
Complex.prototype.equals = function(cmp){
    return this.x === cmp.x && this.y === cmp.y;
}
Complex.prototype.valueOf = function(){
    return this.x;
}
Complex.sum = function(a, b){
    if(a instanceof Complex && b instanceof Complex){
        return new Complex(a.x + b.x, a.y + b.y);
    }
    else{
        return new TypeError();
    }
}
Complex.product = function(a, b){
    if(a instanceof Complex && b instanceof Complex){
        return new Complex(a.x * b.x - (a.y * b.y), a.x * b.y + a.y * b.x);
    }
    else{
        return new TypeError();
    }
}
var a = new Complex(1, 5);//1+5i
alert(a);//{1, 5}