Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

自習メモ〜ネットワークプログラミング

公開されているpdfをやってみるシリーズ(?)。今回はネットワークプログラミングということで、ftpモジュールを使ったファイルのアップロードです。
なお、確認のために、io:format/3で戻り値を全て表示させています。なお、filelib:is_file/1については、2007-05-14 - ます’s Diary - どうでもいい事100選を参考にさせてもらいました :-)

-module(ftp_upload).
-export([upload/6]).

upload(Host, User, Password, RemoteDir, LocalDir, FileList) ->
    {ok, Ftp} = ftp:open(Host),
    io:format("connecting ~p~n", [ftp:user(Ftp, User, Password)]),
    io:format("lpwd ~p~n", [ftp:lpwd(Ftp)]),
    io:format("pwd ~p~n", [ftp:pwd(Ftp)]),
    io:format("cd ~p~n", [ftp:cd(Ftp, RemoteDir)]),
    io:format("lcd ~p~n", [ftp:lcd(Ftp, LocalDir)]),
    io:format("lpwd ~p~n", [ftp:lpwd(Ftp)]),
    io:format("pwd ~p~n", [ftp:pwd(Ftp)]),
    lists:foreach(
        fun(File) ->
            io:format("uploading ~s(~p) => [~p]~n", [File, filelib:is_file(File), ftp:send(Ftp, File)])
        end,
        FileList),
    ftp:close(Ftp).

実行結果は以下のような感じ。

$ pwd
/home/shimooka/work/erlang
]$ ls
ftp_upload.erl
$ erl
Erlang (BEAM) emulator version 5.5.4 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.4  (abort with ^G)
1> c(ftp_upload).
{ok,ftp_upload}
2> ftp_upload:upload("ftp.example.com", "ftpuser", "ftppwd", "./html/", "./", ["ftp_upload.erl"]).

=INFO REPORT==== 6-Jun-2007::10:41:56 ===
The inets application was not started. Has now been started as a temporary application.
connecting ok
lpwd {ok,"/home/shimooka/work/erlang"}
pwd {ok,"/"}
cd ok
lcd ok
lpwd {ok,"/home/shimooka/work/erlang"}
pwd {ok,"/html"}
uploading ftp_upload.erl(true) => [ok]
ok
3>

おお、いい感じいい感じ。
やってることは大したことないんですが、コードやホスト名のtypoとか、どうでも良いところでハマって時間を食ってます。。。もうちょっとしっかりせねば。。。

「リファクタリングが必要」という兆候

リファクタリングPHPに限った話ではないですが、PHPを使っている場合に「どういった兆候が現れるとリファクタリングした方が良いか」といった話がまとめられています。


I have had to go through a php application recently which has given me more than one headache and has required me to use all my possible patience.

あまり目新しいものはないかも知れませんが、以下要点を訳してざっとまとめてみました。間違いがあれば指摘してください :-)

グローバル変数

大量のブラケット

  • 以下のようなコード
<?php
$variable['field']['subfield']['subsubfield1'] = 'value1';
$variable['field']['subfield']['subsubfield2'] = 'value2';
$variable['field']['subfield']['subsubfield3'] = 'value3';
$variable['field']['subfield']['subsubfield4'] = 'value4';
...
    • 配列をうまく使う
<?php
$variable['field']['subfield'] =
  array('subfield1'=>'value1', 'subfield2'=>'value2', 'subfield3'=>'value3', 'subfield4'=>'value4');
  • 違う例
<?php
$prop1 = $res['results'][0]['property1']
$prop2 = $res['results'][0]['property2']
...
    • 改善案はこんな感じ以下のコードは、上記配列に対して動作しません。追記を参照してください
<?php
list($prop1, $prop2...) = array_shift($res['results']);

全て配列にしている

  • 全てpublic扱いになってしまうので、オブジェクトを使おう
    • まあ、PHP5の話だということで。。。

コードの重複

  • コピペじゃなくて、関数化する

終わりのないswitch文

  • 300行のswitch文とか、パッと見て何やってるのか分からないようなコード
  • 適宜関数化する

非常に多くのファイルを修正する必要がある

  • 設計の問題。過度の密結合

ハードコードされた値

  • 定数を使うべき

インターフェースの不一致

  • 戻り値がまちまち
    • たとえば、処理の成否を返す場合、booleanを返したり、0/1を返したりしている
    • 戻り値は一定させる

追記(2007/06/06 16:40)

上にあるlist関数を使ったサンプルですが、正しく動作しません。亀本さん@アシアル、ありがとうございます〜 :-)
list関数は、「数値添字の配列」でのみ使用できます(http://jp.php.net/listにあるmysql_fetch_rowを使ったサンプルを参照)。上記の場合、$resが以下のような配列であれば、list関数が改善策になり得ます。

<?php
/**
 * $prop1 = $res['results'][0][0];
 * $prop2 = $res['results'][0][1];
 * ...
 */
$res = array();
$res['results'][] = array('prop1',
                          'prop2',
                          'prop3',
                          'prop4',
                          'prop5');
$res['results'][] = array('prop6',
                          'prop7',
                          'prop8',
                          'prop9',
                          'prop10');
while (list($prop1, $prop2, $prop3, $prop4, $prop5) = array_shift($res['results'])) {
    var_dump($prop1);
    var_dump($prop2);
    var_dump($prop3);
    var_dump($prop4);
    var_dump($prop5);
}

なお、連想配列の場合、extract関数が使えますが、取り扱い注意ということで。

<?php
$res = array();
$res['results'][] = array('property1' => 'prop1',
                          'property2' => 'prop2',
                          'property3' => 'prop3',
                          'property4' => 'prop4',
                          'property5' => 'prop5');
$res['results'][] = array('property1' => 'prop6',
                          'property2' => 'prop7',
                          'property3' => 'prop8',
                          'property4' => 'prop9',
                          'property5' => 'prop10');

// foreach ($res['results'] as $result) {
//     extract($result);
// でも良いけど、とりあえず1行で。。。「@」を書かないですむ方法ないかなぁ
while (@extract(array_shift($res['results']))) {
    var_dump($property1);
    var_dump($property2);
    var_dump($property3);
    var_dump($property4);
    var_dump($property5);
}