20201127のPHPに関する記事は20件です。

Codeignater3を使ったMPAサイトからNuxt.js+Vueへのサイトリニューアル

はじめに

 Vue.js(CDN版)を採用したPHP動的サイトから、Nuxt.js+Vueを使ったいわゆるSPAサイトへのリニューアルを敢行した。

経緯と歴史

 最初は、フレームワークを使わないサイトとして設計したが、のちにSmatryを採用し、Viewとロジックを分離した第二世代をリリース。その後、Codeignater3を採用し、本格的にPHPフレームワークを採用した第三世代と進化していったが、リプレースの切っ掛けはいずれも機能拡張を繰り返し、いずれも 現状に限界を感じよりモダンな技術を採用していった経緯がある 。

今回は何が限界だったのか

Vueライブラリの複雑化

 当初はJqueryを採用していたが、Vue.jsの導入に合わせライブラリが複雑化し、バージョンアップへの不安と煩雑さがつきまとうことになった。

痒いところに手が届かないフレームワークとViewのカスタマイズの煩雑化

 UIフレームワークとして採用していたMaterializeはVue前提ではないため、開発を進めるにつれCSSをカスタマイズを行う面が多くなり、フレームワークのメリットが少なくなるとともに、CSSの開発に多くの時間が取られることになった。

更新手順およびViewの煩雑化・複雑化

 ページが複雑化するに伴い、PHPのinclude機能で使い回すView画面がスパゲティ化するようになり、速度向上で導入したPWAのServiceWorkerのバージョンを手作業で書き換えるなどの更新の手間が極めて煩雑になりつつあった。

ベストプラクティスな技術を試したい

 こうした状況が重なり、最新のフロントエンドのベストプラクティスとも言える技術で再構築を検討することとなった。当初はログインシステムの関係からVue-Cliのみを導入し、MPAのままの構築を行う事も検討したが、ユーザーへのID・パスワードの再設定のデメリットを考慮しても、Firebase Authenticationを採用した方が、セキュリティ面やプログラミング・維持などあらゆる場面でメリットがあると判断。Nuxt.js+VueによるSPAを採用することとなった。

開発

バックエンド

 SPAとなれば、必然的にフロントとバックエンドが分離されるが、リニューアル前より無限スクロールを採用していたことから部分的にCodeignaterにAPIを実装し、axiosを用いた非同期通信で情報を取得ていたこともあり、開発時間短縮のために、バックエンドは引き続きCodeignaterを改良およびリファクタリングしてそのまま採用することとなった。

フロントエンド

 Vue3およびTSの採用については現時点では見送ることとして、Webにサンプルなどのドキュメントが豊富なVue2.6.11とJSを採用した。
UIフレームワークはvuetifyを採用。最初はとっつきにくかったけど、高機能でデザインの完成度が高いので慣れてしまえば本当に楽。最初からコンポーネント至高なので、こちらも楽だが、無秩序にやってしまうとまたスパゲティになるので注意したい。

開発

 10月頭に開発がスタートをして、形になるリリースまで1ヶ月半。細かな機能追加と調整に二週間程度で完成することになった。

感想

 今回のリニューアルは技術的に本当に勉強になりましたし、サイトの管理と開発が本当に楽になりました。フロントとバックを分離した疎結合化は精神的にも楽になった感があります。Nuxt.jsは現時点でのベストプラクティス的な技術ではありますが、技術的に未熟で採用できないという事情はあるにしろ、基礎を習得しないと応用はできないとはいえ、随分と遠回りした印象もあります。これまでは煩雑な保守管理に時間を浪費していましたが、今後のページはペラ1でも無い限りNuxt.jsファーストで行きたいと思います。

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

