2011.1.17
ジョン・パリス著

RPGの観点からみたPHP - パート6:やり残しを仕上げる

PHPにおける日付と時間、ウェブ・サービス、エラー処理

RPGの観点からみたPHPの基礎への旅路は(差し当たり)、今回で終わりとします。触れていない話題はたくさんありますが、それについてはこれ以上ここでは述べずに中身に入っていきましょう。

PHPにおける日付と時間

PHPには驚くほど強力な日付および時間の処理機能がありますが、その動作のベースはRPGの同様の機能のベースとは根本的に異なりますので、比較することが難しいのです。本稿では、これらの機能のほんの表面的な部分について述べます。PHPの日付および時間処理機能のすべてを本当に調べてみたい方には、膨大な量の読書が待っています。日付と時間に関するもっとも信頼のおける書とされている「PHP Architect's Guide to Date and Time Programminghttp://phparch.com/books/phparchitects-guide-to-date-and-time-programming/を1冊全部です。しかし正直に申し上げますが私はこの本をすべて読んだわけではありませんし、すべてを読む必要は今のところありませんでした。

最初に理解しなければならないのは、RPGの世界では日付、時間、タイム・スタンプは3つの別々のものであるということです。これから説明する理由により、PHPではそれが成り立たないのです。IBM iの世界では、上述のようなデータは真の意味でのデータ・タイプ(データベースの用語でいうタイプL、T、Zのフィールド)か、または文字列や数値として保存される傾向があります。しかし、これはPHPの世界では標準ではありません。PHPでは、日付のもっとも一般的な形式はUnix/Posixのタイム・スタンプ形式です。この形式はバイナリ値で、1970年1月1日午前零時からの経過時間を秒で表した数になっています。IFSを使用した経験をお持ちの方であれば、最後に使用した日などの日付をディレクトリのエントリに格納するときの基礎となっている概念なので、すでにご存じでしょう。

2桁の数字で年を表している方はお気づきになっていると思いますが、この値はウィンドウをベースに動作します。すなわち00年から39年までの範囲の年は21世紀(つまり2000年から2039年)を表すとみなし、40年から99年までの範囲の年は20世紀(つまり1940年から1999年)を表すとみなします。つまり、PHPの日付と同じです。タイム・スタンプは、通常4バイトのバイナリ値として格納されていますので、有限値、つまり2038年の初頭には「使い果たして」しまい、これは興味深いことに2桁で年を表しているウィンドウが使い果たされてしまう時期と近いのです。

PHPで日付を処理する別の方法があります。皆さんがコーディングする際にはこちらの方法を使用されることをお勧めします。特に2038年以降の日付を表現する必要がある場合はなおさらです。ただし、皆さんが目にする大多数のPHPスクリプトはこのタイム・スタンプ形式を使用しており、そのようなスクリプトがどのように動作するのかを理解しておくことは重要です。

PHPで日付を処理するときに理解しておく必要のある主な関数は以下の4つです。

  • time() ― 現在時刻のタイム・スタンプを返します
  • date() ― タイム・スタンプ形式の値を形式文字列に変換します
  • mktime() ― 日付/時刻の個々のコンポーネントからタイム・スタンプを生成します
  • strtotime() ― さまざまな入力形式からタイム・スタンプを生成します

まず、PHP関数の名前がたまたまRPGの関数の名前と一致しているからといって、両者が同じ処理をするということではありません。たとえばdate()関数は%DATE()関数と同じように見えますが、これは言語学でいう「空似言葉」と呼ばれているものです。ではまずdate()関数から調べてみましょう。この関数に本当の意味で等価な関数がRPGにはなく、ほとんどあらゆることができる驚くほど柔軟な関数なので、この関数について少し紙面を割いて説明します。形式に完全に則った日付(たとえば、May 15, 2010)を生成でき、曜日を決定でき、1月1日からの日数を計算できるなどまだまだできることがたくさんあります。皆さんが想像できることのほとんどをdate()関数が処理してくれます。

