あかまこ
トップへ

PHPでつくる無限カレンダー

まず実行結果。

2018年11月
123
45678910
11121314151617
18192021222324
252627282930


ソースコードは以下。

<?php

/**
 * 手作りカレンダークラス
 */
class calendar{

    
// 本日の情報
    
public $today = array();

    
// 表示するカレンダーの情報
    
public $disp = array();

    
// 日曜始まりデフォルト
    
public $startWeek 0;

    
/**
     * インスタンス化されたときに実行される
     */
    
public function __constract(){
    }

    
/**
     * メンバ変数を初期化する
     */
    
public function init(){
        
self::setStartWeek();
        
self::setToday();
        
self::setDisp();
    }

    
/**
     * HTMLに出力する処理
     */
    
public function echoAll(){
        
$echos = array();
        
$echos[] = self::getTable();
        
$echos[] = self::getTitleTr();
        
$echos[] = self::getBeforeTrTd();
        for(
$day=1;$day<32;$day++){
            
$d str_pad($day2'0'STR_PAD_LEFT);
            if(
checkdate($this->disp['m'], $d$this->disp['Y']) === false){
                break;
            }
            
$ts strtotime($this->disp['Y'].$this->disp['m'].$d);

            
$week self::getWeek($ts);
            
$echos[] = self::getTr($week);
            
$echos[] = self::getTd($week);
            
$echos[] = $day;
            
$echos[] = self::getTdClose();
            
$echos[] = self::getTrClose($week);
        }
        
$echos[] = self::getAfterTrTd($ts);
        
$echos[] = self::getTableClose();
        return 
implode('',$echos);
    }

