HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

カーソルの使用方法

もうZend Framework 1(ZF1)の話はいいやって感じだけど一応メモ。

ZF1を使ってDBへのアクセスを実現するには、通常はZend_Db_Table_Abstractクラスを継承したモデルクラスを作って実現すると思うじゃないですか。

<?php

require_once 'Zend/Db/Table/Abstract.php';

class Model_Hoge extends Zend_Db_Table_Abstract {
    protected $_name = 'hoge';
}

で、例えばデータの取得は以下のような感じ。

$query = $hoge->select();
$rows = $hoge->fetchAll($query);
$res= array();
while ($rows->valid()) {
    $row = $rows->current();
    $res[$row->id] = $row->name;
    $rows->next();
}

でも、これだと、Zend_Db_Table_Rowset_Abstractが返された瞬間に全レコードのデータがメモリに載るので、件数が多かったりするとメモリアロケーションエラーが発生する。

そんなわけで「Zend Framework カーソル」なんて検索すると以下のページがヒットしたりするわけなんだが。

Statement?DB Adapter? なんか嫌な予感がした。

先のモデルクラスをベースにすると、以下のように書かないと、大量データの処理の際には使い物にならないという。

$query = $hoge->select();
$stmt = $hoge->getAdapter()->query($query->__toString());
$res = array();
while (($row = $stmt->fetch()) !== false) {
    $res[$row["id"]] = $row["name"];
}

もう、Zend_Db_Table_Rowset_AbstractやZend_Db_Table_Row_Abstractの恩恵一切なし・・・

ついでだったので、INSERTがクソ遅いという問題についても調べてみた。

元のコードはこんな感じ。

$start = time();
for ($i = 10000; $i < 11000; ++$i) {
    $data = array(
        "id" => $i,
        "name" => "hhelibex-{$i}",
    );
    $hoge->insert($data);
}
echo('insert time:' . (time() - $start));
$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $data = array(
            "name" => "HHeLiBeX-{$i}",
        );
        $where = array(
            "id = ?" => $i,
        );
        $hoge->update($data, $where);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('update time:' . (time() - $start));
$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $where = array(
            "id = ?" => $i,
        );
        $hoge->delete($where);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('delete time:' . (time() - $start));

