Fatal Error: Unexpected BLOG

主に自分用の備忘録として

【CentOS】vsFTPdでFTPSかつchrootなFTPサーバを構築する

諸々の事情が絡み合って、外部サーバからFTP接続してファイルをアップロードしなければならない状況になったので、
「SFTPでいいじゃん。」と思って立ててなかったFTPサーバを立てることにしました。

但し平文FTPではやはりアレなので、FTPSを有効にします。
※訳あってSFTPは使えません。

以下、今回の環境です。

  • さくらのVPS
  • CentOS 6.6
  • vsftpd-3.0.3
  • FTPS有効
  • PASV有効

せっかくなのでvsftpd-3.0.3をソースからコンパイルします。
SSL用の秘密鍵と証明書は別件でオレオレのものを生成済みです。


※以下各作業を行う場合は自己責任でお願いします。


なお、以下のサイトを参考にさせて頂きました。
この場を借りて御礼申し上げます。



では、やっていきましょう。

ルート権限でログイン

リモートからのルートログインを制限している場合もあると思いますが、

$ sudo sh

でルートのパスワードを入力すれば、以降ルートのシェル環境で作業できます。

元々入っているvsftpdをアンインストール

入っていれば。

# yum remove vsftpd

ちなみに私はビビリなので、常に「-y」は付けません。

ソースを取得・展開

ソースを展開するディレクトリに移動してwgetで取得、tarで展開します。
今回はhttps://security.appspot.com/downloads/vsftpd-3.0.3.tar.gzから取得できました。

# cd /usr/local/src
# wget https://security.appspot.com/downloads/vsftpd-3.0.3.tar.gz
# tar zxvf vsftpd-3.3.3.tar.gz

展開して出来たディレクトリを丸ごとchownしておきます。

# chown -R root:root vsftpd-3.0.3


インストール先を作成

今回は一か所にまとめたかったので/opt/配下に専用ディレクトリを作成しました。

# mkdir /opt/vsftpd
# mkdir /opt/vsftpd/bin
# mkdir /opt/vsftpd/conf
# mkdir /opt/vsftpd/log
# mkdir /opt/vsftpd/work


builddefs.hを編集

コンパイル時の設定ファイルを編集してSSLtcp_wrappersを有効にします。

# cd ./vsftpd-3.0.3
# vi ./builddefs.h

【builddefs.h】

#ifndef VSF_BUILDDEFS_H
#define VSF_BUILDDEFS_H

#define VSF_BUILD_TCPWRAPPERS ←変更
#define VSF_BUILD_PAM
#define VSF_BUILD_SSL ←変更

#endif


nobodyユーザがいるか

vsftpd-3.0.3ではnobodyユーザが必要とのことで、いなければ作っておきます。

/usr/share/emptyディレクトリがあるか

こちらも必要なようなので、無ければ作成。
ちなみに今回はどちらもデフォルトで有りました。

依存パッケージのインストール

ここまで来て今更ですが、vsftpdのインストールに必要なパッケージを予め入れておきます。

  • make
  • gcc
  • tcp_wrappers-devel.x86_64
  • libcap-devel
  • openssl.x86_64 1.0.1e-48.el6_8.3
  • openssl-devel.x86_64

opensslですが、私の場合既に入っていたものではダメでした。makeが失敗します。
yumでアップデートして比較的新しいものにして下さい。(セキュリティを考えれば当然最新のものが良いです)
なお、CentOSyumでインストールした場合、バージョン番号はアップストリームとは一致しません。
以下のコマンドで更新履歴が見れるので、そちらでチェックします。

# rpm -q --changelog openssl

今回は、履歴の最も新しい日付が「2016/9/22」だったので、まぁいいでしょう。

make実行

現在地は/usr/local/share/vsftpd-3.0.3 です

# make

make install はやりません。インストール先を/opt/vsftp配下に集約するためです。

必要なファイルをインストール用ディレクトリにcp

makeが成功したら実行バイナリと設定ファイル、マニュアルをインストール用ディレクトリにコピーします。

# cp -p ./vsftpd /opt/vsftpd/bin
# cp -p ./vsftpd.conf /opt/vsftpd/conf
# cp -p ./vsftpd.8 /opt/vsftpd/conf
# cp -p ./vsftpd.conf.5 /opt/vsftpd/conf


ファイアウォールの設定