CentOS 8にPHP 8.0, PHP-FPM 8.0をインストール(Remi's RPM repository)

はじめに

Remi's RPM repositoryを利用してCentOS8にPHP8.0をインストール
親記事:PHP, PHP-FPMの各種インストール方法とEOLまとめ
参考:Remi's RPM repository

サポート

本手法で導入した場合、PHP: Supported Versions/PHP: Unsupported Branchesより、2023-11-26がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

note

  • 他のPHPモジュールを入れていた場合、下記コマンドでモジュールのリセットが必要
# dnf module reset php

LOG

インストール

# cat /etc/redhat-release
CentOS Linux release 8.2.2004 (Core)

# dnf install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm
... 略

# dnf module install -y php:remi-8.0
# dnf install -y which
... 略

php-fpm起動/停止

# systemctl start php-fpm
# systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; vendor preset: disabled)
   Active: active (running) since Fri 2020-11-27 13:59:40 UTC; 41s ago
 Main PID: 271 (php-fpm)
   Status: "Processes active: 0, idle: 5, Requests: 0, slow: 0, Traffic: 0req/sec"
    Tasks: 6 (limit: 8920)
   Memory: 19.5M
   CGroup: /docker/acc0e606a3a3ae90da45bde51910a623ac10fc7d9822f68a03c8eb973969839e/system.slice/php-fpm.service
           tq271 php-fpm: master process (/etc/php-fpm.conf)
           tq272 php-fpm: pool www
           tq273 php-fpm: pool www
           tq274 php-fpm: pool www
           tq275 php-fpm: pool www
           mq276 php-fpm: pool www

Nov 27 13:59:40 acc0e606a3a3 systemd[1]: Starting The PHP FastCGI Process Manager...
Nov 27 13:59:40 acc0e606a3a3 systemd[1]: Started The PHP FastCGI Process Manager.
# systemctl stop php-fpm
# systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; vendor preset: disabled)
   Active: inactive (dead)

Nov 27 13:59:40 acc0e606a3a3 systemd[1]: Starting The PHP FastCGI Process Manager...
Nov 27 13:59:40 acc0e606a3a3 systemd[1]: Started The PHP FastCGI Process Manager.
Nov 27 14:01:05 acc0e606a3a3 systemd[1]: Stopping The PHP FastCGI Process Manager...
Nov 27 14:01:05 acc0e606a3a3 systemd[1]: Stopped The PHP FastCGI Process Manager.

php-fpm自動起動設定/設定解除

# systemctl enable php-fpm
Created symlink /etc/systemd/system/multi-user.target.wants/php-fpm.service → /usr/lib/systemd/system/php-fpm.service.
# systemctl list-unit-files --type=service |grep php-fpm
php-fpm.service                        enabled
# systemctl disable php-fpm
Removed /etc/systemd/system/multi-user.target.wants/php-fpm.service.
# systemctl list-unit-files --type=service |grep php-fpm
php-fpm.service                        disabled

各種確認

# which php
/usr/bin/php

# php -v
PHP 8.0.0 (cli) (built: Nov 24 2020 17:04:03) ( NTS gcc x86_64 )
Copyright (c) The PHP Group
Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies

# php -i | grep php.ini
Configuration File (php.ini) Path => /etc
Loaded Configuration File => /etc/php.ini

# which php-fpm
/usr/sbin/php-fpm

# /usr/sbin/php-fpm -v
PHP 8.0.0 (fpm-fcgi) (built: Nov 24 2020 17:04:03)
Copyright (c) The PHP Group
Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies

# dnf module info php:remi-8.0
Failed to set locale, defaulting to C.UTF-8
Last metadata expiration check: 0:01:10 ago on Fri Nov 27 14:03:05 2020.
Name             : php
Stream           : remi-8.0 [e] [a]
Version          : 20201127071524
Context          : 00000000
Architecture     : x86_64
Profiles         : common [d] [i], devel, minimal
Default profiles : common
Repo             : remi-modular
Summary          : PHP scripting language
Description      : Alternative php 8.0 module. The remi-8.0 stream provides more recent versions and more packages than default 8.0 stream.
Requires         : platform:[el8]
Artifacts        : apcu-panel-0:5.1.18-7.el8.remi.8.0.noarch
                 : apcu-panel-0:5.1.19-1.el8.remi.8.0.noarch
                 : libzip-0:1.7.3-1.el8.remi.x86_64
                 : libzip-devel-0:1.7.3-1.el8.remi.x86_64
                 : libzip-tools-0:1.7.3-1.el8.remi.x86_64
                 : php-0:8.0.0-1.el8.remi.x86_64
                 : php-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-ast-0:1.0.10-2.el8.remi.8.0.x86_64
                 : php-ast-0:1.0.10-3.el8.remi.8.0.x86_64
                 : php-bcmath-0:8.0.0-1.el8.remi.x86_64
                 : php-bcmath-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-brotli-0:0.11.1-2.el8.remi.8.0.x86_64
                 : php-brotli-0:0.11.1-3.el8.remi.8.0.x86_64
                 : php-cli-0:8.0.0-1.el8.remi.x86_64
                 : php-cli-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-common-0:8.0.0-1.el8.remi.x86_64
                 : php-common-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-componere-0:3.1.2-1.el8.remi.8.0.x86_64
                 : php-dba-0:8.0.0-1.el8.remi.x86_64
                 : php-dba-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-dbg-0:8.0.0-1.el8.remi.x86_64
                 : php-dbg-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-devel-0:8.0.0-1.el8.remi.x86_64
                 : php-devel-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-embedded-0:8.0.0-1.el8.remi.x86_64
                 : php-embedded-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-enchant-0:8.0.0-1.el8.remi.x86_64
                 : php-enchant-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-fedora-autoloader-0:1.0.1-2.el8.remi.noarch
                 : php-fedora-autoloader-devel-0:1.0.1-2.el8.remi.noarch
                 : php-ffi-0:8.0.0-1.el8.remi.x86_64
                 : php-ffi-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-fpm-0:8.0.0-1.el8.remi.x86_64
                 : php-fpm-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-gd-0:8.0.0-1.el8.remi.x86_64
                 : php-gd-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-geos-0:1.0.0-17.el8.remi.8.0.x86_64
                 : php-geos-0:1.0.0-18.el8.remi.8.0.x86_64
                 : php-gmp-0:8.0.0-1.el8.remi.x86_64
                 : php-gmp-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-imap-0:8.0.0-1.el8.remi.x86_64
                 : php-imap-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-intl-0:8.0.0-1.el8.remi.x86_64
                 : php-intl-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-ldap-0:8.0.0-1.el8.remi.x86_64
                 : php-ldap-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-litespeed-0:8.0.0-1.el8.remi.x86_64
                 : php-litespeed-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-lz4-0:0.3.7-1.el8.remi.8.0.x86_64
                 : php-lz4-0:0.4.0-1.el8.remi.8.0.x86_64
                 : php-maxminddb-0:1.7.0-3.el8.remi.8.0.x86_64
                 : php-maxminddb-0:1.8.0-2.el8.remi.8.0.x86_64
                 : php-mbstring-0:8.0.0-1.el8.remi.x86_64
                 : php-mbstring-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-mysqlnd-0:8.0.0-1.el8.remi.x86_64
                 : php-mysqlnd-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-oci8-0:8.0.0-1.el8.remi.x86_64
                 : php-oci8-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-odbc-0:8.0.0-1.el8.remi.x86_64
                 : php-odbc-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-opcache-0:8.0.0-1.el8.remi.x86_64
                 : php-opcache-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-pdlib-0:1.0.2-4.el8.remi.8.0.x86_64
                 : php-pdlib-0:1.0.2-5.el8.remi.8.0.x86_64
                 : php-pdo-0:8.0.0-1.el8.remi.x86_64
                 : php-pdo-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-pdo-dblib-0:8.0.0-1.el8.remi.x86_64
                 : php-pdo-dblib-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-pdo-firebird-0:8.0.0-1.el8.remi.x86_64
                 : php-pdo-firebird-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-pear-1:1.10.12-2.el8.remi.noarch
                 : php-pear-1:1.10.12-4.el8.remi.noarch
                 : php-pecl-amqp-0:1.10.2-2.el8.remi.8.0.x86_64
                 : php-pecl-amqp-0:1.10.2-3.el8.remi.8.0.x86_64
                 : php-pecl-apcu-0:5.1.18-7.el8.remi.8.0.x86_64
                 : php-pecl-apcu-0:5.1.19-1.el8.remi.8.0.x86_64
                 : php-pecl-apcu-devel-0:5.1.18-7.el8.remi.8.0.x86_64
                 : php-pecl-apcu-devel-0:5.1.19-1.el8.remi.8.0.x86_64
                 : php-pecl-apfd-0:1.0.2-1.el8.remi.8.0.x86_64
                 : php-pecl-apfd-0:1.0.2-2.el8.remi.8.0.x86_64
                 : php-pecl-base58-0:0.1.4-4.el8.remi.8.0.x86_64
                 : php-pecl-base58-0:0.1.4-5.el8.remi.8.0.x86_64
                 : php-pecl-bitset-0:3.0.1-5.el8.remi.8.0.x86_64
                 : php-pecl-couchbase3-0:3.0.4-2.el8.remi.8.0.x86_64
                 : php-pecl-csv-0:0.3.1-2.el8.remi.8.0.x86_64
                 : php-pecl-datadog-trace-0:0.50.0-1.el8.remi.8.0.x86_64
                 : php-pecl-datadog-trace-0:0.51.0-1.el8.remi.8.0.x86_64
                 : php-pecl-dbase-0:7.1.0~RC2-1.el8.remi.8.0.x86_64
                 : php-pecl-dio-0:0.2.0-3.el8.remi.8.0.x86_64
                 : php-pecl-dio-0:0.2.0-4.el8.remi.8.0.x86_64
                 : php-pecl-ds-0:1.3.0-1.el8.remi.8.0.x86_64
                 : php-pecl-ds-0:1.3.0-2.el8.remi.8.0.x86_64
                 : php-pecl-env-0:0.2.1-10.el8.remi.8.0.x86_64
                 : php-pecl-env-0:0.2.1-11.el8.remi.8.0.x86_64
                 : php-pecl-event-0:3.0.0-1.el8.remi.8.0.x86_64
                 : php-pecl-fann-0:1.1.1-18.el8.remi.8.0.x86_64
                 : php-pecl-fann-0:1.1.1-19.el8.remi.8.0.x86_64
                 : php-pecl-geoip-0:1.1.1-14.el8.remi.8.0.x86_64
                 : php-pecl-geospatial-0:0.2.1-3.el8.remi.8.0.x86_64
                 : php-pecl-grpc-0:1.34.0~RC1-1.el8.remi.8.0.x86_64
                 : php-pecl-grpc-0:1.34.0~RC2-1.el8.remi.8.0.x86_64
                 : php-pecl-http-0:4.0.0~beta1-1.el8.remi.8.0.x86_64
                 : php-pecl-http-0:4.0.0~beta1-2.el8.remi.8.0.x86_64
                 : php-pecl-http-devel-0:4.0.0~beta1-1.el8.remi.8.0.x86_64
                 : php-pecl-http-devel-0:4.0.0~beta1-2.el8.remi.8.0.x86_64
                 : php-pecl-http-message-0:0.2.2-3.el8.remi.8.0.x86_64
                 : php-pecl-http-message-0:0.2.2-4.el8.remi.8.0.x86_64
                 : php-pecl-http-message-devel-0:0.2.2-3.el8.remi.8.0.x86_64
                 : php-pecl-http-message-devel-0:0.2.2-4.el8.remi.8.0.x86_64
                 : php-pecl-igbinary-0:3.1.5-3.el8.remi.8.0.x86_64
                 : php-pecl-igbinary-0:3.1.6-1.el8.remi.8.0.x86_64
                 : php-pecl-igbinary-devel-0:3.1.5-3.el8.remi.8.0.x86_64
                 : php-pecl-igbinary-devel-0:3.1.6-1.el8.remi.8.0.x86_64
                 : php-pecl-imagick-0:3.4.4-12.el8.remi.8.0.x86_64
                 : php-pecl-imagick-0:3.4.4-14.el8.remi.8.0.x86_64
                 : php-pecl-imagick-devel-0:3.4.4-12.el8.remi.8.0.x86_64
                 : php-pecl-imagick-devel-0:3.4.4-14.el8.remi.8.0.x86_64
                 : php-pecl-inotify-0:2.0.0-11.el8.remi.8.0.x86_64
                 : php-pecl-inotify-0:2.0.0-12.el8.remi.8.0.x86_64
                 : php-pecl-ip2location-0:8.1.0-1.el8.remi.8.0.x86_64
                 : php-pecl-ip2location-0:8.1.1-1.el8.remi.8.0.x86_64
                 : php-pecl-ip2proxy-0:3.0.0-1.el8.remi.8.0.x86_64
                 : php-pecl-ip2proxy-0:3.0.1-1.el8.remi.8.0.x86_64
                 : php-pecl-json-post-0:1.0.2-3.el8.remi.8.0.x86_64
                 : php-pecl-json-post-0:1.0.2-4.el8.remi.8.0.x86_64
                 : php-pecl-krb5-0:1.1.4-1.el8.remi.8.0.x86_64
                 : php-pecl-krb5-0:1.1.4-2.el8.remi.8.0.x86_64
                 : php-pecl-krb5-devel-0:1.1.4-1.el8.remi.8.0.x86_64
                 : php-pecl-krb5-devel-0:1.1.4-2.el8.remi.8.0.x86_64
                 : php-pecl-lzf-0:1.6.8-3.el8.remi.8.0.x86_64
                 : php-pecl-lzf-0:1.6.8-4.el8.remi.8.0.x86_64
                 : php-pecl-mailparse-0:3.1.1-1.el8.remi.8.0.x86_64
                 : php-pecl-mailparse-0:3.1.1-2.el8.remi.8.0.x86_64
                 : php-pecl-mcrypt-0:1.0.3-4.el8.remi.8.0.x86_64
                 : php-pecl-memcached-0:3.1.5-2.el8.remi.8.0.x86_64
                 : php-pecl-memcached-0:3.1.5-3.el8.remi.8.0.x86_64
                 : php-pecl-memprof-0:2.1.1-1.el8.remi.8.0.x86_64
                 : php-pecl-memprof-0:2.1.2-1.el8.remi.8.0.x86_64
                 : php-pecl-mongodb-0:1.9.0-1.el8.remi.8.0.x86_64
                 : php-pecl-mongodb-0:1.9.0~rc1-1.el8.remi.8.0.x86_64
                 : php-pecl-msgpack-0:2.1.1-2.el8.remi.8.0.x86_64
                 : php-pecl-msgpack-0:2.1.1-3.el8.remi.8.0.x86_64
                 : php-pecl-msgpack-devel-0:2.1.1-2.el8.remi.8.0.x86_64
                 : php-pecl-msgpack-devel-0:2.1.1-3.el8.remi.8.0.x86_64
                 : php-pecl-mustache-0:0.9.2-1.el8.remi.8.0.x86_64
                 : php-pecl-mysqlnd-azure-0:1.1.1-2.el8.remi.8.0.x86_64
                 : php-pecl-oauth-0:2.0.7-1.el8.remi.8.0.x86_64
                 : php-pecl-oauth-0:2.0.7-2.el8.remi.8.0.x86_64
                 : php-pecl-pcov-0:1.0.6-4.el8.remi.8.0.x86_64
                 : php-pecl-pq-0:2.1.8-1.el8.remi.8.0.x86_64
                 : php-pecl-pq-0:2.1.8-2.el8.remi.8.0.x86_64
                 : php-pecl-protobuf-0:3.13.0.1-1.el8.remi.8.0.x86_64
                 : php-pecl-psr-0:1.0.0-5.el8.remi.8.0.x86_64
                 : php-pecl-psr-0:1.0.1-1.el8.remi.8.0.x86_64
                 : php-pecl-psr-devel-0:1.0.0-5.el8.remi.8.0.x86_64
                 : php-pecl-psr-devel-0:1.0.1-1.el8.remi.8.0.x86_64
                 : php-pecl-raphf-0:2.0.1-2.el8.remi.8.0.x86_64
                 : php-pecl-raphf-0:2.0.1-3.el8.remi.8.0.x86_64
                 : php-pecl-raphf-devel-0:2.0.1-2.el8.remi.8.0.x86_64
                 : php-pecl-raphf-devel-0:2.0.1-3.el8.remi.8.0.x86_64
                 : php-pecl-redis5-0:5.3.2-1.el8.remi.8.0.x86_64
                 : php-pecl-redis5-0:5.3.2~RC2-1.el8.remi.8.0.x86_64
                 : php-pecl-rpminfo-0:0.5.1-5.el8.remi.8.0.x86_64
                 : php-pecl-rpminfo-0:0.5.1-6.el8.remi.8.0.x86_64
                 : php-pecl-rrd-0:2.0.1-8.el8.remi.8.0.x86_64
                 : php-pecl-rrd-0:2.0.1-9.el8.remi.8.0.x86_64
                 : php-pecl-runkit7-0:4.0.0~a1-1.el8.remi.8.0.x86_64
                 : php-pecl-runkit7-0:4.0.0~a2-1.el8.remi.8.0.x86_64
                 : php-pecl-scrypt-0:1.4.2-9.el8.remi.8.0.x86_64
                 : php-pecl-selinux-0:0.5.0-3.el8.remi.8.0.x86_64
                 : php-pecl-selinux-0:0.5.0-4.el8.remi.8.0.x86_64
                 : php-pecl-solr2-0:2.5.1-1.el8.remi.8.0.x86_64
                 : php-pecl-solr2-0:2.5.1-2.el8.remi.8.0.x86_64
                 : php-pecl-ssdeep-0:1.1.0-6.el8.remi.8.0.x86_64
                 : php-pecl-ssdeep-0:1.1.0-7.el8.remi.8.0.x86_64
                 : php-pecl-ssh2-0:1.2-3.el8.remi.8.0.x86_64
                 : php-pecl-ssh2-0:1.2-4.el8.remi.8.0.x86_64
                 : php-pecl-swoole4-0:4.5.8-2.el8.remi.8.0.x86_64
                 : php-pecl-swoole4-0:4.5.9-1.el8.remi.8.0.x86_64
                 : php-pecl-swoole4-devel-0:4.5.8-2.el8.remi.8.0.x86_64
                 : php-pecl-swoole4-devel-0:4.5.9-1.el8.remi.8.0.x86_64
                 : php-pecl-translit-0:0.7.0-5.el8.remi.8.0.x86_64
                 : php-pecl-translit-0:0.7.1-1.el8.remi.8.0.x86_64
                 : php-pecl-uuid-0:1.1.0-4.el8.remi.8.0.x86_64
                 : php-pecl-uuid-0:1.2.0-1.el8.remi.8.0.x86_64
                 : php-pecl-vips-0:1.0.12-2.el8.remi.8.0.x86_64
                 : php-pecl-vips-0:1.0.12-3.el8.remi.8.0.x86_64
                 : php-pecl-xattr-0:1.4.0-3.el8.remi.8.0.x86_64
                 : php-pecl-xattr-0:1.4.0-4.el8.remi.8.0.x86_64
                 : php-pecl-xdebug3-0:3.0.0-1.el8.remi.8.0.x86_64
                 : php-pecl-xdebug3-0:3.0.0~rc1-1.el8.remi.8.0.x86_64
                 : php-pecl-xdiff-0:2.0.1-8.el8.remi.8.0.x86_64
                 : php-pecl-xhprof-0:2.2.2-1.el8.remi.8.0.x86_64
                 : php-pecl-xhprof-0:2.2.3-1.el8.remi.8.0.x86_64
                 : php-pecl-xlswriter-0:1.3.6-8.el8.remi.8.0.x86_64
                 : php-pecl-xlswriter-0:1.3.6-9.el8.remi.8.0.x86_64
                 : php-pecl-xmldiff-0:1.1.2-15.el8.remi.8.0.x86_64
                 : php-pecl-xmldiff-0:1.1.2-16.el8.remi.8.0.x86_64
                 : php-pecl-xmldiff-devel-0:1.1.2-15.el8.remi.8.0.x86_64
                 : php-pecl-xmldiff-devel-0:1.1.2-16.el8.remi.8.0.x86_64
                 : php-pecl-xmlrpc-0:1.0.0~DEV.20200602-3.el8.remi.8.0.x86_64
                 : php-pecl-xmlrpc-0:1.0.0~DEV.20200602-4.el8.remi.8.0.x86_64
                 : php-pecl-yac-0:2.2.1-2.el8.remi.8.0.x86_64
                 : php-pecl-yac-0:2.2.1-3.el8.remi.8.0.x86_64
                 : php-pecl-yaconf-0:1.1.0-2.el8.remi.8.0.x86_64
                 : php-pecl-yaconf-0:1.1.0-3.el8.remi.8.0.x86_64
                 : php-pecl-yaconf-devel-0:1.1.0-2.el8.remi.8.0.x86_64
                 : php-pecl-yaconf-devel-0:1.1.0-3.el8.remi.8.0.x86_64
                 : php-pecl-yaml-0:2.2.0~b2-1.el8.remi.8.0.x86_64
                 : php-pecl-yaml-0:2.2.0~b2-2.el8.remi.8.0.x86_64
                 : php-pecl-yaz-0:1.2.3-4.el8.remi.8.0.x86_64
                 : php-pecl-zip-0:1.19.1-1.el8.remi.8.0.x86_64
                 : php-pecl-zip-0:1.19.2-1.el8.remi.8.0.x86_64
                 : php-pecl-zmq-0:1.1.3-13.el8.remi.8.0.x86_64
                 : php-pecl-zmq-0:1.1.3-14.el8.remi.8.0.x86_64
                 : php-pgsql-0:8.0.0-1.el8.remi.x86_64
                 : php-pgsql-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-phpiredis-0:1.0.1-1.el8.remi.8.0.x86_64
                 : php-phpiredis-0:1.0.1-2.el8.remi.8.0.x86_64
                 : php-process-0:8.0.0-1.el8.remi.x86_64
                 : php-process-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-pspell-0:8.0.0-1.el8.remi.x86_64
                 : php-pspell-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-realpath-turbo-0:2.0.0-1.el8.remi.8.0.x86_64
                 : php-realpath-turbo-0:2.0.0-2.el8.remi.8.0.x86_64
                 : php-smbclient-0:1.0.0-8.el8.remi.8.0.x86_64
                 : php-smbclient-0:1.0.0-9.el8.remi.8.0.x86_64
                 : php-snappy-0:0.2.0-1.el8.remi.8.0.x86_64
                 : php-snappy-0:0.2.0-2.el8.remi.8.0.x86_64
                 : php-snmp-0:8.0.0-1.el8.remi.x86_64
                 : php-snmp-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-soap-0:8.0.0-1.el8.remi.x86_64
                 : php-soap-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-sodium-0:8.0.0-1.el8.remi.x86_64
                 : php-sodium-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-sqlsrv-0:5.9.0~preview1-1.el8.remi.8.0.x86_64
                 : php-tidy-0:8.0.0-1.el8.remi.x86_64
                 : php-tidy-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-xml-0:8.0.0-1.el8.remi.x86_64
                 : php-xml-0:8.0.0~rc5-9.el8.remi.x86_64
                 : php-zstd-0:0.9.0-3.el8.remi.8.0.x86_64
                 : php-zstd-0:0.9.0-4.el8.remi.8.0.x86_64
                 : php-zstd-devel-0:0.9.0-3.el8.remi.8.0.x86_64
                 : php-zstd-devel-0:0.9.0-4.el8.remi.8.0.x86_64
                 : unit-php-0:1.20.0-1.el8.remi.8.0.x86_64
                 : unit-php-0:1.21.0-1.el8.remi.8.0.x86_64
                 : xhprof-0:2.2.2-1.el8.remi.8.0.noarch
                 : xhprof-0:2.2.3-1.el8.remi.8.0.noarch

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled, [a]ctive
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CentOS 7にPHP-FPM 8.0をインストール(Remi's RPM repository)

はじめに

Remi's RPM repositoryを利用してCentOS7にPHP-FPM8.0をインストール
親記事:PHP, PHP-FPMの各種インストール方法とEOLまとめ
参考:Remi's RPM repository

サポート

本手法で導入した場合、PHP: Supported Versions/PHP: Unsupported Branchesより、2023-11-26がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

note

  • インストール後の更新はyum --enablerepo=remi-php80 update

LOG

インストール

# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

# yum install -y https://rpms.remirepo.net/enterprise/remi-release-7.rpm
... 略

# yum install -y --enablerepo=remi-php80 php-fpm which
... 略

php-fpm起動/停止

# systemctl start php-fpm
# systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; vendor preset: disabled)
   Active: active (running) since Fri 2020-11-27 13:55:54 UTC; 4s ago
 Main PID: 333 (php-fpm)
   Status: "Ready to handle connections"
   CGroup: /docker/78c480b53127eafffc6bfcdb3eb1e0031f072a827d8afb4f2b0bbd97949deba5/docker/78c480b53127eafffc6bfcdb3eb1e0031f072a827d8afb4f2b0bbd97949deba5/system.slice/php-fpm.service
           tq333 php-fpm: master process (/etc/php-fpm.conf)
           tq334 php-fpm: pool www
           tq335 php-fpm: pool www
           tq336 php-fpm: pool www
           tq337 php-fpm: pool www
           mq338 php-fpm: pool www
           ? 333 php-fpm: master process (/etc/php-fpm.conf)

Nov 27 13:55:54 78c480b53127 systemd[1]: Starting The PHP FastCGI Process Manager...
Nov 27 13:55:54 78c480b53127 systemd[1]: Started The PHP FastCGI Process Manager.
# systemctl stop php-fpm
# systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; vendor preset: disabled)
   Active: inactive (dead)

Nov 27 13:55:54 78c480b53127 systemd[1]: Starting The PHP FastCGI Process Manager...
Nov 27 13:55:54 78c480b53127 systemd[1]: Started The PHP FastCGI Process Manager.
Nov 27 13:56:13 78c480b53127 systemd[1]: Stopping The PHP FastCGI Process Manager...
Nov 27 13:56:13 78c480b53127 systemd[1]: Stopped The PHP FastCGI Process Manager.

php-fpm自動起動設定/設定解除

# systemctl enable php-fpm
Created symlink from /etc/systemd/system/multi-user.target.wants/php-fpm.service to /usr/lib/systemd/system/php-fpm.service.

# systemctl list-unit-files --type=service |grep php-fpm
php-fpm.service                        enabled
# systemctl disable php-fpm
Removed symlink /etc/systemd/system/multi-user.target.wants/php-fpm.service.

# systemctl list-unit-files --type=service |grep php-fpm
php-fpm.service                        disabled

各種確認

# which php
which: no php in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin)

# which php-fpm
/usr/sbin/php-fpm

# php-fpm -v
PHP 8.0.0 (fpm-fcgi) (built: Nov 24 2020 17:04:03)
Copyright (c) The PHP Group
Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies

# yum info php-fpm
Loaded plugins: fastestmirror, ovl
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * epel: nrt.edge.kernel.org
 * extras: ftp.riken.jp
 * remi-safe: ftp.riken.jp
 * updates: ftp.riken.jp
Installed Packages
Name        : php-fpm
Arch        : x86_64
Version     : 8.0.0
Release     : 1.el7.remi
Size        : 6.2 M
Repo        : installed
From repo   : remi-php80
Summary     : PHP FastCGI Process Manager
URL         : http://www.php.net/
License     : PHP and Zend and BSD and MIT and ASL 1.0 and NCSA
Description : PHP-FPM (FastCGI Process Manager) is an alternative PHP FastCGI
            : implementation with some additional features useful for sites of
            : any size, especially busier sites.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CentOS 7にPHP 8.0をインストール(Remi's RPM repository)

はじめに

Remi's RPM repositoryを利用してCentOS7にPHP8.0をインストール
親記事:PHP, PHP-FPMの各種インストール方法とEOLまとめ
参考:Remi's RPM repository

サポート

本手法で導入した場合、PHP: Supported Versions/PHP: Unsupported Branchesより、2023-11-26がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

note

  • インストール後の更新は yum --enablerepo=remi-php80 update

LOG

インストール

# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

# yum install -y https://rpms.remirepo.net/enterprise/remi-release-7.rpm
... 略

# yum install -y --enablerepo=remi-php80 php which
... 略

各種確認

# which php
/usr/bin/php

# php -v
PHP 8.0.0 (cli) (built: Nov 24 2020 17:04:03) ( NTS gcc x86_64 )
Copyright (c) The PHP Group
Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies

# php -i | grep php.ini
Configuration File (php.ini) Path => /etc
Loaded Configuration File => /etc/php.ini

# yum info php
Loaded plugins: fastestmirror, ovl
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * epel: nrt.edge.kernel.org
 * extras: ftp.riken.jp
 * remi-safe: ftp.riken.jp
 * updates: ftp.riken.jp
Installed Packages
Name        : php
Arch        : x86_64
Version     : 8.0.0
Release     : 1.el7.remi
Size        : 5.1 M
Repo        : installed
From repo   : remi-php80
Summary     : PHP scripting language for creating dynamic web sites
URL         : http://www.php.net/
License     : PHP and Zend and BSD and MIT and ASL 1.0 and NCSA
Description : PHP is an HTML-embedded scripting language. PHP attempts to make it
            : easy for developers to write dynamically generated web pages. PHP also
            : offers built-in database integration for several commercial and
            : non-commercial database management systems, so writing a
            : database-enabled webpage with PHP is fairly simple. The most common
            : use of PHP coding is probably as a replacement for CGI scripts.
            :
            : The php package contains the module (often referred to as mod_php)
            : which adds support for the PHP language to Apache HTTP Server.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPで階乗計算

まずは普通に再帰関数で。

test1.php
<?php
function factorial($n) {
    if($n > 0) return $n * factorial($n - 1);
    return 1;
}

for($n = 1; $n <= 180; ++$n) {
    echo "$n! = ", factorial($n), "\n";
}

結果

1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 1307674368000
16! = 20922789888000
17! = 355687428096000
18! = 6402373705728000
19! = 121645100408832000
20! = 2432902008176640000
21! = 5.1090942171709E+19
22! = 1.1240007277776E+21
23! = 2.5852016738885E+22
24! = 6.2044840173324E+23
        :
       中略
        :
168! = 2.5260757449732E+302
169! = 4.2690680090047E+304
170! = 7.257415615308E+306
171! = INF
172! = INF
173! = INF
        :

当然ですが、21!から指数表記に、171!以降はINFに。
ちょっと物足りないので、文字列と配列で1桁ずつ10進数演算するよう変更。

test2.php
<?php
function factorial($n) {
    if($n > 0) return intMul($n, factorial($n - 1));
    return 1;
}

function intMul($a, $b) {
    $a = "$a";
    $b = "$b";
    $r = [];
    $al = strlen($a);
    $bl = strlen($b);

    for($i = 0; $i < $al; ++$i) {
        $_a = $al - 1 - $i;
        for($j = 0; $j < $bl; ++$j) {
            $_b = $bl - 1 - $j;
            $k = $i + $j;
            if(!isset($r[$k])) $r[$k] = 0;
            $r[$k] += $a[$_a] * $b[$_b];
            if($r[$k] > 9) {
                $k1 = $k + 1;
                if(!isset($r[$k1])) $r[$k1] = 0;
                $r[$k1] += (int)($r[$k] / 10);
                $r[$k] %= 10;
            }
        }
    }
    return strrev(implode('', $r));
}

for($n = 1; $n <= 180; ++$n) {
    echo "$n! = ", factorial($n), "\n";
}

結果

1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 1307674368000
16! = 20922789888000
17! = 355687428096000
18! = 6402373705728000
19! = 121645100408832000
20! = 2432902008176640000
21! = 51090942171709440000
22! = 1124000727777607680000
23! = 25852016738884976640000
24! = 620448401733239439360000
        :
       中略
        :
180! = 20089606249913429965 …中略… 986039603200000000000000000000000000000000000000000000

しっかり表示されるものの、1桁ずつ計算しているので結構遅いです。
私の普段使いの数年前のノートPCだと、5000!程度の計算でも数十秒かかります。

$n = 5000;
$start = hrtime(true);
$result = factorial($n);
$end = hrtime(true);
echo "$n! = $result\n", ($end - $start) / 1e9, "\n";

// 5000! = 422857792660554 …中略… 5509378334720000  …中略… 0000000000
// 47.1204944

ある程度の桁数をまとめて計算できるようにしてみます。

echo 999999999 * 999999999; // 999999998000000001
echo 9999999999 * 9999999999; // 9.999999998E+19

9桁同士までなら大丈夫そうなのでとりあえず9桁で。

test3.php
<?php
function factorial($n) {
    if($n > 0) return intMul($n, factorial($n - 1));
    return 1;
}

define('DIGIT', 9);
define('NUMBER', 10 ** DIGIT);
define('F', '%0'. DIGIT. 'd');

function intMul($a, $b) {
    $a = "$a";
    $b = "$b";

    $a_ = $a;
    $ra = [];
    $asn = strlen($a) / DIGIT;
    for($i = 0; $i < $asn; ++$i) {
        array_push($ra, (int)substr($a_, -DIGIT));
        $a_ = substr($a_, 0, -DIGIT);
    }

    $b_ = $b;
    $rb = [];
    $bsn = strlen($b) / DIGIT;
    for($i = 0; $i < $bsn; ++$i) {
        array_push($rb, (int)substr($b_, -DIGIT));
        $b_ = substr($b_, 0, -DIGIT);
    }

    $c = [];
    for($i = 0; $i < count($ra); ++$i) {
        for($j = 0; $j < count($rb); ++$j) {
            $p = $i + $j;
            if(!isset($c[$p])) $c[$p] = 0;
            $c[$p] += $ra[$i] * $rb[$j];
            if($c[$p] >= NUMBER) {
                if(!isset($c[$p + 1])) $c[$p + 1] = 0;
                $c[$p + 1] += (int)($c[$p] / NUMBER);
                $c[$p] %= NUMBER;
            }
        }
    }

    for($i = 0; $i < count($c) - 1; ++$i) $c[$i] = sprintf(F, $c[$i]);
    return implode('', array_reverse($c));
}

$n = 5000;
$start = hrtime(true);
$result = factorial($n);
$end = hrtime(true);
echo "$n! = $result\n", ($end - $start) / 1e9, "\n";

// 5000! = 422857792660554 …中略… 5509378334720000  …中略… 0000000000
// 4.2777483

10倍くらい早くなりました。

自分で組むのはひとまず置いて、GMPに階乗関数gmp_fact()があるのでこれを使ってみます。

test4.php
<?php
$n = 5000;
$start = hrtime(true);
$result = gmp_fact($n);
$end = hrtime(true);
echo "$n! = ", gmp_strval($result), "\n", ($end - $start) / 1e9, "\n";

// 5000! = 422857792660554 …中略… 5509378334720000  …中略… 0000000000
// 0.0003974

更に10000倍くらい早くなりました。

1000000!の場合

// 1000000! = 82639316883312400623 …中略… 25617650584125440000  …中略… 0000000000
// 0.3422455

10000000!の場合

// 10000000! = 12024234005159034561 …中略… 03073025741946880000  …中略… 0000000000
// 6.7120242

階乗を使いたいけれど自分で書きたいわけではないのなら、gmp_fact()を使うのが良さそうですね。

追記
5000!に約4.2秒かかっていた自前計算版(test3.php)も、乗算処理を階乗計算用としてループ内に入れて配列/文字列変換処理コストを節約することで、同じアルゴリズムのままでも3倍くらい早くなりました。
それでもgmp_fact()には到底及びませんが。

test5.php
<?php
function factorial($f) {
    if($f < 0 || $f !== (int)$f) return false;

    $d = 9;
    $dn = 10 ** $d;
    $df = "%0{$d}d";

    $c = [1];
    for($n = 1; $n <= $f; ++$n) {
        $ra = $c;
        $c = [0];
        $b_ = "$n";
        $rb = [];
        $bsn = strlen($n) / $d;
        for($i = 0; $i < $bsn; ++$i) {
            array_push($rb, (int)substr($b_, -$d));
            $b_ = substr($b_, 0, -$d);
        }
        for($i = 0; $i < count($ra); ++$i) {
            for($j = 0; $j < count($rb); ++$j) {
                $p = $i + $j;
                if(!isset($c[$p])) $c[$p] = 0;
                $c[$p] += $ra[$i] * $rb[$j];
                if($c[$p] >= $dn) {
                    $p1 = $p + 1;
                    if(!isset($c[$p1])) $c[$p1] = 0;
                    $c[$p1] += (int)($c[$p] / $dn);
                    $c[$p] %= $dn;
                }
            }
        }
    }
    for($i = 0; $i < count($c) - 1; ++$i) $c[$i] = sprintf($df, $c[$i]);
    return implode('', array_reverse($c));
}

$n = 5000;
$start = hrtime(true);
$result = factorial($n);
$end = hrtime(true);
echo "$n! = $result\n", ($end - $start) / 1e9, "\n";

// 5000! = 422857792660554 …中略… 5509378334720000 …中略… 00000000
// 1.3479136
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP8(PHP-FPM)のメモ

環境

  • CentOS7
    (元々色々なバージョンを動かすのに使ってた)

epel, remi のリポジトリの追加は済んでる環境(下記)

yum -y install epel-release
yum -y install http://rpms.remirepo.net/enterprise/remi-release-7.rpm

PHP8と諸々インストール

yum -y install php80 php80-php php80-php-mbstring php80-php-pdo php80-php-mysqlnd php80-php-gd php80-php-xml php80-php-fpm

設定

cd /etc/opt/remi/php80/php-fpm.d/
cp -a www.conf www.conf-original
vi www.conf
www.conf
listen = 127.0.0.1:9080
pm = ondemand

いろんなバージョンのPHPを動かしているのでポートはそれぞれ被らないように設定

vi /etc/httpd/conf.d/virtualhost.conf
virtualhost.conf
        <Directory "/path/to/php80/document_root">
                <FilesMatch \.php$>
                        SetHandler "proxy:fcgi://localhost:9080"  # ← www.conf に指定したポート番号
                </FilesMatch>
        </Directory>

更新して起動する

service httpd configtest
systemctl start php80-php-fpm
systemctl status php80-php-fpm
systemctl enable php80-php-fpm
systemctl restart httpd

おわり。

Apacheのデフォルトで使用されるバージョンを変える

正解かどうかわからないけど、
PHPのバージョンごとにあるApacheのモジュールを無効化すればOK。

リネームなり削除して読み込まれるモジュールを変える。
なお、複数生きている場合はファイル名昇順で先に来るものが当たる(たぶん)
※ パッケージ更新すると復活するかも…

cd /etc/httpd/conf.modules.d
mv 15-php72-php.conf 15-php72-php.conf_bak
systemctl restart httpd

PHP7のソースをPHP8で動かしてみて引っかかったところと変更内容

自動DI で使ってたリフレクションの一部がコケた

// ↓ NGになった
$tempReflectionClass = $reflectionParameter->getClass();

// ↓ OK
$type = $reflectionParameter->getType();
$tempReflectionClass = $type !== null && !$type->isBuiltin() 
   ? new \ReflectionClass($type->getName())
   : null
;

is_callable を静的でないクラスメソッドに対して
クラス名とメソッド名で読んでいたのがNGになった。

// ↓ NGになった
is_callable([クラス名, 静的でないメソッド名]);

// ↓ OK
is_callable([インスタンス, 静的でないメソッド名]);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

学習を応用し自分で考えたプログラム(随時更新予定)

今回は、お客様アンケート(仮)を作成 2020/11/27

<?php

//評価が0の時にアドバイスをお願いし、0以上の時は感謝の言葉を伝えるプログラム
echo 'より良いサービスを制作するため、アンケートにご協力お願いいたします' . PHP_EOL;

echo '評価(5点満点中):';
$status = trim(fgets(STDIN));

$a = $status;

if ($a > 1) {
    echo 'ご利用ありがとうございました' . PHP_EOL;
} else {
    echo '品質向上の為何かアドバイスをいただけないでしょうか' . PHP_EOL;
}
$Impressions = trim(fgets(STDIN));
//aが1より大きければご利用ありがとうございました!aが1より小さければアドバイスのお願い
//標準入力と、半角スペース削除の処理、if文のみを使用した処理です。

first_production.gif

これからもアウトプットしていきます!

Gif動画の作り方 (参考にさせていただきましたありがとうございます)
https://youtu.be/aQOpbP3hr0A

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

学習を応用し0からプログラム作成(随時更新予定)

今回は、お客様アンケート(仮)を作成 2020/11/27

<?php

//評価が0の時にアドバイスをお願いし、0以上の時は感謝の言葉を伝えるプログラム
echo 'より良いサービスを制作するため、アンケートにご協力お願いいたします' . PHP_EOL;

echo '評価(5点満点中):';
$status = trim(fgets(STDIN));

$a = $status;

if ($a > 1) {
    echo 'ご利用ありがとうございました' . PHP_EOL;
} else {
    echo '品質向上の為何かアドバイスをいただけないでしょうか' . PHP_EOL;
}
$Impressions = trim(fgets(STDIN));
//aが1より大きければご利用ありがとうございました!aが1より小さければアドバイスのお願い
//標準入力と、半角スペース削除の処理、if文のみを使用した処理です。

first_production.gif

これからもアウトプットしていきます!

Gif動画の作り方 (参考にさせていただきましたありがとうございます)
https://youtu.be/aQOpbP3hr0A

Homebrew使って、ffmpegをインストールして簡単に作成

動画の有るディレクトリまで移動
ls -lh動画の容量確認

ffmpeg -i sample.mov -r (数字)(変更したいファイルの名前).gif
-rの後ろに1~20を設定。1秒間に表示したい画像の数大きくなればなるほどスムーズにみれる


ffmpeg -i sample.mov -r 10 sample.gif
enterで完了

GIFの確認は選択して、スペースキーを押す
*他にも様々なオプションがある.

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

LaravelAdminのシャーディング対応

 本記事は、サムザップ Advent Calendar 2020 #2 の12/6の記事です。

 LaravelAdminは、いわゆる管理画面のフレームワークです。(公式: https://laravel-admin.org/docs/en/)

 シンプルながらデザインが整っていて、いちから作るよりはよい機能がそろっています。また、CRUDで作られていて、定型的な表示や操作ならモデル指定ぐらいで作れたりします。

 一方で、ゲーム開発のサーバでは負荷分散のためにシャーディングをしていることが多く、データベースとテーブルを複数組み合わせてひとつのデータを示していることがよくあります。
 LaravelAdminでは、そのままこれらのデータを扱うことができません。
 ここではそんなLaravelAdminでシャーディングしたデータを扱うためのいくつかの方法を述べてみます。

何が問題?

 LaravelAdminではGrid、Formなどいくつかの表示や操作の仕組みを提供していて、それらのクラスに対してモデルを渡したり表示内容などを記述するといった作り方をしています。
 渡すモデルは、あらかじめ生成したモデルを渡すことができるので、シャーディングされたDBやテーブルをシャーディングキーで特定することが事前にできれば、そのシャーディング先を示すモデルを扱うことはできます。
 ただシャーディングされたデータをそのままGridに渡すと、全件表示されてしまいます。たとえばユーザーIDをシャーディングキーにしているものなら、ユーザーIDを指定しても、ほかのユーザーのデータも表示されてしまいます。
 ほかにも条件による絞り込み機能など、サブ的な仕組みには、シャーディングキーを呼び出し元と揃えたい場合があり、シャーディングキーをどうやって渡すかがポイントになりがちです。

シャーディングに対応したgridの作成

 Gridは便利な機能ですが、そのままでは扱えないので、LaravelAdminのコードを改造することにしました。
 考え方として、シャーディングキーをあらかじめフィルターのように使えば、Gridの表示の際、シャーディングキーに合わせた表示がされるはずです。この仕組みを「デフォルトのフィルター」としてGridに用意してやればいいわけです。
 vendor/encore/laravel-admin/src/Grid.phpに以下のコードを追加します。

Grid.php
/**
 * default filter key
 * @var string
 */
protected $defaultFilterKey;

/**
 * default filter value
 * @var mixed
 */
protected $defaultFilterValue;


/**
 * Add default filter.
 *
 * @return void
 */
public function addDefaultFilter($key, $value)
{
    $this->defaultFilterKey = $key;
    $this->defaultFilterValue = $value;
}

 同じGrid.phpのapplyQuery()では、フィルタに基づく条件の設定などを行っています、ここでdefaultFilterKey、defaultFilterValueをwhereして、検索条件に加えます。

Grid.php
/**
 * @return array|Collection|mixed
 */
public function applyQuery()
{
    if (!empty($this->defaultFilterKey)) {
        $this->model()->where($this->defaultFilterKey, $this->defaultFilterValue);
    }

    $this->applyQuickSearch();

    $this->applyColumnFilter();

    $this->applyColumnSearch();

    $this->applySelectorQuery();
}

 使い方は次の通りです。ここでは$userIdがシャーディングキーになります。

$hogemodels = new HogeModel();

//シャードをセット。DBが決まる
$hogemodels->setShard($userId);

//Gridの初期化コールバックでシャードキーを設定。表示を$userIdのものに指定
Grid::init( function(Grid $grid) use ($userId) {
    $grid->addDefaultFilter('user_id', $userId);
});

return Admin::content(function (Content $content) use ($hogemodels, $userId) {
    //Gridの利用
    $content->body(Admin::grid($hogemodels, function (Grid $grid) use ($userId) {
        $grid->user_id('ユーザーID')->sortable();
    }));
});

シャーディングキーを渡して表示条件指定を行う

 フィルター機能にはhiddenフィールドを記述できる仕組みがあるので、それでシャーディングキーを渡すようにすると、うまく呼び出せます。
 以下のようにfilterのコールバックでhidden()を記述します。$userIdがシャーディングキーです。

public function resultview(Request $request)
{
    $userId = $request->user_id;
    $hogemodels = new LogTrophies();
    $hogemodels->setShard($userId);

    Grid::init( function(Grid $grid) use ($userId) {
        $grid->addDefaultFilter('user_id', $userId);
    });
    return Admin::content(function (Content $content) use ($hogemodels, $userId) {
        $content->body(Admin::grid($hogemodels, function (Grid $grid) use ($userId) {
            $grid->user_id('ユーザーID')->sortable();

            $grid->perPages([100, 200, 500, 1000]);
            $grid->filter(function ($filter) use ($userId) {
                $filter->disableIdFilter();
                $filter->hidden('user_id', $userId);
                $filter->equal('type', 'type');
                $filter->equal('key', 'key');
                $filter->equal('value', 'value');
            });
        }));
    });
}

シャーディングキーを渡してEditフォームを利用する

 Editフォームにはhiddenフィールドの記述方法がないので、シャーディングキーを渡すのにちょっと頭を使います。
 Editフォームを呼び出すために使うURLには、呼び出し元のURLが使われます。そこで$grid->setResourceでシャーディングキーを混ぜたURLにしてやることで、シャーディングキーを渡せます。
 大元のGridで以下のようにして

$content->body(Admin::grid($hogemodels, function (Grid $grid) use ($userId) {
    $grid->setResource('/admin/hoge/resultview/' . $userId); //Edit Action用にユーザーID渡すため
    $grid->user_id('ユーザーID')->sortable();
}));

routes.phpは以下のようにシャーディングキーであるUserIdと、テーブルのユニークIDであるidを両方取るように記述します。

routes.php
$router->get('/hoge/resultview/{userId}/{id}/edit', 'HogeController@edit');

Editの受け側には、userId、idの2つの引数を取るように記述します

public function edit($userId, $id)
{
    $hogemodels = new UserBirthdays();
    $hogemodels->setShard($userId);
    Grid::init( function(Grid $grid) use ($userId) {
        $grid->addDefaultFilter('user_id', $userId);
    });

    return Admin::content(function (Content $content) use ($hogemodels, $userId) {
        $content->header('hoge情報');
        $content->description('編集');
        $content->body(
            Admin::form($hogemodels, function (Form $form) use ($hogemodels, $userId) {
                $form->display('ユーザーID')->default($userId);
                $form->setaction('/admin/hoge/modification');
            })
        );
    });
}

まとめ

 LaravelAdminは便利なフレームワークですが、便利であるがゆえに仕組みが決まっているため、最初は作法がわからずとっつきにくいかもしれません。でも、いろいろコードを調べると、便利なものが多くあります。ぜひいろいろ試してみてください。

 明日は @hiroki_shimada の記事です。

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

Laravelバリデーション integerとnumericの動作の違い

  • Laravel6.x
  • PHP7.4

紛らわしいので調べました。

integer の挙動

入力値が整数で構成された文字列か数値であることをバリデートします。

※実際の判定文はこちら

// vendor\laravel\framework\src\Illuminate\Validation\Concerns\ValidatesAttributes.php
public function validateInteger($attribute, $value)
{
    return filter_var($value, FILTER_VALIDATE_INT) !== false;
}

numericの挙動

入力値が数字または数値形式の文字列であることをバリデートします。

※実際の判定文はこちら

// vendor\laravel\framework\src\Illuminate\Validation\Concerns\ValidatesAttributes.php
public function validateNumeric($attribute, $value)
{
    return is_numeric($value);
}

比較

  • ○:バリデーションチェックを通過する
  • ×:バリデーションチェックに引っかかりエラーになる
検証値 integer numeric regex:/^[0-9]+$/i
'0'
'123'
'0123' × 注意!
'+1' ○ 注意! ×
'-1' ○ 注意! ×
'0.1' × ×
'1.1' × ×
'113-0012' × × ×
'090-111-222' × × ×
'123' × × ×

おまけ 動作確認用

<?php
$list = ['0', '123', '0123', '+1', '-1', '0.1', '1.1', '114-0012', '090-2222-3333', '123'];
foreach ($list as $value) {
    echo "$value ";
    if (filter_var($value, FILTER_VALIDATE_INT) !== false) {
        echo " |integer: ○";
    } else {
        echo " |integer: ×";
    }    

    if (is_numeric($value)) {
        echo " |numeric: ○";
    } else {
        echo " |numeric: ×";
    }

    if (preg_match("/^[0-9]+$/i", $value)) {
        echo " |regex: ○";
    } else {
        echo " |regex: ×";
    }

    echo "\n";
}
?>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP/Laravel用語集

はじめに

PHP/Laravelに関する基本的な用語を簡単にまとめました。
特に初学者の場合、用語の意味や意図が分かっていないまま学習をしていると「今何をしてるんだ?」となりがちなので、迷子にならない為にも用語の意味を知っておくことが重要だと思い記事にしました。

私自身、勉強中の身ですので間違っている箇所などありましたら、Twitter(@Tii_engineering)のDMなどでご指摘いただけたら幸いです。
随時更新予定です。


MVCフレームワーク

Model(モデル)、View(ビュー)、Controller(コントローラー)の各機能に分けて整理し、これらのパーツを作ることで開発を行う。

Routing(ルーティング)

アクセスを設定している情報(ルート)を管理する機能。
特定のアドレスにアクセスした時、どの処理を呼び出して実行するかを管理するもの。
「〇〇というアドレスにアクセスしたら、××という処理を呼び出す」という関連付けを行なっている。

Model(モデル)

データ処理全般を担当。データベースアクセスに関する処理全般を扱う。

View(ビュー)

画面表示を担当。
Controllerの指示によって アクセスしてきたユーザーのブラウザに表示するデータを生成する。(HTMLファイルを出力するところのようなイメージ)

Controller(コントローラー)

全体の制御を担当。
必要に応じてModelからデータを取得したり、Viewを利用して画面表示を作成したりする。

Blade

シンプルにHTMLファイルを書くように書くことのできるテンプレートエンジン。
通常のPHPスクリプトファイルを記述するより効率的に作ることができる。
テンプレートを継承し新たなテンプレートを定義したり、レイアウトの一部をセクションとしてはめ込むことができる。

パス(path:通り道)

ファイルやフォルダの場所。
(ファイル名を含む場合と含まない場合がある。)

Migration(マイグレーション)

データベースのバージョン管理機能。
データベースのテーブルを作成・削除等する機能も持つ。

Validation(バリデーション)

Modelでデータを保存する前に、フォームからデータを送信されてきた値が正しい形式で書かれているかどうか、データの不備をあらかじめ防ぐために検証する仕組み

Auth

facade (ファサード)という機能で、ユーザーを認証しログインを管理する機能を自動生成してくれる機能。

composer

Laravelを構成するたくさんの公開ライブラリを矛盾がない状態で管理するツール。
ライブラリ同士は相互に複雑な依存関係がるが、これらの依存関係の管理を一手に引き受けてくれるのがcomposer。

Webpack

CSSやJavaScriptなど複数に分割されているファイルを1つにまとめることができる。
ソースコードを圧縮し、接続速度が早くなる。

Laravel Mix

WebpackやsassをLaravel用に使いやすくオーバーラップしているツール。
Webpackやsassはそれぞれ環境構築などを行うのに時間がかかるがLaravel Mixを使えば設定ファイルに数行記述するだけで、自動的に圧縮等の処理ができる。

GET

指定したURLの内容を取り出すための要求で、最も基本的なHTTPメソッド。
ブラウザからURLを入力してwebページを開くときには、GETメソッドのHTTPリクエストを送っている。

POST

URLに対して情報を要求するだけでなく、クライアントからさまざまなデータを送信することができる。
主にデータを更新するような処理に使われています。

テーブル

分類されたデータベースの種類ごとの単位。
データベースは複数のテーブルを保持し、テーブルごとにデータを管理している。

カラム

データベースの列に相当するもの。「属性」を意味する。

レコード

データそのものを意味する。

インスタンス

データベースとユーザーの間を仲介する役割がある。
インスタンスを介してデータベースを操作することで、データの安全性やパフォーマンスの向上につながる。インスタンスは『データベースにアクセスするための過程』であり、データベースは『データを格納したファイル』という関係性。

スキーマ

データベースの設計図
必要なデータの洗い出しや、そのデータの格納整理のルールを決める。(全体を理解するために割り当てられた『構造』)
データベースでは、そのような作業を『スキーマを定義する』と呼ぶ。

Eloquent ORM

ORMとは、レコードをオブジェクトとして扱えるようにするための仕組み。
Laravelには「Eloquent」というORMが内臓されている。
このEloquentを使って、モデルとデータベースとのやり取りを行うようにしています。

リレーション

テーブルの関連付け。
「Aさんが投稿した記事」というように、ユーザーと記事のテーブルを関連付けることを言う。

デプロイ

公開用のサーバーを用意し、そのサーバーに作成したアプリケーションのインストールを行い本稼働させること。

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

Laravel6 Unitテスト時に「Maximum function nesting level~」のエラーが出た

目的

  • しょーもないミスで時間を費やしてしまったので反省の意味も含めてエラー原因などをメモ的にまとめる

前提情報

  • LaravelのUnitテストを実行したところ「Error: Maximum function nesting level of '256' reached, aborting!」というエラーが出た。
  • テストではServiceを呼び出し、紐付いたRepositoryからデータを取得する事ができるかを確認していた。

原因

  • Serviceクラスの依存注入でそのServiceクラス自体を依存注入してしまっていた。
  • さらにServiceクラス内のメソッドでそのメソッドを呼び出す処理を記載していた。
  • わかりにくいと思うので下記に図を記載する。

    • 理想

      mermaid-diagram-20201127145744.png

    • 今回

      mermaid-diagram-20201127145729.png

  • 上記でなんとなくご理解いただけたと思うがUnitテストで呼び出しているServiceクラスのメソッドが自身を呼び出すようになってしまっていた。

  • 当該ループが256回に達したため、エラーを出力して処理が停止した。

改善策

  • ServiceクラスでRepositoryクラスが依存注入できているかを確認する。
  • 下記画像の流れを意識しながら実装する。

    mermaid-diagram-20201127145744.png

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

【VSCode】PHPの関数が未定義とのエラーが発生。原因は拡張機能のバージョンだった。

【VSCode】PHPの関数が未定義とのエラーが発生。原因は拡張機能のバージョンだった。

使用環境

  • windows 10 Home
  • VSCode
  • PHP intelephense 1.5.4

背景

ふとVSCodeを立ち上げたら、開発途中のソースコードでPHPに関連する関数が未定義だとエラーが発生。つい最近までそんなことはなかったのに!
なんだか最近、VSCodeに振り回されています...

解決方法

調べてみたところ、VSCodeの拡張機能PHP intelephenseのバージョンが原因とのこと。
下記記事にもある通りバージョンを1.5.4→1.2.3まで下げてみたら解決。

【VSCode】PHPで関数のインテリセンスが効かない問題を解決

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

DBを使わず時間制限付きトークンを発行する

新規会員登録やパスワードリセットなどで、期間限定のトークンを発行することがあります。普通はトークンの発行時にトークン自身と有効期限をDBに登録し、トークン検証時にレコードの有効期限を確認します。

ですが、さまざまな事情でDBにトークンを記録したくないとか、そもそもDBが使用できないというような場合があります。そういうときにDBを使用せずに期間限定トークンを発行する方法を考えてみました。

クリティカルな用途には向かないと思いますので、実際に採用するかどうかは慎重にご検討ください。

アイデア

アイデアとしては、トークン内にトークン発行日時を埋め込んでしまえばいいというものです。

トークンを解読して発行日時を書き換えられたら意味がないので、トークンは一方向ハッシュ化します。

一方向ハッシュ化したら発行側でもトークンを検証できませんが、そこは現在日時を使って再度トークンを生成しなおすことで対応します。

実装

実装は以下のようになります。

function generate_token($salt)
{
  $epoch = time();
  return _generate_token($salt, $epoch);
}

function verify_token($salt, $token)
{
  $epoch = time();
  $new_token = _generate_token($salt, $epoch);
  return $token == $new_token;
}

function _generate_token($salt, $epoch)
{
  return crypt((string)$epoch, $salt);
}

これで有効期間1秒のトークンが実装できました。

有効期間1秒ではさすがに使い物になりません。有効期間を1時間にする場合、3600秒前までトークン検証を繰り返します。

function verify_token($salt, $token)
{
  $epoch = time();
  for($i = 0; $i < 3600; $i++) {
    $new_token = _generate_token($salt, $epoch - $i);
    if($token == $new_token) {
      return true;
    }
  }
  return false;
}

検証のたびに3600回もループを回していては負荷が問題になりますので、エポック秒を 3600 で割って使います。

function _generate_token($salt, $epoch)
{
  return crypt((string)floor($epoch / 3600), $salt);
}

しかしこれでは有効期間は1秒から3600秒のどれかになってしまいます。12時台であれば、12時0分でも12時30分でも12時59分でも、全部有効期限は12時59分59秒ってことですね。運良く12時直後にトークンを発行したら1時間近く有効ですが、12時59分付近だと有効期限は1分になってしまい、さすがに実用的ではありません。

そこで以下のように修正します。

function verify_token($salt, $token)
{
  $epoch = time();
  $new_token = _generate_token($salt, $epoch);
  if($token == $new_token) {
    return true;
  }
  $new_token = _generate_token($salt, $epoch - 3600);
  if($token == $new_token) {
    return true;
  }
  return false;
}

これで有効期間は1時間から1時間59分59秒となりました。1時間有効なトークンと言いつつ最長で2時間近く有効になりますので、かなりいい加減な仕様になります。つまりは、その程度のいい加減さが許される程度の用途にしか使えないということです。

欠点

他にも欠点がありまして、トークンにユーザを特定する情報を埋め込むことができません。通常のトークン実装であれば DB をトークンで検索してレコードに紐づけてあったユーザIDなりを取得すればいいのですが、DB を使用していませんから取得できる情報はユーザから渡されるもののみになります。ですので、ユーザIDなどの情報を別パラメータで引き回しておく必要があります。

https://example.com/?token=xxx // ×
https://example.com/?userid=yyy&token=xxx // 〇
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laraveを触って1年経ったのでTIPS

本当は2年くらいかもしれない

確認バージョンは Laravel 7.26.1

よく使うコマンド

ネームスペースとか考慮するとコマンドから作った方が良いです。でもコピペしちゃう

make系

# マイグレーション作成
php artisan make:migration create_xxxxx_table

# モデル&ファクトリー&シーダー作成(app/)
php artisan make:model Models/Xxxx -fs

# コントローラー作成(app/Http/Controllers/)
php artisan make:controller Api/XxxxController --model=Models/Xxxx --api

# フォームリクエスト作成(app/Http/Requests/)
php artisan make:request Api/Xxxx/Store

# コマンド作成(app/Console/Commands/)
php artisan make:command Xxxx

### ide-helperが導入されていれば
# ide_helper:generate
php artisan ide-helper:generate

# ide_helper:models
php artisan ide-helper:models -N

db系

# マイグレーション実行
php artisan migrate

# マイグレーションを一つ戻す
php artisan migrate:rollback --step=1

# DB初期化&DatabaseSeeder実行
php artisan migrate:fresh --seed

# シーダー実行
php artisan db:seed --class=UserSeeder

キャッシュを消す

# 全消し
php artisan optimize:clear

# optimize:clearの中身の実態は下記コマンド
php artisan view:clear && \
php artisan cache:clear && \
php artisan route:clear && \
php artisan config:clear && \
php artisan clear-compiled

ちなみに間違ってもローカル開発環境でphp artisan config:cacheは使わないよう注意してください。
phpunitを走らせた際にキャッシュ化した.envの環境変数が使われてしまいます。
間違えた場合は php artisan config:clearしてください。

公式にも書いてる!
https://readouble.com/laravel/7.x/ja/configuration.html

Factory

公式Faker
https://github.com/fzaninotto/Faker

日本語ソース
https://github.com/fzaninotto/Faker/blob/master/src/Faker/Provider/ja_JP

# ランダム英数字生成(unique使っても衝突してしまう場合)
$faker->unique()->regexify('[a-zA-Z0-9]{1,20}')

Routing

ルーディングの書き方について

個人的にはHTTPメソッドごとにアクションを指定した方がいいかと思います。
Route::resourceを使ってデフォルトアクションを指定できるんですが、ワナがあります。
下記のように公開・下書きで投稿の処理を丸ごと分けたいなどした時に下記のようなルーティングになるかと思います。

api.php

Route::resource('publish/posts', 'Publish\PostController')->only([
    'index',
    'store',
    'show',
    'update',
    'destroy',
]);

Route::resource('draft/posts', 'Draft\PostController')->only([
    'index',
    'store',
    'show',
    'update',
    'destroy',
]);

処理的には問題ないですが、php artisan route:cache(本番環境での高速化に必要)した際にname被りでエラーになります。
なのでシンプルにメソッドごとに書いた方が良いかと思います(いずれresourceで表現できないURIの方が多くなるので)

Route::get('/publish/posts', 'Publish\PostController@index');
Route::post('/publish/posts', 'Publish\PostController@store');
Route::get('/publish/posts/{id}', 'Publish\PostController@show');
Route::patch('/publish/posts/{id}', 'Publish\PostController@update');
Route::delete('/publish/posts/{id}', 'Publish\PostController@destroy');

Route::get('/draft/posts', 'Draft\PostController@index');
Route::post('/draft/posts', 'Draft\PostController@store');
Route::get('/draft/posts/{id}', 'Draft\PostController@show');
Route::patch('/draft/posts/{id}', 'Draft\PostController@update');
Route::delete('/draft/posts/{id}', 'Draft\PostController@destroy');

Controller

  • ルートモデルバインディングはFatControllerになる危険性があるので、使用は注意

FormRequest

  • exists使う際にはdeleted_atを考慮する必要あり
  • min、max、sizeなどは型指定で動きが変わるので注意
  • existsの逆はuniqueを使う
    public function rules()
    {
        return [
            'id'    => 'required|integer|exists:posts,id,deleted_at,NULL',
            'str1'  => 'required|string|max:10',   // 10文字以内
            'str2'  => 'required|string|min:10',   // 10文字以上
            'str3'  => 'required|string|size:10',  // 10文字固定
            'num1'  => 'required|integer|max:10',  // 10以内
            'num2'  => 'required|integer|min:10',  // 10以上
            'num3'  => 'required|integer|size:10', // 10固定
        ];
    }

Eloquent

Model

  • 論理削除を有効にしたい場合はuse SoftDeletes
  • 全モデルでguardedにidを指定しておくと良い。予期せぬキーの登録・更新を防げる。
  • created_atカラムを作成しなかった場合はCREATED_AT = null
  • updated_atカラムを作成しなかった場合はUPDATED_AT = null
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Xxxxx extends Model
{
    use SoftDeletes;

    const CREATED_AT = null;
    const UPDATED_AT = null;

    protected $guarded = [
        'id',
    ];
}

Relation

こんなモデルがあったとして

class User extends Authenticatable
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}
class Post extends Model
{
    use SoftDeletes;

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}
class Comment extends Model
{
    const UPDATED_AT = null;

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function post()
    {
        return $this->belongsTo(Post::class);
    }
}

リレーションメソッドでアクセスした時

  • hasOneの戻り値は Model or null
  • hasManyの戻り値はCollection
  • belongsToの戻り値は Model or null

QueryBuilder

  • クエリビルダの場合はjoinするテーブルのdeleted_atが機能しない
    User::join('posts', 'users.id', '=', 'posts.user_id')->get();
    select * from users inner join posts on users.id = posts.user_id
  • joinされるテーブルがModelの場合はdeleted_atが機能する
    Post::join('comments', 'posts.id', '=', 'comments.post_id')->get();
    select * from posts inner join comments on posts.id = comments.post_id where posts.deleted_at is null

Relation Query

hasで流れるSQL

    User::has('posts.comments')->get();
[2020-12-05 23:39:48] local.DEBUG: SQL {"time":"12.82 ms","sql":"select * from `users` where exists (select * from `posts` where `users`.`id` = `posts`.`user_id` and exists (select * from `comments` where `posts`.`id` = `comments`.`post_id`) and `posts`.`deleted_at` is null)"} 
select * from users
where exists (
    select * from posts
    where users.id = posts.user_id
    and exists (
        select * from comments
        where posts.id = comments.post_id
    )
    and posts.deleted_at is null
)

joinで流れるSQL

    User::join('posts', 'posts.user_id', '=', 'users.id')
        ->join('comments', 'comments.post_id', '=', 'posts.id')
        ->whereNull('posts.deleted_at')
        ->get();
[2020-12-06 00:00:13] local.DEBUG: SQL {"time":"12.11 ms","sql":"select * from `users` inner join `posts` on `posts`.`user_id` = `users`.`id` inner join `comments` on `comments`.`post_id` = `posts`.`id` where `posts`.`deleted_at` is null"} 
select * from users
inner join posts on posts.user_id = users.id
inner join comments on comments.post_id = posts.id
where posts.deleted_at is null

withで流れるSQL

    User::with('posts.comments')->get();
[2020-12-05 23:49:37] local.DEBUG: SQL {"time":"11.54 ms","sql":"select * from `users`"} 
[2020-12-05 23:49:37] local.DEBUG: SQL {"time":"0.70 ms","sql":"select * from `posts` where `posts`.`user_id` in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) and `posts`.`deleted_at` is null"} 
[2020-12-05 23:49:37] local.DEBUG: SQL {"time":"0.41 ms","sql":"select * from `comments` where `comments`.`post_id` in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)"} 
select * from users
select * from posts where posts.user_id in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) and posts.deleted_at is null
select * from comments where comments.post_id in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Console

複数時間でタスクスケジューリングしたい場合(8:30、11:30、18:30など)はcron形式にする。

app/Console/Kernel.php

->cron('30 8,11,18 * * *');
コンソール出力時にログにも残す

トレイトを作成してコマンドクラスでuse PrependsOutput;してあげればOK
普通に$this->info()$this->error()するだけでログに残せます。
テスト時に実行時間を出力していない理由は、コンソール出力値もテストしているためです。

app/Console/Commands/PrependsOutput.php

<?php

namespace App\Console\Commands;

use Carbon\CarbonImmutable;

trait PrependsOutput
{
    /**
     * コンソール出力に追加
     *
     * @param  string  $string
     * @param  string|null  $style
     * @param  int|string|null  $verbosity
     * @return void
     */
    public function line($string, $style = null, $verbosity = null)
    {
        if (\App::environment() === 'testing') {
            parent::line($string, $style, $verbosity);
        } else {
            parent::line(CarbonImmutable::now()->format('[Y-m-d H:i:s] ').$string, $style, $verbosity);
        }
        logger(CarbonImmutable::now()->format('[Y-m-d H:i:s] ').$string);
    }
}

PHPUnitテスト

  • テストが遅い時はxdebugを無効にしてみてください。5倍くらい違う!
# テスト実行
./vendor/bin/phpunit

# 対象のファイルでテスト実行
./vendor/bin/phpunit tests/Feature/XxxxxTest.php

# 対象のファイルの対象の関数でテスト実行
./vendor/bin/phpunit tests/Feature/XxxxxTest.php --filter=xxxxxxxx

# テスト結果をログに残す
./vendor/bin/phpunit --testdox-text=test.txt

他いろいろ

実行SQLを確認したい

下記を参考にしてちょっと直しました
LaravelでSQL文をlaravel.logに出力する

下記をregisterに追加

app/Providers/AppServiceProvider.php

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        // SQL Log
        \DB::listen(function ($query) {
            $sql = preg_replace('/"(.*?)"/', "'$1'", $query->sql);
            for ($i = 0; $i < count($query->bindings); $i++) {
                $bindValue = $query->bindings[$i];
                if (is_bool($bindValue)) {
                    $bindValue = $bindValue ? 'true' : 'false';
                } else {
                    $bindValue = "'".(string)$bindValue."'";
                }
                $sql = preg_replace("/\?/", $bindValue, $sql, 1);
            }
            \Log::debug("SQL", ["time" => sprintf("%.2f ms", $query->time), "sql" => $sql]);
        });
    }