    public function 
setStartWeek(){
        if (isset(
$_REQUEST['sw']) && in_array($_REQUEST['sw'],array('1','0'))) {
            
// 0 : 日曜始まり, 1 : 月曜始まり
            
$this->startWeek = (int) $_REQUEST['sw'];
        }
    }

    
/**
     * HTMLに出力する年、月を決定している
     */
    
public function setDisp(){
        if (isset(
$_REQUEST['ym']) && strlen($_REQUEST['ym']) === && is_numeric($_REQUEST['ym'])) {
            
$this->disp['Y'] = substr($_REQUEST['ym'], 0,4);
            
$this->disp['m'] = substr($_REQUEST['ym'], 4,2);
            
$this->disp['n'] = $this->disp['m']+0;
        } else if (isset(
$this->today['Y'],$this->today['m'])) {
            
$this->disp['Y'] = $this->today['Y'];
            
$this->disp['m'] = $this->today['m'];
            
$this->disp['n'] = $this->disp['m']+0;
        } else {
            
$this->disp['Y'] = date('Y');
            
$this->disp['m'] = date('m');
            
$this->disp['n'] = date('n');
        }
        
$this->disp['ts'] = strtotime($this->disp['Y'].$this->disp['m'].$this->today['d']);
    }
    public function 
setToday(){
        
$this->today['Y'] = date('Y');
        
$this->today['m'] = date('m');
        
$this->today['d'] = date('d');
        
$this->today['n'] = date('n');
        
$this->today['j'] = date('j');
        
$this->today['ts'] = strtotime($this->today['Y'].$this->today['m'].$this->today['d']);
    }
    function 
getWeek($ts){
        
// 0 (日曜)から 6 (土曜)
        
return (int) date('w',$ts);
    }
    function 
getTable(){
        return 
'<table class="table table-sm table-bordered table-calendar">';
    }
    function 
getTableClose(){
        return 
'</table>';
    }
    function 
getTitleTr(){
        
$weeks = array('月''火''水''木''金''土''日');
        if (
$this->startWeek === 0) {
            
$weeks = array('日''月''火''水''木''金''土');
        }
        
$return '<tr class="calendar-title">';
        foreach (
$weeks as $i => $week){
            
$return .= self::getTd(($i+$this->startWeek === $i+$this->startWeek));
            
$return .= $week;
            
$return .= self::getTdClose();
        }
        
$return .= '</tr>';
        return 
$return;
    }
    function 
getBeforeTrTd(){
        
$firstWeek self::getWeek(strtotime($this->disp['Y'].$this->disp['m'].'01'));

        if (
$firstWeek === $this->startWeek) {
            
// 1日が左端からピッタリ埋まっていれば何もしない
            
return '';
        }

        if (
$this->startWeek === 0) {
            
// 日曜始まり
            // あと何個 td が必要か
            
$endI $firstWeek;
        } else {
            
// 月曜始まり
            // あと何個 td が必要か
            
$endI $firstWeek 1;
            if (
$firstWeek === 0) {
                
$endI 6;
            }
        }

        
$return '<tr>';

        
// その月の1日がくるまでtdを埋める
        
for($i=0;$i<$endI;$i++){
            
$return .= self::getTd($i+$this->startWeek);
            
$return .= ' ';
            
$return .= self::getTdClose();
        }
        return 
$return;
    }
    function 
getAfterTrTd($lastTs){
        
$lastWeek self::getWeek($lastTs);


        if (
$this->startWeek === 0) {
            
// 日曜始まり
            
if ($lastWeek === 6) {
                
// 末日が右端までピッタリ埋まっていれば何もしない
                
return '';
            }
            
// あと何個 td が必要か
            
$endI $lastWeek;
        } else {
            
// 月曜始まり
            
if ($lastWeek === 0) {
                
// 末日が右端までピッタリ埋まっていれば何もしない
                
return '';
            }
            
// あと何個 td が必要か
            
$endI $lastWeek;
        }

        
$return '';
        
// その月の末日がくるまでtdを埋める
        
for($i=1;$i<=$endI && $i<7;$i++){
            
$return .= self::getTd($i+$lastWeek === $i+$lastWeek);
            
$return .= ' ';
            
$return .= self::getTdClose();
        }
        
$return .= '</tr>';
        return 
$return;
    }
    function 
getTd($week){
        if (
$week === 6) {
            return 
'<td class="sat">';
        }
        if (
$week === 0) {
            return 
'<td class="sun">';
        }
        return 
'<td>';
    }
    function 
getTdClose(){
        return 
'</td>';
    }
    function 
getTr($week){
        if (
$week === $this->startWeek) {
            return 
'<tr>';
        }
        return 
'';
    }
    function 
getTrClose($week){
        if (
$this->startWeek === 0) {
            
// 日曜始まり
            
if ($week === 6) {
                return 
'</tr>';
            }
        } else {
            
// 月曜始まり
            
if ($week === 0) {
                return 
'</tr>';
            }
        }
        return 
'';
    }

    
/**
     * リンクを作成する。
     */
    
function getNextLink(){
        
// 次のページで現在表示されている年月の翌月を表示するためのリンク先のURLパラメータ
        
return 'ym='.date('Ym',strtotime($this->disp['Y'].$this->disp['m'].'01 + 1month'));
    }

    
/**
     * リンクを作成する
     */
    
function getPreLink(){
        
// 次のページで現在表示されている年月の前月を表示するためのリンク先のURLパラメータ
        
return 'ym='.date('Ym',strtotime($this->disp['Y'].$this->disp['m'].'01 - 1month'));
    }
}
$c = new calendar();
$c->init();

?>
<div class="row text-center mb-3">
    <div class="col-4">
        <a href="?sw=<?php echo $c->startWeek;?>&<?php echo $c->getPreLink();?>" class="btn btn-sm btn-outline-primary">前の月</a>
    </div>
    <div class="col-4 year-month">
        <?php echo $c->disp['Y'];?><?php echo $c->disp['n'];?>
    </div>
    <div class="col-4">
        <a href="?sw=<?php echo $c->startWeek;?>&<?php echo $c->getNextLink();?>" class="btn btn-sm btn-outline-primary">次の月</a>
    </div>
