伊藤清徳の垂直落下式ムーンサルトプレス

PerlとかPHPとかMySQLとか...がんばっても8割だ。

Category: PHP (page 1 of 5)

CentOS7+PleskOnyxな環境でのはまりごと

CentOS7 + PleskOnyxな環境をはじめて借りてハマったことをメモ

イニシャルではApacheモジュール版PHPがうごいていない

Pleskではすこし前のバージョンから

  • PHPの動作モード
  • PHPのバージョンを選択

できるようになっていますが、PleskOnyxでは、PHPの動作モードが

  • FAST CGI
  • FMP

のみになっており、あれ?Apacheモジュールは???となりました。
サーバーを借りたクララオンラインさんに、サポートをおねがいしたところ
http://faq.clara.jp/index.php?solution_id=1350
をご紹介くださいました。なーるほど。Pleskはもともといろーんなものがモジュール化していますが、
mod_php自体もモジュールなんですね。


CPANモジュールが共通ライブラリディレクトリにインストールできない

sshでログインした際に自動的に環境に

  • PERL5LIB=/ユーザーディレクトリ/perl5/lib/perl5:
  • PERL_MB_OPT=–install_base /ユーザーディレクトリ/perl5
  • PERL_LOCAL_LIB_ROOT=:/ユーザーディレクトリ/perl5
  • PERL_MM_OPT=INSTALL_BASE=/ユーザーディレクトリ/perl5

がセットされる。
これに気づかずに3時間なやんだ。とっとと
perl -V
してれば気づいたのに。。。

これら環境変数を削除した上で、CPANインストールすればよろし。


cgi-binディレクトリが有効になっていない

まぁPerl/CGIとかRuby/CGIとかやんなやって話ですが、昔の枯れた技術というのはとても有用でしてね…
で、イニシャル状態では、公開ディレクトリにcgi-binディレクトリがあって、
そっちが有効になっているんです。昔良くあった構成

/ユーザーディレクトリ/cgi-bin/
/ユーザーディレクトリ/httpdocs/

って言う構成がとれないわけです。
ので、ふるいサイトを移行するとちょっとめんどい。

/ユーザーディレクトリ/httpdocs/cgi-bin/

の中に格納すればいいわけですが。


1個1個はどうってことない話ですけど
勘がない環境だとハマりますね。って言うメモです。

NW.JSのPHP版 nightrain

node.jsでネイティブPCアプリを作ることのできるNW.js(旧Node-Webkit)はご存知の方は多いでしょう。
JavascriptというWEBの言語で、ちょっとしたツールを作ったり、デスクトップガジェットを作ったりできるのは、
WEB寄りのプログラマやエンジニアにとっては、嬉しいツールです。
 
で、そんな話をしていると、大体出てくる要求としては、PHPやPerlやRubyで作れないの?って言う要求ですね。
調べてないですが、多分Rubyはありそうです(文化面で)。PerlはPerl/Tkでがんばりましょうや。
PHPは?って探すと、NW.jsのPHP版とも言うべき、nightrainというのがあります。
 
それの話をば。。。


ディレクトリ構成


アプリケーションパッケージを展開すると、こんなディレクトリ構造になっています。
settings.iniでウインドウ状態の定義や、公開ディレクトリ(でいいのか?)の定義をします。
 
定義ファイルを見ると、Macの場合は、Mac標準で入っているPHPを利用するように定義してあるようです。
ということは、相対パスをつかってパッケージングすれば、バージョン違いのPHPを利用したり、
nightrain専用のphp.iniを持ったPHPを使えるということでしょうか。その辺は、未検証ですが。
 


公開ディレクトリ

公開ディレクトリは「www」内で、とりあえずCodeigniterを入れてみたところ、普通に動きました。
普通のWEBサイトのように動いてくれるようです。
__DIR__などのマジック定数もちゃんと処理してくれますし、composerもいけました。
 
まぁPHPなんでマルチスレッドには対応してなかったりなど、悲しいところは
そのままなんですが、広範なライブラリを持つPHPでちょこーっとアプリを作れるのは、
やはり大きいですねー。
 


ところがですね

こんな便利なnightrainなんですが、開発をやめちゃったようで、
プロジェクトページが閉鎖になっています。
 
どうも公開のリポジトリは残っているようなんですが、ビルドがめんどくせぇ。ということで、
Mac版バイナリが見つかったので、
それを共有するために、この記事を書きました。
 
ダウンロードはこちらから
ライセンスは、MITライセンスです。
 
現場からは以上です。

WordPressに無理やりYii2を入れてみた。

PHPerの悩みどころっていうところで、
WordPressはWordPressのお作法があって、PHPの開発手法とは別だったりとか、
WordPressはMVCでなかったり、とか、スカッフォールディングあったらいいのにとか。
いろいろ悩むのである。
WordPressを拡張したり、プラグインをつくったりというのは、
WordPress使いが思うより、遥かに大変なのである。。。
 


フレームワーク使えたらなぁ

とはいえ、WordPressベースの案件というのはかなり数はある。
さてどうしたものか。
 
僕は普段Yii、Yii2というフレームワークをよく利用する。
http://www.yiiframework.com/
日本ではあまり使われないのだけれども、、
(CakePHPとか、Laravelがよく使われるのかな)
スカッフォールディングが非常に使いやすい、
既存のライブラリに頼らず、しっかりとチューニングしている、
なによりモデルの仕様が非常にわかりやすい。
などの理由でYiiを好んで使っている。
余談だが、日本における有名なYiiの使い手さんは田中ひさてるさんです。
 
閑話休題。
 
WordPressベースの案件にしても、何か簡単にデータベースの管理ができるといいなぁというのが、
PHPerの本音だったりするのでは…。
 
というわけで、どうにかしてみようと思った次第。
その話をしましょう!


Yii1.x系ならプラグインがある!

http://cornernote.github.io/yii-embed-wordpress/
YiiをWordPressのプラグインとしてインストールできるようにしてみようという試みのプラグイン。
うん。すごい。
 
使ってみた。そのまんまではスカッフォールディングなどは使えず。ちょこっと改造が必要。
それについては今度どこかで。
それでもYii1系は非常に見通しが良いフレームワークでPHP5.1.0以降をサポートしている。
この点においては、有用だと思う。
 
ただし、このプラグイン自体がメンテされていないのと、
Yii1.x系も公式には2015年末でサポートを切る予定(セキュリティ面での調整は当面続く)なので
その辺りを十分に認識した上で、利用していく必要がある。
 
やはりYii2でいきたいね。


Yii2でやる!

Yii2でやる!俺はやる!!と決めた。

どうしよう。Yii1のプラグインのようにやっても良いが…簡単ではない…
で、考えた、テーマディレクトリにインストールしたらどうか?
調べたら、codeigniterでやっている人がいた。