これを実行すると、環境にもよるが(だいぶ前のLet's Note上のCentOS 7なので)、

insert time:40
update time:41
delete time:37

で、こんなふうに書き換えてみた。

<?php

require_once 'Zend/Db/Table/Abstract.php';

class Model_Hoge extends Zend_Db_Table_Abstract {
        protected $_name = 'hoge';
        public function getTableName() {
                return $this->_name;
        }
}

$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $data = array(
            "id" => $i,
            "name" => "hhelibex-{$i}",
        );
        $hoge->getAdapter()->insert($hoge->getTableName(), $data);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('insert time:' . (time() - $start));
$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $data = array(
            "name" => "HHeLiBeX-{$i}",
        );
        $where = array(
            "id = ?" => $i,
        );
        $hoge->getAdapter()->update($hoge->getTableName(), $data, $where);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('update time:' . (time() - $start));
$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $where = array(
            "id = ?" => $i,
        );
        $hoge->getAdapter()->delete($hoge->getTableName(), $where);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('delete time:' . (time() - $start));

結果は。

insert time:16
update time:16
delete time:14

半分以下にはなった。

Zend Framework 1+PostgreSQL

ある時、Zend Framework 1系+PostgreSQLで組まれたシステムのPostgreSQLバージョンアップを行う機会があったのだけど、PostgreSQL 12以降には上げられないことが判明して、PostgreSQL 11で妥協したことがあった。 CentOS 6のサポート期限も近づいているし、レガシーシステムの面倒を見ているとこういう場面にちょくちょく出くわす。

そこで、各バージョンのZend FrameworkPostgreSQLの組み合わせでの挙動確認を行ってみたメモ・・と思ったが、検証環境の構築を書くのが面倒になったので結論だけ(ぉ。

ちなみに、PostgreSQLについては以下で書いているので参考までに。

hhelibex.hatenablog.jp

Zend Framework 1からPostgreSQL 12以上に接続すると以下のようなエラーが発生する。

exception 'PDOException' with message 'SQLSTATE[42703]: Undefined column: 7 ERROR:  列d.adsrcは存在しません
LINE 9:                 d.adsrc AS default_value,
                        ^' in /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Statement/Pdo.php:228
Stack trace:
#0 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Statement/Pdo.php(228): PDOStatement->execute(Array)
#1 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Statement.php(303): Zend_Db_Statement_Pdo->_execute(Array)
#2 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Adapter/Abstract.php(480): Zend_Db_Statement->execute(Array)
#3 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query('SELECT\n        ...', Array)
#4 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Adapter/Pdo/Pgsql.php(177): Zend_Db_Adapter_Pdo_Abstract->query('SELECT\n        ...')
#5 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Abstract.php(836): Zend_Db_Adapter_Pdo_Pgsql->describeTable('hoge', NULL)
#6 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Abstract.php(875): Zend_Db_Table_Abstract->_setupMetadata()
#7 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Abstract.php(983): Zend_Db_Table_Abstract->_setupPrimaryKey()
#8 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Select.php(100): Zend_Db_Table_Abstract->info()
#9 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Select.php(78): Zend_Db_Table_Select->setTable(Object(Model_Hoge))
#10 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Abstract.php(1019): Zend_Db_Table_Select->__construct(Object(Model_Hoge))
#11 /var/www/html/test/2020-1018-01/v1-1/Application/controllers/IndexController.php(15): Zend_Db_Table_Abstract->select()
#12 /var/www/html/test/2020-1018-01/v1-1/Zend/Controller/Action.php(516): IndexController->indexAction()
#13 /var/www/html/test/2020-1018-01/v1-1/Zend/Controller/Dispatcher/Standard.php(308): Zend_Controller_Action->dispatch('indexAction')
#14 /var/www/html/test/2020-1018-01/v1-1/Zend/Controller/Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#15 /var/www/html/test/2020-1018-01/v1-1/index.php(7): Zend_Controller_Front->dispatch()
#16 {main}

Next exception 'Zend_Db_Statement_Exception' with message 'SQLSTATE[42703]: Undefined column: 7 ERROR:  列d.adsrcは存在しません
LINE 9:                 d.adsrc AS default_value,
                        ^, query was: SELECT
                a.attnum,
                n.nspname,
                c.relname,
                a.attname AS colname,
                t.typname AS type,
                a.atttypmod,
                FORMAT_TYPE(a.atttypid, a.atttypmod) AS complete_type,
                d.adsrc AS default_value,
                a.attnotnull AS notnull,
                a.attlen AS length,
                co.contype,
                ARRAY_TO_STRING(co.conkey, ',') AS conkey
            FROM pg_attribute AS a
                JOIN pg_class AS c ON a.attrelid = c.oid
                JOIN pg_namespace AS n ON c.relnamespace = n.oid
                JOIN pg_type AS t ON a.atttypid = t.oid
                LEFT OUTER JOIN pg_constraint AS co ON (co.conrelid = c.oid
                    AND a.attnum = ANY(co.conkey) AND co.contype = 'p')
                LEFT OUTER JOIN pg_attrdef AS d ON d.adrelid = c.oid AND d.adnum = a.attnum
            WHERE a.attnum > 0 AND c.relname = 'hoge' ORDER BY a.attnum' in /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Statement/Pdo.php:235
Stack trace:
#0 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Statement.php(303): Zend_Db_Statement_Pdo->_execute(Array)
#1 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Adapter/Abstract.php(480): Zend_Db_Statement->execute(Array)
#2 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query('SELECT\n        ...', Array)
#3 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Adapter/Pdo/Pgsql.php(177): Zend_Db_Adapter_Pdo_Abstract->query('SELECT\n        ...')
#4 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Abstract.php(836): Zend_Db_Adapter_Pdo_Pgsql->describeTable('hoge', NULL)
#5 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Abstract.php(875): Zend_Db_Table_Abstract->_setupMetadata()
#6 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Abstract.php(983): Zend_Db_Table_Abstract->_setupPrimaryKey()
#7 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Select.php(100): Zend_Db_Table_Abstract->info()
#8 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Select.php(78): Zend_Db_Table_Select->setTable(Object(Model_Hoge))
#9 /var/www/html/test/2020-1018-01/v1-1/Zend/Db/Table/Abstract.php(1019): Zend_Db_Table_Select->__construct(Object(Model_Hoge))
#10 /var/www/html/test/2020-1018-01/v1-1/Application/controllers/IndexController.php(15): Zend_Db_Table_Abstract->select()
#11 /var/www/html/test/2020-1018-01/v1-1/Zend/Controller/Action.php(516): IndexController->indexAction()
#12 /var/www/html/test/2020-1018-01/v1-1/Zend/Controller/Dispatcher/Standard.php(308): Zend_Controller_Action->dispatch('indexAction')
#13 /var/www/html/test/2020-1018-01/v1-1/Zend/Controller/Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#14 /var/www/html/test/2020-1018-01/v1-1/index.php(7): Zend_Controller_Front->dispatch()
#15 {main}

ここで出てくる「d.adsrc」とは「pg_attrdef.adsrc」のことなので、調べてみる。

$ ( v=11 ; /usr/pgsql-${v}/bin/psql -U postgres -p $((5432+v)) -c '\d pg_attrdef' )
                テーブル "pg_catalog.pg_attrdef"
   列    |      型      | 照合順序 | Null 値を許容 | デフォルト
---------+--------------+----------+---------------+------------
adrelid | oid          |          | not null      |
adnum   | smallint     |          | not null      |
adbin   | pg_node_tree |          |               |
adsrc   | text         |          |               |
インデックス:
    "pg_attrdef_adrelid_adnum_index" UNIQUE, btree (adrelid, adnum)
    "pg_attrdef_oid_index" UNIQUE, btree (oid)
$ ( v=12 ; /usr/pgsql-${v}/bin/psql -U postgres -p $((5432+v)) -c '\d pg_attrdef' )
                テーブル"pg_catalog.pg_attrdef"
   列    |      型      | 照合順序 | Null 値を許容 | デフォルト
---------+--------------+----------+---------------+------------
oid     | oid          |          | not null      |
adrelid | oid          |          | not null      |
adnum   | smallint     |          | not null      |
adbin   | pg_node_tree | C        | not null      |
インデックス:
    "pg_attrdef_adrelid_adnum_index" UNIQUE, btree (adrelid, adnum)
    "pg_attrdef_oid_index" UNIQUE, btree (oid)

確かに「adsrc」カラムがv12から無くなっている。

ちなみに、Zend Framework 2ではPostgreSQL 13まで問題なく動作した。

実際に確かめてみたいという方は、以下のリソースをダウンロードしてごにょごにょすれば(いい加減)。

Zend Framework本体は以下からダウンロードする。

また、いろいろと面倒なので、v2の方はZend Skeleton Applicationに頼る。

yumで標準以外のPostgreSQL環境を構築する

PostgreSQLはバージョンアップが早く、CentOSの標準リポジトリのみに依存していると、本家でEOLになっていって悲しいので、yumで新しいバージョンのPostgreSQL環境を構築するためのメモ。

ちなみに、CentOS 6ではv8.4.20、CentOS 7ではv9.2.24、CentOS 8ではv10.6が標準で入ります。

PostgreSQL公式のyumリポジトリ

PostgreSQLに関しては、本家がyumリポジトリを提供してくれています。

こちらを参照して、まずはリポジトリを追加します。

OSバージョン yumリポジトリURL
CentOS 6 (32bit) https://download.postgresql.org/pub/repos/yum/reporpms/EL-6-i386/pgdg-redhat-repo-latest.noarch.rpm
CentOS 6 (64bit) https://download.postgresql.org/pub/repos/yum/reporpms/EL-6-x86_64/pgdg-redhat-repo-latest.noarch.rpm
CentOS 7 https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
CentOS 8 https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm
# yum -y install <上記のURL>

PostgreSQLパッケージの確認

まずは利用可能なバージョンを確認してみます。

# yum list 'postgresql*' | grep '^postgresql[0-9]*[.]'
postgresql.x86_64                        8.4.20-8.el6_9              @updates   
postgresql.i686                          8.4.20-8.el6_9              base       
postgresql10.x86_64                      10.13-1PGDG.rhel6           pgdg10     
postgresql11.x86_64                      11.8-1PGDG.rhel6            pgdg11     
postgresql12.x86_64                      12.3-1PGDG.rhel6            pgdg12     
postgresql95.x86_64                      9.5.22-1PGDG.rhel6          pgdg95     
postgresql96.x86_64                      9.6.18-1PGDG.rhel6          pgdg96     
# yum list 'postgresql*' | grep '^postgresql[0-9]*[.]'
postgresql.x86_64                         9.2.24-1.el7_5             @updates   
postgresql.i686                           9.2.24-4.el7_8             updates    
postgresql.x86_64                         9.2.24-4.el7_8             updates    
postgresql10.x86_64                       10.13-1PGDG.rhel7          pgdg10     
postgresql11.x86_64                       11.8-1PGDG.rhel7           pgdg11     
postgresql12.x86_64                       12.3-5PGDG.rhel7           pgdg12     
postgresql95.x86_64                       9.5.22-1PGDG.rhel7         pgdg95     
postgresql96.x86_64                       9.6.18-1PGDG.rhel7         pgdg96     
# yum list 'postgresql*' | grep '^postgresql[0-9]*[.]'
postgresql.x86_64                  10.6-1.module_el8.0.0+15+f57f353b @AppStream

あれ、CentOS 8だけ、標準版以外が見つかりません。もう一度。

# less /etc/yum.repos.d/pgdg-redhat-all.repo
# yum list --disablerepo='AppStream' --enablerepo='pgdg*' 'postgresql*' | grep '^postgresql[0-9]*[.]'
postgresql.x86_64                          10.6-1.module_el8.0.0+15+f57f353b @AppStream                      
postgresql10.src                           10.13-1PGDG.rhel8                 pgdg10-source                   
postgresql10.x86_64                        10.13-1PGDG.rhel8                 pgdg10                          
postgresql11.src                           11.8-2PGDG.rhel8                  pgdg11-source                   
postgresql11.src                           11.8-2PGDG.rhel8                  pgdg11-source-updates-testing   
postgresql11.x86_64                        11.8-2PGDG.rhel8                  pgdg11                          
postgresql11.x86_64                        11.8-2PGDG.rhel8                  pgdg11-updates-testing          
postgresql12.src                           12.3-5PGDG.rhel8                  pgdg12-source                   
postgresql12.src                           12.3-5PGDG.rhel8                  pgdg12-source-updates-testing   
postgresql12.x86_64                        12.3-5PGDG.rhel8                  pgdg12                          
postgresql12.x86_64                        12.3-5PGDG.rhel8                  pgdg12-updates-testing          
postgresql13.x86_64                        13-beta1_2PGDG.rhel8              pgdg13-updates-testing          
postgresql94.src                           9.4.26-1PGDG.rhel8                pgdg94-source                   
postgresql94.x86_64                        9.4.26-1PGDG.rhel8                pgdg94                          
postgresql95.src                           9.5.22-1PGDG.rhel8                pgdg95-source                   
postgresql95.x86_64                        9.5.22-1PGDG.rhel8                pgdg95                          
postgresql96.src                           9.6.18-1PGDG.rhel8                pgdg96-source                   
postgresql96.x86_64                        9.6.18-1PGDG.rhel8                pgdg96                          

あれ、「enabled=1」になっているんですけどね。

ちなみに、CentOS 6/7/8ともに、pgdg94は「enabled=0」になっています。念のため。

PostgreSQL環境のインストール

それでは、気を取り直して、PostgreSQL v12をインストールしてみます。

# yum -y install postgresql12-server
# yum -y install --disablerepo='AppStream' --enablerepo='pgdg12' postgresql12-server

ちなみに、CentOS 7でのインストール中に以下のようなエラーが出ますが、今回は無視します。標準版が入っているせいなので。

シンボリックリンク /usr/bin/psql -> /etc/alternatives/pgsql-psql の作成に失敗しました。 /usr/bin/psql がすでに存在しており、シンボリックリンクファイル
ではありません。
 :
 :
# /usr/pgsql-12/bin/psql --version
psql (PostgreSQL) 12.3

とりあえずOKそうです。

環境設定

# service postgresql-12 initdb
データベースを初期化中:                                    [  OK  ]
# vim /var/lib/pgsql/12/data/postgresql.conf
# vim /var/lib/pgsql/12/data/pg_hba.conf
# service postgresql-12 start
postgresql-12 サービスを開始中:                            [  OK  ]
# /usr/pgsql-12/bin/psql -U postgres
psql (12.3)
"help"でヘルプを表示します。

postgres=# \l
                                         データベース一覧
   名前    |  所有者  | エンコーディング |  照合順序   | Ctype(変換演算子) |     アクセス権限      
-----------+----------+------------------+-------------+-------------------+-----------------------
postgres  | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       |
template0 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |          |                  |             |                   | postgres=CTc/postgres
template1 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |          |                  |             |                   | postgres=CTc/postgres
(3 行)

postgres=# \q
# /usr/pgsql-12/bin/postgresql-12-setup initdb
Initializing database ... OK
# vim /var/lib/pgsql/12/data/postgresql.conf
# vim /var/lib/pgsql/12/data/pg_hba.conf
# systemctl start postgresql-12
# /usr/pgsql-12/bin/psql -U postgres
psql (12.3)
"help"でヘルプを表示します。

postgres=# \l
                                         データベース一覧
   名前    |  所有者  | エンコーディング |  照合順序  | Ctype(変換演算子) |     アクセス権限      
-----------+----------+------------------+------------+-------------------+-----------------------
postgres  | postgres | UTF8             | ja_JP.utf8 | ja_JP.utf8        |
template0 | postgres | UTF8             | ja_JP.utf8 | ja_JP.utf8        | =c/postgres          +
           |          |                  |            |                   | postgres=CTc/postgres
template1 | postgres | UTF8             | ja_JP.utf8 | ja_JP.utf8        | =c/postgres          +
           |          |                  |            |                   | postgres=CTc/postgres
(3 行)

postgres=# \q

参考

yumでCentOSに標準以外のPHP実行環境を構築する

CentOSの標準リポジトリのみに依存していると、PHPのバージョンがどんどんEOLになっていって悲しいので、yumで任意のバージョンをインストールして使用するための手順のメモ。

ちなみに、標準リポジトリを使用する場合はCentOS 6ではPHP 5.3.3、CentOS 7ではPHP 5.4.16ですが、CentOS 8では一気にPHP 7.2.11まで飛びます。

Remiプロジェクト

RedHatLinuxPHPの最新版を提供することをミッションとする』だそうです。

ちなみに、多くの記事で「rpms.famillecollet.com」からインストールする例を見ましたが、2020年6月15日現在、ここではミラーされていないようで、本家へのリダイレクトになっています。

Remiリポジトリの追加

上記のサイトでミラーサイトを確認し、適当なところから選び出します。日本では「http://ftp.riken.jp/Linux/remi/」が提供されているので、今回はそこを利用します。

まず、EPELが必要となるので、その追加から。

# rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

こちらは標準リポジトリで提供されているので。

# yum -y install epel-release

続いてRemiリポジトリの追加。

# yum -y install http://ftp.riken.jp/Linux/remi/enterprise/remi-release-6.rpm
# yum -y install http://ftp.riken.jp/Linux/remi/enterprise/remi-release-7.rpm
# yum -y install http://ftp.riken.jp/Linux/remi/enterprise/remi-release-8.rpm

これで完了です。

利用可能なPHPバージョンの確認

以下を実行してみます。

$ yum list 'php*' | grep '^php[0-9]*[.]'
php.x86_64                                    5.3.3-50.el6_10          @updates
php54.x86_64                                  2.1-4.el6.remi           remi-safe
php55.x86_64                                  2.1-5.el6.remi           remi-safe
php56.x86_64                                  3.0-1.el6.remi           remi-safe
php70.x86_64                                  2.0-1.el6.remi           remi-safe
php71.x86_64                                  2.0-1.el6.remi           remi-safe
php72.x86_64                                  2.0-1.el6.remi           remi-safe
php73.x86_64                                  2.0-1.el6.remi           remi-safe
php.x86_64                              5.4.16-46.1.el7_7              @updates
php.x86_64                              5.4.16-48.el7                  base     
php54.x86_64                            2.1-4.el7.remi                 remi-safe
php55.x86_64                            2.1-5.el7.remi                 remi-safe
php56.x86_64                            3.0-1.el7.remi                 remi-safe
php70.x86_64                            2.0-1.el7.remi                 remi-safe
php71.x86_64                            2.0-1.el7.remi                 remi-safe
php72.x86_64                            2.0-1.el7.remi                 remi-safe
php73.x86_64                            2.0-1.el7.remi                 remi-safe
php74.x86_64                            1.0-3.el7.remi                 remi-safe
php80.x86_64                            1.0-3.el7.remi                 remi-safe
php.x86_64                                 7.2.11-1.module_el8.0.0+56+d1ca79aa    @AppStream
php.x86_64                                 7.2.11-2.module_el8.1.0+209+03b9a8ff   AppStream
php56.x86_64                               3.0-1.el8.remi                         remi-safe
php70.x86_64                               2.0-1.el8.remi                         remi-safe
php71.x86_64                               2.0-1.el8.remi                         remi-safe
php72.x86_64                               2.0-1.el8.remi                         remi-safe
php73.x86_64                               2.0-1.el8.remi                         remi-safe
php74.x86_64                               1.0-3.el8.remi                         remi-safe
php80.x86_64                               1.0-3.el8.remi                         remi-safe

とまぁ、こんな感じです。

PHP 5.6の導入

試しにということで、各環境にPHP 5.6を入れて、Apache httpdと連携させてみます。

# yum -y install php56 php56-php httpd

さて、CLIでバージョン番号を確かめてみます。

$ php -v

どのような出力が得られたでしょうか?

PHP 5.3.3 (cli) (built: Nov  1 2019 12:28:08)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies

PHP 5.4.16 (cli) (built: Nov  1 2019 16:04:20)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies

PHP 7.2.11 (cli) (built: Oct  9 2018 15:09:36) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

bash: php: コマンドが見つかりません

これ、実は実体が「/usr/bin/php56」なので、標準版のPHP環境が入っているとそのバージョン、入っていないとコマンドが見つからないとなります。

Apache httpdと連携する場合も注意が必要で、設定ファイルを確認すると以下のような状態になっています。

# ls -1 /etc/httpd/conf.d/php*
/etc/httpd/conf.d/php.conf
/etc/httpd/conf.d/php56-php.conf
# ls -1 /etc/httpd/conf.modules.d/*php*
/etc/httpd/conf.modules.d/10-php.conf
/etc/httpd/conf.modules.d/10-php56-php.conf
# ls -1 /etc/httpd/conf.modules.d/*php*
/etc/httpd/conf.modules.d/10-php56-php.conf
/etc/httpd/conf.modules.d/15-php.conf

CentOS 8では「10-php56-php.conf」のLoadModuleが先に実行されるのでPHP 5.6が走りますが、CentOS 6/7では標準版のLoadModuleが先に実行されるので、注意しておかないと、どれが実行されているのか分からなくなります。

最後に、php.ini の位置も確認しておきましょう。

# /usr/bin/php56 -i 2>/dev/null | grep 'php.ini$'
Loaded Configuration File => /opt/remi/php56/root/etc/php.ini

参考

SSLサーバー証明書を無視するための対応方法

よりにもよって繁忙期に、以下のROOT証明書期限切れで接続不可になるというトラブルに見舞われ、なんじゃゴルァ!と叫びながら対応した記憶の記録。

状況としては、WinSCPだと(証明書のCNがホスト名と違うってエラーが出るけど(ボソッ))接続できるが、Javaプログラムやlftpコマンドだと証明書の期限切れだと怒られる事案。

ひとまず、以下の情報を頼りに証明書チェインの情報を取ってみた。

$ openssl s_client -connect xxxxx.com:21 -starttls ftp -showcerts < /dev/null > hoge.txt
  hoge.txt の中にある証明書4つを分割して別ファイルに保存
$ openssl x509 -text -in 3.txt -noout
  :
        Validity
            Not Before: May 30 10:48:38 2000 GMT
            Not After : May 30 10:48:38 2020 GMT
        Subject: C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust RSA Certification Authority
  :
$ openssl x509 -text -in 4.txt -noout
  :
        Validity
            Not Before: May 30 10:48:38 2000 GMT
            Not After : May 30 10:48:38 2020 GMT
        Subject: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
  :

はい、というわけで。

使い古されているけどすぐに忘れてしまうコード断片のメモ。

FTPSClientの場合

import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.net.ftp.FTPSClient;

public class Main {
    public static void main(String[] args) {
        FTPSClient client = new FTPSClient("TLS", false);
        TrustManager tm = new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
        };
        client.setTrustManager(tm);

        try {
            client.connect("example.com", 21);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

HttpsURLConnectionの場合

ついでに。

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class Mail {
    public static void main(String[] args) {
        TrustManager[] tm = { new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
        } };
        try {
            SSLContext sslcontext = SSLContext.getInstance("SSL");
            sslcontext.init(null, tm, null);
    
            URL connectUrl = new URL("https://exaple.com/");
            HttpsURLConnection urlconn = (HttpsURLConnection) connectUrl.openConnection();
            urlconn.setSSLSocketFactory(sslcontext.getSocketFactory());
    
            urlconn.connect();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

lftpコマンドの場合

さらについで。 冒頭の方のサイトに書いてあるけど。

$ lftp -u uuuu,pppp -e 'set ssl:verify-certificate no' example.com

証明書ファイルなどを確認するコマンド諸々

歳を取ると、いろんな形式の証明書ファイルや、サーバーにアクセスしての証明書の検証方法など、コマンドをすぐに忘れて、そのたびにググるということを繰り返しているので、とりあえず思いついたものを整理しておくテスト。

用語などは適当かもですが、その辺はご容赦を・・・

PEM形式の証明書

以下のような形式のファイルです。

-----BEGIN CERTIFICATE-----
~~~
-----END CERTIFICATE-----

Let's Encryptのcertbotで生成した証明書の表示コマンド例。

openssl x509 -text -noout < /etc/letsencrypt/live/xxx/cert.pem
openssl x509 -text -noout < /etc/letsencrypt/live/xxx/chain.pem

Javaのキーストアファイル

例えば「/etc/pki/java/cacerts」とか「/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.232.b09-0.el7_7.x86_64/jre/lib/security/cacerts」に置いてあるファイルです。

keytool -storepass changeit -list -keystore /etc/pki/java/cacerts

PKCS12形式の証明書

Tomcatなんかで使いますね。そもそもTomcatをフロントエンド(略)

keytool -storepass changeit -list -v -storetype PKCS12 -keystore hoge.p12
openssl pkcs12 -in ssl/ks-lets202003.p12 -nodes -passin pass:changeit | openssl x509 -text

サーバー証明書チェインの確認

実際にサーバーにアクセスして、証明書チェインに問題がないかを確認する方法です。

cat /dev/null | openssl s_client -connect server_host_name:443 -showcerts
cat /dev/null | openssl s_client -connect server_host_name:21 -starttls ftp -showcerts

Eclipse(PDT)でPHPのコードを書く時の不便さを解消する

そもそも、静的型付けでない言語は好きではないのですが、好き嫌いを言っていると仕事が無くなってしまうので・・

そんなわけで、Eclipse(PDT)でPHPのコードを書くわけですが、時にあるクラスのインスタンスの配列を関数/メソッドに渡す形にしたい時があります。

<?php
class A {
  /**
   * ID
   * @var int
   */
  public $id;
  /**
   * 名前
   * @var string
   */
  public $name;
}
<?php
function a0(array $a) {
    foreach ($a as $aa) {
        // A のインスタンスである保証がないし、
        // Eclipseで変数名候補を表示させたりできない(重要!!)
        var_dump($aa->name);
    }
}

この時、関数「a0」内の変数「$aa」が何であるかという保証は何もありませんので、何らかのチェックをする必要があります。

PHPにはtype hintingという機能があり、以下のように書ければいいのですが・・

<?php
// 実際にはエラーになる
function a0(A[] $a) {
    // ・・・
}
<?php
// 実際にはエラーになる
function a0(multitype:A $a) {
    // ・・・
}
<?php
// 実際にはエラーになる
function a0(array(A) $a) {
    // ・・・
}

そんなわけで、Eclipse(PDT)をだますために、ひと工夫したりします。

instanceofで判定する

<?php
function a1(array $a) {
    foreach ($a as $aa) {
        if ($aa instanceof A) {
            var_dump($aa->name);
        }
    }
}

こうすると、「$aa」はクラスAのインスタンスであることが保証されるので、Eclipse(PDT)がそれを解釈して「$aa」をクラスAのインスタンスとして変数名候補の列挙などをしてくれます。

フィルタ用の関数を作る

<?php
function a2(array $a) {
    $a = filterA($a);
    foreach ($a as $aa) {
        var_dump($aa->name);
    }
}
/**
 *
 * @param array $a
 * @return multitype:A
 */
function filterA(array $a) {
    $res = array();
    foreach ($a as $aa) {
        if ($aa instanceof A) {
            $res[] = $aa;
        }
    }
    return $res;
}

ポイントは、関数「filterA」のdocument comment内の「@return multitype:A」です。 この関数を通してやることにより、戻り値がクラスAのインスタンスであることが一応保証されるので、Eclipse(PDT)をだますことができます。


実際のところ、皆さんはこの問題をどうやって乗り越えているんでしょうか・・