見た目の構文は簡単です。

date ( formatString {, timestamp } )

タイム・スタンプが省略された場合は、現在のシステムのタイム・スタンプが使用されます。例をいくつか下に示します。下記の出力例では現在が2010年5月30日の午後1:45であるという前提になっています。(形式文字列で使用できるすべての文字の完全な説明についてはphp.net/manual/en/function.date.phpにあるPHPのマニュアルを参照してください。)

echo '<br>Time in 24 hour format plus day and month: ', date('H:i:s - n/j');
// date() output is: 13:45:00 - 5/30
echo '<br>Today\'s date in M/D/Y format: ', date('m/d/y');
// date() output is: 05/30/10
echo '<br> And in a nicer form with four digit year: ', date('l jS F Y');
// date() output is: Sunday 30th May 2010

date()関数を使用していて遭遇する問題の1つに、使用できる形式文字があまりにたくさんありすぎて、日付を「綺麗に」したい時に予期せぬ結果となる場合があります。たとえば、上記の3番目の例で「Sunday the 30th of May, 2010」と出力したいとしましょう。単語の「the」と「of」を簡単に追加して以下のようなコードにしたくなるかもしれません。

echo date('l the jS of F, Y');

ここで問題となるのは、追加した文字のうちの1つの文字、すなわち「f」が形式文字ではないということです。他のすべての文字は使用可能ですので、その結果は「Sunday 3112America/New_York 30th 2010f May, 2010」といったようなものになります。これは望んでいたものとは全く違います。1つの解決方法としては、出力文字列に挿入したいあらゆる文字をエスケープしておくことです。そうするとコードは以下のようになります。

date('l \t\h\e jS \o\f F, Y');

皆さんはどうかわかりませんが、私はこのようにコーディングされると素早く読み取るのが本当に難しいと感じます。実際、「f」の前にはエスケープ文字が必要ないのにエスケープ文字を付けてしまっていることにお気づきかもしれませんが、これは単にコードを読みやすくするためなのです。一般的には、コロン「:」、スラッシュ「/」、ハイフン「-」等といった文字を挿入する以上のことをする際には、コンポーネントを抽出して連結や従来のPHP変数代入を使用して文字列を形式化する方が良いでしょう。たとえば以下のようなものになるでしょう。

$dayName = date("l"); // Long form of day name
$days = date("t"); // Days in the month
$month = date("F"); // Long form of month name
$today = date("j"); // Day number without leading zero
$suffix = date("S"); // Date suffix e.g. "st", "rd", etc.

echo "Today is the $today$suffix of $month. It is a $dayName and there are $days days in the month<br>";

タイム・スタンプの作成

作成したタイム・スタンプの中身の形式を整える方法について説明しましたので、そもそもタイム・スタンプをどのように作成するのかについて説明しましょう。RPGでは、%DATE()、%TIME()、%TIMESTAMP()などのBIFにパラメータを指定せずに使用してそれぞれの現在値を取得します。PHPではPHP関数time()を使用して現在のタイム・スタンプを取得しますが、これはすでにご存じの通りこれらの要素をすべて効果的に取り入れています。しかしPHPはmktime()関数も提供しており、この関数は前述のBIFと同様の値を返す、似たような機能を提供しています。mktime()の基本構文は次の通りです。

timestamp = mktime( hour, minute, second, month, day, year )

混乱させるといけないので上記の構文中には含めませんでしたが、1番目のパラメータを除く他のパラメータはすべてオプションで、省略された場合は代わりに現在のシステム日付/時刻から適切な値が使用されます。たとえば、

$startTime = mktime( 17, 30, 00, 5, 15 );