Laravelログ+Slackにもログを残す

  1. stackのchannelsにslackを追加
  2. slackのlevelをcritical → debugに変更
  3. envのLOG_SLACK_WEBHOOK_URLにslackのwebhookUrlを指定

config/logging.php

    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => [
                'single',
                'slack', // 追加
            ],
            'ignore_exceptions' => false,
        ],
        
        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel Log',
            'emoji' => ':boom:',
            'level' => 'debug', // critical → debugに変更
        ],

.env

LOG_SLACK_WEBHOOK_URL=xxxxxx

stackのchannelsには複数のdriverが使えるので、fatalエラーと単純なログをチャンネルごとに分けると便利です

    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => [
                'single',
                'slack-critical',
                'slack-debug',
            ],
            'ignore_exceptions' => false,
        ],
        
        'slack-critical' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel critical Log',
            'emoji' => ':boom:',
            'channel' => env('LOG_SLACK_CHANNEL_ALERT'),
            'level' => 'critical',
        ],
        'slack-debug' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel debug Log',
            'emoji' => ':memo:',
            'channel' => env('LOG_SLACK_CHANNEL_DEBUG'),
            'level' => 'debug',
        ],
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelを触って1年経ったのでTIPS

本当は2年くらいかもしれない

確認バージョンは Laravel 7.26.1

