babu_babu_babooのごみ箱

2013-04-29

PHPを学びながら

02:24

最近のPHPは、すごいな〜。


serialize()、unserialize()、

var_export()、eval()

json_encode()、json_decode()、

http_build_query()、parse_str()


json は扱えるし、連想配列さえもファイルにできる。

ちょっと押さえておきたい。


php で、URLのフラグメント以降を捕まえる方法

URL#xxxxx?yy=123

$

$yy = $_GET['yy'];

ホームページを作る

02:09

結局、担当は俺。「久慈地区斎場」のホームページを作った。

毎日プログラムを書いていた。

初めて、予約システムを書く。炉の予約状況を専用ページで更新できる。

MySQLにデータを書き込んでいる。なんとも大袈裟な仕様である

この程度なら、json ファイルを php で書き換えていたほうが楽?。

苦情・問い合わせに対しても専用ページで閲覧できるようにした。

もちろんIEはそこそこ無視してやった。検証するIEが無い!

SEO対策で、グーグルがやってくることを願う。


小遣いが欲しい!

せめて管理料ぐらいちょうだい!



追記

さっそく、ヤフーとグーグルが視察に来た。現在12位。

TomTom 2013/05/01 16:23 はじめまして。

この記事と関係ないコメントになって申し訳ないのですが、
以前babu_babu_babooさんが「教えて!goo」に投稿されていた
クッキーを使ったタイマー(http://oshiete.goo.ne.jp/qa/5984686.html)の
ソースを私のブログに引用させて頂きました。

内容としては、無名関数の中にある関数を使いまわしたいので、
無名関数をクラスに変更して、中の関数をprototypeに移動させるというものです。

無断で引用させて頂いたのですが、ご都合が悪ければ、削除したいと思います。
勝手なお願いで申し訳ないのですが、許可していただけたらありがたいです。

記事のURL
http://fanblogs.jp/ayzfqir5/archive/747/0

以上よろしくお願いします。

babu_babu_baboobabu_babu_baboo 2013/05/01 21:38 Tom さん、

はじめまして。
まず「許可」など必要ありません。大袈裟ですね〜。なんというか恥ずかしい。
そこにあるコードを眺めてました。正直な事を言うと、私はいろいろな資料がないとコードが書けません。
そこからのコピペです。なのでそのコードの元になったのは、私ではありません。
本来であればその人の了承が・・・・でも、その人の代弁致します。
ネットで拾えるものは、もう自由で良くね!かな?。

オブジェクト指向でプログラムを書く人は少ないですね。
で、その人に教わったことがあります。オブジェクト指向は、「状態の変化を表す」だとも教わりました。
xxx.prototype.padding の関数は、「オブジェクト」そのものの変化を表すモノでしょうか?
あちこち呼ばれると便利な関数でしかないように思いませんか?
そんな事を言うと、setCookie, getCookie などもそういうことになりますかね。

オブジェクト指向なら、始まりは大文字で始めるのが通例だそうです。
var TimerCookie=function(id){

確かに、昔は(つい最近まで)なんでも prototype に付けてましたけどね。
なので以前に書いたコードは、今見ると恥ずかしい。

最近は、arguments.callee は、使わない。(名前をつけて呼ぶ)
オブジェクトを作るときに、new を使わずに、
var obj = TimeCookie.create ();
のような形に。


私は、まだまだ未熟です。よりよいコードを書くために、精進しましょう!
最近仕事が忙しく、それが出来ないのが残念ですが。

TomTom 2013/05/02 00:41 ありがとうございます。

>オブジェクト指向なら、始まりは大文字で始めるのが通例だそうです。
確かにそうでした。私自身、久しぶりにオブジェクトで書いたので、忘れていました。

var obj = TimeCookie.create ();
まわりにプログラマがいないので、独学なんですが、newを使ったのは私の知識が古かったみたいです。

1ヵ所でしか使わない関数をどう扱うべきか考えたのですが、煩雑になるので、全部prototypeにしてしまいました。
公開しないで使うのであれば、別のアプローチをしたかもしれません。でも、むしろ公開するからこそ、
もっとしっかり突き詰めたソースであるべきだったのかもと思います。

>よりよいコードを書くために、精進しましょう!
そうですね!いろいろありがとうございます。

TomTom 2013/05/03 18:21 こんにちは。

2つ上(2013/05/01 21:38)のbabu_babu_babooさんのコメントの真意が理解できていなかったと思います。
あれは、オブジェクトにする必要はなかったですね。

反省を込めて新しい記事にまとめました。

「なんでもかんでもオブジェクト化すればいいってもんじゃないな。反省!」
http://fanblogs.jp/ayzfqir5/archive/753/0

ご指摘いただかなかったら、ひとりよがりの無駄なソースのままでした。
ありがとうございます。

babu_babu_baboobabu_babu_baboo 2013/05/03 21:33 Tom さん、

こんばんは。私の真意はそこでなかったり。(涙)&(笑)
私は、プロのプログラマではありません。
指導できるほどの技量もありません。
それを念頭に置いてください。


>オブジェクトにする必要はなかった
どこを境にしてオブジェクト指向的なコードにするとかしないとかではなく、
オブジェクト指向的なコードを書くのならです。

例えば、カウントダウンタイマーを「ストップウオッチ」だと仮定します。
時間を一旦止めるスイッチ、停止を解除するスイッチ、リセットスイッチ、表示する部分(これは微妙)。
これらが部品ですね。状態を変化させるための機能です。
これらが本来の prototype.xxxx の xxxx になるものだと思います。
addDay, getCookie, setCookie, padding は、どの部分にも属さない、便利な関数群です。

padding() は、.load () でしか呼ばれないのですから

timerCookie.prototype.load = (function (padding) {
 return function () {
  var text = '次回まで約';
  var s = (this.targetDay - (new Date).getTime()) / 1000 |0;

  if (s < 0)
   text = this.TIMEOUT_MESS;

  else {
   text += this.padding( s % 86400 / 3600 |0) + '時間' +
    this.padding( s % 3600 / 60 |0) + '分' +
    this.padding( s % 60 |0) + '秒です。';

   (function(obj){
    setTimeout (function(){ obj.load(); }, 1000);
    })(this);
  }

  document.getElementById( this.id ).innerHTML = text;
 };
})(function (n) { return n < 10 ? '0' + n: n; });


でもよかったり。とういうことで続く。

babu_babu_baboobabu_babu_baboo 2013/05/03 21:40 >「なんでもかんでもオブジェクト化すればいいってもんじゃないな。反省!」
で、Tom さんがお書きになったプログラムですが、ちょっと改造してみてください。
同じページ内で、目標とする時間が別々で、それぞれにリセット機能、ストップ機能、再開機能があるブロックが複数個あるやつ。

TomTom 2013/05/03 23:22 >私の真意はそこでなかったり

あれ?じゃあ、最初の解釈で合っていたのかな?
>1ヵ所でしか使わない関数をどう扱うべきか考えたのですが、煩雑になるので、全部prototypeにしてしまいました。
>公開しないで使うのであれば、別のアプローチをしたかもしれません。
この返事書いたときの読み取り方でよかったのかも。


今、真面目にリセット機能、ストップ機能、再開機能があるブロックが複数個あるやつを作りかけたんですが、
ストップ機能(その時刻のままで停止)をつけるのは大変なので、止めました。でも、それをやるなら、オブジェクトにした方が楽だと思います。

実際にオブジェクトというアプローチがいいかどうかは別にして、おっしゃりたい事は、
オブジェクトなら、そういう機能ごとに分けるべきでは、と言うことですよね。また、読み違えていたりして。


ただ、リセット機能、ストップ機能、再開機能があるブロックが複数個あるオブジェクトを作る場合、
僕ならストップした時刻をクッキーに書き込むために、setCookieをprototypeに置いて使いまわすと思います。
private関数とpublic関数に分けられるなら、setCookieをprivateにしたいですけど。


「なんでもかんでもオブジェクト化すればいいってもんじゃないな。反省!」に書いたとおり、あの場合は
オブジェクト化でない方法での、書き換えの方が早いので、効率を考えたらそれが正解だったのではないかと思います。
真意を読み違えていたとはいえ、そういう意味では新しい記事は間違っている事を書いているわけではないと自分では思っています。

いづれにしても、ここに書き込まなかったら、そういう発見はなかったかなと。

babu_babu_baboobabu_babu_baboo 2013/05/04 21:55 Tom さん、

ちょっと書いてみました。でもいい加減ですね。もうちょっと熟考すればよいのかも?

<!DOCTYPE html>
 <meta charset="utf-8">
 <title></title>

<style>
p.mess {
  font-size : large;
  width   : 30em;
}
.left {
  float:left;
}
.right {
  float:right;
}
</style>

<p class="mess">
 <span class="left"></span>
 <span class="right">
  <input type="button" value="start" onclick="countdown0[this.value] ();">
  <input type="button" value="stop" onclick="countdown0[this.value] ();">
  <input type="button" value="reset" onclick="countdown0[this.value] ();">
 </span>
  
</p>

<p class="mess">
 <span class="left"></span>
 <span class="right">
  <input type="button" value="start" onclick="countdown1[this.value] ();">
  <input type="button" value="stop" onclick="countdown1[this.value] ();">
  <input type="button" value="reset" onclick="countdown1[this.value] ();">
 </span>
  
</p>


<script>

(function () {
 
 function CountDownTimer (element, spanTime, id) {
  this.element  = element;
  this.spanTime  = spanTime;
  this.id     = id;
  this.targetTime = null;
  this.timerId  = null;
 }
 
 
 function padding (n) {
  return (n < 10 ? '0': '') + n;
 }


 function timeToString (time) {
  return [
   '次回まで約',
   padding (time % 86400000 / 3600000 |0), '時間',
   padding (time % 3600000 / 60000 | 0), '分',
   padding (time % 60000 / 1000 | 0), '秒です。'
  ].join ('');
 }
 

 function toTime (day, hours, min, sec, msec) {
  if (31 < day)
   throw new Error;
  return Date.UTC (1970, 0, (day || 0) + 1, hours || 0, min || 0, sec || 0, msec || 0);
 }

 
 function addDate (time, date) {
  return new Date (+(date || new Date) + time);
 }
 

 function setCookie (name, value, expires) {
  var cookie = [ encodeURIComponent (name) + '=' + encodeURIComponent (value) ];
  if (expires)
    cookie.push ('expires=' + new Date (expires).toUTCString ());
  document.cookie = cookie.join ('; ');
 }


 function getCookie (name) {
  var n = encodeURIComponent (name).replace (/\W/g, '\\$&');
  var v = document.cookie.match (new RegExp (n + '\\s*=\\s*(.*?)(?:[;,\\s]|$)'));
  return (v) ? decodeURIComponent (v[1]): '';
 }
 
 
 function loop (that) {
  var time = +that.targetTime - (new Date);

  if (time < 1) {
   that.stop (true);
   that.element.textContent = MESS_END;
  }
  else
   that.element.textContent = timeToString (time);
 }

 
 function reload (id) {
  var t = getCookie (id);
  return t ? new Date (Date.parse (t)): null;
 }
 
 
 function start () {
  if (! this.timerId) {
   if (! this.targetTime)
    this.reset ();

   this.timerId = setInterval (loop, INTERVAL, this); // No!! ie
   loop (this);
  }
 }
 
 
 function stop (sw) {
  var id = this.timerId;

  if (id) {
   clearInterval (id);
   this.timerId = null;
   if (! sw)
    this.element.textContent = MESS_STOP;
  }
 }
 
 
 function reset () {
  var id = this.id, t;

  this.stop ();
  this.targetTime = t = addDate (this.spanTime);
  this.element.textContent = MESS_READY;

  setCookie (id, String (t), addDate (toTime (SAVE_DAY)));
 }


 function create (element, spanTime) {
  var id = 'MyID' + (ID++), obj;
  
  if (arguments.length < 1)
   throw new Error;
  
  obj = new CountDownTimer (element, spanTime || toTime (1), id);
  obj.targetTime = reload (obj.id);

  return obj;
 }
 
 //---------------
 var ID     = 0;
 var SAVE_DAY  = 10;
 var INTERVAL  = 1000;
 var MESS_READY = '準備OK!!';
 var MESS_STOP = '一時中断しています';
 var MESS_END  = '終了しました';
 
 //---------------
 CountDownTimer.prototype.start = start;
 CountDownTimer.prototype.stop = stop;
 CountDownTimer.prototype.reset = reset;
 
 CountDownTimer.create = create;
 CountDownTimer.toTime = toTime;
 CountDownTimer.addTime = toTime;

 this.CountDownTimer = CountDownTimer;
 
}) ();

//__________________________________

var view_area = document.querySelectorAll ('p.mess > span.left');
var countdown0 = CountDownTimer.create (view_area[0]); // 1day
var countdown1 = CountDownTimer.create (view_area[1], CountDownTimer.addTime (0, 0, 0,10)); //12hours
countdown0.start ();
countdown1.start ();
</script>

babu_babu_baboobabu_babu_baboo 2013/05/04 21:58 書き忘れた。IEは無理です。
Firefox です。

setInterval の第3引数が、IEとは違います。
querySelectorAll も使っています。

babu_babu_baboobabu_babu_baboo 2013/05/04 22:06 書き忘れた。
コメントには12時間と書いておきながら、実際は10秒でした。
spanTime の最大値は、31日ぐらいです。

プログラムは、「変更に強く」そして「丈夫に作る」が基本。
細分化して構築していく感じです。

Object.create は、本来の書き方ではありません。

TomTom 2013/05/05 08:50 僕のコメントのために、わざわざスクリプトを書かせてしまって、ごめんなさい。

僕が書いて提示しなければいけないのに
>ストップ機能(その時刻のままで停止)をつけるのは大変なので、止めました。
大変失礼な事をしました。お詫びします。

おっしゃっていることがわかりました。(多分今度こそ)

なるほど、確かにこれなら padding や timeToString はライブラリ関数として他のソースにいつでも持っていけますね。

>僕ならストップした時刻をクッキーに書き込むために、setCookieをprototypeに置いて使いまわすと思います。
でも、function setCookie (name, value, expires) も提示していただいたようにすれば、ライブラリとして利用できる。

クラスを使いまわすという発想があったのですが、すべてを prototypeに入れる必要はないですね。
あちこち使いまわすライブラリ関数がクラスのメンバである必要はないわけです。

今度こそ、意図を読み取れているといいのですが。
今回は突然の書き込みだったのに丁寧に対応していただきありがとうございます。
僕には大変勉強になりました。

>Object.create は、本来の書き方ではありません。
了解です。

>よりよいコードを書くために、精進しましょう!
ありがとうございます。今後ともよろしくお願いします。

aposapos 2013/05/05 11:29 横から出しゃばりますが、

原則として関数は副作用のないように書きます。

function f (x) { var a = 1; return a + x; }
var r = f(2);

しかし、コードの規模が大きくなってくると、外部パラメータで関数の挙動を変えたい場合が出てきます。

var g = 0.9;
function f (x) { var a = 1; return (a + x) * g; }
var r = f(2);

ところで、上記は下記とほぼ同じです。

var g = 0.9;
function f (x) { var a = 1; return (a + x) * this.g; }
var r = f(2);

「ほぼ」と書いたのは、大事な点が異なるからです。下の方は関数呼び出し時の this 値を変更することができます。

var r = f.call({ g: 0.8 }, 2);

すると、今度は関数呼び出し時の this 値(外部パラメータ)になりうるものをまとめておく必要が出てきます。

function Ctx (g) { this.g = g; }
var r = f.call(new Ctx(0.8), 2);

ここまで来ると、関数と外部パラメータ群とを一緒にしておいた方が良いでしょう。

Ctx.prototype.f = function f (x) { return f.call(this, x); };
var r = new Ctx (0.8).f(2);

---

というわけで、関数は外部パラメータ(this 値)に依存するものと、外部パラメータに依存しないもの(副作用のないもの)とに分けられます。この場合の setCookie() と getCookie() は後者です。

後者のようなものに何か接頭辞を付けて持ち運ぶ必要があるなら、例えば HTTPUtil.getCookie() のようにすれば十分です。副作用のない関数群を -Util にまとめるのは近年の私個人の命名規則ですが、以下のように関数を切り離せることを意味します。

var getCookie = HTTPUtil.getCookie;
var r = getCookie(document.cookie, 'foo');

もし、-Util を継承する何かを作る必要があれば、Object.create() から作れば良い。

var HTTPUtil2 = Object.create(HTTPUtil);

コンストラクタがないので instanceof は使えませんが、その代わり isProrotypeOf() が使えます。

HTTPUtil.isPrototypeOf(HTTPUtil2); // true

小難しいことを言えば「何を抽象化しているのか」ですが、ここでは少し具体的なやり方を提案してみました。

TomTom 2013/05/05 17:24 aposさん、はじめまして。ありがとうございます。

>というわけで、関数は外部パラメータ(this 値)に依存するものと、外部パラメータに依存しないもの(副作用のないもの)とに分けられます。

今はよくわかります。私が、それを理解するために時間がかかってしまい、お二方に大変な労力を消費させてしまいました。

とても感謝しています!そして、ありがとうございます!

babu_babu_baboobabu_babu_baboo 2013/05/05 17:54 apos さん、
お久しぶりです。またやっちゃいましたか。

Tom さん、
いつも撃たれているのが私です。
今回は、create () の中にある、変数 ID のことを含んでいます。
皆んなが寝静まる頃に、訂正します。