開発ツール」カテゴリーアーカイブ

CentOS7にphpenv/php-buildを入れてPHPのバージョンを自在に切り替える

前提

以下の環境で動作確認

要素 バージョン
CentOS 7.3.1611
git 1.8.3.1
phpenv 1.1.1-2

概要

Dockerで構築したほぼ空っぽのCentOS7に対し、各種パッケージをインストール後、phpenv/php-buildをインストールし、PHPのバージョンを自在に切り替えたお話。PHPのバージョンを上げたり下げたりすることを容易に行うことが目的。

本記事ではほぼ空っぽのCentOS7を利用しているので、色々な基本となるパッケージの導入も行ってるが、多くの環境では既に入っているものが殆どなので必要に応じてインストールする形で問題ない。

phpenv/php-buildとは

phpenv: PHPの複数のバージョンを一元管理し、バージョンの切り替えを容易に行うためのツール
php-build: phpenvで使用するPHPのインストールツール

下準備

ツールはGithubで公開されてるのでgitが必要。ファイルの書き換えなどでvimを使う。パッケージのダウンロードにwgetを使う。

$ yum install -y git vim wget

CentOS7の標準リポジトリだけだと足りないので、EPELを追加する。

$ yum install epel-release

EPELリポジトリを有効にする(デフォルトで有効になってる?)

$ vim /etc/yum.repos.d/epel.repo
enabled=1

PHPに依存するパッケージ、コンパイルに必要パッケージなどなどを纏めてドン(元々入ってるものが多数、不要なものもあるかも)
PHPのバージョンによっては依存モジュールが異なる場合がある模様。その都度エラーメッセージが出るのでインストールすればよい。

$ yum install -y yum install gcc bison libxml2 libxml2-devel openssl-devel libcurl-devel libjpeg-turbo-devel libpng-devel libmcrypt-devel readline-devel libtidy-devel libxslt-devel bzip2 bzip2-devel libicu-devel  gcc-c++ tidyp make re2c file libtool-ltdl-devel autoconf automake patch mysql-devel

phpenvのインストール

Githubから本体を落とす。/usr/srcあたりに置くのが定石なのでそこでclone

$ cd /usr/src
$ git clone https://github.com/CHH/phpenv.git

本体の中にインストールスクリプトが含まれているのでそれを実行する

$ cd phpenv/bin/
$ ./phpenv-install.sh
Installing phpenv in /root/.phpenv
remote: Counting objects: 2620, done.
remote: Total 2620 (delta 0), reused 0 (delta 0), pack-reused 2620
Receiving objects: 100% (2620/2620), 491.71 KiB | 368.00 KiB/s, done.
Resolving deltas: 100% (1640/1640), done.
Success.

export PATH="/root/.phpenv/bin:$PATH"
eval "$(phpenv init -)"

Add above line at the end of your ~/.bashrc and restart your shell to use phpenv.

これでphpenvは使えるようになったが、コマンドですぐに利用できるようにパスを通す。
以下をbashrcなりに追記する

export PATH="/root/.phpenv/bin:$PATH"
eval "$(phpenv init -)"

bashrcを再読込

$ source ~/.bashrc

phpenvが読めることを確認

$ phpenv --version
rbenv 1.1.1-2-g615f844

php-buildのインストール

PHPをインストールするためのphp-buildを取得し、phpenvのプラグインディレクトリに配置する。こちらもGithubから取得できるので以下の手順でCone

$ git clone https://github.com/CHH/php-build.git ~/.phpenv/plugins/php-build

以下コマンドでインストール可能なPHPの一覧が出てくるので、これが出ればOK

$ phpenv install --list
(中略)
  7.1.0
  7.1.1
  7.1.2
  7.1.3
  7.1.4
  7.1.5
  7.1.6
  7.1.7
  7.1.8
  7.1snapshot
  7.2.0beta1
  7.2.0beta2
  7.2snapshot
  master

最新のPHPをインストールする

前項でインストール可能なPHPの一覧を確認した所、開発段階の7.2までインストール可能になっているが、今回は安定版の最新版である7.1.8をインストールする。

インストール時間が長い。その上インストールログがあんまり出ないので固まったとも思ってしまうが、気長に待てば終わるか、エラーメッセージがちゃんと出るので待機する。(本環境では11分程度)

