20200524のPHPに関する記事は9件です。

PHP トレイトの検証

今回はトレイトのお話をしていきます。
公式ドキュメントとやっていることは同じかもしれませんが、
個人的にわかりやすいようにメモのような形で解説していきます。
1〜3は公式ドキュメントと挙動が同じですが、
4は個人的に気になったところを書いていきます。

1.基本的な使い方

test_sample.php
<?php

require_once(__DIR__ . '/HelloWorld.php');

$app = new say();

$app->sayHello();
$app->sayWorld();

?>
HelloWorld.php
<?php

trait HelloWorld
{
    public function sayHello(){
        echo 'Hello ';
    }

}

class say
{
    use HelloWorld;
    public function sayWorld(){
        echo 'world';
    }

}

?>
$ php test_sample.php
Hello world

useでトレイトを読み込めば使えます。

2.同じメソッド名の場合

test_sample.php
<?php

require_once(__DIR__ . '/HelloWorld.php');

$app = new say();

$app->sayHelloWorld();

?>
HelloWorld.php
<?php

trait HelloWorld
{
    public function sayHelloWorld(){
        echo '1';
    }

}

class say
{
    use HelloWorld;
    public function sayHelloWorld(){
        echo '2';
    }

}

?>
$ php test_sample.php
2

上記の場合は、sayクラスのメソッドが優先されています。

3.トレイトが複数の場合

test_sample.php
<?php

require_once(__DIR__ . '/HelloWorld.php');

$app = new say();

$app->sayHello();
$app->sayWorld();
$app->sayHelloWorld();

?>
HelloWorld.php
<?php

trait Hello
{
    public function sayHello(){
        echo 'Hello ';
    }

}

trait World
{
    public function sayWorld(){
        echo 'World';
    }

}

class say
{
    use Hello, World;
    public function sayHelloWorld(){
        echo '!';
    }

}

?>
$ php test_sample.php
Hello World!

複数の場合は、カンマ区切りでいけます。

4.コンストラクタとデストラクタは使える?

ここからが個人的に気になるところなので検証します

test_sample.php
<?php

require_once(__DIR__ . '/HelloWorld.php');

$app = new say();

$app->sayHelloWorld();

?>
HelloWorld.php
<?php

trait Hello
{
    public function __construct(){
        echo 'Hello';
    }

    public function __destruct(){
        echo 'world';
    }

}

class say
{
    use Hello;
    public function sayHelloWorld(){
        echo '_';
    }

}

?>
$ php test_sample.php
Hello_world

結論:コンストラクタもデストラクタも呼ばれる

ちなみに、トレイトを親クラスとして継承させるのは無理みたいです。
逆も無理です。

例1.
class say extends Hello

例2.
trait Hello extends say
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel newしたら「dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.61.dylib」エラーが出るときの解決法

laravel new アプリ名

とすると、

dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.61.dylib
  Referenced from: /usr/local/bin/php
  Reason: image not found
Abort trap: 6

とエラーが表示されました。

homebrewのバージョンが原因のようです。

brew update && brew upgrade

するとエラーになりました。権限なないことが原因のようです。

Error: Running Homebrew as root is extremely dangerous and no longer supported.
As Homebrew does not drop privileges on installation you would be giving all

下記のコマンドで権限を付与します。

sudo chown -R  /usr/local
usage: chown [-fhnv] [-R [-H | -L | -P]] owner[:group] file ...
       chown [-fhnv] [-R [-H | -L | -P]] :group file ...

再度homebrewのバージョンアップします。

brew update && brew upgrade

今度は成功しました。

そして、

homebrewをバージョンアップしたので、Laravelアプリを作成します。

laravel new アプリ名
Package manifest generated successfully.
Application ready! Build something amazing.

今度は成功しました?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPの基礎文法メモ

はじめに

最近私が勉強したPHPの基礎文法から個人的に覚えておきたいと感じた箇所をピックアップしていこうと思います。

開始タグ

改行して2行目以降に記述いていきます。
phpのみの記述の場合閉じタグは書きません。

<?php

終了タグ、閉じタグ

?>

出力

echo 'hoge';

echo
1つ以上の文字列を出力します。
リスト形式の引数を受け付け、返り値を持ちません。

print 'hoge';

print
文字列を出力します。
複数の引数を指定できません。また返り値は常に1となります。

echoとprintそれぞれの違いとは?

echoは複数の引数が取れるので以下のようなことができます。

echo  "str", $a,  $b,  $c;

printは引数を一つしか取れないのでこのようなことはできません。

.(ピリオド)

文字列の連結をします。
前後にスペースを空けると見やすくなります。

'hello' . 'world' . 'ok'

変数

$name = 'john';

定数(const)

定数は慣習的に大文字で、変数と違い$はつきません。
同じ名前で再代入できません。

const NAME = 'john';

定数(define)

define('NAME', 'john'); // NAMEにjohnを代入

constとdefineの違いは?

クラス定数はconst
それ以外はdefineで定義します。

PHP_EOL

ターミナルで改行をします。

$name = 'john';

echo 'Hello' . $name . PHP_EOL;
echo 'Hi' . $name . PHP_EOL . PHP_EOL;

PHP_EOLは定義済み定数です。
2個連続でつけると一行あけることができます。
\n と同じ意味ですが、\n は''(シングルクォーテーション)で使えません。