という式は、タイム・スタンプ$startTimeに対して今年の5月15日の午後5:30と設定します。 mktime()の興味深い側面は、指定されたパラメータが範囲外の場合(たとえば月の値として15が渡された場合)、この関数は素直にそれを処理しようとして必要に応じて年の値を調整してくれるのです。ですから次のようなコードを実行すると、

$startTime = mktime( 17, 30, 00, 15, 15, 2010 );

echo date('H:i:s - n/j/Y', $startTime);

年の値としては2010を指定したにもかかわらず、1年進められて2011となっているのにお気づきになるでしょう。上記のような場合には日付変換エラーを発行するというRPGの処理方法に比べて、この処理方法の方が良いのか私自身の中でまだ葛藤があります。しかし、たとえば年、月、日など何でも良いのですが、日付を進めるという方法は確かに有用ではあるのでこの方法の方が良いのではと心が傾きつつあります。しかし、月と年のパラメータを間違って逆に指定したためにデバッグ上問題が発生した経験があるのも事実です。

タイム・スタンプを作成するもう1つの方法はstrtotime()関数です。この小悪魔的関数は、生涯にお目にかかれるであろう、「私の言うとおりにして」的な関数でプログラマにとっての天国に最も近いものです。date()関数の場合と同様に、strtotime()関数の定義も外見的には簡単です。

strtotime( dateString { , relativeBase } )

しかしstrtotime()関数の能力は簡単などというものではありません。ある時点を表現するために正当な文字列表現であるとみなされるものは、そのほとんどをこの関数への入力として使用することができます。たとえば、「Tomorrow」、「Next Friday」、「-2 weeks」や「Last Monday July」でさえも正しい入力となります。夢中になって「the」や「of」などという単語を文字列中に入れない限り問題ないでしょう。PHPのマニュアル(php.net/manual/en/function.strtotime.php)には、使用できる略語やキーワードの全一覧が載っています。お望みであれば、返り値のタイム・スタンプをチェックして「true」となっているかどうかを確かめることで、入力が正当であるかどうかを確認してください。strtotime()関数が意図する日付に計算することができない場合は、返り値がゼロとなります。2番目のパラメータ(ここではrelativeBaseという名前を付けましたが)の目的は、「+2 weeks」などの多様なすべての要求のアンカー・ポイントを提供することです。このパラメータが省略された場合はシステムの現在時刻がベースとして使用されます。

さらに日付について

PHPでの日付処理サポートは広範でしかも発展を続けている分野です。たとえば、日付の期間について触れていないと思っている方もいらっしゃるかもしれません。これには理由が2つあります。1つ目の理由は、そうした計算の処理の仕方はそれこそPHPプログラマの数だけあるということです。Googleで自分のニーズに一番合った方法を探してみるのも良いでしょう。2番目の理由は、PHP 5.2から日付、時間、カレンダー機能全体が新しくなって加わり、日付の期間についても直接サポートするようになったということです。この新しい日付処理機能では8バイトの二進数値を使って、長期間の年数に渡る日付を保持することができます。どれくらい長期間かというと、PHPマニュアル中の該当する項目を見てみると「対象範囲は2920億年前から2920億年後まで」となっています。つまり、今日から2920億年後までの間に低温学的によほどの進化がない限り、そんな先のことについて心配する必要はないということです。

PHPのウェブ・サービス

ウェブ・サービスは日ごとにその重要性を増してきています。PHPはウェブ・サービスをサポートする組み込みの機能を持ってから数年たっており、ウェブ・サービスを簡単に利用できるようになっています。

本稿で示す例は、webservicex.netで利用可能なフリーの通貨変換サービスを使用しています。このサービスの詳細な記述については、同サイトのホーム・ページの「Currency Convertor」というリンクをクリックしてください。また同ページにはウェブ・サービス記述言語(WSDL:Web Services Description Language)ファイルの格納位置へのリンクもあります。ウェブ・サービスを使用する際に必要な情報はすべてこのWSDLファイルにあります。WSDLがどういうものかについて説明するのは本稿の範囲を超えていますが、オンラインでより詳細な情報が提供されております(SystemiNetwork.comに掲載されているスコット・クレメント氏の記事「RPG Consuming Web Services with HTTPAPI and SoapUI (記事ID 56532)」を参照ください)。

