20191128のPHPに関する記事は25件です。

[Laravel]シーディングができなかった原因

UserTableSeederとFoldersTableSeederの二つをシーディングしたかったのだがうまくいかない

UserTableSeeder
class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
      DB::table("users")->insert([
          "name" => "test",
          'email' => 'dummy@email.com',
          'password' => bcrypt('test1234'),
          'created_at' => Carbon::now(),
          'updated_at' => Carbon::now(),
      ]);   
    }
}
FoldersTableSeeder
class FoldersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {

        $user = DB::table("users")->first();    

        $titles = ["プライベート","仕事","旅行"];
            foreach ($titles as $title) {
                DB::table("folders")->insert([
                "title" => $title,
                "user_id" => $user->id,
                "created_at" => Carbon::now(),
                "updated_at" => Carbon::now(),
            ]);
        }
    }
}

シーディングしようとするとこのエラーメッセージが出る

>php artisan db:seed

In FoldersTableSeeder.php
Trying to get property 'id' of non-object

FoldersTableSeederの「存在しないidというプロパティを取得しようとしている」。
「"user_id" => $user->id」の部分「id」が存在しない?

これはそもそもfirstメソッドでuserテーブルから取得したものだった。
もしかしてまだuserテーブルのシーディングができていないためデータがないのでは?

そこでuserテーブルから個別にシーディングすると

>php artisan db:seed --class=UsersTableSeeder
Database seeding completed successfully.

>php artisan db:seed --class=FoldersTableSeeder
Database seeding completed successfully.

成功。
やはりそういうことだったらしい。
複数のシーディングを同時に行うとテーブル間の関係がある場合うまくいかないことがあるようだ。

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

ぼくがかんがえるさいきょうのミッション機能

Studioz Advent Calendar day2の記事となります。

もう師走かぁ。掃除しなきゃなぁ・・・
というわけで今年で個人的に大掃除だった「ミッション機能」について。

負荷の少ないミッションをどう作るか

概要

  • ソーシャルゲームを作る上で、どのタイトルでも企画化される「◯◯を3回クリアしよう!」的なミッション機能をどう設計するべきかを考える

ミッション機能のよくある要件

  • ミッションは「全期間」「デイリー」「ウィークリー」などの期限設定がある
  • 複数同時開催する
  • TOPページで「クリア済があればNEWマーク(もしくはクリア済数)のバッジを出したい」
  • 運用のったら後はマスタデータいれるだけで自動でシクヨロ
  • (妄想の果てに)必要なミッションパターンは100個とか

課題

  • NEWマークひとつ出すにしても「開催中のミッションのうちいずれか一つをクリアしているか?」が必要になる
  • クリア済数の場合は当然全て舐めた上で、クリア数を返してやる必要がある
  • ミッションはゲーム回遊促進の目的が強いことから、「所持カード数」「クエストのクリア回数」「ボスの撃破数」など幅広いデータの参照が必要になる事が多い
  • 「全期間」「デイリー」「ウィークリー」クリア判定対象のデータが増えるほど設計次第だが負荷になる

ようは下手に作るとバッジ一つ出すにしても処理が死ぬほど高負荷になる
企画は面白さ・あるべき論を追求するべきで負荷なんて考慮しないのが常なので、
いかに将来的にも低負荷なミッションを作れるかはサーバーエンジニアには腕の見せどころである
(とはいえミッション機能は、設計コストもサーバー負荷も、非常に高い企画である事は理解してもらいたい)

設計パターン

パターンA:TOPページでクリア判定をするケース

  • ◯)クリア判定がTOPページに集約される為、個別の機能側(クエストやバトルなど)ではクリア判定は不要
  • ✕)クリア判定が仕込まれたTOPページは広範囲のデータを取得、判断する必要がある(負荷が高い)

パターンB:機能側でクリア判定をし、TOPページは新着情報の有無のみチェックするケース

  • ◯)TOPページは新着情報の有無のみチェックする為、参照データが少なく負荷が低い
  • ✕)各機能側にクリア判定処理を実装する必要がある →コピペソースや、亜種の機能が多い場合影響範囲や保守範囲が広くなる

パターンC:非同期でなんとか(Batch処理でクリア判定するとか)

  • ◯)ゲームサーバー自体への負荷は少ない
  • ✕)反映がリアルタイムでない上、ユーザーの進捗とデッドロックする可能性も

よくある哀しみの事例

  • 「ユーザーさんに色々やらせたい」と並列で開催するミッションが増えてたケース

    • なんでクエストクリア◯回が30個も並列開催してんのーーー!!etc..
  • 機能追加で「◯◯機能の進捗もミッション追加しよう」と次から次へ新パターンが増えていくケース

    • 似たような処理の乱立や、肥大化し続けるミッションクリア判定のメソッド・・・
  • なんでもマスタで設定できるようにしたいを素直に受け「◯◯を✕✕の時に△△をクリアするミッション」が実装される

    • 条件設定の為マスタデータに増え続ける、そのミッションでしか使わない謎のカラム
    • しまいにはjsonでもたせるとかはじまり担当がいなくなって誰も使わないが負荷だけ残る
  • クリア対象となるデータ範囲が運用につれ拡張されていたケース(当然クリア判定数も応じて増える)

    • 所持カードは当初100枚だったが、気づいたら1000枚越え・・・
    • 対象ボスは気づいたら3体から100体に・・・
    • 過去タイトルでも頭の良さそうなコードで「これ当時は動いたんだろうけど今クエリ1000回流れてるケド・・・」という実装になっているケースは多々。
    • 対象データの将来の肥大化を意識しておかないと負荷になった頃には首が回らなくなる
  • 肥大化するソース

public static function getProgressData( $userData, $missionData ) {
  switch ( $missionData['checkType'] ) {
    case BOSS_DEFEAT:
        //ボスを撃破したかどうかの処理がN行
        break;
  case LV_UP:
        //レベルがあがっているかの処理がN行
        break;
  case CLEAR_STAGE:
        //ステージクリアしているかの処理がN行
        break;
  //延々と続くケース文。中身は仕様変更に合わせて細かい分岐条件が入れ子・・・
  }
}

必要要件の整理

  • ソースは肥大化させたくない
  • クリア判定処理は各所に仕込みたくない
  • TOPページは軽くしたい。でもクリア数は出したい
  • ミッションパターンの拡張がしやすい。でも使われなくなっても負荷は残したくない
  • 並列開催されても負荷は高くならないようにしたい

オワタ\(^o^)/

ソース肥大化問題への対策

Strategyパターンを利用する

  • ミッションは性質上パターンが存在するので、パターンごとにアルゴリズムを変えるストラテジパターンが適切。
  • ifelseやswitchが多様されるケースでは効果的な場合が多い
    private static function _getMissionPattern( $userId, EnMissionData $missionData ) {
        $model = null;
        switch ( $missionData->getType() ) {
            case MissionModel::TYPE_USER_LEVEL:
                $pattern = new MissionPatternUserLv($userId);
                break;
            case MissionModel::TYPE_USER_TOTAL_TITLE_SET:
                $pattern = new MissionPatternUserTitleSet($userId);
                break;
            case MissionModel::TYPE_USER_TOTAL_LEADER_MONSTER_SET:
                $pattern = new MissionPatternUserLeaderMonsterSet($userId);
                break;
        }
        return $pattern
    }

クリア判定だとか前提条件チェックはそれぞれのMissionPatternクラスに書く。
このメソッドは「渡されたミッションのタイプから必要なインスタンスを返す」だけが責務。
各MissionPatternは汎化されたabstract missionPatternを継承し共通のクリア判定などはそちらを利用、
必要な拡張条件のみオーバーライドなり差分コーディングなり。

クラス設計

image.png
責務をそれぞれに分散させて継承でつなぐ。
一つのメソッドやクラスで複数の責務を担わせている匂いを感じたら立ち止まって設計を考えること。

抽象ミッションパターンクラスに実装した処理
  • マスタデータのrowデータから対応する報酬データを返す
  • 表示をスキップするかどうか
  • ボーダー(必要クリア数など)を返す
  • 進捗を返す
  • クリアチェック処理
日次ミッションパターン実装
  • データのsave先をdailyのテーブルに向ける
    • userId + missionId + segmentId(YYYYMMDD)でunique idx
    • 毎日レコードを増やす設計にするとデータ肥大化面倒(ログはDBでなくs3とか外に出そう)
    • 更新はuserId + missionIdで、segmnetIdをupdateすることで当日を意識させる
    • 使い手には前日のデータを意識させないように注意
週次ミッションパターン実装
  • データのsave先をweeklyのテーブルに向ける
    • userId + mission_id + segmentIdで同上
    • segmentIdはYYYYMMDDでなく週次ミッションの起算日
    • 翌週のデータを意識させないように注意
全期間ミッションパターン実装
  • 特に意識することはなし
    • ミッションが増えてきたときにどうするかはまだ答えがない・・・

クリア判定処理は各所に仕込みたくない

結論、トリガーは各所に仕込むようにした。
ただし数行でよいように設計する

①クリア情報をミッションマネージャに渡す

    protected function execCommon() { //クエストの成否に限らず必ず呼ばれる処理

        //-----------------------------------------
        // ミッション更新
        //-----------------------------------------
        //判定に必要な情報をオブジェクト設定
        $missionDto = new MissionClearDto();
        $missionDto->questClearFlg = $this->reqClearFlg;
        $missionDto->stageId = (int)$this->stageData['world_stage_id'];

        //更新する
        UserMissionDataManager::getInstance($this->userId)->execClearCheckByAction(MissionModel::CLEAR_CHECK_CATEGORY_QUEST, $missionDto);
        UserMissionDataManager::getInstance($this->userId)->save();

        //-----------------------------------------
        //他のミッションのあれこれ
        //-----------------------------------------
    }

②開催中のものからタイプが一致するものだけ処理する

    public function execClearCheckByAction($checkType, MissionClearDto $clearDataObj = null ) { //共通クリア判定処理
        $res = 0;
        $openUserMissionObjList = $this->_openMissionCollection->getOpenMissionList();
        if ( !$openUserMissionObjList ) {
            return $res;
        }
        foreach ( $openUserMissionObjList as $missionObj ) {
            $tmpPattern = AMissionPatternModel::getInstance($this->_userId, $missionObj);
            if ( !$tmpPattern ) {
                $errorMsg = __METHOD__.__LINE__."[プログラムエラー]missionPatternが存在しない? missionId={$missionObj->getId()} mission_type={$missionObj->getType()}";
                continue;
            }
            $tmpCheckType   = $tmpPattern->getClearCheckType();
            if ( $checkType != $tmpCheckType ) {
                continue;
            }
            $this->execClearCheckById($missionObj, $clearDataObj);
        }
        return $res;
    }

③指定したIDのミッションをクリア判定する(タイプごとに挙動だけ変わる)

    public function execClearCheckById(EnMissionData $missionObj, MissionClearDto $clearDto = null) {
        $status = AMissionPatternModel::CLEAR_CHECK_STATUS_NG;
        if ( !$missionObj ) {
            return $status;
        }

        //missionTypeごとにクリア判定を行う
        $tmpPattern = AMissionPatternModel::getInstance($this->_userId, $missionObj);
        if (!$tmpPattern) {
            $errorMsg = __METHOD__.__LINE__."[プログラムエラー]missionPatternが存在しない? missionId={$missionObj->getId()} mission_type={$missionObj->getType()}";
            VenusLogger::log($errorMsg);
            return $status;
        }

        $status = $tmpPattern->execClearCheck($missionObj, $clearDto); //ここがmissionTypeごとに異なる振る舞いになる
        if ( $status == AMissionPatternModel::CLEAR_CHECK_STATUS_CLEAR ) {
            //外部からオンメモリをクリア済にする
            $tmpPattern->setClearFlg($missionObj);
            $tmpPattern->setResult($missionObj);
        }
        return $status;
    }
①だけ使い回せば、いろんなところにクリア判定を仕込める

ex1)特定のガチャを実行した時

    /**
     * ミッション処理
     * @return int
     */
    protected function execMissionCheck() {
        //判定に必要な情報をオブジェクト設定
        $missionDto = new MissionClearDto();
        $missionDto->gachaExecflg = ON_FLG;
        $missionDto->gachaThemeId = $this->themeId
        //更新する
        UserMissionDataManager::getInstance($this->userId)->execClearCheckByAction(MissionModel::CLEAR_CHECK_CATEGORY_GACHA, $missionDto);
        $res = UserMissionDataManager::getInstance($this->userId)->save();
        return $res;
    }

ex2)PVPで勝利した時

    protected function execMissionCheck() {

        if ( $this->roomData['room_type'] == AArenaRoomModel::ROOM_TYPE_TRAINING ) {
            return false;
        }

        //判定に必要な情報をオブジェクト設定
        $missionDto = new MissionClearDto();
        $missionDto->arenaWinFlg = $this->reqResult;

        //ミッションの更新
        UserMissionDataManager::getInstance($this->userId)->execClearCheckByAction(MissionModel::CLEAR_CHECK_CATEGORY_ARENA, $missionDto);
        UserMissionDataManager::getInstance($this->userId)->save();

        return true;
    }

TOPページは軽くしたい。でもクリア数は出したい

行動側に更新処理が仕込まれているので、基本的にはTOPページは「クリアしているか?」だけを意識すればよいので軽くなる。

クリア履歴があるものは判定をしないようにする

「クリア条件を満たしているか?」を毎回確認するから重くなるので、クリア履歴があるものは条件判定しないようにする

    /**
     * クリア判定処理
     *
     * この処理はlist取得時に呼ばれる為、ここにクエリでデータ取得を入れると負荷となる為、
     * 原則的にはuser_mission_result_tの結果参照のみ行い、
     * 各トリガーアクション側でどうしてもMAP遷移時などのクリアを実装する場合
     * サブクラスのexecClearCheck()をオーバーライドして使うこと
     *
     * @param EnMissionData $missionObj
     *
     * @return mixed
     */
    public function isClear( EnMissionData $missionObj ) {
        $isClear = false;
        if (empty($missionObj)) {
            return $isClear;
        }

        $userMissionResultManager = $this->_getResultDataManager();
        $userMissionResultObj = $userMissionResultManager->getResult($missionObj->getId());
        if ( $userMissionResultObj ) {
            $this->isClearList[$missionObj->getId()] = true;
            $isClear = true;
        }
        return $isClear;
    }

同種タイプで使い回せるものはstaticに持つ

    /**
     * クリア判定
     * @param EnMissionData $missionObj
     * @return bool
     */
    public function isClear(EnMissionData $missionObj ) {
        $isClear = parent::isClear($missionObj);
        $targetItemId = $missionObj['targetId'];
        //指定アイテムの所持情報を取得する
        $userItemData = UserItemDataManager::getInstance($this->userId)->getUserItemData($targetItemId); //←ここ
        if ( $missionObj['border'] <= $userItemData['num']) {
            $isClear = true;
        }
        return $isClear;
    }

所持アイテム情報はアイテムIDごとにクエリを投げるのではなく、一括で取得したItemDataManagerに聞きにいくだけにする。

ミッションパターンの拡張がしやすい。でも使われなくなっても負荷は残したくない

  • missionPatternごとにstrategyパターン化
  • クリア判定処理はmissionManagerを呼ぶだけ

これで使われないものはクリア判定は動かないし、patternが増えてもソースが増えるだけで済む。

並列開催されても負荷は高くならないようにしたい

  • 同種タイプの進捗をstaticで持っていることである程度typeごとに負荷がかかりにくい
    • アイテム数とか、ポイント数とかは共通化しやすい
  • とはいえステージクリアみたいな広範囲のデータが必要なものは、一括でとるとメモリ負荷が大きいので悩ましい・・・

実際やってみてどう?

  • 2019/4に担当サービスのミッションを上記のやり方でリニューアルしました
  • 負荷はどうなった?
    • 改善。400-500クエリ流れて1s近くかかっていたが30-50クエリほどに改善。劇的ビフォーアフター
  • 作りやすさは?
    • 設計意図を理解するまではちょっと敷居が高いかも
    • でもエンジニア若手がミッション追加できてたみたいなので合格点かな
  • 他によかったところ
    • missionのマスタのカラムを掃除できてプランナーの負荷・バグリスクが減った