'(シングルクォーテーション)と"(ダブルクォーテーション)

変数を埋め込まない場合はどちらをつかってもOKです。

「$name」という文字列や"(ダブルクォーテーション)を出力したいときは、シングルクォーテーションを使う必要があります。

echo '$name'; // ○
echo "$name"; // x

echo 'hello!"bob"';

長めのテキストを表現する方法

<?php
  $text = <<<'EOT'
  ここに書いた文章の改行や字下げを保持したまま
     textという変数に
  代入してくれる
  EOT;
?>

'EOT' ←ここで必ず改行。コメントも書いてはいけません。
EOTでも”EOT”でもOK
”EOT”で変数も埋め込めます。

// 変数を展開しない書き方をnowdoc(ナウドキュメント)
$text = <<<'EOT'
// 変数を展開する方の書き方はheredoc(ヒアドキュメント)と呼ぶ
$text = <<<"EOT"

EOTは終端記号と呼ばれます。
好きな名前でいいが例では「EndOfText」という意味でEOTとしています。

データ型

データ型 具体例
文字列(string) 'hello' "world"
整数(int) 5 -20
浮動小数点数(float) 3.2 -1.8
null null
真偽値(bool) true false
配列(array) [4, 1, 7]
オブジェクト(object) new object()

今扱っている変数の型がどの型か調べる

var_dump() 

<?php

$a = 'hello';
$b = 15;
$c = -2.8;
$d = null;
$e = true;

var_dump($a);
var_dump($b);
var_dump($c);
var_dump($d);
var_dump($e);

実行結果

~ $ php main.php
string(5) "hello"
int(15)
float(-2.8)
NULL
bool(true)

型を変換したい場合

<?php

$a = 'hello';
$b = 15;
$c = -2.8;
$d = null;
$e = true;

// 値の前に () で型を指定してあげれば OK
$a = (float)15;
$b = (string)2.8;

var_dump($a);
var_dump($b);

実行して確認

~ $ php main.php
float(15)
string(3) "2.8"

変数のスコープ

PHPでは関数の外で定義された変数を使うことは出来ないというルールがあります。
そこで変数に対して関数の外で定義されたという意味でglobalを付けます。

以下は買い物の計算結果(税込)を表しています。

<?php

$rate = 1.1; // 10%の税

function sum($a, $b, $c)
{
  global $rate; // 関数の外で定義された変数
  return ($a + $b + $c) * $rate; // 計算結果に税をかける
}

echo sum(50, 100, 150) + sum(200, 400, 600) . PHP_EOL;

しかしこのglobalを使うとコードが長くなった時にどこで変数の値が変更されているか追いにくくなります。

そこでこの関数内で使う変数は引数で渡してあげるか、その関数内で定義します。

<?php
######関数の外######
$rate = 1.1; // グローバルスコープ(関数の外全てで有効)
######関数の中######
function sum($a, $b, $c)
{
  $rate = 1.08; // ローカルスコープ(関数の中のみ有効)
  return ($a + $b + $c) * $rate; // 計算結果に税をかける
}

echo sum(50, 100, 150) + sum(200, 400, 600) . PHP_EOL;

こちらの計算結果は$rate = 1.08;が有効になり、
1.08の税率がかけられ1620になります。


このような変数の有効範囲をスコープと呼びます。

関数の外にあるスコープは関数の外の全てで有効でグローバルスコープと言います。
関数の中にあるスコープはその関数内のみで有効でローカルスコープと言います。

定数のスコープは?

PHP: 定数 - Manual

定数のスコープはグローバルです。 つまり、スコープによらずスクリプトの中ではどこでも定数に アクセスできます。

returnとは?

関数内でreturnと書くとその時点で値が戻されて、それ以降の処理は無視されます。

returnした時点で関数の呼び出し元に処理が戻り、結果として「関数内のそれ以降の処理」が実行されないということになります。returnは関数内でしか使えません。

exitとの違い
exitはその時点で処理を終了してそれ以降は実行しない。関数の中でも外でも使えます。

無名関数

以下の関数は現在、3つの値を渡すと合計を返してくれる状態。
この関数を値として扱うことができるので、

<?php

function sum($a, $b, $c) {
  return $a + $b + $c;
}

変数に代入します。

$sum = function ($a, $b, $c) { //無名関数
  return $a + $b + $c;
};

何をしたか説明します。

function sum($a, $b, $c) { // 変更前

$sum = function ($a, $b, $c) { // 変更後

名前はわかりやすく同じにします。
代入するのは処理そのものだけにしたいので
functionの後の名前は不要になります。
この時functionの後ろにスペースを開ける必要があるので注意してください。

return $a + $b + $c;
} // 変更前

return $a + $b + $c;
}; // 変更後

また、普通の関数は最後に;(セミコロン)は不要ですが、
このように値として代入する場合は必要です。

その上でこの関数を実行したい場合は、変数名に()を付けて引数を渡せばOKです。

$sum = function ($a, $b, $c) { // 無名関数
  return $a + $b + $c;
};

echo $sum(100, 300, 500) . PHP_EOL; // 引数を渡して実行

この関数には名前がないので無名関数、もしくはクロージャーと呼びます。

上の関数とどちらを使っても結果は同じですが、無名関数を使えるようになると
関数自体を別の関数に引数にして渡すことができたりと便利になります。