このサービスを使用するPHPのコードは簡単なので、その動作の説明とともにここ(図―1)に載せておきます。ただしその説明をする前に指摘しておきたいのは、私が使用したPHPサポートにはオブジェクトが含まれているという点です。本シリーズの冒頭に述べた通り、私はPHPのオブジェクト指向(OO: Object-Oriented)プログラミングについて説明するつもりはありません。ここで説明するのは、PHPスクリプトの中から他人が作ったオブジェクト(OPO: Other People’s Object)を使用する方法です。そのようなサポートを通常の手続き型プログラミングにどう取り入れるのかを説明するには、ウェブ・サービスが格好の例なのです。

ここで問題としているサービスはソープ(SOAP: Simple Object Access Protocol)を使用するので、私が使用したオブジェクトはSoapClientクラスに属すオブジェクトです。

実際の処理は、SoapClientクラスの新しいインスタンスを作成するところから始まります(図―1のC)。このサービスのWSDLファイルの位置が図―1のBで設定された%wsdlパラメータとして渡されている点に注意してください。これはこのファイルから抽出された情報で、サービスを利用しているコードがサービスとやり取りする方法を決定できるようにしています。

一旦インスタンスが作成されてしまえば、その中のメソッドのどれでも呼び出すことができます。実際、このオブジェクトにはConversionRateというメソッドがたった1つあるだけで、変換元通貨から変換先通貨への正しいレートを計算するために、変換元と変換先の通貨コードを入力として受け取る必要があります。ConversionRateメソッドを実際にどのように呼び出しているかは図―1のDを見ればわかります。このオブジェクトのメソッドが、配列で使用する「=>」記号ではなく「->」記号を使って参照されている点に注意してください。このメソッドは$currenciesというパラメータを渡されていますが、このパラメータは図―1のAで定義されています。このパラメータは、必要とするパラメータ名をキーとし通貨コードを連想値とする連想配列である点に注意してください。

では呼び出す必要のあるメソッドの名前とパラメータの名前をどうやって知ることができたのでしょうか。それは、WSDLファイルを勉強していればわかったのでしょうが、とても大変な勉強となっていたでしょう。WSDLは意地悪で厄介なものです。私はWSDLを勉強する代わりに、前述した記事にあるスコット・クレメント氏のアドバイスに従いSoapUIツールを使用しました。SoapUIのディスプレイの一部を図―2に示します。

このツールでメソッド(図―2のA)とパラメータの名前(図―2のB)を見つけるのは簡単です。同様に、このツール内からテストのリクエストを実行することで、換金レートが返されてくるインスタンス変数の名前を割り出すこともできました。

このウェブ・サービスは単純なオブジェクト($result)を返し、その中にConversionRateResultというインスタンス変数が含まれています。この変数の値が図―1のEで参照されている様子がお分かりいただけるでしょう。ここまでくれば、あとはこの変換レートを以後の計算でうまく利用するだけです。

ウェブ・サービスのまとめ

ご覧の通り、PHPでウェブ・サービスを使用するのは簡単で、しかも問題が生じた時でもウェブ上でたくさんの例があります。郵便局から正しい郵便番号を取得する場合でも、UPSの荷物の配送状況を確認する場合でも、株のポートフォリオの価値を調べる場合でも、PHPが貴重なツールであるということがお分かりいただけるでしょう。後述の「ウェブ・サービスのメソッドとインスタンス変数名の割り出し」では、メソッド名とインスタンス名の別の割り出し方法について説明しています。

例外とエラー処理

