SpotifyAPIでクイックプレイリスト作成ツールを作ってみた

 

お久しぶりです。長いことサボってました…😓
ちょっと怠けてる今日この頃です、、
残りの2019年気を引き締めて頑張ります🙌

 

サボってる間、何をしていたかというと、ただただライブに行きまくってました。
ライブのはしごとかもしちゃってました!疲れましたけど、幸せでした😇

行くライブは対バンが多かったので、ライブ前の予習にプレイリストを作ってました。(ライブ後に作ることもありますが)
わたしは横着なので、そのプレイリストを作ることさえ面倒でした。
そこで、アーティスト名を入力するだけで、Spotifyプレイリストを作成してくれるツールを作りましたー!✨

https://dj-kin29.herokuapp.com/

 

ぜひ対バンや小さめのフェス前に、全出演アーティストを入力してサクっとプレイリスト作って楽しんじゃってください✊!!

 

使い方

  1. https://dj-kin29.herokuapp.com/にアクセスし、各項目の入力をする。
    • アーティスト名 ( 必須 ) [5個まで入力可]
    • プレイリスト名 ( 任意 ) [default: dj-kin29-日時]
    • cookie使用の許可 ( 必須 )

  2. (初回のみ)Spotifyログインをし、アクセス許可を同意する。
  3. プレイリスト(非公開)が作成されます!!!
    リンクより、プレイリストに飛ぶことができます。

 

※ アーティスト名検索は、ヒットした一番上のものとするので意図しないアーティストでプレイリストが作成されることがありますので、ご注意ください ⚠️

 

余談ですが、ここで入力したアーティストは、今私がはまっているアーティストさん達です☺️
ぜひ聞いてみてください🎧⚡️

 

使ったもの

参考

 

SpotifyAPIの使い方や、実装内容はまた別記事で書きたいと思います!

 

Composerでrequireしたときのエラー解決法

 

どうも!名古屋生活満喫してます。
大須が好きで毎週行ってしまってます。
ただ、友達がいないので一人で大体いってますw
名古屋で遊んでくれる友達欲しい。。。

今回の記事は、
PHPerなら、とても助かっているであろうComposerについてです。
いろんなパッケージ使おうとrequireしてたら、なんか導入できない!
英語でわかわからんってなって諦めてました。
そろそろ次に進めなければと思い、私が問題解決を行った方法をまとめます。

 

composerコマンドだけで、できる方法はありそうなので見つけたら教えてください!
私も見つけたら書きます\(^^)/

 

Composerとは…

公式サイト:https://getcomposer.org/

PHPで何かを作るときに、
既存のライブラリやパッケージを導入/更新するときに使います。
そのとき、依存管理もしてくれるツールです。
JavaScriptでいうnpm的なやつだと思っています。

Composerの公式サイトに飛んだときに気づいたのですが、
ロゴのおじさん、いろんなカラーパターンがある!!!
リロードするたびに変化するのでぜひ遊んでみてください!

composer_uncle

(多分5パターン)

 

 

本題に入ります。

例えばこんな例

hautelook/alice-bundleをdev環境に導入する時….
下記のエラーで導入できなかった例です。

$ composer require hautelook/alice-bundle --dev
Using version ^2.5 for hautelook/alice-bundle
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for hautelook/alice-bundle ^2.5 -> satisfiable by hautelook/alice-bundle[2.5.0].
    - hautelook/alice-bundle 2.5.0 requires doctrine/orm ^2.5.11 -> satisfiable by doctrine/orm[2.5.x-dev, 2.6.x-dev, 2.7.x-dev, v2.5.11, v2.5.12, v2.5.13, v2.5.14, v2.6.0, v2.6.1, v2.6.2, v2.6.3] but these conflict with your requirements or minimum-stability.


Installation failed, reverting ./composer.json to its original content.

 

日本語でざっくり訳すと(わたしの解釈です)

hautelook/alice-bundleのv2.5.0(バージョン指定してないので最新版をインストールしようとします)は、
doctrine/ormのv2.5.0以上が必要です。

 

 

ということは、doctrine/ormが今v2.5.0以下なんです。
いざ、確認!!!

$ composer show doctrine/orm    //導入中のpackage(doctrine/orm)詳細を表示
name     : doctrine/orm
descrip. : Object-Relational-Mapper for PHP
keywords : database, orm
versions : * v2.4.7
...

 

エラー警告通りで、doctrine/ormのバージョンは、2.5.0以下の2.4.7でした。
ということで、現状のパッケージ群のままでhautelook/alice-bundleを導入する想定で問題解決していきたいと思います。