よく使うコマンド

ネームスペースとか考慮するとコマンドから作った方が良いです。でもコピペしちゃう

make系

# マイグレーション作成
php artisan make:migration create_xxxxx_table

# モデル&ファクトリー&シーダー作成(app/)
php artisan make:model Models/Xxxx -fs

# コントローラー作成(app/Http/Controllers/)
php artisan make:controller Api/XxxxController --model=Models/Xxxx --api

# フォームリクエスト作成(app/Http/Requests/)
php artisan make:request Api/Xxxx/Store

# コマンド作成(app/Console/Commands/)
php artisan make:command Xxxx

### ide-helperが導入されていれば
# ide_helper:generate
php artisan ide-helper:generate

# ide_helper:models
php artisan ide-helper:models -N

db系

# マイグレーション実行
php artisan migrate

# マイグレーションを一つ戻す
php artisan migrate:rollback --step=1

# DB初期化&DatabaseSeeder実行
php artisan migrate:fresh --seed

# シーダー実行
php artisan db:seed --class=UserSeeder

キャッシュを消す

# 全消し
php artisan optimize:clear

# optimize:clearの中身の実態は下記コマンド
php artisan view:clear && \
php artisan cache:clear && \
php artisan route:clear && \
php artisan config:clear && \
php artisan clear-compiled