</div>
<div><?php echo $c->echoAll();?></div>
<div class="row">
    <div class="col-12 text-right">
        <a href="?sw=<?php echo $c->startWeek === '0' '1';?>&<?php echo $c->getNextLink();?>" class="btn btn-sm btn-outline-secondary"><?php echo $c->startWeek === '日曜始まり表示' '月曜始まり表示切り替え' ?></a>
    </div>
</div>


活字の雲


そうだWEBデザイナーになろう

SEIYU赤羽店でのアルバイトしていた24歳。
スーパーの品出しバックヤードでの仕事で商品管理システムのPOSを触る機会があった。
純粋にパソコンはや通信の事が不思議に思ったためパソコンの技術を身に着けてWEBデザイナーになるべく「WEBデザイナー初心者歓迎!」という求人をしていたシステム会社の面接を受けてみた。

私「デザイナー志望です!」
面接官「いま、デザイン部は人いっぱいだから、システム部ならいいよ」
私「(む、システムってなんだろ?まぁ、ひとまず入社できたら後からデザイン部に転向すればいいか・・) 何でもやります!」

パソコンの電源の付け方もままならないプログラマー未学習者(ほぼWindows未経験)として入社。
これが私のエンジニア人生のスタートです。

今思うと面接官にこう思われていたかも「こいつの服装だせぇ・・デザイナーむいてないっしょ」と。
エンジニアに向いている人の服装は、なんというか、こう、一種のもはや制服なのでは?とも思える傾向がありますからね。

放置プレー

勤務初日、どんな仕事が待っているのかと意気込み出社。
間もなく、私の意気込みとは真反対のテンションでヌボーっと現れた直属となる部長K。
初日ということで面談をし、聞くとK部長は年下ではないか、、さすがIT業界だな夢があるハハハなどと思いながらも何となく焦りを与えられた気になる。
これから更に絶望的な焦りを覚えることになるとは、つゆにも思わず。
自分の席についてつかの間、年下の部長からポイっと2冊の本を渡される。
「1週間で覚えるPHPプログラミング」みたいなPHPの初級学習本と、同レベルの「たのしいMysqlデータベース」みたいなの学習本。
そして、「これやっておいて」以上。
これが最初のお仕事です。

パソコンとは?WEBとは?プログラムとは?データベースとは?
学生が学ぶレベルの知識を会社で働きながら学ばせ得ていただいた。本当に恵まれた環境でした。感謝。

ただ、業務はできません。戦力外通告を受けたスポーツ選手の気持ちはこういうものかなぁ。
上司や周りの同僚からも、ほぼ「丸一日放置プレー」が数日間続く。
いっこうに進歩が見えない私の横で、たった3ヶ月前に入社した先輩は既に難しそうな案件をしている。
「・・これはまずい!」
プログラムのスキルを早く身につけねば、明日には席があるかはわからない。

独学ってなんだろう?

今となっては、よくお客様から「プログラムの学校通ったのですか?独学ですか?」と聞かれる。
専門学校にも大学にもいってない。
直前までスーパーの品出しをしていた。
先輩から技術的なことを教わることは無く、オフィスのビルの1Fにあった本屋で昼休みに書籍をひそかに購入した。
ただ、誰からも教わったり指導されたりしないことが独学と呼ぶのであれば、私の場合は違う。
周りの先輩エンジニアから一番重要なことを教えてもらえたので独学ではない。
ただし教えてもらったのは1つだけ。

私「すみません、ちょっとお時間よろしいでしょうか・・? PHPってサーバーにインストールするのでしょうか?」
先輩H「・・・ググレカス。」
私「なるほど!ありがとうございます!

私「すみません、ちょっとお時間よろしいでしょうか・・? アパッチからPHPを実行するのでしょうか?」
先輩S「・・・・ググレカス。」
私「御意!