終わりに

ゲームサービスは高い確率でサービス途中でミッションを作ることになると思うので、早めに意識して設計しておくとよいと思います。
正解はないと思うのでもっとよい作りをこれから模索していければ。

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

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

はじめに

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

サポート

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

note

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

LOG

インストール

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

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

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

php-fpm起動/停止

# systemctl start php-fpm
# systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; vendor preset: disabled)
   Active: active (running) since Thu 2019-11-28 12:16:59 UTC; 3s ago
 Main PID: 256 (php-fpm)
   Status: "Ready to handle connections"
    Tasks: 6 (limit: 8989)
   Memory: 19.1M
   CGroup: /docker/e2cab96d7e3e1b99f792ef7ce65d73243cf1827b5e5514e8b52aa20550d6c662/system.slice/php-fpm.service
           tq256 php-fpm: master process (/etc/php-fpm.conf)
           tq257 php-fpm: pool www
           tq258 php-fpm: pool www
           tq259 php-fpm: pool www
           tq260 php-fpm: pool www
           mq261 php-fpm: pool www

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

Nov 28 12:16:59 e2cab96d7e3e systemd[1]: Starting The PHP FastCGI Process Manager...
Nov 28 12:16:59 e2cab96d7e3e systemd[1]: Started The PHP FastCGI Process Manager.
Nov 28 12:18:08 e2cab96d7e3e systemd[1]: Stopping The PHP FastCGI Process Manager...
Nov 28 12:18:08 e2cab96d7e3e systemd[1]: Stopped The PHP FastCGI Process Manager.

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

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

各種確認

# which php
/usr/bin/php

# php -v
PHP 7.4.0 (cli) (built: Nov 26 2019 20:13:36) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies

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

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

# /usr/sbin/php-fpm -v
PHP 7.4.0 (fpm-fcgi) (built: Nov 26 2019 20:13:36)
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies

# dnf module info php:remi-7.4
Failed to set locale, defaulting to C
Last metadata expiration check: 0:06:10 ago on Thu Nov 28 12:16:02 2019.
Name             : php
Stream           : remi-7.4 [e] [a]
Version          : 20191128090017
Context          : 00000000
Profiles         : common [d] [i], devel, minimal
Default profiles : common
Repo             : remi-modular
Summary          : PHP scripting language
Description      : Alternative php 7.4 module. The remi-7.4 stream provides more recent versions and more packages than default 7.4 stream.
Artifacts        : apcu-panel-0:5.1.17-5.el8.remi.7.4.noarch
                 : apcu-panel-0:5.1.18-1.el8.remi.7.4.noarch
                 : libzip-0:1.5.2-1.el8.remi.x86_64
                 : libzip-devel-0:1.5.2-1.el8.remi.x86_64
                 : libzip-tools-0:1.5.2-1.el8.remi.x86_64
                 : php-0:7.4.0-1.el8.remi.x86_64
                 : php-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-ast-0:1.0.4-1.el8.remi.7.4.x86_64
                 : php-ast-0:1.0.5-1.el8.remi.7.4.x86_64
                 : php-bcmath-0:7.4.0-1.el8.remi.x86_64
                 : php-bcmath-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-brotli-0:0.7.0-4.el8.remi.7.4.x86_64
                 : php-cli-0:7.4.0-1.el8.remi.x86_64
                 : php-cli-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-common-0:7.4.0-1.el8.remi.x86_64
                 : php-common-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-componere-0:3.1.0-4.el8.remi.7.4.x86_64
                 : php-componere-0:3.1.1-1.el8.remi.7.4.x86_64
                 : php-dba-0:7.4.0-1.el8.remi.x86_64
                 : php-dba-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-dbg-0:7.4.0-1.el8.remi.x86_64
                 : php-dbg-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-devel-0:7.4.0-1.el8.remi.x86_64
                 : php-devel-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-embedded-0:7.4.0-1.el8.remi.x86_64
                 : php-embedded-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-enchant-0:7.4.0-1.el8.remi.x86_64
                 : php-enchant-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-fedora-autoloader-0:1.0.0-5.el8.remi.noarch
                 : php-ffi-0:7.4.0-1.el8.remi.x86_64
                 : php-ffi-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-fpm-0:7.4.0-1.el8.remi.x86_64
                 : php-fpm-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-gd-0:7.4.0-1.el8.remi.x86_64
                 : php-gd-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-gmp-0:7.4.0-1.el8.remi.x86_64
                 : php-gmp-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-horde-horde-lz4-0:1.0.10-7.el8.remi.7.4.x86_64
                 : php-imap-0:7.4.0-1.el8.remi.x86_64
                 : php-imap-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-intl-0:7.4.0-1.el8.remi.x86_64
                 : php-intl-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-json-0:7.4.0-1.el8.remi.x86_64
                 : php-json-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-ldap-0:7.4.0-1.el8.remi.x86_64
                 : php-ldap-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-libvirt-0:0.5.4-5.el8.remi.7.4.x86_64
                 : php-libvirt-doc-0:0.5.4-5.el8.remi.7.4.noarch
                 : php-litespeed-0:7.4.0-1.el8.remi.x86_64
                 : php-litespeed-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-lz4-0:0.3.6-2.el8.remi.7.4.x86_64
                 : php-maxminddb-0:1.4.1-2.el8.remi.7.4.x86_64
                 : php-maxminddb-0:1.5.0-3.el8.remi.7.4.x86_64
                 : php-mbstring-0:7.4.0-1.el8.remi.x86_64
                 : php-mbstring-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-mysqlnd-0:7.4.0-1.el8.remi.x86_64
                 : php-mysqlnd-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-oci8-0:7.4.0-1.el8.remi.x86_64
                 : php-oci8-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-odbc-0:7.4.0-1.el8.remi.x86_64
                 : php-odbc-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-opcache-0:7.4.0-1.el8.remi.x86_64
                 : php-opcache-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-pdo-0:7.4.0-1.el8.remi.x86_64
                 : php-pdo-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-pdo-dblib-0:7.4.0-1.el8.remi.x86_64
                 : php-pdo-dblib-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-pear-1:1.10.10-1.el8.remi.noarch
                 : php-pear-1:1.10.10-2.el8.remi.noarch
                 : php-pecl-ahocorasick-0:0.0.7-3.el8.remi.7.4.x86_64
                 : php-pecl-amqp-0:1.9.4-9.el8.remi.7.4.x86_64
                 : php-pecl-apcu-0:5.1.17-5.el8.remi.7.4.x86_64
                 : php-pecl-apcu-0:5.1.18-1.el8.remi.7.4.x86_64
                 : php-pecl-apcu-bc-0:1.0.5-3.el8.remi.7.4.x86_64
                 : php-pecl-apcu-devel-0:5.1.17-5.el8.remi.7.4.x86_64
                 : php-pecl-apcu-devel-0:5.1.18-1.el8.remi.7.4.x86_64
                 : php-pecl-apfd-0:1.0.1-13.el8.remi.7.4.x86_64
                 : php-pecl-base58-0:0.1.4-3.el8.remi.7.4.x86_64
                 : php-pecl-bitset-0:3.0.1-4.el8.remi.7.4.x86_64
                 : php-pecl-cassandra-0:1.3.2-8.el8.remi.7.4.x86_64
                 : php-pecl-cmark-0:1.2.0-3.el8.remi.7.4.x86_64
                 : php-pecl-couchbase2-0:2.6.1-2.el8.remi.7.4.x86_64
                 : php-pecl-crypto-0:0.3.1-9.el8.remi.7.4.x86_64
                 : php-pecl-datadog-trace-0:0.34.0-1.el8.remi.7.4.x86_64
                 : php-pecl-datadog-trace-0:0.34.1-1.el8.remi.7.4.x86_64
                 : php-pecl-dbase-0:7.0.0-3.el8.remi.7.4.x86_64
                 : php-pecl-dbase-0:7.0.1-1.el8.remi.7.4.x86_64
                 : php-pecl-decimal-0:1.3.0-3.el8.remi.7.4.x86_64
                 : php-pecl-dio-0:0.1.0-8.el8.remi.7.4.x86_64
                 : php-pecl-druid-0:1.0.0-5.el8.remi.7.4.x86_64
                 : php-pecl-ds-0:1.2.9-2.el8.remi.7.4.x86_64
                 : php-pecl-eio-0:2.0.4-5.el8.remi.7.4.x86_64
                 : php-pecl-env-0:0.2.1-8.el8.remi.7.4.x86_64
                 : php-pecl-ev-0:1.0.6-5.el8.remi.7.4.x86_64
                 : php-pecl-event-0:2.5.3-3.el8.remi.7.4.x86_64
                 : php-pecl-fann-0:1.1.1-14.el8.remi.7.4.x86_64
                 : php-pecl-gearman-0:2.0.5-5.el8.remi.7.4.x86_64
                 : php-pecl-gearman-0:2.0.6-1.el8.remi.7.4.x86_64
                 : php-pecl-gender-0:1.1.0-14.el8.remi.7.4.x86_64
                 : php-pecl-geoip-0:1.1.1-11.el8.remi.7.4.x86_64
                 : php-pecl-geospatial-0:0.2.1-2.el8.remi.7.4.x86_64
                 : php-pecl-gmagick-0:2.0.5~RC1-6.el8.remi.7.4.x86_64
                 : php-pecl-gnupg-0:1.4.0-9.el8.remi.7.4.x86_64
                 : php-pecl-grpc-0:1.24.0-1.el8.remi.7.4.x86_64
                 : php-pecl-grpc-0:1.25.0-1.el8.remi.7.4.x86_64
                 : php-pecl-handlebars-0:0.8.3-2.el8.remi.7.4.x86_64
                 : php-pecl-handlebars-devel-0:0.8.3-2.el8.remi.7.4.x86_64
                 : php-pecl-hdr-histogram-0:0.3.0-10.el8.remi.7.4.x86_64
                 : php-pecl-hdr-histogram-0:0.3.0-9.el8.remi.7.4.x86_64
                 : php-pecl-hprose-0:1.6.8-3.el8.remi.7.4.x86_64
                 : php-pecl-hrtime-0:0.6.0-7.el8.remi.7.4.x86_64
                 : php-pecl-http-0:3.2.2-1.el8.remi.7.4.x86_64
                 : php-pecl-http-0:3.2.3-1.el8.remi.7.4.x86_64
                 : php-pecl-http-devel-0:3.2.2-1.el8.remi.7.4.x86_64
                 : php-pecl-http-devel-0:3.2.3-1.el8.remi.7.4.x86_64
                 : php-pecl-http-message-0:0.2.0-1.el8.remi.7.4.x86_64
                 : php-pecl-http-message-0:0.2.1-1.el8.remi.7.4.x86_64
                 : php-pecl-http-message-devel-0:0.2.0-1.el8.remi.7.4.x86_64
                 : php-pecl-http-message-devel-0:0.2.1-1.el8.remi.7.4.x86_64
                 : php-pecl-igbinary-0:3.0.1-5.el8.remi.7.4.x86_64
                 : php-pecl-igbinary-devel-0:3.0.1-5.el8.remi.7.4.x86_64
                 : php-pecl-imagick-0:3.4.4-6.el8.remi.7.4.x86_64
                 : php-pecl-imagick-devel-0:3.4.4-6.el8.remi.7.4.x86_64
                 : php-pecl-inotify-0:2.0.0-10.el8.remi.7.4.x86_64
                 : php-pecl-ip2location-0:8.0.1-5.el8.remi.7.4.x86_64
                 : php-pecl-json-post-0:1.0.1-12.el8.remi.7.4.x86_64
                 : php-pecl-krb5-0:1.1.2-8.el8.remi.7.4.x86_64
                 : php-pecl-krb5-devel-0:1.1.2-8.el8.remi.7.4.x86_64
                 : php-pecl-leveldb-0:0.2.1-6.el8.remi.7.4.x86_64
                 : php-pecl-lua-0:2.0.6-3.el8.remi.7.4.x86_64
                 : php-pecl-luasandbox-0:3.0.3-4.el8.remi.7.4.x86_64
                 : php-pecl-lzf-0:1.6.7-3.el8.remi.7.4.x86_64
                 : php-pecl-mailparse-0:3.0.3-3.el8.remi.7.4.x86_64
                 : php-pecl-mcrypt-0:1.0.2-4.el8.remi.7.4.x86_64
                 : php-pecl-mcrypt-0:1.0.3-1.el8.remi.7.4.x86_64
                 : php-pecl-memcache-0:4.0.4-4.el8.remi.7.4.x86_64
                 : php-pecl-memcached-0:3.1.3-4.el8.remi.7.4.x86_64
                 : php-pecl-memcached-0:3.1.4-1.el8.remi.7.4.x86_64
                 : php-pecl-memprof-0:2.0.0-6.el8.remi.7.4.x86_64
                 : php-pecl-mogilefs-0:0.9.3.1-10.el8.remi.7.4.x86_64
                 : php-pecl-mongodb-0:1.5.5-3.el8.remi.7.4.x86_64
                 : php-pecl-mongodb-0:1.6.0-1.el8.remi.7.4.x86_64
                 : php-pecl-mosquitto-0:0.4.0-7.el8.remi.7.4.x86_64
                 : php-pecl-msgpack-0:2.0.3-3.el8.remi.7.4.x86_64
                 : php-pecl-msgpack-devel-0:2.0.3-3.el8.remi.7.4.x86_64
                 : php-pecl-mustache-0:0.9.0-4.el8.remi.7.4.x86_64
                 : php-pecl-mysql-0:1.0.0-0.23.20190415.d7643af.el8.remi.7.4.x86_64
                 : php-pecl-mysql-xdevapi-0:8.0.17-3.el8.remi.7.4.x86_64
                 : php-pecl-mysql-xdevapi-0:8.0.18-1.el8.remi.7.4.x86_64
                 : php-pecl-mysqlnd-azure-0:1.0.1-1.el8.remi.7.4.x86_64
                 : php-pecl-mysqlnd-azure-0:1.0.2-1.el8.remi.7.4.x86_64
                 : php-pecl-nsq-0:3.4.3-1.el8.remi.7.4.x86_64
                 : php-pecl-nsq-0:3.5.0-1.el8.remi.7.4.x86_64
                 : php-pecl-oauth-0:2.0.3-6.el8.remi.7.4.x86_64
                 : php-pecl-opencensus-0:0.2.2-5.el8.remi.7.4.x86_64
                 : php-pecl-parallel-0:1.1.3-1.el8.remi.7.4.x86_64
                 : php-pecl-parle-0:0.8.1-8.el8.remi.7.4.x86_64
                 : php-pecl-pcov-0:1.0.6-2.el8.remi.7.4.x86_64
                 : php-pecl-pdflib-0:4.1.2-5.el8.remi.7.4.x86_64
                 : php-pecl-pdflib-0:4.1.3-1.el8.remi.7.4.x86_64
                 : php-pecl-pq-0:2.1.5-4.el8.remi.7.4.x86_64
                 : php-pecl-propro-0:2.1.0-5.el8.remi.7.4.x86_64
                 : php-pecl-propro-devel-0:2.1.0-5.el8.remi.7.4.x86_64
                 : php-pecl-psr-0:0.7.0-2.el8.remi.7.4.x86_64
                 : php-pecl-psr-devel-0:0.7.0-2.el8.remi.7.4.x86_64
                 : php-pecl-radius-0:1.4.0-0.7.b1.el8.remi.7.4.x86_64
                 : php-pecl-raphf-0:2.0.0-9.el8.remi.7.4.x86_64
                 : php-pecl-raphf-0:2.0.1-1.el8.remi.7.4.x86_64
                 : php-pecl-raphf-devel-0:2.0.0-9.el8.remi.7.4.x86_64
                 : php-pecl-raphf-devel-0:2.0.1-1.el8.remi.7.4.x86_64
                 : php-pecl-rar-0:4.0.0-6.el8.remi.7.4.x86_64
                 : php-pecl-rdkafka-0:3.1.2-2.el8.remi.7.4.x86_64
                 : php-pecl-rdkafka4-0:4.0.0-1.el8.remi.7.4.x86_64
                 : php-pecl-recode-0:1.0.0~DEV.20190723-3.el8.remi.7.4.x86_64
                 : php-pecl-redis5-0:5.1.0-1.el8.remi.7.4.x86_64
                 : php-pecl-redis5-0:5.1.1-1.el8.remi.7.4.x86_64
                 : php-pecl-rpminfo-0:0.2.1-5.el8.remi.7.4.x86_64
                 : php-pecl-rrd-0:2.0.1-7.el8.remi.7.4.x86_64
                 : php-pecl-sandbox-0:0.1.2-2.el8.remi.7.4.x86_64
                 : php-pecl-sandbox-0:0.1.3-1.el8.remi.7.4.x86_64
                 : php-pecl-scoutapm-0:1.0.1-1.el8.remi.7.4.x86_64
                 : php-pecl-scoutapm-0:1.0.2-1.el8.remi.7.4.x86_64
                 : php-pecl-scrypt-0:1.4.2-8.el8.remi.7.4.x86_64
                 : php-pecl-sdl-0:2.2.2-2.el8.remi.7.4.x86_64
                 : php-pecl-sdl-0:2.3.0-1.el8.remi.7.4.x86_64
                 : php-pecl-seasclick-0:0.1.0-3.el8.remi.7.4.x86_64
                 : php-pecl-seaslog-0:2.0.2-3.el8.remi.7.4.x86_64
                 : php-pecl-selinux-0:0.4.2-2.el8.remi.7.4.x86_64
                 : php-pecl-solr2-0:2.5.0-3.el8.remi.7.4.x86_64
                 : php-pecl-sphinx-0:1.4.0-0.8.20181116.d958afb.el8.remi.7.4.x86_64
                 : php-pecl-ssdeep-0:1.1.0-5.el8.remi.7.4.x86_64
                 : php-pecl-ssh2-0:1.1.2-6.el8.remi.7.4.x86_64
                 : php-pecl-ssh2-0:1.2-1.el8.remi.7.4.x86_64
                 : php-pecl-stats-0:2.0.3-8.el8.remi.7.4.x86_64
                 : php-pecl-stomp-0:2.0.2-5.el8.remi.7.4.x86_64
                 : php-pecl-swoole4-0:4.4.10-1.el8.remi.7.4.x86_64
                 : php-pecl-swoole4-0:4.4.12-1.el8.remi.7.4.x86_64
                 : php-pecl-swoole4-devel-0:4.4.10-1.el8.remi.7.4.x86_64
                 : php-pecl-swoole4-devel-0:4.4.12-1.el8.remi.7.4.x86_64
                 : php-pecl-sync-0:1.1.1-6.el8.remi.7.4.x86_64
                 : php-pecl-trader-0:0.5.0-5.el8.remi.7.4.x86_64
                 : php-pecl-translit-0:0.6.3-4.el8.remi.7.4.x86_64
                 : php-pecl-uopz-0:6.1.0-5.el8.remi.7.4.x86_64
                 : php-pecl-uopz-0:6.1.1-1.el8.remi.7.4.x86_64
                 : php-pecl-uploadprogress-0:1.0.3.1-20.el8.remi.7.4.x86_64
                 : php-pecl-uuid-0:1.0.4-16.el8.remi.7.4.x86_64
                 : php-pecl-uuid-0:1.0.5-1.el8.remi.7.4.x86_64
                 : php-pecl-uv-0:0.2.4-3.el8.remi.7.4.x86_64
                 : php-pecl-varnish-0:1.2.4-4.el8.remi.7.4.x86_64
                 : php-pecl-vips-0:1.0.10-1.el8.remi.7.4.x86_64
                 : php-pecl-vips-0:1.0.9-3.el8.remi.7.4.x86_64
                 : php-pecl-vld-0:0.16.0-3.el8.remi.7.4.x86_64
                 : php-pecl-wddx-0:1.0.0~DEV.20190320-6.el8.remi.7.4.x86_64
                 : php-pecl-xattr-0:1.3.0-8.el8.remi.7.4.x86_64
                 : php-pecl-xdebug-0:2.8.0-1.el8.remi.7.4.x86_64
                 : php-pecl-xdebug-0:2.8.0~beta2-2.el8.remi.7.4.x86_64
                 : php-pecl-xdiff-0:2.0.1-7.el8.remi.7.4.x86_64
                 : php-pecl-xlswriter-0:1.3.1-2.el8.remi.7.4.x86_64
                 : php-pecl-xlswriter-0:1.3.2-1.el8.remi.7.4.x86_64
                 : php-pecl-xmldiff-0:1.1.2-14.el8.remi.7.4.x86_64
                 : php-pecl-xmldiff-devel-0:1.1.2-14.el8.remi.7.4.x86_64
                 : php-pecl-xxtea-0:1.0.11-8.el8.remi.7.4.x86_64
                 : php-pecl-yac-0:2.0.2-6.el8.remi.7.4.x86_64
                 : php-pecl-yaconf-0:1.0.8-0.4.20180622.d5f267a.el8.remi.7.4.x86_64
                 : php-pecl-yaconf-devel-0:1.0.8-0.4.20180622.d5f267a.el8.remi.7.4.x86_64
                 : php-pecl-yaml-0:2.0.4-4.el8.remi.7.4.x86_64
                 : php-pecl-yaz-0:1.2.3-3.el8.remi.7.4.x86_64
                 : php-pecl-zip-0:1.15.4-7.el8.remi.7.4.x86_64
                 : php-pecl-zip-0:1.15.5-1.el8.remi.7.4.x86_64
                 : php-pecl-zmq-0:1.1.3-10.el8.remi.7.4.x86_64
                 : php-pecl-zmq-0:1.1.3-11.el8.remi.7.4.x86_64
                 : php-pggi-0:0.4.0-1.el8.remi.7.4.x86_64
                 : php-pggi-0:0.4.1-1.el8.remi.7.4.x86_64
                 : php-pgsql-0:7.4.0-1.el8.remi.x86_64
                 : php-pgsql-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-phalcon4-0:4.0.0~rc.2-1.el8.remi.7.4.x86_64
                 : php-phalcon4-0:4.0.0~rc.3-1.el8.remi.7.4.x86_64
                 : php-phpiredis-0:1.0.0-13.el8.remi.7.4.x86_64
                 : php-pinba-0:1.1.1-6.el8.remi.7.4.x86_64
                 : php-process-0:7.4.0-1.el8.remi.x86_64
                 : php-process-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-pspell-0:7.4.0-1.el8.remi.x86_64
                 : php-pspell-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-smbclient-0:1.0.0-4.el8.remi.7.4.x86_64
                 : php-snappy-0:0.1.9-4.el8.remi.7.4.x86_64
                 : php-snmp-0:7.4.0-1.el8.remi.x86_64
                 : php-snmp-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-soap-0:7.4.0-1.el8.remi.x86_64
                 : php-soap-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-sodium-0:7.4.0-1.el8.remi.x86_64
                 : php-sodium-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-sqlsrv-0:5.6.1-5.el8.remi.7.4.x86_64
                 : php-sqlsrv-0:5.7.0~preview-1.el8.remi.7.4.x86_64
                 : php-tidy-0:7.4.0-1.el8.remi.x86_64
                 : php-tidy-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-wkhtmltox-0:0.3.2-0.5.20171112.fecc9af.el8.remi.7.4.x86_64
                 : php-xml-0:7.4.0-1.el8.remi.x86_64
                 : php-xml-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-xmlrpc-0:7.4.0-1.el8.remi.x86_64
                 : php-xmlrpc-0:7.4.0~RC6-15.el8.remi.x86_64
                 : php-zephir-parser-0:1.3.1-2.el8.remi.7.4.x86_64
                 : php-zephir-parser-0:1.3.2-1.el8.remi.7.4.x86_64
                 : php-zephir-parser-devel-0:1.3.1-2.el8.remi.7.4.x86_64
                 : php-zephir-parser-devel-0:1.3.2-1.el8.remi.7.4.x86_64
                 : php-zstd-0:0.7.4-2.el8.remi.7.4.x86_64
                 : php-zstd-devel-0:0.7.4-2.el8.remi.7.4.x86_64
                 : unit-php-0:1.12.0-1.el8.remi.7.4.x86_64
                 : unit-php-0:1.13.0-1.el8.remi.7.4.x86_64

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

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