なるほど!single.phpなどindex.php以外のテーマファイルがなければ、自動的にindex.phpが、まず呼ばれる。
多くのフレームワークもindex.phpを起点としてルーティングしているので、
そのことを利用すれば、フレームワークをテーマディレクトリにインストールすることはできる!


テーマディレクトリにYii2をインストール

今回は、twentyfifteenテーマをコピーする形で「yii2」というテーマを作っていくことにしましょう。
子テーマで作るなどやり方はいくつかありますが、学習のためということで。
 
下準備として、WordPressをインストール後、
パーマリンク設定もGETパラメータを不要とする設定にしておいてください。
 
まず、wp-content/themes/yii2ディレクトリをつくります。
 
それでその中にYii2をインストールしますが、Yii2はcomposerでインストールします。
WordPressユーザーにはもしかするとcomposerは馴染みがないかもしれません。
composerはPHPのライブラリなどのパッケージをインストール管理をする仕組みです。
最近はアプリケーションのコアをcomposerで提供するなど利用が進んでいます。
Yii2など最近のフレームワークではインストールをcomposerでインストールすることを推奨することが多いので、
覚えておいて損はないでしょう。
 
というわけで、yii2ディレクトリを黒い画面で操作することになります。
 
まずはcomposer自体のインストールを。(Windowsの場合はまた違うの注意)

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

 
つぎにYii2をインストール

cd wp-content/themes/yii2
composer global require "fxp/composer-asset-plugin:~1.0.0"
composer create-project --prefer-dist yiisoft/yii2-app-basic .

 
つぎ、WordPressでは、テーマの構成として

  • index.php
  • style.css
  • functions.php

を最低限必要なファイルとなっています。
index.phpは、YiiによってインストールされているのでOKですから、
style.cssとfunctions.phpをtwentyfifteenフォルダからyii2フォルダにコピーします。
 
Yii2のインストール初期値では、
webという名前のディレクトリを公開ディレクトリして想定していますので、
構造を変更します。
wp-content/themes/yii2/web/
ディレクトリ内の各ファイルを
wp-content/themes/yii2/
ディレクトリへ移動し、
wp-content/themes/yii2/web/
を削除します。
 

これで「インストールの作業」は終了です。
ここからは「設定の作業」です。


style.css

WordPressでは、テーマ名などをstyle.cssの冒頭コメントから取得する仕組みになっていますので、
まずstyle.cssを変更しましょう。

/*
Theme Name: Yii2
Theme URI: http://agilmente.com/
Author: the Kiyonori Ito
Author URI: http://agilmente.com/
Description: Yii2 on WordPress Test!
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Tags: black, 
Text Domain: Yii2

This theme, like WordPress, is licensed under the GPL.
Use it to make something cool, have fun, and share what you've learned with others.
*/

冒頭をこのように自分のテーマのものとして書き換えます。
 
OK!NEXT!


functions.php

とくにやることなし!
OK!NEXT!


index.php

index.phpにはちょっと仕掛けが必要です。

  • yiiの公開ディレクトリを変更したための書き換え
  • WordPressがmagic_quotesを使うことを前提としている

特に後者が厄介で、PHP5.3では非推奨となっていますが、WordPressのお作法としては必須で、
WordPress自前のmagic_quotesがグローバルに影響を与えています。
(この辺が WordPress ≠PHPなわけなんですが)
 
というわけで、これを回避しましょう。

// comment out the following two lines when deployed to production
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');

$config = require(__DIR__ . '/config/web.php');

//wordpressのマジッククォーツを回避
$_GET = stripslashes_deep($_GET);
$_POST = stripslashes_deep($_POST);
//stripslashes_deep($_REQUEST);//リクエスト全体も必要な場合。

(new yii\web\Application($config))->run();

このように書き換えます。

OK!NEXT!


config/web.php

Yii2の設定ファイルを変更します。
 
まず、$config[‘components’]に

        //ショートカットURL
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'enableStrictParsing' => false,
            'rules' => [
                '' => 'wordpress/index',
            ],
        ],

を追加。デフォルトインストール状態では、Yii2はPrettyUrlになっていませんので、これを有効にします。
また、一旦どんなURLだろうとも、wordpressというYii2のコントローラーにいれてしまう設定をします。
これについては後述。
 
次に、同じく$config[‘components’]に

        //アセットの場所を設定
        'assetManager' => [
            'basePath' => get_template_directory() . '/assets/',
            'baseUrl' => get_template_directory_uri() . '/assets/',
        ],

を設定します。
これは、Yiiが認識するwebrootと、実際のYiiのインストールディレクトリにズレがあるのを修正します。
 
次、$config[‘components’][‘request’]に

    'enableCsrfValidation' => false,

を設定します。いまのところまだ研究中なんですが、Yii2のCSRF対策がうまく効かなくなるので、自動化は中止します。
必要であれば自前のヘルパを用意するのがよいでしょう。
 
OK!NEXT!


config/db.php

データベース設定です。これはWPの設定を引き継ぎたいところです。

return [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=' . DB_CHARSET,
    'username' => DB_USER,
    'password' => DB_PASSWORD,
    'charset' => 'utf8',
	'tablePrefix' => $wpdb->prefix
];

このように設定します。WPの定数とプリフィックスを取得して設定すればOK。

OK!これで設定は完了です。
次に必要なのはコントローラーの設置です。


controllers/WordpressController.php

controllersフォルダの中にWordpressController.phpを作成し

namespace app\controllers;
use Yii;

class WordPressController extends \yii\web\Controller
{
    public function actionIndex()
    {
		//ルーティングを書き換える
		$route = $this->checkMethod();
		if( $route !== FALSE )
		{
			return Yii::$app->runAction($route);
		}
		return $this->wp();
    }
	
	// ----------------------------------------------------
	/**
	 * WP用の外出し
	 */
	protected function wp()
	{
		
		if ( is_robots() ) {
			do_action('do_robots');
			return;
		}
		else if ( is_feed() ) {
			do_feed();
			return;
		}
		else if ( is_trackback() ) {
			include(ABSPATH . 'wp-trackback.php');
			return;
		}
		else if ( is_404() ) {
			return $this->renderPartial('404' );
		}
		else if ( is_search() )
		{
			return $this->renderPartial('search' );
		}
		else if ( is_tax() )
		{
			return $this->renderPartial('taxonomy' );
		}
		else if ( is_home() )
		{
			return $this->renderPartial('index' );
		}
		else if ( is_attachment() )
		{
			//remove_filter('the_content', 'prepend_attachment');
			return $this->renderPartial('attachment' );
		}
		else if ( is_single() )
		{
			return $this->renderPartial('single' );
		}
		else if ( is_page() )
		{
			return $this->renderPartial('page' );
		}
		else if ( is_category() )
		{
			return $this->renderPartial('archive' );
		}
		else if ( is_tag() )
		{
			return $this->renderPartial('archive' );
		}
		else if ( is_author() )
		{
			return $this->renderPartial('author' );
		}
		else if ( is_date() )
		{
			return $this->renderPartial('archive' );
		}
		else if ( is_archive() )
		{
			return $this->renderPartial('archive' );
		}
		else if ( is_comments_popup() )
		{
			return $this->renderPartial('comments_popup' );
		}
		else if ( is_paged() )
		{
			return $this->renderPartial('paged' );
		}
		else
		{
			return $this->renderPartial('index' );
		}
	}
	