以前のSystemiNetwork.comの記事「RPGプログラマがPHPを好む理由トップ5 (記事ID 65131)」では、RPGとPHPの間に基本的なエラー処理および例外処理のモデルに類似性があると述べました。この話題について詳細には触れてきませんでしたので、ここでその基本的なことを復習してみたいと思います。

PHPがエラーを処理して表示する方法は、php.iniファイル中の設定によって管理されます。しかしその設定は、通常は新しいコードをテストするための設定にはなっていません。テスト中はどんなエラーもスクリプトの出力、つまりブラウザ・ウィンドウに表示して欲しいものですが、ほとんどのサーバーではそのような設定になっていません。定義されていない変数などといった警告もすべて表示させたいのです。しかしこれとて標準ではそういう設定にはなっていません。これはなぜでしょうか。それはそのようなエラーをブラウザ画面上に表示させようとすると、作成しているHTMLのレイアウトに混乱を生じさせ、その結果としてユーザーがそのメッセージを目にしないかもしれないからです。

メッセージを確実に表示させるために、私は各スクリプトの先頭に以下の2行を挿入しています。

error_reporting(E_ALL);
ini_set('display_errors', true);

Error_reporting(E_ALL)は、PHPに対して非推奨関数に関するものを除いてすべてのエラーと警告を知らせてほしいと伝えるためのものです。.iniファイルのdisplay_errors値をtrueに設定することで、PHPにそうしたエラーと警告メッセージを表示するように伝えています。開発段階においてはこうした設定は妥当なものですが、実運用環境ではエラーについて何らかの対処をしなければなりません。しかしどう対処すればよいのでしょうか。RPGでのオプションの1つは、プログラム状態サブルーチン(PSSR: Program Status Subroutine)を定義するというものです。等価なオプションをPHPが提供してくれます。自分で定義したエラー処理ルーチンを指定するために、set_error_handler()関数を使用してエラーが発生した時に呼び出したい関数を指定します(図―3のB)。特定のタイプのエラーが発生した時だけこの関数を呼び出したいと指定することも可能ですが、すべてのエラーに対してその関数を呼び出し、その後にそのルーチン内のロジックで特定のタイプのエラーに対するアクションを制御するのが普通です。

エラー処理関数の定義を図―3のAに示します。エラー処理関数は少なくとも2つのパラメータを受け取らなければなりません。1つ目のパラメータにはエラー・コード(エラーの重大度とエラーのタイプが記されています)、2つ目のパラメータにはエラーのテキストが含まれています。この他にオプションのパラメータが3つあります。エラーが発生したファイルの名前、関連する行番号、エラーが発生した時に使用されていたすべての変数とその値からなる配列を含んだパラメータです。ご想像の通り、最後のパラメータはサイズが大きくなりやすくあまり使用されません。私の例では最初の4つのパラメータを指定していますが、カスタマイズしたエラー表示ではファイルの詳細は使用していません。私のテスト・ルーチンのコードを図―3に示します。

カスタムのエラー・ハンドラー(図―3のB)を有効にする前にこのスクリプトを実行すると、ブラウザに以下の出力が表示されます。

Notice: Undefined variable: notKnown in /htdocs/Penton/Exception1.php on line 22
Warning: Division by zero in /htdocs/Penton/Exception1.php on line 24
Apparently nothing wrong

このハンドラーを有効にすると、出力は以下のように変わります。

There was a problem
Error: Undefined variable: notKnown at line 22
There was a problem
Error: Division by zero at line 24
Apparently nothing wrong

