20200801のPHPに関する記事は10件です。

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でテストファイヤーフォックスと入力し、送信ボタンをクリックします

動きました

Chromeの画面
a.png

Firefoxの画面
b.png

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

【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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP + Google Drive API】アプリケーションから自動で共有ドライブにアクセスしてファイル操作する

実現すること

アプリケーション側からGoogleDriveの共有ドライブへ自動でファイルアップロードや検索を行いたい:nerd:

なお、共有ドライブを外部に公開する必要があるため、所属する組織の運営方針等を確認してから行いましょう。
わたしは外部公開しないで直接共有ドライブへファイル操作を行う方法は見つけられませんでした。

GoogleDriveを操作する準備

やることは3つです。
1. GCPでサービスアカウントを作成
2. GoogleDrive側の設定
3. 共有ドライブにアクセスするためのパラメーター付与(アプリケーション側)

GCPでサービスアカウントを作成

GCPへアクセスします。
共有ドライブを所有する当該組織とプロジェクトを作成または選択します。
ナビゲーションメニューより「APIとサービス」からGoogleDriveAPIの有効化しておきます。
サイドメニューより「IAMと管理」よりサービスアカウントを作成します。
その後、作成したサービスカウントに対して、JSONのcredental keyも作成しておきます。

GoogleDrive側の設定

共有ドライブを許可された組織外のメンバーへの共有設定

:computer: Google Adminより
[アプリ] 次に [G Suite] 次に [ドライブとドキュメント] にアクセスします。
その後、[組織名] 以外のユーザーからのファイル受信を [組織名] のユーザーに許可するを選択します。
つまりアクセス権限をゆるくします。

:computer: 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();
    }

supportsTeamDrivessupportsAllDrivesに該当するパラメーターになります。
ほかのパラメーターは検索順位をどうするか?といった設定になり、こちらも必須になります。たぶん実行結果が変わるんじゃないでしょうか:nerd:
マイドライブにアクセスするときとは違い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はアップロードするファイルが巨大な場合必要になります。

以上、どなたかの助けになりましたら幸いです。:nerd:

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

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.php
hello world

http://localhost:3000 にアクセスしてhello worldを確認

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

【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配下のエラーログで特に何も出てこなくなれば大丈夫だと思います。

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

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; と定義したときの流れとしては、

  1. 定数trueが、bool(true) に展開される
  2. $aに、bool(true) が格納される

と考えるとよさげです。

補足

NULLも同様でした。同様の方法で確認できます。

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

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)

参考書籍

プログラミングPHP 第3版

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

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つの方法があります。

  1. routes/api.phpに記載
  2. ハンドラメソッドのコンストラクタに記載

ここでは、「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で定義し、Middlewareconnect.writeの指定を行います。

Route::middleware(['connect.write'])->group(function () {
    Route::post('/user', '/Api/User/StoreAction');
    Route::put('/user/{user}', '/Api/User/UpdateAction');
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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を開いて新規リポジトリ作成

  1. Newボタンを押して新規作成
  2. リポジトリをPrivateに
  3. gitignoreにLaravelを追加
  4. create Repositoryを押して
  5. add readme.mdをクリックして完了
  6. リポジトリの画面の右側のCodeボタンをクリックして
  7. 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に文字列を登録

  1. 右上のアイコンを押してアカウントメニューを表示
  2. Settings ⇒ SSH and GPG keys
  3. New SSH keyをクリック
  4. Titleは適当なものをつける
  5. Keyと書いてあるところに上記(cat ~/.ssh/id_rsa.pubコマンド)で表示されたキーを張り付け
  6. 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導入していじるみたいです。

今まで色々やってきたことの解像度が大分上がってきて楽しくなってきたぞ~(夜中のテンション)

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

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の情報もちゃんと切り出す
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む