Search A.I.
Menu
ホーム

メールマガジン ホームページプログラミングテク

Windowsテク
Javaアプレット サンプル
Java Q & A
JavaScript Q & A
Perl Q & A
Perl レッスン
PHP レッスン
PHPテク
MS-DOS コマンド集
UNIX コマンド集
SQL コマンド集
SEの基礎講座
WEBシステム開発受注します
]無料フォームメール送信サービス
リンク集
Perl Q & A

関数をライブラリ化するには?
変数の宣言をしないとエラーにしてもらうようにするには?
クラス化するには?
メールを送信するには?


<< 前のページへ



・ライブラリの作成方法
今までいくつかのPerlの関数をご紹介しましたが、毎回新しいページを追加する度に
同じ関数をコピペしていたのでは効率も悪いですし、ファイルサイズが大きくなり、
あまりよくありません。

そこで、関数を集めたファイルをライブラリとして保存し、インクルードして
どのページからも関数を使用できるようにする方法をご紹介したいと思います。

関数ライブラリのファイルの記述には、いくつかルールがあります。

・1行目にパッケージ名を書く
・ファイルの最後に1;を書く

以下にサンプルを記述します。

▼サンプル(common.pl)----------------------------
package Common;

#------------------------------------------------------------------
# 関数 : DspMsg
# 概要 : メッセージ表示
# 引数 : タイトル、メッセージ
# 戻値 : なし
#------------------------------------------------------------------
sub DspMsg
{

    my $title = @_[0];
    my $msg = @_[1];
    print << "END_OF_HTML";
Content-type: text/html;

<HTML>
<HEAD>
<META http-equiv="Content-type" content="text/html; charset=Shift_JIS">
<TITLE>$title</TITLE>
</HEAD>
<BODY>
<P><FONT style="font-size=9pt">$msg</FONT></P>
</BODY>
</HTML>
END_OF_HTML
    return;
}
1;
▲-----------------------------------

ファイル名は何でも構いませんが、インクルードするときに使用します。

また、パッケージ名も何でも構いませんが、こちらも関数を呼び出す時に使用します
ので、分かりやすいものにしましょう。

さて、次に呼び出し側ですが、まずは呼び出し側のサンプルを以下に記述します。

▼CGIサンプル-------------------------
#! /usr/local/bin/perl

use strict;

require './common.pl';

&Common::DspMsg("TEST", "テストメッセージ");

exit;
▲-----------------------------------

requireで、関数ライブラリファイルのパスを指定します。

関数を呼び出す時は、

パッケージ名::関数名

となります。もちろん、引数や戻り値なども通常の関数同じように指定できます。

関数ライブラリファイルを作成すれば、違うページで共有できるだけでなく、
別のサイトのシステムを構築する時にも、ライブラリを持ってくれば、その
関数を使用できるので、大変便利です。



・変数宣言を必須にする
use strict;
と記述します。

これは、何なのかと申しますと、変数は全て宣言してから使用するというルールに
変更するというものです。

Perlは、基本的に変数を宣言しなくても使用できる作りになっています。
プログラムを書く人にとってはこれはとても楽になりますが、逆に宣言をしないと
困る場合が出てきます。

例えば、

-----------------------------------------------------------------
$test_string = "test";

# ・・・・・いろいろとプログラムが書かれている

print $test_strnig;

# ・・・・・いろいろとプログラムが書かれている
-----------------------------------------------------------------

といったプログラムを書いたとします。

しかし、これを実行すると、何も表示されません。

よく見ると、print文のところの「$test_string」が「$test_strnig」とスペルが
間違っています。

そのため、$test_strnigという変数は初めて使用されるが、中身は空なので、
何も表示されないのです。

もし、$test_string変数の宣言をしなくてはならないルールだったら、プログラムを
実行した時に、「何行目の$test_strnigが宣言されていません。」
とデバッガがエラーメッセージで親切に教えてくれます。 (実際は英語ですが・・)

簡単なプログラムならすぐに間違いを見つけることができると思いますが、
ものすごく大きいプロジェクトになってきた場合、たかが 1 文字間違えたくらいで
どこが間違えているのか分からないと、逆に効率が悪くなってしまうのです。