はじめに

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

サポート

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

note

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

LOG

インストール

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

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

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

php-fpm起動/停止

# systemctl start php-fpm
# systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; vendor preset: disabled)
   Active: active (running) since Thu 2019-11-28 12:07:48 UTC; 4s ago
 Main PID: 366 (php-fpm)
   Status: "Ready to handle connections"
   CGroup: /docker/8d7650266982ff9cfdead038e82d5288de7214747f0b496ef257ddd22e9e984c/docker/8d7650266982ff9cfdead038e82d5288de7214747f0b496ef257ddd22e9e984c/system.slice/php-fpm.service
           tq366 php-fpm: master process (/etc/php-fpm.conf)
           tq367 php-fpm: pool www
           tq368 php-fpm: pool www
           tq369 php-fpm: pool www
           tq370 php-fpm: pool www
           mq371 php-fpm: pool www
           ? 366 php-fpm: master process (/etc/php-fpm.conf)

Nov 28 12:07:48 8d7650266982 systemd[1]: Starting The PHP FastCGI Process Manager...
Nov 28 12:07:48 8d7650266982 systemd[1]: Started The PHP FastCGI Process Manager.
# systemctl stop php-fpm
# systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; vendor preset: disabled)
   Active: inactive (dead)

Nov 28 12:07:48 8d7650266982 systemd[1]: Starting The PHP FastCGI Process Manager...
Nov 28 12:07:48 8d7650266982 systemd[1]: Started The PHP FastCGI Process Manager.
Nov 28 12:08:08 8d7650266982 systemd[1]: Stopping The PHP FastCGI Process Manager...
Nov 28 12:08:08 8d7650266982 systemd[1]: Stopped The PHP FastCGI Process Manager.

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

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

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

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

各種確認

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

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

# php-fpm -v
PHP 7.4.0 (fpm-fcgi) (built: Nov 26 2019 20:13:36)
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies

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

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

はじめに

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

サポート

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

note

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

LOG

インストール

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

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

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

各種確認

# which php
/usr/bin/php

# php -v
PHP 7.4.0 (cli) (built: Nov 26 2019 20:13:36) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies

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

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

LaravelのHerokuへのデプロイ、DBはMySQL。

データベースにMYSQLを使う

HerokuではデータベースとしてPostgreSQLになっているため、アプリごとにClearDBを入れなければならない。利用にはクレジットカードが必要。(ClearDBの利用自体は無料)

$ heroku addons:add cleardb

上記にコマンドの入力またはHerokuのサイトのsettingタブから導入できる。

Procfile を作成する

ProcfileというHerokuの設定ファイルを作成。サーバーにApacheを使うこと、public/をドキュメントルートとすることを宣言するために使います。アプリフォルダの直下に作成する。

Procfile
web: vendor/bin/heroku-php-apache2 public/

中身は上記のように。

環境変数の設定

どの項目を登録すべきかよくわからなかったためいろいろやってみたが注意すべきはDBに関する項目。

DB_DATABASE=[データベース名]
DB_HOST=[ホスト名]
DB_USERNAME=[ユーザー名]
DB_PASSWORD=[パスワード]

これらはenvファイルの設定ではなくclearDBの設定で、そのURL内にある。下記コマンドで表示。

heroku config:get CLEARDB_DATABASE_URL

#URLが表示される
mysql://[ユーザー名]:[パスワード]@[ホスト名]/[データベース名]?reconnect=true

またセキュリティ関連のAPP_KEYの設定も必須。

heroku config:set APP_KEY=$(php artisan --no-ansi key:generate --show)

envファイルのAPP_KEYの項目をartisanコマンドで取得

HerokuのMYSQLにmigrateする

自分はここに一番時間を取られた。マイグレーションがうまくいかない。

Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes

何度やってもこのエラーメッセージが出てくる。訳すと「指定されたキーは長すぎます。キーの長さは最大767バイトです。」

調べていくとvarchar型の文字数を191に制限しなければこのエラーが出るらしい。

app\Providers\AppServiceProvider.phpに以下を追加。

AppServieProvider.php
use Illuminate\Support\Facades\Schema;

public function boot()
{
    Schema::defaultStringLength(191);
}

自分はSchemaクラスをuseするのを忘れていたため時間がとられてしまった。
use Illuminate\Support\Facades\Schema;を忘れないように。

デプロイする

HerokuではアプリをGitを利用してデプロイする。自分はGitも初心者だったためここでもかなり時間を要した。

まずはHerokuのアプリをリモートとして登録

git init
heroku git:remote -a <herokuのアプリ名>

ローカルリポジトリにコミットし

git add .
git commit -m "コミットメッセージ"

プッシュすることによりデプロイする

git push heroku master

問題がなければheroku openでサイトを開くことができる

heroku open


思った以上に時間がかかってしまった。自分が犯していた間違いはdevelopブランチにProcfileファイルを作成し、マージしないままmasterをプッシュをしていたこと。そのほかにもGitの基本的な操作ができていなかった。HerokuよりもGitに慣れていないことを痛感させられた。

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

【Laravel5.8】PHP7.4でTrying to access array offset on valueが山盛り出るようになった

Laravel5.8で試したからLaravel5.8としているだけで、他のバージョンや、あるいは他のフレームワークでも発生すると思います。

PHP7.4.0がリリースされました

で、PHP7.4からスカラー型変数に配列アクセスするとE_NOTICEが出るようになりました。

$a = null;
echo $a[1];

これはPHP7.3までは何も言いませんでしたが、7.4ではE_NOTICEが発生します。
さらにPHP8ではE_WARNINGになる予定です。

これの何が問題って、値が入ってるかどうかわからないテーブルにリレーション張ってる場合ですよ。

テーブルAのモデル
class TableA extends Model{
    /**
     * テーブルBへのリレーション
     */
    public function TableB()
    {
        return $this->belongsTo(\path\to\TableB::class, 'b_id', 'id');
    }
}

コントローラはwithで引っ張ってきてbladeに投げるだけ。

コントローラ
    $tableAs = TableA::with(['TableB'])->get();
    return view('hoge.blade.php', [$tableAs]);

ビューではなんか適当に表示。

hoge.blade.php
@foreach ($tableAs as $tableA)
    ID: {{ $tableA->id }}
    Bの値: {{ $tableA->TableB['name'] }}
@endforeach

テンプレートはだいたいこんな書き方をしてると思うのですが、というか私がしてるのですが、テーブルBに値が無かった場合、これまで"Bの値"は単に空白になっていました。
PHP7.4に上げた瞬間、至る所でLaravelがErrorExceptionを吐くようになってえらいことになりましたよ。

根本的に正しく解決するにはどうすればいいのかはよくわかりませんが、とりあえず

    Bの値: {{ $tableA->TableB['name'] ?? '' }}

ってすることで事なきを得た。

事なきを得たと思ったんですが、実はこれLaravel本体側でも発生するんですよね。
確認したところではemailバリデータを使うと出てきます。
そのうち修正されるとは思いますが、先にLaravelのアップデートを終わらせるまでPHP本体のアプデは待った方がよいでしょう。

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

PHP関連パッケージのアップデート後にセッション処理でエラーになったときの回避策

はじめに

PHP関連のパッケージアップデートしたら、セッション処理エラーを吐かれた。
そんなときの回避策。

エラー出力の内容

Apacheのエラーログに以下メッセージが出力されている。

Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php/session)

回避策

とりあえず、なんでもいいから復旧させたい!という場合は、/var/lib/php/session の権限を777にする。

$ chmod 777 /usr/lib/php/session

PHP関連パッケージがバージョンアップされたときに、
"/var/lib/php/session" の権限が変わってしまったことが原因なので、
権限を777にすることで回避します。

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

【PHP超初心者】おみくじプログラムを作ってみた

はじめに

