- 投稿日:2020-08-11T21:43:00+09:00
【Eloquent】よく使うメソッド集
Eloquentは便利ですよね。どんなメソッドがあるかまとめたくなったので、ここに書いちゃおうと思います。
*使用環境
PHP 7.4.5
Laravel 7.22.4基本的な使い方
まずはモデルクラスを用意します。Itemクラスならば以下のようなコマンドで作れます。
php artisan make:model Item
後はそのモデルクラスを使って、あとで出る表にあるメソッドでどんどんデータ抽出するだけです。こんな感じで->でくっつけていきます。<?php use Item; //この一文を書き忘れるとエラーになってしまう。 $result = Item::where('value', 1000)->limit(10)->get();集計系(selectで使うような)
メソッド名 引数 説明 max 1.カラム名 max('value')とすればselect max(value)ということになります。 結合系(joinを使うような)
メソッド名 引数 説明 join 1.テーブル名 2.主となるテーブルの結合カラム 3.演算子(=など) 4.従となるテーブルの結合カラム join('type', 'item.item_type', '=', 'type.id')とすれば、inner join type on item.item_type = type.idということになります。 抽出系(whereで使うような)
メソッド名 引数 説明 where 1.カラム名 2.完全一致させるもの where('id', 'solabito331')とすればwhere id='solabito331'という抽出のしかたになります。 where 1.カラム名 2.演算子(=など) 3.比較対象 where('id', '>', 10)とすればwhere id>10 という抽出のしかたになります。 whereNotNull 1.カラム名 whereNotNull('age')とすれば、where age is not nullという抽出のしかたになります。 limit 1.件数 limit(10)とすれば、10件に絞られます。
- 投稿日:2020-08-11T21:16:16+09:00
独学未経験エンジニアがweb系自社開発企業でアルバイトを2ヶ月してみて感じたこと
簡単な自己紹介
- 大学では機械工学を専攻
- プログラマに魅力を感じ2019年新卒でメーカー子会社のIT会社に入社
- 入社してから10ヶ月で会社を退職
- そこから4ヶ月間独学で勉強し、現在web系の自社開発企業でアルバイト中
ニートになって独学していた時代
新卒で入社したときに感じたことや退職理由はまた今度別の記事で書いてみたいと思います。
勉強したこと一覧
progate(HTML,CSS,Javascript,Ruby,RubyonRails)
railsチュートリアル解説動画
2周はした。1周目は動画を見るだけで、2周目は手を動かしながらでした。
かなりお世話になり、railsチュートリアルとは友達になった気がするRuby on Rails5 超入門
amazonリンク
読んだのは3割ぐらいだけRuby on Rails5 速習実践ガイド
amazonリンク
辞書的な感じで使ったりした。暇なときに読んでみてもためになることが多い。Gitが、おもしろいほどわかる基本の使い方33
amazonリンク
gitの操作の基礎を学んだ。すぐに読み終わる内容なので、読みやすいキタミ式イラストIT塾 基本情報技術者
amazonリンク
コンピュターの基礎を理解するのにはちょーど良かった。時間をかけてちょっとずつ読んだwebを支える技術
amazonリンク
何を書いているのか全然分からなかった。kindle版で技術書は読みにくい。図解即戦力 AWSのしくみと技術がしっかりわかる教科書
amazonリンク
AWSのサービスの概要は大体理解したけど、実装まではできずプログラミングスクール受講(ポテパンキャンプ)
ポテパンのリンク
gitの操作、N+1問題、Rspecの書き方はここで自主的に学んだEveryday Rails - RSpecによるRailsテスト入門 購入リンク
ちょくちょく参考にできた。これも詰まったときに検索する用。ポートフォリオ作成
当時はAWS,Docker, CI/CD, Kubernetes等についてはほぼほぼ理解できずに、断念していました。
開発環境なんかはrailsチュートリアルで使っていたcloud9を使っていました。
まずはHerokuに自分が一生懸命作成したアプリケーションをデプロイし、公開できるようにしました。
一応独自ドメイン,SSL化程度は行っておき、Herokuは有料プランを使用し、少しでも応答速度が早くなるようにしておきました。
一応当時のポートフォリオのソースコードのリンクを貼っておきます。
https://github.com/ak2-lucky/clothes-app
ファッションが好きだった僕は洋服のレビューサイトを作りました。この記事を見ている駆け出しエンジニアの日々勉強されている方々はこう思っているかもしれません。
AWSにデプロイすらできないのに、webエンジニアになれるの?
Dockerで開発環境も構築できないの?
自動テストぐらいはやっといたら?現役のwebエンジニアも同じことを思うでしょう。
ポートフォリオも作りきれないような奴はエンジニアになる資格なんかないよって。
なぜならポートフォリオだけは運にも左右されない自分の努力だけで100パーセント作りきれる成果物だからです。確かにそうです。
僕の努力不足です。何の異論もありません。
認めます。そして就職活動をはじめます。
就職活動時代
結果からゆうとほぼ全落ち。
面接までいったのは1.2回でした。
面談してくれた会社で研究開発で人材を募集しているからそこに来ないか?と言ってくれた企業もありました。(結局いかず)
コロナの影響とかいう言い訳はしません。
全て自分の実力不足。
アルバイトも採用している企業に応募し,1社だけ何とかアルバイトとして採用していただきました。今回で自分の市場価値を知りました。
大学は何も考えずに過ごし、新卒で入社した会社を何の成果も残さず10ヶ月で退職。
世間はそんな奴のことを評価はしてくれません。
もちろん当然の評価です。アルバイトとしてweb系自社開発企業で働く(今)
技術スタック:AWS,Docker,Laravel+vue.js
働いてみるまでLaravelはおろかPHPも触ったことありませんでした。vue.jsもですが。
詳細は省きますが、実際に働いてみて感じるのは、プログラムを書く以外のことです。
技術のキャッチアップ云々の話ではなくて、組織作り、開発の体制やフローなどの重要さについてです。
自分はプログラミングが苦ではありません。
しかし、プログラミング業務以外の部分でストップすることが多い場合があります。(例えば、なぜこの変更を加えられたのかというコメントがないandコードを見ても分からない)
連携がうまく取れなくて、個々の意思で変更が加えられたりするので、デザインがバラバラ。
issue作成者等に確認もしないで変更を進められていたり。
CI/CDもそのうちの一つですが、issueを作成するのは誰なのか、誰の判断を最終的に仰ぐのか、UIのデザインは好きに決めていいのか、プルリクではどこを見ればいいのか、など他にもいろいろありますが。。。会社によって開発の進め方などは違うと思うので、一概にあれが悪い、これが悪いというわけではありませんが、開発体制や組織作りはサービス開発にかなり大きく影響するように感じました。
開発言語や技術以前にこういった根本的な開発の導線を確保する重要性を学びました。
最後に
ここまで読んでいただいた方には、いろいろ思うことがあると思います。
2ヶ月しか働いていない、しかもアルバイト如きが何を偉そうなこと言ってるんだ!
とか思ってる方いるかもしれません。ですが、これは個人の一意見であります。
ただ同じように駆け出しエンジニアの方の参考になればいいなと思って書きました。webエンジニアへの道は高く険しいように感じます。
しかし、自分にとって登りたい山がどれだけ高かろうが、険しかろうが、関係ありません。
ただ登るだけ
毎日頑張って積み上げて、疲れたら一緒にサボりましょう。長くなりましたが、初投稿は以上になります。
ここまで読んでいただきありがとうございました。
- 投稿日:2020-08-11T21:12:39+09:00
PHP_CodeSnifferのルールまとめ
概要
PHP_CodeSnifferにはどのようなルールがあるのか調べてまとめています。
バージョン^3.5を対象としています。PHP_CodeSnifferとは
PHP_CodeSnifferはコーディング標準の違反検出するスクリプトと、それを自動で修正するスクリプトです。
PHP_CodeSniffer is a set of two PHP scripts; the main phpcs script that tokenizes PHP, JavaScript and CSS files to detect violations of a defined coding standard, and a second phpcbf script to automatically correct coding standard violations. PHP_CodeSniffer is an essential development tool that ensures your code remains clean and consistent.
デフォルトのコーディング標準
デフォルトで次のコーディング標準がインストールされています。
$ phpcs -i The installed coding standards are MySource, PEAR, PSR1, PSR12, PSR2, Squiz and Zendこの中からPSR2、PSR12にどのようなルールが含まれるかリストアップします。
$ phpcs --standard=PSR12 -eルール一覧
※ MySource、Zendは対象(PSR2、PSR12)に含まれていないので省略します。
PSR2 PSR12 Generic Arrays ArrayIndent DisallowLongArraySyntax DisallowShortArraySyntax Classes DuplicateClassName OpeningBraceSameLine CodeAnalysis AssignmentInCondition EmptyPHPStatement EmptyStatement ForLoopShouldBeWhileLoop ForLoopWithTestFunctionCall JumbledIncrementer UnconditionalIfStatement UnnecessaryFinalModifier UnusedFunctionParameter UselessOverridingMethod Commenting DocComment Fixme Todo ControlStructures DisallowYodaConditions InlineControlStructure ○ ○ Debug CSSLint ClosureLinter ESLint JSHint Files ByteOrderMark ○ ○ EndFileNewline EndFileNoNewline ExecutableFile InlineHTML LineEndings ○ ○ LineLength ○ ○ LowercasedFilename OneClassPerFile OneInterfacePerFile OneObjectStructurePerFile OneTraitPerFile Formatting DisallowMultipleStatements ○ ○ MultipleStatementAlignment NoSpaceAfterCast SpaceAfterCast SpaceAfterNot SpaceBeforeCast Functions CallTimePassByReference FunctionCallArgumentSpacing ○ ○ OpeningFunctionBraceBsdAllman OpeningFunctionBraceKernighanRitchie Metrics CyclomaticComplexity NestingLevel NamingConventions CamelCapsFunctionName ConstructorName UpperCaseConstantName ○ ○ PHP BacktickOperator CharacterBeforePHPOpeningTag ClosingPHPTag DeprecatedFunctions DisallowAlternativePHPTags ○ ○ DisallowRequestSuperglobal DisallowShortOpenTag ○ ○ DiscourageGoto ForbiddenFunctions LowerCaseConstant ○ ○ LowerCaseKeyword ○ ○ LowerCaseType ○ NoSilencedErrors RequireStrictTypes SAPIUsage Syntax UpperCaseConstant Strings UnnecessaryStringConcat VersionControl GitMergeConflict SubversionProperties WhiteSpace ArbitraryParenthesesSpacing DisallowSpaceIndent DisallowTabIndent ○ ○ IncrementDecrementSpacing ○ LanguageConstructSpacing ScopeIndent ○ ○ SpreadOperatorSpacingAfter PEAR Classes ClassDeclaration Commenting ClassComment FileComment FunctionComment InlineComment ControlStructures ControlSignature MultiLineCondition Files IncludingFile Formatting MultiLineAssignment Functions FunctionCallSignature FunctionDeclaration ValidDefaultValue ○ NamingConventions ValidClassName ValidFunctionName ValidVariableName WhiteSpace ObjectOperatorIndent ScopeClosingBrace ScopeIndent PSR1 Classes ClassDeclaration ○ ○ Files SideEffects ○ ○ Methods CamelCapsMethodName ○ ○ PSR2 Classes ClassDeclaration ○ ○ PropertyDeclaration ○ ○ ControlStructures ControlStructureSpacing ○ ElseIfDeclaration ○ ○ SwitchDeclaration ○ ○ Files ClosingTag ○ ○ EndFileNewline ○ ○ Methods FunctionCallSignature ○ ○ FunctionClosingBrace ○ ○ MethodDeclaration ○ ○ Namespaces NamespaceDeclaration ○ UseDeclaration ○ PSR12 Classes AnonClassDeclaration ○ ClassInstantiation ○ ClosingBrace ○ ControlStructures BooleanOperatorPlacement ○ ControlStructureSpacing ○ Files DeclareStatement ○ FileHeader ○ ImportStatement ○ OpenTag ○ Functions NullableTypeDeclaration ○ ReturnTypeDeclaration ○ Namespaces CompoundNamespaceDepth ○ Operators OperatorSpacing ○ Properties ConstantVisibility ○ Traits UseDeclaration ○ Squiz Arrays ArrayBracketSpacing ArrayDeclaration CSS ClassDefinitionClosingBraceSpace ClassDefinitionNameSpacing ClassDefinitionOpeningBraceSpace ColonSpacing ColourDefinition DisallowMultipleStyleDefinitions DuplicateClassDefinition DuplicateStyleDefinition EmptyClassDefinition EmptyStyleDefinition ForbiddenStyles Indentation LowercaseStyleDefinition MissingColon NamedColours Opacity SemicolonSpacing ShorthandSize Classes ClassDeclaration ClassFileName DuplicateProperty LowercaseClassKeywords SelfMemberReference ValidClassName ○ ○ Commenting BlockComment ClassComment ClosingDeclarationComment DocCommentAlignment EmptyCatchComment FileComment FunctionComment FunctionCommentThrowTag InlineComment LongConditionClosingComment PostStatementComment VariableComment ControlStructures ControlSignature ○ ○ ElseIfDeclaration ForEachLoopDeclaration ○ ○ ForLoopDeclaration ○ ○ InlineIfDeclaration LowercaseDeclaration ○ ○ SwitchDeclaration Debug JSLint JavaScriptLint Files FileExtension Formatting OperatorBracket Functions FunctionDeclarationArgumentSpacing ○ ○ FunctionDeclaration ○ ○ FunctionDuplicateArgument GlobalFunction LowercaseFunctionKeywords ○ ○ MultiLineFunctionDeclaration ○ ○ NamingConventions NamingConventions ValidFunctionName ValidVariableName Objects DisallowObjectStringIndex ObjectInstantiation ObjectMemberComma Operators ComparisonOperatorUsage IncrementDecrementUsage ValidLogicalOperators PHP CommentedOutCode DisallowBooleanStatement DisallowComparisonAssignment DisallowInlineIf DisallowMultipleAssignments DisallowSizeFunctionsInLoops DiscouragedFunctions EmbeddedPhp Eval GlobalKeyword Heredoc InnerFunctions LowercasePHPFunctions NonExecutableCode Scope MemberVarScope MethodScope ○ ○ StaticThisUsage Strings ConcatenationSpacing DoubleQuoteUsage EchoedStrings WhiteSpace CastSpacing ○ ControlStructureSpacing ○ ○ FunctionClosingBraceSpace FunctionOpeningBraceSpace FunctionSpacing LanguageConstructSpacing LogicalOperatorSpacing MemberVarSpacing ObjectOperatorSpacing OperatorSpacing PropertyLabelSpacing ScopeClosingBrace ○ ○ ScopeKeywordSpacing ○ ○ SemicolonSpacing SuperfluousWhitespace ○ ○ ルール詳細
それぞれのルールがどのようなチェックを行うのかまとめていきます。
随時追加予定。Generic.Arrays.ArrayIndent
配列の要素をスペース4つでインデントされているかチェックします。
インデントの数はカスタマイズ可能です。$array = [ 'ok', 'ng', // Array key not indented correctly; expected 4 spaces but found 2 ];Generic.Arrays.DisallowLongArraySyntax
長い配列構文を禁止します。
$ok = []; $ng = array(); // Short array syntax must be used to define arraysGeneric.Arrays.DisallowShortArraySyntax
短い配列構文を禁止します。
DisallowLongArraySyntaxの逆です。$ok = array(); $ng = []; // Short array syntax is not allowedGeneric.Classes.DuplicateClassName
同じクラスまたはインターフェース名が複数のファイルで使用されている場合、エラーとなります。
Generic.Classes.OpeningBraceSameLine
クラス/インターフェース/トレイトの開き括弧がクラス宣言と同じ行にあることを確認します。
class OK {} class NG { // Opening brace should be on the same line as the declaration for class NG }Generic.CodeAnalysis.AssignmentInCondition
条件内で行われている変数の割り当てを検出します。
ただし三項演算では検出されません。if ($ng = true) { // Variable assignment found within a condition. Did you mean to do a comparison ? } // Note: this sniff does not detect variable assignments in the conditional part of ternaries! $note = true ? 'a' : 'b';Generic.CodeAnalysis.EmptyPHPStatement
空のPHPステートメントに対してチェックします。
- 間に実行可能コードがない2つのセミコロン
- 空のPHP open-closeタグの組み合わせ
<?php ; // Empty PHP statement detected: superfluous semi-colon. ?> <?php ?> // Empty PHP open/close tag combination detected.Generic.CodeAnalysis.EmptyStatement
空のステートメントを検出します。(try, catch, finally, do, else, elseif, for, foreach, if, switch, while)
完全に空であるか、空文字またはコメントだけの場合も空とみなされます。if (true) { // Empty IF statement detected // empty }おそらく特定のステートメントを除外することも可能です。
<rule ref="Generic.CodeAnalysis.EmptyStatement"> <!-- But allow empty catch --> <exclude name="Generic.CodeAnalysis.EmptyStatement.DetectedCatch"/> </rule>Generic.CodeAnalysis.ForLoopShouldBeWhileLoop
whileループに簡略化できるforループを検出します。
for (; true; ) { // This FOR loop can be simplified to a WHILE loop break; }Generic.CodeAnalysis.ForLoopWithTestFunctionCall
テスト式で関数呼び出しを使用するforループを検出します。
$a = [1, 2, 3, 4]; for ($i = 0; $i < count($a); $i++) { // Avoid function calls in a FOR loop test part $a[$i] *= $i; }Generic.CodeAnalysis.JumbledIncrementer
forループでジャンピングするインクリメンターを検出します。
for ($i = 0; $i < 10; $i++) { // Loop incrementor ($i) jumbling with inner loop for ($k = 0; $k < 20; $i++) { echo 'Hello'; } }Generic.CodeAnalysis.UnconditionalIfStatement
無条件のifおよびelseifステートメントを検出します。
trueかfalseのみ設定されている条件が検出されます。if (true) { // Avoid IF statements that are always true or false echo 'Hello'; }Generic.CodeAnalysis.UnnecessaryFinalModifier
finalクラス内の不要なfinal修飾子を検出します。
final class Sample { public final function hoge() // Unnecessary FINAL modifier in FINAL class { } }Generic.CodeAnalysis.UnusedFunctionParameter
未使用の関数パラメーターを検出します。
ただし関数の中身が空、またはコメントのみの場合は検出されません。function hoge($hoge) { // The method parameter $hoge is never used echo 'Hello'; } function fuga($fuga) { }Generic.CodeAnalysis.UselessOverridingMethod
単に親を呼び出す、不要なオーバーライドされたメソッドを検出します。
class Hoge { public function __construct() { echo 'Hello'; } } class Fuga extends Hoge { public function __construct() // Possible useless method overriding detected { parent::__construct(); } }Generic.Commenting.DocComment
docブロックが基本的なフォーマットに従っていることを確認します。
/** */ /** example */Generic.Commenting.Fixme
FIXMEコメントについて警告します。
// fixme: hoge
Generic.Commenting.Todo
TODOコメントについて警告します。
// todo: hoge
訂正等
PHP_CodeSnifferのバージョン
3.0 → ^3.5
- 投稿日:2020-08-11T19:37:55+09:00
LaravelのUnitテストでRefreshDatabaseを使ったらSAVEPOINT trans2 does not exist が出た話
現象
Laravelを利用した環境で通常のケースではTransactionも機能しており問題なかったが、RefreshDatabaseを用いたPHPUnitでの実行ではエラーが発生していた。
Doctrine\DBAL\Driver\PDOException : SQLSTATE[42000]: Syntax error or access violation: 1305 SAVEPOINT trans2 does not exist /src/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:43 /src/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:260 /src/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:238 /src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/RefreshDatabase.php:90 /src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:233 /src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:149 Caused by PDOException: SQLSTATE[42000]: Syntax error or access violation: 1305 SAVEPOINT trans2 does not exist /src/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:41 /src/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:260 /src/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:238 /src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/RefreshDatabase.php:90 /src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:233 /src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:149環境
全てdockerコンテナ内
- mysql: 5.7
- Laravel: 6.18.34
- PHPUnit: 9.3.0
先に結論
- 今回の場合はテストの中(正確にはseeder)でテーブルに対してtruncateを発行していたのが問題になっていた。
- これはMySQLの仕様によるもので暗黙的なコミットが発生してしまうため。
調査方法
- MySQLのクエリログを有効にする
[mysqld] general_log=ON general_log_file=general_query.log
- テストを実行する
/var/lib/mysql/general_query.log
を確認- QueryとExecuteだけ抜き出し
今回はこの時点でSTART TRANSACTION後にテーブルのtruncateを行っていた事に気づけたが一応MySQLコンソールにクエリ流し込んでエラーになることを確認したりもした。
あとがき
LaravelのRefreshDatabaseでRollback出来ない、Transactionがおかしいみたいな内容はググると結構出てくるが、結局きちんと解決しているものが少ないように思えたので残してみることにした。
はじめはLaravel側でクエリログを出力していたが、SAVEPOINT周りなんかは
TransactionRolledBack
、TransactionCommitted
、TransactionBeginning
で追いきれなかったのでMySQL側でログ出力したほうが確実だったなと後から反省。
- 投稿日:2020-08-11T19:26:09+09:00
VSCode Remote-Containers で Laravel7 開発環境 ( CentOS8+PHP7.4+MongoDB4.4 ) を爆速で準備する
はじめに
Docker で開発環境コンテナをサクッと準備。実際の開発は VSCode + Remote-Containers ( 拡張機能 ) を使ってコンテナ内で行うための手順をまとめています。記事内容に間違いなどありましたら、コメント欄にてご指摘お願いいたします。
まだ色々と制約はありますが、とりあえずコンテナを立ち上げるところまでは上手くいったので公開します。前提条件
- Docker ( Windows/MacOS の方は Docker Desktop ) が使用できること
- VSCode が使用できること
ファイル構成
以下の構成でディレクトリとファイルを作成していきます。
とりあえず動かしてみたい方は GitHub から Clone してください。Project/ ├─ .devcontainer/ │ ├─ devcontainer.json │ └─ Dockerfile └─ mongodb-org.repo手順.1 : VSCode に Remote-Containers ( 拡張機能 ) をインストール
VSCode に Remote-Containers という拡張機能をインストールしてください。
手順.2 : devcontainer.json 作成
.devcontainer/devcontainer.json{ "name": "test", "context": "..", "dockerFile": "Dockerfile", "settings": { "terminal.integrated.shell.linux": null, "workbench.startupEditor": "newUntitledFile", "editor.minimap.enabled": true, "vsicons.dontShowNewVersionMessage": true, "explorer.confirmDelete": false, "files.autoSave": "afterDelay" }, "appPort": [ 8000, 27017 ], "remoteUser": "vscode", "workspaceFolder": "/var/www/html", "extensions": [ "alefragnani.bookmarks", "mikestead.dotenv", "mhutchie.git-graph", "eamodio.gitlens", "onecentlin.laravel-blade", "austenc.laravel-blade-spacer", "onecentlin.laravel5-snippets", "cjhowe7.laravel-blade", "felixfbecker.php-pack", "vscode-icons-team.vscode-icons" ] }手順.3 : Dockerfile 作成
.devcontainer/DockerfileFROM centos:8 LABEL maintainer="slangsoft" LABEL version="1.0" LABEL description="Laravel sample project for trying out the VS Code Remote - Containers extension." ARG USERNAME=vscode ARG USER_UID=1000 ARG USER_GID=$USER_UID ARG PATH_LARAVEL=/var/www/html # Basic settings RUN set -x \ && dnf -y upgrade \ && dnf -y install sudo dnf-utils wget git \ && groupadd --gid $USER_GID $USERNAME \ && useradd --shell /bin/bash --uid $USER_UID --gid $USER_GID --create-home $USERNAME \ && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ && chmod 0440 /etc/sudoers.d/$USERNAME # Install nginx RUN dnf -y install nginx # Install php RUN set -x \ && dnf module reset php \ && dnf -y install epel-release glibc-locale-source glibc-langpack-en \ && dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm \ && dnf -y module install php:remi-7.4 # Install Laravel EXPOSE 8000 RUN set -x \ && dnf -y install php-mcrypt php-pdo php-bcmath php-tokenizer php-xml php-mysqlnd php-gd php-intl php-zip php-opcache php-pecl-xdebug \ && wget https://getcomposer.org/installer -O composer-installer.php \ && php composer-installer.php --filename=composer --install-dir=/usr/local/bin \ && composer self-update \ && composer create-project laravel/laravel $PATH_LARAVEL --prefer-dist \ && groupadd laravel \ && gpasswd -a $USERNAME laravel \ && gpasswd -a nginx laravel \ && find $PATH_LARAVEL -type d -exec chmod 775 \{\} \; \ && find $PATH_LARAVEL -type f -exec chmod 664 \{\} \; \ && chown -R :$USERNAME $PATH_LARAVEL \ && find $PATH_LARAVEL -type d -exec chmod g+s \{\} \; \ && setfacl -R -d -m g::rwx $PATH_LARAVEL # Install MongoDB EXPOSE 27017 COPY ./mongodb-org.repo /etc/yum.repos.d/ RUN dnf -y install mongodb-org # Set locale RUN localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 ENV LANG="ja_JP.UTF-8" ENV LANGUAGE="ja_JP:ja" ENV LC_ALL="ja_JP.UTF-8" # Working directory setting WORKDIR $PATH_LARAVEL手順.4 : mongodb-org.repo 作成
mongodb-org.repo[mongodb-org-4.4] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.4/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-4.4.asc手順.5 : コンテナ起動
コマンドパレットを開いて [remote-containers: Open Folder in Container...] を実行するとコンテナのビルドが始まります。無事にビルドが完了すると、コンテナ内で VSCode が起動します。
参考リンク
- VS CodeでDocker開発コンテナを便利に使おう
- CentOS8にLaravel7をインストールする
- CentOS8にMongoDBをインストールする手順
- Dockerfile リファレンス
- 【2020年1月】令和だし本格的にVSCodeのRemote Containerで、爆速の"開発コンテナ"始めよう
改訂履歴
- 2020/08/11
初版公開 ( ほとんど説明も無い叩き台記事 )
- 投稿日:2020-08-11T17:46:44+09:00
【PHP/Laravel】Class 'Collective\Html\HtmlServiceProvider' not found への対処法
はじめに
Laravelでアプリ開発をしております。
環境構築が完了し、Formを実装した際にエラーが発生。
備忘のために記録しております。
同じような方の助けになれば幸いです。開発環境
・MacOS:10.15.6 (Catalina)
・PHP7.3.20
・Laravel6.18.35対処法
①プロジェクト直下の「composer.json」に「"laravelcollective/html": "^6.0"」を追記
※これでもエラーが起きる場合はLaravelのバージョンとlaravelcollective/htmlのバージョンを合わせる(ex. Laravel5.8なら、laravelcollective/htmlの5.8)。
composer.json"require": { "php": "^7.2", "fideloper/proxy": "^4.0", "laravel/framework": "^6.2", "laravel/tinker": "^2.0" , "laravelcollective/html": "^6.0" },②ターミナルで「composer update」を実行
$ composer update Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 2 installs, 3 updates, 0 removals - Updating egulias/email-validator (2.1.18 => 2.1.19): Downloading (100%) - Installing league/mime-type-detection (1.4.0): Downloading (100%) - Updating league/flysystem (1.0.70 => 1.1.0): Downloading (100%) - Updating nikic/php-parser (v4.7.0 => v4.8.0): Downloading (100%) - Installing laravelcollective/html (v6.1.2): Downloading (100%) Package jakub-onderka/php-console-color is abandoned, you should avoid using it. Use php-parallel-lint/php-console-color instead. Package jakub-onderka/php-console-highlighter is abandoned, you should avoid using it. Use php-parallel-lint/php-console-highlighter instead. Writing lock file Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: laravelcollective/html Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully. 40 packages you are using are looking for funding. Use the `composer fund` command to find out more!③「config/app.php」への下記の追記
※Laravel5.5以上はPackage Auto Discoveryが入っているため不要。
config/app.php'providers' => [ Collective\Html\HtmlServiceProvider::class, ], 'aliases' => [ 'Form' => Collective\Html\FormFacade::class, 'Html' => Collective\Html\HtmlFacade::class, ],以上となります。
以下、参考にさせていただいたサイトも掲載させていただきます。
ご参考にしていただければ。参考文献
・Laravelで「Formクラスが無い」とエラーが出た時の対処法
・【Laravel】Formファサード(laravelcollective/html)をインストールする
- 投稿日:2020-08-11T17:05:02+09:00
"Test code or tested code did not (only) close its own output buffers"エラーの調査方法
Cakephp3でControllerのテストをしていたのですが、次のようなメッセージが表示されました
Test code or tested code did not (only) close its own output buffers
エラーメッセージでコードを検索すると、出力バッファの階数を検査している箇所でエラーになっていることが分かりました。出力バッファを開始したもののきちんと終了できていないことが原因でにエラーになっています。
/vendor/phpunit/phpunit/src/Framework/TestCase.phpprivate function stopOutputBuffering() { if (\ob_get_level() != $this->outputBufferingLevel) { while (\ob_get_level() >= $this->outputBufferingLevel) { \ob_end_clean(); } throw new RiskyTestError( 'Test code or tested code did not (only) close its own output buffers' ); } ... 以下続きますそこで、テスト対象のコードから ob_start() を探して、終了するまでの間に何か起きていないか調べてました。デバッガを止めながら実行してみると、途中でExceptionを吐いている箇所を発見することができました。
まとめると
Test code or tested code did not (only) close its own output buffers
のエラーになった場合は、デバッガでステップ実行しながらExceptionを吐き出している箇所を見つけましょう。ということになります。バージョン
- PHPUnit 6.5.14
- Cakephp 3.7.7
- 投稿日:2020-08-11T15:15:12+09:00
PHPでRedis Streams操作のメモ
PHPでStream Consumingしたい、ということでRedis Streamsを使って試してみたという記録です。Swooleを使ってサーバー1台でスケールするのかも試してみました。
関連ドキュメントをざっと読んで試してみた程度の内容なので間違っている箇所などあるかもしれません。Redis Streams
Redis5で実装されたStreaming API. Kafkaインスパイアな作りになっている。
PHPからRedis Streamsの使用
phpredisの4.2でサポートされた模様。以前は、導入が楽だという理由でpredisを使うことが多かったけど、predisではサポートされていない。predisは完全に開発が止まってそう。
Redis Server
試すだけならDockerでいいですね。
docker run -d --rm -p 6379:6379 redis:5.0.9一応Streams APIが動くか確認したかったので
redis-cli
など使って$ redis-cli > XADD mystream * key value > XREAD COUNT 1 STREAMS mystream 0-0 1) 1) "mystream" 2) 1) 1) "1596783209134-0" 2) 1) "key" 2) "value"みたいな結果になることを確認する。コマンドが複雑で手で打つと(私は)だいたい何回かエラーになるのでコピペする方が楽だと思う。
XADD, XREADのコマンドの詳細はredisのサイトを見てください。Redis StreamsのConsumerにはメッセージングシステムは無く、Pub/SubのようにSubscribeしておけばProduceされたことを通知される、という挙動にはならない。
Consumer
ざっくりこんな感じのコードで動いた。
<?php $redis = new Redis(); $redis->connect('redis-server', 6379); $consumer = new Consumer($redis); while (true) { $values = $consumer->consume(); foreach ($values[$streamName] ?? [] as $consumed) { foreach ($consumed as $key => $value) { print_r($value); } } } class Consumer { private $redis; public function __construct(Redis $redis) { $this->redis = $redis; $this->createGroupIfNotExists(); } private function createGroupIfNotExists() { // XREADではなくXREADGROUPを使うため、事前にConsumerGroupが存在することを保証する // わざわざxInfoで確認してるけど、XGROUPにはGROUPがなかったときに作成する機能もあり // ライブラリもサポートしているので素直にxGroupのMKSTREAMオプションを有効にする方が良いでしょう $groupInfo = $this->redis->xInfo('GROUP', 'mystream', 'mygroup'); if (!$groupInfo) { $this->redis->xGroup('CREATE', 'mystream', 'mygroup', '>'); } // これと等価 // '>' の意味は概要を後述しますが、Redis StreamsのIntroductionを読むと良いです。 // $this->redis->xGroup('CREATE', 'mystream', 'mygroup', '>', $mkStream = true); } public function consume() { return $this->redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], $count = 10, $block = 2000); } }なぜ
XREAD
ではなくXREADGROUP
を使うかというと、スケーラビリティのためと、単純にどこまでのIDを読み取ったか管理するのがめんどくさいから。XREAD
にはRedis側にどこのキーまで読んだか、というオフセットを記録するような仕組みが無いようなので、Consumer単位で自動的に未取得部分のレコードを取得したい場合はXREADGROUP
を使用する必要がありそう。$this->redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], $count = 10, $block = 2000);
consume
メソッドのこの行の意味は
Consumer Groupgroup1
の Consumerconsumer1
で、mystream
というキーのストリームから、group1で未取得のレコードを最大10件取得する。すべてのレコードを取得済みの場合、レコードが追加されることを最大2,000msec待つ。
という挙動になる。このうち、未取得のレコードを最大10件取得する
を実現するために['mystream' => '>']
と指定をする。スケーラビリティ
Consumerがスケールするのかを見てみる。
KafkaだとConsumerをスケールさせるためにはTopicにパーティションを設定し、Consumer GroupにConsumerを追加したり、Fan Outさせたりするのだと思う(実運用したことが無いので自信は無い)。
一方Redis Streamsにはこういうパーティションの概念は無さそう。しかし、
XREADGROUP
の'>'
はGROUPに対しての値なので、GROUPにConsumerを追加してXREADGROUP
を叩けば自動的にそれぞれのConsumerで取得できる。リバランスも気にする必要は無い。
サーバー台数(プロセス数)が増えてBLOCKのリクエストが溜まったときのレイテンシーどうなんだろう。READリクエストがキューに積まれて順番に配信されるのかな。特定のConsumerが特定のパーティションに紐づくわけでもないのでホットスポットも考えなくて良い、となるのかな。とりあえずは単純にサーバー台数(プロセス数)を増やせばスケールできるのだと思うが、別プロセスで動かすとなるとConsumerの名前決めがめんどくさそう。名前はGROUP内でユニークにしろという制約があるので、マジメにやると大変そう。同じ名前にしても動きはするので、consumerの状態を見るとか、メトリクスを管理するとか運用上の理由が大きいのかな。
Swoole
Redis Streamsにはいわゆるメッセージングシステムが無く、BLOCKを使いながら同期的に処理できるのでPlain-PHPと相性がいいんじゃないか?と思うけど、Swooleを使って単一プロセスでスケールできるか試してみる。
方針として、
XREADGROUP
は単一プロセスで十分なスループットだという前提の上で、値を取得したらConsumerのワーカー部分を並行で稼働できようにしている。XREADGROUP
が追いつかないようになったらRedisへのリクエストも多重化すれば良さそう。コードはこんな感じになった。
<?php $messageBufferChannel = new Co\Channel(3); // Worker Co\run(function () use ($messageBufferChannel) { // Worker内の処理を多重度を制限するためのChannel $concurrencyCapChannel = new Co\Channel(5); while (true) { $values = $messageBufferChannel->pop(); if ($values) { $concurrencyCapChannel->push(true); go(function() use ($values, $concurrencyCapChannel) { echo "got values " . count($values['mystream']) . "\n"; $concurrencyCapChannel->pop(); }); } } }); // Consumer Co\run(function () use ($messageBufferChannel) { $redis = new Redis(); $redis->connect('redis-server', 6379); while (true) { $values = $redis->xReadGroup('group1', 'consumer10', ['mystream' => '>'], $count = 10, $block = 2000); if ($values) { $messageBufferChannel->push($values); } } });Goもしばらく書いてなかったのでだいぶ難航した。こんな感じで良かったんだったかな…
ちなみにSwooleをDockerで動かすには https://www.swoole.co.uk/docs/get-started/try-docker を参考にすれば良いのだけど、ここで紹介されているイメージにはphpredisが入っていないので
RUN pecl install redis && docker-php-ext-enable redis
をDockerfileに適当に追加してビルドしてる。
試しに書いてはみたものの、Swooleについての知識が薄くて実際にプロダクションで使うにはもっと調べないと不安で使えないな。
関連ドキュメント
- 投稿日:2020-08-11T14:39:29+09:00
SimpleSAMLphpで属性をマッピングする
"属性をマッピングする" とは
SimpleSAMLphp とはなにか、SAML とは、などの説明は割愛します。こちらです。
今回やりたいことは、SAML で認証・認可を行う際に渡される属性の名前、およびその値を別名に変換することです。
属性名をマッピングする
ここを読みます。
あらゆる SP や IdP に対して同じように処理を行う場合は、config/config.php を編集します。個別に設定したい場合は、以下のファイルを編集します。
- SP において、特定の SP について設定 : config/authsources.php
- SP において、リモート IdP について設定 : metadata/saml20-idp-remote.php または metadata/shib13-idp-remote.php
- IdP において、ホスト IdP について設定 : metadata/saml20-idp-hosted.php または metadata/shib13-idp-hosted.php
- IdP において、特定の SP について設定 : metadata/saml20-sp-remote.php または metadata/shib13-sp-remote.php
記法
属性 "A" を属性 "B" にマッピングするとき、
'authproc' => array( 50 => array( 'class' => 'core:AttributeMap', 'A' => 'B', ), ),50 は 50 じゃなくてもいいです。
複数定義する時は、","区切りで並列に並べます。
authproc 内で属性マッピング以外にも様々なフィルターを設定できます。詳しくはこちら。マッピングのルールを別ファイルで定義することもできます。
addurnprefix.php で定義した場合、(デフォルトで入ってます。 attributemap/addurnprefix.php)'authproc' => array( 50 => array( 'class' => 'core:AttributeMap', 'addurnprefix', ), ),addurnprefix.php<?php $attributemap = array( 'sn' => 'urn:mace:dir:attribute-def:sn', 'telephoneNumber' => 'urn:mace:dir:attribute-def:telephoneNumber', 'facsimileTelephoneNumber' => 'urn:mace:dir:attribute-def:facsimileTelephoneNumber', 'postalAddress' => 'urn:mace:dir:attribute-def:postalAddress', 'givenName' => 'urn:mace:dir:attribute-def:givenName', 'homePhone' => 'urn:mace:dir:attribute-def:homePhone', 'homePostalAddress' => 'urn:mace:dir:attribute-def:homePostalAddress', 'mail' => 'urn:mace:dir:attribute-def:mail', 'mobile' => 'urn:mace:dir:attribute-def:mobile', 'preferredLanguage' => 'urn:mace:dir:attribute-def:preferredLanguage', 'eduPersonPrincipalName' => 'urn:mace:dir:attribute-def:eduPersonPrincipalName', 'eduPersonAffiliation' => 'urn:mace:dir:attribute-def:eduPersonAffiliation', 'eduPersonScopedAffiliation' => 'urn:mace:dir:attribute-def:eduPersonScopedAffiliation', 'eduPersonEntitlement' => 'urn:mace:dir:attribute-def:eduPersonEntitlement', 'eduPersonOrgDN' => 'urn:mace:dir:attribute-def:eduPersonOrgDN', 'eduPersonOrgUnitDN' => 'urn:mace:dir:attribute-def:eduPersonOrgUnitDN', );属性値をマッピングする
ここまでは、属性の名前を変換していましたが、次は実際に渡される属性の値を変換します。
詳しくは、ここを読みます。
属性名のマッピング同様に config.php などのファイルを編集します。記法
属性 "A" が 属性値 "attr_1" または "attr_2" を含む時、属性 "B" に属性値 "attr_12" を加える場合、
'authproc' => array( 50 => array( 'class' => 'core:AttributeValueMap', 'sourceattribute' => 'A', 'targetattribute' => 'B', 'values' => array( 'attr_12' => array( 'attr_1', 'attr_2', ), ), ),この場合、渡される属性のうち、属性 "A" は消去され、属性 "B" はもともと持っていた属性値に加えて "attr_12" が追加されます。
属性 "A" を残したい場合は %keep、属性 "B" がもともと持っていた属性値を新しく "attr_12" に変換したい場合には %replace を使います。'authproc' => array( 50 => array( 'class' => 'core:AttributeValueMap', 'sourceattribute' => 'A', 'targetattribute' => 'B', '%replace', // 追加 '%keep', // 追加 'values' => array( 'attr_12' => array( 'attr_1', 'attr_2', ), ), ),values の配列内で複数の属性値について定義することができます。
属性名をマッピングする場合のように別ファイルで定義することができるのかどうかは不明です……。まだまだ手探りなのでまた追記します。
- 投稿日:2020-08-11T14:29:26+09:00
dockerでcomposer updateしようとしたらメモリ不足。。ターミナルからphp.iniでmemoryの設定変更
- 投稿日:2020-08-11T14:02:12+09:00
【PHP】基礎 FizzBuzz
はじめに
phpの基礎を学ぶ過程でFizzBuzzのプログラムを組みました。
ソース
1~100までの連続する整数を生成し、
3の倍数: Fizz
5の倍数: Buzz
3と5の倍数: FizzBuzzと出力します。
出力優先度は FizzBuzz > Fizz, Buzz
となっております。php<?php for ($i = 1; $i < 101; $i++) { if ($i % 3 === 0 && $i % 5 === 0) { echo $i.":FizzBuzz"; echo "<br/>"; } else if ($i % 3 === 0) { echo $i.":Fizz"; echo "<br/>"; } else if ($i % 5 === 0) { echo $i.":Buzz"; echo "<br/>"; } else { echo $i; echo "<br/>"; } } ?>出力結果
1 2 3:Fizz 4 5:Buzz 6:Fizz 7 8 9:Fizz 10:Buzz 11 12:Fizz 13 14 15:FizzBuzz 16 17 18:Fizz . . .
- 投稿日:2020-08-11T13:59:53+09:00
忘れやすいがよく使う PHPでログを取る方法
ログの設定 ini_set('log_errors', 'on'); ログを取るか ini_set('error_log', 'php.log'); ログの出力ファイルを指定する ログを吐かせる error_log('SUCCESS');
- 投稿日:2020-08-11T12:45:33+09:00
三項演算子の世界へようこそ
はじめに
三項演算子、使ってますか?
先日とうとう後輩の @miriwo 氏がめでたく三項演算子に目覚めたということで、これはめでたいということで筆を執りました。まあ、タイトルだけ見るとエルビス演算子に目覚めたのかなって気になってましたけど。
それはそれで大変便利なのです。三項演算子とは
例を見てみましょう。
転載元: https://chiicomi.com/smp/r_news_detail?id=19e92c3c-0b86-11e6-8bd0-a0369f7b8e6eこちらは我らがジェフユナイテッド市原千葉のマスコット、ジェフィとユニティです。
(千葉なのに)秋田犬の双子のわんこなんですが、キャラクターデザイン上の取り計らいで、大きさと背番号以外は区別がつかないようになっています。
ある程度のレベルのジェフサポになると 「キリッとしてるほうがジェフィ、やんちゃな方がユニティ」 とか 「耳がシャキッとしてるほうがジェフィ、丸っこい耳がユニティ」 とか言い出したりするんですが、他サポからは 「ジェフサポは(いろいろな意味で)あたまがおかしい」 と思われているので聞き入れてもらえません。なので、犬の背番号が2だったらジェフィ、2じゃなかったユニティを
name
に代入するコードをJavaScriptで書いてみました。const name = (dog.uniform_number === 2) ? 'Jefy' : 'Unity';
?
の左に判定条件、その右に判定がtrue
のときの値、:
を挟んでその右がfalse
のときの値です。
これをたまにいる三項演算子否定派が書くとこんな感じになります。const name = ''; if(dog.uniform_number === 2){ name = 'Jefy'; } else { name = 'Unity'; }長い……
要はこういうのをif文ではなく式で簡便に表現できるというのが三項演算子です。
実は本名は「条件演算子」といいます。https://ja.wikipedia.org/wiki/%E6%9D%A1%E4%BB%B6%E6%BC%94%E7%AE%97%E5%AD%90
三項演算子は結構どの言語にも実装されています。
入れ子の三項演算子
あんまりやってはいけないのですが、三項演算子はネスト(入れ子)して書くことができます。
例を見てみましょう。
実はジェフのマスコットはジェフィとユニティのオス犬の双子だけではなく、どこからともなく現れた みなちゃん という白い雑種のメス犬もいます。写真の一番右の子ですね。
ちなみにジェフィとユニティは毎年シーズンシートを買って入っている一般サポーターらしいのですが、みなちゃんはれっきとしたジェフユナイテッド株式会社の社員です。そんな設定の話は置いといて、みなちゃんの背番号はサポーターナンバーと呼ばれる12番です。つまりこんな感じになります。
const name = (dog.uniform_number === 2) ? 'Jefy' : (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang';どうですか? 見づらくてハゲそうですよね。
見やすくするテクニックとして、記号の前で改行するというのがあります。
外人が書くコードでよく見かけますよね。const name = (dog.uniform_number === 2) ? 'Jefy' : (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang';ただ、この入れ子の三項演算子、PHPだけなぜか動作が違います。あいつはなぜか 右から評価 するので、こんな感じに書かないと思ったとおりになりません。
$name = (dog.uniform_number === 2) ? 'Jefy' : ( (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang');不毛感がすごい……
おわりに
三項演算子の特殊な書き方として、エルビス演算子とかnull合体演算子の話は次回書きます。
それではお元気で! Enjoy, Summer!!
- 投稿日:2020-08-11T12:45:33+09:00
(ジェフユナイテッドで覚える)三項演算子の世界へようこそ
はじめに
三項演算子、使ってますか?
先日とうとう後輩の @miriwo 氏がめでたく三項演算子に目覚めたということで、これはめでたいということで筆を執りました。まあ、タイトルだけ見るとエルビス演算子に目覚めたのかなって気になってましたけど。
それはそれで大変便利なのです。三項演算子とは
例を見てみましょう。
転載元: https://chiicomi.com/smp/r_news_detail?id=19e92c3c-0b86-11e6-8bd0-a0369f7b8e6eこちらは我らがジェフユナイテッド市原千葉のマスコット、ジェフィとユニティです。
(千葉なのに)秋田犬の双子のわんこなんですが、キャラクターデザイン上の取り計らいで、大きさと背番号以外は区別がつかないようになっています。
ある程度のレベルのジェフサポになると 「キリッとしてるほうがジェフィ、やんちゃな方がユニティ」 とか 「耳がシャキッとしてるほうがジェフィ、丸っこい耳がユニティ」 とか言い出したりするんですが、他サポからは 「ジェフサポは(いろいろな意味で)あたまがおかしい」 と思われているので聞き入れてもらえません。なので、犬の背番号が2だったらジェフィ、2じゃなかったユニティを
name
に代入するコードをJavaScriptで書いてみました。const name = (dog.uniform_number === 2) ? 'Jefy' : 'Unity';
?
の左に判定条件、その右に判定がtrue
のときの値、:
を挟んでその右がfalse
のときの値です。
これをたまにいる三項演算子否定派が書くとこんな感じになります。const name = ''; if(dog.uniform_number === 2){ name = 'Jefy'; } else { name = 'Unity'; }長い……
要はこういうのをif文ではなく式で簡便に表現できるというのが三項演算子です。
実は本名は「条件演算子」といいます。https://ja.wikipedia.org/wiki/%E6%9D%A1%E4%BB%B6%E6%BC%94%E7%AE%97%E5%AD%90
三項演算子は結構どの言語にも実装されています。
入れ子の三項演算子
あんまりやってはいけないのですが、三項演算子はネスト(入れ子)して書くことができます。
例を見てみましょう。
実はジェフのマスコットはジェフィとユニティのオス犬の双子だけではなく、どこからともなく現れた みなちゃん という白い雑種のメス犬もいます。写真の一番右の子ですね。
ちなみにジェフィとユニティは毎年シーズンシートを買って入っている一般サポーターらしいのですが、みなちゃんはれっきとしたジェフユナイテッド株式会社の社員です。そんな設定の話は置いといて、みなちゃんの背番号はサポーターナンバーと呼ばれる12番です。つまりこんな感じになります。
const name = (dog.uniform_number === 2) ? 'Jefy' : (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang';どうですか? 見づらくてハゲそうですよね。
見やすくするテクニックとして、記号の前で改行するというのがあります。
外人が書くコードでよく見かけますよね。const name = (dog.uniform_number === 2) ? 'Jefy' : (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang';ただ、この入れ子の三項演算子、PHPだけなぜか動作が違います。あいつはなぜか 右から評価 するので、カッコを使って、こんな感じに書かないと思ったとおりになりません。
$name = (dog.uniform_number === 2) ? 'Jefy' : ( (dog.uniform_number === 9) ? 'Unity' : 'Mina-chang');不毛感がすごい……
※追記
@rana_kualu さんからコメントでいただきましたが、カッコを使わない三項演算子のネストはPHP7.4からは非推奨、PHP8からはエラーになるとのことです。
参考: 【PHP8.0】PHPの三項演算子が他言語の実装に一歩近付くおわりに
三項演算子の特殊な書き方として、エルビス演算子とかnull合体演算子の話は次回書きます。
それではお元気で! Enjoy, Summer!!
- 投稿日:2020-08-11T11:50:31+09:00
GuzzleHttpでのレスポンスをモック化してテストする
まえがき
GuzzleHttpを利用してHTTPリクエストをしている処理のユニットテストを書きたい。
GuzzleHttpにはレスポンスをモック化する機能が用意されているようなので、こちらを利用してみることにします。実装例
HogeTest.php<?php use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Client; class HogeTest extends \Codeception\Test\Unit { /** * @group guzzletest */ public function testGuzzleTest() { /* Arrange */ $response = new Response(200, [], 'Response Body'); $mock = new MockHandler([$response]); $handler = HandlerStack::create($mock); $client = new Client(['handler' => $handler]); $url = 'http://localhost:8080/'; $params = [ 'token' => 'hogehoge' ]; /* Act */ $res = $client->post($url, ['form_params' => $params]); /* Assert */ $this->assertEquals('Response Body', $res->getBody()); } }実行結果$ vendor/bin/codecept run unit --group guzzletest -c common/ Codeception PHP Testing Framework v3.0.2 Powered by PHPUnit 8.2.4 by Sebastian Bergmann and contributors. Running with seed: [Groups] guzzletest Common\tests.unit Tests (1) ----------------------------------------------------------- ✔ HogeTest: Guzzle test (0.04s) --------------------------------------------------------------------------------------- Time: 1.96 seconds, Memory: 32.00 MB OK (1 test, 1 assertion)
- 上記例では、テストコードで直接クライアントを作成している
- GuzzleHttpを利用しているServiceクラスやcomponentのテストを行う場合、setClient()などを実装していれば、MockHandlerで生成したClientをテストコードからセットしてレスポンスをモック化する
つまりGuzzleはとても便利。。
- 投稿日:2020-08-11T10:17:06+09:00
PHP array_key_exists関数
目的
- 便利そうな関数だったので自分用に使い方をざっとまとめてみる
前提情報
- 下記のサービスを用いて関数の振る舞いを確認した。
機能
- 指定した文字列が指定した配列に格納されていることを判定する関数である。
例
下記にarray_key_exists関数の例を記載する。
//配列に文字列が含まれているかを確認する処理、含まれていればtrueが返される。 array_key_exists(文字列, 配列);具体例
- 配列
$array
に「str_1」というインデックスが存在していることを判定する処理を記載する。格納されている場合trueを、格納されていない場合falseを返す。
array_key_exists('str_1', $array);下記の様な処理が実行された場合「str_1の要素が存在します」と出力される。
$array = [ 'str_1' => 1, ]; if (array_key_exists('str_1', $array)) { echo 'str_1 の要素が存在します'; }参考文献
- 投稿日:2020-08-11T10:06:41+09:00
【Wordpress】標準テーマの Powered by Wordpress を書きかえる
Wordpressの標準テーマはシンプルで使いやすいですが、企業のウェブサイトで利用する場合など、フッターの「Powered by Wordpress」を削除したい場合が多々あります。
CSSでパワードバイを非表示にする
これまでは、アップデート時の影響を考慮してテンプレートを直接編集することは避け、追加CSSに以下を記述して非表示にしていました。
.powered-by-wordpress { display: none; }プラグインでパワードバイを書きかえる
上記の方法だと、非表示にするだけ自由に編集はできません。
また、HTML上には Powerd by Wordpressの文字が残ったままになります。そこで、パワードバイを書きかえるプラグインを作成しました。
Elvez Edit Powered By(プラグインのダウンロードページ)設定画面でテキストとURLを入力すると、パワードバイを書きかえてくれます。
何も設定しない場合は非表示になるので、パワードバイの表記が不要な場合にも対応できます。プラグインの機能はシンプルですが、書きかえるテキストを保存したり、javascriptに値を渡したりと、いくつか実装で工夫をしています。こういう実装もできるというのがあれば、ぜひ教えてください。
他にも社内で利用しているソースコードの一部を、無料プラグインとして公開しています。
https://elvez.co.jp/wordpress/
使ってみて役に立つと思ったら、ぜひ寄付など応援をお願いします。
- 投稿日:2020-08-11T09:12:11+09:00
【Laravel】メール送信機能
はじめに
問い合わせフォームで問い合わせがあった場合に、内容をメールで通知するシステムを組んだ際の備忘録。
LaravelのMailableクラスを利用し送信します。
ファイル構成
app
|- Http
| |- SendController.php
|
|- SendMail.phpresources
|- views
|- contact
|- mail.blade.php.env
コード
Controller
send
メソッドの引数に、Mailable
クラスを継承したクラスを渡す。SendController.php<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Mail; use Illuminate\Http\Request; class SendController extends Controller { public function submit(Request $req){ //メールを送信 Mail::to("送信先アドレス") ->send(new SendMail($req['message'])); return view('sample'); } }Mailableクラス
コンストラクタで表示するメッセージリストを受け取る。
build
メソッドで、あらかじめ準備したbladeを利用して本文を作成する。
HTMLメールの場合view
メソッド、通常テキストの場合text
メソッドを使う。SendMail.php<?php namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; class SendMail extends Mailable { use Queueable, SerializesModels; protected $message; /** * Create a new message instance. * * @return void */ public function __construct($message) { //コンストラクタで表示するメッセージを受け取る $this->message = $message; } /** * Build the message. * * @return $this */ public function build() { return $this->text('contact.mail') ->subject('件名') ->with(['message' => $this->message]); } }View
変数を展開して本文を作成する。
mail.blade.php問い合わせが来ました。 --------------------- 名前:{{$message->name}} アドレス:{{$message->address}} 内容:{{$message->body}} ---------------------.env
.env
にメールサーバの情報を記載する。.envMAIL_MAILER=smtp MAIL_HOST=*** MAIL_PORT=*** MAIL_USERNAME=*** MAIL_PASSWORD=*** MAIL_ENCRYPTION=tls MAIL_FROM_ADDRESS=*** MAIL_FROM_NAME="${APP_NAME}"最後に
利用するサーバの情報が間違っていてはまりました。
処理を何度も見直したことで理解が深まったのでOK!!!
- 投稿日:2020-08-11T02:49:29+09:00
nginxの最低限これだけやっとけば安心だろう設定
セキュリティ面や運用面など考え、これだけ設定しておけば一先ず大丈夫だろうと思うものを
書籍や沢山の記事を参考にさせて頂き、まとめました。※もしこれも設定したほうがいい、ここ間違ってるよなど
アドバイスがあれば是非コメントくださいアウトライン
- nginx設定まとめ
- 各設定の簡易解説
- 今後の課題
nginx設定まとめ
内容については後述で解説しますが、configの全体としてはこんな感じです。
nginx.confuser nginx; worker_processes auto; worker_rlimit_nofile 10000; error_log /var/log/nginx/error.log info; pid /var/run/nginx.pid; events { worker_connections 1024; multi_accept on; use epoll; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer"' '"$http_user_agent" "$http_x_forwarded_for" $request_uri $request_time'; access_log /var/log/nginx/access.log main; include /etc/nginx/conf.d/*.conf; sendfile on; tcp_nopush on; keepalive_timeout 300; server_tokens off; server_names_hash_bucket_size 128; ssl_protocols TLSv1.2 TLSv1.3; add_header "X-XSS-Protection" "1; mode=block"; add_header X-Frame-Options SAMEORIGIN; gzip on; gzip_proxied any; gzip_comp_level 1; gzip_disable "msie6"; gzip_min_length 1000; gzip_types text/plain text/xml text/css application/javascript application/json text/csv application/csv; } server { listen 80; server_name ~.com; charset UTF-8; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log info; root /var/www/html/; index index.php; fastcgi_read_timeout 300; set_real_ip_from 192.168.1.1/24; real_ip_header X-Forwarded-For; client_max_body_size 100M; client_body_buffer_size 50M; proxy_request_buffering on; # proxy_buffer_size ~k; # proxy_buffers ~ ~k; # proxy_busy_buffers_size ~k; location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } location ~ .*\.(jpg|JPG|gif|GIF|png|PNG|swf|SWF|css|CSS|js|JS|inc|INC|ico|ICO)$ { access_log off; } location ~ /\. { deny all; } location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param HTTPS on; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } if ($request_method !~ ^(GET|HEAD|POST|OPTIONS)$ ){ return 444; } if (!-d $request_filename) { rewrite ^/(.+)/$ /$1 permanent; } } server { listen 80 default_server; server_name _; return 404; }※環境
・CentOS7
・nginx-1.16.1
・ロードバランサー(ELB)使用各設定の簡易解説
・worker_processes
worker_processes auto;nginxのworkerプロセス数の設定ができ、autoにすればCPUのコア数をみて自動で設定してくれる。
・worker_rlimit_nofile
worker_rlimit_nofile 10000;workerプロセスが最大に開けるファイル数の制限。この辺を設定しておけば「too many open files」問題を回避できる。
OSで扱える最大ファイル数÷CPUコア数=最大設定値・worker_connections
worker_connections 1024;1つのワーカープロセスが同時に処理できる最大接続数。
worker_rlimit_nofile(ファイルディスクリプタ)値以上を設定することは出来ない。・multi_accept
multi_accept on;同時リクエスト受付on
無効にするとNginxはリクエストを1つずつ処理する・multi_accept
use epoll;コネクションの処理方式。
selecet,poll,epollの選択肢のうち、epollが扱うファイルが無制限で高速な処理が可能。・log_format
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer"' '"$http_user_agent" "$http_x_forwarded_for" $request_uri $request_time';アクセスログの書式を設定。ログに欲しい情報があればここに追加する。
経験上、$http_x_forwarded_for, $request_uri, $request_time
はログに出力されていたら便利。・sendfile
sendfile on;onにするとカーネルのsendfileを使用し、効率良くファイルの内容をクライアントに送信できる。
・tcp_nopush
tcp_nopush on;onにするとレスポンスヘッダとファイルをまとめて送るようになり、より少ないパケット数で効率よく転送できる。
・keepalive_timeout
keepalive_timeout 300;HTTP通信をタイムアウトせずに待つ秒数。サービスやインフラ環境と相談。
・server_tokens
server_tokens off;バージョン情報隠蔽。
・server_names_hash_bucket_size
server_names_hash_bucket_size 64;仮想ホスト名が長くても大丈夫なように設定。
それでもincrease server_name...
のようなエラーがでる場合は、さらに多く設定する。・ssl_protocols
ssl_protocols TLSv1.2 TLSv1.3;ダウングレード攻撃やPOODLE対策でSSLのバージョン指定設定。(AWSのELBを使用してる場合は、そこでされるので不要)
・X-XSS-Protection
add_header "X-XSS-Protection" "1; mode=block";各ブラウザのクロスサイトスクリプティング(XSS)のフィルタ機能を有効化。
・X-Frame-Options
add_header X-Frame-Options SAMEORIGIN;外部サイトでiframeでの表示を禁止する
・gzip
gzip on;コンテンツをgzip圧縮して転送する。
・gzip_proxied
gzip_proxied any;anyにすることで、リクエスト別で圧縮するか判断せず、全てのリクエストを対象とする。
・gzip_comp_level
gzip_comp_level 1;圧縮レベルを設定。0~9の間で設定可能(0は非圧縮)。
・gzip_disable
gzip_disable "msie6";gzip圧縮に対応していない場合もあるので、gzip圧縮をしないブラウザを設定。
・gzip_min_length
gzip_min_length 1000;容量の軽いファイルだと圧縮のほうが時間がかかったりするので、gzip圧縮される最小の容量サイズを設定。
・gzip_types
gzip_types text/plain text/xml text/css application/javascript application/json text/csv application/csv;圧縮対象のファイルタイプを設定。
・fastcgi_read_timeout
fastcgi_read_timeout 300;FastCGIプロセスへデータを送信するための待ち時間。nginx.confだけでなく、他のタイムアウト設定とも合わせたほうが良い。
・set_real_ip_from
set_real_ip_from 192.168.1.1/24;nginxの前にELBやプロキシなどを挟んでいる場合、それらのIPを信頼できるものとして設定。
・real_ip_header
real_ip_header X-Forwarded-For;ELB等のIPではなく、接続元(クライアント)のIPを $_SERVER ['REMOTE_ADDR']で取得できるようにする。
・client_max_body_size
client_max_body_size 100M;ファイルアップロードの際の最大サイズ(デフォルト:1m)
・client_body_buffer_size
client_body_buffer_size 50M;クライアントから受け取ったリクエストボディをどのくらいメモリにバッファリングするかの設定。
無しにするか、エラーが出ない&メモリを食わない程度の値に設定。・proxy_request_buffering
proxy_request_buffering on;onにすると、リクエストをプロキシされたサーバーに送信する前に、リクエスト本体全体がクライアントから読み取られる。
・location
location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } location ~ .*\.(jpg|JPG|gif|GIF|png|PNG|swf|SWF|css|CSS|js|JS|inc|INC|ico|ICO)$ { access_log off; }ファビコンや静的ファイルなどの不要なログは出力しないように設定。
location ~ /\. { deny all; }「.(ピリオド)」から始まるファイルへのアクセスは禁止するように設定。
location / { try_files $uri $uri/ /index.php$is_args$args; }指定されたpathファイルを実行するように設定。
・その他
if ($request_method !~ ^(GET|HEAD|POST|OPTIONS)$ ){ return 444; }HTTPメソッドの制限(GET|HEAD|POST|OPTIONSのみ許容している)。
if (!-d $request_filename) { rewrite ^/(.+)/$ /$1 permanent; }トレイリングスラッシュを取り除いてリダイレクトする設定。
server { listen 80 default_server; server_name _; return 404; }想定外のホストでアクセスがあった場合の処理を設定。
これがないと、設定ファイルの先頭に記述してあるホストに振り分けられるので注意。今後の課題
今回あまり深く触れなかった、proxy_bufferやworker_connectionsなどの設定値に関しては、
まだ理解が浅いため、適切といえる値は設定することができなかった。
もし、この辺の知見を上げるのに最適な参考資料など、ご存じの方がいれば教えていただけると幸いです