	// ----------------------------------------------------
	/**
	 * コントローラーメソッドの存在チェック
	 */
	private function checkMethod()
	{
		$pathInfo = explode( '/', Yii::$app->request->pathInfo );
		$controllerList = [];
		while( count($pathInfo) > 0 )
		{
			$controllerList[] = array_shift( $pathInfo );
			$method = $pathInfo[0];
			$controllerListSet = $controllerList;
			$contorollerName = array_pop($controllerListSet);
			$contorollerName = ucfirst( $contorollerName ) . 'Controller';
			
			$controllerFile = 
				Yii::getAlias('@app')
				. '/controllers/'
				. (
					(
						empty($controllerListSet)
					)?'':implode( '/', $controllerListSet ) . '/' 
				  )
				. $contorollerName
				. '.php';
			$method = $pathInfo[0];
			if( $method == '')
			{
				$method = 'index';
			}
			
			if(file_exists($controllerFile))
			{
				$space = 'app\\controllers\\' . $contorollerName;
				$setMethod = 'action' . ucfirst($method);
				$methodExists = method_exists( $space, $setMethod );
				if( $methodExists === true )
				{
					$return = implode( '/', $controllerList ) . '/' . $method;
					return $return;
				}
			}
		}
		return false;
	}
	
	// ----------------------------------------------------

}

と書きます。
ここでは何をやっているかと言いますと、上述のweb.php設定の中で、
いかなるURLであろうとも、一旦wordpressコントローラーのindexアクションを呼ぶように設定しています。
このアクションの中で、WPの投稿であればsingle.phpなどを呼び、そうでなければ、
Yiiのコントローラーアクションを呼ぶという設定をします。
YiiではHMVCというデザインパターンを採用されており、
コントローラアクション内から別のコントローラアクションをコールできるようにしています。
 
そのことを利用して

      URLからYiiのコントローラーアクションがあればHMVCコール
      WPのページならWPのテンプレート

という順番でルーティングを処理するようにしています。
ここのところで少々速度を食われてしまうわけですが…
 
現時点ではGETパラメータをどうするのかという点については、
考慮していません。
ここについてはURL構造の問題ですので、続編をお待ちください。
最低限YiiがWPの中で動くことを想定しています。


ビューファイルをコピー

次に、WPのsingle.phpなどをYiiのビューとしてコピーします。
このとき注意しなくてはならないのは、
single.phpやarchive.phpなどWPから最初にコールされるファイルは
Yiiのビューファイルとして配置。
header.phpなどのテーマファイルの中からコールされるファイルは、
Yiiのルートに置く必要があります。
 
yii2/views/wordpressフォルダを作成し、
その中に

  • 404.php
  • archive.php
  • author-bio.php
  • image.php
  • index.php
  • page.php
  • search.php
  • single.php
  • sidebar.php

をコピー。それ以外のファイルはYiiのindex.phpと並ぶように配置します。
 
よっしゃ次最後!


views/layout/main.php

最後に、Yiiのレイアウトファイルから余分なタグを削ります。
views/layout/main.php

<?php

/* @var $this \yii\web\View */
/* @var $content string */

use yii\helpers\Html;
use yii\bootstrap\Nav;
use yii\bootstrap\NavBar;
use yii\widgets\Breadcrumbs;
use app\assets\AppAsset;

AppAsset::register($this);
?>
<?php $this->beginPage() ?>
    
    
    
    <?= Html::encode($this->title) ?>
    <?php $this->head() ?>
	
	
	<?php $this->beginBody() ?>

isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], ]) ?>
<?php $this->endBody() ?> <?php $this->endPage() ?>

とします。
 
本来であれば、WP側のheader.phpとの兼ね合いでタイトルを設定したりは必要だと思いますが、
まずはこれだけあれば大丈夫でしょうという動きを。
 


以上で、なんとかWPの中でYii2を動かすという流れでした。
この状態で
http://domain/gii
を動かすとYiiのスカッフォールディングが動きます。


 
▲上記のような画面構成になります。
なんとかWPの中でYiiが動くようになりました。
これで複雑な検索を必要とするページを作ったり、
WPではまかない切れない難しい一覧ページを出力したりできます。
WPのデータベース設定を継承するようにつくられているので、
WPのデータベーステーブルに対してモデルファイルをつくっておけば、
WPの複雑なリレーションに対して簡単なデータ呼び出しも可能になるでしょう。
 
次回は、

  • GETパラメータの処理
  • header.phpの最適化
  • WPのログオン情報を継承してYiiのコントローラにアクセス制限

をやってみます。
 


P.S.
上述の通り、ほとんどのWEBアプリケーションフレームワークは
一旦index.phpが呼ばれる前提になっていますので、
Yii以外でもHMVCがサポートされていれば、
同じアプローチができるとおもいます!
 
出てこい!俺より強いやつ!!

[覚書]Yiiで複数データ・ソースを扱う

今日は完全な覚書です。
 
僕はYiiが大好きです。あ、PHPのフレームワークのことです。
(ぼくにとっては)使いやすい。現在はYii2がベータ版で開発中。
giiというGUIでのCRUDジェネレータがあり、
管理画面を作るにはとっても手軽、かつ、高機能に振る舞ってくれる
CRUD画面をつくってくれます。
 


こういったフレームワークにかならずついてくるのがORMやらARやら。
YiiにももちろんARがあります。
CActiveRecordを継承したクラスをつくれば、それで「はい完成」。
(もちろんテーブル名の設定などは必要ですが)
 
ただし、CActiveRecordは、初期値でフレームワークに設定されるDBへの接続を前提につくられており、複数データソースに接続する場合はちょこっと改造が必要です。
 


複数データソースへの接続

'components'=>array(
	'db'=>array(
		'connectionString' => 'mysql:host=host;dbname=databasename',
		'emulatePrepare' => true,
		'enableParamLogging' => true,
		'username' => 'user',
		'password' => 'password',
		'charset' => 'utf8',
	),
)

初期値ではこんな感じの設定がconfigにあります。
1つのデータソースへの接続ですね。
 
これを…