私が作成したエラー処理ルーチンはスクリプトの実行の終了については何もしませんので、スクリプトは処理を続け、「... nothing wrong」というメッセージを表示します。エラー・ハンドラーにdie()関数を追加することもできましたが、そうすると$notKnownが定義されていないというだけでスクリプトの実行が終了してしまい、それは私の意図するところではないからです。これを回避する方法の1つが、エラー・コードを調べてそれに対して反応するというものです。エラー・ハンドラーに数行追加することで(図―4)、重大でないエラーの場合はスクリプトの実行を継続し、重大なエラーの場合はスクリプトの実行を停止することができます。これは図―4のAで$errno変数とPHP定義の定数E_WARNINGとを比較し、この比較条件が成り立てばスクリプトを停止させている箇所でお分かりいただけると思います。その結果のブラウザ表示は以下の通りです。「... nothing wrong」というメッセージを表示させるコードに到達する前にスクリプトが終了していますので、このメッセージが出力されていない点に注意してください。

There was a problem
Error: Undefined variable: notKnown at line 22
Error ignored
There was a problem
Error: Division by zero at line 24
Script Terminated

これでRPGのPSSRと同等のものが実現できたことになります。これを次のレベルに持って行ってMONITORグループと同等のものについて説明する前に、PHPでは自分固有のエラーをわざと引き起こす簡単な方法が提供されていることについて触れておきます。私はこれをtrigger_error()関数を使用して、適切な目立つエラー・メッセージを与えることで実現しています。

E.g., Trigger_error('The sky is falling ... The sky is falling!!');

2番目のパラメータを指定して、発生させるエラーの重大度を決めることもできます。E_USER_ERRORは致命的、E_USER_WARNINGは致命的ではなくE_WARNINGと同じレベル、E_USER_NOTICEはE_NOTICEと同じレベルです。ユーザー定義のエラー・ハンドラーがない場合は、E_USER_ERROR 以外は致命的ではありません。

RPGのMONITORに相当するもの

ではMONITORグループはどうでしょうか。PHPにはMONITORグループに相当するものがあるのでしょうか。その答えは「Yes」でもあり「No」でもあります。Try/Catchグループの形式で直接相当するものがあり、これはPHPバージョン5で新しい例外モデルの一部として導入されたものです。ただしこれには1つ欠点があります。下位互換性を保つために、PHPの古い関数は必要な例外をスローするようには改良されてないのです。PHPはRPGと同様に、時として必要以上に古いお荷物を引きずるようです。この決断による結果、「ゼロによる除算」を発生させるコードをいくらtry/catchでラップしても、その例外をキャッチすることができないのです。これはつまりコードの置かれている文脈によっては、特定の状況に対して異なる反応をするエラー・ハンドラーを書くのが難しいということになります。場合によっては(たとえばインベントリの項目の平均値を分析するなど)、ゼロによる除算がそれなりに起こることを想定することがあります。あるいは別の場合には(たとえば請求書の税金を計算するなど)、コード内のどこか他でエラーが起こっていることを意味しているかもしれません。

したがってtry/catchモデルを使用したい場合は、古い関数が発生させたあらゆるエラーで例外がスローされるようにしておかなければなりません。幸いなことに、これは簡単にできます。try/catchブロックがキャッチできるような例外を既存のユーザー定義関数がスローできるように修正するだけです(図―5のA)。図―5はこのテクニックを使用して前述のエラー・ハンドラー(図―4)を修正したものです。

エラー文字列とエラー番号をパラメータとして例外に渡して、catchブロックで問い合わせできるようにすることができる点に注意してください。どういうわけか行番号をパラメータとして渡すことはできません。しかしキャッチした例外中で行番号を見たいので、行番号をエラー・テキストに連結しています。

次にtry/catchブロック中で「問題を起こしている」コードの実行をラップしています(図―6のA)。PHPのtryはRPGのMONITORに相当するものです。RPGでは、次に複数のON-ERRORブロックをコードし、各ブロックを異なるエラー・ステータスに関連付けることもあります。PHPでは1つの例外タイプに対して1つのエラー・ブロックしか許されていませんので、RPGの動作を真似するには異なるタイプの例外をスローしなければなりません。catchブロック内の特定のエラーの原因と必要な復旧策を確定した方がずっと簡単だと私はわかりました。