条件演算子

まずは、合計がマイナス値だったら0を返す
計算結果を変数に入れてあげて条件分岐する関数があるとします。

function sum($a, $b, $c) 
{
  $total = $a + $b + $c; // 合計値を$totalに代入

  if ($total < 0) {
    return 0; // 0を返す
  }  else {
    return $total; // 計算結果を返す
  }
}

echo sum(100, 300, 500) . PHP_EOL; // 900
echo sum(-1000, 300, 500) . PHP_EOL; // 0

実行結果

~ $ php main.php
900
0

この条件分岐を条件演算子でもっと短く書くことが出来ます。

function sum($a, $b, $c) 
{
  $total = $a + $b + $c;

  // if ($total < 0) {
  //   return 0;
  // }  else {
  //   return $total;
  // }
  return $total < 0 ? 0 : $total; // 条件 ? 値 : 値
}

echo sum(100, 300, 500) . PHP_EOL;
echo sum(-1000, 300, 500) . PHP_EOL;

たった一行にすることができます。
[条件 ? 値 : 値] という記法で書きます。
trueだったら左の値、falseだったら右の値に条件分岐します。

条件演算子は短く書けますが、短所としては一見して見づらいという点があります。

型付け

引数の型を指定する方法です。

<?php

function showInfo(string $name, int $score): void
{
  echo $name . ':' . $score . PHP_EOL;
}
// このように名前とテストの点数を表示したかったとする
// showInfo('john', 40);
// しかし今回は以下のように予期しない引数を渡してしまったとする
showInfo('john', 'canada');

function showInfo(string $name, int $score): void
引数の前に型の名前をこのように指定すると、
文字列と整数値だけしか受け付けたくないという指定ができます。

なお、返り値の型も指定することができて:のあとに書けばOKです。

返り値がない場合はこのように:voidという特殊なキーワードを使います。

それでは実行してみます。

Fatal error: Uncaught TypeError: 
Argument 2 passed to showInfo() must be of the type int

このようなエラーが出てintがくるべきところにstring、つまり文字列が来てしまっているというのがわかります。

型付けには強弱がある

このような型指定はデフォルトでは弱い型付けという方式になっていて、
違った型でも可能な限り正しい型に変換してしまうという性質があります。

intの部分に文字列の'6'等を渡してもPHPが数値に変換して実行します。

もし変換してほしくない場合は強い型付けをする必要があります。
declare文を最初につけます。

<?php

declare(strict_types=1); // 強い型付けを指定

function showInfo(string $name, int $score): void
{
  echo $name . ':' . $score . PHP_EOL;
}

showInfo('john', 'canada');

こちらを実行すると以下のようなエラーが出ます。

Fatal error: Uncaught TypeError: Argument 2 pa
ssed to showInfo() must be of the type int, st
ring given,

Argument 2:2番目の引数
2番目の引数がintじゃないと言ってくれています。

nullも一緒に渡せる

function showInfo(?string $name, ?int $score): void

型付けの前に?をつけることでnullかstringかという意味になります。
条件分岐や引数の型につけることができます。
ただし、整数か文字列かのような指定はできません。
あくまでnullかそれ以外かという指定にしか使えません。

配列

例えばゲームのスコアを管理したいとして、
最初のスコアが85点、次が30点、次が100点だったとします。

<?php

$score1 = 85;
$score2 = 30;
$score3 = 100;

これらは同じ意味合いのデータなのでまとめて同じ名前で扱えたほうが便利になります。

<?php

$scores = [85, 30, 100];

このように複数形のscoresという変数名にまとめることができます。
これを配列といいます。

また以下のような表記も可能です。

$scores = [
  85,
  30,
  100,
];

このように改行を挟んでもOKで、最後に,を入れてもOKとなります。
こうすると全ての行が同じ形式になって追加や削除、入れ替えがしやすいです。

それぞれの要素にアクセス

変数名に大括弧をつけて、その値が何番目かというインデックスを与えます。

echo $scores[1] . PHP_EOL;

実行結果

~ $ php main.php
35

インデックスは0から始まるので35が表示されます。
与えたインデックスを添字と言います。

値の変更

インデックスを使い代入すればOKです。

$scores[1] = 60;
echo $scores[1] . PHP_EOL;

実行結果

~ $ php main.php
60

配列のキーを指定

配列の中身を表示するにはvar_dump()か変数に関する情報を見せてくれる print_r() という命令を使います。

<?php

$scores = [
  85,
  35,
  100,
];

var_dump($scores);
print_r($scores);

実行結果

~ $ php main.php
array(3) {
  [0]=>
  int(85)
  [1]=>
  int(30)
  [2]=>
  int(100)
}

Array
(
    [0] => 85
    [1] => 35
    [2] => 100
)

値にアクセスするための 0 からの連番を好きな数値や文字列にすることができます。

<?php

$scores = [
  'first'   => 85,
  'second'  => 30,
  'third'   => 100,
];

var_dump($scores);
print_r($scores);

実行結果

~ $ php main.php
array(3) {
  ["first"]=>
  int(85)
  ["second"]=>
  int(30)
  ["third"]=>
  int(100)
}
Array
(
    [first] => 85
    [second] => 30
    [third] => 100
)

これで連番がキーに変わっています。
'first' 'second' 'third' に当たる部分を値に対するキーと呼びます。