FTP接続用のポートを開けておきます。

# vi /etc/sysconfig/iptables

iptables

...(省略)...
-A INPUT -p tcp -m tcp --dport 20 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 21 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 50021 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 50040 -j ACCEPT
...(省略)...

下2つはPASVモード用です。

iptablesにモジュールをロードさせる

iptables関連でもう一つ。iptablesの設定ファイルを編集してFTP用のモジュールをロードさせます。

# vi /etc/sysconfig/iptables-config

iptables-config】

...(省略)...
IPTABLES_MODULES="ip_conntrack_netbios_ns ip_conntrack_ftp ip_nat_ftp"
...(省略)...



書き終えたらiptablesを再起動しておきます。

# service iptables restart


vsftpdの設定を編集

vsftpdの設定を変更します。

# vi /opt/vsftpd/conf/vsftpd.conf

【vsftpd.conf】

anonymous_enable=NO  #匿名アクセスを拒否
local_enable=YES     #ローカルユーザのログイン許可
write_enable=YES     #ローカルユーザの書込許可
local_mask=022       #デフォルトのパーミションを755に

...(省略)...

xferlog_file=/opt/vsftpd/log/vsftpd.log  #ログファイル
xferlog_std_format=YES                   #ログファイルフォーマット

...(省略)...

ascii_upload_enable=YES    #ASCIIモードでのアップロードを許可
ascii_download_enable=YES  #ASCIIモードでのダウンロードを許可

...(省略)...

chroot_local_user=YES                                 #ローカルユーザをchroot(※1)
chroot_list_enable=YES                                #chroot_listを有効
chroot_list_file=/opt/vsftpd/conf/vsftpd.chroot_list  #↑がYESなので、これはchrootされないユーザのリスト

...(省略)...

listen=YES

...(以下追加)...

# Enable writing under chroot
allow_writeable_chroot=YES  #chroot下にあるユーザの書込を許可

# About SSL
ssl_enable=YES                        #SSL有効
rsa_cert_file=/path/to/cert/file.pem  #サーバ証明書(pemファイル)のパス(※2)
ssl_sslv2=NO                          #SSLv2は無効
ssl_sslv3=NO                          #SSLv3も無効
ssl_tlsv1=YES                         #TLSv1を有効
force_local_data_ssl=YES              #SSLを強制
force_local_logins_ssl=YES            #SSLを強制

# About PASV
pasv_address=your.host.name  #PASVモードでアクセスするホスト名(グローバルIPでも可)
pasv_enable=YES              #PASVモード有効
pasv_min_port=50021          #PASVモードで使用するポート番号の最小値
pasv_max_port=50040          #PASVモードで使用するポート番号の最大値
※1

chroot_local_userの値によって、chroot_list_fileで指定したファイルの扱いが変わります。
chroot_local_user=YESの場合、chrootされないユーザのリストになり、
chroot_local_user=NOの場合、chrootされるユーザのリストになります。

※2

証明書のみ、または秘密鍵のみのファイルではNGです。
証明書と秘密鍵が併記されたpemファイルが必要です。無ければ作成して下さい。
※それぞれのファイルの生成は割愛します。

# (cat /path/to/cert/file.crt ; cat /path/to/key/file.key) > /path/to/cert/file.pem

chroot_listを作成しておく

今回の設定ではchroot_listはchrootされないユーザのリストです。
FTPで接続するユーザは全てchrootしておきたいので、今回は空にしておきました。

# echo -n > /opt/vsftpd/conf/vsftpd.chroot_list


起動スクリプトの設置

ソースからコンパイルしたので、起動スクリプトも作成しなければなりません。
これについては、前述の参考サイト様を大いに参考にさせて頂きました。というかほぼまんまです。

# vi /etc/init.d/vsftpd

【/etc/init.d/vsftpd】

#!/bin/bash
#
# vsftpd    This shell script takes care of starting and stopping
#           standalone vsftpd.
#
# chkconfig: 2345 60 50
# description: Vsftpd is a ftp deamon, which is the program \
#              that answers incoming ftp service requests.
# processname: vsftpd
# config: /opt/vsftpd/conf/vsftpd.conf

# Source function library.
. /etc/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ "${NETWORKING}" = "no" ] && exit 0
# Check that the binary is excutable.
[ -x /opt/vsftpd/bin/vsftpd ] || exit 0

RETVAL=0
prog="vsftpd"