そこで、use strictを使用して、変数を宣言しなければならないルールに変更する
という方法が有効になります。

#! /usr/local/bin/perl
の後に

use strict;

と書くだけです。

実際の宣言は、

my $a;
my ($b, @c);
my (%data);
my $d = 10;

といった感じになります。

$aと、配列の@aを宣言すると、別々のものとして扱われます。
ハッシュ配列の%aも別のものとして扱われます。

先ほどのサンプルでは、

-----------------------------------------------------------------
use strict;
my $test_string = "test";

# ・・・・・いろいろとプログラムが書かれている

print $test_strnig;

# ・・・・・いろいろとプログラムが書かれている
-----------------------------------------------------------------

こうすると、実行した時にエラーとなります。サーバによっては、エラーメッセージ
が表示されないので、その場合は、Perlのデバッガを使用すると分かります。

宣言されていない変数名を使用すると、エラーになるので、いちいち宣言を
書かなくてはならなくなります。少々面倒ですが、バグの原因を少なくできるので、
なるべく使用する習慣をつけることをお薦めします。

myの他、localも宣言文となります。



・Perlのクラス化

Perlの場合、C言語やJavaなどと違い、クラスという概念がありません。しかし、
擬似的にクラスのように扱える方法がありますので、それをご紹介しようと思い
ます。

クラスについての説明は、オブジェクト指向なども説明しなければならないの
ですが、これにについては、ちゃんと説明すると本1冊以上にもなるので、
ここでは簡単にご説明します。

クラスとは、一言で言うと、変数や関数を一まとまりにしたものです。
さらに、作成したクラスを継承して、新たにクラスを作り、拡張していく
ことができます。

クラスは、変数や関数を自由に持たせることができるので、例えば、出力する
関数をまとめてクラス化したり、データベースに接続するための変数や関数を
まとめてクラス化することができます。

基本的に、クラスは再利用をするために作成するもので、再利用が難しい場合は
クラス化する意味はないと言えます。クラスを作成する際には、どのような
クラスを作成すれば効率が良いかを、きちんと設計する必要があります。

今回は、Perlのクラスの記述方法をご紹介します。

以下にサンプルを記述します。

▼サンプル(common.pm)----------------------------
package Common;

#--------------------------------------------------------
# 関数 : new
# 概要 : メンバ変数の初期値を設定
# 引数 : なし
# 戻値 : インスタンス
#--------------------------------------------------------
sub new
{

    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    bless($self, $class);

    # メンバ変数の設定
    $self->{member_a} = $_[0];
    $self->{member_b} = "This is a test.";

    return $self;

}

#------------------------------------------------------------------
# 関数 : print_test
# 概要 : メンバ変数の出力
# 引数 : なし
# 戻値 : なし
#------------------------------------------------------------------
sub print_test
{

    my $self = shift;
    if ($self->{member_a})
    {
      print $self->{member_b};
    }
}

1;
▲-----------------------------------

Perlのクラスは、少々分かりづらいのですが、細かい部分は気にせず、こういう
ものだと思ってください。

まず、コンストラクタという、クラスを初期化するための関数を書きます。
この中で、クラス内の変数を定義、初期化します。(クラスの持つ変数を
メンバ変数といいます。)

コンストラクタ名は、newでなくても構いませんが、newが一般的に使用されます。

コンストラクタ内で、クラスを使用するための準備をします。簡単に説明すると、
最初に、第1引数でクラス名が渡ってくるので、それを受け取り、次に無名の
ハッシュリファレンスを作成し、bless関数でオブジェクトとクラス名を渡します。

メンバ変数がある場合は、サンプルのようにメンバ変数を書いて初期化します。

クラス内に関数(クラスの持つ関数をメンバ関数といいます。)を書く場合は、
サンプルのように、普通の関数を書く要領で記述できます。

print_test関数では、メンバ変数の判定、出力を行っていますが、メンバ変数や
メンバ関数を使用するには、まず、第1引数のクラス名を受け取る必要があります。