ちなみに間違ってもローカル開発環境でphp artisan config:cacheは使わないよう注意してください。
phpunitを走らせた際にキャッシュ化した.envの環境変数が使われてしまいます。
間違えた場合は php artisan config:clearしてください。

公式にも書いてる!
https://readouble.com/laravel/7.x/ja/configuration.html

Factory

公式Faker
https://github.com/fzaninotto/Faker

日本語ソース
https://github.com/fzaninotto/Faker/blob/master/src/Faker/Provider/ja_JP

# ランダム英数字生成(unique使っても衝突してしまう場合)
$faker->unique()->regexify('[a-zA-Z0-9]{1,20}')

Routing

ルーディングの書き方について

個人的にはHTTPメソッドごとにアクションを指定した方がいいかと思います。
Route::resourceを使ってデフォルトアクションを指定できるんですが、ワナがあります。
下記のように公開・下書きで投稿の処理を丸ごと分けたいなどした時に下記のようなルーティングになるかと思います。

api.php

Route::resource('publish/posts', 'Publish\PostController')->only([
    'index',
    'store',
    'show',
    'update',
    'destroy',
]);

Route::resource('draft/posts', 'Draft\PostController')->only([
    'index',
    'store',
    'show',
    'update',
    'destroy',
]);

処理的には問題ないですが、php artisan route:cache(本番環境での高速化に必要)した際にname被りでエラーになります。
なのでシンプルにメソッドごとに書いた方が良いかと思います(いずれresourceで表現できないURIの方が多くなるので)