個々の値にアクセス

<?php

$scores = [
  'first'  => 85,
  'second' => 30,
  'third'  => 100,
];

echo $scores['third'] . PHP_EOL;

実行結果

~ $ php main.php
100

配列は値とキーがセットになったデータ構造で、
キーを指定しなければ 0 からの連番がキーになります。

配列 array() と []

古い書き方が array() 新しい書き方が []
違いはありません。

PHP: 配列 - Manual

PHP 5.4 以降では配列の短縮構文が追加され、 array() の代わりに [] を使えるようになりました。

foreachで配列を操作する

as を使うことで配列のすべての要素に対して何らかの処理を行います。

<?php

$scores = [
  'first'  => 85, 
  'second' => 30, 
  'third'  => 100,
];

foreach ($scores as $score) {
  echo $score . PHP_EOL;
}

個々のスコアという意味で単数形のscoreと付けました。
$scoreの部分の変数名は好きに付けられます。

実行結果

~ $ php main.php
85
30
100

\$scores から値を一個ずつ取り出して $score に代入してから、処理を実行してくれます。

キーも合わせて表示する場合

<?php

$scores = [
  'first'  => 85, 
  'second' => 30, 
  'third'  => 100,
];

foreach ($scores as $key => $score) {
  echo $key . ' - ' . $score . PHP_EOL;
}

foreach ($scores as $key => $score)
このキーの変数名に \$key が入ってきます。

echo $key . ' - ' . $score . PHP_EOL;
\$score と合わせて以下のように表示します。

~ $ php main.php
first - 85
second - 30
third - 100

配列とforeachはよく合わせて使うことがあります。

配列の要素を展開する

別の配列で管理していた値を途中に入れ込みたい場合の説明をします。

数値だけでなく別のデータ値(文字列)や配列も入れることができます。
...を付けると配列の中身が入れ込まれます。
配列の中に配列を入れたり、別のデータ値を入れることもできます。

<?php

$moreScores = [
  48,
  71,
  'excellent',
  [55, 29, 84]
];

$scores = [
  85,
  30,
  ...$moreScores, // 配列の中身が入れ込まれる
  100,
];

print_r($scores);

実行結果

~ $ php main.php
Array
(
    [0] => 85
    [1] => 30
    [2] => 48
    [3] => 71
    [4] => excellent
    [5] => Array
        (
            [0] => 55
            [1] => 29
            [2] => 84
        )

    [6] => 100
)

84にアクセスしたい場合は?

配列の5番目の中の2番目の要素になるので、

echo $scores[5][2] . PHP_EOL;

これでアクセスできます。

可変長引数

(...仮引数)にすると渡された全ての引数を配列にして、代入してくれます。
このような引数を可変長引数と言います。

<?php

function sum(...$numbers) // 可変長引数
{
  $total = 0;
  foreach ($numbers as $number) {
    $total += $number;
  }
  return $total;
}
echo sum(1, 3, 5) . PHP_EOL;
echo sum(4, 2, 5, 1) . PHP_EOL;

実行結果

~ $ php main.php
9
12

順に説明をします。

function sum(...$numbers)

これが可変長引数です。関数の引数が何個きても合計を出すことができます。

$total = 0;

ここで合計を初期化します。