また、クラスが破棄された時に呼ばれるデストラクタというものもありますが、
省略可能です。

さて、次に呼び出し側ですが、まずは呼び出し側のサンプルを以下に記述します。

▼CGIサンプル-------------------------
#! /usr/local/bin/perl

use strict;
use Common;

my $test = Common->new(1);

print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<HTML>
<BODY>
END_OF_HTML

$test->print_test();

print << "END_OF_HTML";
</BODY>
</HTML>
END_OF_HTML

exit;
▲-----------------------------------

"use パッケージ名"で呼び出しますが、クラス化したファイルは、同じディレクトリ
に置くか、Perlライブラリのディレクトリに置く必要があります。別の場所に置き
たい場合は、ライブラリをインクルードする設定する配列に、ディレクトリを追加
すればOKです。

まず、newのコンストラクタを呼び、Commonのオブジェクトを作成します。

一度オブジェクト化したら、メンバ変数やメンバ関数を呼び出すことができます。
"->"という矢印のような形式で呼び出します。

前回は、クラスの基本的なことをお話しましたが、今回はクラスの継承についてを
ご紹介いたします。

一度作成したクラスは、クラスを継承して機能を拡張していくことで、さらに威力を
発揮します。

継承されるクラスを親クラス、継承したクラスを子クラスなどと呼びます。もちろん
一つの親クラスに、複数の子クラスを継承させることも可能です。

子クラスは、親クラスのメンバ変数やメンバ関数をそのまま使用できます。さらに、
子クラスにメンバ変数やメンバ関数を追加していくことができます。

例えば、データベースに接続する親クラスを作成し、さらにその子クラスとして、
そのデータベースのテーブルを取得、更新する子クラスを複数作成するなどという
使用方法が考えられます。

クラスの継承を行うには、子クラスのファイル内で親クラスパッケージを呼び出し、
子クラスのコンストラクタ内で、親クラスのコンストラクタを呼び出します。

以下にサンプルを記述します。

▼サンプル(child.pm)----------------------------
# 子クラス
package Child;

# Parentという親クラスのファイルを呼び出す
use base qw(Paranet);

#--------------------------------------------------------
# 関数 : new
# 概要 : メンバ変数の初期値を設定
# 引数 : なし
# 戻値 : インスタンス
#--------------------------------------------------------
sub new
{

    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    bless($self, $class);
    # 親クラスのコンストラクタを呼ぶ
    $self = $self->Parent::new();

    # メンバ変数の設定
    $self->{member_b} = "Child member_b";

    return $self;

}

#------------------------------------------------------------------
# 関数 : print_child
# 概要 : メンバ変数の出力
# 引数 : なし
# 戻値 : なし
#------------------------------------------------------------------
sub print_child
{

    my $self = shift;
    print $self->{member_a};
    print $self->{member_b};
}

1;
▲-----------------------------------

▼サンプル(parent.pm)----------------------------
# 親クラス
package Parent;

#--------------------------------------------------------
# 関数 : new
# 概要 : メンバ変数の初期値を設定
# 引数 : なし
# 戻値 : インスタンス
#--------------------------------------------------------
sub new
{

    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    bless($self, $class);

    # メンバ変数の設定
    $self->{member_a} = "Parent member_a";

    return $self;

}

#------------------------------------------------------------------
# 関数 : print_parent
# 概要 : メンバ変数の出力
# 引数 : なし
# 戻値 : なし
#------------------------------------------------------------------
sub print_parent
{

    my $self = shift;
    print $self->{member_a};
}

1;
▲-----------------------------------

ここでは、親クラスとしてParentクラス、子クラスとしてChildクラスをそれぞれ
parant.pmとchild.pmに定義しています。

親クラスのパッケージを読み込むには、
use base qw(親クラスのパッケージ名);
とします。

また、親クラスのコンストラクタを呼ぶ場合、
$self = $self->Parent::new();
としています。
$self->new();
だけでは、子クラスのnew()が呼び出されてしまいますので、明示的に親クラスの
new()を呼ぶようにします。