start() {
    # Start deamons.
    if [ -d /opt/vsftpd ]
    then
        for i in `ls /opt/vsftpd/conf/*.conf`
        do
            site=`basename $i .conf`
            echo -n $"Starting $prog for $site: "
            /opt/vsftpd/bin/vsftpd $i &
            RETVAL=$?
            [ $RETVAL -eq 0 ] && {
                touch /var/lock/subsys/$prog
                success $"$prog $site"
            }
            echo
        done
    else
        RETVAL=1
    fi
    return $RETVAL
}

stop() {
    # Stop daemons.
    echo -n $"Shutting down $prog: "
    killproc $prog
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
    return $RETVAL
}

# See how we were called.
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart|reload)
        stop
        start
        RETVAL=$?
        ;;
    condrestart)
        if [ -f /var/lock/subsys/$prog ]
        then
            stop
            start
            RETVAL=$?
        fi
        ;;
    status)
        status $prog
        RETVAL=$?
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart|condrestart|status}"
        exit 1
esac

exit $RETVAL

※上記はあくまで参考として、コピペでそのまま使用することは避けて下さい。環境によっては動作しないかもしれないので。
※繰り返しますが設定は自己責任でお願いします。

自動起動設定

サーバ起動時に自動で起動するようchkconfigに追加します。

# chkconfig --add vsftpd


確認

以下のコマンドで起動、停止、状態確認が出来ます。

# service vsftpd start
# service vsftpd stop
# service vsftpd restart
# service vsftpd status



クライアントから接続してみてFTPS接続できたら成功です。
お疲れ様でした。


おまけ

今回、クライアントからPASVモードでファイル一覧が取得できない事象が発生しまして、結局はiptablesの設定だったんですが、
その際、クライアントからどのポートにアクセスしているか以下の情報から分かるということを知りました。

#サーバのレスポンス
227 Entering Passive Mode (xxx,xxx,xxx,xxx,nnn,mmm).

上記の「xxx.xxx.xxx.xxx」はサーバのグローバルIPですが、
後半の「nnn,mmm」の部分。
これは、

256 * nnn + mmm = ポート番号

ということだそうです。
己の無知を噛み締めた次第。忘れないようにメモ。

【EC-CUBE】複数の商品を大量に購入すると、購入データが途切れる現象

複数の商品を大量にカートに入れると、その後の購入フローや管理画面から注文データを確認した際に、配送情報や送料などのデータが消失してしまう事象がありました。
危うくハマりかけていたところ、弊社のウィザードから天の声を頂きましたので、忘れないようにメモ。

環境は以下の通り

以下のサイトを参考にさせて頂きました。

発生した事象

一般的に通常想定しうる商品数で購入すると正常に動作し、データも完全な状態で保存されていましたが、
以下のような場合にデータが一部消失するという事象が発生しました。

  • 複数の商品をカートへ保存(個数ではなく、商品IDが複数ということ)
  • それぞれの商品データ(商品名や説明文など)がそこそこの長さを持っている
  • 注文者情報や配送先情報も比較的長め

実際にデータの消失を確認出来たものは、送料、配送先情報(名前、住所など全て)です。

解決方法

テーブルのカラムのデータ型を修正することで解決出来ました。

ALTER TABLE dtb_order_temp MODIFY session LONGTEXT DEFAULT NULL;
ALTER TABLE dtb_session MODIFY sess_data LONGTEXT DEFAULT NULL;

デフォルトではTEXT型になっているので、これをLONGTEXT型に変更します。
dtb_sessionの方についてはもしかしたら必要ないかもしれません。

原因

注文情報を一時的に保存するためのdtb_order_tempテーブルには、セッション情報をJSONの状態で保存するフィールドがあり、
TEXT型で定義されています。
TEXT型の最大長は65,535バイトとのことですが、この上限を超えるデータを登録しようとすると、切り詰められて登録されます。
大抵の場合はこの最大長に収まるようですが、超えた場合に不完全な状態のJSONが保存され、以降の処理で参照されることになります。
今回確認した事象では、送料や配送先情報の部分が切り詰められてしまっていたということでしょう。

おわり

LONGTEXT型に変更後は、同様の注文内容でも問題なく処理できていました。
ただ、LONGTEXT型は最大長が4,294,967,295(約4G)バイトとのことなので、これはさすがに大げさかもしれません。。。
ちなみに、一段階小さいMEDIUMTEXT型は最大長16,777,215(約16M)バイトだそうで、こちらで十分かもしれませんね。