'components'=>array(
	'db'=>array(
		'connectionString' => 'mysql:host=host;dbname=databasename',
		'emulatePrepare' => true,
		'enableParamLogging' => true,
		'username' => 'user',
		'password' => 'password',
		'charset' => 'utf8',
	),
	'db2'=>array(
		'class' => 'CDbConnection',//←これ追加
		'connectionString' => 'mysql:host=host;dbname=database2',
		'emulatePrepare' => true,
		'enableParamLogging' => true,
		'username' => 'user',
		'password' => 'password',
		'charset' => 'utf8',
	),
)

こんな風にすると、2つめのデータソースへは

Yii::app()->db2

でアクセス可能です。おぉ。便利。


ARで利用

class TableModel extends CActiveRecord
{
	/**
	 * テーブル名を指定
	 * @return string the associated database table name
	 */
	public function tableName()
	{
		return 'tablename';
	}
	
}

YiiのActiveRecordは最低限これだけ書けばARクラスをつくることができます。
ただ、前述のとおり、Yiiのアクティブレコードは初期で設定されているYii::app()->dbを前提としていますので、2つめのデータソースのAR作成は、ちょこっと変更が必要。
 
そのやり方が…

class DB2Model extends CActiveRecord
{
	
	/**
	 * データ接続先を設定
	 */
	public function getDbConnection()
	{
		return Yii::app()->db2;
	}
	
	/**
	 * テーブル名
	 * @return string the associated database table name
	 */
	public function tableName()
	{
		return 'sessions';
	}
	
}

こんな感じgetDbConnection()をオーバーロードして、
configで追加した別のデータソースをリターンするだけ。
 
おお。さらに便利。


 
誰得です。現場からは以上です。

YiiでInter-Mediatorを使ってみる

INTER-MediatorというPHPアプリケーションフレームワークがあります。
HTMLからのDBカラム指定でデータが呼び出せたり、
Javascriptの制御でDB操作ができたりなど、なかなか重宝するフレームワークです。

が、必ずしも万能というわけではないのかなというところがあり、
私はSymfonyなどに代表されるMVCフレームワークにINTER-MEdiator(以下、IMと略す)を統合して、
利用しています。

以前はCodeigniterにIMを統合するという記事を書きましたが、今回はYiiに統合をしてみます。


Yii?

PHPのフレームワークといえば?というところで最初にあがってくるようなフレームワークではないですが、柔軟で高速で、かつ、スカッフォールディングなどベースの生成が頭がいいので、気に入って利用しています。他方CakePHPほどの「なんでも感」はないので、PHPerとしての「地力」が試されるフレームワークであると思っています。


早速やってみましょう

これよりさきはMySQLへの接続を前提としてお話をすすめます。

IMのファイルは、yiiのアプリケーションディレクトリ(通常であればprotected)に配置します。

/protected/Inter-Mediator/...

のような感じですね。
特に何かコマンドを打つ必要はありません。


設定ファイルの書き換え

データベースへの接続設定をIMでも使いまわせるようにするために、
初期値ではメイン設定に閉じ込められているデータベース設定を、
別ファイルに移します。

/protected/config/main.phpの

'db'=> array(
			'connectionString' => 'mysql:host=localhost;dbname=dbname',
			'emulatePrepare' => true,
			'username' => 'user',
			'password' => 'pass',
			'charset' => 'utf8',
		), 

の値の部分をカットして、

'db'=> require dirname(__FILE__) . '/database.php'

とします。新規に/protected/config/database.phpを作成して、その中を

<??php
return array(
			'connectionString' => 'mysql:host=localhost;dbname=dbname',
			'emulatePrepare' => true,
			'username' => 'user',
			'password' => 'password',
			'charset' => 'utf8',
		);

とします。
さらに、main.phpのほうには、Yiiのセッション機能を使えるようにするために、加筆をします。
(IMの設定情報をセッションで保持することで柔軟に利用できるようにします)

//DBを外に
		'db'=> require dirname(__FILE__) . '/database.php',
		
		
		//セッションを追加
		'session'=>array(//追加
				'class'=>'CDbHttpSession',//追加
				'sessionTableName'=>'sessions',//追加
				'connectionID'=>'db',//追加
		),

先ほどのDBの設定の部分の下に、セッションを利用可能にしておきます。
そして、下記に説明するヘルパを自動読み込みさせるために、サードパーティーライブラリのフォルダを自動ロードするように設定します。

	'import'=>array(
		'application.models.*',
		'application.components.*',
	),

とします。

	'import'=>array(
		'application.models.*',
		'application.components.*',
		'application.vendor.*',
	),

に書き換えます。
設定ファイルの書き換えは以上。


ヘルパファイルの作成

続いて、IMの設定をラクにするヘルパファイルを作ります。
/protected/vendor/ImHelper.php
を作成し、

<??php
YiiBase::import('application.Inter-Mediator.*');
class ImHelper
{
	//デバッグ
	private static $debug = FALSE;
	
	// ----------------------------------------------------
	
	/**
	 * IMコードのセット
	 */
	public static function setImCode( $im1, $im2 = array(), $im3 = array() )
	{
		//データベース設定をロードしてIMの引数に設定
		$database = require YiiBase::getPathOfAlias('application.config') . '/database.php';
		$im3['db-class'] = 'PDO';
		$im3['dsn'] = $database['connectionString'];
		$im3['user'] = $database['username'];
		$im3['password'] = $database['password'];
		
		
		//一時コードの発行
		$code = sha1( uniqid( '', true ) );
		$data = array(
			'im1' => $im1,
			'im2' => $im2,
			'im3' => $im3,
			'debug' => self::$debug
		);
		Yii::app()->session['im_temporary_code_' . $code] = $data;
		
		return $code;
	}
	
	// ----------------------------------------------------
	
	/**
	 * IMコードの取得
	 * @param type $im 
	 */
	
	public static function getImCode( $code )
	{
		global $callURL;
		$callURL = Yii::app()->createAbsoluteUrl( 'im/get', array('code'=>$code) );
		require_once 'INTER-Mediator.php';
		$params = Yii::app()->session['im_temporary_code_' . $code];
		//echo 'var test = ' . json_encode($params) . ';';
		IM_Entry( $params['im1'], $params['im2'],  $params['im3'], $params['debug'] );
	}
	
	// ----------------------------------------------------
	
	/**
	 * URL作成
	 */
	public static function getUrl( $im1, $im2 = array(), $im3 = array() )
	{
		$code = self::setImCode($im1, $im2, $im3);
		return Yii::app()->createAbsoluteUrl( 'im/get', array('code'=>$code) );
	}
	
	// ----------------------------------------------------
}

と書いて起きます。

YiiBase::import('application.Inter-Mediator.*');

で、IMのフォルダ内をオートロードの対象にしておきます。


IM自体の改造

IM自体の改造が必要です。

まず、INTER-Mediator.phpを改造します。