Route::get('/publish/posts', 'Publish\PostController@index');
Route::post('/publish/posts', 'Publish\PostController@store');
Route::get('/publish/posts/{id}', 'Publish\PostController@show');
Route::patch('/publish/posts/{id}', 'Publish\PostController@update');
Route::delete('/publish/posts/{id}', 'Publish\PostController@destroy');

Route::get('/draft/posts', 'Draft\PostController@index');
Route::post('/draft/posts', 'Draft\PostController@store');
Route::get('/draft/posts/{id}', 'Draft\PostController@show');
Route::patch('/draft/posts/{id}', 'Draft\PostController@update');
Route::delete('/draft/posts/{id}', 'Draft\PostController@destroy');

Controller

  • ルートモデルバインディングはFatControllerになる危険性があるので、使用は注意

FormRequest

  • exists使う際にはdeleted_atを考慮する必要あり
  • min、max、sizeなどは型指定で動きが変わるので注意
  • existsの逆はuniqueを使う
    public function rules()
    {
        return [
            'id'    => 'required|integer|exists:posts,id,deleted_at,NULL',
            'str1'  => 'required|string|max:10',   // 10文字以内
            'str2'  => 'required|string|min:10',   // 10文字以上
            'str3'  => 'required|string|size:10',  // 10文字固定
            'num1'  => 'required|integer|max:10',  // 10以内
            'num2'  => 'required|integer|min:10',  // 10以上
            'num3'  => 'required|integer|size:10', // 10固定
        ];
    }

