- 投稿日:2020-09-08T23:00:29+09:00
CakePHP3のfindでcontain要素に検索条件を追加する
例えばUsersテーブルにUserInfoテーブルが紐付いていて、prefecture_id=2で名字が佐藤か工藤の1名をFindしたいときは下記のようになります。
useで$prefecture_idをわたしてあげるのがミソです。
UsersController.php$prefecture_id = 2; $user = $this->Users->find()->contain( ['UserInfo' => function($q) use ($prefecture_id) { return $q->where(['UserInfo.prefecture_id' => $prefecture_id]); }]) ->where([ 'Users.last_name IN' => ['佐藤', '工藤'] ]) ->first();かんたんですね。
- 投稿日:2020-09-08T21:34:18+09:00
PHP・GCの話-4話)MemoryLeakと解除できない変数データ
前書き
- すべての記事は、自分の勉強目的と主観の整理を含めています。あくまで参考レベルで活用してください。もし誤った情報などがあればご意見をいただけるととっても嬉しいです。
- 内容では、省略するか曖昧な説明で、わかりづらいところもあると思います。そこは、連絡いただければ補足などを追加するので、ぜひ負担なくご連絡ください。
- 本文での「GC」は、「Garbage Collection, Garbage Collector」の意味しており、略語として使われています。
- この記事は、連載を前提に構成されています。
※ 連載目録
- PHP・GCの話-1話)なぜGarbageCollection? メモリとGCを意識する
- PHP・GCの話-2話)変数の管理情報、zval containerとreference count
- PHP・GCの話-3話)変数データのメモリからの消滅
- PHP・GCの話-4話)MemoryLeakと解除できない変数データ(←現在の記事)
- PHP・GCの話-5話)GC登場。GC発生条件とroot buffer ⇨ 準備中
- PHP・GCの話-6話)管理対象の巡回・削除。Garbage Collection Cycle ⇨ 準備中
- PHP・GCの話-7話)GC関連機能紹介(GC Statistics, Weak Reference Type)(END) ⇨ 準備中
※ 連載で使うサンプルコード
● ExampleGc.php : 2話から6話までの内容で使うサンプルコードです。
● ExampleWeakReference : 7話のWeakReferenceの内容で使うサンプルコードです。本連載記事は、基本的にこのサンプルコードをベースに説明をしています。
サンプルコードは、必ず見る必要も実行してみる必要もありません。
各話ごとに、コードを分解して動作原理と結果を解説しますので、基本記事の内容で足りるように心がけます。
あくまで、全体コードをみたい、手元で回してみたい、修正して回してみたいという方向けです。今回の話
今回は、以下のものを話そうと思うます。
- 1. Memory Leakとは?
- 2. 解除できない変数データの例 (循環参照)
- 3. Summary
今回からは、本格的にサンプルコードを引用しながら、見ていきますので、
以下のリンクのコードを一緒に参考にしながら見ると良いと思います。1. Memory Leakとは?
シンプルに言うと、
もう使えない変数データが、メモリを専有し続ける現象
ですね。これを「ゴミ・Garbage」と表現したりもします。
もうちょっと詳しく説いて行くために、Memory Leakの定義をwikiから引用すると、
https://en.wikipedia.org/wiki/Memory_leak#cite_note-1
① In computer science, a memory leak is a type of resource leak that occurs when a computer program incorrectly manages memory allocations in such a way that memory which is no longer needed is not released.
② A memory leak may also happen when an object is stored in memory but cannot be accessed by the running code.少し意訳すると
① コンピューター工学において、Memory Leakとは、プログラムで、もう使わないのに解除されないような、間違ったメモリ空間の割当により起きる、有限な資源の無駄遣いの現象です。
② 一例として、memory leakは、メモリに保存はされているものの、実行コード上ではこれ以上サクセスされないオブジェクトにより起きることもありえます。ですね。
それ以外で一番多い事例は、ネットワークコネクションやグラフィックなどのresourceタイプの解除を忘れることだと思います。
※1
②に対するMemory Leakの一つの例が、今から見ていく「循環参照」です。
引き続き、②の事例の「循環参照」に関して詳しく見ていきます。2. 解除できない変数データの例
ここでの「解除できない」というのは、
プログラマーの意図的には、すでに解除しており、これ以上使われるはずのない変数の元データがメモリ上にの残り続けること
を意味します。
簡単な例えとしては、オブジェクト同士の「循環参照」があります。
簡単なコードで紹介すると以下のコードで再現できます。$a = new \stdClass; $b = new \stdClass; //循環参照 $a->node = $b; $b->node = $a; //これ以上使われないはずなので、このケースは、メモリからデータは消滅しない unset($a); unset($b);これがなぜ、問題になるのかを、サンプルコードと実行結果で解説します。
1) サンプルコード例
● クラス定義引用
Sample Code Link on Githubabstract class Base { private $dummyData; private $tag = null; private $nodes = array(); public function __construct($tag) { $this->tag = $tag; $this->dummyData = str_repeat('a', 20*1024*1024); //20M Byte size approximately } /** * add Reference as ChildNode */ public function addNode(object $obj) { $this->nodes[] = $obj; return $this; } } class AliveInScope extends Base {} class CircularReference extends Base {}● コード引用
Sample Code Link on Githubprivate function doExampleGcBasic() { //...中略 Log::debug(null, ['event' => 'new', 'msg' => 'V']); $alive = new AliveInScope('V'); //...中略 Log::debug(null, ['event' => 'new', 'msg' => 'A, B']); $circleA = new CircularReference('A'); $circleB = new CircularReference('B'); Log::debug(null, ['event' => 'set', 'msg' => '$alive`s reference to A']); $circleA->addNode($alive); Log::debug(null, ['event' => 'set', 'msg' => 'circluar reference on A B']); $circleA->addNode($circleB); $circleB->addNode($circleA); xdebug_debug_zval('alive'); xdebug_debug_zval('circleA'); xdebug_debug_zval('circleB'); $this->logMemUsage(); Log::debug(null, ['event' => 'unset', 'msg' => 'A, B']); unset($circleA); unset($circleB); xdebug_debug_zval('alive'); xdebug_debug_zval('circleA'); xdebug_debug_zval('circleB'); $this->logMemUsage(); //...中略● 実行結果引用
root@bc290870f5e9:/var/www/html/subdomain/laravel# ./artisan example:gc | cut -d "$" -f 1 [2020-09-07 20:30:37] local.DEBUG: {"event":"new","msg":"V"} alive: (refcount=1, is_ref=0)=class App\Console\Commands\AliveInScope { private [2020-09-07 20:30:37] local.DEBUG: {"Memory Usage(Bytes)":"37,060,552"} [2020-09-07 20:30:37] local.DEBUG: {"event":"new","msg":"A, B"} [2020-09-07 20:30:37] local.DEBUG: {"event":"set","msg":" [2020-09-07 20:30:37] local.DEBUG: {"event":"set","msg":"circluar reference on A B"} alive: (refcount=2, is_ref=0)=class App\Console\Commands\AliveInScope { private circleA: (refcount=2, is_ref=0)=class App\Console\Commands\CircularReference { private circleB: (refcount=2, is_ref=0)=class App\Console\Commands\CircularReference { private [2020-09-07 20:30:37] local.DEBUG: {"Memory Usage(Bytes)":"79,015,032"} [2020-09-07 20:30:37] local.DEBUG: {"event":"unset","msg":"A, B"} alive: (refcount=2, is_ref=0)=class App\Console\Commands\AliveInScope { private circleA: no such symbol circleB: no such symbol [2020-09-07 20:30:37] local.DEBUG: {"Memory Usage(Bytes)":"79,015,672"}2) コードと実行結果の解説
Log::debug(null, ['event' => 'set', 'msg' => 'circluar reference on A B']); $circleA->addNode($circleB); $circleB->addNode($circleA);[2020-09-07 20:30:37] local.DEBUG: {"event":"set","msg":"circluar reference on A B"} ...中略 [2020-09-07 20:30:37] local.DEBUG: {"Memory Usage(Bytes)":"79,015,032"}ソスコード上の上記のポイントで、AとBは、お互いの内部でお互いを参照することになります。
その後のメモリの使用量は「79,015,032」Bytesになっています。Log::debug(null, ['event' => 'unset', 'msg' => 'A, B']); unset($circleA); unset($circleB);[2020-09-07 20:30:37] local.DEBUG: {"event":"unset","msg":"A, B"} alive: (refcount=2, is_ref=0)=class App\Console\Commands\AliveInScope { private circleA: no such symbol circleB: no such symbol [2020-09-07 20:30:37] local.DEBUG: {"Memory Usage(Bytes)":"79,015,672"}unsetをして、A,Bは、これ以上使うこともできないのに、なぜかメモリの使用量は減っていません。
つまり、使うこのもないのに、有限であるメモリ空間をずっと専有していることになります。なぜ、メモリの使用量は減らずに、実のデータがずっと残り続けるのでしょう。
3) コードの実行時に起きる変数と参照カウントの変化解説(GIF)
①循環参照変数の生成の段階の変化
上記のイメージの4番目のように、AとBは、
$circleA
,$circleB
の参照以外に、各自の内部で、お互いを参照するようになります。
だとしたら、$circleA
と$circleB
をunsetし、変数を無効にしたらどうなるのでしょう。②
$circleA
と$circleB
をunsetした後の段階の変化 (絵が間違っている)前回の「変数が消滅しない条件」は
「実際のデータの参照が一つ無効になる」時、参照カウント(refcount)が「1以上」であれば、データは消滅せずに残り続ける。
でした。
絵の2番めのように、
$circleA
と$circleB
の変数を解除したことで、2つとも実のデータにアクセスできなくなりました。
しかし、元のデータはお互いを参照しているので、参照カウントは2→1になりますが、その時の参照カウントが1以上であるため、「どこかで使われている」とシステムでは認識し、メモリから解除することはできなくなります。なので、プログラマー的には、消滅してほしいデータであるにも関わらず、ずっとメモリに残り続けることになります。
まさに、Memory Leakとして話した、
もう使えない変数データが、メモリを専有し続ける現象
であり、ゴミ・Garbage
ですね。だとしたら、このゴミ問題を解決するために、PHPではどういう機能を提供しているのでしょうか。
その一つが、次回に登場するGC・Garbage Collectionになります。
そこは、次回に詳しく説明することになります。3. Summary
今回で、最低限に覚えて頂くと良い内容は以下になります。
- Memory Leakとは、「もう使えない変数データが、メモリを専有し続ける現象」であり、「ゴミ」が残る現象
- 循環参照は、Memory Leakのわかりやすい例であり、GCが収集する対象としてのわかりやすい一例
- このゴミ問題を解決するために、PHPではどういう機能の一つが、GC・Garbage Collection後書き
1話から今回まではGCの背景になる、変数の仕様・動作・消滅メカニズムなどを話してきました。
次からは、本格的にGCのメカニズムに対して話していきます。
GCの発生条件や明示的に発生させる方法、GCが起きたら行われるメカニズムを解説していきます。この時に、zvalと参照カウントの理解と、変数の消滅基準、消滅せず残り続けるデータの理解が必要なので、それを意識した上でご覧頂けると嬉しいです。
説明とは不足なところか、分かりづらいところはあるかもですが、フードバック頂けると補足とか訂正いたしますので、宜しくお願いします。
※注釈
※1
▶ resourceタイプの解除を忘れること実のところ最近は、resourceタイプに対しても使われなくなったら自動解除してくれたりします。しかし例外な場合も無いわけでは無いのでresourceタイプ(または違う言語での類似タイプ)の解除は意識しておくと良いです。!
- 投稿日:2020-09-08T17:26:16+09:00
bindValue()の仮引数は何を示しているのか?
この記事でわかること
- PHPのPDOクラスの関数
bindValue()
の仮引数の中身がわかるそもそもbindValue()とは
public PDOStatement::bindValue ( mixed $parameter , mixed $value [, int $data_type = PDO::PARAM_STR ] ) : boolプリペアドステートメントで使用する SQL 文の中で、 対応する名前あるいは疑問符のプレースホルダに値をバインドします。
PHP公式リファレンスものすごく雑に要約すると、bindValue()とは、
下記SQLクエリ文の"?"部分を別の文字列に置換することができる関数です。
SQLインジェクション対策ですね。
$sql = "insert into phptodo (name, done, priority) values (?, 0, ?)";
特に、プリペアドステートメント?PDO?な方は、こちらの記事にまとまっているので参照すると幸せになれます。
SQLインジェクション?という方は、安全なWebサイトの作り方を参照すると幸せになれます。
なお、bindValue()は第三引数まで取ります。
bindValue()の各引数について考える
サンプルコード
$name = "hoge" $priority = "high" $sql = "insert into phptodo (name, done, priority) values (?, 0, ?)"; // SQLインジェクション対策のプレースホルダ(=?) $stmt = $dbh->prepare($sql); $stmt->bindValue(1, $name, PDO::PARAM_STR); $stmt->bindValue(2, $priority, PDO::PARAM_STR); $stmt->execute(); // $sql = "insert into phptodo (name, done, priority) values ("hoge", 0, "high")" // となるbindValue()の第一引数
どのプレースホルダ(
$sql
内の"?"のこと ) に関数を適用するかを数字で指定します。例えばサンプルコードでは values(?, 0, ?)となっているので、
values内の第一引数の "?" に変更を加えたい場合 => 1
values内の第三引数の "?" に変更を加えたい場合 => 2と記述します。
数字は0ではなく1から始まるので注意!bindValue()の第二引数
第一引数で指定したプレースホルダに何を代入するかを指定します。
ここでは変数$name
や$priority
を代入しています。bindValue()の第三引数
第二引数で指定した内容をどのようなデータ型にするかを指定します。
PDO::PARAM_INT
PDO::PARAM_STR
などこれらはInteger型の定数で、例えば
PDO::PARAM_STR
はInteger型の2
を表しています。
(このため、PDO::PARAM_STR
ではなく2
と記述しても同等の内容が実行されます)print(PDO:PARAM_STR) // 2他の定数はここ
プレースホルダに"?"を使わない方法
蛇足ですが、プレースホルダを任意の文字列にして可読性を高める方式もあります。
SQL文の"?"を
:hoge
や:fuga
として、
更にbindValue()
の第一引数を数字ではなく":hoge"
や":fuga"
と指定します。サンプルコード
$sql = "insert into phptodo (name, done, priority) values (:name, 0, :priority)"; $stmt = $dbh->prepare($sql); $stmt->bindValue(":name", $name, PDO::PARAM_STR); $stmt->bindValue(":priority", $priority, PDO::PARAM_STR); $stmt->execute();参考
- 投稿日:2020-09-08T16:33:22+09:00
メモ書き : Laravel8 Release & Upgrade
2020/09/08 リリース。バグフィックス期間は 2021/03/08 セキュリティフィックスは2021/09/08 までという予定。
そして、新機能が多いため Upgrade の量は少ないですが、factory をゴリゴリ使っているとちょっと注意です。( migration の
schema:dump
やった後のmigrate
したときの挙動が怖そうなので、そこらへんは自己責任でしてください)アップグレードに対する更新をソースコード単位で見たい場合は Github comparision tool を見ると良いですよ。
個人的なメモ: upgrade の時は phpstan で静的解析を通すのが良いですよーよー。
[新機能] Laravel Jetstream
see : https://github.com/laravel/jetstream
新しいパッケージだそうです。
Scaffolding の livewire か Inertiaを使って、ええ感じに Vue や Tailwind CSS っぽく使って…?
なんやこれ全くわからん。bladeを抜かした管理画面を自動で作るよってことかしら。
[新機能] Model Directory
Eloquent Model の初期位置が
app/Models
の中になりました。[新機能] Model Factory Classes
factory がクラスになりました。例えば User factory が旧だとベタなPHPスクリプトだったのが、クラスで指定するようになります。
旧 Factory を使う場合は laravel/legacy-factories を使うことになります。極力書き換えるのをおすすめします。
<?php /** @var \Illuminate\Database\Eloquent\Factory $factory */ use Faker\Generator as Faker; $factory->define( \App\Eloquent\User::class, function (Faker $faker){ return [ 'name' => $this->faker->name, 'email' => $this->faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ]; } );新::
<?php namespace Database\Factories; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; class UserFactory extends Factory { /** * The name of the factory's corresponding model. * * @var string */ protected $model = User::class; /** * Define the model's default state. * * @return array */ public function definition() { return [ 'name' => $this->faker->name, 'email' => $this->faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ]; } }これに伴い、Eloquent 側からfactory を作ることになるそうです。
<?php use App\Model\User; User::factory()->count(50)->create();[新機能] Migration Squash
SQL で書かれたスキーマの出力ができるようになります。出力されたスキーマは
database/schema
に保存されます。migration にも関わってくる。(後で)
php artisan schema:dump[新機能] Job Batching
Bus
でバッチ処理が追加されて、axios みたいな書き方で使えるっぽいです。<?php use App\Jobs\ProcessPodcast; use App\Podcast; use Illuminate\Bus\Batch; use Illuminate\Support\Facades\Batch; use Throwable; $batch = Bus::batch([ new ProcessPodcast(Podcast::find(1)), new ProcessPodcast(Podcast::find(2)), new ProcessPodcast(Podcast::find(3)), new ProcessPodcast(Podcast::find(4)), new ProcessPodcast(Podcast::find(5)), ])->then(function (Batch $batch) { // All jobs completed successfully... })->catch(function (Batch $batch, Throwable $e) { // First batch job failure detected... })->finally(function (Batch $batch) { // The batch has finished executing... })->dispatch(); return $batch->id;[新機能] Improved Rate Limit
Requtest Rate Limit を
throttle
ミドルウェアで制御してるが、もうちょっとフレキシブルにレートリミットを変えられるようになるっぽいです。例えば、会員ユーザのレートリミットは無し、それ以外はレートリミットは100回/分にする、とする場合は下記のように変更できるみたいです。
<?php use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Support\Facades\RateLimiter; // おそらく AppServiceProvider の中で設定 RateLimiter::for('uploads', function (Request $request) { return $request->user()->vipCustomer() ? Limit::none() : Limit::perMinute(100); });ミドルウェアの
throttole
で指定することで使える様になるみたいです。Route::middleware(['throttle:uploads'])->group(function(){ Route::post('/audio', function(){ }); Route::post('/video', function(){ }); });[新機能] Improved Maintenance Mode
php artisan down
でメンテナンスモードにした時の挙動を云々。
- --secret='1630542a-246b-4b66-afa1-dd72a4c43515'
このオプションを使うことで
/
に来たユーザは/1630542a-246b-4b66-afa1-dd72a4c43515
にリダイレクトされます。
- --render='errors::503'
このオプションを使うことで、表示するテンプレを指定することができる。
[新機能] Closure Dispatch/Chain
dispatch helper に
->catch(function(){})
ができるようになりました。おそらく、上記のBus
と同じ理屈。<?php use Throwable; dispatch(function () use ($podcast) { $podcast->publish(); })->catch(function (Throwable $e) { // This job has failed... });[新機能] Dynamic Blade Components
Blade::component('package-alert', AlertComponent::class);
で登録したコンポーネントを<x-package-alert/>
とテンプレート側に書くことで使えるようになります。クラス名は
camelCase
で書き、コンポーネント名はkebab-case
で書くみたい。Component Methods
コンポーネントテンプレート内でメソッドを動かすことができる。
/** * Determine if the given option is the current selected option. * * @param string $option * @return bool */ public function isSelected($option) { return $option === $this->selected; }<option {{ $isSelected($value) ? 'selected="selected"' : '' }} value="{{ $value }}"> {{ $label }} </option(何か vue みたいだなとぼんやり)
[新機能] Event Listener Improvements
Facade で
\Event::listen
をする時に、Queueを使うようにデコレートする事ができます。use App\Events\PodcastProcessed; use function Illuminate\Events\queueable; use Illuminate\Support\Facades\Event; Event::listen(queueable(function (PodcastProcessed $event) { // }));そして
queue job
のように細かくしてする事ができて->catch
チェインができます。Event::listen(queueable(function (PodcastProcessed $event) { // })->catch(function (PodcastProcessed $event, Throwable $e) { // The queued listener failed... })->onConnection('redis') ->onQueue('podcasts') ->delay(now()->addSeconds(10)) );[新機能] Time Testing Helpers
Carbon::now()
で返す時間を指定することができます。[新機能] Artisan serve Improvements
.env
ファイルを更新したばあいに、artisan serve
は自動に設定を読み直します。[新機能] Tailwind Pagination Views
Pagination のテンプレートに今までの Bootstrap3, Bootstrap4 の他に Tailwind が使えます。
[Upgrade] パッケージ依存
"laravel/framework": "^8.0"
"nunomaduro/collision": "^5.0"
"guzzlehttp/guzzle": "^7.0.1"
"facade/ignition": "^2.3.6"
他のパッケージを使っているばあいは、それらもアップデートしてください
- https://github.com/laravel/horizon/blob/master/UPGRADE.md
- https://github.com/laravel/passport/blob/master/UPGRADE.md
- Socialite v5.0
- Telescope v4.0
[Upgrade][Medium] Use PHP >=7.3.0
事情により PHP は 7.3.0 以上を指定してください。
[Upgrade][Low]
isset
method
Illuminate\Support\Collection
のoffsetExists
の挙動がかわりました。のでisset
で確認した時の挙動が変わります。<?php $collection = collect([null]); // Laravel 7.x - true isset($collection[0]); // Laravel 8.x - false isset($collection[0]);[Upgrade][High] Model Factories
model factories がクラス化されました。7.x までの factory を書き換えをせずに使い続ける場合は
laravel/legacy-factory
を使う必要があります。[Upgrade][Low] The Castable Interface
Castable
のcastUsing
の引数$arguments
に、array
のタイプ指定が加わりましたので、追加をしてください。[Upgrade][Low] Increment / Decrement Events
model の
update
およびsave
が動いた時にincrement
,decrement
インスタンスメソッドが動きます。[Upgrade][Low] The Dispatcher Contract
Illuminate\Contracts\Events\Dispatcher
のlisten
メソッドの第二引数$listener
の標準値にnull
を付けられました。public function listen($events, $listener = null);[Upgrade][Optional] Mentenance Mode Updates
新しく入った機能
--render
等を使う場合はpublic/index.php
を更新してください。この条件分岐の行はLARAVEL_START
の直下に書く必要があります。<?php define('LARAVEL_START', microtime(true)); if (file_exists(__DIR__.'/../storage/framework/maintenance.php')) { require __DIR__.'/../storage/framework/maintenance.php'; }[Upgrade][Low] Manager $app Property
Illuminate\Support\Manager
クラスの$app
プロパティが消えました。もしそのプロパティを使用している場合は$container
プロパティを使用してください。[Upgrade][Low] The elixir Helper
elixir
ヘルパーは削除されました。Laravel Mix に移行してください。[Upgrade][Low] Mail::sendNow Method
sendNow
メソッドは削除されました。send
メソッドを使ってください。[Upgrade][High] Pagination Defaults
Paginate の標準テンプレートが Tailwind CSS になりました。いままでの BootStrap を使い続ける場合は
AppServiceProvider
のboot
メソッドに\Illuminate\Pagination\Paginator::useBootstrap();
を記入してください。[Upgrade][High] Queue::retryAfter method / property
QueuedJob, Mailer, Notification, Listener にある
retryAfter
メソッドとretryAfter
プロパティの名前がbackoff
に変更されました。[Upgrade][High] Queue::timeoutAt property
QueueJob、Notification, Listener の
timeoutAt
プロパティがretryUntil
になりました。[Upgrade][Optional] Failed Jobs Table Batch Support
queue_driver が
database
でfailed_jobs
を使用している時に、uuid
フィールドが必要になります。<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; Schema::table('failed_jobs', function (Blueprint $table) { $table->string('uuid')->after('id')->unique(); });次に
queue.php
のfailed.driver
にdatabase-uuids
を設置します。[Upgrade][Low] The cron-expression Library
Cron 書式の解析に使用している
dragonmantank/cron-expression
のバージョンが3.x
に更新されました。
下手に込み入った書き方をしている場合だと意図しない時間に動きますので、変更履歴を見て確認をする必要があります。[Upgrade][Low] The Session Contract
\Session::pull()
メソッドが追加されました。/** * Get the value of a given key and then forget it. * * @param string $key * @param mixed $default * @return mixed */ public function pull($key, $default = null);[Upgrade][Medium] Testing, The assertExactJson Method
assertExactJson
メソッドは数値キーの順番が同じである事をチェックするようになりました。順番が違っても良い場合はassertSimilarJson
を使うようにしてください。ぼんやり:: こういう事?
$asset = [ 0 =>1, 1 =>1, 2 =>1]; $data = ['1'=>1, '0'=>1, '2'=>1]; $this->assertExactJson(json_encode($asset), json_encode($data)) // => false $this->assertSimilarJson(json_encode($asset), json_encode($data)) // => true[Upgrade][Low] Database Rule Connections
unique
,exists
ルールにgetConnectionName
メソッドが追加されました。
- 投稿日:2020-09-08T15:35:06+09:00
【Laravel 】localhost/プロジェクト名/public ←publicをなくす方法
調べたところ方法はいくつかあるが一番スマートな方法かなと思ったのでメモとして残します。
.htaccessを作成する
.htaccessとは隠しファイルの1つでリダイレクトなどの設定を記述するファイルのことです。
そこに以下記述をコピペします。
<IfModule mod_rewrite.c> <IfModule mod_negotiation.c> Options -MultiViews </IfModule> RewriteEngine On RewriteCond %{REQUEST_FILENAME} -d [OR] RewriteCond %{REQUEST_FILENAME} -f RewriteRule ^ ^$1 [N] RewriteCond %{REQUEST_URI} (\.\w+$) [NC] RewriteRule ^(.*)$ public/$1 RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ server.php </IfModule>参照:https://gist.github.com/liaotzukai/8e61a3f6dd82c267e05270b505eb6d5a
vscodeなどでコードを貼り付けてLaravelのルートディレクトリに保存します。
保存しても隠しファイルなのでフォルダには表示されていません。
隠しファイルを表示させたい時はそのフォルダで
(macの場合)command + shift + .(ドット)
を入力すると表示させることができます。
以上で完了です!
localhost/プロジェクト名 でルートディレクトリへ遷移できます。
- 投稿日:2020-09-08T14:56:18+09:00
WordPress 決済プラグイン導入
WordPressでクラウドファンディングサービスを構築し、決済プラグインを導入しました。
その試行錯誤です。「物語を共有するクラウドファンディング」mamenoki
まず、WP Attractive Donations Systemを導入(購入)しました。
カッコイイのですが、
https://codecanyon.net/item/wp-attractive-donations-system-easy-stripe-paypal-donations/16982796
ドルなら良いかもしれないけど、これを1円単位で合わせるのは、現実的ではありません。
それに、違うプラグインですが、
https://liapoc.com/stripe-100.html
と同じ現象が起きました。
Stripeに送信すると、¥1 ⇒ ¥100になる、ドル使用が前提だから?
/wp-content/plugins/WP_AttractiveDonationsSystem/includes/ads-core.php
$amount *= 100;
⇒$amount *= 1;
で解決しましたが、
- Steps of the progress bar :の下限額を99円以下にすると機能しない
- ドル単位にすると、逆に1/100になる
これは厳しい…
(Stripeのアカウントを作って、会社の口座をリンクして、テストまで出来た、のみが成果)なので、WP Full Stripe (Premium)を導入(購入)しました。
https://paymentsplugin.com/
これは完全に年単位のサブスクなので、注意しないと、永遠に使用料を引き落とされます(たぶん)。https://yuyauver98.me/wpfullstripe-translation/
を参考に日本語化も行いました。
- 投稿日:2020-09-08T13:50:29+09:00
HTML-PHP-post
同一ページ内で2つ以上の送信ボタンの区別
自身の振り返りようです。
<!-- html --> <form method="post"> <label>苗字:<input type="text" size="10" name="fname"></label> <input type="submit" value="送信"> </form> <form method="post"> <label>名前:<input type="text" size="10" name="name"></label> <input type="submit" value="送信"> </form><?php if($_SERVER['REQUEST_METHOD'] === 'POST'){ if(isset($_POST['fname']) === TRUE){ $btn=($_POST['fname']); } if(isset($_POST['name']) === TRUE){ $btn=($_POST['name']); } } ?>何らかの関係で送信ボタンを分けて送りたい場合、このままだとどちらかのボタンを押しても同時に両方の内容が送信されてしまう。
それぞれ個別で送信できるようにするためには<!-- html --> <form method="post"> <label>苗字:<input type="text" size="10" name="fname"></label> <button type="submit" name="btn" value="insert">送信</button> </form> <form method="post"> <label>名前:<input type="text" size="10" name="name"></label> <button type="submit" name="btn" value="updata">送信</button> </form><?php if(isset($_POST['btn']) === TRUE){ $btn=($_POST['btn']); } if($btn === 'insert'){ if(isset($_POST['fname']) === TRUE){ $fname=($_POST['fname']); } if($btn === 'updata'){ if(isset($_POST['name']) === TRUE){ $name=($_POST['name']); } ?>
- 投稿日:2020-09-08T13:04:57+09:00
LaravelでFirebase Admin SDK for PHPを利用する(トークン再取得編)
Firebase Admin SDK for PHPでは、自動でトークン取得はできないっぽい
例えば、Javascriptなら以下のように
currentUser.getIdToken()
で取得できる。
期限切れ前に自動で更新してくれるので便利。firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) { // Send token to your backend via HTTPS // ... }).catch(function(error) { // Handle error });Firebase Admin SDK for PHPには似たような機能はなかった。
トークンが切れるたびにいちいちログインし直すのもなんだか嫌。解決方法
// ログインしていない場合 if(!Session::get('token')){ $signInResult = $this->auth->signInAnonymously(); Session::put('token', $signInResult->idToken()); Session::put('refresh_token', $signInResult->refreshToken()); } // トークン認証 try { $auth->verifyIdToken(Session::get('token')); } catch (\Exception $e) { // ここはExpiredTokenとかに絞った方が良い // 期限切れなら更新トークンを使って再取得 $refresh_token = Session::get('refresh_token'); $signInResult = $auth->signInWithRefreshToken($refresh_token); Session::put('token', $signInResult->idToken()); }流れは以下のような感じ。
1)ログイン時の結果にrefresh_token
があるので、セッションか何かに取っておく。
2)トークンが期限切れになったらverifyIdToken()
で例外が発生するので、signInWithRefreshToken()
で再取得リフレッシュトークンが変更されるタイミングはそんなにないので、とりあえず使いたい場合は上記で間に合うと思う。
- 投稿日:2020-09-08T11:58:02+09:00
[ WordPress ] リンクなしでカテゴリを出力する
WordPressの投稿記事に紐づいたカテゴリを出力するには、通常 <?php the_category(); ?> を使いますが、その場合リンクもつきます。
このリンクをなくしたいと思いました。実装方法は、<?php get_the_category(); ?> を用いて一旦配列に格納して、ループで表示させました。また、複数のカテゴリを表示させる際セパレート(,)をつけて見やすくしましたが、最後のカテゴリが出力された際セパレータがつかないよう条件分岐もさせています。
loop.php<?php $categories = get_the_category(); $length = count($categories); $i = 0; foreach($categories as $cat){ $i++; if($length == $i){ echo $cat->cat_name; }else{ echo $cat->cat_name . ', '; } } ?>implode を使う
上記を投稿したところ、下記のように implode を使えばシンプルに実装できるとコメントを頂戴しました。
technote-space様、ありがとうございました!
loop.php<?php $categories = get_the_category(); echo implode(', ', array_map(function($cat) { return $cat->cat_name; }, $categories)); ?>参考URL
- 投稿日:2020-09-08T10:06:50+09:00
Laravel オリジナルの設定ファイルを作成してコントローラで呼び出す
目的
- Laravelの設定ファイルへの追記方法と設定ファイルの読み込み方法をまとめる
- ※本説明でいう設定ファイルとは.envファイルではなく、
アプリ名ディレクトリ/config
直下にあるファイルを指す実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2GHzクアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB
- ソフトウェア環境
項目 情報 備考 PHPバージョン 7.4.3 Homwbrewを用いて導入 Laravelバージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする 前提条件
- 実施環境に記載した環境、またはそれに準ずる環境が用意されておりLaravelアプリが作成されていること。
前提情報
- 筆者は下記の記事で作成したアプリを用いて本記事の動作を確認する。
- 下記に本記事を記載する際に検証で使用したソースコードのリモートリポジトリのリンクを下記に記載する。
読後感
アプリ名ディレクトリ/config
直下の設定ファイルに独自の設定を追記することができる。アプリ名ディレクトリ/config
直下の設定ファイルに記載されている値を読み込み表示することができる。概要
- 設定ファイルの作成と記載
- ルーティング情報の追記
- コントローラファイルの作成と記載
- ビューファイルの作成と記載
- 確認
詳細
設定ファイルの作成と記載
アプリ名ディレクトリで下記コマンドを実行して設定ファイルを作成する。
$ vi config_test.php開いたファイルに下記の内容を追記し保存して閉じる。
アプリ名ディレクトリ/config/config_test.php<?php return [ # 文字列の設定 'config_str' => 'これは設定ファイルで設定された文字列です。', ] ?>ルーティング情報の追記
アプリ名ディレクトリで下記コマンドを実行してルーティングファイルを開く。
$ vi routes/web.php下記の内容を追記する。
アプリ名ディレクトリ/routes/web.phpRoute::get('/config_check', 'ConfigCheckController@config_check')->name('config_check');コントローラファイルの作成と記載
アプリ名ディレクトリで下記コマンドを実行してコントローラファイルを作成する。
$ php artisan make:controller ConfigCheckControllerアプリ名ディレクトリで下記コマンドを実行してただいま作成したコントローラファイルを開く。
$ vi app/Http/Controllers/ConfigCheckController.php下記の様に記載を行う。
アプリ名ディレクトリ/app/Http/Controllers/ConfigCheckController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; // 下記を追記する use Illuminate\Support\Facades\Config; class ConfigCheckController extends Controller { // 下記を追記する public function config_check() { // アプリ名ディレクトリ/config直下に存在するconfig_test.phpファイルで定義されているconfig_strの文字列を$strに格納する処理 $str = Config::get('config_test.config_str'); return view('checks.config_check', [ 'str' => $str, ]); } // 上記までを追記する }ビューファイルの作成と記載
アプリ名ディレクトリで下記コマンドを実行してビューファイルを格納するディレクトリを作成する。
$ mkdir resources/views/checksアプリ名ディレクトリで下記コマンドを実行してビューファイルを作成する。
$ vi resources/views/checks/config_check.blade.php開いたビューファイルに下記の内容を記載する。
アプリ名ディレクトリ/resources/views/checks/config_check.blade.php{{ $str }}確認
アプリ名ディレクトリで下記コマンドを実行してローカルサーバを起動する。
$ php artisan serveブラウザで下記にアクセスする。(Authの認証機能が付与されているLaravelアプリで下記にアクセスする場合、ログインが必要になる場合がある。)
下記の様に、設定ファイルに記載した内容がブラウザ上で表示されていれば作業完了である。
*画像
参考文献
- 投稿日:2020-09-08T08:51:10+09:00
[PHP]365日+曜日を出力する
今日の日付を出力するには下記のように記述する。
day.php<?php print(date('n/j(D)')); ?>
n
は月を取得する
j
は日付けを取得する
D
は曜日を英語の曜日(頭3文字)を取得するtimezoneの設定をしていなければ、直接記述することで変更できる。
day.php<?php date_default_timezone_set('Asia/Tokyo'); print(date('n/j(D)')); ?>ここで、1日後の日付+曜日を取得するにはどうすれば良いか考える。
day.php<?php print(date('n/j(D)', strtotime('+1day')); ?>これで1日後の日付+曜日を取得できた。
※
strtotime
とはString to Timestamp
の略で
文字列をTimestamp型に変換できるファンクションのこと。
+1day
なら1日後
-1day
なら1日前
+365day
なら1年後とすることができる。
strtotime('+1day')という部分をfor文を用いて365回繰り返せば良さそう。
day.php<?php for($i=1; $i<=365; $i++); { $date = strtotime('+' . $i . 'day'); print(date('n/j(D)', $date)); print "\n"; } ?>これで365日分取得できました。
[ 解説 ]
①変数
i=1
として、365以下
で繰り返し、毎回変数iに1を足す
for ($i=1; $i<=365; $i++);②
$date
という変数にstrtotime
の中身を分解して代入
$date = strtotime('+' . $i . 'day'); // strtotime('+1day')の、'+1day'の部分 1を$iとしている③
strtotime
ファンクションを記述していた部分に変数$date
を置くprint(date('n/j(D)', $date));④見やすいように改行する
print "\n";ちなみに、{}の部分は下記のように書き換えることもできる
day.php<?php for ($i=1; $i<=365; $i++): $date = strtotime('+' . $i . 'day'); print(date('n/j(D)', $date)); print "\n"; endfor; ?>こちらの方が何に対する閉じタグなのかがわかりやすい。
while文も同じで下記のように記述できる。while (....): .... endwhile;
以上です。お疲れ様でした。
- 投稿日:2020-09-08T08:51:10+09:00
phpで365日+曜日を出力する
今日の日付を出力するには下記のように記述する。
day.php<?php print(date('n/j(D)')); ?>
n
は月を取得する
j
は日付けを取得する
D
は曜日を英語の曜日(頭3文字)を取得するtimezoneの設定をしていなければ、直接記述することで変更できる。
day.php<?php date_default_timezone_set('Asia/Tokyo'); print(date('n/j(D)')); ?>ここで、1日後の日付+曜日を取得するにはどうすれば良いか考える。
day.php<?php print(date('n/j(D)', strtotime('+1day')); ?>これで1日後の日付+曜日を取得できた。
※
strtotime
とはString to Timestamp
の略で
文字列をTimestamp型に変換できるファンクションのこと。
+1day
なら1日後
-1day
なら1日前
+365day
なら1年後とすることができる。
strtotime('+1day')という部分をfor文を用いて365回繰り返せば良さそう。
day.php<?php for($i=1; $i<=365; $i++); { $date = strtotime('+' . $i . 'day'); print(date('n/j(D)', $date)); print "\n"; } ?>これで365日分取得できました。
[ 解説 ]
①変数
i=1
として、365以下
で繰り返し、毎回変数iに1を足す
for ($i=1; $i<=365; $i++);②
$date
という変数にstrtotime
の中身を分解して代入
$date = strtotime('+' . $i . 'day'); // strtotime('+1day')の、'+1day'の部分 1を$iとしている③
strtotime
ファンクションを記述していた部分に変数$date
を置くprint(date('n/j(D)', $date));④見やすいように改行する
print "\n";ちなみに、{}の部分は下記のように書き換えることもできる
day.php<?php for ($i=1; $i<=365; $i++): $date = strtotime('+' . $i . 'day'); print(date('n/j(D)', $date)); print "\n"; endfor; ?>こちらの方が何に対する閉じタグなのかがわかりやすい。
while文も同じで下記のように記述できる。while (....): .... endwhile;
以上です。お疲れ様でした。
- 投稿日:2020-09-08T00:44:01+09:00
fitbitのapi連携と集中力判定(PHP,Javascript)
記事の概要
作成したポートフォリオの解説です。以下をまとめています。
- 背景
- 主な機能
- 開発手順
- 工夫点
- 課題
背景
「ポモドーロ・テクニック」という時間管理術が話題になっており、そのためのアプリなどもあるが、実際に集中しているかどうかを判断するのは単純なポモドーロタイマーだけでは難しい。
今回は心拍数と集中力の関係に着目し、集中力を判定するアプリを作成しました。スペック
- 言語
- PHP 7.4.2
- javascript
- DBMS
- MySQL 5.7.26
- 開発環境
- MacOS Catalina 10.15.5
- MAMP 5.7
- ライブラリ
- jquery
- フレームワーク
- Bootstrap 4.2
- バージョン管理
- Git 2.24.3
- 本番環境
- xserver
- ウェアラブル端末
- fitbit inspireHR
主な機能
ポモドーロタイマー
・ホーム画面
スタートを押すと25分タイマーが作動します。
タイマーの下には今日と今週の作業記録が表示されます。
・25分が終わると
「お疲れ様です。5分休憩しましょう!」が表示されます。
データ
・データ画面
「1週間のポモドーロ回数の推移」と「直近の作業の心拍数の推移が表示されます。
開発手順
- 要件定義
- 環境設定
- データベース設計
- コーディング
- xserverデプロイ
1.用件定義
今回作成するアプリに必要な機能
・タイマー機能
・データ記録
・データ表示
・チャート表示
・fitbitのAPIからのデータ取得php,Javascriptで開発を行う。
2.環境選定
本番環境では知名度の高い「xserver」を使用する。
GitとGitHubは練習として使っていく。3.データベース設計
カラムはシンプルに
・ID
・start_at(作業開始時間)
・stop_at(作業終了時間)4.コーディング
コーディングを実施
4.1データベース作成
MAMPのphpMyAdminを使ってデータベースを作成。
4.2ホーム画面
タイマーのカウント表示はJavasciptで動的に実装。
「START」、「STOP」、「RESET」ボタンを押した時の処理を実装。
作業終了時、DBに自動的に登録される。(Ajax)
画面のデザインはBootstrapを用いて時間短縮。4.3データ画面
グラフはchartistというjsのライブラリで作成。
作業記録はホーム画面同様、MAMPのDBから取得する。心拍数の記録はfitbitのAPIから取得。
以下サイトを参考にカスタマイズ
https://qiita.com/RINYU_DRVO/items/6607a0aa7ca3294f8e47MAMPのDBから直近のデータを取り出してAPIに渡すことで
直近の作業時間あたりの心拍数の推移を取得。
(ここが一番苦労した、、、)取得した心拍数のデータをもとに
平均心拍数と集中力判定を実施。集中力判定は以下を基準に作成。
1.高集中:平均心拍数が高く、心拍数の分散(以下、分散)が低い
2.高ストレス:平均心拍数が高く、分散が高い
3.安定集中:平均心拍数が低く、分散が低い
4.集中していない:平均心拍数が低く、分散が高い※閾値については今後調整します。
5.xserverデプロイ
本番環境ではxserverを使用。
xserver上にDBを立て、プログラムもDB接続部分の修正。工夫点
・世の中にない機能を考え、実装まで持って行った
・自分で使いたいと思うようなアプリにすることができた今後の課題
一通りの動作ができた時点で完成としました。
主な課題は以下の通りです。
- チャートの追加(1週間ごとの比較など)
- 作業内容の記録(どんな作業の時にどんな集中状態なのか評価するため)
参考文献
GitHubアカウント
freedog1
https://github.com/freedog1/pomodoro