if (!class_exists('Crypt_RSA')) {
    require_once($currentDir . 'phpseclib' . DIRECTORY_SEPARATOR . 'Crypt' . DIRECTORY_SEPARATOR . 'RSA.php');
}
if (!class_exists('Crypt_Hash')) {
    require_once($currentDir . 'phpseclib' . DIRECTORY_SEPARATOR . 'Crypt' . DIRECTORY_SEPARATOR . 'Hash.php');
}
if (!class_exists('Math_BigInteger')) {
    require_once($currentDir . 'phpseclib' . DIRECTORY_SEPARATOR . 'Math' . DIRECTORY_SEPARATOR . 'BigInteger.php');
}

//Yiiのオートローダーを一旦解除
spl_autoload_unregister(array('YiiBase', 'autoload'));


if (!class_exists('Crypt_RSA')) {
    require_once($currentDir . 'phpseclib' . DIRECTORY_SEPARATOR . 'Crypt' . DIRECTORY_SEPARATOR . 'RSA.php');
}
if (!class_exists('Crypt_Hash')) {
    require_once($currentDir . 'phpseclib' . DIRECTORY_SEPARATOR . 'Crypt' . DIRECTORY_SEPARATOR . 'Hash.php');
}
if (!class_exists('Math_BigInteger')) {
    require_once($currentDir . 'phpseclib' . DIRECTORY_SEPARATOR . 'Math' . DIRECTORY_SEPARATOR . 'BigInteger.php');
}

//Yiiの再登録
spl_autoload_register(array('YiiBase', 'autoload'));

とします。これは、先のImHerlper.phpでIMのディレクトリ内をオートロードの対象にしたわけですが、PHP拡張である「Crypt_RSA」などのクラスの存在拡張をチェックすると、自動的にファイルを探しに知ってしまうのを回避しています。

続いて、GenerateJSCode.phpを改造します。

public function generateInitialJSCode($datasource, $options, $dbspecification, $debug)
{
	$q = '"';

public function generateInitialJSCode($datasource, $options, $dbspecification, $debug)
{
	global $callURL;
	$q = '"';

としておきます。IMのコードを見ると、$callURLがセットされていれば、このURLを優先して利用する仕組みになっているようですので、globalにして、先のImHelper.php内でコールするURLを指定できるように改造しています。

もしかすると、

if (isset($callURL)) {

if ( $callURL != '' ){

にしておいたほうが賢明かもしれません。


IM処理用コントローラーの設置

次にIMの処理を担うコントローラーファイルを設置します。
/protected/controllers/ImController.php
を作成し

<??php
class ImController extends CController
{
	
	// ----------------------------------------------------
	
	/**
	 * アクセスルール
	 */
	public function accessRules()
	{
		return array(
				'actions'=>array(  ),
				'users'=>array('@'),
			),
		);
	}
	
	// ----------------------------------------------------
	
	/**
	 * IM用コード出力
	 */
	public function actionGet( $code )
	{
		echo ImHelper::getImCode($code);
	}
	
	// ----------------------------------------------------
}

と保存しておきます。アクセス状況によってはアクセスルールを適切に設定したほうがよいでしょう。


準備完了

準備ができました。
あとは任意のコントローラーアクション内で、

$ds = array(
			array(
				'name' => 'categories',
				'records' => 20,
				'paging' => true,
			),
		);
		$def = array();
		$ds2 = array();
		
		$url = ImHelper::getUrl(
				$ds,
				$def,
				$ds2
		);

とすれば、$urlにIMの設定を呼び出すための一時URLが発行されるため、
viewの中で表示するなり、clientScriptクラスに登録するなりすればOK

ImHelper::getUrl()の引数は
IM_Entry()の引数に引き当てられます。
DBへの接続設定はImHelperで自動的に割り当てられるので、不要です。


これで、開発高速化!

現場からは以上です。

INTER-Mediatorのバリデーションを構築するクラスをつくったった

さてさて今回もまたまたINTER-Mediatorですよ。
本当にseezooとINTER-Mediatorに感謝です。
 


INTER-Mediatorのバリデーションは

'validation' => array(
	array(
		'filed' => 'filed_nam1',
		'rule'	=> 'value == ""',
		'message' => 'フィールド名1は必須です'
	),
	array(
		'filed' => 'filed_nam1',
		'rule'	=> 'value.match(/[0-9]{3}\-[0-9]{4}/)',
		'message' => 'フィールド名1は郵便番号形式で書いてください。'
	),
),

のように書きます。「rule」の項目がjavascript中でeval()で評価され、
falseが返されれば「message」の項目がalertされるという仕組みで動いています。
実にシンプルな仕組みです。
 


 
シンプルな仕組みですが、毎回javascriptのコードを書くのは面倒っていうのと、
大体バリデーションでやりたいことって決まってますよね。
たとえば何か入力しないとダメとか、数字以外の文字がはいってたらダメとか。
郵便番号形式とか。
 
その辺を予めエラーメッセージと合わせて、
ある程度自動化してくれるPHPクラスを作りました。
(ただバリデーションの内容はいい加減なのと、ライブラリとしては未完成です)
 

<?php
/* =============================================================================
 * INTER-Mediator用バリデーション構築クラス
 * @Author KiyonoriIto
 * ========================================================================== */
class im_validation
{
	// ----------------------------------------------------
	/**
	 * 構築
	 */
	public static function get_validation_def( $rules )
	{
		$return = array();
		
		foreach( $rules as $one )
		{
			foreach( $one['rules'] as $method )
			{
				if( is_callable('self::' . $method)  )
				{
					$return[] = self::$method( $one['filed'], $one['name'] );
				}
			}
		}
		
		return $return;
	}
	
	// ----------------------------------------------------
	// 以下バリデーション定義
	// ----------------------------------------------------
	
	/**
	 * 必須
	 * @param string $field
	 * @param string $name
	 * @return array
	 */
	protected static function required( $field, $name )
	{
		$js = '(
				function(v){
					if(v==\'\'){
						return false;
					}
					else{
						return true;
					}
				})(value)';
		$message =  $name . 'は必須項目です';
		
		
		return array(
			'field' => $field,
			'rule' => $js,
			'message' => $message
		);
	}
	
	// ----------------------------------------------------
	
	/**
	 * 数字かチェック
	 * @param string $field
	 * @param string $name
	 * @return array
	 */
	protected static function numeric( $field, $name )
	{
		$js = '(function(v){
					if( v.match(/[^0-9]/)){
						return false;
					}
					else{
						return true;
					}
				})(value)';
		
		$message =  $name . 'は半角数字で入力してください';
		
		return array(
			'field' => $field,
			'rule' => $js,
			'message' => $message
		);
	}
	
	// ----------------------------------------------------
}

▲はい。超いい加減でしょw