foreach ($numbers as $number) { 

それぞれの要素を$numberで取り出します。

$total += $number;

$totalに足していきます。

return $total;

合計値が返されます。

複数の返り値を受け取る方法

配列を返り値にすることもできます。
合計だけでなく平均も返すようにして、配列を作ります。

<?php

function getStats(...$numbers)
{
  $total = 0;
  foreach ($numbers as $number) {
    $total += $number;
  }
  return [$total, $total / count($numbers)];
}

print_r(getStats(3, 5, 7));

return [$total, $total / count($numbers)];
返り値は配列で1つ目の要素が\$total 2つ目の要素を平均にしたいので
2つ目はこのように\$totalを$numbersの個数で割ります。

count

配列の個数を求めるためにcount()を使っています。

count — 変数に含まれるすべての要素、 あるいはオブジェクトに含まれる何かの数を数える
https://www.php.net/manual/ja/function.count.php

実行結果

~ $ php main.php
Array
(
    [0] => 15
    [1] => 5
)

合計と平均が出ています。

それぞれの値を変数に一気に代入する方法

list($sum, $average) = getStats(1, 3, 5);

もしくは

[$sum, $average] = getStats(1, 3, 5);

上記どちらかでそれぞれの値を変数に一気に代入できます。
実際にやってみます。

<?php

function getStats(...$numbers)
{
  $total = 0;
  foreach ($numbers as $number) {
    $total += $number;
  }
  return [$total, $total / count($numbers)];
}

[$sum, $average] = getStats(3, 5, 7);

echo $sum . PHP_EOL;
echo $average . PHP_EOL;

実行結果

~ $ php main.php
15
5
~ $ 

これで配列を使い複数の返り値を受ける方法がわかりました。

これでPHP基礎文法は終わりです。

ここまで読んでいただきありがとうございました!

おわりに

記事の作成を通じてたくさん勉強になりました。

最高のアウトプットは「人に教えること」と言いますが、
それに近いことができるので、成長できます。

基礎学習からアウトプットしていくことは非常に効果があると感じます。

以前はハードルを感じて迷ってましたがやってみてよかったです!
記事の質は、続けていくことで良くしていければいいと思います。

まだPHPの初歩しかやってないので今後色々知らないことがもっと増えると思います。
しっかりまとめて学習していきます。

Links

PHP: 言語リファレンス - Manual - PHP.net
https://www.php.net/manual/ja/langref.php

ドットインストール 詳解PHP 基礎文法編
https://dotinstall.com/lessons/basic_php_grammer

PHPの「define」と「const」の違い
https://qiita.com/schrosis/items/485b984e05b2eb4521b4


Twitterでも毎日プログラミングについて投稿してます。
よかったらフォローお願いします:blush:
https://twitter.com/DawaProgramming

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP+LINE Messaging APIでクイズアプリを作ってみよう

今回やりたいこと

今回はPHPでLINE Messaging APIを使いクイズアプリを作成します。

仕様

・リッチメニューをタップしクイズを出題
・4つの選択肢から回答する
・正解の場合のみ解説の参考サイトurlを添付
・クイズはDB管理とし、クイズの追加・編集・削除機能がある管理画面の作成
※管理画面は後日公開予定です。

こちらから友達追加してお試しいただけます!
https://lin.ee/3AOdmRRlo
スクリーンショット 2020-05-24 13.56.43.png

イメージ

あいさつメッセージ
IMG_6287.PNG

リッチメニューをタップするとクイズを出題
IMG_6306.PNG

不正解の選択肢をタップした場合のメッセージ
IMG_6289.PNG

正解の選択肢をタップした場合のメッセージ
IMG_6290.PNG

いいえをタップした場合のメッセージ
IMG_6305.PNG

はいをタップした場合、次のクイズが出題
IMG_6306.PNG

以上の仕様を実装します。

VPSで環境構築

VPSはこちらを参考に構築しました。詳細は下記サイトから確認してみてください。他にも参考になる記事がたくさんあるのでググってみて下さい!
ネコでもわかる!さくらのVPS講座 〜第一回:VPSてなんだろう?〜

Messaging APIを設定

Messaging APIの概要はこちら

プログラム

DBカラム

quizzes : id | quiz | ans1 | ans2 | ans3 | ans4 | correct | url | deleted

quiz:問題文
ans1~4:選択肢1~4
correct:正解番号
url:解説サイトのurl
deleted:論理削除用

index.php
<?php

$accessToken = 'アクセストークン'; 
//file_get_contents()関数でPOSTされたJSON文字列を取り出し
$jsonString = file_get_contents('php://input'); 
//エラーログ記録
error_log($jsonString); 
//json_decode()関数でJSONをデコードして変数に格納
$jsonObj = json_decode($jsonString); 
//メッセージイベントを取得
//スタンプ、テキスト、画像などの場合がある
$message = $jsonObj->{"events"}[0]->{"message"};
//ReplyToken取得
//受信したイベントに対して返信を行うために必要
$replyToken = $jsonObj->{"events"}[0]->{"replyToken"};
//メッセージタイプ取得
//ここで、受信したメッセージがテキストか画像かなどを判別
$type = $jsonObj->{'events'}[0]->{'type'};

// 送られてきたメッセージの中身からレスポンスのタイプを選択 

//$typeがmessageの場合
if ($type == 'message') {
   //messageがtextであり、かつtextがクイズの場合クイズを返信する
    if ($message->{"text"} == 'クイズ') {
    //DB接続
        $dbh = new PDO("mysql:host=localhost;dbname=DB名", 'ユーザー名', 'パスワード');
     //sql文を変数にセット
       //DBから論理削除されていないクイズIDの配列を作る
        $sql = "SELECT id FROM quizzes WHERE deleted = 0";
        $res = $dbh->query($sql);
        $quizIdArray = array();
        foreach ($res as $row) {
            array_push($quizIdArray, $row['id']);
        }
    //クイズIDをランダムに並べ替える
        shuffle($quizIdArray);
      //クイズID配列の先頭要素をキーに、クイズを取得し表示する
        $targetQuizId = $quizIdArray[0];
        $sql2 = "select * from quizzes where id = $targetQuizId";
        $stmt = $dbh->query($sql2);
        $targetRow = $stmt->fetch();
    //$messageDataにクイズの内容を代入する
        $messageData = [ 
            'type' => 'template', 
            'altText' => 'クイズ', 
            'template' => [ 'type' => 'buttons', 'text' => $targetRow['quiz'], 
            'actions' => [
            [ 'type' => 'postback', 'label' => $targetRow['ans1'], 'data' => 'ans=' . $targetRow['ans1'] . '&num=1&correct=' . $targetRow['correct'] . '&id=' . $targetRow['id'] ],
            [ 'type' => 'postback', 'label' => $targetRow['ans2'], 'data' => 'ans=' . $targetRow['ans2'] . '&num=2&correct=' . $targetRow['correct'] . '&id=' . $targetRow['id'] ],
            [ 'type' => 'postback', 'label' => $targetRow['ans3'], 'data' => 'ans=' . $targetRow['ans3'] . '&num=3&correct=' . $targetRow['correct'] . '&id=' . $targetRow['id'] ],
            [ 'type' => 'postback', 'label' => $targetRow['ans4'], 'data' => 'ans=' . $targetRow['ans4'] . '&num=4&correct=' . $targetRow['correct'] . '&id=' . $targetRow['id'] ],
            ] 
            ]
        ]; 
    //messageがtextであり、かつtextがいいえであった場合の返信を$massageDataに代入
    }elseif ($message->{"text"} == 'いいえ') {
        $messageData = [ 'type' => 'text', 'text' => 'いつも[解剖学クイズbot]をご利用いただきありがとうございます。クイズチャレンジお疲れ様でした!楽しんでいただけましたでしょうか?またのチャレンジをお待ちしております!' ]; 
        $messageData2 = [ 'type' => 'text', 'text' => '再度クイズにチャレンジする場合は、「クイズにチャレンジ!」をタップしてください!' ]; 
    } else {
   //$typeが上記以外の場合の返信内容を$messageDataに代入
        $messageData = [ 'type' => 'text', 'text' => '申し訳ありません、その操作には対応しておりません。「クイズにチャレンジ!」をタップしてクイズにチャレンジしてみて下さい。' ]; 
    } 
}
//$typeがpostbackの場合
if ($type == 'postback') {
   // JSONデータからポストバックデータを取得
    $postback = $jsonObj->{"events"}[0]->{"postback"}->{"data"};
   //文字列を名前と値に分解し変数に代入
    parse_str($postback, $data);
   //各値を変数に代入
    $answer = $data["ans"];
    $number = $data["num"];
    $id = $data["id"];
    $correct = $data["correct"];
   //ユーザーの選択した選択肢と正解番号を比較し、正解・不正解の場合で返信内容を$massageDataに代入
   //正解の場合
    if ($number == $correct) {
        $dbh = new PDO("mysql:host=localhost;dbname=DB名", 'ユーザー名', 'パスワード');
   //選択された選択肢のIDから解説サイトのurlを取得
        $sql3 = "SELECT url FROM quizzes WHERE id = $id";
        $stmt2 = $dbh->query($sql3);
        $targetRowUrl = $stmt2->fetch();
        $url = $targetRowUrl['url'];
   //$massageDataに返信内容を代入
        $messageData = [ 'type' => 'text', 'text' => $answer . 'ですね。' ]; 
        $messageData2 = [ 'type' => 'text', 'text' => '素晴らしい!『正解』です!!!' ]; 
        $messageData3 = [ 
            'type' => 'template',
            'altText' => '解説', 
            'template' => [
            'type' => 'buttons',
            'title' => '解説です!',
            'text' => '確認してみてね!', 
            'actions' => [
            [
            'type' => 'uri',
            'label' => 'Wikipediaへ移動', 
            'uri' => "$url", 
            ]
            ]
            ] 
        ]; 
        $messageData4 = [ 
            'type' => 'template', 
            'altText' => '次のクイズにチャレンジしますか?', 
            'template' => [ 'type' => 'confirm', 'text' => '次のクイズにチャレンジしますか?', 
            'actions' => [
            [ 'type' => 'message', 'label' => 'はい', 'text' => 'クイズ' ],
            [ 'type' => 'message', 'label' => 'いいえ', 'text' => 'いいえ' ], 
            ] 
            ]
        ];
    }else{
   //不正解の場合の返信内容を$massageDataに代入
        $messageData = [ 'type' => 'text', 'text' => $answer . 'ですね。' ]; 
        $messageData2 = [ 'type' => 'text', 'text' => 'むむ、、『不正解』です。' ]; 
    }
}
//メッセージの数に合わせ条件分岐
if ($messageData4 == true) {
    $response = [ 'replyToken' => $replyToken, 'messages' => [$messageData,$messageData2,$messageData3,$messageData4] ]; 
}elseif ($messageData3 == true) {
    $response = [ 'replyToken' => $replyToken, 'messages' => [$messageData,$messageData2,$messageData3] ]; 
}elseif ($messageData2 == true) {
    $response = [ 'replyToken' => $replyToken, 'messages' => [$messageData,$messageData2] ]; 
}else{
    $response = [ 'replyToken' => $replyToken, 'messages' => [$messageData] ]; 
}
error_log(json_encode($response)); 
//curlセッション初期化。urlも設定
$ch = curl_init('https://api.line.me/v2/bot/message/reply'); 
//curlオプション設定
curl_setopt($ch, CURLOPT_POST, true); 
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($response)); 
curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json; charser=UTF-8', 'Authorization: Bearer ' . $accessToken )); 
//curl実行
$result = curl_exec($ch);
//エラーログ記録
error_log($result); 
//curlセッション終了
curl_close($ch);