Eloquent

Model

  • 論理削除を有効にしたい場合はuse SoftDeletes
  • 全モデルでguardedにidを指定しておくと良い。予期せぬキーの登録・更新を防げる。
  • created_atカラムを作成しなかった場合はCREATED_AT = null
  • updated_atカラムを作成しなかった場合はUPDATED_AT = null
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Xxxxx extends Model
{
    use SoftDeletes;

    const CREATED_AT = null;
    const UPDATED_AT = null;

    protected $guarded = [
        'id',
    ];
}

Relation

こんなモデルがあったとして

class User extends Authenticatable
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}
class Post extends Model
{
    use SoftDeletes;

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}
class Comment extends Model
{
    const UPDATED_AT = null;

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function post()
    {
        return $this->belongsTo(Post::class);
    }
}

リレーションメソッドでアクセスした時

  • hasOneの戻り値は Model or null
  • hasManyの戻り値はCollection
  • belongsToの戻り値は Model or null

QueryBuilder

  • クエリビルダの場合はjoinするテーブルのdeleted_atが機能しない
    User::join('posts', 'users.id', '=', 'posts.user_id')->get();
    select * from users inner join posts on users.id = posts.user_id
  • joinされるテーブルがModelの場合はdeleted_atが機能する
    Post::join('comments', 'posts.id', '=', 'comments.post_id')->get();
    select * from posts inner join comments on posts.id = comments.post_id where posts.deleted_at is null

Relation Query

hasで流れるSQL

    User::has('posts.comments')->get();
[2020-12-05 23:39:48] local.DEBUG: SQL {"time":"12.82 ms","sql":"select * from `users` where exists (select * from `posts` where `users`.`id` = `posts`.`user_id` and exists (select * from `comments` where `posts`.`id` = `comments`.`post_id`) and `posts`.`deleted_at` is null)"} 
select * from users
where exists (
    select * from posts
    where users.id = posts.user_id
    and exists (
        select * from comments
        where posts.id = comments.post_id
    )
    and posts.deleted_at is null
)

joinで流れるSQL

    User::join('posts', 'posts.user_id', '=', 'users.id')
        ->join('comments', 'comments.post_id', '=', 'posts.id')
        ->whereNull('posts.deleted_at')
        ->get();
[2020-12-06 00:00:13] local.DEBUG: SQL {"time":"12.11 ms","sql":"select * from `users` inner join `posts` on `posts`.`user_id` = `users`.`id` inner join `comments` on `comments`.`post_id` = `posts`.`id` where `posts`.`deleted_at` is null"} 
select * from users
inner join posts on posts.user_id = users.id
inner join comments on comments.post_id = posts.id
where posts.deleted_at is null

withで流れるSQL

    User::with('posts.comments')->get();
[2020-12-05 23:49:37] local.DEBUG: SQL {"time":"11.54 ms","sql":"select * from `users`"} 
[2020-12-05 23:49:37] local.DEBUG: SQL {"time":"0.70 ms","sql":"select * from `posts` where `posts`.`user_id` in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) and `posts`.`deleted_at` is null"} 
[2020-12-05 23:49:37] local.DEBUG: SQL {"time":"0.41 ms","sql":"select * from `comments` where `comments`.`post_id` in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)"} 
select * from users
select * from posts where posts.user_id in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) and posts.deleted_at is null
select * from comments where comments.post_id in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Console

複数時間でタスクスケジューリングしたい場合(8:30、11:30、18:30など)はcron形式にする。

app/Console/Kernel.php

->cron('30 8,11,18 * * *');
コンソール出力時にログにも残す

トレイトを作成してコマンドクラスでuse PrependsOutput;してあげればOK
普通に$this->info()$this->error()するだけでログに残せます。
テスト時に実行時間を出力していない理由は、コンソール出力値もテストしているためです。

app/Console/Commands/PrependsOutput.php

<?php

namespace App\Console\Commands;

use Carbon\CarbonImmutable;

trait PrependsOutput
{
    /**
     * コンソール出力に追加
     *
     * @param  string  $string
     * @param  string|null  $style
     * @param  int|string|null  $verbosity
     * @return void
     */
    public function line($string, $style = null, $verbosity = null)
    {
        if (\App::environment() === 'testing') {
            parent::line($string, $style, $verbosity);
        } else {
            parent::line(CarbonImmutable::now()->format('[Y-m-d H:i:s] ').$string, $style, $verbosity);
        }
        logger(CarbonImmutable::now()->format('[Y-m-d H:i:s] ').$string);
    }
}

PHPUnitテスト

  • テストが遅い時はxdebugを無効にしてみてください。5倍くらい違う!
# テスト実行
./vendor/bin/phpunit

# 対象のファイルでテスト実行
./vendor/bin/phpunit tests/Feature/XxxxxTest.php

# 対象のファイルの対象の関数でテスト実行
./vendor/bin/phpunit tests/Feature/XxxxxTest.php --filter=xxxxxxxx

# テスト結果をログに残す
./vendor/bin/phpunit --testdox-text=test.txt

他いろいろ

実行SQLを確認したい

下記を参考にしてちょっと直しました
LaravelでSQL文をlaravel.logに出力する

下記をregisterに追加

app/Providers/AppServiceProvider.php

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        // SQL Log
        \DB::listen(function ($query) {
            $sql = preg_replace('/"(.*?)"/', "'$1'", $query->sql);
            for ($i = 0; $i < count($query->bindings); $i++) {
                $bindValue = $query->bindings[$i];
                if (is_bool($bindValue)) {
                    $bindValue = $bindValue ? 'true' : 'false';
                } else {
                    $bindValue = "'".(string)$bindValue."'";
                }
                $sql = preg_replace("/\?/", $bindValue, $sql, 1);
            }
            \Log::debug("SQL", ["time" => sprintf("%.2f ms", $query->time), "sql" => $sql]);
        });
    }

Laravelログ+Slackにもログを残す

  1. stackのchannelsにslackを追加
  2. slackのlevelをcritical → debugに変更
  3. envのLOG_SLACK_WEBHOOK_URLにslackのwebhookUrlを指定

config/logging.php

    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => [
                'single',
                'slack', // 追加
            ],
            'ignore_exceptions' => false,
        ],
        
        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel Log',
            'emoji' => ':boom:',
            'level' => 'debug', // critical → debugに変更
        ],

.env

LOG_SLACK_WEBHOOK_URL=xxxxxx

stackのchannelsには複数のdriverが使えるので、fatalエラーと単純なログをチャンネルごとに分けると便利です

    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => [
                'single',
                'slack-critical',
                'slack-debug',
            ],
            'ignore_exceptions' => false,
        ],
        
        'slack-critical' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel critical Log',
            'emoji' => ':boom:',
            'channel' => env('LOG_SLACK_CHANNEL_ALERT'),
            'level' => 'critical',
        ],
        'slack-debug' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel debug Log',
            'emoji' => ':memo:',
            'channel' => env('LOG_SLACK_CHANNEL_DEBUG'),
            'level' => 'debug',
        ],
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel 7 Socialiteを使ってLINE認証を実装してみる。