protected static functionで、メソッドを追加すれば、
バリデーションのパターンは増やせます。
多分、JSとPHPが理解できる人なら、
バリデーションメソッドの内容は何をしているかわかるでしょう。
(説明もいい加減でごめんなさい)
 


 
で、この使いかたですが、
INTER-Mediatorの呼び出し定義をしているファイルから、
上記クラスをrequireするなりして、
 

$rules = array(
		array(
			'filed' => 'price',
			'name' => '価格',
			'rules' => array(
				'required',
				'numeric'
			)
		),
	);
$validation = im_validation::get_validation_def($rules);

 
とすると、$validationにINTER-Mediator用のバリデーション定義が返ります。
$rulesでは、
フィールド名、その名前、実行したいバリデーションメソッド名を配列で
それぞれ指定してやればOKです。
 

 
多分役に立つんじゃないかなーと。

INTER-Mediatorのページネーション機能を少し便利に – 1

最近積極的な利用をしているINTER-Mediatorのお話。
 
seezoo cmsの管理画面に統合して、利用しまくっています。
何かアプリケーションをつくるとき、エンドユーザーが見るフロント側はCMSに任せて、
運営側がつかう管理画面をINTER-Mediatorに任せるというやり方で、
工程数をかなり圧縮することができています。
 
seezoo cmsとINTER-Mediatorに感謝です。
 


 
さて、そんなINTER-Mediatorですが、
まったくもって十分といったら、そうでもなくて、
中でもページネーション機能は、もうちょっとどうにかしたいなぁと思うわけです。
 


 
INTER-Mediatorでは

<div id="IM_NAVIGATOR"></div>

というコードをHTML中にかいておけば、
ページめくり機能を実装してくれます。
 
とても便利です。
 
が、このページめくり機能、
まさに「ページめくり」で、本のページをめくるように1ページずつ移動するか、
あるいは、一番最初、一番最後のページにジャンプする機能しかありません。
考え方が一番近いのは「カード型DB」の感じです。
 


 
何かを管理させる機能をつくるとき、
検索結果のページが10ページくらいになることは、
ざらにあることでしょう。
 
そのページを1ページずつめくっていくのでは、
ちょっと面倒。
できれば、数字を入力して、そのページにジャンプできるようにしたほうが、
便利!!!
と思ったわけです。
つまり、
 


ページヘ


 
▲こんなものをページネーション機能の中に入れたいわけです。
移動のボタンを押すことなくonchangeで書き換えがおこるとなお良いわけです。
 


 
さて、実際にコレをつくっていきましょう。
INTER-Mediatorに同梱されている「INTER-Mediator.js」を編集します。
 
が、このファイルはminifyされているので、
そのままでは非常に編集しづらいです。
 
大事なおともだちまいぺーすたんのブログにminifyを元に戻すサイトが紹介されていますので、
これを参考に、minifyを元に戻して、作業を続けましょう。
 


 

node = document.createElement("SPAN");
navigation.appendChild(node);
node.appendChild(document.createTextNode((navLabel == null || navLabel[3] == null) ? ">>" : navLabel[3]));
node.setAttribute("class", "IM_NAV_button" + (start + pageSize >= allCount ? disableClass : ""));
endPageCount = allCount - pageSize;
INTERMediatorLib.addEvent(node, "click", function () {
INTERMediator.startFrom = (endPageCount > 0) ? endPageCount : 0;
INTERMediator.constructMain(true)
});

 
こんなところがあります。
このへんがページング機能を実装しているところで、
上記のコードの後ろに、
 

node = document.createElement("SPAN");
navigation.appendChild(node);
var c_node = document.createElement("INPUT");
c_node.setAttribute("class", 'IM_PAGE_JUMP');
c_node.setAttribute("type", 'text');
c_node.setAttribute("value", ( INTERMediator.startFrom / pageSize ) + 1 );
node.appendChild(c_node);
node.appendChild( document.createTextNode('ページ目へ') );
INTERMediatorLib.addEvent(
c_node,
"change",
function(){
if( this.value < 1){this.value = 1;}
var max_page = Math.ceil( allCount / pageSize );
if( max_page < this.value ){
this.value = max_page;
}
INTERMediator.startFrom = ( ~~this.value - 1 ) * pageSize;
INTERMediator.constructMain(true);
}
);

 
を追加します。
これで、意図したことができます。
 
再度、minifyして完了!
 


 
ここであることに気がつきます。
 

INTERMediator.startFrom = 0;

 
このプロパティが、DBから取得開始するインデックスを指定するプロパティです。
INTER-Mediatorを利用するページにこのプロパティを追加すれば、
意図したページ番号から表示を開始することができます。
 
この値をcookieか何かに保存しておけば、
一度別のページに移動して、戻ってきた時、
さっき見ていたページ番号を保存するということができます。
 


 
おまけ!
 
INTER-Mediatorでは、処理中INTER-Mediatorのロゴが
ページの左上に表示されます。
これを独自のロゴに変更可能です。
 

showProgress: function () {
}

 
というメソッドがあります。
この中にある

c.setAttribute("src", "data:image/gif;base64,xxxx");

が、画像データです。
ここを書換えてやれば、処理中に表示されるロゴデータを書き換えできます。

Inter-MediatorをCodeigniterから使ってみる

INTER-MediatorというPHPアプリケーションフレームワークがあります。
かなり前から存在は知っていたものの、今回はじめて使ってみました。
普通に使うには、私個人としてメリットがなかったので使って来なかったのですが、
他のフレームワークと一緒に使うと便利なんじゃね?
ってことで、Codeigniterで使ってみることに。
 
え?なんでかって?seezoo cmsのコミッターだからさ!
 


そもそも、INTER-Mediatorってなによ?

 
そもそも、INTER-Mediatorってなによ?ってことなんですが、
従来のPHPやPHPフレームワークでは、
SQLやORMをつかってデータベースのレコードを呼んできて、
 

<table>
<?php foreach( $rows as $row ): ?>
<tr> <td><?php $row['field']; ?></td> </tr> <?php endforeach; ?>
</table>

 
のようなコードを書かなくてはなりませんでした。
(PHPTALをつかうともっとスッキリHTMLのポリシーに近くなりますが)
 
Inter-Mediatorの場合、PHPファイルに規定の書き方で、
呼び出すテーブル・1ページあたりのレコード数などを書き入れ、
そのファイルをjavascriptファイルとして、HTMLから呼び出すだけで、
 

<table>
<tr>
<td class="IM[テーブル名@フィールド名"></td>
</tr>
</table>

 
こんなHTMLを書けば、Javascript制御でclassで指定された
テーブル名/フィールド名のデータを表示してくれます。
また、複数レコードを呼ぶ設定にしている場合は、
テーブルの行数もレコード数分追加してくれます。


定義ファイルの中身

INTER-Mediatorの定義ファイルは
 