?>

工夫した点

・クイズをDB管理とし、追加・編集・削除機能をつけた
・クイズをランダムに出題
・ユーザーからのレスポンスに応じて異なる処理

改善案

・メッセージの数に合わせた条件分岐がダサいのでスマートに書けないものか。。。
・クイズ内容を身体の部位ごとに指定するなど、分野を分ける
・解説を外部サイトに依存しているが、bot内で完結しても良いか
・その場合、画像付きの解説などを入れるとUXが高められそう

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ロリポップでsqliteを使って超簡易的なブログを作る

■使用したレンタルサーバー
ロリポップ ライトプラン(エコノミーでも可能)

■手順

phpliteadminを公式サイトからダウンロードする。

ダウンロードしたファイルを修正する。
phpliteadmin.config.sample.php

このファイルの以下の部分を修正する。
$password = 'admin';
admin部分を好きなパスワードに変更する。

次に、レンタルサーバーにフォルダを作り、
先程ダウンロードしたphpliteadminの内容すべてをアップロードする。

ブラウザからhttp経由でphpliteadmin.phpファイルにアクセスする。
すると、管理画面よりデータベースの操作が可能になる。

ロリポップはpublicフォルダより上にファイルの配置ができないらしい。
なので、publicフォルダ内に配置して、アクセス制限をかければ問題ないかも。