図―6のBで、適切なメソッドを呼び出すことでエラー・コードとテキスト値を例外オブジェクト$eからどのように抽出しているかがお分かりいただけると思います。Exceptionクラスで利用できるメソッドをチェックしてみると、そのメソッドには行番号を取り出す機能が含まれています。しかし取り出された値は例外をスローした行の値であり、元々ゼロによる除算をした行の値ではありませんから、この状況でいえば役に立ちません。

修正したスクリプトを実行すると以下のような出力になります。

Ignored the message: Undefined variable: notKnown at line 20
Caught exception - error code 2 with message text Division by zero at line 22

もちろん現実的には、おそらくcatchブロックを使用して変数$zに適切な値をセットしてスクリプトの実行を継続するでしょうが、これで話のつじつまがどう合っているかおわかりいただけると思います。

エラー処理の要約
PHPの例外処理機能についてほんの表面的な部分について説明しただけですが、その基本的な部分が、特に処理されていない例外のデフォルトの動作については、RPGの例外処理のそれに非常に近くマッピングされていることがお分かりいただけたと思います。これ以上についてはPHPのオブジェクト指向プログラミングの側面により深く入り込まなければならないでしょうが、それは本シリーズの範囲を超えていますので、PHPの例外処理とエラー処理機能のより詳細については皆さんご自身で探求してみてください。

配列に関する補足見解

先日カレンダー処理の例をいくつか見ていたら、RPGプログラマとして自分が思いつくべきであったと思うテクニックに出会いました。それは、RPGの1から始まる配列のインデックスとPHPのゼロから始まるインデックスの違いをどのようにして簡単に適応するかというものです。

問題となった例は、月の数字を使用して配列から適切な名前を取り出すようにと設計されていたものです。インデックスとして[$monthNumber - 1]のようなものを使用するのではなく、プログラマは単に配列のエントリを1から始めたいのです。

$monthNames = array ( 1 => 'January', 'February', 'March', ... );

配列の最初のエントリを強制的に1にすることで、以後のエントリはそれぞれの(正しい)位置に続きます。つまり配列の最初のエントリである0(ゼロ)のインデックスは使用されないままとなります。

こうしたインデックスをデータベースに格納するときは、自然とRPGのアプローチであるベース-1を使用することになります。このテクニックを使用することで、単に同じ値を使用してPHPの配列にアクセスすることができます。

まとめ

まだまだ話せることはいろいろあります。少し思い浮かべただけでも、PDFの作成、電子メールの送付、Excelのスプレッドシートの作成と更新などについても触れたかったのです。しかし書けば書くほど書きたい話題が頭の中に浮かんできてしまうので、最終的には編集者と私自身の正気を保つためにここで線を引かなければなりません。本シリーズで前述した通り、PHPに関する事柄について説明するために、たまにはこの神聖な紙面に戻ってきたいと思いますが、メインとなるシリーズはこれで終結としたいと思います。触れておくべき話題があったり、もっと説明してほしい話題があれば是非お知らせください。どうしたら良いか考えてみます。

筆を置く前に最後に1つ。多くの皆さんはSitepoint (sitepoint.com)をすでにご覧になったかと思います。もしご覧になっていないのであれば、是非ご覧になってみてください。最近たまたま幸運にもSitepointのソフト・コピーの特別バーゲンを見つけて本5冊をお得な価格で購入できました。その本を全部読んでみていずれもお金を出して買って良かったと思いました。その本には、PHP、MySQLそしてもちろんHTML、CSS、JavaScriptに関する確かなそして実践的な情報が載っています。是非お読みいただいて、できればオンラインのコースも受講してみてください。しかし値段のことに触れたことは内緒にしておいてください。値上がりするのがいやなので。

次回お逢いするまで、このシリーズをお楽しみいただいて私と同様PHPを楽しんでいただければと思います。

ページトップ

ボタン