はじめてPerlさわったよ

# Perl初めて触れた

Perlってなに？というはなしは色んな所でされているので割愛する。

ちょっとお仕事で触ることがあったので `Perl v5.10.1` で開発をした際に調べたことをまとめた。
エンハンス的な案件だったので限定的な機能しか触ってない。なので、解説も限定的なのでご了承ください。

## 基礎的で特徴的なところ

身もふたもないけど wikipedia 見たほうが早いかもしれない。

### 変数名のルール

 変数名 | ルール
 :-- |:--
 `$obj` | スカラ型の値、オブジェクトのインスタンスを格納する場合は、変数名の前に `$` をつける。配列、ハッシュ（連想配列）の要素にアクセスする場合も使用する。
 `@arry` | 配列型の値を格納する場合は、変数名の前に `@` をつける。
 `%hash` | ハッシュ（連想配列）の値を格納する場合は、変数名の前に `%` をつける。
 
`$` , `@` , `%` はシジルというらしい。
シジルの前に `\` をつけると、リファレンス型をスカラ値として値を取り出せる。

リファレンス型のスカラ値はシジルをつけてアクセスすると実体を取り出せる。

※ wikipedia 参照。
 
### 変数宣言について

 変数宣言方法 | 効果
 :-- |:--
 `$v = 123` | 大域変数として宣言する
 `local $v = 123` | 動的スコープで、ブロック内のみで有効な局所変数として宣言する。
 `my $v = 123` | 静的スコープで、ブロック内のみで有効な局所変数として宣言する。

`my` はPerl5.0から登場したもので、それ以来 local は非推奨らしい。
※ [wikipedia: Perl](https://ja.wikipedia.org/wiki/Perl) 参照

### 関数呼び出しについて

Perlでは関数じゃなくて **サブルーチン** という。

Rubyと同じ感覚でいける。下記２つは同じ。

```
open my DATA, "filename.txt";
print DATA;
```

```
open(my DATA, "filename.txt");
print DATA;
```

違いがわからないが、下記の記述方法もある。

```
&open(my DATA, "filename.txt");
```

引数がない場合は、カッコなしでもいいっぽい。

```
&doSomething;
```

関数ポインタに対して実行してる感じなのかな？？

### 条件分岐

Ruby と同じ感覚でいける。

```
if ($v < 123) {
  print "$v is under 123 (" . $v . ")";
} elsif ($v eq 123) {
  print "$v is 123";
}
```

条件式の後置ができる。

```
print "$v is under 123 (" . $v . ")" if ($v < 123);
```

if の反転、`unless` がある。

```
print "$v is not 123 (" . $v . ")" unless ($v == 123);
```

### プロパティ、メソッドアクセス

PHPっぽい、`->` を使用する。

```
my $ua = LWP::UserAgent->new;
$ua->agent("Dummy User Agent.");
```

## コマンドライン上からプログラムを実行する

```
perl -M<リンクするモジュール> -e 'プログラム'
```

上記の構文を使ってモジュールがインストール済みか確認するようなコードがよく見られる。

インストールされていないモジュール Furl のインストール確認だと下記のようになる。

```
$ perl -MFurl -e "Furl"
Can't locate Furl.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .).
BEGIN failed--compilation aborted.
```

インストールされているモジュールだと下記のように何も出力されない。

```
$ perl -MLWP -e "LWP"
$
```

※ FurlとLWPはどちらも通信系のモジュール。

後述のモジュールのアンインストールはコレを使う。

## パッケージ・モジュール管理

パッケージ管理には CPAN を使用する。
使用するユーザのホームディレクトリに配置されたりする。

ユーザローカルにコンテンツルートを設定していて、cpanもユーザローカル配下においてある場合は下記のような実装が必要かも。

```
use lib "/home/vagrant/perl5/lib/perl5";
use URI;

$url = URI->new('https://<なんかしらのエンドポイント>');
```

他にもいろいろやり方はあるっぽい。
上記自体は、パスが変わると使えなくなるのであまりおすすめできない。

**おまけ**

ほかにあるいろいろについて。
いろいろと言ってもやることは、ライブラリのサーチパスを設定すること。
ライブラリのサーチパスは `@INC` という変数と、 `PERL5LIB` という環境変数に入っているらしい。

```
$ perl -e "print @INC;"
/usr/local/lib64/perl5/usr/local/share/perl5/usr/lib64/perl5/vendor_perl/usr/share/perl5/vendor_perl/usr/lib64/perl5
```

設定のしかた、apacheで動かす上で反映させる方法は未調査。

### インストール

```
$ cpan install <モジュール>
```

でモジュールを取り込む。

```
$ cpan <モジュール>
```

で実際にコンパイル？インストール？するという流れっぽい。

下記のように、cpanコンソールを使用することもできる。

```
$ cpan
Terminal does not support AddHistory.

cpan shell -- CPAN exploration and modules installation (v1.9402)
Enter 'h' for help.
cpan[1]>
```

help 出すと下記のようになる。

```
cpan[1]> help

Display Information                                                (ver 1.9402)
 command  argument          description
 a,b,d,m  WORD or /REGEXP/  about authors, bundles, distributions, modules
 i        WORD or /REGEXP/  about any of the above
 ls       AUTHOR or GLOB    about files in the author's directory
    (with WORD being a module, bundle or author name or a distribution
    name of the form AUTHOR/DISTRIBUTION)

Download, Test, Make, Install...
 get      download                     clean    make clean
 make     make (implies get)           look     open subshell in dist directory
 test     make test (implies make)     readme   display these README files
 install  make install (implies test)  perldoc  display POD documentation

