PHP
PSysh

PsySH のタブ補完が動かない

PsySH のタブ補完が動かないのをなんとか動くようにできたのでメモ。

  • タブ補完に使われてる readline 拡張モジュールが内部で使ってる Editline (libedit) が環境によって?ちゃんと動かない
  • readline 拡張は Editline の代わりに GNU Readline を使うこともできる (ビルド時に選ぶ)
  • Ubuntu 16.04 のapt で入る php7.0-readline は --with-libedit だった
  • GNU Readline を使うように、readline 拡張を --with-readline (GNU Readline) でビルドしたものに差し替えたらタブ補完が動くようになった
  • さらに、Editline だと日本語が入力できなかったのも一緒に直った

環境

$ cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.1 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.1 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
UBUNTU_CODENAME=xenial

$ php -v
PHP 7.0.22-0ubuntu0.16.04.1 (cli) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.0.22-0ubuntu0.16.04.1, Copyright (c) 1999-2017, by Zend Technologies

$ php -r 'print_r(readline_info());'
Array
(
    [line_buffer] =>
    [point] => 0
    [end] => 0
    [library_version] => EditLine wrapper // <-- libedit
    [readline_name] =>
    [attempted_completion_over] => 0
)

readline 拡張モジュールのビルド

$ apt install libreadline6-dev

// php のソースをダウンロード
$ cd /usr/local/src
$ curl -LO http://jp2.php.net/get/php-7.0.22.tar.gz/from/this/mirror
$ tar xzf php-7.0.22.tar.gz

// phpize & configure
$ cd php-7.0.22/ext/readline
$ phpize
$ ./configure --with-readline=/usr/include/readline --without-libedit

なぜか --without-libedit を指定しても libedit を使おうとする (そしてコケる)

checking for libedit readline replacement... yes, shared
configure: error: Please reinstall libedit - I cannot find readline.h

configure の中で無条件に libedit を有効にしてる部分があるのでそこをコメントアウトする。
参考: http://xoops.ryus.co.jp/modules/d3blog/details.php?bid=38

ext/readline/configure
ext_output="yes, shared"
ext_shared=yes
case $PHP_LIBEDIT in
shared,*)
  PHP_LIBEDIT=`echo "$PHP_LIBEDIT"|$SED 's/^shared,//'`
  ;;
shared)
  PHP_LIBEDIT=yes
  ;;
no)
  ext_output=no
  ext_shared=no
  ;;
*)
  ext_output=yes
  ext_shared=no
  ;;
esac

  ### 3909行目あたりの3行をコメントアウト
  # ext_output="yes, shared"
  # ext_shared=yes
  # test "$PHP_LIBEDIT" = "no" && PHP_LIBEDIT=yes
  ### ここまでコメントアウト

{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ext_output" >&5
$as_echo "$ext_output" >&6; }
// やり直し
$ ./configure --with-readline=/usr/include/readline --without-libedit
(...)
checking for libedit readline replacement... no
checking for readline support... yes, shared
(...)

// 念のため元の readline.so をバックアップ
$ mv /usr/lib/php/20151012/readline.so{,.bak}

// ビルドすると readline.so ができる
$ make
$ make install

readline_info() の戻り値が変わって GNU Readline が使われてそうな感じになる。

$ php -r 'print_r(readline_info());'
Array
(
    [line_buffer] =>
    [point] => 0
    [end] => 0
    [mark] => 0
    [done] => 0
    [pending_input] => 0
    [prompt] =>
    [terminal_name] =>
    [library_version] => 6.3
    [readline_name] => other
    [attempted_completion_over] => 0
)

動くようになった!

$ drush php
Psy Shell v0.8.12 (PHP 7.0.22-0ubuntu0.16.04.1 — cli) by Justin Hileman
>>> Drupal::<TAB>
Drupal::$container                     Drupal::keyValue
Drupal::CORE_COMPATIBILITY             Drupal::keyValueExpirable
Drupal::CORE_MINIMUM_SCHEMA_VERSION    Drupal::l
Drupal::VERSION                        Drupal::languageManager
Drupal::accessManager                  Drupal::linkGenerator
Drupal::cache                          Drupal::lock
...

最後に

PsySH の動きを追うと、このバグ?のせいな感じ
PHP :: Bug #73292 :: readline_info returns empty/old line_buffer info

ただ他に困ってる人が見つからないので環境依存の何かなのか。
ともあれ動くようになって便利