PHPの超初心者向け書籍を購入し、初めて作ったプログラムを公開します。
rand関数を使って、おみくじプログラムを作ってみました。

おみくじプログラムの中身

Omikuji.php

<?php
$fortunes = array('大吉','中吉','小吉','末吉','凶');
$result = $fortunes[rand(0, count($fortunes)-1)];
echo "あなたの今日の運命は". $result . "です。". PHP_EOL;

発行するとランダムで、今日の運勢が出力されます。
試しに発行してみると、

$ php Omikuji.php 
あなたの今日の運命は凶です。

凶でした。

以下使用している関数など

・array
配列

・rond
乱数を生成する

・PHP_EOL
改行コードを出力する

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

Laravel6.0始める前にMacのphpは7.2以上にすべき話【対策を解説】

Laravel6.0を遂に試してみようということで、

いつもの通り、

$ composer create-project laravel/laravel qiita --prefer-dist "6.0.*"

とバージョンを指定して、composerでプロジェクトを作成しようとしました。

そしたら、エラーが出てしまいました。

理由は、

Laravel6.0はpcのphpが7.2以上である必要がある

ということでした。

MAMPはphp7.3.1なのになんでやと思ってたんですが、pcは7.1.2だったんですね...

ということで、

pc(Mac)のphpのバージョンを7.3~にあげる方法を解説します

前提環境
MacOS MOjave 10.14.5
composerインストール済み
homebrewインストール済み

homebrewまだの人は各自インストールお願いします?

brewコマンドでphpをインストールする

まずはbrewコマンドでphp7系を検索してください。

$ brew search php@7

すると、以下の画面になります。

==> Formulae
php@7.1                    php@7.2                    php@7.3

そしたら、以下のコマンドを入力してください。

$ brew install php@7.3

これでphp7.3がインストールされます。

一応コマンドでバージョンを確認します。

$ php -v

スクリーンショット 2019-11-28 14.32.51.png

やったぜ

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

RailsとLaravelの比較

はじめに

この記事はRailsとLaravelを比較分析してみた記事です.
Railsは以前から勉強していて,新たにLaravelを使ってみました.
Railsの勉強にはRuby on Rails 5 超入門改訂4版 基礎 Ruby on Rails (IMPRESS KISO SERIES)を使いました.
Ruby on Rails 5 超入門についてはチュートリアル形式で一見わかりやすいのですが,誤植などが多いのとレベルが優しすぎるのであまりオススメしません.

Laravelの勉強はPHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応を使いました.買う前に中身を確認しなかった僕が悪いのですが,本の厚さの割に内容が薄いです.また辞書的に使う分には良いのですが,チュートリアル形式で作っていく感じではないので,フレームワークを使ったことのない人などにはオススメ出来ません.

RailsとLaravelの基本情報

*以下,2019/11/28現在の情報

Rails Laravel
言語 Ruby PHP
初リリース 2004年 2011年
github https://github.com/rails/rails https://github.com/laravel/framework
github start数 44.6K 19.5K

githubのstart数ではrailsの方がLarvelの倍くらいの数になっています.Railsはいま流行りと個人的に思っていたりするのですが,リリースからはもう10年以上経過していました.

Googleトレンドで調べてみた

Googleトレンドでは検索のトレンドを調べて,検索キーワードで比較したりできるサービスです.Googleは面白いサービスを作っていますね.
これを使ってRailsとLaravelについて調べてみました.

すべての国

https://trends.google.co.jp/trends/explore?date=all&q=Rails,Laravel

スクリーンショット 2019-11-28 16.02.49.png

日本

https://trends.google.co.jp/trends/explore?date=all&geo=JP&q=Rails,Laravel

スクリーンショット 2019-11-28 16.03.22.png

Laravelはリリースが2011年なので,2012年頃まではほぼ0です.
全ての国の場合ではLaravelがRailsをやや追い越したくらいになっています.日本の場合ではまだRailsを追いかけていますが,このままの状態が続くと追い越しそうです.

個人的分析

ここからはRailsとLaravelを使ってみた感想になります.

本,参考資料などの多さは? 勝者:Rails

Railsの方がネットでググったときなどの参考になる記事などが多いです.それはRailsで使われている言語のRubyの開発者が日本人(まつもとゆきひろ氏)なのでRubyの日本語資料がそもそも多いという点と,Laravelより7年早くリリースしているからかなと思っています.

*IT分野の中で日本発祥の技術はRubyと深層学習のChainerくらいしか知らない(他にあったらコメントください)のでRubyは本当すごいです.

勉強のしやすさは? 勝者:Rails

難易度的にもRailsとLaravelは遜色ないですが,やはり本や参考資料などが多い方が勉強しやすいです.以前Go言語のフレームワークのGinを使う機会があったのですが,公式以外のドキュメントが少なくて開発しにくかったことがあります.

使いやすさは? 同じ

  • railsにはscaffoldという色々必要なものを一発で作ってくれるコマンドがあるのですが,Laravelは標準でサポートしていません(あるけど,githubの更新も止まっているのでバージョンに寄っては今後使えなくなるかも).
  • Laravelは比較的命名規則などが緩いので,ネットで調べても色々な書き方をしているのでわかりにくいです.
  • 逆にRailsは規則が厳しいのですが,厳しいが故に,変数名などを修正するのが億劫になります.
  • Laravelはデフォルトでログイン認証などが入っているので,インストールなどの手間が省けます.

RailsとLaravelのどっちがいいの? 若干Laravelかな

ここまで読んだ人にはRailsの方が良い感じに見えますが,他のエンジニアの人の意見を聞くと

「Railsは比較的開発が速くできるからスタートアップに多いが,保守・運用や難しい機能にはRailsは向いておらず,JavaやPHPのフレームワークの方が開発が容易」

という意見が多かったです(社会人になって1ヶ月くらいなのでこの辺は自分の中で不確定要素).確かに求人とか見てるとPHPとかJavaの募集の方が多いなという印象を持ったのでLaravelとしました.

まとめ

Railsが最強(僕の大学の教授)という人もいれば,Railsはオワコンと言っている人もいますのでどうなんですかね...
僕の周囲ではRailsの方がよく耳にします.
Rails,LarvelはRubyとPHPという言語の違いは多少ありますが,考え方や使い方はほとんど同じです.個人的には勉強のしやすいRailsを最初にやってMVCの基礎を勉強して,Laravelとかに移ればいいかなと思います.

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

PHP(DB連携)

書いてあること

  • PHP(DB連携)
  • 忘れたとき用の自分メモ

関連Web

公式サイト

http://php.net/

仕様確認

http://php.net/manual/ja/index.php

DB接続・切断

DB接続文字列(\$dsn)は接続先DBごとに書式が違うため注意。
切断する場合はPDOオブジェクト変数(\$db)にnullをセットする。
接続エラーに対応するため、例外処理(try〜catch〜finally)を設定する。

<?php
$dsn = 'mysql:dbname=[DB名]; host=[IPアドレス]; charset=utf8';
$usr = 'ユーザー名';
$passwd = 'パスワード';

try {
  $db = new PDO($dsn, $usr, $passwd);
  print '接続に成功しました。';
} catch (PDOException $e) {
  print "接続エラー:{$e->getMessage()}";
} finally {
  $db = null;
}

例外クラスで呼び出せるメソッド

メソッド 概要
getMessage() 例外メッセージ
getCode() 例外コード
getFile() 例外が発生したファイル
getLine() 例外が発生した行
getPrevious() 前の例外
getTrace() バックトレース(配列)
getTraceAsString() バックトレース(文字列)

SQLクエリの発行(名前付きパラメータ)

下記はフォームに入力した内容をDBへINSETする例。
prepareメソッドで実行するSQLクエリ、および後からパラメータを割り当てるプレイスホルダを設定。
bindValueメソッドでプレイスホルダに値を格納。

DBManager.php
<?php
function getDB() {
  $dsn = 'mysql:dbname=[DB名]; host=[IPアドレス]; charset=utf8';
  $usr = 'ユーザー名';
  $passwd = 'パスワード';

  //DBへの接続を確立
  $db = new PDO($dsn, $usr, $passwd);
  return $db;
}
insert_process.php
<?php
require_once '../DBManager.php';

try {
  //DBへの接続を確立
  $db = getDB();

  //insert命令を準備
  $stt = $db->prepare('INSERT INTO book(isbn, title, price) VALUES (:isbn, :title, :price)');

  //プレイスホルダに入力内容をセット
  $stt->bindValue(':isbn', $_POST['isbn']);
  $stt->bindValue(':title', $_POST['title']);
  $stt->bindValue(':price', $_POST['price']);

  //insert命令の実行
  $stt->execute;
} catch (PDOException $e) {
  print "エラーメッセージ:{$e->getMessage()}";
} finally {
  $db = null;
}

SQLクエリの発行(名前なしパラメータ)

プレイスホルダは「:名前」という形式ではなく、「?」も指定可能。
bindValueのインデックスは1から始まる点に注意。

insert_process.php
<?php
require_once '../DBManager.php';

try {
  //DBへの接続を確立
  $db = getDB();

  //insert命令を準備
  $stt = $db->prepare('INSERT INTO book(isbn, title, price) VALUES (?, ?, ?)');

  //プレイスホルダに入力内容をセット
  $stt->bindValue(1, $_POST['isbn']);
  $stt->bindValue(2, $_POST['title']);
  $stt->bindValue(3, $_POST['price']);

  //insert命令の実行
  $stt->execute;
} catch (PDOException $e) {
  print "エラーメッセージ:{$e->getMessage()}";
} finally {
  $db = null;
}

SQLクエリの実行、オートインクリメント(自動採番)の取得

パラメータ・戻り値なしでSQLクエリを実行する場合は、execメソッドを利用する。
execはselectでは利用不可。パラメータなしselectを行う場合はqueryメソッドは利用する。
SQLクエリでレコード登録後、自動採番された値をlastInsertIdメソッドで取得する。

<?php
require_once '../DBManager.php';

try {
  //DBへの接続を確立
  $db = getDB();

  //insert命令を実行
  $db->exec("INSERT ・・・");

  //直近のinsertで採番されたオートインクリメント値を取得
  print $db->lastInsertId();
} catch (PDOException $e) {
  print "エラーメッセージ:{$e->getMessage()}";
} finally {
  $db = null;
}

結果セットの取得

結果セットの取得は、フェッチメソッド・フェッチモードを利用する。

Encode.php
<?php
function e(string $str, string $charset = 'UTF-8'): string {
  return htmlspecialchars($str, ENT_QUOTES | ENT_HTML5, $charset);
}
result.php
<?php
require_once '../DBManager.php';
require_once '../Encode.php';
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>結果セット</title>
</head>
<body>
  <table border="1">
    <tr>
      <th>ISBNコード</th><th>書名</th><th>価格</th><th>出版社</th><th>刊行日</th>
    </tr>
<?php
try {
  //DBへの接続を確立
  $db = getDb();

  //select命令の実行
  $stt = $db->prepare('SELECT * FROM book ORDER BY published DESC');
  //パラメータがない場合は、queryメソッドを利用することも可能
  //$stt = $db->query('SELECT * FROM book ORDER BY published DESC');
  $stt->execte();

  //結果セットの内容を順に出力
  while($row = $stt->fetch(PDO::FETCH_ASSOC)) {
?>
    <tr>
      <td><?= e($row['isbn']) ?></td>
      <td><?= e($row['title']) ?></td>
      <td><?= e($row['price']) ?></td>
      <td><?= e($row['publish']) ?></td>
      <td><?= e($row['published']) ?></td>
    </tr>
<?php
  }
} catch (PDOException $e) {
  print "エラーメッセージ:{$e->getMessage()}";
} finally {
  $db = null;
}
?>
  </table>
</body>
</html>

フェッチメソッド、フェッチモード

<?php
$stt->フェッチメソッド(フェッチモード)

フェッチメソッド

フェッチメソッド 説明
fetch レコードポインタを移動しながら順番にレコードを読み込む
fetchAll 結果セットの内容をまとめて配列に読み込む
fetchColumn 結果セットの先頭列のみ取得
fetchObject 結果セットから取得した結果をオブジェクトに読み込む

フェッチモード

フェッチモード 取得の形式 値の取得(例)
PDO::FETCH_ASSOC 連想配列 $row['name']
PDO::FETCH_NUM 一般配列 $row[0]
PDO::FETCH_BOTH 連想配列/一般配列 \$row['name']/$row[0]
PDO::FETCH_NAMED 連想配列 $row['name']
PDO::FETCH_OBJ オブジェクト $row->name
PDO::FETCH_COLUMN スカラー値(先頭行の値) $row
PDO::FETCH_BOUND bintColumnメソッドでバインドされたPHP変数 $name
PDO::FETCH_CLASS 指定されたクラスにフェッチ $row->name

パラメータの割当方法

パラメータの割当方法には、bindValueメソッドにより直接値を割り当てる方法、および
bindParamにより変数の参照を割り当てる方法がある。
bintParamを利用することによりSQL文のプレイスホルダ定義後に、変数に格納した値を
利用してクエリを実行することが可能となる。

update_process.php
<?php
require_once '../DBManager.php';

try {
  //DBへの接続を確立
  $db = getDB();

  //update命令を準備
  $stt = $db->prepare('UPDATE book SET title=:title, price=:price WHERE isbn=:isbn');

  //プレイスホルダに変数を関連付け
  $stt->bindParam(':isbn', $isbn);
  $stt->bindParam(':title', $title);
  $stt->bindParam(':price', $price);

  //繰り返し
  for($i = i; $i <= $_POST['cnt']; $i++) {
    $isbn = $_POST['isbn'.$i];
    $title = $_POST['title'.$i];
    $price = $_POST['price'.$i];

    //update命令の実行
    $stt->execute;
  }
} catch (PDOException $e) {
  print "エラーメッセージ:{$e->getMessage()}";
} finally {
  $db = null;
}

トランザクション処理

transaction_process.php
<?php
require_once '../DBManager.php';