<?php
require_once ('INTER-Mediator/INTER-Mediator.php');
IM_Entry(
	//呼び出すレコードの定義
	array(
		array(
			'records' => 1,
			'name' => 'person',
			'key' => 'id',
		),
	),
	//オプション設定
	null,
	//DBへの接続設定
	array('db-class' => 'PDO'),
	//デバッグレベルの設定
	false
);

こんな感じです。詳しくは公式サイトのドキュメントをご覧下さい。
IM_Entry();が、HTMLのクラスセレクタを解析して、
DBのレコード/フィールドを差し込むJavascriptコードを出力してくれる
そんな仕組みになっているようです。
 


Codeignireから使うには?

 
上記の様に、INTER-Mediatorの定義ファイルは実に単純なしくみです。
この定義ファイルを出力するコントローラーをつくれば、
ページの出力がかなり簡単になるのでは?
と思ったわけです。
 
気をつけるポイントとして、
INTER-Mediatorでは、PDOでMySQLなどへの接続を行なっているので、
CodeigniterのDBドライバインスタンスをINTER-Mediatorで使うことはできません。
ただ、CodeigniterでつかっているDBの設定はそのまま引き継ぎたい。
そんなわけで、以下のようなコントローラーを作ってみました。
 

<?php
class im_require extends CI_Controller
{
	public function index()
	{
		//IMをrequire
		require FCPATH.'INTER-Mediator/INTER-Mediator.php';
		
		//データベース設定を呼び出す
		require APPPATH . 'config/database.php';
		$dsn = $db[$active_group];
		
		//第三引数
		$dbset = array();
		$dbset['db-class'] = 'PDO';
		$dbset['dsn'] = 'mysql:dbname=' . $dsn['database'] . ';host=' . $dsn['hostname'];
		$dbset['user'] = $dsn['username'];
		$dbset['password'] = $dsn['password'];
		
		
		IM_Entry(
			array(
					array(
						'records' => 1,
						'paging' => true,
						'name' => 'person'
					)
			),
			null,
			$dbset,
			0
		);
	}
}

 
DB設定ファイルを呼び出して、今アクティブなDB設定を
INTER-MediatorのPDO設定に入れているという簡単なしくみです。
 
あとは、どのテーブルを呼び出すかなどを、
GET/POSTのパラメータなどから呼び出せるようにしたり、
YAMLなどで定義できるようにしておけば、
1つのコントローラーで、複数のINTER-Mediatorの定義を作ることができるようになります。
 


INTER-Mediatorの改造

 
さて、定義ファイルを作成するコントローラーができたわけですが、
これだけでは、ちょっとまずいところがありますので、
INTER-Mediator自体の改造が少し必要です。
GenerateJSCode.phpを改造します。
68行目付近に
 

$pathToMySelf = (isset($scriptPathPrefix) ? $scriptPathPrefix : '')
. $_SERVER['SCRIPT_NAME'] . (isset($scriptPathSufix) ? $scriptPathSufix : '');

 
があります。
この状態ですと、定義ファイルの物理的なパスをJavascript中でXHRのURLとして利用してしまうので、
URIルーティングがあるCodeigniterでは、index.phpが呼ばれてしまい、
正しく動作してくれません。
 

$pathToMySelf = (isset($scriptPathPrefix) ? $scriptPathPrefix : '')

			. $_SERVER['SCRIPT_NAME'] . (isset($scriptPathSufix) ? $scriptPathSufix : '');

if(function_exists('get_instance'))
{
	$ci = get_instance();
	$pathToMySelf = (isset($scriptPathPrefix) ? $scriptPathPrefix : '')
		. '/index.php/' . $ci->uri->uri_string()
		. (isset($scriptPathSufix) ? $scriptPathSufix : '');
}

このように書き換えています。
Codeigniterからの呼び出しでない場合も踏まえて、
コントローラーインスタンスが呼べるかどうかで、振り分けをしています。
 
上記のコードでは、GETパラメーターへの考慮がされていないので、
実務レベルで使うには、そのあたりも考慮した改造が更に必要でしょう。
 


viewファイルの記述は?

viewファイルのへの記述は特に気にするような点はなく、
ここまでで作った定義作成コントローラーをjavascriptとして呼び出して、
bodyにonload="INTERMediator.construct(true);"をつければOKです。
 

<script type="text/javascript" src="http://localhost/index.php/im_require"></script>

  

<body onload="INTERMediator.construct(true);">

 
こんな感じですね。
 


seezoo cmsに入れる?

 
seezoo cmsはCodeigniterベースにできているので、
ここまでで書いたコントローラーを突っ込めばうごいてくれますし、
seezoo cmsには管理者/会員承認機能がそろっているので、
このへんもからめると面白いものができそうです。
 
また、Codeigniter自身のModel(DB)機能を使えば、
Inter-Mediartorではカバーできない複雑な処理も行うこともできますから、
「とても便利系」なライブラリやフレームワークにありがちな、
「あーここまではできないかーorz」という手詰まり感も回避することができます。
 


将来展望

実は、私今年のはじめからseezoo cmsベースで似たようなものを作ろうとしていたのですが、
仕様の練り直しなどで進んでいなかったところがあり、
一気に解決した感じです。
 
CodeigniterにはDB構造を操作するメソッドも揃っているので、
テーブル作成やインデックスの付与などを管理画面からできるようにすれば、
オンライン版FileMakerのようなものができるのでは!?
と思っています。
 
もう少しいじってみよう!
 


P.S.

INTER-Mediatorのコードを覗くと、
プロパティが

var $var = '';

のような、ちょっと古い書き方がされているところがあるようです。
この辺りは改善してもらえるとうれしいなー。

知らんうちにスパム配信サーバ扱いにならないために

オンラインCRMを開発していると、
顧客をセグメントして、メール配信なんていうことは結構行われます。
僕の場合、PHPを使って配信することが多いです。
 
数通のメールならいいですが、何百通・何千通のメールを定期送信していると、
大抵、アカウントがなくなったなどの問題で、エラーメールが返って来ます。
こういったメールアドレスに対して、繰り返しメールを送っていると、
そのうち、送信元のサーバーは「スパムメールを送っている」と判定されかねないので、
エラーが返ったメールアドレスに対しては適切に
次回以降送信しないなどの対応が必要です。
 
多くのメールマガジン送信スタンドはそういった機能をもっていますが、
自分でその機能をつくるにはどうしたらいいでしょうか。
という話です。
 
PHPでqdmail(SMTP送信)を利用して送信した場合を想定して書いていきます。


 
何も設定せずにメールを送信する場合、
エラーメールは大抵の場合、
送信メールサーバー、あるいは受信メールサーバーのどちらかのpostmasterアカウントから
「エラーが有りました」という旨のメールが返信されます。

どのメールアドレスに対してエラーがあったのかなどは、
本文中に書かれていることが多いです。
 
