読者です 読者をやめる 読者になる 読者になる

技術メモ

業務で調べたこととか...

smarty(FuelPHP)でphpタグを利用する

やりたいこと

キャッシュを有効にしつつ、cssやjsファイルの変更を確実に反映させる – doop


クラスView_Smarty(fuel/packages/parser/classes/view/smarty.php)のメソッドparserの下記1行

<?phpclass View_Smarty extends \View
{public static function parser()
   {static::$_parser = new Smarty(); // !!!

を下記2行に置き換える

<?phpstatic::$_parser = new \SmartyBC();
   static::$_parser->php_handling = \Smarty::PHP_ALLOW;


これで、smartyファイルの中でphpタグが使える

<script type="text/javascript" src="/assets/js/app/sample.js?date={php} echo date('YmdHis', filemtime('./assets/js/app/sample.js')); {/php}"></script>


参考
Smarty 3.1.13 のテンプレート内で生php実行 - Qiita

スクロール追尾ボタンをつくる

スクロール追尾ボタンをつくりたい

そこで...sticky
https://github.com/garand/sticky

When the target element is about to be hidden, the plugin will add the class className to it (and to a wrapper added as its parent), set it to position: fixed and calculate its new top, based on the element's height, the page height and the topSpacing and bottomSpacing options.


結果、こんな感じで

<script src="/assets/plugins/jquery/jquery.min.js"></script>
<script src="/assets/plugins/garand-sticky/jquery.sticky.js"></script>
<a href="javascript:void(0)" id="sticky_button">ボタン</a>
$(document).ready(function() {
	$("#sticky_button").sticky({ 
		zIndex: 1000, // 重なってしまわぬよう 
		center: true
	});
});


ちなみにボタンはcss
CSSだけで作るアイコン付きボタンの作り方 : アシアルブログ

少し修正して利用
例えば、以下の機能はブラウザによって利用できないので...

background-image: -webkit-linear-gradient(top, #FC0, #F60);

こうするとか

background: linear-gradient(#1D62F0, #05FBFF);


こんなものもあるのか...
CSSのposition:stickyがすごく便利 | q-Az

FuelでAjaxを使う

Ajaxでリクエストを投げて、FuelPHPのコントローラで処理する。

フロント

var res_json = $.ajax({
	type          : 'post',
	url           : '/result/fetch.json',
	data          : JSON.stringify(post_data), // POSTするJSONデータ
	contentType   : 'application/json',
	dataType      : 'json',
	processData   : false,
	async         : false,
	cache         : false,
	scriptCharset : 'utf-8',
	// 成功
	success: function(json_data) {
		return json_data;
	},
	// 失敗
	error: function() {
	},
	// 完了時
	complete: function() {
	}
}).responseText;

var res_obj = $.parseJSON(res_json);
var result = res_obj.result;


戻り値を参照する場合は、$.ajax()の戻り値であるXMLHttpRequestオブジェクトを変換する必要がある。
responseTextメソッドで文字列に変換して、$.parseJSONでパースする運びとなる。

Ajaxについては下記を確認する。
jQuery.ajax(options) - jQuery 日本語リファレンス

サーバ

<?php

/**
 * WebAPIとして機能する
 *
 */
class Controller_Result extends Controller_Rest
{
   /**
    * /result/fetch.json
    *
    */
    public function post_fetch()
    {
        $res = self::fetch_response();

        return $this->response($res, 200);
    }

    private static function fetch_response()
    {
	// レスポンスを取得するための何かしら
	Input::json();
	...
    }
}


REST形式でJSON/XMLデータを返したい場合は、RestControllerを使う。
fuel/app/classes/controllerディレクトリ内にクラスを作成し、Controller_Rest クラスを継承する。

リクエストURLの末尾によって、コントローラが返すデータが自動整形される。
例えば、/result/fetch.jsonの場合はJSONデータが返される。

RestControllerについては下記を確認する。

プロフェッショナルプログラマー: Fuel PHP で Ajax
jQueryでJSONをPOSTしてFuelPHPで受け取る — A Day in Serenity (Reloaded) — PHP, FuelPHP, Linux or something
FuelPHP の Rest コントローラの使い方とデフォルトフォーマットの変更の仕方 - Qiita

パイプで並列処理

はじめに

Software Design 2012年8月号」の「開眼シェルスクリプト」の焼き直し。
Webにあるので一読することを勧める。
開眼シェルスクリプト2012年8月号 – 上田ブログ

パイプ

パイプにつなげたコマンドは並列稼働する(複数のCPUをなるたけ休まず働かせようとする)。
複数のCPUが、パイプで連結された各コマンドの動作/停止を高速に切り替えることで、
加工データをバケツリレー形式で前から後ろへ送っていく。
CPUの数に応じて効果の度合いは変動するものの、コマンドを細かく分割するほうが、時間当たりの処理量が増える。

並列化されていることは、以下のようにして確認できる。

  • timeコマンドの結果、「user > real」となっている(1個のCPUでは「実際に掛かった待ち時間」よりもCPU時間の方が長くなることはない、基本的に「user + sys = real」)。
    ※正確な処理時間を測る場合は、繰り返し計測を行い、統計情報(平均値とばらつきの傾向等)を求める必要がある

  • topコマンドの結果、対象コマンド(パイプより連結された複数のコマンド)のCPU使用率が100%を超えている(例えば、計150%であれば、1.5個分のCPUを使用しているといえる)。

並列化の限界

パイプでつながれたコマンドが並列実行されるのは、データが滞りなく流れている時のみ。
パイプ前後のコマンドがデータの受け取りに手間取っている場合には、思いのほか並列実行の恩恵が受けれない。

事例として次のようなものが挙げられる。

  • 負荷にばらつきがある場合、一番負荷の高いコマンドの処理時間以上に処理が早く終わることはない
    例えば、grepの検索結果をawkで処理する(grepの処理するデータ量がawkの処理するデータ量よりも多い)場合、grep側の処理が終わるのを待つ必要がある。

  • データをブロックするコマンドがある場合、そのコマンドがボトルネックになる
    例えば、sortコマンドは全データを読み込まないと結果を出力できないため、sortコマンドをパイプでつなげた場合、sortが終わるまで後続の処理は実行されない。
    対応としては、sortコマンドの位置をできるだけ後ろに回す、またはsortコマンドの処理するデータ量を減らすことで処理効率を上げる。

psqlでSQLとメタコマンドを併用する

結論

実行するSQLとメタコマンドが記述されたファイルsqlite.txtを用意する。
※デリミタがタブ文字の場合は、カンマを「Ctrl+v+tab」に置き換える。

delete from zipcode_info;
delete from zipcode_info2;
\copy zipcode_info (pref_code, city_code, zipcode) from data.csv with delimiter ','
\copy zipcode_info2 (pref_code, city_code, zipcode) from data2.csv with delimiter ','

psqlを実行する。

psql -U user_name -h host_name -d db_name -f sql.txt -1 2>&1

事の始まり

シェルからpsqlコマンドでトランザクション - その他(プログラミング・Web制作) 締切済 | 教えて!goo

テーブルの更新(レコードの削除+レコードの流し込み)を行いたい。
上との違いは、対象となるテーブルが複数あることくらい。

調べてみる

copy
CSVファイルをデータベースにインポートする - Qiita

psql
https://www.postgresql.jp/document/9.1/html/app-psql.html

オプション「-c command」の説明を見ると...

このため、このオプションではSQLpsqlタコマンドを混在させることはできません。 これらを同時に使用するには、echo '\x \\ SELECT * FROM foo;' | psqlのようにパイプを使って文字列をpsqlに渡します(\\はメタコマンドの区切り文字です。)。
コマンド文字列が複数のSQLコマンドを含む場合、トランザクションを複数に分けるBEGIN/COMMITコマンドが明示的に文字列内に含まれない限り、それらのコマンドは1つのトランザクションで処理されます。


SQLとメタコマンドをパイプで渡せば、psqlで実行できそう。

やってみる

こういうことはできない

psql -U user_name -h host_name -d db_name -c "delete from zipcode_info; \copy zipcode_info (pref_code, city_code, zipcode) from data.csv with delimiter ','"

から...

echo "select * from zipcode_info;" | psql -U user_name -h host_name -d db_name

的なノリで、

echo "delete from zipcode_info; \copy zipcode_info (pref_code, city_code, zipcode) from data.csv with delimiter ','" | psql -U user_name -h host_name -d db_name

できた。
けど、これだとトランザクションが張れていないので…

echo "BEGIN; delete from zipcode_info; \copy zipcode_info (pref_code, city_code, zipcode) from data.csv with delimiter ',' COMMIT;" | psql -U user_name -h host_name -d db_name

\copyのパースに失敗…
いろいろ試してみる

echo "BEGIN; delete from zipcode_info; \copy zipcode_info (pref_code, city_code, zipcode) from data.csv with delimiter ',' \\ COMMIT;" | psql -U user_name -h host_name -d db_name
echo "BEGIN; delete from zipcode_info; \copy zipcode_info (pref_code, city_code, zipcode) from data.csv with delimiter ',' \n COMMIT;" | psql -U user_name -h host_name -d db_name

タメだ…区切り文字が分からない…

もう一度調べてみる

psql
https://www.postgresql.jp/document/9.1/html/app-psql.html

オプション「-f filename」の説明を見ると...

対話式にコマンドを読み取るのではなく、filenameファイルをコマンドのソースとして使用します。 このファイルの処理が終了した後、psqlは終了します。 これは\i内部コマンドとほぼ同じ効力を持ちます。


オプション「-1」の説明を見ると...

fオプションを使用してpsqlスクリプトを実行する時、このオプションを併記すると、スクリプトをBEGIN/COMMITで囲み、単一トランザクション内でスクリプトを実行します。 これにより確実にすべてのコマンドが完全に成功するか、変更がまったく行われないかのいずれかになります。


これだ

もう一度やってみる

実行するSQLとメタコマンドが記述されたファイルを用意する。

delete from zipcode_info;
\copy zipcode_info (pref_code, city_code, zipcode) from data.csv with delimiter ','

トランザクション張る必要があるからオプション付けて、

psql -U user_name -h host_name -d db_name -f sql.txt -1

できた(エラーが発生すると反映されない、トランザクションが効いてる)。

対象となるテーブルが複数ある場合は、複数のSQLとメタコマンドが記述されたファイルを用意して、

delete from zipcode_info;
delete from zipcode_info2;
\copy zipcode_info (pref_code, city_code, zipcode) from data.csv with delimiter ','
\copy zipcode_info2 (pref_code, city_code, zipcode) from data2.csv with delimiter ','

psqlコマンドを実行してみる。

psql -U user_name -h host_name -d db_name -f sql.txt -1 2>&1

できてる。

仮にできなかったら

psqlコマンドで個々のSQLやメタコマンドは実行できるから、トランザクション機能をどこかで補完する必要がある。
オブサーバ的なテーブルで参照先のテーブルを切り替えることで、トランザクション機能を肩代わりすれば問題ない。
本来想定していたテーブルと合わせてミラーリング用のテーブルを用意し、更新処理が正常に終わったら参照するテーブルを切り替えることで対応する。

1つのサーバで複数の開発環境をつくる

前提条件

  • php(phpenv,composer導入済み)、apacheで稼働
  • 何かしらのフレームワークを利用(Fuelを想定)
  • バージョン管理されてる(gitbucketを想定)

リソースの取得

まずは、管理リソースを。

# cd /var/www/
# git clone -b develop http://~.git 開発用ディレクトリ
# chown -R user:user 開発用ディレクトリ


次に、管理外リソースを。

// composer利用に伴いバージョン調整
$ cd /var/www/開発用ディレクトリ
$ phpenv install 5.6.15
$ phpenv local 5.6.15
$ phpenv versions // バージョン確認

// composerパッケージの取得
$ php composer.phar self-update
$ php composer.phar install


↑2つ(管理/管理外リソースの取得)はコピーで済むなら、それでも。

# cd /var/www/
# cp org_dir 開発用ディレクトリ

開発者固有の設定

git等

$ vi 開発用ディレクトリ/.git/config
// 以下を追記
[user]
        name = aitaka
        email = aitaka@~.co.jp

$ git remote set-url origin http://aitaka@~.git

Apache(バーチャルホスト)の設定

いろいろやり方はあるけど、今回は「違うポートで違うサイトを運営する」
バーチャルホストの例 - Apache HTTP サーバ バージョン 2.2


ポートの利用状況について調べる

$ netstat -anp | grep ポート番号

参考
Linuxでアプリケーションが使用中のポート番号を調べる - 日々の報告書


バーチャルホストを追加する

# vi /etc/httpd/conf/httpd.conf
// 以下を追記
// ポート番号を890としたとき
Listen 890
<VirtualHost *:890>
    DocumentRoot /var/www/開発用ディレクトリ/public/
    ServerName dev1
    <Directory /var/www/開発用ディレクトリ/public>
      AllowOverride All
      Options Indexes FollowSymLinks
    </Directory>
</VirtualHost>

# service httpd restart

参考
ポートベースのバーチャルホストを設定する - MogLog
いまさら聞けない!? Web系開発者のためのサーバ知識(3):Apacheで仮想ホストを動かそう (1/3) - @IT

firewallの設定

通過許可ポートの追加

# vi /etc/sysconfig/iptables
// 以下を追記
// ポート番号を890としたとき
-A INPUT -m state --state NEW -m tcp -p tcp --dport 890 -j ACCEPT

# service iptables restart

参考
iptablesの設定方法 – さくらのサポート情報

gitでよく使うの

gitでよく使うコマンドをまとめてみた。

clone

git clone -b ブランチ名 リモートリポジトリのURL [ローカルリポジトリ名]

remote

# リモートリポジトリ一覧
git remote -v

# リモートリポジトリ追加
git remote add リモートリポジトリ名 リモート先(URLにユーザ名を含めたもの、「http://aitaka@~.git」的な)

# リモートリポジトリ設定
git remote set-url リモートリポジトリ名 リモートリポジトリのURL

# リモートリポジトリ削除
git remote rm リモートリポジトリ名

branch

# 作成及び移動
git checkout -b ブランチ名

# ローカルブランチの削除
git branch -d ブランチ名

# リモートブランチの削除(空ブランチのpush)
git push origin :ブランチ名

log

# ログ特定
git log --grep='コミットメッセージに含まれるキーワード'

# 差分表示
git log -p

diff

# ワークツリーとステージ領域
git diff

# ワークツリーと最新コミット
git diff HEAD

add

# 一斉追加
git add .

commit

# 簡易コミット
git commit -m コミット概要

reset

# コミットのみ取り消し(コードは変わらず)
git reset --soft コミットID

# コード修正/コミットの取り消し
git reset --hard コミットID

merge

# マージした記録を残す
git checkout ブランチ名1
git merge --no-ff ブランチ名2

rebase

# コミットの統合(対象コミットは、最新コミットからn個遡ったもの)
git rebase -i HEAD~n
# 統合するコミットIDのpickをfixupに書き換える
# 編集エディタを保存して終了すると、上記で編集したコミットが統合されている(なかったことになる)

push

git push リモートリポジトリ名(originとか) ローカルブランチ名:リモートブランチ名

# ブランチが同じ場合
git push リモートリポジトリ名(originとか) ブランチ名

pull

git pull リモートリポジトリ名(originとか) リモートブランチ名:ローカルブランチ名

# ブランチが同じ場合
git pull リモートリポジトリ名(originとか) ブランチ名