Upgrade
 r        WORDs or /REGEXP/ or NONE    report updates for some/matching/all modules
 upgrade  WORDs or /REGEXP/ or NONE    upgrade some/matching/all modules

Pragmas
 force  CMD    try hard to do command  fforce CMD    try harder
 notest CMD    skip testing

Other
 h,?           display this menu       ! perl-code   eval a perl command
 o conf [opt]  set and query options   q             quit the cpan shell
 reload cpan   load CPAN.pm again      reload index  load newer indices
 autobundle    Snapshot                recent        latest CPAN uploads
```

### アンインストール

削除時に cpan は使用しない。
※ cpanm というコマンドでは削除ができるらしい。

```
# 削除対象のモジュールに関連するファイルの確認。
perl -MConfig -MExtUtils::Install -e '($FULLEXT=shift)=~s{-}{/}g; uninstall "$Config{sitearchexp}/auto/$FULLEXT/.packlist", 1, 1' Furl
```

```
# 削除を実行する。
perl -MConfig -MExtUtils::Install -e '($FULLEXT=shift)=~s{-}{/}g; uninstall "$Config{sitearchexp}/auto/$FULLEXT/.packlist", 1' Furl
```

ワンライナーでアンインストール事項してるだけ。
perlのプログラムでパッケージ操作するのね・・。

**参考**

* http://goro.kuni.to/install/478/

### cpan と cpanm

「CPANからモジュールを取得、アンパック、インストールする」ためのコマンドラインツールとのこと。

**備考**

Macだと `brew install cpanminus` でインストールできるっぽい。

**参考**

* [perldoc: cpanminus](http://perldoc.jp/docs/modules/App-cpanminus-1.7001/lib/App/cpanminus/fatscript.pod)

## 構文

### エラー処理

```
<なんか処理> or die
```

`or` は、論理演算子の or で、`||` と機能自体は同じだが、優先順位が違う。
※ とはいえ、 `|| die` にしてもだいたい問題なく動くらしい。

`die` は、標準エラーに引数が渡されていれば、それの値を出力して処理を終了する関数。
イメージ的には下記のような感じかな。

```
sub die {
  print 'args to stderr';
  exit;
}
```

エラー出たらログに吐いて捨てるようなときによく使うっぽい。

## Macに導入する

使ったことはないが、Ruby で言う rbenv 的な `plenv` がある。
anyenv で管理可能らしい。

## 通信する

通信する際に使用するモジュールは前述の `LWP` と `Furl` が一般的。
サーバのモジュール構成を操作できない環境だと `LWP` のほうが安泰っぽい。

### LWP の使い方ざっくり

なんかしらのエンドポイントに対してパラメータ `key1=value1` を付与して GETでリクエストを送る。

```
use LWP::UserAgent;
use URI;

$url = URI->new("https://なんかしらのエンドポイント");
$url->query_form("key", "value");

my $ua = LWP::UserAgent->new;
$ua->agent("Dummy User Agent");

my $response = $ua->get($url);
if ($response->is_success) {
  # success;
} else {
  # failed;
}
```

リクエストを送るのがユーザエージェントであるというのがちょっと面白いなと思った。

### HTTPS のサーバにリクエストを送信する

HTTPSのサーバにリクエストをする際は、下記のモジュールが必要。

* `LWP::Protocol::https` ※ LWPを使用する場合。
* `Net::SSL`

**SELinux問題**

下記のようなエラーが出ることがある。

```
500 Can't connect to www.google.com:443 (Permission denied) Content-Type: text/plain Client-Date: Fri, 27 Jul 2018 18:18:17 GMT Client-Warning: Internal response Can't connect to www.google.com:443 (Permission denied) Permission denied at /usr/local/share/perl5/LWP/Protocol/http.pm line 50.
```

コレ自体は LWP も Furl も出る。
SELinuxが有効だとPermission Deniedが発生する。
※ SELinuxを適切に設定する方法・・・。

突貫対応なら、下記を実行すればとりあえず動くはず。

```
$ sudo setenforce 0
$ getenforce # 確認。Permissiveになっていれば、Permission Deniedが出ないはず。
```

## JSON デコードざっくり

API呼び出してJSONもらってパースして・・・なんてよくあるケースですしね。
ついでに Furl ざっくり。
※ サンプルは Furl で通信した結果のレスポンスボディをJSONとしてパースするもの。
※ Encode, Furl, JSON をインストールしておく。

JSONモジュールの `from_json` をすることでハッシュが取得できる。

```
use Encode;
use Furl;
use JSON;

$url = URI->new('https://<なんかしらのエンドポイント>');
$url->query_form('key1', 'value1');

$furl = Furl->new(
  agent => 'Mozilla',
  timeout => 25
);

$response = $furl->get($url);
if ($response->is_success) {
  $body = decode_utf8($response->content);
  $result = from_json($body);

  if ($result->{'success'}) {
    print 'success!!';
  } else {
    print 'failed';
  }
} else {
  print 'failed';
}
```

## 所感

思いの外ちゃんとした言語だった。
日本の方が開発したモジュールが結構あって、ちょっと感動した。

記事がやたらと古い。そして、エンジニア向けっぽくない記事が多い気がした。
情報がまとまってる場所もあまりない気がした。

perl書くんだけど、ファイルの拡張子は cgi だったり、cfgだったりしてちょっと混乱した。
フレームワーク慣れしてるととっつきにくい気がする。（ってのはCGIに関する感想かも）

perlといえば！な正規表現を今回触れることがなく、解説することができなかった。まぁ、そこら辺は多くの人がまとめてくれてるんじゃないかしら。