はじめに

Laravel7でLINE認証機能を実装しました。
記事自体はポチポチあるけど、古い記事が多いしやり方も色々あってなんかうまくいかない...。
少し手間取りましたが、無事実装できたので備忘録として纏めます。

偉大な参考記事

ほとんど下記URLの作成者様のコーディングを参考にして書いております。
https://tdomy.com/2020/08/how-to-use-laravel-socialite/

上記記事に加えてLINEのDevelopersの詳細等を補足して説明します。
上記記事ではLaravel6っぽいです。
私のはLaravel7でやってみます。

実装環境

・XAMPP
・Windows10
・PHP 7.4.11
・Laravel 7.31

LINE Developersで諸々登録

まずLINE Developersに登録してアプリで使うidやらなんやらを取得します。

登録したら早速プロバイダーを作成してみましょう。名前は何でもオッケーです。
LINEプロバイダー作成.png.jpg

そしたらLINEログインチャンネルを作成しましょう。

LINE選択画面.png.jpg

チャネル作成の画面でチャネル名だのなんだのを設定できます。
ここでの名前は自分の覚えやすい名前等で登録します。

・チャネルタイプはLINE Loginを選択
・アプリの種類は該当のものにチェック
その他は埋めるところは埋めましょう。

チャネル.png.jpg

チャネルを作成できたら上記画面のようになります。
このページでチャネルIDとチャネルシークレットは後々使用します。
ここのLINEログインをクリックしてコールバックURLを設定しましょう。
URLはhttps://localhost/アプリ名/public/login/line/callback みたいな感じで。

それでは次はログインで使用するLINEアカウントに権限をつけましょう。
LINE Developersで登録したアカウントを今回ログインで使用するものと同じであればやらなくておkです。
ビジネスアカウントとかで登録している場合は下記をしないとエラーになるのでやりましょう。

{DBE7073A-DD54-4C25-B1A1-8CF5592ACD98}.png.jpg
権限はAdminにしてください。

これで準備オッケー!
それではLaravelに移りましょー。

Socialiteを導入

Laravelのcreateからauthの導入は省きます。
それでは下記コマンドでsocialiteを入れましょう。

$composer require laravel/socialite

これでfacebookやgoogleとかは使えるのですが、
残念ながらLINEは対象外...。
ということで追加でLINE専用プロパイダーを入れましょう。

$ composer require socialiteproviders/line

これでプロパイダーはオッケー。次は環境変数ですね。

環境変数を入れる

先ほどLINE Developersで取得したIDだのを記載します。

config/service.php
'line' => [
      'client_id'     => env('LINE_CLIENT_ID'),
      'client_secret' => env('LINE_CLIENT_SECRET'),
      'redirect'      => env('LINE_CLIENT_CALLBACK'),
  ],
.env
LINE_CLIENT_ID=ここにclientIDを入れる
LINE_CLIENT_SECRET=ここにシークレットIDを入れる
LINE_CLIENT_CALLBACK=/login/line/callback

こんな感じでオッケーです。

プロパイダー追加の記述

LINEのように別途でプロパイダーを追加した場合は下記の記述をします。

config/app.php
'providers' => [

    // デフォルトでいろいろ書かれてます

    SocialiteProviders\Manager\ServiceProvider::class, // ここを追記
];

もういっちょ

app/Providers/EventServiceProvider.php
protected $listen = [

    // デフォルトでなんかいろいろ書かれてる

    \SocialiteProviders\Manager\SocialiteWasCalled::class => [
        'SocialiteProviders\\Line\\LineExtendSocialite@handle', //ここの二行を追記
    ],
];

よっしゃこれでSocialiteを使う準備オッケー!

Usersテーブルを変更する

LaravelではデフォルトでUsersテーブルがありますが、
今回のユーザー新規登録ではname,provider,provided_user_idで登録させるとします。

migrations/2014_10_12_000000_create_users_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
          $table->bigIncrements('id');
          $table->string('name');
          $table->enum('provider', ['line', 'twitter']);
          $table->string('provided_user_id');
          $table->timestamps();

          $table->unique(['provider', 'provided_user_id']);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

編集し終えたら忘れずにmigrateします。

Userモデルを実装

Userモデルもデフォルトであるのでそれを使います。

app/User.php
<?php

namespace App;

use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Auth\Authenticatable;

class User extends Model implements AuthenticatableContract
{
    use Authenticatable, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'provider', 'provided_user_id',
    ];

    /**
     * Get the password for the user.
     *
     * @return string
     */
    public function getAuthPassword()
    {
        // We don't use password login.
        return '';
    }

    /**
     * Get the column name for the "remember me" token.
     *
     * @return string
     */
    public function getRememberTokenName()
    {
        // We don't use this.
        return '';
    }
}

これでモデルもオッケーです。
次はコントローラー記述です。

コントローラー実装

Authを作ったときに自動でLoginControllerが作られるのでそれを使います。

app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth; //追記
use Laravel\Socialite\Facades\Socialite; //追記
use App\User; //追記


class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = RouteServiceProvider::HOME;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    /**ここから下が追記 */
    /**
     * Redirect the user to the provider authentication page.
     *
     * @param string $provider
     * @return \Illuminate\Http\Response
     */
    public function redirectToProvider($provider)
    {
        return Socialite::driver($provider)->redirect();
    }

    /**
     * Obtain the user information from the provider.
     *
     * @param string $provider
     * @return \Illuminate\Http\Response
     */
    public function handleProviderCallback($provider)
    {
        $provided_user = Socialite::driver($provider)->user();

        $user = User::where('provider', $provider)
            ->where('provided_user_id', $provided_user->id)
            ->first();

        if ($user === null) {
            // redirect confirm
            $user = User::create([
              'name'               => $provided_user->name,
              'provider'           => $provider,
              'provided_user_id'   => $provided_user->id,
            ]);
        }

        Auth::login($user);

        return redirect()->route('home');
    }

    /**
     * Log the user out of the application.
     *
     * @return \Illuminate\Http\Response
     */
    public function logout()
    {
        Auth::logout();

        return redirect()->route('home');
    }
}

return redirect先のパスは各自好きなrouteを指定してください。
今回はデフォルトでルート付けされているhomeにしています。

ルーティングを作る

さて、最後にloginとlogoutに関するルーティングをして終わりです!

routes/web.php
<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

Route::get('/login/{provider}', 'Auth\LoginController@redirectToProvider');
Route::get('/login/{provider}/callback', 'Auth\LoginController@handleProviderCallback');
Route::get('/logout', 'Auth\LoginController@logout')->name('logout');

これでルート付けもできた!ということでLineログインまで飛んでみましょう。
今回はURLで飛んでみます。
https://locallhost/アプリ名/public/login/line で飛んでみると....。

ログイン.png.jpg

ここでメアドとパスワードを入れてみる。

ログイン後.png.jpg

You are logged in!!!!!!!!!!!!!!!!!
やったー!ログインできた!ついでにMySQLを見るとしっかりidが追加されてました!

感想

Laravel7の記事がとても少ないし、LINEのプロパイダー記事も古い記事が多く割と躓きました。
参考記事の方はlaravel6だったのですが、同じ方法で7もできました。
他のプロパイダーを追加した場合の記事も完成次第投稿したいと思います。

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

意地でもvimでコード補完したい人へ

まだvimの補完で消耗してんの?

PHPStorm便利だよねえっていうツイートをみるといつも羨ましいvim使いです。
そりゃねえお金あったらPHPStormとか使いますよ。そうだ!そうだ!お金で解決しよう(社長よろしく)

でも生来のvim使いなのにそんな文明の利器に負けていいのか、そう自問自答して何年たったのだろう。そうVisualStudioCodeを見ながら・・・!

TL;DR

  • coc.vimをつかえ

設定はほとんど公式サイトのそのままで問題ない。インストールにハマることはなかった。動作はとても快適。

https://github.com/neoclide/coc.nvim

思えばvimでのコード補完の道のりは長く険しいものでした

vim使いの人はご存知かもしれませんが、私も例に漏れずneocomplete / deopleteなどなどと共に歩んで来ました。ところがvimのバージョンが上がるたびに何故か動かない、設定がうまくできない。

私のvimスキル不足とういうのも大いにあるのでしょうが、いつも勢いでmac osアップデートして死ぬとかbrew upgradeとかして動かないbuglist見るとかして苦労していました。

どうしてもvimそのものにこだわる私としてはneovimやmacvimへの移行は絶対にしない(でも補完動かないのでモヤモヤが募る・・・)

そこで見つけたcoc.vim

LSPでvim補完しちゃう

LSPとはMicrosoftがつくって公開したプロトコル。VSCodeでもつかってる(から補完とかできるわけです)。詳しくは他の人がめちゃくちゃちゃんと書いてくれてます。(Qiitaの民はいつも感心させられる)

https://qiita.com/atsushieno/items/ce31df9bd88e98eec5c4

こまけえことはいいんだよ、インストール方法は?

https://github.com/neoclide/coc.nvim/wiki/Install-coc.nvim

phpの人は

:CocInstall coc-phplsだけでいける。特に何かを設定しないといけないとかはない。公式サイトに書いてあるとおりにやれば十分に動くと思う。

ちゃんと動いてるかの確認は

vimで拡張子がターゲットのものに関して、入力すると補完のポップアップがでてくるので候補の横に[LS]と出てればOKです。

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

switch文[PHP]

switch文をPHPで書く

if, elseifによる分岐が多く複雑な場合、switch文で書き換えるとシンプルで読みやすいコードにできる。switch(式)の(式)がcaseの値と一致したとき、そのブロックが実行される。caseのどれにも一致しなかった時、defaultのブロックが実行される
1470803893312.png

break

break命令は現在のブロックから脱出するための命令。break命令がないと、後ろに続くcaseブロックが続けて実行されてしまう

    switch($remainder) {
      case 0:
        echo "大吉です。";
        break;

      case 1:
        echo "中吉です。";
        break;

      case 2:
        echo "小吉です。";
        break;

      default:
        echo "凶です。";
        break;
    }

以上!

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

Xdebug3.0.0がリリースされたので、ver2からの雑なコンバート

Xdebug 3.0.0 is out!

11/25にXdebug 3.0.0 is out!されたわけですが、pecl install xdebugと記載していて、まんまと勝手にver3がインストールされてしまい、まんまと以前の設定で動かず焦っている今日この頃ですが皆さんいかがお過ごしでしょうか?

そんなわけで、私が使用している設定をコンバートし、ひとまず変更を加えて動くところまで雑にやって、雑にまとめました。

mode設定

ver2.x系でとりあえずステップデバッグを使うためには、下記の設定を行っていたと思います。いや、むしろこれがほぼすべてと言ってもよいくらい。

xdebug.remote_enable=1
xdebug.default_enable=0
xdebug.profiler_enable=0
xdebug.auto_trace=0
xdebug.coverage_enable=0

ver3.x系では

xdebug.mode=debug

となります。

modeにいくつかの設定が集約されたようで、そのvalueの記載でフラグを管理していく感じです。

以前の設定とmodeのvalueのコンバートはこう。

以前の設定 modeのvalue
default_enable develop
profiler_enable profile
remote_enable debug

同時に設定したい場合はvalueをカンマ区切り。

xdebug.mode=develop,debug

その他コンバートが必要だったもの

xdebug.remote_autostartを設定する場合、下記の2つを設定するようです。

  • xdebug.mode=debug
  • xdebug.start_with_request=yes.

だいたいの人が必要そうな設定はこちらです。

以前の設定 新しい設定
profiler_output_dir output_dir
remote_host client_host
remote_port client_port

output_dir系はoutput_dirにまとめられたっぽいです。

ここまでの設定でブレークポイントまでは動かすことができました。

ポート番号

私はポートを指定していたので引っかからなかったのですが、デフォルトのポート番号が9003へ変更になっています。

参考

参考にしたのは公式ドキュメント。
結局、必要な設定を検索しながらポチポチ地道に書き換えていくしかなさそうです。

動いたini

最期にXdebugが動作したiniを記載しておきます。
開発環境はPhpStormで、Dockerサーバーです。

xdebug.idekey=PHPSTORM 
xdebug.client_host=host.docker.internal 
xdebug.client_port=9010 
xdebug.idekey=PHPSTORM 
xdebug.mode=debug,develop 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む