Parentクラスにmember_aというメンバ変数を定義していますが、子クラス側でも
print_child内で、自分のメンバと同様に呼び出し方で呼び出しているのが分かる
かと思います。

また、Perlの場合、一つの子クラスが複数の親クラスを継承することができます。
複数のクラスを継承する場合は、複数のパッケージを読み込んで、それぞれの
コンストラクタを呼び出せばOKです。

それでは、実際にこのクラスを呼び出す側のサンプルを以下に記述します。

▼CGIサンプル-------------------------
#! /usr/local/bin/perl

use strict;
use Child;

my $test = Child->new();

print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<HTML>
<BODY>
END_OF_HTML

# 子クラスのメンバ関数を呼び出す
$test->print_child();

# 親クラスのメンバ関数を呼び出す
$test->print_parent();

print << "END_OF_HTML";
</BODY>
</HTML>
END_OF_HTML

exit;
▲-----------------------------------

今回の場合、子クラスの方だけを呼び出せばOKです。

子クラスのメンバ関数(print_child)を呼び出すと、親クラスのメンバ変数と
子クラスのメンバ変数が表示されると思います。

子クラスのオブジェクトから直接、親クラスのメンバ関数(print_parent)を
呼び出すこともできます。

前回までに、クラス化の基本をご説明しましたが、今回は実用的なサンプルを
ご紹介したいと思います。

よくクラス化して使用されるのが、親クラスでデータベース接続して、子クラスで
テーブル毎の処理を書くというのがあります。

ここでは、そのサンプルを書きます。
ちょっと長いですが、ざっと見てみてください。

▼サンプル(db_class.pm)----------------------------
# データベース接続クラス
package db_class;

use strict;
use DBI;

#--------------------------------------------------------
# 関数 : new
# 概要 : メンバ変数の初期値を設定
# 引数 : なし
# 戻値 : インスタンス
#--------------------------------------------------------
sub new
{

    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    $self->{DB_HOST} = "localhost";
    $self->{DB_PORT} = "5432";
    $self->{DB_NAME} = "test";
    $self->{DB_USER} = "postgres";
    $self->{DB_PSWD} = "postgres";
    $self->{DB_CONN} = undef;
    if (@_[0]) { $self->{DB_NAME} = @_[0] }
    if (@_[1]) { $self->{DB_USER} = @_[1] }
    if (@_[2]) { $self->{DB_PSWD} = @_[2] }
    if (@_[3]) { $self->{DB_HOST} = @_[3] }
    if (@_[4]) { $self->{DB_PORT} = @_[4] }
    bless($self, $class);
    return $self;
}

#--------------------------------------------------------
# 関数 : db_connect
# 概要 : データベース接続処理
# 引数 : 1) データベース名 (任意)
# 2) データベースユーザ名 (任意)
# 3) データベースパスワード (任意)
# 4) データベースホスト名 (任意)
# 5) データベースポート番号 (任意)
# 戻値 : データベースコネクション
#--------------------------------------------------------
sub db_connect
{

    my $self = shift;
    if (@_[0]) { $self->{DB_NAME} = @_[0] }
    if (@_[1]) { $self->{DB_USER} = @_[1] }
    if (@_[2]) { $self->{DB_PSWD} = @_[2] }
    if (@_[3]) { $self->{DB_HOST} = @_[3] }
    if (@_[4]) { $self->{DB_PORT} = @_[4] }

    # データベース接続
    $self->{DB_CONN} = DBI->connect('DBI:Pg:dbname=' . $self->{DB_NAME} .
    ';host=' . $self->{DB_HOST} .
    ';port=' . $self->{DB_PORT},
    $self->{DB_USER}, $self->{DB_PSWD});
    if(!$self->{DB_CONN})
    {

      # エラー処理
      return 0;
    }
    return $self->{DB_CONN};
}

#--------------------------------------------------------
# 関数 : db_disconnect
# 概要 : データベース切断処理
# 引数 : データベースコネクション (任意)
# 戻値 : なし
#--------------------------------------------------------
sub db_disconnect
{

    my $self = shift;
    if (@_) { $self->{DB_CONN} = shift }
    $self->{DB_CONN}->disconnect;
}

