- 投稿日:2020-08-01T18:20:56+09:00
PHPでWebSocket
PHPでWebSocketやっていきます
プロジェクト作成
Composerプロジェクトをつくります
Composerをインストールしていない方はインストールしてください
Composerをインストールする適当なフォルダをつくって
コマンドラインでcdして行きます
composer init
これでcomposer.jsonできます
今回はRatchetを使っていきます
Ratchet (http://socketo.me/)
composer require cboden/ratchet
これでRatchetが使えるようになりますsrcフォルダをつくります
composer.jsonのautoload修正します
composer.jsonは下記にしますcomposer.json{ "autoload" : { "psr-4" : { "App\\" : "src" } }, "require": { "cboden/ratchet": "^0.4.3" } }コマンドラインで
composer dump-autoload
これで修正したautoloadが反映されますフォルダ構成は下記になります
`-- sample |-- src |-- vendor `-- composer.jsonファイル作成
(1) srcフォルダ配下にChat.php作成
Chat.php<?php namespace App; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; class Chat implements MessageComponentInterface { protected $clients; public function __construct() { $this->clients = new \SplObjectStorage; } public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); } public function onMessage(ConnectionInterface $from, $msg) { foreach ($this->clients as $client) { $data = ['msg' => $msg]; if ($from === $client) { $data['position'] = 'right'; } else { $data['position'] = 'left'; } $client->send(json_encode($data)); } } public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); } public function onError(ConnectionInterface $conn, \Exception $e) { $conn->close(); } }webSocket接続してきたら自動的にonOpenが実行されます。今回は
$this->clients
にコネクションを追加しています
クライアントがデータをsendしてきたら自動的にonMessageが実行されます。今回はjsonを返すようにしています(2) srcフォルダ配下にwsServer.php作成
wsServer.php<?php namespace App; use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; use App\Chat; require dirname(__DIR__) . '\vendor\autoload.php'; $server = IoServer::factory( new HttpServer( new WsServer( new Chat() ) ), 8282 ); $server->run();webSocketのエントリポイントになります
8282は今回WebSocketで使うポートです。firewallで許可しているポートにしてください
先ほど作成したChatクラスを与えてWebSocketサーバーをrunします(3) HTTPサーバーの公開フォルダにws.html作成
ws.html<html> <head> <title>sample</title> <style type="text/css"> .container { width: 100%; height: 100%; box-sizing: border-box; } .msg-log { width: 100%; height: 92%; vertical-align:top; box-sizing: border-box; padding: 0px; border: black solid 1px; overflow-y: scroll; } .input-area { width: 100%; box-sizing: border-box; } .msg { width: 90%; height: 8%; vertical-align:top; box-sizing: border-box; padding: 0px; float: left; } .btn { width: 10%; height: 8%; vertical-align:top; box-sizing: border-box; padding: 0px; } .receive-msg-left { border-radius: 10px; border: black solid 1px; padding: 10px; margin: 10px; display: inline-block; float: left; } .receive-msg-right { border-radius: 10px; border: black solid 1px; padding: 10px; margin: 10px; display: inline-block; float: right; background-color: #00FF00; } .br { clear: both; } </style> <script type="text/javascript"> var conn = ""; function open(){ conn = new WebSocket('ws://localhost:8282'); conn.onopen = function(e) { }; conn.onerror = function(e) { alert("エラーが発生しました"); }; conn.onmessage = function(e) { var data = JSON.parse(e.data); var divObj = document.createElement("DIV"); if (data["position"] == "left") { divObj.className = 'receive-msg-left'; } else { divObj.className = 'receive-msg-right'; } var msgSplit = data["msg"].split('\n'); for (var i in msgSplit) { var msg = document.createTextNode(msgSplit[i]); var rowObj = document.createElement("DIV"); rowObj.appendChild(msg); divObj.appendChild(rowObj); } var msgLog = document.getElementById("msg_log"); msgLog.appendChild(divObj); var br = document.createElement("BR"); br.className = 'br'; msgLog.appendChild(br); msgLog.scrollTop = msgLog.scrollHeight; }; conn.onclose = function() { alert("切断しました"); setTimeout(open, 5000); }; } function send(){ conn.send(document.getElementById("msg").value); } function close(){ conn.close(); } open(); </script> </head> <body> <div class="container"> <div id="msg_log" class="msg-log"></div> <div class="input-area"> <textarea id="msg" class="msg"></textarea> <button class="btn" onclick="send();" >送信</button> </div> </div> </body> </html>
conn = new WebSocket('ws://localhost:8282');
で接続してます。8282は先ほどwsServer.phpに書いたポートです
conn.send(document.getElementById("msg").value);
でwebSocketサーバーにデータ送信しています
conn.onmessage
はwebSocketサーバーからsendがあった場合に自動的に実行されます動作確認
コマンドラインで
srcフォルダにcdして
php wsServer.php
これでWebSocketサーバーが起動しましたHTTPサーバを起動し
HTTPサーバーの公開フォルダにつくったws.htmlにブラウザでアクセスしてみましょう
http://localhost/ws.html
ChromeとFirefoxでアクセスしてみましょうChromeでテストクロームと入力し、送信ボタンをクリックします
Firefoxでテストファイヤーフォックスと入力し、送信ボタンをクリックします動きました
- 投稿日:2020-08-01T17:20:22+09:00
【Laravel】Artisanコマンド基礎
Artisanコマンドとは
- PHPのフレームワークであるLaravel専用のコマンド
- Artisanコマンド使用することでモデルやコントローラを簡単に作成することが出来る
アプリケーション作成
ターミナルlaravel new アプリケーション名
サーバー立ち上げ
ターミナルphp artisan serve
コントローラ作成
ターミナルphp artisan make:controller コントローラ名
サービスプロバイダー作成
ターミナルphp artisan make:provider サービスプロバイダー名
ミドルアウェア作成
ターミナルphp artisan make:middleware ミドルウェア名
フォームリクエスト作成
ターミナルphp artisan make:request フォームリクエスト名
マイグレーションファイル作成
ターミナルphp artisan make:migration create_テーブル名_table
マイグレーションファイルの実行状況確認
;ターミナルphp artisan migrate:status
マイグレーションを実行
ターミナルphp artisan migrate
マイグレーションファイルのロールバック
ターミナルphp artisan migrate:rollback
シーダーファイルの作成
ターミナルphp artisan make:seeder シーダー名
シーダーを実行する
ターミナルphp artisan db:seed
- 投稿日:2020-08-01T15:47:09+09:00
【PHP + Google Drive API】アプリケーションから自動で共有ドライブにアクセスしてファイル操作する
実現すること
アプリケーション側からGoogleDriveの共有ドライブへ自動でファイルアップロードや検索を行いたい
なお、共有ドライブを外部に公開する必要があるため、所属する組織の運営方針等を確認してから行いましょう。
わたしは外部公開しないで直接共有ドライブへファイル操作を行う方法は見つけられませんでした。GoogleDriveを操作する準備
やることは3つです。
1. GCPでサービスアカウントを作成
2. GoogleDrive側の設定
3. 共有ドライブにアクセスするためのパラメーター付与(アプリケーション側)GCPでサービスアカウントを作成
GCPへアクセスします。
共有ドライブを所有する当該組織とプロジェクトを作成または選択します。
ナビゲーションメニューより「APIとサービス」からGoogleDriveAPIの有効化しておきます。
サイドメニューより「IAMと管理」よりサービスアカウントを作成します。
その後、作成したサービスカウントに対して、JSONのcredental keyも作成しておきます。GoogleDrive側の設定
共有ドライブを許可された組織外のメンバーへの共有設定
Google Adminより
[アプリ] 次に [G Suite] 次に [ドライブとドキュメント] にアクセスします。
その後、[組織名] 以外のユーザーからのファイル受信を [組織名] のユーザーに許可するを選択します。
つまりアクセス権限をゆるくします。GoogleDriveより
操作したい共有ドライブへアクセスします。
その後、共有ドライブ名▼
より共有ドライブの設定
を選択します。
ここからメンバー外ともファイルを共有できるように設定しておきます。
そして、先程作成したサービスアカウントのメールアドレスを共有ドライブのメンバーとして登録します。共有ドライブにアクセスするためのパラメーター付与
環境はPHP 7.1.*, google/apiclient 2.0です。
GoogleDriveを操作するための準備
class GoogleApiDrive { private $service; public function __construct($credentials, $allow_scope_list) { $google_client = new Google_Client(); $google_client->useApplicationDefaultCredentials(); $google_client->setAuthConfig($credentials); $google_client->addScope($allow_scope_list); $this->service = new Google_Service_Drive($google_client); } }Google Drive を操作するためのインスタンスを生成します。
このあたりは他の記事でもよく紹介されていますね。共有ドライブへリクエストするときの専用パラメーターが存在する
Implement shared drive support
公式ドキュメントによるとsupportsAllDrives=true
を付与することで共有ドライブへアクセスを前提としたアプリケーションであることをGoogleDrive側へ通知できるようです。
このパラメーターをApiClientの各メソッドのリクエスト時に付与してあげれば共有ドライブを操作できるようになるわけです。共有ドライブへファイル検索メソッドの実装
public function getDriveFilesByFolderIdFromSharedDrive(string $drive_id, string $folder_id) { $query = "'" . $folder_id . "' in parents"; $result = $this->service->files->listFiles([ "corpora" => "drive", "includeTeamDriveItems" => true, "supportsTeamDrives" => true, "teamDriveId" => $drive_id, "q" => $query, ]); return $result->getFiles(); }
supportsTeamDrives
がsupportsAllDrives
に該当するパラメーターになります。
ほかのパラメーターは検索順位をどうするか?といった設定になり、こちらも必須になります。たぶん実行結果が変わるんじゃないでしょうか
マイドライブにアクセスするときとは違いteamDriveId
が必須になります。
なぜならフォルダーに対してのメンバーになったわけではなく、共有ドライブのメンバーであり、そのメンバーとなったドライブのIDを特定してあげる必要があるからです。共有ドライブへフォルダ・ファイル作成メソッドの実装
フォルダ作成は以下のようなメソッドになります。
$file_metadata = new Google_Service_Drive_DriveFile([ 'name' => 'folder name', 'mimeType' => 'application/vnd.google-apps.folder', 'parents' => [$folder_id], ]); $options = [ 'fields' => 'id', 'supportsTeamDrives' => true, ]; public function createFile(Google_Service_Drive_DriveFile $file_metadata, array $options) { $file = $this->service->files->create($file_metadata, $options); return $file; }
$options
の方に検索でお馴染みのsupportsTeamDrives
をセットします。つぎはファイル作成、CSVファイルを作成する想定のメソッドになります。
$file_metadata_csv = new Google_Service_Drive_DriveFile([ 'name' => $csv_name . '.csv', 'parents' => [$folder_id], ]); $options = [ 'data' => $content, 'mimeType' => 'text/csv', 'uploadType' => 'resumable', 'fields' => 'id', 'supportsTeamDrives' => true, ]; public function createFile(Google_Service_Drive_DriveFile $file_metadata, array $options) { $file = $this->service->files->create($file_metadata, $options); return $file; }ほぼ同じです。
mimeType
をいじることでアップロードするファイルの種別を変更していきます。
resumable
はアップロードするファイルが巨大な場合必要になります。以上、どなたかの助けになりましたら幸いです。
- 投稿日:2020-08-01T13:45:12+09:00
PHPの超雑な開発環境を作る
$ cd ~/sample_app $ php -S localhost:3000 Listening on http://localhost:3000 Document root is /Users/your_name/sample_app Press Ctrl-C to quit.sample_app/index.phphello worldhttp://localhost:3000 にアクセスしてhello worldを確認
- 投稿日:2020-08-01T10:13:20+09:00
【PHP】Trying to get property 'methods' of non-object in 〜 のエラー解決方法
はじめに
エラーログで
Trying to get property 'methods' of non-object in 〜
って出てきたことはありませんか?
存在しない変数のプロパティやnullの値を参照した時にこのエラー発生するもので、私たまに出てくるんですが大概の場合はすぐ解決できます。
今回は実務でこのエラーが出てきて(配属されてすぐなので人のコードの修正でした...)その修正でした。issetを用いてエラーを解決
Trying to get property 'methods' of non-object in 〜
のエラーなのですが、存在しない変数のプロパティやnullの値を参照した時にこのエラー発生するの、issetやisnullなどの関数を使うとチェック出来ます。
今回はissetで解決したのですが、前の部分で実装していたforeachループ
が効いていて後続の処理でエラーが起きていました。
なので今回の対象の部分にisset($hoge)と実装すると解決します。
おわりに
ちゃんと実装できているかの確認は画面の動作確認と、
storage/logs
配下のエラーログで特に何も出てこなくなれば大丈夫だと思います。
- 投稿日:2020-08-01T09:55:33+09:00
PHPのBoolean型で利用する、true, falseという単語は「定数」
TRUE, FALSEとはなんなのか
Boolean型の変数において格納する true, false というキーワード。
TRUE, True, true のように様々に書くことができます。私は true, false 派です。
馴染み深いものですが、よくよく考えるとこれは何なんでしょうか。boolean リテラルを指定するには、定数 TRUE または FALSE を指定してください。 両方とも大文字小文字に依存しません。
https://www.php.net/manual/ja/language.types.boolean.phpというわけで、定数でした。
大文字小文字を気にせず使えるのは、定数だから
true, false は大文字小文字を気にせず使えます。
$flag = TRUE; $flag = True; $flag = true;これは、
define()
で定義された定数についてPHPが大文字小文字を区別しないことから、このような挙動になっていると推測されます。
参考:Q. PHPは大文字小文字を区別するか?#定数定義を確認する
ドキュメントに記載があります。
TRUE (boolean)
Booleans も参照ください。
FALSE (boolean)
Booleans も参照ください。
https://www.php.net/manual/ja/reserved.constants.php「定義済みの定数」としてドキュメントに記載されていることが確認できました。
実際に、
get_defined_constants()
という関数で確認してみます。$ php -r 'var_dump(get_defined_constants());' ... (省略) ... 'TRUE' => bool(true) 'FALSE' => bool(false) ... (省略) ...定義されてるのが確認できました!!
true, falseは真偽値そのもの (Booleanリテラル) ではない
というわけで、
TRUE, True, true 定数 FALSE, False, false 定数 bool(true) 真偽値 (Booleanリテラル) bool(false) 真偽値 (Booleanリテラル) という感じになります。true, false の定数はあくまで定数であり、真偽値そのものではありません。
例えば、
$a = true;
と定義したときの流れとしては、
- 定数
true
が、bool(true)
に展開される$a
に、bool(true)
が格納されると考えるとよさげです。
補足
NULLも同様でした。同様の方法で確認できます。
- 投稿日:2020-08-01T05:19:55+09:00
PHPの言語仕様 (命令文とセミコロン)
環境
$ php7.2 --version PHP 7.2.24-0ubuntu0.18.04.6 (cli)記事中のコードについては上記環境で確認を行っています
命令文の区切り文字としてセミコロン (;) を使用する
動作する
<?php $lang = 'PHP';動作しない
<?php $lang = 'PHP'PHP Parse error: syntax error, unexpected end of file in ~/aaa.php on line 3
閉じ括弧後のセミコロンは省略できる
動作する
<?php if (true) { echo 'Hello, PHP!'; };動作する
<?php if (true) { echo 'Hello, PHP!'; }省略した記法のほうが一般的である。
PHP終了タグの直前のセミコロンは省略できる
動作する
<?php echo 'PHP' ?>動作するが、統一性を持たせるため基本的に記載したほうがよい。
省略可能な箇所であっても、セミコロンをつけておくことをお勧めします。あとからコードを読み直す際に、そのほうがわかりやすくなります。 (参考書籍 p.18)
参考書籍
- 投稿日:2020-08-01T04:04:06+09:00
LaravelでAPI単位で接続先DBを切り替える
TL; DR
Laravelでは、接続先のDBのホストのread/write接続が自動的に設定されすが、OracleDatabaseへの接続をyajra/OCI8を用いると、このread/write接続が行えません。
そのため、Middlewareにて、エンドポイント単位で接続先のDBを切り替えます。
環境
- Windows 10
- PHP 7.2
- Laravel 5.5
- Oci8 5
- Oracle database
Middleware作成
今回は、read接続先指定用とwrite接続先指定用のMiddlewareを作成します。
作成手順は、以下の要領です。/var/www/html# php artisan make:middleware ConnectWriteDatabase Middleware created successfully. /var/www/html# php artisan make:middleware ConnectReadDatabase Middleware created successfully.これで、以下のMiddlewareが作成されました。
ConnectWriteDatabase.php
<?php namespace App\Http\Middleware; use Closure; class ConnectWriteDatabase { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { return $next($request); } }ConnectReadDatabase.php
<?php namespace App\Http\Middleware; use Closure; class ConnectReadDatabase { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { return $next($request); } }Middlewareの登録
作成したMidllewareはそのままでは使用できません。そのため、使用できるようにするためには、Karnel.phpへの登録が必要です。通常ですとMiddlewareを全HTTPリクエストに適用するにはGlobal MiddlewareとしてKarnerl.phpへの登録を行う必要があります。
ですが、今回は特定のHTTPリクエストに対してMiddlewareを適用するため、Kernelの
$routeMiddleware
への登録を行います。App\Http\Kernel.php
<?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { /** 省略 */ /** * The application's route middleware. * * These middleware may be assigned to groups or used individually. * * @var array */ protected $routeMiddleware = [ // ここにMidlewareを追加 'connect.write' => \App\Http\Middleware\ConnectWriteDatabase::class, ]; }Middlewareのエンドポイントへの適用
指定したエンドポイントに作成したMiddlewareを適用するには、大きく2つの方法があります。
- routes/api.phpに記載
- ハンドラメソッドのコンストラクタに記載
ここでは、「1.routes/api.phpに記載」の方法をとります。
Laravelでは、Routingの設定では、routesディレクトリ直下のルートファイルで定義をします。
このとき、RouteGroup
を使用することで、複数のRoutingの定義対象に対して、適用するMiddlewareを指定することができます。例:公式ドキュメントRoutingから抜粋
Route::middleware(['first', 'second'])->group(function () { Route::get('/', function () { // firstとsecondミドルウェアを使用 }); Route::get('user/profile', function () { // firstとsecondミドルウェアを使用 }); });この要領で、write接続を行うエンドポイントを定義するRouteメソッドをGroupで定義し、Middleware
connect.write
の指定を行います。例
Route::middleware(['connect.write'])->group(function () { Route::post('/user', '/Api/User/StoreAction'); Route::put('/user/{user}', '/Api/User/UpdateAction'); });
- 投稿日:2020-08-01T03:17:15+09:00
yps並走記録 Task3 SQL:テーブル作成(復習)バッチ作成(復習)~php.ini設定~GitHubにファイルをアップロード~WordPress5.4.2セットアップ
早くも3週目になりました、yps
今回はなるべくリアルタイムで課題をやりながらログを取っていきたいと思います。まずはSQL周りの復習
MySQLのエンコード設定を直します
エンコード設定をutf8mb4に戻す(mysql cliから日本語扱えるようにutf8にしてた)
sudo vi /etc/my.cnf
最終行に以下を追記
[client] default-character-set=utf8mb4編集を保存し、mysqlを再起動
sudo systemctl restart mysql
練習で使うデータをダウンロード
cd /tmp
sudo yum install wget
wget http://tech.pjin.jp/wp-content/uploads/2016/04/worldcup2014.zip
unzip http://worldcup2014.zip
データの確認
ls -la worldcup2014.sql
データベース作成
MySQLにログインして…
mysql -u root -p (パスワードを入力)
データベースを作成
create database worldcup2014db;
使うデータベースにDLしたデータを指定
use worldcup2014db;
source ./worldcup2014.sql;
テーブルを表示して確認
show tables;
確認用バッチ処理とコマンドを作成していきます
参考:バッチ処理とは?
Laravelでモデルを作成(参考:https://blog.codecamp.jp/php_mvc01)
php artisan make:model Models/Player
作成したモデルを確認
ls -la app/Models/Player.php
バッチ記述用のファイルを作成
php artisan make:command TestCommand
作成したファイルを確認
ls -la app/Console/Commands/TestCommand.php
ここからは
1. SSH接続したVS Codeを使って
2. /var/www/html/ypsフォルダ(Laravelのプロジェクトファイル)
で作業していきますapp/console/commandsのCommands.phpファイルに以下を記述
Commands.php<?php namespace App\Console\Commands; use Illuminate\Console\Command; use App\Models\Player; class TestCommand extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'test_command'; /** * The console command description. * * @var string */ protected $description = 'Test Command'; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return int */ public function handle() { $players = Player::get(); foreach($players as $player) { echo $player->name."\n"; } return 0; } }保存したらターミナルで以下のコマンドを打って確認
キャッシュをクリア
php artisan config:clear
上記で作成したバッチコマンドを入力
php artisan test_command | head
無事選手名が表示されればOK
MySQL日本語が打てない問題の一先ずの対応 = sqlファイルを作成して読み込ませる
どうしてもコマンドを使って行う場合の対応。
≒基本的にLaravelを使ってSQLは操作するので特に問題はなさそう?一時ファイルに移動
cd /var/tmp
sqlファイルを作成
vi get_players.sql
以下を記述
use worldcup2014db; select * from players where name = '酒井';ターミナルで下記を打ってパスワードを入力
mysql -u root -p < ./get_players.sql
酒井選手が2桁表示できればOK
(ファイルに出力したい場合はmysql -u root -p < ./get_players.sql > ./out.txt
)~MySQLの復習&バッチ作成ここまで~
php.iniファイルを編集
主にPHPをアプリケーションで使用する際に日本語で文字化けしたりしないような設定をしていくようです。
※事前に
sudo yum install colordiff -y
で差分を見られるようにしておく参考リンクを見ながらphp.iniの設定を編集
sudo vi /etc/php.ini
まずはデフォルトのエンコーディングがUTF-8に…
default_charset = "UTF-8"
しかしこれ、あとで調べたら5.2以降のPHPはデフォルトでUTF-8になってるみたい…ということで設定要らんかったのかorz
気を取り直して、mbstringの探しつつ確認をしていきます。
[mbstring]の設定は一番下の方にありました…
mbstring.language = Japanese
//コメントアウトを外す
mbstring.encoding_translation = Off
//コメントアウトを外してoffにする
mbstring.detect_order = auto
//コメントアウト外す※参考リンクが古いためか、ブログ記事では手動で設定するような記述になっているがautoのままでいいみたいです
(多分PHPのバージョンの関係)
date.timezone = Asia/Tokyo
PHPのバージョンが表示されてしまわないように変更
expose_php = Off
パフォーマンス関連の設定
memory_limit = 128M
これはデフォルトの設定なので確認だけポストリクエストのマックスサイズを128Mに変更
post_max_size = 128M
ファイルをアップロードする際のマックスサイズをPOSTに合わせて128Mに変更
upload_max_filesize = 128M
ついでにエラーログの設定もしておきましょう
error_log= "/var/log/php_errors.log"
全部できてれば…
colordiff -u /etc/php.ini.org /etc/php.ini
って打てば--- /etc/php.ini.org 2020-07-07 18:04:58.000000000 +0900 +++ /etc/php.ini 2020-07-31 23:28:50.392713681 +0900 @@ -373,7 +373,7 @@ ; threat in any way, but it makes it possible to determine whether you use PHP ; on your server or not. ; http://php.net/expose-php -expose_php = On +expose_php = Off ;;;;;;;;;;;;;;;;;;; --- /etc/php.ini.org 2020-07-07 18:04:58.000000000 +0900 +++ /etc/php.ini 2020-08-01 01:02:55.357913512 +0900 @@ -373,7 +373,7 @@ ; threat in any way, but it makes it possible to determine whether you use PHP ; on your server or not. ; http://php.net/expose-php -expose_php = On +expose_php = Off ;;;;;;;;;;;;;;;;;;; ; Resource Limits ; @@ -584,6 +584,7 @@ ; http://php.net/error-log ; Example: ;error_log = php_errors.log +error_log = "/var/log/php_errors.log" ; Log errors to syslog (Event Log on Windows). ;error_log = syslog @@ -690,7 +691,7 @@ ; Its value may be 0 to disable the limit. It is ignored if POST data reading ; is disabled through enable_post_data_reading. ; http://php.net/post-max-size -post_max_size = 8M +post_max_size = 128M ; Automatically add files before PHP document. ; http://php.net/auto-prepend-file @@ -842,7 +843,7 @@ ; Maximum allowed size for uploaded files. ; http://php.net/upload-max-filesize -upload_max_filesize = 2M +upload_max_filesize = 128M ; Maximum number of files that can be uploaded via a single request max_file_uploads = 20 @@ -919,7 +920,7 @@ [Date] ; Defines the default timezone used by the date functions ; http://php.net/date.timezone -;date.timezone = +date.timezone = Asia/Tokyo ; http://php.net/date.default-latitude ;date.default_latitude = 31.7667 @@ -1533,7 +1534,7 @@ ; language for internal character representation. ; This affects mb_send_mail() and mbstring.detect_order. ; http://php.net/mbstring.language -;mbstring.language = Japanese +mbstring.language = Japanese ; Use of this INI entry is deprecated, use global internal_encoding instead. ; internal/script encoding. @@ -1571,7 +1572,7 @@ ; automatic encoding detection order. ; "auto" detect order is changed according to mbstring.language ; http://php.net/mbstring.detect-order -;mbstring.detect_order = auto +mbstring.detect_order = auto ; substitute_character used when character cannot be converted ; one from anotherってなるはず(らしい)
そしてコンソールに戻ってphpのエラーログファイル作成&設定を行います
sudo touch /var/log/php_errors.log
sudo chown nginx:nginx /var/log/php_errors.log
ここまでできたら設定反映のためにphp-fpmとnginxを再起動
sudo systemctl restart php-fpm
sudo systemctl restart nginx
~php.iniの設定ここまで~
Gitインスコ&GitHubとの連携
参考までに予備知識:https://qiita.com/moonbass630/items/383fc8300a83784e4c82
まずはgitをインストール
sudo yum install git -y
ディレクトリをLaravelのプロジェクトに移動して…
cd /var/www/html/yps
gitをinitします(意味が分からない方のための参考リンク:https://26gram.com/git-init)
git init
ブラウザでGitHubを開いて新規リポジトリ作成
- Newボタンを押して新規作成
- リポジトリをPrivateに
- gitignoreにLaravelを追加
- create Repositoryを押して
- add readme.mdをクリックして完了
- リポジトリの画面の右側のCodeボタンをクリックして
- Clone with SSHと書かれたところに書いてあるgit@github.comから始まるリポジトリのurlをコピー
※GitHubにはすぐ戻ってくるのでブラウザは開いたままが推奨
Gitの設定ファイルを記述
ターミナルでまたLaravelのプロジェクトファイルへ戻ります
cd /var/www/html/yps/
Gitの設定ファイルを編集
sudo vi .git/config
下記を記述します
[remote "origin"] url = "リポジトリのurl" //作成時にコピーしたもの fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master [user] name = 自分のgithubユーザー名 email = 自分のメアド [core] repositoryformatversion = 0 filemode = true bare = false接続用の秘密鍵を作成
ssh-keygen -t rsa -b 4096 -C "自分のメアド"
enterを押して
パスワードを2回入力下記コマンドを打って表示される文字列をコピーしておく
cat ~/.ssh/id_rsa.pub
GitHubに文字列を登録
- 右上のアイコンを押してアカウントメニューを表示
- Settings ⇒ SSH and GPG keys
- New SSH keyをクリック
- Titleは適当なものをつける
- Keyと書いてあるところに上記(
cat ~/.ssh/id_rsa.pub
コマンド)で表示されたキーを張り付け- Add SSH keyを押すと登録完了
GitHubをリモート登録
ターミナルでLaravelプロジェクトフォルダに戻ります
cd /var/www/html/yps/
差分ファイルを全てステージングして、マスターブランチにpush
git add .
git commit -am "initial"
git push origin master
yesを入力してパスワードを入力すればソースコードの登録完了。develop / feature ブランチを切る
参考リンク:https://qiita.com/Naoki206/items/e5520453f92dcd4274f1
参考リンクを見ながらdevelopブランチとfeatureブランチを切る
$git branch
現在のブランチの確認
$git branch develop
developブランチ作成
$git checkout develop
developブランチへ移動
$git push origin develop
リモートに反映~git導入&GitHub連携ここまで~
Masterから分岐させて開発終了後にmergeを使ってmasterブランチへ取り込むことによってgitではバージョン管理をする…
Gitは一応Progateでいじって、Qiitaでも初心者向けの記事読んだりしてたけど、今回色々いじってようやく仕組みが腑に落ちた感あるな~今回はまとめ作ってたらすんごい時間になってしまったのでこの辺で…
次回はWP導入していじるみたいです。今まで色々やってきたことの解像度が大分上がってきて楽しくなってきたぞ~(夜中のテンション)
- 投稿日:2020-08-01T02:35:00+09:00
PHPで書いたレガシーコードをテスト可能にする
はじめに
久々にPHPの酷いコードにテストを足したのでまとめるよ。
患者さん事例
PHPって地の文ベタ書きで全然かけてしまう。
なので雑に書いたコードが適当なフォルダに突っ込まれていて、それをApatchが直接参照していたりするわけだ。
拡張されまくったり炎上したりで結果的にとんでもなく酷いコードが出来上がる。
そう、こんな感じに。index.php<?php require_once "External.php"; // global変数定義 // おもむろに追加された認証ロジック if ($_SERVER['REQUEST_METHOD'] === 'GET') { // 業務ロジック echo "<html>"; if (/* なんかややこしい判定 */) { echo "へんなとこでボディを返したり"; if (/* if文の深淵で */){ exit; } // なんてされてたら目も当てられない } else{ header("クレイジーなところでヘッダを返していたりする"); } echo "</html>"; } else { // 業務ロジック echo "<html>どうのこうの</html>"; }テストコードを加えたいところだがこのままでは無理だ。
なので最低限の修正でテスト可能にしておこう。地の文いきなり走り出す問題の対処
最悪なことに地の文は読み込まれても実行されてしまう。だからまずはそれを封じる必要がある。
IndexTest.php<?php require_once "product code"; // ここでプロダクトコードが動いてしまう! class IndexTest extends TestCase { public function testPostWithNoParameter() { /* ここで実行したいのに! */ } }これはもうなんともできんのでプロダクトコードに変更を加える。
index.php<?php require_once "External.php"; // global変数定義 function run($globalVarA, $globalVarB) { // 元あった地の文 } if (http_response_code() !== false) { run($globalVarA, $globalVarB); }末尾のIF判定のおかげでUT実行時は
run
は実行されない。
(もっといいアイデアがあればよこしやがってください)注意したいのはグローバル変数で、それだけで死刑に相当するのだが今は粛清より修正だ。
こればっかりは埋まっているのを探し当てるしか無い。最もリスクのある部分。これで一旦画面ポチポチしなくてもUTから
run
をけるだけでテストできるようになった。echo/headerの追放
コイツラはapatch側に処理を返しているデータだ。
だからUTでこの値をチェックしたい。なのでrun
の返り値にしてやろうじゃないか。index.php<?php require_once "External.php"; // global変数定義 class HttpResponse { // なんかいい感じに定義 }; function run($globalVarA, $globalVarB) { $headers = []; $contents = []; // 元あった地の文 // header(xxx); => $headers[] .= xxx; // echo xxx; => $contents .= xxx; return new HttpResponse($headers, $contents); } if (http_response_code() !== false) { $httpResponse = run($globalVarA, $globalVarB); foreach($httpResponse->headers as $header) { header($header); } echo $httpResponse->body(); }サーバから渡ってくる値も変えたいな
サーバから渡ってくる色んな値を想定してテストしたいよね?
だから引数で渡せるようにしよう。index.php<?php require_once "External.php"; // global変数定義 class HttpResponse { // なんかいい感じに定義 }; function run( $globalVarA, $globalVarB, $requestMethod, $requestParameter, /* 他にあればどうぞ */ ) { $headers = []; $contents = []; // 元あった地の文 // header(xxx); ---> $headers[] .= xxx; // echo xxx; ---> $contents .= xxx; return new HttpResponse($headers, $contents); } if (http_response_code() !== false) { $httpResponse = run( $globalVarA, $globalVarB, $_SERVER['REQUEST_METHOD'], $_RESUEST, /* 他にあればどうぞ */ ); foreach($httpResponse->headers as $header) { header($header); } echo $httpResponse->body(); }まぁだいたいこんな感じです。
その他
- run関数をクラス化したかったらする(自分は関数でいいと思う)
- CookieやSessionの情報もちゃんと切り出す