$ phpenv install 7.1.8
[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
[Info]: Loaded github Plugin.
[Info]: Loaded uprofiler Plugin.
[Info]: Loaded xdebug Plugin.
[Info]: Loaded xhprof Plugin.
[Info]: Loaded zendopcache Plugin.
[Info]: php.ini-production gets used as php.ini
[Info]: Building 7.1.8 into /root/.phpenv/versions/7.1.8
[Downloading]: https://secure.php.net/distributions/php-7.1.8.tar.bz2
[Preparing]: /tmp/php-build/source/7.1.8
[Compiling]: /tmp/php-build/source/7.1.8
[xdebug]: Installing version 2.5.5
[xdebug]: Compiling xdebug in /tmp/php-build/source/xdebug-2.5.5
[xdebug]: Installing xdebug configuration in /root/.phpenv/versions/7.1.8/etc/conf.d/xdebug.ini
[xdebug]: Cleaning up.
[Info]: Enabling Opcache...
[Info]: Done
[Info]: The Log File is not empty, but the Build did not fail. Maybe just warnings got logged. You can review the log in /tmp/php-build.7.1.8.20170815015550.log
[Success]: Built 7.1.8 successfully.

PHP7.1.8がインストールできたので、この環境で利用するデフォルトのPHPを7.1.8に設定する

$ phpenv global 7.1.8

PHP7.1.8が利用できる状態になったことが確認できる

$ php -v
PHP 7.1.8 (cli) (built: Aug 15 2017 02:06:59) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.1.8, Copyright (c) 1999-2017, by Zend Technologies
    with Xdebug v2.5.5, Copyright (c) 2002-2017, by Derick Rethans

古いPHPをインストールし、切り替える

開発内容によっては意図的に古いPHPを使用せざるを得ない場面もある。そんな場合にphpenvは効果的。ここではPHP5.2.17(デフォルトでインストールできる最も古いPHP)をインストールする。

$ phpenv install 5.2.17
[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
[Info]: Loaded github Plugin.
[Info]: Loaded uprofiler Plugin.
[Info]: Loaded xdebug Plugin.
[Info]: Loaded xhprof Plugin.
[Info]: Loaded zendopcache Plugin.
[Info]: php.ini-production gets used as php.ini
[Info]: Building 5.2.17 into /root/.phpenv/versions/5.2.17
[Downloading]: https://git.php.net/?p=web/php-distributions.git;a=blob;f=php-5.2.17.tar.bz2;h=90d654f7b9c60320f6228194ed1d2f12976a3a59;hb=bed93a30bbc2104f6e88e6ef142891a4c289502f
[Info]: Applying patches: /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/gmp.c.patch /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/xp_ssl.c.patch /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/zip_direct.c.patch /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/php-5.4.6-libxml2-2.9.patch
[Preparing]: /tmp/php-build/source/5.2.17
[Compiling]: /tmp/php-build/source/5.2.17
[xdebug]: Installing version 2.2.7
[xdebug]: Compiling xdebug in /tmp/php-build/source/xdebug-2.2.7
[xdebug]: Installing xdebug configuration in /root/.phpenv/versions/5.2.17/etc/conf.d/xdebug.ini
[xdebug]: Cleaning up.
[Info]: The Log File is not empty, but the Build did not fail. Maybe just warnings got logged. You can review the log in /tmp/php-build.5.2.17.20170815021852.log
[Success]: Built 5.2.17 successfully.

versionsコマンドを叩くと、以下のようにインストール済みのPHP一覧と、デフォルトに設定されたバージョン(7.1.8)が確認できる

$ phpenv versions
  5.2.17
* 7.1.8 (set by /root/.phpenv/version)

globalコマンドでバージョンを切り替えると

$ phpenv global 5.2.17

デフォルトが変わり

$ phpenv versions
* 5.2.17 (set by /root/.phpenv/version)
  7.1.8

5.2.17が利用できる状態になる

$ php --version
PHP 5.2.17 (cli) (built: Aug 15 2017 02:23:16)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2010 Zend Technologies
    with Xdebug v2.2.7, Copyright (c) 2002-2015, by Derick Rethans

備考

phpenv installで、以下のようなエラーが出て

-----------------
|  BUILD ERROR  |
-----------------

Here are the last 10 lines from the log:

何らかの対応を行った後は、中途半端にデータが残っている可能性のある下記ディレクトリを削除しておいたほうが無難な模様

$ rm /tmp/php-build -rf

所感

  • これまでrbenv(ruby),pyenv(python),nodenv(node)を使ってきたが、それ比べて導入に一番苦労した。依存が多すぎる。
  • 同様に一番インストールにも時間がかかる。コンパイルが長すぎる
  • いくつかのバージョンを一度入れてしまえば後は他の言語と同様なので容易
  • apacheとか関連モジュールは結局バージョンに依存してしまうからあんまり効果的でないのかも
  • 他言語含めて、anyenvというのを使えばまとめて出来たりするらしい。今度調べたい。

はじめてのvimscriptとvimプラグインの作成

前提

以下の環境で作業を行った

要素 バージョン
debian 8.6
vim 7.4
neobundle 7.2.051
git 2.11.0

概要

vimは長らく使ってきたけどvimscriptを書いたことがない私が、vimscriptの基本を勉強し、簡単なvimプラグインを作成、NeoBundleでインストールできる状態にしたお話。

Vim scriptとは

Vim上で動作するスクリプトのこと。

:set filetype=html

のような、コマンドモードで”:”から始まるコマンドの集合。

最も身近なvimscriptは.vimrcで、これはvim起動時に読み込まれるvimscriptである。
つまり、vimscriptの書き方を知っていれば、より効果的なvimrcを記述することができる。

Vim script入門

vim scriptの入門は、以下を参考にした。
Vim script と vimrc の正しい書き方

本記事は簡単に基本文法を紹介する程度にするので、具体的に学びたい場合は各種参考ページを利用すること。

実行方法

vimscriptは基本的にscript.vimのように、拡張子をvimにしてファイルを作成する。
vimファイルはvimコマンドから実行することもできるが、多分以下のように、スクリプトをvimで編集中にsourceコマンドを叩くのが一番早い。

変数と代入

vim scriptには変数宣言の概念はなく、変数への代入と同時に変数が作成される模様。
let で変数への代入を行う。echoを見てもわかるように、シェルスクリプトに似ていると感じる。

let number = 1
let string = "hoge"
let array  = [1,2,3,4,5]

echo number
echo string
echo array

実行結果

1
hoge
[1, 2, 3, 4, 5]

条件分岐

ifとelseifとendifを使う。これもシェルスクリプトと同じ。

let number = 1
if number == 1
  echo "number is 1"
elseif number == 2
  echo "number is 2"
else
  echo "other"
endif

繰り返し

for文とwhile文が使える。while文の中でletを用いて変数を再宣言しているように見えるが、vim scriptにおけるletは代入のコマンドなので代入する場合は必ず必要。

for i in [1,2,3]
  echo i
endfor

let n = 0
while n < 10
  echo n
  let n += 1
endwhile

関数

関数もシェルスクリプトと同じようだが、気をつけたい所が2点。

まず、function! と”!”をつけることで、同名の関数が既にロード済みの場合上書きすることができる。こうしないと、同じバッファ内で複数回同じスクリプトを読んだ時に、同名の関数が既に定義済みだよとエラーが出るので”!”を付ける必要がある。

次に、関数の引数であるnumberを参照するために”a:”を頭につけている。これは変数のスコープの指定を表し、aとつけることでnumberは関数内にあるnumberであることを表す。aの他に、グローバルスコープ用の接頭辞やスクリプトファイル内スコープ用の接頭辞があるので、必要に応じて書き換える必要がある。

function! Double(number)
  return a:number * 2
endfunction

let result = Double(10)
echo Double(10)

vimプラグインの作成

本記事では、簡単すぎず難しくもないシンプルなvimプラグインを実装する。

今回作成するプラグインは、:RandDateTimeと入力することで、1980年から、2017年までの範囲内のランダムな日付を、Y-M-D H:m:sの形式でカーソル位置に挿入するコマンドを利用できるようにするプラグインで、まったくもって実用性はないが、個人的にもう少し改良して実用的なものにする予定。

ディレクトリ構成

作成するプラグイン名をmy-vim-pluginとした場合、同名のディレクトリを作成し、その中にpluginというディレクトリを作成、さらにその下にvim scriptを記述する。my-vim-plugin直下にはREADMEを入れたりするみたいだが、今回はスクリプトファイル一つで完結させるので、ディレクトリ構成は下記のようになる。

vagrant$ tree my-vim-plugin/
my-vim-plugin/
└── plugin
    └── script.vim

1 directory, 1 file

乱数を生成する関数

まず、ランダムな日付を生成するために、指定した範囲内での数値をランダムに戻す関数を実装する。
残念ながら、vim scriptには乱数を生成するための仕組みは存在しない。とは言え、今回は時刻データを用いて擬似的な乱数を生成することにした。

作成した関数は以下の通り。GetRandom関数は、minとmaxを引数にとり、min <= n <= max の範囲のランダムな整数を戻す。
reltime関数は、現在のUNIXTIMEを秒とマイクロ秒の配列で戻す。配列の二番目の要素がマイクロ秒になるので、その数値を使って擬似乱数を生成している。

function! GetRandom(min, max)
  return reltime()[1] % (a:max - a:min + 1) + a:min
endfunction

ランダムな日付を取得する関数

前項で作成したGetRandom関数を用いて、1980年から2017年までのランダムな日付文字列を戻す、GetRandomDate関数を以下のように実装する。なお、今回は実装の簡略化のため、日は1日から28日までの確実に存在する日付のみを対象とするようにした。

function! GetRandomDate()
  return GetRandom(1980, 2017) . '-' . GetRandom(1, 12) . '-' . GetRandom(1, 28)
endfunction

ランダムな日付時刻を取得する関数

同様に、前項で作成したGetRandomDate関数を用いて、ランダムな時刻までを含めて戻すGetRandomDateTime関数を以下のように実装する

function! GetRandomDateTime()
  return GetRandomDate() . ' ' . GetRandom(0, 23) . ':' . GetRandom(0, 59) . ':' . GetRandom(0, 59)
endfunction

ランダムな日付時刻をカーソル位置に挿入する関数

前項で作成したGetRandomDateTime関数は、ランダム日付時刻を取得するだけなので、新たにInsertRandomDateTime関数を以下のように実装する。
executeコマンドは、以降の任意のコマンドをvim上で実行するためのコマンド。:normalは以降のコマンドをノーマルモードで実行するコマンド。aでカーソルの次の位置から挿入モードを開始、そしてdatetimeを入力するという手はずになる。

function! InsertRandomDateTime()
  let datetime = GetRandomDateTime()
  execute ":normal a" . datetime
endfunction

作成した関数を呼び出すコマンドを定義

ここまででランダム日付時刻文字列を挿入する仕組みができあがったので、それを呼び出すためのコマンドを定義する。コマンドの定義にはcomman関数を用い、コマンド名と実行内容を引数に設定する。(callは、指定した関数を実行するコマンド)

command! RandDateTime call InsertRandomDateTime()

動作確認

以上で、以下のような自作のvimプラグインが完成した。

function! GetRandom(min, max)
  return reltime()[1] % (a:max - a:min + 1) + a:min
endfunction

function! GetRandomDate()
  return GetRandom(1980, 2017) . '-' . GetRandom(1, 12) . '-' . GetRandom(1, 28)
endfunction

function! GetRandomDateTime()
  return GetRandomDate() . ' ' . GetRandom(0, 23) . ':' . GetRandom(0, 59) . ':' . GetRandom(0, 59)
endfunction

function! InsertRandomDateTime()
  let datetime = GetRandomDateTime()
  execute ":normal a" . datetime
endfunction

command! RandDateTime call InsertRandomDateTime()

これをsourceコマンドで読み込むことで、現在開いているバッファ上でRandDateTimeコマンドが利用できるようになる。

NeoBundleでインストールできるようにする

NeoBundleはvimプラグインを管理するためのツールで、決まったディレクトリ構成でGithub上にVimプラグインを公開すれば、誰でも手軽にそれをインストールできるようになる。

今回は既にNeoBundleを想定したディレクトリ構成にしているので、これをこのままGithub上にあげる。
Sa2Knight/my-vim-plugin

この状態で、既にNeoBundleが利用できる状態のvimrcに以下を追加

NeoBundle "Sa2Knight/my-vim-plugin"

vimを起動すると、以下のようにインストールするかの確認が出るので、Yを入力する

Not installed bundles:  ['my-vim-plugin']
Press ENTER or type command to continue
Install bundles now?
(y)es, [N]o:

Github経由で先程作成した自作プラグインがインストールされ、次回起動時から自動で:RandDateTimeコマンドが使えるようになる。

所感

vimscript及びvimプラグインの非常に基礎的な部分にようやく手を付けられたので、今後もっと深い部分を勉強して実用的なプラグインを作りたい。とりあえず今回作ったプラグインには以下のような改良点があるので、それを実装しつつノウハウを見つけていく。

  • ランダムの範囲を外から指定できるようにする
  • 日付時刻文字列のフォーマットを外から指定できるようにする
  • 乱数値が一桁になった場合0で詰めるようにする
  • コマンドを叩かずともキーバインドですぐに実行できるようにする