正規表現をつかって、本文中のメールアドレスを抜き出すなどすれば
いいかと思うかもしれませんが、
こちらのとおり(Perlの例ですが)
メールアドレスを正規表現で正しく抜き出すというのは、
非常に骨の折れる話です。
 


送信メールサーバーにPostfixが利用されているサーバーなら、
こういった問題を比較的簡単に処理してくれる機能があります。
VERPという機能です。
こちらが簡単な説明書です。
 


仕組みの概要は
 
not_exists@sample.com(送信先メアド、存在しない)
return@sample.com(エラーリターン先メアド、Return-Pathで指定)
 
という条件でメールを送った場合、

return+not_exists=sample.com@sample.com

にバウンスメール(エラーメール)が送信されます。
return@sample.comに対して
ちょうど「Gmail エイリアス」のような形式で送信をしてくれるのです。
(エラーがあったメールアドレスの「@」は「=」に変換されます)
 
この形式で送信してくれれば、
メールトリガーで何かのスクリプトを起動して
「To」をパースすれば、どのメールアドレスにエラーがあったのか、
簡単に計算出来るという仕組みです。
 
これは便利です。


qdmailでの送信の仕方はこちらを参考にしました。
ありがとうございます。

require_once('qdsmtp.php');

$param = array(
'host'=>'smtp.example.com',
'port'=> 25 ,
'from'=>'<from@example.com> XVERP',
'protocol'=>'SMTP',
);
$smtp = new QdSmtp($param);

SMTP接続の際に設定するFROMメールアドレスが、
Return-Pathになるので、
そのメールアドレスに対して「XVERP」オプションを指定すれば、
基本的には動きます。
(SMTP認証が必要な場合は、オプション値が異なるので、
 くわしくはqdmailの設定を見てください)


 
動かない時の注意点が2つ。
 


【その1】
/etc/postfix/mail.cfに設定を追加する

smtpd_authorized_verp_clients = $mynetworks

を追加しなければ動きません。
実質的には、この設定がVERPでのバウンス処理のスイッチになっているようです。
デフォルト値は「no」になっている環境が多いようです。


【その2】
PHP実行環境(つまり送信元クライアント)とSMTPサーバーが
別PC・サーバーの場合
 
この場合、まず1つに、SMTPは認証が必要になる場合が多く、
最近の場合、SMTP認証を行う場合は、
接続ポートがサブミッションポート「587」でないといけないことがあります。
まずは、そちらを確認します。
 
その上で、
/etc/postfix/mail.cf
にさらに設定を加えなければなりません。
 
通常の場合$mynetworksという変数には
「127.0.0.1」つまりローカルのみが指定されます。
その1の設定を見ると、
 

smtpd_authorized_verp_clients = $mynetworks

 
VERPが許可されるクライアントは「$mynetworks」だけですよ。
という設定なので、
つまり、ローカルからの接続のみが許可されていることになります。
 
$mynetworksに接続元を追加してやらなければいけません。
こちらを書くと長くなってしまうので、説明については
この辺りをご参考に。


最近はCMSなどにメール配信機能がついていたりして、
非常に便利ですが、
知らず知らずのうちにスパム配信サーバーとなっていることのないように、
こういった対応はおこなっておくべきでしょう。

seezoo cms をお名前.comのSDで使う話

どうもseezoo cmsの魔改造野郎イトウキヨノリです。


今日はseezoo cmsをお名前.comの共有ホスティングSDで使った時の話です。

インストールに関しては、特に問題がありません。
DBを作ると「xxxxxxxxxx.cgidb」という謎のホストを指定されますが、
恐る恐るインストールしてみたら、
そこは問題なくインストールできました。
 


が、ハマったのが短縮URLです。
http://domain/index.php/hogehoge/

http://domain/hogehoge/
に短縮する奴ですね。


seezooには予め様々な環境でテストして、
最大公約数的に大丈夫であろうという
mod_rewriteの記述を示してくれます。
これを.htaccessに書き入れてindex.phpと同じ場所にアップすれば、
ほとんどのサーバーでは、
正常に「index.php」を除いたURLで動作してくれます。


が、お名前のSDサーバーではどうやらいくつか問題点があります。

RewriteEngine On
RewriteBase [パス]
RewriteCond $1 !^(index\.php|sitemap.xml|sitemap_ssl.xml|css|js|captcha|uploads|templates|blocks|phpMyAdmin|.+\.gif$|.+\.jpg$|.+\.png$|.+\.js$|.+\.css$|.+\.json$|.+\.ico$|.+\.swf$|.+\.flv$)
RewriteRule ^(.*)$ index.php?__SZREQ__=$1 [QSA,L]

共有ホスティングでPHPがCGIモードで動いている場合は
こんな記述を示してくれます。
CGIモードの場合、PHPのグローバル変数$_SERVER[‘PATH_INFO’]が取得できないことがあり、
その代わりにパスの情報をGETパラメータとしてPHPに渡しています。

また[パス]のところには、
seezooが動いているパスを自動で挿入してくれます。

seezooの場合、ほとんどのサーバーで、
この方法で動きます。


が、お名前.comのSDの場合、このままでは動きません。
まず、「[パス]」の部分、ここは、seezooが適切なパスを取得できないようです。
(サーバーマシンのルートからのフルパスが出力されてしまいます)

どうやら、PHPに渡される環境変数がおかしいようです。
したがって、seezoo側ではどうしようもないですから、
手動で書き換えることになります。

ドキュメントルートでseezooを動かすなら、

RewriteEngine On
RewriteBase /
RewriteCond $1 !^(index\.php|sitemap.xml|sitemap_ssl.xml|css|js|captcha|uploads|templates|blocks|phpMyAdmin|.+\.gif$|.+\.jpg$|.+\.png$|.+\.js$|.+\.css$|.+\.json$|.+\.ico$|.+\.swf$|.+\.flv$)
RewriteRule ^(.*)$ index.php?__SZREQ__=$1 [QSA,L]

このような感じですね。


これでもまだ動かない。なぜ…。
どうもパス解決がseezoo内部でできていない。なぜ…

調べる時間がなかったため、原点に立ち返ることに。
むか~し書いた僕の記事を元に
index.phpの冒頭に

if( isset( $_GET['__SZREQ__'] ) ){
    $_SERVER['PATH_INFO'] = $_GET['__SZREQ__'];
}

と書いたら動いた!

あれー。。。seezooにはこのへんの判定スクリプトも入ってるはずなんだけどなぁ。。。


ちなみに同時的に、seezooの根底で動いているフレームワークである、
Codeigniterをインストールする必要があったのですが、
Codeigniterでも同じ問題が。同じ方法で解決。
どうやらお名前.comのSDはクセモンのようです。

SDでCakeの短縮URLが動かない旨のブログを散見しましたが、
おそらく同じような問題だと思われます。
テストは割愛しますが、Cake使いの方も参考になれば。

Older posts