■以下ファイルで、記事一覧が表示可能。

index.php
<?php
$db = new SQLite3('sqlite/blog');
$sql = "SELECT * FROM blog";
$result = $db->query($sql);
while ($row = $result->fetchArray()) {
echo <<< eof
<li><a href="prev.php?id={$row['id']}">{$row['title']}</a> <span style="font-size:12px;">({$row['date']})</span></li>
eof;
}
?>

■個別の記事ページはこんな感じ。

prev.php
<?php
$id = intval($_GET['id']);
$db = new SQLite3('sqlite/blog');
$sql = "SELECT * FROM blog where id = {$id}";
$result = $db->query($sql);
while ($row = $result->fetchArray()) {
    $title = $row['title'];
    $date = $row['date'];
    $content = $row['content'];
};
?>

<?php include("meta.php");?>

<title><?php echo $title;?></title>
<?php include("header.php");?>

<h2><?php echo $title;?></h2>
<p><?php echo $content;?></p>
<p>更新日:<?php echo $date;?></p>

<?php include("side.php");?>
<?php include("footer.php");?>

■説明
共通部分はincludeで対応した。
GETでidを渡し、sqliteからデータを取得して記事を表示させている。
記事の投稿、編集、削除はphpliteadmin管理画面上から行う。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 実行時に選択肢を出したい(標準入力)

やりたいこと

今回やりたいこととしては、PHPファイルを実行して下記のような選択肢を出したい。

$ php sample.php
sample.phpを実行しますか? (y/n)

こいつを作ってみたい。

恐らく、『PHP 標準入力』などでググれば出てくる。

実践

簡単な処理を書いてみました。

sample.php
<?php

echo 'sample.phpを実行しますか? (y/n)';

$anser = trim(fgets(STDIN));

if($anser == 'y'){
    echo '実行しました';
} else {
    echo '中断しました';
}

?>

下記でできるみたいですね。

$anser = trim(fgets(STDIN));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel 勉強中のAPIについて超基礎部分を簡単にまとめる

目的

  • APIについて勉強中であるが、知識として溜まってきて整理が必要なのでまとめる
  • ※APIは難しいものではないので気楽に読んでいただけたら幸いである

実施環境

  • ハードウェア環境(下記の二つの環境で確認)
項目 情報
OS macOS Catalina(10.15.3)
ハードウェア MacBook Pro (16-inch ,2019)
プロセッサ 2.6 GHz 6コアIntel Core i7
メモリ 16 GB 2667 MHz DDR4
グラフィックス AMD Radeon Pro 5300M 4 GB Intel UHD Graphics 630 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.3 Homwbrewを用いて導入
Laravel バージョン 7.0.8 commposerを用いて導入
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いて導入

そもそもLaravelAPI(APIサーバ)って??

  • 指定のURLなどのリクエストをAPIサーバに送るとURLに紐づけられた情報がレスポンスとして得られるサーバの事である。
  • 主に自分の様な初学者のみなさんの勉強しているLaravelはそのLaravelアプリ内でブラウザ表示まで完結しているものがほとんどである。
  • 初学者のみなさんが勉強するLaravelアプリはリクエストに対してビューなどの「綺麗に見せるためのデータ」も含めてレスポンスとして返却している。
  • APIとはリクエストに対してその「綺麗に見せるためのデータ」を省き、DBに格納された文字列や数値、ステータスコードのみを返却するものである。
  • 「リクエストをもらう」 → 「Laravelフレームワーク内で処理する」 → 「レスポンスを返す」という流れはAPIも一緒である。(この記事で最悪これさえ理解できていればOK!)
  • 下記にLaravelアプリ(初学者のみなさんが最初に勉強するもの)の処理の簡易図を記載する。

    スクリーンショット 2020-05-22 14.36.49.png

  • 下記にLaravelAPIの処理の簡易図を記載する。

    スクリーンショット 2020-05-22 14.37.01.png

LaravelをAPIとして使用した際のレスポンスのデータ

  • レスポンスとして得られるデータはJSONというデータフォーマットを用いる。
  • ビューなどのデータが含まれないデータだけがLaravelのAPIから返却される。
  • 下記に今の段階での情報でのLaravelAPIの処理の簡易図を記載する。

    スクリーンショット 2020-05-22 14.59.35.png

APIのレスポンスを確認できるアプリ

  • 「スマホアプリなどがないとLaravelAPIの勉強できないんじゃない?」と感じたと思う。
  • 「スマホアプリなど」の役割をしてくれるPC用アプリケーションがある。
  • 下記のPostmanというアプリを使用することにより、LaravelAPIにリクエストを送ることができる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP デストラクタの検証

デストラクタについて書いていきます。
こいつもインスタンス化した段階で呼ばれます。

あんまり使っているところは見ないのですが、
知識として入れていきます。
基本的に、処理の最後に呼ばれる印象です。

