- 投稿日:2020-01-19T21:51:52+09:00
PHP基礎構文 マジックメソッドを全て利用してみる
前提
- 実行環境
- PHP 7.1.32
- 本エントリーでは、PHPが用意する全マジックメソッドに関する挙動について触れる
マジックメソッドとは
インスタンスがある特定の条件になったときに、明示的にcallしなくても暗黙的にcallされるメソッド
引用元:便利だけど使いどころが難しいPHPの代表的なマジックメソッドと無名関数の使い方
- 暗黙的にコールされる事がポイント
__construct()
- オブジェクトが生成されるたびにコールされるメソッド
class Message { public function __construct() { print 'call ' . __METHOD__ . PHP_EOL; } } var_dump(new Message()); /* 出力結果 */ call Message::__construct object(Message)#1 (0) { }__destruct()
- オブジェクトを参照するメソッドが1つもなくなった時(全てコールされた後)にコールされるメソッド
class Message { public function __construct() { print 'call ' . __METHOD__ . PHP_EOL; } public function __destruct() { print 'call ' . __METHOD__ . PHP_EOL; } } var_dump(new Message()); /* 出力結果 */ call Message::__construct object(Message)#1 (0) { } call Message::__destruct__call()
- アクセス不能なメソッド(未定義のメソッドやプライベートなメソッドなど)に対してアクセスした時にコールされるメソッド
<?php class Message { public function __call(string $name, array $arguments) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; print 'Value of second argument $arguments: ' . implode($arguments) . PHP_EOL; } } $message = new Message(); $message->getMessage('Hello'); var_dump($message); /* 出力結果 */ call Message::__call Value of first argument $name: getMessage Value of second argument $arguments: Hello object(Message)#1 (0) { }__callStatic()
- アクセス不能メソッドを静的にアクセスした時にコールされるメソッド
class Message { public static function __callStatic(string $name, array $arguments) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; print 'Value of second argument $arguments: ' . implode($arguments) . PHP_EOL; } } $message = new Message(); $message::getMessage('Hello'); var_dump($message); /* 出力結果 */ call Message::__callStatic Value of first argument $name: getMessage Value of second argument $arguments: Hello object(Message)#1 (0) { }__get()
- アクセス不能なプロパティ(未定義のプロパティやプライベートなプロパティなど)に対してアクセスした時にコールされるメソッド(主に、データの読み込む際に使用する)
<?php class Message { private $message = []; public function __get(string $name) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; return $this->message; } } $message = new Message(); var_dump($message->message); /* 出力結果 */ call Message::__get Value of first argument $name: message array(0) { }__set()
- アクセス不能なプロパティ(未定義のプロパティやプライベートなプロパティなど)に対してアクセスした時にコールされるメソッド(主に、データの書き込む際に使用する)
<?php class Message { private $message = []; public function __get(string $name) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; if (array_key_exists($name, $this->message)) { return true; } else { return false; } } public function __set(string $name, $value) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; print 'Value of second argument $value: ' . $value . PHP_EOL; $this->message[$name] = $value; print PHP_EOL; } } $message = new Message(); $message->message = 'Hello'; var_dump($message->message); /* 出力結果 */ call Message::__set Value of first argument $name: message Value of second argument $value: Hello call Message::__get Value of first argument $name: message bool(true)__isset()
- アクセス不能なプロパティ(未定義のプロパティやプライベートなプロパティなど)に対して、issetやemptyを実行した時にコールされるメソッド
<?php class Message { private $message = []; public function __isset(string $name) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; return isset($this->message[$name]); } } $message = new Message(); var_dump(isset($message->message)); /* 出力結果 */ call Message::__isset Value of first argument $name: message bool(false)__unset()
- アクセス不能なプロパティ(未定義のプロパティやプライベートなプロパティなど)に対して、unsetを実行した時にコールされるメソッド
<?php class Message { private $message = []; public function __unset(string $name) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; unset($this->message[$name]); } } $message = new Message(); unset($message->message); /* 出力結果 */ call Message::__unset Value of first argument $name: message__sleep()
- serialize()をコールした時にコールされるメソッド
class Message { public function __sleep() { print 'call ' . __METHOD__ . PHP_EOL; return []; } } $message = new Message(); var_dump(serialize($message)); /* 出力結果 */ call Message::__sleep string(18) "O:7:"Message":0:{}"__wakeup()
- unserialize()をコールした時にコールされるメソッド
class Message { public function __wakeup() { print 'call ' . __METHOD__ . PHP_EOL; return []; } } $message = new Message(); $serializedMessage = serialize($message); var_dump(unserialize($serializedMessage)); /* 出力結果 */ call Message::__wakeup object(Message)#2 (0) { }__toString()
- インスタンスを文字列として出力する時にコールされるメソッド
class Message { public function __toString() { print 'call ' . __METHOD__ . PHP_EOL; return 'Hello' . PHP_EOL; } } $message = new Message(); echo $message; /* 出力結果 */ call Message::__toString Hello__invoke()
- オブジェクトを関数としてコールした時にコールされるメソッド
class Message { public function __invoke($value) { print 'call ' . __METHOD__ . PHP_EOL; echo $value . PHP_EOL; } } $message = new Message(); $message('Hello'); var_dump($message); /* 出力結果 */ call Message::__invoke Hello object(Message)#1 (0) { }__set_state()
- var_export()によってエクスポートされた時にコールされるメソッド
class Message { public $val_a; public $val_b; public static function __set_state(array $properties) { $this->val_a = $properties['val_a']; $this->val_b = $properties['val_b']; print 'call ' . __METHOD__ . PHP_EOL; } } $message = new Message(); $message->val_a = 'Hello'; $message->val_b = 'Good morning'; var_export($message); /* 出力結果 */ Message::__set_state(array( 'val_a' => 'Hello', 'val_b' => 'Good morning', ))__clone()
- クローンキーワードによってオブジェクトをコールした時にコールされるメソッド
class Message { public function __clone() { print 'call ' . __METHOD__ . PHP_EOL; } } $message = new Message(); var_dump(clone $message); /* 出力結果 */ call Message::__clone object(Message)#2 (0) { }__debugInfo()
- var_dump()によってオブジェクトをコールした時にコールされるメソッド
class Message { public function __debugInfo() { print 'call ' . __METHOD__ . PHP_EOL; return []; } } var_dump(new Message()); /* 出力結果 */ call Message::__debugInfo object(Message)#1 (0) { }
- 投稿日:2020-01-19T21:48:03+09:00
PHP基礎構文 マジックメソッドを全て利用してみる
マジックメソッドとは
インスタンスがある特定の条件になったときに、明示的にcallしなくても暗黙的にcallされるメソッド
引用元:便利だけど使いどころが難しいPHPの代表的なマジックメソッドと無名関数の使い方
- 暗黙的にコールされる事がポイント
__construct()
- オブジェクトが生成されるたびにコールされるメソッド
class Message { public function __construct() { print 'call ' . __METHOD__ . PHP_EOL; } } var_dump(new Message()); /* 出力結果 */ call Message::__construct object(Message)#1 (0) { }__destruct()
- オブジェクトを参照するメソッドが1つもなくなった時(全てコールされた後)にコールされるメソッド
class Message { public function __construct() { print 'call ' . __METHOD__ . PHP_EOL; } public function __destruct() { print 'call ' . __METHOD__ . PHP_EOL; } } var_dump(new Message()); /* 出力結果 */ call Message::__construct object(Message)#1 (0) { } call Message::__destruct__call()
- アクセス不能なメソッド(未定義のメソッドやプライベートなメソッドなど)に対してアクセスした時にコールされるメソッド
<?php class Message { public function __call(string $name, array $arguments) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; print 'Value of second argument $arguments: ' . implode($arguments) . PHP_EOL; } } $message = new Message(); $message->getMessage('Hello'); var_dump($message); /* 出力結果 */ call Message::__call Value of first argument $name: getMessage Value of second argument $arguments: Hello object(Message)#1 (0) { }__callStatic()
- アクセス不能メソッドを静的にアクセスした時にコールされるメソッド
class Message { public static function __callStatic(string $name, array $arguments) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; print 'Value of second argument $arguments: ' . implode($arguments) . PHP_EOL; } } $message = new Message(); $message::getMessage('Hello'); var_dump($message); /* 出力結果 */ call Message::__callStatic Value of first argument $name: getMessage Value of second argument $arguments: Hello object(Message)#1 (0) { }__get()
- アクセス不能なプロパティ(未定義のプロパティやプライベートなプロパティなど)に対してアクセスした時にコールされるメソッド(主に、データの読み込む際に使用する)
<?php class Message { private $message = []; public function __get(string $name) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; return $this->message; } } $message = new Message(); var_dump($message->message); /* 出力結果 */ call Message::__get Value of first argument $name: message array(0) { }__set()
- アクセス不能なプロパティ(未定義のプロパティやプライベートなプロパティなど)に対してアクセスした時にコールされるメソッド(主に、データの書き込む際に使用する)
<?php class Message { private $message = []; public function __get(string $name) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; if (array_key_exists($name, $this->message)) { return true; } else { return false; } } public function __set(string $name, $value) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; print 'Value of second argument $value: ' . $value . PHP_EOL; $this->message[$name] = $value; print PHP_EOL; } } $message = new Message(); $message->message = 'Hello'; var_dump($message->message); /* 出力結果 */ call Message::__set Value of first argument $name: message Value of second argument $value: Hello call Message::__get Value of first argument $name: message bool(true)__isset()
- アクセス不能なプロパティ(未定義のプロパティやプライベートなプロパティなど)に対して、issetやemptyを実行した時にコールされるメソッド
<?php class Message { private $message = []; public function __isset(string $name) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; return isset($this->message[$name]); } } $message = new Message(); var_dump(isset($message->message)); /* 出力結果 */ call Message::__isset Value of first argument $name: message bool(false)__unset()
- アクセス不能なプロパティ(未定義のプロパティやプライベートなプロパティなど)に対して、unsetを実行した時にコールされるメソッド
<?php class Message { private $message = []; public function __unset(string $name) { print 'call ' . __METHOD__ . PHP_EOL; print 'Value of first argument $name: ' . $name . PHP_EOL; unset($this->message[$name]); } } $message = new Message(); unset($message->message); /* 出力結果 */ call Message::__unset Value of first argument $name: message__sleep()
- serialize()をコールした時にコールされるメソッド
class Message { public function __sleep() { print 'call ' . __METHOD__ . PHP_EOL; return []; } } $message = new Message(); var_dump(serialize($message)); /* 出力結果 */ call Message::__sleep string(18) "O:7:"Message":0:{}"__wakeup()
- unserialize()をコールした時にコールされるメソッド
class Message { public function __wakeup() { print 'call ' . __METHOD__ . PHP_EOL; return []; } } $message = new Message(); $serializedMessage = serialize($message); var_dump(unserialize($serializedMessage)); /* 出力結果 */ call Message::__wakeup object(Message)#2 (0) { }__toString()
- インスタンスを文字列として出力する時にコールされるメソッド
class Message { public function __toString() { print 'call ' . __METHOD__ . PHP_EOL; return 'Hello' . PHP_EOL; } } $message = new Message(); echo $message; /* 出力結果 */ call Message::__toString Hello__invoke()
- オブジェクトを関数としてコールした時にコールされるメソッド
class Message { public function __invoke($value) { print 'call ' . __METHOD__ . PHP_EOL; echo $value . PHP_EOL; } } $message = new Message(); $message('Hello'); var_dump($message); /* 出力結果 */ call Message::__invoke Hello object(Message)#1 (0) { }__set_state()
- var_export()によってエクスポートされた時にコールされるメソッド
class Message { public $val_a; public $val_b; public static function __set_state(array $properties) { $this->val_a = $properties['val_a']; $this->val_b = $properties['val_b']; print 'call ' . __METHOD__ . PHP_EOL; } } $message = new Message(); $message->val_a = 'Hello'; $message->val_b = 'Good morning'; var_export($message); /* 出力結果 */ Message::__set_state(array( 'val_a' => 'Hello', 'val_b' => 'Good morning', ))__clone()
- クローンキーワードによってオブジェクトをコールした時にコールされるメソッド
class Message { public function __clone() { print 'call ' . __METHOD__ . PHP_EOL; } } $message = new Message(); var_dump(clone $message); /* 出力結果 */ call Message::__clone object(Message)#2 (0) { }__debugInfo()
- var_dump()によってオブジェクトをコールした時にコールされるメソッド
class Message { public function __debugInfo() { print 'call ' . __METHOD__ . PHP_EOL; return []; } } var_dump(new Message()); /* 出力結果 */ call Message::__debugInfo object(Message)#1 (0) { }
- 投稿日:2020-01-19T20:33:08+09:00
PHPの「define」と「const」の違い
どちらも定数を定義するときに使う
<?php define('MY_CONST_1', 'defineで定義した定数'); const MY_CONST_2 = 'constで定義した定数'; echo MY_CONST_1; // defineで定義した定数 echo MY_CONST_2; // constで定義した定数1.関数と構文
define
は関数
const
は構文
define
は関数の呼び出しのオーバーヘッドがあるため 遅い
const
は関数じゃないから 速い2.変数や、関数の戻り値を使えるか使えないか
define
は変数や、関数の戻り値を使える<?php $val = '適当な変数'; define('MY_CONST', $val); echo MY_CONST; // 適当な変数<?php define('NOW', microtime(true)); echo NOW; // 1579431033.9034
const
は変数や、関数の戻り値を使えない<?php $val = '適当な変数'; const MY_CONST = $val; // PHP Fatal error: Constant expression contains invalid operations in ...<?php const NOW = microtime(true); // PHP Fatal error: Constant expression contains invalid operations in ...いったん定数にすれば使える
<?php define('NOW', microtime(true)); const MY_CONST = NOW; echo MY_CONST; // 1579431879.06643.トップレベル以外で使えるか使えないか
const
はif
やfor
function
の中では使えない<?php if (true) { define('MY_CONST', 'defineはifの中でも使える'); }<?php if (true) { const MY_CONST = 'constは構文エラーになる'; // PHP Parse error: syntax error, unexpected 'const' (T_CONST) in ... }4.クラス定数
define
はクラス定数を定義するときに使えない<?php class Example { public define('MY_CONST', 'defineは関数だから構文エラーになる'); } // PHP Parse error: syntax error, unexpected 'define' (T_STRING), expecting function (T_FUNCTION) or const (T_CONST) in ...<?php class Example { public const MY_CONST = 'クラス定数を定義するときに使う'; } echo Example::MY_CONST; // クラス定数を定義するときに使うまとめ
クラス定数を定義するときは
const
を使うクラス定数以外は
const
を使うと速いけど
何回も定義するわけじゃないし、const
には制約もあるから
define
を使った方が無難
- 投稿日:2020-01-19T20:11:24+09:00
laravelで作った画像も表示できるブックレビューの機能について ※ポートフォリオの解説です
こんなアプリを作りました
http://book-review123456.herokuapp.com/articles
書籍のカバー画像も表示できる掲示板アプリです。画像はGoogleBooksAPIsを利用して取得します。
APIを利用した画像の取得方法については下記にまとめました ↓laravelでカバー画像も表示できるブックレビューアプリを作った。※自分の学習用です
デプロイ出来れば一番良かったのですが、出来なかったのでイメージ画像と言葉で補足します。
(gitはしばらくの間はprivateにしておこうと思います。個人情報的に。特定の相手にだけ共有しようかと)機能はこんな感じです。
- 画像も表示できるブックレビューの投稿
- ログイン/新規登録
- バリデーション(ログイン時だけレビューの投稿・編集ができる)
- お問い合わせ
- 退会処理
- TwitterOAuth
- いいね機能
TwitterOAuthは個人情報的にアレかな〜と思って、gitで共有する段階ではやはり封印しました。
反省と今後の課題
機能に関する課題は別記事に書いたので、勉強について。
しばらくはLaravelではなく、生のPHPで制作し勉強した方がいいのかなと思いました。
なので、こちらの記事を参考に勉強を進めたいです。
- 投稿日:2020-01-19T19:57:25+09:00
LambdaでPHPを実行する手順
なるべく、簡潔にできるようにまとめてみました。
前提
Mac
AWSアカウント作成済
Dockerインストール済手順
- Docker内で、PHPの実行環境を作り、それをzip化
- そのzipファイルをLambdaにアップロード
- API GatewayでAPI作成
1. Docker内で、PHPの実行環境を作り、それをzip化
stackery/phplambdalayer
というツールを使うと、makeコマンド一つで簡単にできました。このツールには、PHPの実行環境をzip化するために必要なスクリプトや、Lambdaでカスタムランタイム自体を動作させるboostrapファイル等がまとめて入っている
$ git clone https://github.com/stackery/php-lambda-layer.git $ cd php-lambda-layer $ make php71.zip
php71.zip
というファイルが作成される
make php71.zip
の内容を見ると、Makefilephp71.zip: docker run --rm -e http_proxy=${http_proxy} -v $(ROOT_DIR):/opt/layer lambci/lambda:build-provided /opt/layer/build.sh
lambci/lambda
というlambda環境のDockerイメージを使っている- そのイメージで起動したDockerコンテナ内で、
build.sh
が実行されているbuild.sh
の内容を見ると、Lambda用のPHP7.1実行環境を作って、zip化しているbuild.sh#!/bin/bash # 依存関係により、php本体もここでインストールされている yum install -y php71-mbstring.x86_64 zip php71-pgsql php71-mysqli mkdir /tmp/layer cd /tmp/layer cp /opt/layer/bootstrap . sed "s/PHP_MINOR_VERSION/1/g" /opt/layer/php.ini >php.ini mkdir bin cp /usr/bin/php bin/ mkdir lib for lib in libncurses.so.5 libtinfo.so.5 libpcre.so.0; do cp "/lib64/${lib}" lib/ done cp /usr/lib64/libedit.so.0 lib/ cp /usr/lib64/libpq.so.5 lib/ cp -a /usr/lib64/php lib/ zip -r /opt/layer/php71.zip .2. 作成したzipファイルをLambdaにアップロード
ここから下は、全てAWSのコンソールでやりました。
2-1. レイヤー作成
レイヤーは、実行環境をアップロードしておくことで、複数のLambda関数で利用できるようにするもの
- レイヤーを作成し、作成したzipファイルをアップロードする
- レイヤーバージョンARNは、後で使うため、メモっておく
2-2. Lambda関数作成
- 「一から作成」
- ランタイム:「ユーザー独自のブートストラップを提供する」
- 「実行ロール」(IAMロール)も一緒に作成される
- すでにある3つのファイルは使わないため、削除。代わりに、以下のindex.phpを作成
index.php<?php phpinfo(); ?>2-3. Lambda関数とレイヤーを結びつける
「レイヤーバージョンARNを提供」を選択し、上でメモった「レイヤーバージョンARN」を貼る
3. API GatewayでAPI作成
「APIの作成」
- メソッドの作成:Any
- 「Lambdaプロキシ統合の使用」にチェックを入れる
- 最後にAPIのデプロイ(ステージ名入力。このステージは、アクセスするパスになる)
APIへのアクセスは、該当するLambda関数を選択→APIGatewayを選択すると、APIエンドポイントが表示され、ワンクリックでできる。
参照
- 投稿日:2020-01-19T18:26:39+09:00
PHPの「static」は「3種類」??
負けた
1.静的メンバ
静的プロパティ、静的メソッドを定義するために使うパターン
<?php class Example { /** * @var string */ protected static $staticProperty = '静的プロパティ'; /** * @return string */ final public static function getStaticProperty(): string { return self::$staticProperty; } } echo Example::getStaticProperty(); // 静的プロパティ一番よく使うパターン
クラス自体に持たせるメンバ
インスタンス化しないで使う静的メンバには
::
(スコープ演算子)を使ってアクセスする
スコープ演算子の左辺はクラス
、右辺は静的メンバ
2.静的変数
- コンパイルされる時に 1度だけ 初期化される
- 同スコープ内で複数回初期化される場合 最後に初期化される値 になる
- 変数 や、インスタンス を使って初期化 できない
- スコープが外れてもガベコレされない
- 再度初期化された時、前回の値で初期化される
- メソッドの中で使うと静的プロパティと同じ挙動をする
最初の一度のみ
0
で初期化され
スコープが外れてもガベコレされず
前回の値が入っていることがわかる例
↓<?php /** * カウントアップする */ function countUp(): int { static $cnt = 0; return ++$cnt; } echo countUp(); // 1 echo countUp(); // 2 echo countUp(); // 3参照を切ってもガベコレされない
同スコープ内で複数回初期化される場合 最後に初期化された値になる
再度宣言された時、前回の値で初期化される例
↓<?php /** * 第1引数で指定した名前の変数が存在する場合、その値をechoします * 存在しない場合は「参照ないよ」とechoします * * @param string $valName * @return void */ function echoIfExists(string $valName): void { echo $GLOBALS[$valName] ?? '参照ないよ'; } /** * 改行文字を返す * コマンドラインから実行されている場合は \n * それ以外は <br> * * @return string */ function endOfLine(): string { return php_sapi_name() === 'cli' ? PHP_EOL : nl2br(PHP_EOL); } // 静的変数を初期化する(1回目) static $staticVal = '静的変数_1'; echoIfExists('staticVal'); echo endOfLine(); // 静的変数_2 // ↑1じゃなく2で初期化されている $staticVal = '値を書き換える'; // 参照消す(普通の変数ならガベコレされる) unset($staticVal); echoIfExists('staticVal'); echo endOfLine(); // 参照無いよ // 静的変数を初期化する(2回目) static $staticVal = '静的変数_2'; echoIfExists('staticVal'); echo endOfLine(); // 値を書き換える // ↑前回の値で初期化されている変数を使って初期化できない例
↓<?php $val = '適当な変数'; // 変数で初期化を試みる static $staticVal = $val; // PHP Fatal error: Constant expression contains invalid operations in ...変数を使っても定義できる 例
↓<?php $val = '変数'; define('MY_CONST', $val); // 変数を定数にする static $staticVal = MY_CONST; echo $staticVal; // 変数インスタンスを使って初期化できない例
↓<?php // インスタンスで初期化を試みる static $staticVal = new class{}; // PHP Fatal error: Constant expression contains invalid operations in ...もちろんクロージャもインスタンスなのでだめ
<?php // クロージャで初期化を試みる static $staticVal = function () {}; // PHP Fatal error: Constant expression contains invalid operations in ...ユーザー定義定数を使って初期化できない例
↓<?php static $staticVal = '普通に初期化'; // PHP Warning: Use of undefined constant MY_CONST - assumed 'MY_CONST' $val = '変数'; define('MY_CONST', $val); // 変数を定数にする static $staticVal = MY_CONST;同スコープ内で複数回初期化される場合 最後に初期化された値 になる ため
MY_CONST
で初期化しようとするけど
最初の初期化のタイミングにはMY_CONST
は存在しないのでエラーになるメソッドの中で使うと静的プロパティと大体同じ挙動をする例
↓
PHPの静的変数 (static変数) の挙動まとめ静的プロパティとメソッド内の静的変数の違いは
参照を切れるか と 外からアクセスできるかプロパティは
unset()
できないけど
静的変数はunset()
できる静的プロパティはメンバなので
Reflection
を使えば
private
であってもアクセスすることができる
静的変数はローカルスコープなので絶対にアクセスできない3.遅延静的束縛
静的メンバにアクセスする際に使う特別なクラスに
self
とstatic
があります
self
はそのメソッドが定義してあるクラス自身
- コンパイルされるときに決定(束縛)される
static
はそのメソッドが呼び出されたクラス自身
- ランタイムで決定(束縛)される
名前の通り遅延して束縛されるわけですね
具体的にどういう違いがあるかというと、継承したときに違いが現れます
<?php class Base { /** * selfが指すクラスをechoする * * @return void */ public static function echoSelfClass(): void { echo self::class; } /** * staticが指すクラスをechoする * * @return void */ public static function echoStaticClass(): void { echo static::class; } } class Sample extends Base {} Sample::echoSelfClass(); // Base Sample::echoStaticClass(); // Sampleまとめ
PHPの
static
は難しいまとまってない
- 投稿日:2020-01-19T16:40:49+09:00
Docker+Laravelでタスクスケジューラ
目的
通常サーバのタスクスケジューラはcronにて行うが、
- 少し面倒であること
- Laravelにタスクスケジューラの機能があること
から、画面を作成して画面経由でタスクスケジュール設定を行えるものを作成してみる。
前提
下記の環境で実装した。
- PHP7.1.x
- Laravel6.x+AdminLTE3
- PostgreSQL11.4
- Docker phpfpm
サンプル仕様
サーバのタスクスケジュールのため、画面操作するためにログインが必要とした。
タスク自体は別途実装済みであるとして、タスク名と実行スケジュールを設定する画面を
用意する。
タスクの実行はLaravelにタスクスケジューラをcron経由で実行するものとして、タスクスケジューラの設定を先の画面で設定したものを反映させるものとする。タスクの実装
詳細は省略するが、タスクの雛形をartisanで作成できる。
% php artisan make:command タスク名とりあえず、タスクが動いたか確認したいため、下記のタスクを実装した。
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use Illuminate\Support\Facades\Log; class Shout extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'command:shout'; 〜省略〜 /** * Execute the console command. * * @return mixed */ public function handle() { Log::info("start shout!!"); } }見ての通り、実行されたらログが出力されるだけのタスクである。
モデル定義
定義するモデルはScrapingCommandである。
migrateの内容を下記に示す。?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateScrapingCommandsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('scraping_commands', function (Blueprint $table) { $table->bigIncrements('id'); $table->string("name",200); $table->string("cmd",200); $table->integer("cron1")->nullable(); $table->integer("cron2")->nullable(); $table->integer("cron3")->nullable(); $table->integer("cron4")->nullable(); $table->integer("cron5")->nullable(); $table->integer("flg")->default(0); $table->softDeletes(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('scraping_commands'); } }画面実装
ログイン画面
タスク一覧画面
登録されているタスクの一覧表示している。
タスクスケジュールはcronと同じ形式である。スケジュールとは別に有効/無効の設定が可能である。
タスク設定画面
タスクの設定を行う。コマンド名はartisanで使用するコマンド名(\$signatureに設定しているもの)を設定する。
設定内容はScrapingCommandモデルに保存される。
Laravel側のスケジュール定義
LaravelのタスクスケジュールはApp\Console\Kernelのscheduleメソッドで定義するため、
同メソッドを下記のように修正した。<?php namespace App\Console; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Support\Facades\Log; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use App\ScrapingCommand; class Kernel extends ConsoleKernel { 〜省略〜 /** * Define the application's command schedule. * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { $lst = ScrapingCommand::where("flg",1)->get(); foreach($lst as $rec) { Log::info("cmd:".$rec->cmd); Log::info("cron:".$rec->cronStr()); $schedule->command($rec->cmd)->cron($rec->cronStr()); } } 〜省略〜 }Cron側の設定
Cron側はLaravelのタスクスケジューラを実行するだけなので、
* * * * * /usr/local/bin/php /svr/app/artisan schedule:run >> /var/log/cron.log 2>&1 (注)/svr/appはLaravelをインストールしたディレクトリ本来はcrontab -e で上記のファイルを作成することになるが、今回はDockerなので、
rootファイルとして作成しておく。以上で、要望を実装済みであるが、今回はDockerで実装したため、コンテナ上でcronのインストールが必要になる。
そこで、cronのインストールはDockerイメージ作成時に行う。よってDockerファイルは
FROM php:fpm # For composer RUN apt-get update \ && apt-get install -y gcc make zlib1g-dev libzip-dev libpng-dev unzip libmcrypt-dev libjpeg-dev libfreetype6-dev \ && docker-php-ext-install zip \ && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include \ && docker-php-ext-install -j$(nproc) gd # Install composer RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ && php composer-setup.php \ && php -r "unlink('composer-setup.php');" \ && mv composer.phar /usr/local/bin/composer # Set composer environment ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer ENV PATH $PATH:/composer/vendor/bin # Install laravel installer RUN composer global require "laravel/installer" # PHP's DB setting RUN apt-get update \ && apt-get install -y libpq-dev \ && docker-php-ext-install pdo_mysql pdo_pgsql # Install Node.js RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \ && apt-get update \ && apt-get install -y nodejs RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime RUN apt-get install -y cron RUN mkdir /debugbar RUN chmod go+w /debugbar WORKDIR /svrとした。
次にcrontabの登録とcron起動が必要になるが、今回は手動で行うものとした。
よって、上記で作成したイメージからコンテナを起動して、コンテナ上で% cp root /var/spool/cron/crontab % service cron startを実行する。
rootファイルはあらかじめ/svrにコピーしておくこと。考察
cronの設定って結構面倒なものである。何よりわざわざサーバにログインして作業しないと行けないのである。
この仕組みだと、画面で設定するだけなので大分楽である。タスクはLaravel上で動くため、
DB接続とかログ出力とかの考慮が必要なくなる。問題点は
- crontabの設定とcronの起動をコンテナ起動後手動で行わなければならない。
Dockerイメージでcronの起動もできるらしいが、今回は割愛した- 5分毎等のスケジュールに対応できてない
これは実装の問題であるが。。。サンプルで実装したプロジェクトはGitHubにアップしたので、必要であれば参照していただきたい。
- 投稿日:2020-01-19T16:40:24+09:00
PHPの備忘録
- 投稿日:2020-01-19T15:27:55+09:00
【個人開発】Webサイトを作ってみた
つくったもの
概要
予算300円以内で買えるだけのお菓子が選ばれ、その結果を楽しめるサイトです。
なぜ作ったか
仕事でPHPなどは触っているもののWebサイトを作成したことがなかったので習作として作成しました。
制作に挫折しないよう扱ったことのない技術は極力避け、完成させることを第一目標にしました。使用技術
OS:CentOS 7.7
言語:PHP 5.4
DB:MariaDB 5.5.64
キャッシュ:memcached 1.4.15
プラグイン:lightbox2
VPS:KagoyaVPS
DNS:お名前ドットコム
広告:i-mobile Affiliate工夫したところ
・なるべくDBにアクセスしないようにDBアクセスの結果はキャッシュに乗せるようにしています。
・管理ページでCSVファイルをアップロードできる仕組みにしているため、データの追加や更新が容易にできるようになっています。苦労したところ
・本番環境でモジュールのインストールがうまくいかず試行錯誤していました。
具体的にはphp-pecl-memcachedです。
OSをCentOS6からCentOS7にすることで依存関係の問題は解決しました。
・データ作成に想定より時間がかかりました。
今後のデータ追加を考慮してデータ構造を何回も変えていたので、そのたびにデータ修正をしていました。管理ツール
管理ツールのページでは以下のことが行えます。
- マスターデータ閲覧
- DBのマスターデータ参照。
- マスターデータ(CSV)のインポート
- CSVファイルを選択し、DBのテーブルにインポート。
- キャッシュクリア
- memcachedのクリア。
制作管理
完成させるためにタスクの洗い出しや進捗記録をつけていました。
仕事みたいで微妙ですが、終わりが見えない作業をやってる感よりは良いかなと。・機能リスト
最終的に実装したい機能のリスト。
優先順位をつけ、優先度の高いものから実装していく。
・Todo
機能リストの項目を細かく分解してタスク化。
ふと時間ができた時、取り掛かれるタスクをパッと見つけるためのもの。
機能リストには無い思いつきもここに追加。
・仕様書
仕様どおりに作るためのものではなく、あとでどんな実装をしたのか確認するためのもの。
最初に各仕様を記述する枠だけは作っておいて書けるものは書く。
作りながら考えたものは実装した後で仕様書に実装をフィードバック。
・作業記録
前回の作業で何をやってたっけ…?というのを思い出すためのもの。
副産物として少しづつコツコツ進んでる実感を得られる。
制作期間
2019年10月下旬から2020年01月上旬が製作期間です。
子供がいるので休日もまとまった時間が取れず、出社前の15分や休日にふとできた30分の空き時間などを積み重ねていました。まとめ
・小さいながらも作成したWebサイトが公開に至れたのはよかったです。
個人開発の記事でよく見る「完全を求めてるといつまでも公開できない」というのは実感としてよく分かります。
どこかで切り上げてまとめに入る決断が必要です。
・思うがままに作成できる個人開発は楽しいです。
面倒だったら仕様を変えられるし、思いつきもすぐ反映できます。
とはいえデータ作成とか、あぁこれ自分がやらないといけないんだよな…と思ったりします。
・制作管理の資料を作成していたのは良かったです。
一段落したときに自分のやったことを見返せますし、制作での資産が分かりやすくまとまっているので今後に活かすことができます。
- 投稿日:2020-01-19T14:57:33+09:00
CentOSにcomposerをインストールする方法
はじめに
phpを用いた開発の時によく用いられるcomposerについてインストール方法をまとめます。
そもそもcomposerとは一言で言うとphpのパッケージ管理システムです。
phpのパッケージ依存管理ツールであるComposerを利用することで、パッケージの管理コストを下げ開発効率をあげることができます。
composerはプロジェクトが必要とするライブラリやパッケージを管理していて、それをもとにインストールをすることができます。
例えば、あるライブラリをインストールしようとしたときにその前に特定のライブラリをインストールしなければならないような依存関係を自動的にインストールしてくれる便利なツールです。composerをインストールする
今回は公式のやり方とyumを用いてインストールする方法の2種類をまとめます。
他のパッケージをインストールする時にyumを用いている場合は依存関係などのバージョン管理しやすいようにするため、yumを用いてインストールする方法をおすすめします。
※今回はphpコマンドyumコマンドが使えると言う前提で話を進めます。
phpはパージョン7.3系です。①公式のcomposerインストール手順
command# インストーラをダウンロードする(phpコマンド以外にもcurlやwgetなどのコマンドでダウンロードもできます。) $ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" # ダウンロードしたインストーラのハッシュをチェックし、正しければ'Installer verified'と表示する $ php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" # インストーラを実行する $ php composer-setup.php # インストーラを削除する $ php -r "unlink('composer-setup.php');" #CentOSのどこからでも使えるようにするために、/usr/local/binフォルダにcomposer.pharを移動させる $ sudo mv composer.phar /usr/local/bin/composer②yumを用いたcomposerインストール手順
command#yumでcomposerをインストールする $ sudo yum install --enablerepo=remi,remi-php73 composer #(中略) エラー: パッケージ: composer-1.9.2-1.el7.remi.noarch (remi) 要求: php-zipphp-zipがないと怒られてしまいました。。。
なので下記のようにphp-pecl-zipを指定してインストールされると成功しました!!
(php-zip指定してもなぜかできませんでした。。。なぜか分かる方いたら教えてください。。。)command$ sudo yum install --enablerepo=remi,remi-php73 php-pecl-zip composercomposerの配置とインストール確認
command#CentOSのどこからでも使えるようにするために、/usr/local/binフォルダにcomposer.pharを移動させる $ sudo mv composer.phar /usr/local/bin/composer #インストール確認する(大きくcomposerの文字とバージョンと出てくれば完了です) $ composer -vさいごに
何かパッケージをインストールするときは公式のやり方を見つつ自分の開発環境とあっているか確認しながら行うと良いかと思います。
- 投稿日:2020-01-19T01:17:28+09:00
PHPで暗号学的に安全でランダムな文字列を柔軟に生成する方法
やりたいこと
- ランダムな文字列を取得したいよ。
- PHPの標準関数だけでやりたいよ。
- 暗号学的に安全なランダム性を持たせたいよ。
- 出力で使用する文字や文字長を指定したいよ。(0~fだけとかヤダよ)
- PHP5系では動かせなくてもいいよ。
暗号学的に安全じゃない方法とは?
「ランダムな文字列をゲットしたいな」というとき、よく以下のようなコードが使われるかと思います。
echo sha1(uniqid(mt_rand(), true)); // 出力例 : f282fe197b5f9837a520118d5636facc0ff3832d実はこれだと問題があって(まぁ実際はほぼ問題になることは無いと思いますが…)、「mt_rand()」も「uniqid()」も生成される値が暗号学的に安全ではないとリファレンスに明記されています。
PHPマニュアル - 関数リファレンス
あと、そもそもこの方法だと「出力で使用する文字を指定」したり、「出力文字長を指定」したりできないですね。
暗号学的に安全な方法1(0~f固定だけど簡単な方法)
出力される文字が0~f固定でも良い場合は、以下のコードでOKです。
$length = 16; echo substr(bin2hex(random_bytes($length / 2 + 1)), 0, $length); // 出力例 : cf7ffd47e6cb2399random_bytes()は、暗号学的に安全なランダムバイト列を生成します。
生成したバイト列をbin2hex()で文字列に変換し、最後にsubstr()で必要な長さに切っています。PHPマニュアル - 関数リファレンス
暗号学的に安全な方法2(0~f固定じゃないし文字長指定できるし使用文字を指定できる方法)
見た方が早いですね。以下の通りです。
function random_string($length = 16) { // 出力で使用する文字を決める。 // 例えば、以下の場合は、「英数字から見間違え安い文字を除外した文字」のみにしてある。 // (lとかOとか除外してある) $baseChars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789'; $baseCharLength = strlen($baseChars); // 指定された数だけランダムな文字を取得する $output = ''; for ($i = 0; $i < $length; $i++) { // rand()ではなく、暗号学的に安全なrandom_int()を使用する。 // ちなみに、文字列は角括弧で任意の文字にアクセスできるので、substr()じゃなくてOK。 $output .= $baseChars[random_int(0, $baseCharLength - 1)]; } return $output; }コメントにも書いてありますが、出力で使用したい文字を$baseCharsで指定しています。
そこから、必要な文字長だけランダムに文字を取り出していく、という流れです。このとき、ランダムな取り出し位置はrandom_int()で指定します。random_int()で生成される値は暗号学的に安全であることがリファレンスにも明記されています。
PHPマニュアル - 関数リファレンス
出力結果はこんな感じです。
for ($i = 0; $i < 5; $i++) { echo random_string(16)."\n"; } // deWWGQ9wWGBVDFcU // ui7usFxSx8FwWPCM // d7AGQmydVcPbcwv6 // xzuVnAKCr5QB7Rz8 // SHHc8UV3Ha5F2th3出力する文字にマルチバイト文字を使用したい場合
マルチバイト文字の場合は「strlenではなくmb_strlenを使う」、「文字列からの文字取り出しは角括弧ではなくmb_substrを使う」にします。
function mb_random_string_iroha($length = 16) { $baseChars = 'いろはにほへとちりぬるをわかよたれそつねならむうゐのおくやまけふこえてあさきゆめみしゑひもせすん'; // マルチバイト文字を使う場合は、mb_strlen()で。 $baseCharLength = mb_strlen($baseChars); $output = ''; for ($i = 0; $i < $length; $i++) { $pos = random_int(0, $baseCharLength - 1); // マルチバイト文字ならこちらもmb_substr()で。 $output .= mb_substr($baseChars, $pos, 1); } return $output; }出力結果はこんな感じです。
for ($i = 0; $i < 5; $i++) { echo mb_random_string_iroha(16)."\n"; } // なたれあなやろおおてほねさえれゆ // あよらゑふめさたなりよゐむきたせ // ゆてるいもそしやぬりゐてちひたか // しのけちほねせねえつゆくすおめせ // のつすいらなほれれむりとよまさる
最後に、高精度の姓名分割ツール とか Writeningっていうメモサービス とか、その他色々作ってるのでぜひプロフィール欄のホムペを覗いてみてください〜