そんなとき使うのが、「packagist 」です!
packagistでは、composerで導入できるパッケージ群が管理されていて検索ができてバージョンごとの詳細も確認できます。

そこで導入したいパッケージ(hautelook/alice-bundle)を検索して、左側のバージョン選択欄からバージョンを下げながら見ていって、
導入ができない原因のパッケージ(doctrine/orm)のバージョン(現在v2.4.7)が通る導入したいパッケージ(hautelook/alice-bundle)のバージョンを見つけます。

\日本語下手でごめんなさい…/

packagist検索結果

 

この方法により
hautelook/alice-bundlev1.4.1であれば、
doctrine/orm: ^2.4doctrine/ormv2.4以上なので
導入できることがわかりました!

 

したがって、hautelook/alice-bundleを最新版ではなくv1.4.1をバージョン指定で導入してみます。

$ composer require hautelook/alice-bundle:1.4.1 --dev
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
  - Installing fzaninotto/faker (v1.8.0): Downloading (70%)
  ....

いけましたー!!!

$ composer show hautelook/alice-bundle
name     : hautelook/alice-bundle
descrip. : Symfony2 Bundle to manage fixtures with Alice and Faker.
keywords : Fixture, alice, faker, orm, symfony
versions : * v1.4.1
...

 

便利なComposerコマンド一覧

参考:https://qiita.com/KEINOS/items/86a16b06af6e936a1841

インストール済みのパッケージ情報の一覧を取得

$  composer show -i
You are using the deprecated option "installed". Only installed packages are shown by default now. The --all option can be used to show all packages.
...
phpunit/phpunit                    7.5.12   The PHP Unit Testing framework.

 

パッケージ名指定もできます。

$ composer show [vendor]/[package]
$ composer show phpunit/phpunit
name     : phpunit/phpunit
descrip. : The PHP Unit Testing framework.
keywords : phpunit, testing, xunit
versions : * 7.5.12
type     : library
license  : BSD 3-Clause "New" or "Revised" License (BSD-3-Clause) (OSI approved) https://spdx.org/licenses/BSD-3-Clause.html#licenseText
source   : [git] https://github.com/sebastianbergmann/phpunit.git 9ba59817745b0fe0c1a5a3032dfd4a6d2994ad1c
dist     : [zip] https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ba59817745b0fe0c1a5a3032dfd4a6d2994ad1c 9ba59817745b0fe0c1a5a3032dfd4a6d2994ad1c
path     : /path/vendor/phpunit/phpunit
names    : phpunit/phpunit
...

 

まとめ

英語のエラーって怖いって思いがちですが、
問題解決方法だったりヒントを表示してくれるだけでほんと助かるなーって気持ちに改めてなりました。

phpenvでいろんなPHPバージョンを操る

 

どうも、PHP大好きなんでPHPの記事ばっかで、すみません。

PHPってバージョンごとに書き方違うし、
レガシーな業務ではPHP5系だけど、個人ではPHP7系使いたいってことがよくあります。
そんなとき、composerで管理しちゃえばいいんですが、phpenv使ってバージョン切り替えできるとpsyshでも試せるので良いです!
そこで、phpenvでのバージョン切り替え方法をいっつも忘れてしまうので備忘録として書きます。

phpenvとphp-buildの導入

以下の記事を参考に導入しました。
phpenvとは別に、php-buildが必要なのを知らず私は導入に手こずりました^^;
https://qiita.com/ispern/items/97e3e6d910eb98b5de75

PHPのバージョンを切り替える

今回はグローバルのPHPバージョンを切り替える方法を記載します。
ローカルの場合はglobalの部分をlocalに置換すれば大体行けると思います。(多分)

▼現在のバージョンを確認

$ php -v
PHP 7.1.30 (cli) (built: Jul  4 2019 21:55:42) ( NTS )
...

 

▼現在適用されているphp.ini(設定ファイル)の確認
現在、phpenvで7.1.30が適用されいることがわかります。

$ php --ini
Configuration File (php.ini) Path: /Users/use_name/.phpenv/versions/7.1.30/etc
Loaded Configuration File:         /Users/use_name/.phpenv/versions/7.1.30/etc/php.ini
Scan for additional .ini files in: 
...

 

▼phpenvでインストールされているバージョン一覧と適用中バージョンの確認
*がついているのが、現在適用中のバージョンになります。
7.1.30の他に7.2.20を導入しています。

$ phpenv versions
  system
* 7.1.30 (set by /Users/user_name/.phpenv/version)
  7.2.20

 