#--------------------------------------------------------
# 関数 : db_exec
# 概要 : SQL文実行
# 引数 : 1) 実行するSQL文
# 戻値 : ステートメントハンドルオブジェクト
#--------------------------------------------------------
sub db_exec
{

    my $self = shift;

    # SQL文を設定
    my $st = $self->{DB_CONN}->prepare(@_[0]);
    if (!($st))
    {

      return 0;
    }

    # SQL文を実行
    $st->execute();
    if ($st->rows() == 0)
    {

      return 0;
    }

    # ステートメントハンドルを返す
    return $st;

}

1;
▲-----------------------------------

▼サンプル(data_class.pm)----------------------------
# データをデータベースに格納するクラス
package data_class;

use strict;
use DBI;
use base qw(db_class);

#--------------------------------------------------------
# 関数 : new
# 概要 : メンバ変数の初期値を設定
# 引数 : なし
# 戻値 : インスタンス
#--------------------------------------------------------
sub new
{

    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    $self->{NAME} = 1;
    $self->{PRICE} = "";
    if (@_[0]) { $self->{ID} = @_[0] };
    if (@_[1]) { $self->{NAME} = @_[1] }
    if (@_[2]) { $self->{PRICE} = @_[1] }
    bless($self, $class);
    $self = $self->db_class::new();
    return $self;
}

#--------------------------------------------------------
# 関数 : db_get_detail
# 概要 : 詳細情報取得し、メンバ変数に設定
# 引数 : ID (任意)
# 戻値 : データ情報配列、エラーの場合は0
#--------------------------------------------------------
sub db_get_detail
{

    my $self = shift;
    my ($rhash, %hash, $st);
    my $id = @_[0];

    # IDの指定がない場合は、メンバ変数を使用する
    if ($id eq '')
    {

      $id = $self->{ID};
    }

    # IDが空の場合は、エラーを返す
    if ($id eq '')
    {

      return 0;
    }

    # データ取得SQL文作成
    my $sql = "select * from drink where name = '" . $id . "'";

    # SQL文を実行
    if (!($st = $self->db_exec($sql)))
    {

      return 0;
    }

    # フィールド名と値を取得する
    $rhash = $st->fetchrow_hashref();
    $st->finish;
    %hash = %$rhash;

    # 取得した値をメンバ変数に設定
    $self->{ID} = $hash{'id'};
    $self->{NAME} = $hash{'name'};
    $self->{PRICE} = $hash{'price'};

    # データを返す
    return %hash;

}

1;
▲-----------------------------------