ググレカスとはつまりGoogleで検索しろということ。
「PHP インストール サーバー」等と知りたいことをGoogleで検索すれば、そこに先生がいる、この世界に感謝です。
インターネット上に指導してくれる文献や、もはや答えが落ちているわけで、独学という概念は無い世界です。
私のような初心者プログラマーの疑問などは、世界のどこかで誰かがインターネット上に答えを掲載してくれているわけです。
逆にお手本にすることが沢山ありすぎるため、これは不要な情報だな、と判別する力が必要です。

活字の雲を漂う

ググレカスという言葉にショックを受けましたが、基本的には自分で調べる、社会人なら当たり前でなこと。初心者歓迎という言葉に甘えていた。
Googleで検索すれば先生がいる、とは言えなかなか理解できる回答しか得られない。
"PHP 変数" だの "オブジェクト指向" だのという未知の言葉を1つ調べると更に5つ分からない言葉が現れる。
現れた分からない言葉の一つを調べていくと、また5つ分からない言葉が出てくる・・・といった無限ループ、無限拡散に陥る
当時は 分からない ではなく 理解不能 というのが正しいでしょう。
何が分からないのかが分からないのだ。
何がを理解したかったのかさえ忘れてしまう。
意味の解らない言葉だらけで、まるで活字の雲の中を漂っているように迷子になり、何を調べていたのかもわからなくなる状態だった。
活字の雲の中から意味のある言葉の道筋を探す日々。

調べても調べても言葉が頭に入ってこない。
「・・・これはまずい!」
WEBデザイナー志望だったことを忘れてしまうほど焦りを感じていた。

無限カレンダーの思い出

先ほども申したようにググレカスというのは、自分でパソコンで調べろよということだが、実際は優しい先輩ばかりで、業務の合間に丁寧に的確に教えてくれました。
逆に、仕事ができる先輩たちは通常業務で忙しそうだから、こっちが質問し辛らかったというわけ。
必然的に独学するように追い込み、調べる力をつけさせる、嫌な人は辞めればいい、残った人員はスキルが上がっていく、計算された会社の組織だったのか。(プラス思考)
プログラマー初心者が、14年前にスパルタ(放置プレー)教育の中、最初の1週間で学習したのが「PHPで無限カレンダーをつくる」だ。
WEBサイト上で未来どこまでも続くカレンダーを作るにはどうしたらよいのだろうか? HTMLファイルを無限に作るってこと? 違うよなぁ。どうやらPHPで実現できるっぽいんだよなぁ。「PHP カレンダー」ググる。「PHP 日付け」ググる。「PHP URL パラメーター」ググる。
ネット上に落ちているソースコードのコピペでは納得いきませんでした。そしてうまく動かない。
落ちているソースコードをつなぎ合わせて 出来たー とぬか喜びするのではだめ。
SSHでローカルのテストPCに接続し、LINUX RedHat系をインストール、OS上にWEBサーバー構築、PHPをインストールして自分でHTMLもPHPもCSSも一からコーディングして作らないと意味がない。そうじゃないとクビになるのだと思っていた。というかそういうお題だった。
最初はつたなく汚く読みづらい PHP ソースコードだったはず。でも、自分の力で作ったカレンダーだ。と思うと単純に嬉しかった記憶がある。
プログラム初心者がPHPで作った自作カレンダーは、エンジニアへの道を後押しするには充分な成果物だった。

それからというもの「DBを用いてスケジュールを保存」したり、「保存したスケジュール内容を無限カレンダーに表示」したり、自分が欲しいと思う機能や処理(昨今、世に出回っているようなスケジュールアプリ)を実装・開発してくなかで、プログラムやネットワークの理解を深めていった。
当時のPHP4系の時代にはCakePHPやLarabelなどのフレームワークもあまり出回ってなく、SQLインジェクションやクロスサイトスクリプティング対策等のセキュアなコーディングまでは初心者には頭がまわりませんでしたが、スケジュールアプリ自体は半年はかからず作れた。
たぶん、切羽詰まればこの程度の事は誰でもできるんだと思う。
そんな当時を思い出して、いまごろ無限カレンダーをPHPコーディングしてみましたよと。
2018.4