【EC-CUBE】商品規格を設定している商品の在庫を、規格ではなく商品単位で動くようにする

タイトルがちょっと分かりにくいですが、まとめると以下の様な感じです。

  • 商品Aに規格設定を使って「ラッピング有無」を設定
  • 商品Aの規格画面で、それぞれの規格に在庫「10」を設定

  商品A「ラッピング有り」→ 10
  商品A「ラッピング無し」→ 10

  • 商品A「ラッピング有り」を1個受注
  • 商品Aの全ての規格の在庫が -1される

  商品A「ラッピング有り」→ 9
  商品A「ラッピング無し」→ 9


在庫の増減は、デフォルトだと、product_class_id毎に計算されているので、これを
product_id毎に増減するようにします。


環境は以下の通り


以下のサイトを参考にさせて頂きました。

SC_Product.php

在庫の増減はSC_product.phpに記述されていますので、以下のように変更します。
data/class/SC_Product.php の536行目あたり

<?php
public function reduceStock($productClassId, $quantity)
    {
        if ($quantity == 0) {
            return false;
        }

        $objQuery =& SC_Query_Ex::getSingletonInstance();

        // $productClassIdから、該当するproduct_idを取得
        $arrRet = $objQuery->select('product_id', 'dtb_products_class', 'product_class_id = ?', array($productClassId));
        if (empty($arrRet)) return false;
        $product_id = $arrRet[0]['product_id'];

        // product_idに紐づく全てのproduct_class_idに対して、在庫のUPDATEを実行
        $objQuery->update('dtb_products_class', array(),
                          'product_id = ?', array($product_id),
                          array('stock' => 'stock - ?'), array($quantity));
        // TODO エラーハンドリング

        $productsClass = $this->getDetailAndProductsClass($productClassId);
        if ($productsClass['stock_unlimited'] != '1' && $productsClass['stock'] < 0) {
            return false;
        }

        return true;
    }

変更はこれだけ。
ただし、product_idに紐づく全ての規格の在庫が連動してしまうので、一つでも在庫を個別に管理したい規格がある場合は改修が必要です。

【Arduino】予備知識ゼロからArduinoをはじめてみる

前々から気にはなっていたのですが。
何はともあれやってみようということで、uno版を購入。

とりあえず本家サイトのGetting Started通りに進めてみようと思います。
ちなみにPCはWindows8.1です。

f:id:al_lucky:20151227004257j:plain:w300

IDEをインストール

何はともあれ、Arduino用にIDEをインストールします。
Windows向けにはインストーラが提供されているので、普通にインストールすればOK。

本体とPCを接続

USBケーブルでArduinoとPCを接続します。
繫ぐとこんな感じ。

f:id:al_lucky:20151227012424j:plain:w300

アガるw

本家にはこのあとドライバを更新しろって書いてあるけど、
更新しようとしたら「最新のドライバがインストールされています」との事で何も起きず。
ちなみに通常通りにインストールした場合、該当のドライバは以下のパスにあります。
※PC環境によっては違うこともあるかも。

C:\Program Files (x86)\Arduino\drivers

Arduinoアプリケーションを起動

先ほどインストールしたArduinoアプリケーションを起動します。
で、ボードとポートを指定する必要があるとのこと。

ボードの指定

ツールマイコンボード>Arduino/Genuino Uno

ポートの指定

シリアルポート>COM3(Arduino/Genuino Uno)

サンプルスケッチのアップロード

ファイル>スケッチの例>01.Basics>Blink を開きます。
開いたら、「マイコンボードへ書き込む」をクリック。
コンソール部分にメモリ関連のメッセージがツラツラと出たあと、ステータスバーに「書き込みが完了しました。」と出ます。
Arduino本体の13ピンのLED(横にLって書いてあるとこ)が点滅していれば成功です。

f:id:al_lucky:20151227022531j:plain:w300

まとめ

どうやら
IDEでスケッチを作成→Arduinoへアップロード
という流れのよう。
フムフム。

【PHP】DOMDocument::loadHTML()で日本語が化けるときの対処法と残った謎