▼phpenvでインストール可能なバージョンリスト一覧を表示

$ phpenv install -l
Available versions:
  5.2.17
...
  7.3.6
  7.3.7
...

 

▼バージョンを指定して、インストールをする。
今回は新たに、v5.6.1を入れます。
!!!時間かかります!!!

$ phpenv install 5.6.40
[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
...

 

インストールし終わったら、、、
▼インストール済みの一覧にあるか確認します。
5.6.1が新しく追加されています。

$ phpenv versions
  system
  5.6.1
* 7.1.30 (set by /Users/user_name/.phpenv/version)
  7.2.20

 

▼hashの更新をします。
!!!rehash忘れがちなので、忘れずに!!!

$ phpenv rehash

 

▼グローバルのPHPバージョンを切り替えます。

$ phpenv global 5.6.1

 

▼現在のバージョンを確認します。
見事に5.6.1に切り替えれています\(^^)/

$ phpenv versions
  system
* 5.6.1 (set by /Users/user_name/.phpenv/version)
  7.1.30
  7.2.20
$ php -v
PHP 5.6.1 (cli) (built: Jul 14 2019 18:29:23)
...

参考:https://www.aiship.jp/knowhow/archives/25452

まとめ

phpenvってpyenvって打ち間違えますよね!!!?
わたしだけですか?
(多分、みんなそうなはず)

 

これほしい!

DateTime::modifyに気をつける

 

どうも!実は、
この一ヶ月有給消化と言う名の
ニートしてまして、
明日から社会人に戻ります。。。
遅刻しないかとかすっごい心配です^^
新しい環境にdkwk(ドキドキワクワク)してます!

ちょっと遅刻に絡めて、
DateTime::modify
を使用する上で、気をつけるべきところを
備忘録として書いておきます。

 

※知ってる人は知ってるであろうことですし、
まあ、そりゃそうでしょって思う方もいると思います。

DateTime::modifyは使うたびに、
タイムスタンプを変更しています。
なので、何度も何度も使用すると予期せぬことに…

参考:https://www.php.net/manual/ja/datetime.modify.php

 

test.php

<?php

$d = new DateTime();
echo "{$d->format('Y-m-d')} \n";
// 本日、2019-06-30が起点になる

echo "{$d->modify('+1 days')->format('Y-m-d')} \n";
// 予想: 2019-06-30の1日後の「2019-07-01」

echo "{$d->modify('+1 month')->format('Y-m-d')} \n";
//予想: 2019-06-30の1ヶ月後の「2019-07-30」
$ php test.php
2019-06-30
2019-07-01
2019-08-01  //実は、2019-07-01の1ヶ月後になる!

$d->modify(‘+1 month’)の前に、
$d->modify(‘+1 days’)を実行しているために
このような結果になっています。

 

 

予想通りにしたいときは、
newし直したり、cloneしなおすといい感じになります。

<?php

$d = new DateTime();
echo "{$d->format('Y-m-d')} \n";
// 本日、2019-06-30が起点になる

echo "{$d->modify('+1 days')->format('Y-m-d')} \n";
// 予想: 2019-06-30の1日後の「2019-07-01」

/* ここから変更 */
$d_1 = new DateTime(); //newし直す
echo "{$d_1->modify('+1 month')->format('Y-m-d')} \n";
//予想: 2019-06-30の1ヶ月後の「2019-07-30」
$ php test.php
2019-06-30
2019-07-01
2019-07-30  //2019-06-30の1ヶ月後になる!(予想通り)

 

 

date_modify — DateTime::modify() のエイリアス

っていうのは、知らなかったです・・・。
時間を操るって癖があって難しいですねー
っていうことで、明日から遅刻しないように頑張ります!!!

 

Herokuのアドオン「Heroku Postgres」を使ってみる

 

どうも!
大好きな宮崎を旅立ちまして、名古屋市民になりました。
4日間荷物が来なかったので、
床に毛布一枚でねるという生活で腰が痛かったです。
荷物がきた時は嬉しくて、
4時間以内にすべてダンボールを開封し部屋完成させました。

Heroku好きなんですが、DB使えるの知らなかったです。
!! アドオンでPostgreSQLを使えるのです !!(無料枠あり)

使い方

1.Heroku Postgresの導入・データベースの作成

– コマンドより

$ cd [APP_NAME]
$ heroku addons:create heroku-postgresql:hobby-dev
Creating heroku-postgresql:hobby-dev on ⬢ [APP_NAME]... ⣾
Creating heroku-postgresql:hobby-dev on ⬢ [APP_NAME]... free
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pg:copy
Created [DATABASE_NAME] as HEROKU_POSTGRESQL_GRAY_URL
Use heroku addons:docs heroku-postgresql to view documentation

データベースが作成されます。
[DATABASE_NAME]の部分に具体的なデータベース名が入ります。

$ heroku pg:info
(node:6031) [DEP0066] ...
=== DATABASE_URL
Plan:                  Hobby-dev
Status:                Available
Connections:           0/20
PG Version:            11.3
Created:               2019-06-19 01:54 UTC
Data Size:             8.1 MB
Tables:                3
Rows:                  1/10000 (In compliance)
Fork/Follow:           Unsupported
Rollback:              Unsupported
Continuous Protection: Off
Add-on:                [DATABASE_NAME]

 

– Herokuダッシュボードより
Herokuダッシュボード > Heroku Postgresを導入したいAPPを選択後、
Resorce  > Add-ons に遷移する。
「Heroku Postgres」を検索し、プランを選んでをProvisionする
今回はフリープランの、「Hobby Dev – Free」を選びました。

データベースが作成されます。
[DATABASE_NAME]の部分に具体的なデータベース名が入ります。

 

2.データベース操作

– コマンドより
※ローカルPCより、HerokuPostgresに接続して直接SQL操作をしたい場合、
ローカルPCにpsqlコマンドが導入されていないとできませんので以下を参考に導入してください。
参考:https://qiita.com/ucan-lab/items/fb74af3d78e71407db7b

$ heroku pg:psql [DATABASE_NAME]  //データベースに接続
(node:6110) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
--> Connecting to [DATABASE_NAME]
psql (11.3)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.



-- テーブル作成
[APP_NAME]::DATABASE_URL=> CREATE TABLE member_tb (id serial PRIMARY KEY,user_id TEXT NOT NULL,display_name TEXT NOT NULL);
CREATE TABLE

[APP_NAME]::DATABASE_URL=> SELECT * FROM member_tb;
 id | user_id | display_name
----+---------+--------------
(0 rows)



-- レコード挿入
[APP_NAME]::DATABASE_URL=> INSERT INTO member_tb(user_id, display_name) VALUES ('abc123', 'taro');
INSERT 0 1

[APP_NAME]::DATABASE_URL=> SELECT * FROM member_tb;
 id | user_id | display_name
----+---------+--------------
  1 | abc123  | taro
(1 row)

 

– Herokuダッシュボードより
Datestoresにてデータベースを選択後、
Dataclips > Create a new dataclip

ここでは、読み込み(SELECT)のみ実行可能。
書き込み(CREATE TABLEやINSERT)はできませんでした・・・。

 

 

3.アプリとデータベースの接続例

データベースが作成されると自動で、
Setting > Config Varsに「DATABASE_URL」が追加されます。
これをgetenvで使用します!
※データベースが複数存在する場合、「HEROKU_POSTGRESQL_GRAY_URL」「HEROKU_POSTGRESQL_MAROON_URL」のように追加されます。

PHP Data Objects (PDO) 拡張モジュールを使いました。

<?php

$url = parse_url(getenv('DATABASE_URL'));
$dsn = sprintf('pgsql:host=%s;dbname=%s', $url['host'], substr($url['path'], 1));
$pdo = new PDO($dsn, $url['user'], $url['pass']);


$stmt = $pdo->prepare("SELECT * FROM member_tb WHERE id = ?");
$stmt->execute([$id]);
$result = $stmt->fetch();

 

参考:

http://neos21.hatenablog.com/entry/2018/12/06/080000
https://qiita.com/ucan-lab/items/fb74af3d78e71407db7b

まとめ

無料枠あるのすごいありがたいです^^
これでいっぱいアプリ作れますね。
わたしはHerokuPostgresを使ってLINEBOT作成中です。
近々、BOTの記事投稿できるように頑張りまーす!

複数チケットサービス検索用ライブラリを作って、はじめてのpackagist登録

どうもー!
5月なのに暑いですね、そして梅雨こないですねー
宮崎にも来る気配なしです。

 

今日も音楽フェスに行くのですが、
皆さん、チケットを取る際にどのチケット販売サービスを使っていますか?
どういう基準で選んでますか?
ポイントだったり会員ランクなどてしょうか。

 

入手困難なバンドなんかだと、
私の経験でよくあるのがA,Bというサービスではsold outにもかかわらず、
Cでは残りわずかで残っていたりするのです。
ありがたく即購入するんですが、
この事実に辿りつくまで、
サービスAで検索→sold outを確認
→サービスBで検索→sold outを確認
→サービスCで検索→残りわずかを確認
→嬉し恥ずかし焦る気持ちを抑える
→無事ゲット!!!(ゴール)

ゴールに行くまで3サイトも順番に回るのです。
順番や早さによっては、
先に早く見ておけば取れたかもしれないチケットを
失ってしまうのです。
そんな自分の経験から、複数サービス
(現在チケットぴあ、eplus、ローチケ)を
一括検索できるライブラリを作りました!
スクレイピングを使ってるので、
リニューアルとかされると困りますw

Github→kin29/ticket-hunter

 

Packagistにも登録済みですので、

$ composer require kin29/ticket-hunter

からも取得可能でございます。

 

今回、Packagistへの登録を初めてしました!

こちらを参考にさせてもらいました。

https://qiita.com/niikunihiro/items/fbd696e506e734782d8f

6. GithubのサービスフックにPackagistを登録する

は、自動になったのか勝手に登録された気がしました。

 

 

使い方は、簡単です!

  • 使いたい販売サービス名(TicketPia/Eplus/LawsonTicketのどれか、複数指定可)
  • キーワード(「雨のパレード」など)

を渡すだけです。

<?php

try {
  $ticketVendors = new Kin29\TicketHunter\TicketHunter(['TicketPia', 'Eplus', 'LawsonTicket'], '雨のパレード');
  $ticketVendors->echoJson($ticketVendors->getList());
} catch (\Exception $e) {
    die($e->getMessage());
}

出力結果はこんな感じです。

{
  "TicketPia": [
    {
      "title": "GRAPEVINE/雨のパレード〔愛知〕",
      "date_time": "2019/8/2(金)",
      "pref_id": "23",
      "pref_name": "愛知県",
      "stage": "名古屋クラブクアトロ",
      "sale_method": "先行抽選",
      "sale_status": "近日抽選受付2019/6/8(土) 10:00 ~ 2019/6/10(月) 18:00",
      "link": "http://ticket.pia.jp/pia/ticketInformation.do?eventCd=1922500&rlsCd=&lotRlsCd=27947"
    },
    ...
  ],
  "Eplus": [
    {...省略...},
  ],
  "LawsonTicket": [
    {...省略...},
  ]
}

販売前の場合、販売開始日を取得できるといいな
と今気づきました、issueに入れておこう!
日付のフォーマットも入れてないな、、、入れよう。
アウトプットしてると気づくことて多いですよね、大切。

 

このライブラリを作っていた矢先、先を越されたかのように、
Freaxがリリースれました、、便利ですね。

内容が被っていますが、ライブラリはなさそうなので作り続けました^^;

 

今後は、このライブラリを使ってAPIを作りたいなとおもっています。
BearSundayでまた作りたいなあと思っております。
さらに、検索フォームをSymfonyで作ろうかと思っています。

 

怠けないように宣言しておきます!

では、森道市場2019行ってきます!

 

 

Factoryパターン〜AbstractFactory〜

Head FirstのFactoryパターンの
例え話が「ピザ屋」のお話だったので、
すんごいピザが食べたくなり、ピザホール一人で食べました。
やっぱりマルゲリータですよね。
んで、追いオリーブオイル。

 

Factoryパターン一族

– Simple Factory[正確にはFactoryパターンではない]
– Factory Method
– Abstract Factory

今回は最後のAbstract Factoryについてです!

 Abstract Factory

オブジェクトを作成をカプセル化し、依存度を下げる。(Factory Methodと同じ)
FactoryMethodと異なり、オブジェクトコンポジションを使います。

具象クラスを指定することなく、
一連の関連オブジェクトや依存オブジェクトを作成するための
インターフェースを提供する。

→ 使い手は、抽象インターフェースを使って、
実際に作成される具体的な製品を知ることなく、
一連の製品を作成できる。

いいところ:
使い手は、具体的な製品の詳細から完全に分離されます。
→ 役割分担できて、個々のクラスの責任が小さくなる

 

クラス図はこんな感じ

 

 

Head Firstでは、
– 工場:ピザの食材を作る食材工場
– 製品:ピザの食材
を例としていました。

こんな感じ(PHPStormのクラス図作れるの教えてもらったので、早速)

PizzaIngredientFactoryクラスで、
関連する一連の食材(Dough, Sauce, Cheese, …)オブジェクトを
まとめてグループ化して作れるのがいいところです。

 

AbstractFactoryのサンプルコード

 

▶︎ PizzaIngredientFactory  ←抽象的な食材工場

interface PizzaIngredientFactory
{
    //実装クラスにピザの食材(製品)の作成方法は任す。
    public function createDough(): DoughInterface;
    public function createSauce(): SauceInterface;
    public function createCheese(): CheeseInterface;
       ...
}

 

▶︎ NYPizzaIngredientFactory (PizzaIngredientFactoryの実装クラス)

↑具象な食材工場

class NYPizzaIngredientFactory implements PizzaIngredientFactory
{
    //具体的なピザの食材(オブジェクト)を作成する。
    public function createDough(): DoughInterface
    {
        return new ThinCrustDough();
    }

    public function createSauce(): SauceInterface
    {
        return new MarinaraSauce();
    }
...

 

▶︎ DoughInterface ←抽象的な食材

interface DoughInterface
{
    public function getName(): string;
}

 

▶︎ ThinCrustDough (DoughInterfaceの実装クラス)

↑具象な食材

class ThinCrustDough implements DoughInterface
{
    private $name;

    public function __construct()
    {
        $this->name = "うっすい生地";
    }

    public function getName(): string
    {
        return $this->name;
    }
}

 

 

ただし、食材が追加された時
Product(MeetInterface, Bacon)の追加だけでなく、
PizzaIngredientFactoryと
PizzaIngredientFactoryを実装する全てのクラスを変更する必要があります。
→影響範囲が大きめです。

例: 肉(食材)を追加する場合

– PizzaIngredientFactory

interface PizzaIngredientFactory
{
    //実装クラスにピザの食材(製品)の作成方法は任す。
    public function createDough(): DoughInterface;
    public function createSauce(): SauceInterface;
    public function createCheese(): CheeseInterface;
   ...
+   public function createMeet(): CheeseInterface;
}

– NYPizzaIngredientFactory

class NYPizzaIngredientFactory implements PizzaIngredientFactory
{
    ...
+   public function createMeet()
+   {
+       //MeetInterfaceの実装クラスをインスタンス化する
+       return new Bacon();
+   }
}

 

 

 

ある程度、固定された一連のオブジェクトがあって、
それらを作成する必要がある時に使えそうですね。

 

 

Singleton(シングルトン)を使ってみる

 

今回は、ご存知デザパタの
第5章 Singleton 〜たった一つのインスタンス〜
について自分なりにまとめたいと思います。

 

シングルトンって音がかわいいですよね、「トン」とかがw

シングルトン(Singleton)とは

何も考えず、実装をしているとなんども同じインスタンスを生成しがちです。
・指定したクラスのインスタンスが絶対1つしか存在しないことを保証したい
・インスタンスが1つしか存在しないことを、プログラム上で表現したい

そんなときに、インスタンス生成コストを下げるためにも、
Singletonパターンを使います。

 

参考:
PHPで書き換えてくれてるソース
https://github.com/HappyDays-jQuery/GoF/tree/master/src/Singleton

 

 

こんな感じ

全体コード→https://github.com/kin29/DezaPata/pull/4/files

– Singletonクラス
– コンテキスト(使う側)

Singletonクラス

<?php

namespace DP\Singleton;

class Singleton
{
    /**
     * @var Singleton
     */
    //static宣言がされているため、
    //クラスのインスタンス化の必要なしにアクセスできる。
    //ただしprivateなので同クラス内のからしかアクセスできない
    private static $singleton;

    //privateなので、同クラス内からしかインスタンス生成をできない。
    //他のクラスからnew Singletonをするとエラーになる。
    //このクラスではgetInstanceがインスタンス生成をしている。
    private function __construct()
    {
        echo "create SingletonClassInstance\n";
    }

    //すでに$singletonにインスタンスがセットされている場合は、newしない。
    public static function getInstance()
    {
        //$singletonがstatic宣言されているため、
             //$this->singletonでアクセスできない。
        if (is_null(self::$singleton)) {
            self::$singleton = new Singleton();
        }
        return self::$singleton;
    }
}

 

 

contextSingleton.php ←コンテキスト(使う側)

<?php

use DP\Singleton\Singleton;

echo "Singletonクラスを普通にnewしてみる。\n";
new Singleton(); //たぶんおこられる。

外部クラスからnewすると
Singleton::__construct()がprivateなので怒られます。

$ php contextSingleton.php

Singletonクラスを普通にnewしてみる。
PHP Fatal error: Uncaught Error: Call to private DP\Singleton\Singleton::__construct() from invalid context in /Users/XXXXX/DezaPata/contextSingleton.php:10
Stack trace:
#0 {main}
 thrown in /Users/XXXXX/DezaPata/contextSingleton.php on line 10

 

 

contextSingleton.php ←コンテキスト(使う側)

<?php

use DP\Singleton\Singleton;

echo "getInstanceしてみる(instance1)\n";
$instance1 = Singleton::getInstance();
echo "もう一度、getInstanceしてみる(instance2)\n";
$instance2 = Singleton::getInstance();
echo "2回目のgetInstanceはインスタンス生成を行わないため、constructを通らないはず\n";
echo "(instance1 === instance2)なのか確認\n";
echo ($instance1 === $instance2) . "\n";

$ php contextSingleton.php

getInstanceしてみる(instance1)
create SingletonClassInstance
もう一度、getInstanceしてみる(instance2)
2回目のgetInstanceはインスタンス生成を行わないため、constructを通らないはず
(instance1 === instance2)なのか確認
1

Singletonクラスのインスタンスを得ることができるgetInsranceメソッドを
2回実行してみる。
1回目でインスタンス生成によりコンストラクラにて初期化でいを行い、
2回目はインスタンス生成していないことがわかる。
さらに、1回目と2回目のインスタンスは同じであることもわかる。

→ Singletonクラスは1つしか存在せず、
getInstanceで毎回同じSingletonクラスのインスタンスを得ることができました!!!

 

<<追記 2019-03-12>>

ただし、このままの方法ではタッチの差で、

if (is_null(self::$singleton))

判定をした場合には
インスタンスが1つ以上作られる可能性があります。

この対策としては二つがあります。
●同期化
●初期化時点でインスタンスを生成し、
getInstanceでは常にそのインスタンスを返すようにする。

class Singleton
{
    private static $singleton = new Singleton();

    private function __construct()
    {
        echo "create SingletonClassInstance\n";
    }

    public static function getInstance()
    {
        return self::$singleton;
    }
}

 

 

static宣言

HogeClass.php

class HogeClass
{
    public static $staticProperty = "staticなプロパティです。\n";

    public static function staticMethod()
    {
        return "staticなメソッドです。\n";
    }
}

 

staticなメソッド、staticなプロパティともに
インスタンス化せずとも外部からアクセスが可能である。

echo HogeClass::$staticProperty;  //staticなプロパティです。
echo HogeClass::staticMethod();   //staticなメソッドです。

 

HogeClass内からのアクセスもself::で行う。
$this->で行うと、エラーとなる。

PHP Fatal error: Uncaught Error: Using $this when not in object context in

 

※注意!

staticなプロパティはインスタンス化されたクラスオブジェクトから
アクセスすることはできない(static なメソッドにはアクセスできます)。

$HogeObj = new HogeClass;
echo $HogeObj->staticProperty;
//PHP Notice:  Accessing static property HogeClass::$staticProperty as non static in...
echo $HogeObj->staticMethod();   //staticなメソッドです。

 

 

イテレーターを(iterator)を使ってみる

 

今回は、ご存知デザパタの
第1章 Iterator 〜1つ1つ数えあげる〜
について自分なりにまとめたいと思います。

 

イテレーター(iterator)とは

何かの集合体を、数える人
→ 何かの集合体(=aggregate)つまり数える対象が必要
→ 何かの集合体(=aggregate)がいてこその役に立つ

参考:
PHPで書き換えてくれてるソース
https://github.com/HappyDays-jQuery/GoF/tree/master/src/Iterator

 

※PHPでは組み込みで複数のインターフェースがあるので便利!
Iteratorインターフェース
IteratorAggregateインターフェース ..など

何かの集合体(aggregate)と、それを数える人(iterator)を別クラスにすることで、
役割分担ができ、疎結合になる。
→ ただし、aggregateとiteratorを別にする必要がない場合は、
両方の役割をもつIteratorAggregateを使うこともできる。

 

こんな感じ

全体コード→https://github.com/kin29/DezaPata/pull/2/files

– Bookクラス
– BookShelfクラス(=aggregate)
– BookShelfIteratorクラス(=iterator)
– コンテキスト(使う側)

BookShelf(集合体)の中にBook(要素)があり、
BookShelfIteratorが本を一つ一つ数えていく感じです。

BookShelfクラス

<?php

/**
 * 何か(=本)の集合体[Aggregate]
 */
class BookShelf
{
....
    public function getIterator(): BookShelfIterator
    {
        return new BookShelfIterator($this);
    }
}

コンテキスト(使う側)

<?php 

$bookShelf = new BookShelf();
$bookShelf->appendBook(new Book('星の王子様'));
$bookShelf->appendBook(new Book('かいけつゾロリ'));
$bookShelf->appendBook(new Book('ミッケ!'));
$bookShelfIt = $bookShelf->getIterator();

while ($bookShelfIt->hasNext())
{
    echo $bookShelfIt->next()->getName() . "\n";
}

実行すると、appendした本の名前が入れた順番に1つずつ表示されます。

$ php contextIterator.php
星の王子様
かいけつゾロリ
ミッケ!

 

イテレータのいいところ

何かの集合体(=aggregate)や数え方(=iterator)を変更しても、
イテレータを使う側には変更の必要がない。

例)
変更前:数え方→集合体へ要素を追加した順番(FIFO)
変更前:数え方→集合体へ要素を追加した逆の順番(FILO)

<?php 

namespace DP\Iterator; 

/**
 * 何か(=本)の集合体を数える人[Iterator]
 *
 * Class BookShelfIterator
 * @package DP\Iterator
 */ 

class BookShelfIterator {

    private $bookShelf;
    private $index;
  
    public function __construct(BookShelf $bookShelf)
    { 
        $this->bookShelf = $bookShelf;
-       $this->index = 0;
+       $this->index = $this->bookShelf->getLength() - 1;
    }

    public function hasNext(): bool
    {
-       return $this->index < $this->bookShelf->getLength();
+       return $this->index >= 0;
    }

    public function next(): Book
    {
        $book = $this->bookShelf->getBookAt($this->index);
-       $this->index++;
+       $this->index--;
        return $book;
    }
}

実行結果

$ php contextIterator_FILO.php
ミッケ!
かいけつゾロリ
星の王子様

 

イテレータ、便利だしスマートですね!

しかし未だ、これイテレータパターン使えるっ
って言う場面に直面したことがないので、
何か集合体を見つけては、ソースに落とし込むとかして
そういう直感を養っていこうと思います。

デザインパターン入門を読み始めました。〜GoFとは〜

 

有名なやつですね。

存在はもちろん知っていましたが、
難しそうという思い込みで読んでいませんでした。

読まなくては!といういい機会をいただき読み始めました。
入門なのもあり、Javaを知らない私でも意外に読める感じでした!
(思い込みはだめですね。)

 

PHPerな方が、この本と一緒によむといいのが、
デザパタをPHPで書いてくれてるやつ → HappyDays-jQuery/GoF

 

今回は、この本の導入部分のまとめを
自分なりに書いていきたいと思います。

 

GoF(ゴフ)とは

\「GoF」ってよく聞くけど、なんですか?/
という、私の初歩的な疑問がありました。
「はじめに」部分に詳しく書いてくれてました。

開発する上でのよくあるパターンを「デザインパターン」という形で整理したのが、
GoF(ゴフ)と呼ばれた4人だそうです。※GoF = the Gang of Fourの略

もっと詳しく言うと、このGoFにより
オブジェクト指向における再利用のためのデザインパターン
という本がかかれました。
この本では、23個のデザインパターンに「名前」をつけ、
「カタログ」としてまとめられています。

 

–まとめ–
GoF(ゴフ)とは、
デザインパターンを整理してくれた4人の人たちの名称。

 

デザインパターンを学ぶ前に

デザインパターンとは、クラスやインターフェースの関係性である。

デザインパターンの目的は、プログラムを再利用可能なものにすること。
→プログラムを部品として再利用すること
→プログラムを常に、機能拡張や変更を加えていくものとして見る必要がある。

なので、デザインパターンの理解を深めるためには以下を考えると良い。
・どんな機能が拡張される可能性があるか
・その拡張が行われると、修正が必要になるクラスはどれか
・逆に、修正が不要なクラスはどれか

 

また、役割を理解する必要がある。
この本では、デザインパターンをドラマに例えて整理しています。

– 白雪姫(というドラマ) … デザインパターン
– 白雪姫(登場人物) … クラスA
– 王子様(登場人物) … クラスB

白雪姫には、りんごを食べて永遠の眠りについてしまうという役目があるように、
クラスAは役割がある。
また、王子様には、白雪姫にキスをして目覚めさせるという役目があるように、
クラスBには役割がある。
白雪姫が眠らなければ王子様も役目を果たせないように、
し役割をまっとうする必要がある。
登場人物全員が役割を果たしてドラマができあがるように、
デザインパターンもできるという感じでしょうか。

 

次は、第1章Iteratorについてまとめたいと思います!