try {
  //DBへの接続を確立
  $db = getDB();

  //トランザクションを開始
  $db->beginTransaction();

  処理;

  //コミット
  $db->commit();
} catch (PDOException $e) {
  //ロールバック
  $db->rollBack();
  print "エラーメッセージ:{$e->getMessage()}";
} finally {
  $db = null;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP(開発用簡易サーバーの起動)

書いてあること

  • PHP(開発用簡易サーバーの起動)
  • 忘れたとき用の自分メモ

関連Web

公式サイト

http://php.net/

仕様確認

http://php.net/manual/ja/index.php

開発用簡易サーバーの起動

PHP付属の機能により、開発用簡易サーバーを利用することができる。

1.ドキュメントルートとするディレクトリに移動
2.IPアドレスを確認
3.php -S 開発PCのIPアドレス:ポート番号で開発用簡易サーバーを起動
4.ドキュメントルートが合っていることを確認

0.or0qe2xhk.png

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

Deployerでローカルホストへデプロイ

PHP Deployerとは:question:

PHP で作られたデプロイツール :airplane:
公式URL: https://deployer.org/
ドキュメント: https://deployer.org/docs
ソースコード:https://github.com/deployphp/deployer

(英語ですがChromeの日本語翻訳を使えばほとんど読めると思います。)

むかしむかし:hatching_chick:

Gitもまだ使っていない頃、たくさんのPHPやhtml,cssを手動(SFTP)でサーバへアップロードしているのを見てあまりに大変そうなのでサポートするツールを作り便利に使ってもらっていた。

そして数年後:baby_chick:

とあるプログラムの改修サポートをしていたらなんだかんだ私がサーバへファイルをアップロードする必要が出てきたので従来の手順通りにやってみたが、やはり手間なのでコマンド一発で終わりにしたいのでデプロイヤーを使おうと思い始める。

なぜ Deployer:question:

いろんなツールを検討したわけじゃないですが普段、PHPに慣れているのとやってみたら必要十分な機能を持っていたので:sunny:

なぜ? ローカルホストへデプロイ

触りながら

  • サーバを持っていない人
  • サーバ接続がうまくいかなくて詰まった
  • サーバにSSHログインしたあと簡単にデプロイしたい

などDeployerでどんなことができるかはローカルで誰でもさくっとテストできるのではないかということでlocalhostデプロイにしました。:grinning:

さて導入から (OS Xでやってます):desktop:

一応、自分のPHPのバージョン:snowflake:

$ php -v
PHP 7.1.23 (cli) (built: Feb 22 2019 22:19:32) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies

Deployerのダウンロード:inbox_tray:

公式には

curl -LO https://deployer.org/deployer.phar
mv deployer.phar /usr/local/bin/dep
chmod +x /usr/local/bin/dep

とありますが、 /usr/local/bin へは諸事情あって配置できなかったので

curl -LO https://deployer.org/deployer.phar
mv deployer.phar dep
chmod +x dep
./dep list

でやっても問題なく動きます。 (以下同じフォルダへインストールした状態で進めます)
ちなみに

php dep list

だと+xのパーミッションいりません

curl のオプション -LO ってなに? :see_no_evil:

  • -L, --location
    Follow redirects (H)
    リダイレクトを追いかける。
  • -O, --remote-name
    Write output to a file named as the remote file
    リモートファイルを書き出す。

何気なしに入力してたけどhelpからおさらいしてみた

curlってなんじゃい?って人も大丈夫:star:

https://deployer.org/download/
の Downloads の一覧からクリックしてダウンロードもできます。(ハッシュ値は念の為、確認しましょう)
何らかの理由で古いバージョンが必要な場合もこのアドレスからどうぞ
(今回は6.6.0を使ってます。)

では使う前に初期化

$ ./dep init

を実行して全ての問になにも入力せずEnterを押してすべてデフォルト設定でテストするには完了です。
(あとから修正、やりなおしもできます)

もし入力を間違えても大丈夫 :confounded:

同じフォルダにdeploy.phpができている時は削除してもう一度initのコマンドを同じようにやり直せば大丈夫です。

deploy.php ができていれば成功です。

テスト用のローカルデプロイにしてみたのを貼っちゃいます。:page_facing_up:

<?php
// ./dep init で作ってくれたもの
namespace Deployer;
// ./dep init で作ってくれたもの
require 'recipe/common.php';

// プロジェクト名。自分でわかりやすいのを付けましょう。 
// set('xxx') はあとの処理で get('xxx') で取得できます。
set('application', 'local-deploy');

// プロジェクトのgitリポジトリを書く(ここにはサンプルにDeployerのリポジトリを書いてます)
set('repository', 'https://github.com/deployphp/deployer.git');

// [Optional] Allocate tty for git clone. Default value is false.
// ここはデフォルトのままにしてますが、git cloneの仕方でtrue or falseを切り替える
set('git_tty', true);

// Shared files/dirs between deploys
// デプロイしても共有するディレクトリ、ログやキャッシュなど必要であれば
set('shared_files', []);
set('shared_dirs', []);

// Webサーバが書き込み可能にするディレクトリ
set('writable_dirs', []);

// 環境を指定しない場合のデフォルトのステージ(環境)
set('default_stage', 'develop');

// 現在のディレクトリ位置を保存する(追加した)
// このようにPHPの関数が使えます。
set('current_dir', realpath(''));

// ロールバックできる世代数
// スペルを間違えると反映されません(sを最後につけ忘れた経験あり)
set('keep_releases', 3);

// ローカルホストに開発環境をデプロイする想定
// 環境は開発、ブランチはnext、デプロイ先は実行しているフォルダにあるdevへ
localhost('local-dev')
    ->stage('develop')
    ->set('branch', 'next')
    ->set('deploy_path', get('current_dir').'/dev')
    ;

// ローカルホストに本番環境をデプロイする想定
// 環境は開発、ブランチはnext、デプロイ先は実行しているフォルダにあるdevへ
localhost('local-pro')
    ->stage('production')
    ->set('branch', 'master')
    ->set('deploy_path', get('current_dir').'/pro')
    ;

// Tasks
// 実際のデプロイのフローと説明
desc('Deploy your project to localhost');
task('deploy', [
    'deploy:info',
    'deploy:prepare',
    'deploy:lock',
    'deploy:release',
    'deploy:update_code',
    'deploy:shared',
    'deploy:writable',
    // 'deploy:vendors', // composerを使っていないのでコメントアウト。他のタスクも自由に入れ替えができます。
    'deploy:clear_paths',
    'deploy:symlink',
    'deploy:unlock',
    'cleanup',
    'success'
]);

// [Optional] If deploy fails automatically unlock.
// deployが失敗した場合にはunlockを呼び出してね。という設定
after('deploy:failed', 'deploy:unlock');

// 自分で足したHelloWorld
// ./dep hello
// で実行できます
desc('Hello world');
task('hello', function() {
    // Hello Worldと出して終わり
    writeln('Hello world');
});

デプロイテストをする前にデプロイ先となるフォルダを作成する:open_file_folder:

デプロイ先のフォルダを作成する。

mkdir dev pro
$ ls
dep  deploy.php  dev pro

こんな配置になります。

開発想定へデプロイ

$ ./dep deploy develop

defaultをdevelopにしてるので最後のdevelopは省略可能です。

本番想定へデプロイ

$ ./dep deploy production

以下のように必要ないコマンドを削除、コメントアウトしたり、足したり、名前を変えたりできます。:cherries:

task('depuroi', [
    'deploy:info',
    'hello', // 追加
    'deploy:prepare',
    'deploy:lock',
    'deploy:release',
    'deploy:update_code',
    // 'deploy:shared',  // 外してみた
    // 'deploy:writable',// 外してみた
    // 'deploy:vendors', // composerを使っていないのでコメントアウト。他のタスクも自由に入れ替えができます。
    'deploy:clear_paths',
    'deploy:symlink',
    'deploy:unlock',
    'cleanup',
    'success'
]);

タスクの名前を変更しているので、この場合の実行はこのように実行します。

$ ./dep depuroi

デプロイ完了

作業しているところのdev,proにそれぞれGitHubのDeployerのブランチ違いがデプロイされてると思います。

順調にできたら

Stage環境の追加をやってみる

stgフォルダとstage環境の設定を追加してデプロイしてみてください。

もし分からなくなったら

作ってたフォルダを削除して最初からやり直せば大丈夫です。

次はドキュメントへ

https://deployer.org/docs

一度、動けばイメージが湧くと思うのでドキュメントを見てみてください。
あとはこの処理どうなってるんだろうというのはGitHubの検索で入力すると処理が閲覧できてドキュメントと一緒に見ると分かりやすいです。

なお、社内のぜんぜん違うプロジェクトのdeploy.phpでは全く記述内容が違ったので人それぞれですので正解はありません!
ちゃんと思ったとおりデプロイできることが正解です!

サーバがある人は

サーバへの接続はネットにいろいろ情報があるので挑戦してみてください。

終わり:champagne:

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

51歳からのプログラミング 備忘 XREA様の無料サーバーにLaravel

もっと簡単にできるかと思ったけれど、Laravelを導入するだけで丸1日潰れた。でも、先輩方がサイトで記録を残してくれているので、こんな僕でもなんとか独学で!参考にするのなら、他のサイトがおすすめ!

はじめに

無料レンタルサーバーは XREA様
紹介する方法は、XREAに、
・ teraTarmを使って、ローカルで作成したLaravelをアップロードする方法
  XREAのファイルマネージャーでもZIPをあげられる
・ composer.pharを使ってLaravelをインストールする方法

参考サイト

teraTarmでアップロードする方法

・ laravelファイルをZIPなどで圧縮
・ アップローダーでZipをアップ!
  teraTarmならクリックドロップ!
  XREAのファイルマネージャーなら、ZIP形式でアップロードを選択!
 (これだとアップロード時に解凍してくれる!)
・ 不要なファイル(phpunit)を削除!

teraTarm で zip を解凍するのは

$ unzip myProject.zip

XREAに、composer.pharでLaraveをインストールする方法

参考サイト様

下記様、大変にお世話になりました。ありがとうございます!
tanidaiz.com様
@haessal様
@t_mitarai様

SSH登録と許可

   説明
XREA無料版の登録 XREAにログインして無料版を申し込む
SSH登録 alue-domain GMO管理画面・コントロールパネル

サーバー/XREA

コントロールパネル/ウェブ

FTP設定で「SSH登録」を選択
SSH許可 XREA

サイト設定

ツール/セキュリティ

SSH接続IP許可

SSH接続

teraTermの場合なら
teraTerm起動
・ ドメイン(IPアドレス)
・ ユーザー(FTP設定のアカウント)
・ パスフレーズ(FTPのパスワード)
を入力

   説明
ドメイン alue-domain GMO

コントロールパネル

アカウント@サーバー/「IPアドレス XXX.XXX.XXX.XXX」のIPアドレス
UserとPass XREA

サイト設定

FTP設定」

アカウントとパスワード

composerの導入

説明はこちら-> composer installation

XREAの場合には
phpコマンドはCLI版を使うそうです。
php71cliとかphp72cliとかでコマンド!

$ php72cli -r "copy('...
$ php72cli -r "if(...
$ php72cli composer-setup.php
$ php72cli -r "unlink(...

の部分をteraTarmなどのターミナルで実行!

composerを起動

$ php72cli composer.phar
// ↓
// COMPOSERのかっこいい奴が表示される

Laravelの導入

@haessal様のご指導通りに

ファイル構成

 composer.phar
 laravel/myproject/
 public_html

laravel php / XREA php / ssh通信のphpのレベル

laravel ver を開発中のローカルのlaravel verと合わせるために、6.0以降を 5.5を導入します。
laravel 6.0 5.5 に対応するphpは 7.2 7.0以上ですので、XREAのphpバージョンもそうします。
phpバージョンが違うと、laraveの導入時にエラーになったり、導入後にサイト表示できなかったりします。
また、teratermで使用するphpcliも、larave導入に必要なversionを使いましょう。 今回は、php7.2 php7.0を使います。php72cli php70cli で、ゴー!

新しいバージョンをって思ったんですけど、うまくできなかったー

$ mkdir laravel
$ php72cli -d register_argc_argv=1 composer.phar create-project --no-dev --prefer-dist laravel/laravel myproject "5.5.*"
$ mv myProject laravel/myProject

シンボリックリンクする

ln -s ’リンク先’ ’documentRoute’
documentRouteは~/virtual/UserAccount/public_html

$ ln -s ~/UserAccount/laravel/PROJECT/public ~/UserAccount/public_html/(エリアス)

Error

Whoops, looks like something went wrong

phpのバージョンが違っているので、表示できません、っていうエラー?。

Whoops, looks like something went wrong.
(1/1) FatalThrowableError
Parse error: syntax error, unexpected '?', expecting variable (T_VARIABLE)
in Translator.php line 91

011128.JPG

XREAで使っているPHPバージョンと、導入したlaravelで使ってるPHPバージョンがあってなかった、ってことでした。

ローカルのlaravel XREAのlaravel XREAのphp teraTermのphp の各レベルは合わせるのが吉かな?

laravel導入時に、phpunit系のエラーが出て中断

php72cli -d register_argc_argv=1 composer.phar create-project --no-dev --prefer-dist laravel/.....

--no-devでインストールしないように指定したんだけれど、

Your requirments could not be resolvel to an installable set of packages

Problem 1
-phpunit/phpunit ...
...

というレッドエラーが出た。
すべてphpunitエラーなので、今回はインストールで--no-devは指定せず、エラー発生後にディレクトリーからphpunitを削除して、再インストールすることにしたよ。

削除方法はいろいろあるけれど、今回は、
XARE - サイト設定 - FTP設定 - ファイルマネージャー - laravelホルダ - phpunit 削除
で、他のファイルを確認しながら削除してみました。

再インストールね!

$ php72cli -d register_argc_argv=1 composer.phar create-project --prefer-dist laravel/laravel ...

http://www.UserDomain/myProject
にアクセスしたけれど HTTP500エラー orz

もう疲れたので、導入するlaravel version を 5.5 にして、phpもそれらに合わせることにしました(汗

無事に
011128-2.JPG

laravel version 6.0を導入しようとすると、インストールできず、エラーになるのはなんでだろ?

No such file or directory

これは迷いました!

調べてたら、転送する時の文字コードが違うのでは(エンコード)?とか出てきましたが、最終的にLaravelプロジェクトの名前がよくなかったよう。当初は laravel01 というプロジェクト名をアップしたら、実際にファイルはアップされているのですが、teraTarmでは認識されませんでした。プロジェクト名を laravelapp とか、数字の入ってないのに変更したらOKだった!

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

Laravel6.5 419 page expired にハマったとき

Laravel6.5でセッションがうまく機能しなかったとき

419エラーの原因は大体、form内に"@csrf"を書き忘れている場合だと思います。

しかし、CRSF対策のコードを記述してもエラーが解決しなかったので頭を悩ませましたが、

Laravel\project\storage\framework\sessions

のなかにあるセッションを保存しているファイルをすべて削除したら
正常に稼働しました。

ネット上ではいくら探しても見つからなかったため、記事に起こしておきます。

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

Symfony4 のインストールについて調べてみたメモ

PHPフレームワーク[Symfony4]をインストールした時のメモ書きです。

参考リンク

インストールする環境

  • Docker Desktop 2.1.0.4
  • mac osx
  • Symfony4

インストール手順

https://symfony.com/releases/4.4
最新のLTS が4.4.0 なので、これをインストールする。

$ mkdir project-name
$ cd project-name
$ git clone https://github.com/eko/docker-symfony.git
$ cd docker-symfony/
$ docker-compose up
cd symfony
rm -r ./var
$ cd ..
$ composer create-project symfony/website-skeleton symfony "4.4.*"

Installing symfony/website-skeleton (v4.4.99)
  - Installing symfony/website-skeleton (v4.4.99): Loading from cache
Created project in symfony
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing symfony/flex (v1.4.8): Loading from cache
Symfony operations: 1 recipe (7217b9f2de59952c53cbf0acf656d5ac)
  - Configuring symfony/flex (>=1.0): From github.com/symfony/recipes:master
Loading composer repositories with package information
Updating dependencies (including require-dev)
Restricting packages listed in "symfony/symfony" to "4.4.*"
Package operations: 102 installs, 0 updates, 0 removals
  - Installing ocramius/package-versions (1.4.2): Loading from cache
  - Installing symfony/asset (v4.4.0): Loading from cache
  - Installing symfony/dotenv (v4.4.0): Loading from cache
  - Installing psr/container (1.0.0): Loading from cache
  - Installing symfony/service-contracts (v1.1.8): Loading from cache
  - Installing psr/cache (1.0.1): Loading from cache
  - Installing symfony/var-exporter (v4.4.0): Loading from cache
  - Installing symfony/cache-contracts (v1.1.7): Loading from cache
  - Installing psr/log (1.1.2): Loading from cache
  - Installing symfony/cache (v4.4.0): Loading from cache
  - Installing symfony/expression-language (v4.4.0): Loading from cache
  - Installing symfony/inflector (v4.4.0): Loading from cache
  - Installing symfony/property-access (v4.4.0): Loading from cache
  - Installing symfony/polyfill-mbstring (v1.13.0): Loading from cache
  - Installing symfony/options-resolver (v4.4.0): Loading from cache
  - Installing symfony/intl (v4.4.0): Loading from cache
  - Installing symfony/polyfill-intl-icu (v1.13.0): Loading from cache
  - Installing symfony/event-dispatcher-contracts (v1.1.7): Loading from cache
  - Installing symfony/event-dispatcher (v4.4.0): Loading from cache
  - Installing symfony/form (v4.4.0): Loading from cache
  - Installing symfony/polyfill-php73 (v1.13.0): Loading from cache
  - Installing symfony/http-client-contracts (v1.1.8): Loading from cache
  - Installing symfony/http-client (v4.4.0): Loading from cache
  - Installing symfony/polyfill-php72 (v1.13.0): Loading from cache
  - Installing symfony/polyfill-intl-idn (v1.13.0): Loading from cache
  - Installing symfony/mime (v4.4.0): Loading from cache
  - Installing doctrine/lexer (1.0.2): Loading from cache
  - Installing egulias/email-validator (2.1.11): Loading from cache
  - Installing symfony/mailer (v4.4.0): Loading from cache
  - Installing symfony/process (v4.4.0): Loading from cache
  - Installing symfony/http-foundation (v4.4.0): Loading from cache
  - Installing symfony/var-dumper (v4.4.0): Loading from cache
  - Installing symfony/debug (v4.4.0): Loading from cache
  - Installing symfony/error-handler (v4.4.0): Loading from cache
  - Installing symfony/http-kernel (v4.4.0): Loading from cache
  - Installing symfony/security-core (v4.4.0): Loading from cache
  - Installing symfony/security-http (v4.4.0): Loading from cache
  - Installing symfony/security-guard (v4.4.0): Loading from cache
  - Installing symfony/security-csrf (v4.4.0): Loading from cache
  - Installing symfony/dependency-injection (v4.4.0): Loading from cache
  - Installing symfony/filesystem (v4.4.0): Loading from cache
  - Installing symfony/config (v4.4.0): Loading from cache
  - Installing symfony/security-bundle (v4.4.0): Loading from cache
  - Installing symfony/translation-contracts (v1.1.7): Loading from cache
  - Installing symfony/translation (v4.4.0): Loading from cache
  - Installing twig/twig (v2.12.2): Loading from cache
  - Installing symfony/twig-bridge (v4.4.0): Loading from cache
  - Installing symfony/twig-bundle (v4.4.0): Loading from cache
  - Installing symfony/routing (v4.4.0): Loading from cache
  - Installing symfony/finder (v4.4.0): Loading from cache
  - Installing symfony/framework-bundle (v4.4.0): Loading from cache
  - Installing twig/extra-bundle (v3.0.0): Loading from cache
  - Installing symfony/twig-pack (v1.0.0): Loading from cache
  - Installing symfony/validator (v4.4.0): Loading from cache
  - Installing psr/link (1.0.0): Loading from cache
  - Installing symfony/web-link (v4.4.0): Loading from cache
  - Installing doctrine/annotations (v1.8.0): Loading from cache
  - Installing sensio/framework-extra-bundle (v5.5.1): Loading from cache
  - Installing monolog/monolog (1.25.2): Loading from cache
  - Installing symfony/monolog-bridge (v4.4.0): Loading from cache
  - Installing symfony/stopwatch (v4.4.0): Loading from cache
  - Installing symfony/console (v4.4.0): Loading from cache
  - Installing zendframework/zend-eventmanager (3.2.1): Loading from cache
  - Installing zendframework/zend-code (3.4.0): Loading from cache
  - Installing ocramius/proxy-manager (2.1.1): Loading from cache
  - Installing doctrine/event-manager (1.1.0): Loading from cache
  - Installing doctrine/cache (1.9.1): Loading from cache
  - Installing doctrine/dbal (v2.9.3): Loading from cache
  - Installing doctrine/reflection (v1.0.0): Loading from cache
  - Installing doctrine/collections (1.6.4): Loading from cache
  - Installing doctrine/persistence (1.2.0): Loading from cache
  - Installing doctrine/inflector (1.3.1): Loading from cache
  - Installing doctrine/common (v2.11.0): Loading from cache
  - Installing doctrine/migrations (2.2.0): Loading from cache
  - Installing symfony/doctrine-bridge (v4.4.0): Loading from cache
  - Installing jdorn/sql-formatter (v1.2.17): Loading from cache
  - Installing symfony/yaml (v4.4.0): Loading from cache
  - Installing doctrine/doctrine-bundle (2.0.0): Loading from cache
  - Installing doctrine/doctrine-migrations-bundle (2.1.2): Loading from cache
  - Installing doctrine/instantiator (1.3.0): Loading from cache
  - Installing doctrine/orm (v2.7.0): Loading from cache
  - Installing symfony/orm-pack (v1.0.7): Loading from cache
  - Installing symfony/serializer (v4.4.0): Loading from cache
  - Installing symfony/property-info (v4.4.0): Loading from cache
  - Installing webmozart/assert (1.6.0): Loading from cache
  - Installing phpdocumentor/reflection-common (2.0.0): Loading from cache
  - Installing phpdocumentor/type-resolver (1.0.1): Loading from cache
  - Installing phpdocumentor/reflection-docblock (4.3.2): Loading from cache
  - Installing symfony/serializer-pack (v1.0.2): Loading from cache
  - Installing symfony/web-profiler-bundle (v4.4.0): Loading from cache
  - Installing symfony/profiler-pack (v1.0.4): Loading from cache
  - Installing easycorp/easy-log-handler (v1.0.9): Loading from cache
  - Installing symfony/monolog-bundle (v3.5.0): Loading from cache
  - Installing symfony/debug-bundle (v4.4.0): Loading from cache
  - Installing symfony/debug-pack (v1.0.7): Loading from cache
  - Installing nikic/php-parser (v4.3.0): Loading from cache
  - Installing symfony/maker-bundle (v1.14.3): Loading from cache
  - Installing symfony/phpunit-bridge (v5.0.0): Loading from cache
  - Installing symfony/css-selector (v4.4.0): Loading from cache
  - Installing symfony/dom-crawler (v4.4.0): Loading from cache
  - Installing symfony/browser-kit (v4.4.0): Loading from cache
  - Installing symfony/test-pack (v1.0.6): Loading from cache
Writing lock file
Generating autoload files
Symfony operations: 19 recipes (7217b9f2de59952c53cbf0acf656d5ac)
  - Configuring symfony/framework-bundle (>=4.4): From github.com/symfony/recipes:master
  - Configuring symfony/mailer (>=4.3): From github.com/symfony/recipes:master
  - Configuring symfony/security-bundle (>=4.4): From github.com/symfony/recipes:master
  - Configuring symfony/translation (>=3.3): From github.com/symfony/recipes:master
  - Configuring symfony/twig-bundle (>=4.4): From github.com/symfony/recipes:master
  - Configuring symfony/routing (>=4.2): From github.com/symfony/recipes:master
  - Configuring twig/extra-bundle (>=v3.0.0): From auto-generated recipe
  - Configuring symfony/validator (>=4.3): From github.com/symfony/recipes:master
  - Configuring doctrine/annotations (>=1.0): From github.com/symfony/recipes:master
  - Configuring sensio/framework-extra-bundle (>=5.2): From github.com/symfony/recipes:master
  - Configuring symfony/console (>=4.4): From github.com/symfony/recipes:master
  - Configuring doctrine/doctrine-bundle (>=1.12): From github.com/symfony/recipes:master
  - Configuring doctrine/doctrine-migrations-bundle (>=1.2): From github.com/symfony/recipes:master
  - Configuring symfony/web-profiler-bundle (>=3.3): From github.com/symfony/recipes:master
  - Configuring easycorp/easy-log-handler (>=1.0): From github.com/symfony/recipes:master
  - Configuring symfony/monolog-bundle (>=3.3): From github.com/symfony/recipes:master
  - Configuring symfony/debug-bundle (>=4.1): From github.com/symfony/recipes:master
  - Configuring symfony/maker-bundle (>=1.0): From github.com/symfony/recipes:master
  - Configuring symfony/phpunit-bridge (>=4.3): From github.com/symfony/recipes:master
ocramius/package-versions:  Generating version class...
ocramius/package-versions: ...done generating version class
Executing script cache:clear [OK]
Executing script assets:install public [OK]

Some files may have been created or updated to configure your new packages.
Please review, edit and commit them: these files are yours.

Some files may have been created or updated to configure your new packages.
Please review, edit and commit them: these files are yours.


 What's next?


  * Run your application:
    1. Go to the project directory
    2. Create your code repository with the git init command
    3. Download the Symfony CLI at https://symfony.com/download to install a development web server

  * Read the documentation at https://symfony.com/doc


 What's next?


  * You're ready to send emails.

  * If you want to send emails via a supported email provider, install
    the corresponding bridge.
    For instance, composer require mailgun-mailer for Mailgun.

  * If you want to send emails asynchronously:

    1. Install the messenger component by running composer require messenger;
    2. Add 'Symfony\Component\Mailer\Messenger\SendEmailMessage': amqp to the
       config/packages/messenger.yaml file under framework.messenger.routing
       and replace amqp with your transport name of choice.

  * Read the documentation at https://symfony.com/doc/master/mailer.html


 Database Configuration


  * Modify your DATABASE_URL config in .env

  * Configure the driver (mysql) and
    server_version (5.7) in config/packages/doctrine.yaml


 How to test?


  * Write test cases in the tests/ folder
  * Run php bin/phpunit

実行

http://symfony.localhost

スクリーンショット 2019-11-28 12.59.03.png


ひとまずここまで。

参考

書籍

  • PHPフレームワークSymfony4入門
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP(スーパーグローバル変数、セッション)

書いてあること

  • PHP(スーパーグローバル変数、セッション)
  • 忘れたとき用の自分メモ

関連Web

公式サイト

http://php.net/

仕様確認

http://php.net/manual/ja/index.php

PHPで利用可能なスーパーグローバル変数

変数名 内容
$_POST POST形式のHTMLフォームから渡された情報
$_GET クエリ情報(~?キー名=値)経由で渡された情報
$_FILES アップロードされたファイルに関する情報
$_SERVER リクエストヘッダ、またはサーバ固有の変数情報
$_ENV サーバ側で定義された環境変数
$_COOKIE クッキー経由で渡された情報
$_SESSION セッション経由で渡された情報
$_REQUEST \$_GET、\$_POST、\$_COOKIEの値をまとめて管理

セッションの利用

<?php

//セッション開始
session_start();

//セッション変数へ値を格納
$_SESSION['username'] = "nakamura";

//セッション変数の参照
echo $_SESSION['username'] = "nakamura";

//セッション変数の削除
unset($_SESSION['username']);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Stripe Checkoutで簡単な通販ページを作った話

この記事は、マイナビ Advent Calendar 2019 24日目の記事です。
アドベントカレンダー2本目は、様々な決済プラットフォームの中からStripe Checkoutを使って通販ページを作成した話をします。実装の話だけ読みたい方はこちら

背景

最近、友人と一緒に同人誌を作りました。
大阪の社会主義的な風景を映し出した写真集『OSAKAN SOCIALISM』
即売会や書店で売るだけでなく通販もやってみたいよねという話で盛り上がり、色々と決済の方法を検討してみました。

比較検討

通販をするにあたって、まずはいくつかの決済プラットフォームの比較を行いました。月間決済回数100件以下、決済単価1500円程度を想定。選定基準は、1.画面遷移が少ないこと 2.手数料が安く、料金体系がシンプルであること 3.準備(≒実装)が容易であることの3点です。
事前に調べて良さそうだったのは、PayPal、PAY.JP、Stripeの3サービスでした。BASEやSTORES.jpのようなオンラインストアの利用も考えましたが、なるべくシンプルな動線を実現したいと思ったので、今回は候補から外しています(取扱商品が増えたら検討したいです)。

PayPal PAY.JP Stripe
画面遷移 5ステップ 3ステップ 2ステップ
手数料 3.6%+40円 3.0% 3.6%
準備

PayPal

決済手数料は商品価格の3.6%+40円、少額決済(1回の取引あたり2,357円以下)の申請をすると5%+7円と、他2サービスに比べて割高。
PayPalはさまざまなサービスを展開していますが、今回は最もスタンダードなウェブペイメントスタンダードを検討しました。画面遷移は最低5ステップ。動線を短縮できるページ埋め込みも用意されていますが、別途利用料と審査が必要だったので今回は導入を見送りました。

自サイト→PayPalで会員登録(中で2,3ステップ)→PayPal決済画面→確認画面→支払い完了

準備は3ステップ。価格や商品名などの入力項目を埋めるとコードが生成されるので、ほとんど実装の必要はありません。

ビジネスアカウント登録→決済ボタンの作成→HTML内へ埋込

- 決済ボタンの作成方法|開発者向け-PayPal(ペイパル)

PAY.JP

決済手数料は商品価格の3.0%と今回比較するサービスの中でもっとも安価です。
PAY.JPの中でも導入コストが低く、画面遷移を少なくできるCheckoutを検討しました。ですが、さらに調査した結果、Checkoutには配送先住所など住所に関わるパラメータが用意されておらず、決済前後に住所の入力ページが必要なようだったので、こちらも導入を見送りました。

自サイト内のフォームで顧客情報を入力→自サイト内でカード情報を入力→決済完了

ちなみに準備は2ステップ。アカウント登録をしてHTML内にコードを埋め込めば、基本はOK。

アカウント登録→コードを生成してHTML内へ埋込

Stripe

決済手数料は商品価格の3.6%、料金体系もシンプルで分かりやすいです。
- Stripe: 料金体系 | 日本
動線をもっとも短くできるStripe Checkoutを検討しました。画面遷移は2ステップ。PAY.JP Checkoutと異なり、stripeShippingAddressという住所に関するパタメータが用意されています。

自サイト内で住所とカード情報を入力→決済完了

準備は3ステップ。ドキュメントやライブラリが十分に整備されているので、ハードルは高くありません。ただ、今回は諸事情からCheckoutのlegacy versionを採用したので、サーバー側の決済処理の実装があり何度か嵌りました。なお、new versionではサーバー側の実装が必要ないようです。

アカウント登録→クライアント側のコードを生成してHTML内へ埋込→サーバー側の実装

ということで、画面遷移が他サービスよりも少ない点、手数料や実装コストを踏まえて、Stripe Checkoutを導入することにしました。

実装

さっそくフロントの実装から。

<form action="./checkout.php" method="POST">
  <script src="https://checkout.stripe.com/checkout.js"
  class="stripe-button"
  data-key="pk_test_xxxxxxxxxxxxxxx"
  data-amount="7777"
  data-description="OSAKAN SOCIALISM"
  data-image="https://osakansocialism.h1den.com/images/favicon.png"
  data-locale="ja"
  data-currency="jpy"
  data-shippingAddress="true"
  data-zip-code="true"
  data-allow-remember-me="false"
  data-label="WEBでの購入">
  </script>
</form>

HTML内に上記のコードを埋め込んで決済を走らせてみたら、決済がダッシュボードに反映されず…どうやらトークンがStripeに飛んでいるだけのようです。

あくまでCheckoutを用いたフォーム部分だけの話で、そこで得たトークン情報を使ってSDKつないで支払い処理するのは別の話のようでした。
JavaScript - Stripe Checkoutでテスト決済が通らない|teratail

実はこの時まで、てっきりHTMLの埋め込みだけで動くものと思い込んでました… 既に上では書きましたが、Stripe Checkoutは”legacy”と”new”の2バージョンがあり、legacyではサーバー側の実装が別途必要です。
気を取り直して、ここからは公式ドキュメントを参照しながらバックエンドの実装をPHPでやっていきます。

まずはComposerを導入。その後、StripeのPHP向けのライブラリをインストール。

$ composer require stripe/stripe-php

ライブラリがインストールできたら、checkout.phpを実装していく。

ini_set( 'display_errors', 1 );
ini_set( 'error_reporting', E_ALL );

require_once( dirname(__FILE__).'/stripe/init.php');
\Stripe\Stripe::setApiKey("sk_test_xxxxxxxxxxxxxxx");

$token = $_POST['stripeToken'];
$email = $_POST['stripeEmail'];

$customer = \Stripe\Customer::create([
    'email' => $email,
    'source'  => $token,
]);

$charge = \Stripe\Charge::create([
  'customer' => $customer->id,
  'amount'   => 7777,
  'currency' => 'jpy',
  'description' => "OSAKAN SOCIALISM",
]);

header('Location: https://osakansocialism.h1den.com/');
exit;

こんな感じ。$customerで顧客が、$chargeで決済が作成されます。

途中、上のドキュメントの存在に気づかず実装してて、領収書が送れず嵌りましたが、最後はちゃんと送れるようになりました。良かった…
スクリーンショット 2019-11-27 14.24.53.png
こんな感じ。

スクリーンショット 2019-11-27 17.30.53.png
ダッシュボードの開発者ログでは、トークンの送信、顧客の作成、請求が正常に行われている様子が見れます。

そして実際はこういう動きをします。よさそう!

まとめ

途中、自分の理解不足による嵌りもありましたが、総じてスムーズに導入することができました。上で挙げたメリットのほかに、管理画面が使いやすかったり、定期支払いやクーポンの適用に対応していたりと良い感じなので、個人で制作活動をしている方は、オンラインストアと並行して導入検討してみてはいかがでしょうか。

謝辞

nagaramen、ありがとうございました。途中でいろいろと無茶言ってすいませんでした…

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

PHP(オブジェクト指向)

書いてあること

  • PHP(オブジェクト指向)
  • 忘れたとき用の自分メモ

関連Web

公式サイト

http://php.net/

仕様確認

http://php.net/manual/ja/index.php

クラス

クラスの定義

<?php
class [クラス名] {

  //プロパティ定義
  [アクセス修飾子] $プロパティ名;

  //静的プロパティ定義
  [アクセス修飾子] static $プロパティ名;

  //クラス定数
  const [定数名] = ;

  //クラスがインスタンス化されるタイミングで実行される
  [アクセス修飾子] function __construct() {
    処理;
  }

  //オブジェクトが破棄されるタイミングで実行される
  [アクセス修飾子] function __destruct() {
    処理;
  }

  //メソッド定義
  [アクセス修飾子] function [メソッド名]() {
    処理;
  }

  //静的メソッド定義
  [アクセス修飾子] static function [メソッド名]() {
    処理;
  }

  //セッター(カプセル化)
  public function setセッター名([データ型] $引数) {
    $this->プロパティ名 = $引数;
  }

  //ゲッター(カプセル化)
  public function getゲッター名() {
    print $this->$プロパティ名 ;
  }
}

クラスの利用

<?php
//クラスファイルの読込
require_once 'sample_class.php';

//クラスのインスタンス化
$変数名 = new クラス名(引数, );

//インスタンスメソッド、インスタンスプロパティの利用
$変数名(戻り値) = オブジェクト変数->メソッド名(引数, );
オブジェクト変数->プロパティ名 = ;

//静的メソッド、静的プロパティの利用
$変数名(戻り値) = クラス名::メソッド名(引数, );
クラス名::プロパティ名 = ;

//静的メソッド、静的プロパティを変数で利用
$クラス変数 = 'クラス名';
$変数名(戻り値) = $クラス変数::メソッド名(引数, );
$クラス変数::プロパティ名 = ;

継承、オーバーライド

オーバーライドとはスーパークラスで定義されたメソッドを、サブクラスで上書きすること。
スーパークラスのメソッドを明示的に呼び出す場合は、parentを利用する。

<?php
//継承元のクラスフアイル読み込み
require_once 'sample_class.php';

class [サブクラス名] extends [スーパークラス名] {

  //プロパティ定義
  [アクセス修飾子] $プロパティ名;

  //クラスがインスタンス化されるタイミングで実行される
  [アクセス修飾子] function __construct() {

  //スーパークラスのコンストラクタを起動
  parent::__construct();
    処理;
  }

  //メソッド定義
  [アクセス修飾子] function [メソッド名]() {

    //スーパークラスのメソッドを起動
    parent::メソッド名();

    処理;
  }
}

マジックメソッド

マジックメソッドとは、PHPであらかじめ特定の役割を与えられたメソッドのこと。
主なマジックメソッドは下記の通り。

メソッド 呼び出しのタイミング
__get 未定義のプロパティを取得しようとしたとき
__set 未定義のプロパティを設定しようとしたとき
__isset 未定義のプロパティをisset関数で処理しようとしたとき
__unset 未定義のプロパティをunset関数で処理しようとした時
__call 未定義のインスタンスメソッドコールをしたとき
__callStatic 未定義の静的メソッドをコールしたとき
__toString print命令などでオブジェクトの文字列表現を要求されたとき
__invoke オブジェクトが関数の形式で呼び出されたとき
__clone clone命令でオブジェクトを複製したとき
__debugINfo var_dump命令でオブジェクトをダンプしようとしたとき

名前空間

名前空間とクラスファイルを配置するディレクトリ名は一致させる必要がある。
例えば下記名前空間の場合、『アプリケーションルート/ディレクトリ1/ディレクトリ2/ディレクトリ3』に配置する。

名前空間の定義

<?php
namespacee [ディレクトリ1]\[ディレクトリ2]\[ディレクトリ3];

class [クラス名] {
  ・・・
}

名前空間の利用(非修飾名:名前空間を含まない名前)

<?php
namespace approot\dir1\dir2;

require_once 'MyClass.php';
print MyClass::showClass();

名前空間の利用(修飾名:名前空間の階層区切り文字を含んだ名前)

<?php
namespace approot\dir1;

require_once 'dir2/MyClass.php';
print dir2\MyClass::showClass();

名前空間の利用(完全修飾名:「\」で始まる修飾名)

<?php
require_once 'approot/dir1/dir2/MyClass.php';
print \approot\dir1\dir2\MyClass::showClass();

クラスのインポート

<?php
require_once 'approot/dir1/dir2/MyClass.php';
require_once 'approot/dir1/dir2/MyClass2.php';

use \approot\dir1\dir2\MyClass;
use \approot\dir1\dir2\MyClass2;

print MyClass::showClass();
print MyClass2::showClass();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP(組み込み関数)

書いてあること

  • PHP(組み込み関数)
  • 忘れたとき用の自分メモ

関連Web

公式サイト

http://php.net/

仕様確認

http://php.net/manual/ja/index.php

文字列関数

関数 概要
mb_internal_encoding 内部文字コードを指定する
mb_strlen 文字列の長さを取得する
strtolower 指定文字列を全て小文字にする
strtoupper 指定文字列を全て大文字にする
lcfirst 指定文字列の先頭のみ小文字にする
ucfirst 指定文字列の先頭のみ大文字にする
ucworlds 単語の区切りを全て大文字にする
mb_substr 部分文字列を取得する
str_replace 部分文字列を置換する
explode 文字列を特定の区切り文字で分割する
mb_strpos 特定の文字位置を検索する。最初に見つかった位置を返す
mb_strrpos 特定の文字位置を検索する。最後に見つかった位置を返す
printf 文字列を整形する
mb_convert_kana 文字列を変換する
mb_convert_encoding 文字エンコーディングを変換する
mb_convert_variables 文字エンコーディングを変換する。配列やオブジェクトなどの複合型の値を変換
mb_send_mail 電子メールを送信する

日付・時刻関数

関数 概要
checkdate 日付が妥当か判定
date タイムスタンプ値を指定フォーマットで整形
time 現在のタイムスタンプ値を取得
mktime 指定された年月日、時分秒に対応するタイムスタンプ値を取得
strtotime 与えられた日付文字列に対応するタイムスタンプ値を取得

配列関数

関数 概要
count 配列の要素数を取得する
array_merge 配列の内容を連結する
implode 配列の各要素を結合する
array_push 配列の末尾に追加
array_pop 配列の末尾から除去
array_shift 配列の先頭に追加
array_unshift 配列の先頭から除去
array_splice 配列に複数要素を追加/置換/削除する
array_slice 配列から特定範囲の要素を取得する
array_search 配列の内容を検索する
in_array 配列に特定の要素が存在するか確認する

ファイルシステム関数

関数 概要
fopen ファイルを開く
fclose ファイルを閉じる
fwrite ファイルへの書き込み
flock ファイルのロック
fgetcsv タブ区切りテキストの読み込み
fget
file
タブ区切りテキストの読み込み

ファイル情報取得関数

関数 説明
fileatime($path) 最終アクセス日時
filectime($path) 作成日時
basename($path) ファイル名
filemtime($path) 最終更新日時
dirname($path) ファイルパス(ファイル名除く)
filesize($path) ファイルサイズ
is_dir($path) ファイルがフォルダか?(true/false)
is_file($path) ファイルが通常ファイルか?(true/false)
is_link($path) ファイルがシンボリックリンクか?(true/false)

その他の関数

関数 概要
unset 変数を破棄する
is_array 変数のデータ型を判定する(配列型)
is_bool 変数のデータ型を判定する(論理型)
is_double
is_float
is_real
変数のデータ型を判定する(浮動小数点型)
is_int
is_integer
is_long
変数のデータ型を判定する(整数型)
is_null 変数のデータ型を判定する(null型)
is_numeric 変数のデータ型を判定する(数値型)
is_object 変数のデータ型を判定する(オブジェクト型)
is_resource 変数のデータ型を判定する(リソース型)
is_scalar 変数のデータ型を判定する(スカラー型)
is_string 変数のデータ型を判定する(文字列型)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP(標準クラスライブラリ)

書いてあること

  • PHP(標準クラスライブラリ)
  • 忘れたとき用の自分メモ

関連Web

公式サイト

http://php.net/

仕様確認

http://php.net/manual/ja/index.php

標準クラスライブラリの操作

<?php

//クラスのインスタンス化
$変数名 = new クラス名(引数, );

//インスタンスメソッド、インスタンスプロパティの利用
$変数名(戻り値) = オブジェクト変数->メソッド名(引数, );
オブジェクト変数->プロパティ名 = ;

//静的メソッド、静的プロパティの利用
$変数名(戻り値) = クラス名::メソッド名(引数, );
クラス名::プロパティ名 = ;

//静的メソッド、静的プロパティを変数で利用
$クラス変数 = 'クラス名';
$変数名(戻り値) = $クラス変数::メソッド名(引数, );
$クラス変数::プロパティ名 = ;

DateTimeクラス

<?php

//現在の日付・時刻から生成
$now = new DateTime();
print $now->format('Y年m月d日 H:i:s');

//指定したの日付・時刻から生成
//「YYYY-MM-DD」「YYYY-MM」「hh:mm:ss」「hh:mm」のような形式も認識可能
$now = new DateTime('2018/4/15 08:10:00');
print $now->format('Y年m月d日 H:i:s');

//年月日、時分秒を数値で個別に指定
$now = new DateTime();
$now->setDate(2018, 3,10);
$now->setTime(15, 35, 10);
print $now->format('Y年m月d日 H:i:s');

//タイムスタンプ値を設定
//タイムスタンプ値:1970年01月01日 00:00:00からの経過秒
$now = new DateTime();
$now->setTimestamp(time());
print $now->format('Y年m月d日 H:i:s');

//日付・時刻を指定フォーマットで成形
$now = new DateTime();
print $now->format('Y年m月d日 H:i:s');

//日付・時刻文字列を解析してDateTimeオブジェクトを生成
$fmt = 'Y年m月d日 H時i分s秒';
$time = '2018年4月16日 09時26分35秒';
$dt = DateTime::createFromFormat($fmt, $time);
print $now->format('Y年m月d日 H:i:s');

//日付・時刻を加算、減算
//加算、減算する値はDateIntervalクラス「P日付間隔T時刻間隔」で指定
$now = new DateTime();
print $now->format('Y年m月d日 H:i:s');
$dt->add(new DateInterval('P1YT10H'));
print $now->format('Y年m月d日 H:i:s');
$dt->sub(new DateInterval('P3MT20M'));
print $now->format('Y年m月d日 H:i:s');

//日付・時刻の差分を取得
$dt1 = new DateTime('2018/04/16 10:00:00');
$dt2 = new DateTime('2018/01/23');
$interval = $dt1->diff($dt2, true);
print $interval->format('Y年m月d日 H:i:s');

DateIntervalクラスによる日付・時刻間隔指定

単位 説明
Y
M
D
W 週(Dと同時に指定不可
H 時間
M
S

DirectoryIteratorクラス

<?php

//フォルダを開く
//ルートフォルダを開き、ファイルを順番に処理
$dir = new DirectoryIterator('./');
foreach ($dir as $file) {
  if ($file->isFile()) {
    //処理
  }
}

DirectoryIteratorクラスの主なメソッド

メソッド名 説明
getATime 最終アクセス日時
getCTime 作成日時
getFilename ファイル名
getMTime 最終更新日時
getPath ファイルパス(ファイル名除く)
getPathname ファイルパス(ファイル名含む)
getSize ファイルサイズ
isDir ファイルがフォルダか?(true/false)
isFile ファイルが通常ファイルか?(true/false)
isLink ファイルがシンボリックリンクか?(true/false)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP(ユーザー定義関数)

書いてあること

  • PHP(ユーザー定義関数)
  • 忘れたとき用の自分メモ

関連Web

公式サイト

http://php.net/

仕様確認

http://php.net/manual/ja/index.php

ユーザー定義関数

宣言

<?php
function 関数名(仮引数, ) {
  // 任意の処理
  return 戻り値;
}

引数、戻り値の型宣言

関数宣言時に引数、戻り値に明示的な型指定を行うことができる。

<?php
function 関数名(データ型 仮引数, ): 戻り値のデータ型 {
  // 任意の処理
  return 戻り値;
}

型宣言で利用できるデータ型

データ型 説明
bool 真偽値
float 浮動小数点数
int 整数
string 文字列
array 配列
callable コールバック関数
クラス、インターフェース名 指定されたクラス・インサーフェース
self 現在のクラス

外部スクリプトファイルの呼び出し

<?php
require_once 'PHPファイル名';

グローバル変数

<?php
global $変数名;

静的変数

<?php
static $変数名 = ;

引数の初期値を指定

<?php
function 関数名(データ型 仮引数 = 初期値, データ型 仮引数 = 初期値, ): 戻り値のデータ型 {
  // 任意の処理
  return 戻り値;
}

引数の参照渡し

仮引数の前に$記号を追記す

<?php
function 関数名(&仮引数, ) {
  // 任意の処理
  return 戻り値;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP(基本、変数、配列、演算子、制御構文)

書いてあること

  • PHP(基本、変数、配列、演算子、制御構文)
  • 忘れたとき用の自分メモ

関連Web

公式サイト

http://php.net/

仕様確認

http://php.net/manual/ja/index.php

PHPタグ(PHPの記載方法)

・HTML埋め込み

<?php
処理
?>

※PHP処理で該当ファイルのコーディングが終わる場合、終了タグは不要。

<?php
処理

コメント

・1行コメント

// コメント

・複数行コメント

/*
コメント
コメント
*/

変数

変数宣言

<?php
$変数名 = ;

表示

<?php
//表示
print "表示内容";

//配列の内容表示
print_r($配列名);

//データ型と値の表示
var_dump(表示内容配列);

変数の命名規則

  • 「$変数名」の形式
  • 1文字目はアルファベット、アンダースコアのいずれか
  • 2文字目以降はアルファベット、数字、アンダースコアのいずれか
  • 大文字、小文字は区別する
  • 予約語でないこと

変数の記法

下記の記法がある。
PHPでは通常、クラス(コンストラクタ)・インターフェースはパスカル記法、変数・関数はキャメルケース記法を利用する

記法 説明 表記例
キャメルケース(Camelcase)記法 単語の区切りは大文字で表記。ただし先頭のみ小文字 myApp
パスカル(Pascal)記法 単語の区切りは大文字で表記。先頭も大文字 MyApp
アンダースコア記法 全ての単語を小文字で表記。区切りはアンダースコア my_app

定数

定数宣言

<?php
const 定数名 = ;

定数の命名規則

  • 先頭に「$」記号を付ける必要はない
  • 予約後でないこと
  • すべて大文字で記載(構文規則ではなく、通例)

定義済の定数

定数 説明
__FILE__ 実行中のファイル名(絶対ぱす)
__DIR__ 実行中のファイルが存在するフォルダ
__LINE__ 実行中の行番号
__FUNCTION__ 実行中の関数名
__CLASS__ 実行中のクラス名(所属する名前空間を含む)
__METHOD__ 実行中のメソッド名
__TRAIT__ 実行中のトレイと名(所属する名前空間を含む)
__NAMESPACE__ 現在の名前空間
__DIRECTORY_SEPARATOR__ フォルダの区切り文字
Windowsの場合は「¥」、Linuxの場合は「/」
__PATH_SEPARATOR__ パスの区切り文字
Windowsの場合は「;」、Linuxの場合は「:」
__PHP_VERSION__ 使用しているPHPバージョン

データ型

分類

分類 説明
スカラー型 1つの変数で1つの値だけを扱う
複合型 1つの変数で複数の値を扱う
特殊型 スカラー型、複合型のいずれにも分類されない特殊な型

データ型

データ型 分類 説明
論理リテラル(bool) スカラー型 真(true)、偽(false)の状態を表す。PHPでは次の値をfalseとみなす。
・空文字列、または文字列の0
・整数リテラルの0
・浮動小数点リテラルの0.0
・要素数が0である空の配列
・null(未定義)
整数リテラル スカラー型 日常的に使っている整数の表現で正数、0(ゼロ)、負数を扱うことができる。
プレフィックスに2進数なら「0b」、8進数なら「0」、16進数なら「0x」を設定する
文字列リテラル スカラー型 シングルクォート、またはダブルクォートでくくって定義する。
シングルクォート、またはダブルクォートを文字列内で使用する場合はエスケープ文字(「\」バックスラッシュ)を付与する
配列型(array) 複合型 データの集合
オブジェクト型(object) 複合型 データと手続きの集合
リソース型(resource) 特殊型 外部リソースへの参照
ヌル型(null) 特殊型 値が未定義であることを表す

文字列

エスケープ文字

シングルクォート、またはダブルクォートを文字列内で使用する場合はエスケープ文字(「\」バックスラッシュ)を付与する

シングルクォートとダブルクォートの違い

変数を展開するかどうか
シングルクォートは展開しない、ダブルクォートは展開する

エスケープシーケンスを認識するかどうか

エスケープシーケンス 説明
\r 復帰(キャリッジリターン)
\n 改行
\t 水平タブ
\v 垂直タブ
\f フォームフィード
\$ ドル記号
\\ バックスラッシュ
\" ダブルクォート
\nnn 3桁の8進数nnnで表された文字
\xnn 2桁の16進数nnnで表された文字
\U{nnnn} 4桁の16進数nnnnで表されたUnicode文字列

ヒアドキュメント

「<<<EOD」から「EOD;」までを文字列リテラルとみなす。
「EOD」は文字列の開始・終了を表すデリミタ(区切り文字)のため、開始・終了が対応していれば適当な文字でもよい。

<?php
$msg = <<<EOD
これはヒアドキュメントです。
対応していれば開始・終了は適当な文字列を設定することが可能です。
EOD;

配列

<?php
//配列宣言
$配列名 = [値1, 値2, ];

//配列参照
$配列名[添字]

//配列代入
$配列名[添字] = ;

//配列代入(添字なしの場合は最後に追加)
$配列名[] = ;

//利用例
$data = ['中村', '山田', '掛谷', '日尾', ];
$data[1] = '薄井';
$data[] = '矢吹';
print $data[0]; //中村
print $data[1]; //薄井
print $data[4]; //矢吹
print_r($data); //配列の内容確認

連想配列

<?php
//連想配列宣言
$連想配列名 = [キー1 => 値1, キー2 => 値2, ];

//連想配列参照
$連想配列名[キー]

//連想配列代入
//既存キーの場合は値を更新、新規キーの場合は値を追加
$連想配列名[キー] = ;

//利用例
$members = [
    '中村テスト1' => '神奈川県川崎市幸区…',
    '中村テスト2' => '神奈川県川崎市高津区…',
];
$members['中村テスト1'] = '埼玉県熊谷市…';
$members['中村テスト3'] = '神奈川県川崎市川崎区…';
print $members['中村テスト1']; // 埼玉県熊谷市…
print $members['中村テスト3']; // 神奈川県川崎市川崎区…

多次元配列

<?php

//利用例①
$dimension1 = [
    ['X-1', 'X-2', 'X-3'],
    ['Y-1', 'Y-2', 'Y-3'],
    ['Z-1', 'Z-2', 'Z-3'],
];

//利用例②
$dimension2 = [
    [
        'name' => '山田太郎',
        'age' => 35,
        'sex' => '男',
    ],
    [
        'name' => '鈴木二郎',
        'age' => 30,
        'sex' => '男',
    ],
    [
        'name' => '佐藤花子',
        'age' => 25,
        'sex' => '女',
    ],
];

データ型の変換(キャスト)

データ型 説明
(int)、(integer) 整数型へおキャスト
(bool)、(boolean) 論理型へのキャスト
(float)、(double)、(real) 浮動小数点型へのキャスト
(string) 文字列型へのキャスト
(binary) バイナリ文字列へのキャスト
(array) 配列へのキャスト
(object) オブジェクトへのキャスト
(unset) null型へのキャスト
bindec 2進数を10進数に変換
octdec 8進数を10進数に変換
hexdec 16進数を10進数に変換

代数演算子

演算子 説明
+ 数値の和
- 数値の差
* 数値の積
/ 数値の商
% 数値の剰余
** 数値の累乗
++ 前置加算(代入前に加算)
++ 後置加算(代入後に加算)
-- 前置減算(代入前に減算)
-- 後置減算(代入後に減算)

代入演算子

演算子 説明
= 変数などに値を代入
+= 左辺と右辺を加算した結果を代入
-= 左辺から右辺を減算した結果を代入
*= 左辺と右辺を乗算した結果を代入
**= 左辺を右辺で累乗した結果を代入
/= 左辺を右辺で除算した結果を代入
%= 左辺を右辺で除算した余りを代入
.= 左辺と右辺を連結した文字列を代入
&= 左辺と右辺をビット論理積した結果を代入
\ =
^= 左辺と右辺をビット排他論理和した結果を代入
<<= 左辺を右辺の値だけ左シフトした結果を代入
>>= 左辺を右辺の値だけ右シフトした結果を代入

値の代入

<?php
$x = 1;
$y = $x;
$x = 5;
print $y; // 1

参照の代入(&を記述するとメモリ上のアドレスがコピーされる)

<?php
$x = 1;
$y = &$x;
$x = 5;
print $y; // 5

比較演算子

演算子 説明
== 左辺と右辺の値が等しい場合はtrue
=== 左辺と右辺の値が等しく、かつ同じデータが型である場合はtrue
!= 左辺と右辺の値が等しくない場合にtrue
<> 左辺と右辺の値が等しくない場合にtrue
!== 左辺と右辺の値が等しくない、または同じデータ型でない場合にtrue
< 左辺が右辺より小さい場合にtrue
> 左辺が右辺より大きい場合にtrue
<= 左辺が右辺以下の場合にtrue
>= 左辺が右辺以上の場合にtrue
<=> 宇宙船演算子。左辺が右辺より小さい場合は-1、左辺と右辺が等しい場合は0、
左辺が右辺より大きい場合は1
?: 条件演算子。「条件式 ? 式1 : 式2」と記述することで、条件式がtrueの場合は式1、falseの場合は式2となる
?? null合体演算子。「左辺 ?? 右辺」と記述することで、左辺がnullでなければその値、左辺がnullの場合は右辺の値となる

論理演算子

演算子 説明
&& 論理積。左右の式がともにtrueの場合にtrue
and 論理積。左右の式がともにtrueの場合にtrue
\ \
or 論理和。左右の式のいずれかがtrueの場合にtrue
xor 排他的論理和。左右の式いずれかがtrueで、かつ双方ともtrueではない場合にtrue
! 否定。式がtrueの場合はfalse、falseの場合はtrue

ビット演算子

演算子 説明
& 論理積。左式/右式の双方にセットされているビットをセット
\
^ 排他的論理和。左式/右式のいずれかでセットされており、かつ双方にセットされていないビットをセット
~ 否定。ビットを反転
<< ビットを左にシフト
>> ビットを右にシフト

その他の演算子

演算子 説明
. 文字列演算子。左式/右式の文字列を連結
`` 実行演算子。バッククォートで囲んだブロックをシェルコマンドとして実行
@ エラー制御演算子。特定の式の先頭に付加することで、その命令で発生したエラーメッセージを抑止する

条件分岐

<?php

//条件分岐(if)
if (条件式) {
  処理;
} elseif (条件式) {
  処理;
} else {
  処理;
}

//条件分岐(switch)
switch () {
  case 値1:
    処理;
    break;
  case 値2:
    処理;
    break;
  default:
    処理;
    break;
}

繰り返し

<?php

//前判定
while (条件式) {
  trueの間繰り返す処理;
}

//後判定
do {
  trueの間繰り返す処理;
} while (条件式);

//指定回数繰り返し
for ($変数名 = ; 条件式; 増減式) {
  繰り返す処理;
}

//配列を先頭から順に処理
foreach (配列 as 値変数) {
  繰り返す処理;
}

//連想配列を先頭から順に処理
foreach (連想配列 as キー変数 => 値変数) {
  繰り返す処理;
}

//繰り返しを抜ける
break;

//繰り返し処理を1回スキップ
continue;

HTML内での制御構文

PHPタブとHTMLは入れ子に記述することができる

<?php for ($i = 1; $i < 6; $i++) { ?>
  <p>こんにちは、世界!</p>
<?php } ?>

制御命令の別構文

if、switch、while、for、foreachはカッコ({〜})の代わりに、開始の中カッコをコロン(:)、し終了の中カッコを
「endif;」、「endswitch;」、「endwhile;」、「endfor;」、「endforeach;」で記述することができる

<?php
if (条件式) :
  処理;
endif;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでC#アプリケーションとデータをやり取りするためのAPIを作ってみる

はじめに

当記事では、C#アプリケーションとデータをやり取りするAPIを作成して実際に動かしてみます。
C#アプリケーションから、ライブラリを使用して直接データベースに接続することも可能ですが、
C#等の高級言語は「リバースエンジニアリングしやすい」といった特徴や、そもそも外部からの接続を許してしまうデータベースはセキュアではないので、基本的にはAPIを通す必要があります。

環境

Visual Studio 2019 Professional
Windows 10 1903
.NET Framework: 4.7.2
XAMPP: 7.3.7
PHP: 7.3.7
Apache: 2.4.39
MariaDB: 10.3.16

データベース

テーブルを新規作成します。
今回は例として以下の構造で作成しました。
23358688b8684b29fe96195c69bfedf5.png

PHP側

今回作成するのは以下3つのファイルです。

class.php
database.php
index.php

PHPのフルソースコードは以下の通りです。
今回はあくまで一例として行いますので、複数のデータが見つかった場合や例外エラーの処理は一切していません。

ソースコード

database.php
<?php
class database{
    public $db;

    private $db_host = "localhost";
    private $db_user = "root";
    private $db_password = "";
    private $db_name = "testdb";

    public function connect()
    {
        $this->db = new PDO("mysql:host=" . $this->db_host . ";dbname=" . $this->db_name, $this->db_user, $this->db_password);

        if(!$this->db){
            die("Failed to connect");
        }
    }
}
?>
class.php
<?php
include "database.php";
class _main_ extends database
{
    public function __construct()
    {
        $this->connect();
    }

    public function InsertData($data)
    {
        if (empty($data)) {
            die('{"result":"failed"}');
        } else {
            $query = $this->db->prepare("INSERT INTO data_table (data) VALUES ('$data')");
            $query->execute();

            die('{"result":"success"}');
        }
    }

    public function GetData()
    {
        $query = $this->db->prepare("SELECT * FROM data_table LIMIT 1");
        $query->execute();
        $result = $query->fetch(PDO::FETCH_ASSOC);

        if (isset($result)) {
            $data = $result["data"];
            die('{"result":"success", "data":"' . $data . '"}');
        } else {
            die('{"result":"failed"}');
        }
    }
}

?>
index.php
<?php
//ini_set( 'display_errors', 1 );
//error_reporting(E_ALL);

include "class.php";
$main = new _main_;
if(isset($_POST['type'])) $Type = $_POST['type'];
if(isset($_POST['data'])) $Data = $_POST['data'];

if(isset($Type))
{
    switch (strip_tags($Type))
    {
        case "t_insert_data": $main->InsertData($Data); break;
        case "t_get_data": $main->GetData(); break;
    }
}
?>

C#側

.NET Frameworkを使用します。
Jsonを扱うので、Newtonsoft Jsonを予めNuGetでインストールして下さい。
18c8268b08b08a0109e7e3c8140708bd.png

ソースコード

using(WebClient wc = new WebClient()) { }としているのは、必ずリソースが破棄されるからです。
NameValueCollectionに値を格納し、それを渡す感じです。
Encoding.Default.GetString(wc.UploadValues(URL, Values));でPOSTし、返ってきた値を関数の返り値としています。
一応、WebExceptionはキャッチします。

Handle.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace datatest
{
    class Handler
    {
        //ポスト先のURL
        public static string URL { get; set; }

        //コンストラクタ
        public Handler() { }

        public static string DoPost(NameValueCollection Values)
        {
            try
            {
                using(WebClient wc = new WebClient())
                {
                    return Encoding.Default.GetString(wc.UploadValues(URL, Values));
                }
            }
            catch (WebException e)
            {
                MessageBox.Show(e.Message);
                Dictionary<string, object> ERROR = new Dictionary<string, object>();
                HttpWebResponse response = (HttpWebResponse)e.Response;
                switch (response.StatusCode)
                {
                    case HttpStatusCode.NotFound:
                        ERROR.Add("result", "net_error_not_found");
                        break;
                    case HttpStatusCode.RequestEntityTooLarge:
                        ERROR.Add("result", "net_error_request_entry_too_large");
                        break;
                    case HttpStatusCode.ServiceUnavailable:
                        ERROR.Add("result", "net_error_service_unavailable");
                        break;
                    case HttpStatusCode.Forbidden:
                        ERROR.Add("result", "net_error_forbidden");
                        break;
                    default:
                        ERROR.Add("result", "net_error_unknown" + Environment.NewLine + e.Message);
                        break;
                }
                return JsonConvert.SerializeObject(ERROR);
            }
        }
    }
}

フォームがロードされたタイミングでハンドラーのURLをセットし、
NameValueCollectionに各値を格納してHandler.DoPost()するだけです。
Stringとして帰ってくるので、JObject.Parse()でパースする必要があります。

Form1.cs
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace datatest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Handler.URL = "http://localhost/api/index.php";
        }

        private static void InsertData(string data)
        {
            var values = new NameValueCollection();
            values["type"] = "t_insert_data";
            values["data"] = data;
            string result = Handler.DoPost(values);
            if (result != "")
            {
                var jobj = JObject.Parse(result);
                switch ((string)jobj["result"])
                {
                    case "success":
                        MessageBox.Show("Success!");
                        break;
                    case "failed":
                        MessageBox.Show("Failed!");
                        break;
                    default:
                        MessageBox.Show("Unknown error!");
                        break;
                }
            }
        }

        private static void GetData()
        {
            var values = new NameValueCollection();
            values["type"] = "t_get_data";
            string result = Handler.DoPost(values);
            MessageBox.Show(result);
            if (result != "")
            {
                var jobj = JObject.Parse(result);
                switch ((string)jobj["result"])
                {
                    case "success":
                        MessageBox.Show("Success!" + Environment.NewLine + "Data: " + (string)jobj["data"]);
                        break;
                    case "failed":
                        MessageBox.Show("Failed!");
                        break;
                    default:
                        MessageBox.Show("Unknown error!");
                        break;
                }
            }
        }

        private void BtnInsert_Click(object sender, EventArgs e)
        {
            InsertData("SETO_KOUJI");
        }

        private void BtnGet_Click(object sender, EventArgs e)
        {
            GetData();
        }
    }
}

実行結果

InsertData
f50400ab75c092e9cca8fee357cbb5b6.png

GetData
18ca9f79fe35d70f50a497c066a4035b.png

無事データのやり取りを行うことができました。
このとき、データベースの中身は以下のようになっています。

e57922efa41d6387cf2f38c3677cc15a (1).png

正しい値を取得できていますね。

最後に

今回は、C#アプリケーションとデータベースでのやり取りにAPIを使用してみました。
APIを使用しても、肝心の通信の中身が暗号化されていなければセキュアであるとは言えないですし、中間者攻撃も容易にできてしまいます。
実際に実装して運用する際は十分な注意を払う必要がありますね。

通信内容の暗号化に関しては、セッションを利用して共通鍵方式で暗号化するのも手段の一つだと思います。

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