ファイルから読み込んだ断片的なHTML文書をDOMDocumentクラスを使って解析しようとした時に文字化けに遭遇した。
とりあえず文字化けは回避出来たけど、原因とか根本的な解決とか謎のままなのでメモ。

以下のサイトを参考にさせて頂きました。

環境は以下

loadHTML()したドキュメントをsaveHTML()すると化ける

読み込んでいるデータも出力先もソースファイルもエンコードUTF-8
loadHTML()するデータは断片的なものなので<head>が含まれていない。(ワケあってここは制御できない)
そのあたりも関係していそう。
現象としては、HTMLエンティティがそのまま文字列として出てきちゃってる感じ?
出力をブラウザで表示しても、ファイルに保存しても日本語部分が化ける。

対処法

loadHTML()とsaveHTML()の直前でmb_convert_encoding()してあげたら化けなかった。

<?php
$stringHTML = "<p>HTMLドキュメント</p>"; //なにかしらのHTML文字列
$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding($stringHTML, 'HTML-ENTITIES', 'utf-8'));

$out = mb_convert_encoding($dom->saveHTML(), 'utf-8', 'HTML-ENTITIES');
echo $out;

'HTML-ENTITIES'でエンコーディングするっていう発想がなかったし、見落としかもしれないけど公式にも記述は無い。

一応文字化けは回避できたけど、根本的なところが理解できていないので、忘れないようにメモ。

余談

DOMDocumentの公式マニュアル見ると、loadHTML()はスタティックにコールできるって書いてあるけど、
下の方にしれっと「コールすることは出来るけど、E_STRICT エラーが発生します。」って書いてあるので注意。

【EC-CUBE】Googleアナリティクスのコンバージョンタグを設置する

タイトル通り、GoogleアナリティクスのコンバージョンタグをEC-CUBEの購入完了画面へ設置する方法です。
以下のサイトを参考にさせて頂きました。

EC-CUBEのバージョンは2.13です。

デフォルトだと注文情報は完了画面でunsetされてる

LC_Page_Shopping_Complete.phpの51行目あたり

<?php
    public function process()
    {
        parent::process();
        $this->action();
        $this->sendResponse();
        // プラグインなどで order_id を取得する場合があるため,  ここで unset する
        unset($_SESSION['order_id']);
    }

このままだと注文情報をテンプレートへ渡せず、コンバージョンタグが設定出来ないので、変更します。

コンバージョンタグに必要な情報をテンプレートへ渡せるようにする

コンバージョンの測定には注文情報が必要なので、unset前にページオブジェクトのフィールドへ格納します。
process()メソッド内でorder_idをunsetする前にaction()メソッドが走るので、そちらでデータを取得します。

<?php
    public function action()
    {
        $this->arrInfo = SC_Helper_DB_Ex::sfGetBasisData();

        //コンバージョンタグ対応用
        if (is_numeric($_SESSION['order_id'])) {
            $objPurchase = new SC_Helper_Purchase_Ex();
            $this->arrOrder = $objPurchase->getOrder($_SESSION['order_id']);
            $this->arrOrderDetail = $objPurchase->getOrderDetail($_SESSION['order_id']);
        }
    }

これでテンプレート側で$arrOrder及び$arrOrderDetailへアクセスすれば注文情報を取得出来ます。

テンプレートへコンバージョンタグを設置