ざっくりした理解なので、実際に書いていきます。

test_sample.php
<?php

require_once(__DIR__ . '/classTest.php');

$app = new classTest();

?>
classTest.php
<?php

class classTest
{
    public function __construct(){
        echo 'Fist:メッセージ';
    }

    public function __destruct(){
        echo 'Last:メッセージ';
    }

}

?>
・実行結果
Fist:メッセージ
Last:メッセージ

では、他のメソッドがある前提で検証していきます。
classTestクラスのsampleメソッドを呼んだ場合どうなるでしょう。

test_sample.php
<?php

require_once(__DIR__ . '/classTest.php');

$app = new classTest();
$app->sample();

?>
classTest.php
<?php

class classTest
{
    public function __construct(){
        echo 'Fist:メッセージ';
    }

    public function __destruct(){
        echo 'Last:メッセージ';
    }

    public function sample(){
        echo 'sample:メッセージ';
    }

}

?>
・実行結果
Fist:メッセージ
sample:メッセージ
last:メッセージ

sampleメソッドが呼ばれた後にデストラクタが呼ばれています。

では、test_sample.phpの中の最後に
testという文字列を出力したらどうなるか検証していきます。

test_sample.php
<?php

require_once(__DIR__ . '/classTest.php');

$app = new classTest();
$app->sample();
echo 'test';

?>
classTest.php
<?php

class classTest
{
    public function __construct(){
        echo 'Fist:メッセージ';
    }

    public function __destruct(){
        echo 'Last:メッセージ';
    }

    public function sample(){
        echo 'sample:メッセージ';
    }

}

?>
・実行結果
Fist:メッセージ
sample:メッセージ
test
Last:メッセージ

ここでわかるのは、インスタン化が行われたプログラム(test_sample.php)の
最後に呼ばれるということですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP コンストラクタの検証

1.constractの挙動

コンストラクタメソッドはよく使います。
個人的には『一番最初に呼ばれるメソッド』というざっくりした印象でした。
調べたところ、インスタンス化した段階で呼ばれます。

ではまず、シンプルなコードで試してみましょう。

test_sample.php
<?php

require_once(__DIR__ . '/classTest.php');

$app = new classTest();

?>
classTest.php
<?php

class classTest
{
    public function __construct(){
        echo 'constructメッセージ';
    }

}

?>
・実行結果
constructメッセージ

2.コンストラクタを親・子クラスで書いたら

では、親クラスのコンストラクタが呼ばれるか、
子クラスのコンストラクタが呼ばれるか
見ていきましょう。

test_sample.php
<?php

require_once(__DIR__ . '/classTest.php');

$app = new childClass();

?>
classTest.php
<?php

class classTest
{
    public function __construct(){
        echo '親メッセージ';
    }

}


class childClass extends classTest
{
    public function __construct(){
        echo '子メッセージ';
    }

}

?>
・実行結果
子メッセージ

この結果からわかるように、子クラスのコンストラクトが呼ばれます。
(多分、オーバーライドされてます)
親クラスのコンストラクタは呼ばれません。

実は公式ドキュメントに書いてありましたが、実際に実行しないと納得できないので実行しました。
https://www.php.net/manual/ja/language.oop5.decon.php

注意: 子クラスがコンストラクタを有している場合、親クラスのコンストラクタが 暗黙の内にコールされることはありません。 親クラスのコンストラクタを実行するには、子クラスのコンストラクタの 中で parent::__construct() をコールすることが 必要です。 子クラスでコンストラクタを定義していない場合は、親クラスのコンストラクタを継承します (ただし、private 宣言されている場合は除く)。 これは、通常のクラスメソッドと同様です。

3.親クラスコンストラクタを呼びたい

では、公式ドキュメントの通りにコンストラクタの中で
親クラスのコンストラクタを呼んでみましょう。

test_sample.php
<?php

require_once(__DIR__ . '/classTest.php');

$app = new childClass();

?>
classTest.php
<?php

class classTest
{
    public function __construct(){
        echo '親メッセージ';
    }

}


class childClass extends classTest
{
    public function __construct(){
        parent::__construct();
    }

}

?>
・実行結果
親メッセージ

これで親クラスのコンストラクタが呼ばれるようになりました。

ちなみに、この場合でも親クラスのコンストラクタが呼ばれます。

classTest.php
class childClass extends classTest
{

}

4.コンストラクタの変数を保存

うまいタイトルが思いつきませんでしたが、コンストラクタで呼ばれた変数は
その後に別のメソッドで使用可能です。

classTest.php
<?php

require_once(__DIR__ . '/classTest.php');

$app = new classTest();

$app->message();

?>
classTest.php
class classTest
{
    private $message;

    public function __construct(){
        $this->$message = 'メッセージ';
    }

    public function message(){
        echo $this->$message;
    }

}

?>
・実行結果
メッセージ

では、子クラスでも使えるのか見ていきましょう。

classTest.php
<?php

require_once(__DIR__ . '/classTest.php');

$app = new childClass();

$app->message();

?>
classTest.php
class classTest
{
    private $message;

    public function __construct(){
        $this->$message = 'メッセージ';
    }

}

class childClass extends classTest
{
    public function message(){
        echo $this->$message;
    }
}

?>
・実行結果
メッセージ

いけました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む