少し処理が良くないかもしれませんが、参考にはなるかと思います。(^^;く

このサンプルは、DBIを使用してPostgreSQLに接続しています。

db_classクラスの方でデータベース接続やクローズ、SQL実行関数を実装して
います。他にもトランザクションを制御する関数などもあると良いでしょう。

デフォルト値でデータベース名やユーザ名を指定していますが、db_classクラスの
db_connect関数で引数で渡せば、違うデータベースにアクセスすることもできます。

data_classの方では、'id','name','price'というフィールドを持つ'drink'テーブル
にアクセスするクラスを作成してみました。

data_classのdb_get_detail関数は、IDを指定して詳細を取得する関数です。
他にも、ここでは省略しましたが、テーブルの一覧を取得する関数なども作成した
方が良いでしょう。

db_get_detail関数では、メンバ変数にデータベースから取得した値を格納し、
さらに戻り値としても配列を返しています。

では、実際にこのクラスを呼び出す側のサンプルを以下に記述します。

▼CGIサンプル-------------------------
#! /usr/local/bin/perl

use strict;
use data_class;

my ($data, $con, %drink);

$data = data_class->new();
$con = $data->db_connect("test", "postgres", "postgres", "localhost", "5432");
if (!$con)
{
print "Content-type: text/html\n\n";
print "Error";
}

%drink = $data->db_get_detail('1');
$data->db_disconnect($con);

print << "END_OF_HTML";
Content-type: text/html;

<HTML>
<BODY>
END_OF_HTML

print $drink{'name'};
print $data->{PRICE};

print << "END_OF_HTML";
</BODY>
</HTML>
END_OF_HTML

exit;
▲-----------------------------------

子クラスのdata_classをオブジェクト化し、親クラスdb_classのdb_connect関数を
使用してデータベースに接続します。

子クラスのdb_get_detailでIDに'1'を指定して詳細データを取り出し、表示して
います。

print文では、2通りの方法で出力していますが、戻り値として受け取るのであれば、
メンバ変数を無理に使用してなくても良いと思います。

※Perlの場合、厳密的なクラスではありませんので、ご注意ください。
  あくまで、擬似的にクラス化させる例をご紹介しています。



・メール送信方法
WEBサーバにメールを送信するプログラム(メールエージェントと言います)が
インストールされている場合、Perlからメールを送信することができます。
通常、レンタルサーバなどにはメール送信機能が組み込まれています。

メールエージェントには、SendMailやQMailなどの種類があります。
Perlでは、これらのメールエージェントを起動することで、メールを送信する
ことができます。

Pealでメールを送信するには、メールヘッダを作成し、ファイルをオープンする
要領で、メールエージェントを起動させ、ハンドルに本文を書きます。

メールエージェントのパスは、サーバによって異なりますので、使用している
サーバを調べてみてください。レンタルサーバの場合は、大抵ホームページに掲載
されています。

以下にメール送信関数例を記述します。

▼サンプル----------------------------

#------------------------------------------------------------------
# 関数 : SendMail
# 概要 : メールを送信する
# 引数 : メールエージェントのパス、宛先、送信者、件名、本文、
# (送信社名)、(返信先)、(エラー時の送信先)
# 戻値 : 成功 1、失敗 0
#------------------------------------------------------------------
sub SendMail
{

    # 日本語ライブラリの読み込み
    require "./jcode.pl";

    # 件名
    $subject = $_[3];

    # 本文
    $mail_body = $_[4];

    # JISコードへ変換(Perl 5以降)
    jcode::convert(\$subject, "jis");
    jcode::convert(\$mail_body, "jis");

    # メールエージェントのパスを設定
    my $mailer = $_[0];

    # メールヘッダを作成
    my $mlhd = "From: " . $_[2] . "\n";
    $mlhd .= "To: " . $_[1] . "\n";
    $mlhd .= "Subject: " . $subject . "\n";
    if ($_[5])
    {

      $mlhd .= ": " . $_[5] . "\n";
    }
    if ($_[6])
    {
      $mlhd .= "Reply-To: " . $_[5] . "\n";
    }
    if ($_[7])
    {
      $mlhd .= "Error-To: " . $_[6] . "\n";
    }
    $mlhd .= "\n";

    # メール書き込みを開始する
    open(MAIL, "| $mailer") or return 0;

    # メールヘッダを記述
    print MAIL $mlhd;

    # メール本文を記述
    print MAIL $mail_body;

    # メールをクローズする
    close(MAIL);

}

▲-----------------------------------

Perlの場合、メールヘッダなどを作成しなければならないので、関数化してしまった
方が楽です。

日本語の文字コードを変換するために、jcode.plを読み込んでいるので、なければ
jcode.plを同じディレクトリに置く必要があります。

この関数の使用例は、以下のようになります。

▼サンプル----------------------------
#! /usr/local/bin/perl

use strict;
my $result_message;

if (SendMail("/bin/SendMail",
"to@aaa.co.jp",
"from@aaa.co.jp",
"件名です",
"テストメールの\n本文です。\n"))
{

    $result_message = "メールを送信しました。";
}
else
{
    $result_message = "メール送信に失敗しました。";
}

print "Content-type:text/html\n\n";
print << "END_OF_HTML";
<html>
<body>
$result_message
</body>
</html>
END_OF_HTML

(SendMail関数をここに挿入)

exit;
▲-----------------------------------

メールエージェントがインストールされていなかったり、メールエージェントのパス
が違っていたりした場合は、エラーとなります。



<< 前のページへ Top


このエントリーをはてなブックマークに追加



OfficeLance

お問い合わせはこちらから