上記でテンプレートへ渡した注文情報をテンプレート側でコンバージョンタグ内へ展開します。
site_frame.tplの足元へ、ページクラス名による条件分岐で設置します。

    <!--GA-->
    <script>
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
        ga('create', 'UA-XXXXXXXX-X', 'auto');
        ga('require', 'ecommerce', 'ecommerce.js');
        ga('send', 'pageview');
    </script>
    <!--//GA-->
    <!--{* Google conversion tag *}-->
    <!--{if $tpl_page_class_name eq "LC_Page_Shopping_Complete" && count($arrOrder) > 0 && count($arrOrderDetail) > 0}-->
    <script>
        ga('ecommerce:addTransaction', {
            id: '<!--{$arrOrder.order_id|h}-->', // Transaction ID - this is normally generated by your system.
            affiliation: '<!--{$arrSiteInfo.shop_name}-->', // Affiliation or store name
            revenue: '<!--{$arrOrder.payment_total|h}-->', // Grand Total
            shipping: '<!--{$arrOrder.deliv_fee|h}-->' , // Shipping cost
            tax: '<!--{$arrOrder.tax|h}-->' // Tax.
        });
        <!--{foreach from=$arrOrderDetail item=orderDetail name=order_detail}-->
        ga('ecommerce:addItem', {
            id: '<!--{$arrOrder.order_id|h}-->', // Transaction ID.
            sku: '<!--{$orderDetail.product_code|h}-->', // SKU/code.
            name: '<!--{$orderDetail.product_name|h}-->', // Product name.
            price: '<!--{$orderDetail.price|h}-->', // Unit price.
            quantity: '<!--{$orderDetail.quantity|h}-->' // Quantity.
        });
        <!--{/foreach}-->
        ga('ecommerce:send');
    </script>
    <!--{/if}-->
    <!--{* //Google conversion tag *}-->

これでいいはず。

【EC-CUBE】動的ブロックを追加する

EC-CUBEに動的ブロックを追加する方法です。
以下のサイトを参考にしました。

環境

管理画面からブロックを追加

まずは通常通り、EC-CUBEの管理画面からブロックを追加します。
もはや説明不要ですね。

DBに情報を追加

管理画面からブロックを追加すると、データベースにレコードが追加されますが、そのままだとただ内容を表示するだけの静的なブロックとしてしか機能しません。
ので、該当のレコードに値を追加します。

UPDATE `dtb_bloc` SET `php_path` = 'frontparts/bloc/new_bloc.php';

ブロックが呼び出された時に実行されるPHPファイルを`php_path`に指定します。
パスは各々の環境に合わせて読み替えて下さい。

ファイルを設置

当たり前ですが、上記で追加したPHPファイルのパスにPHPファイルを作成しておきます。
同じディレクトリにある別のブロックのものをコピーすると楽ちんです。
大体以下の感じになると思います。

<?php
require_once realpath(dirname(__FILE__)) . '/../../require.php';
require_once CLASS_EX_REALDIR . 'page_extends/frontparts/bloc/LC_Page_FrontParts_Bloc_NewBloc_Ex.php';

$objPage = new LC_Page_FrontParts_Bloc_NewBloc_Ex();
$objPage->blocItems = $params['items'];
$objPage->init();
$objPage->process();

「NewBloc」の部分は作成するブロックの名前にしておくと良いでしょう。

クラス設置

上記のPHPでrequireしているクラスを実際に作成します。
EC-CUBEのお作法としてクラスとそれを継承するクラスを作成します。
継承先のクラスで何も変更しないとしても、一応両方作ります。
今回の場合は以下の2つのファイルを設置します。

/data/class/pages/frontparts/bloc/LC_Page_FrontParts_Bloc_NewBloc.php
/data/class_extends/page_extends/frontparts/bloc/LC_Page_FrontParts_Bloc_NewBloc_Ex.php

こちらも同じディレクトリにある別のブロックのものをコピーして作成します。

継承クラス実装

extendsの方はほとんどいじらないので先にやっつけちゃいます。

<?php
require_once CLASS_REALDIR . 'pages/frontparts/bloc/LC_Page_FrontParts_Bloc_NewBloc.php';

class LC_Page_FrontParts_Bloc_NewBloc_Ex extends LC_Page_FrontParts_Bloc_NewBloc
{
    /**
     * Page を初期化する.
     *
     * @return void
     */
    function init()
    {
        parent::init();
    }

    /**
     * Page のプロセス.
     *
     * @return void
     */
    function process()
    {
        parent::process();
    }
}

ファイル名とクラス名を変えただけです。

クラス作成

こちらが実装の本番。

<?php
require_once CLASS_EX_REALDIR . 'page_extends/frontparts/bloc/LC_Page_FrontParts_Bloc_Ex.php';

class LC_Page_FrontParts_Bloc_NewBloc extends LC_Page_FrontParts_Bloc_Ex
{
    public function init()
    {
        parent::init();
    }

    public function process()
    {
        $this->action();
        $this->sendResponse();
    }

    public function action()
    {

        //ここにメインの処理を書く

    }

このクラスでの処理結果をテンプレートの方へ渡す場合はaction()メソッド内で以下のようにします。

<?php
    public function action()
    {
        ....
        $var = /*なにがしかの処理結果*/;
        $this->result = $var;
    }

こうしておくとtplファイルの方で同名の変数に$varが格納された状態になります。

<!--{$result}-->

↑これでアクセス出来ます。

以上。