20200702のPHPに関する記事は11件です。

【PHP】添字配列をarray_mergeすると、配列の連番が0からになる

添字配列をarray_mergeすると、配列の番号が0からの連番になる

// 2つの配列をarray_mergeする
$array1 = [
    '101' => 'red',
    '103' => 'yellow'
];

$array2 = [
    '100' => 'blue'
];

予想する返却値

array(3) {
  [100]=>
  string(3) "blue"
  [101]=>
  string(6) "red"
  [103]=>
  string(4) "yellow"
}

実際の返却値

$array_merge = array_merge($array1, $array2);

var_dump($array_merge);

// 実行結果
array(3) {
  [0]=>
  string(3) "red"
  [1]=>
  string(6) "yellow"
  [2]=>
  string(4) "blue"
}

例えば

以下のような時は注意が必要です。

  • 年数を配列のkeyにしている時
  • 「01」や「001」のような値をkeyにしている時
    • 「10」や「100」のように桁が上がったときにバグる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP7.4開発環境をUbuntuに作成

前提条件

  • virtualboxをインストール済
  • vagrantをインストール済
Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://vagrantcloud.com/search.
  config.vm.box = "ubuntu/xenial64"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # NOTE: This will enable public access to the opened port
  config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine and only allow access
  # via 127.0.0.1 to disable public access
  # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  config.vm.synced_folder "./share_folder", "/var/www"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  config.vm.provider "virtualbox" do |vb|
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
      vb.memory = "2048"
  end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

  # Enable provisioning with a shell script. Additional provisioners such as
  # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   apt-get update
  #   apt-get install -y apache2
  # SHELL
end

Ubuntu立ち上げ

プロジェクトディレクトリに上記のVagrantfileを設置後
ubuntuにssh接続

$ vagrant up
$ vagrant ssh

mysql5.7インストール

#パッケージをアップデート
$ sudo apt update
# mysql5.7をインストール
$ sudo apt install mysql-server-5.7
#接続確認
$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 5.7.30-0ubuntu0.16.04.1 (Ubuntu)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

php7.4をインストール

ubuntuにPPAを追加

$ sudo add-apt-repository ppa:ondrej/php
$ sudo apt update

php7.4をインストール

$ sudo apt install php7.4
#確認
$ php -v
PHP 7.4.7 (cli) (built: Jun 12 2020 07:43:48) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.7, Copyright (c), by Zend Technologies

compserをインストール

#Composer公式サイトのコードをコピペ
$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
   php -r "if (hash_file('sha384', 'composer-setup.php') === 
  'e0012edf3e80b6978849f5eff0d4b4e4c79ff1609dd1e613307e16318854d24ae64f26d17af3ef0bf7cfb710ca74755a') { echo 'Installer verified'; } else { echo 
  'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
  php composer-setup.php
  php -r "unlink('composer-setup.php');"

#composerを移動しpathを通す
$ mv composer.phar /usr/local/bin/composer

#確認
$ composer --version
Composer version 1.10.8 2020-06-24 21:23:30

PHP動作確認

apacheのデフォルト公開ディレクトリは/var/www/htmlなので、
/var/www/html以下にindex.phpを作成しphpの動作確認

$ cd /var/www
$ mkdir html
$ vi index.php
index.php
<?php

echo 'hello php';

http:192.168.33.10にアクセスし
hello phpが表示されていればOK

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

ラズパイで温湿度とCPU温度の取得とグラフの表示

エアコンをラズパイから操作できるようになったので、室温と湿度をから自動的にエアコンをつけたいと思った。

温湿度は前の記事を参考にBME280で取得する。
ついでにラズパイのCPU温度も取得してグラフにする。

まずは完成したグラフがこちら
スクリーンショット 2020-07-02 16.14.32.png

コード

window.onload = function() {
  view();
  let sl = document.querySelector("select.date")
  sl.value = today();
}

function view(date) {
  if (!date) date = today();
  let room_temp = [];
  let room_hum = [];
  let cpu = [];
  let labels = []

  fetch('http://192.168.3.2/temperature/getData.php?date=' + date)
    .then(function(response) {
      return response.text();
    })
    .then(function(myJson) {
      // console.log(myJson)
      myJson = JSON.parse(myJson);
      // console.log(myJson)
      myJson.forEach((e) => {
        room_temp.push(parseFloat(e.room_temp));
        room_hum.push(parseFloat(e.room_hum));
        cpu.push(parseFloat(e.cpu));
        labels.push(getDatetime(new Date(Number(e.time * 1000))))
      })
      graf(labels, room_temp, room_hum, cpu, date)
    });
}

function change() {
  let sl = document.querySelector("select.date")
  view(sl.value);
}

function getDatetime(now) {
  let Hour = ("0" + now.getHours()).slice(-2);
  let Min = ("0" + now.getMinutes()).slice(-2);
  let Sec = ("0" + now.getSeconds()).slice(-2);
  return Hour + ":" + Min;
}

function today(date) {
  let now = new Date();
  let Y = now.getFullYear();
  let M = ("0" + (Number(now.getMonth()) + 1)).slice(-2);
  let D = ("0" + now.getDate()).slice(-2);
  return date = Y + '_' + M + '_' + D;
}

function graf(labels, room_temp, room_hum, cpu, date) {

  let ctx = document.getElementById('myChart').getContext('2d');

  let myChart = new Chart(ctx, {
    type: "line",
    data: {
      labels: labels,
      datasets: [{
        label: "室温",
        data: room_temp,
        borderColor: "rgb(255, 99, 132)",
        backgroundColor: "rgba(255, 99, 132, 0)"
      }, {
        label: "湿度",
        data: room_hum,
        borderColor: "rgb(54, 162, 235)",
        backgroundColor: "rgba(54, 162, 235, 0)"
      }, {
        label: "CPU",
        data: cpu,
        borderColor: "rgb(120, 120, 120)",
        backgroundColor: "rgba(54, 162, 235, 0)"
      }]
    },
    options: {
      tooltips: {
        callbacks: {}
      },
      title: {
        display: true,
        text: '温湿度とラズパイ温度 ' + date
      },
      elements: {
        line: {
          tension: 0
        }
      },
      scales: {
        xAxes: [{
          type: 'time',
          time: {
            parser: "mm:ss",
            unit: 'minute',
            unitStepSize: 1,
            displayFormats: {
              'minute': 'mm',
            },

          }
        }],
        yAxes: [{
          ticks: {
            beginAtZero: true
          },

        }]
      }
    }
  });
}

解説

DB

+-----------+---------+------+-----+---------+----------------+
| Field     | Type    | Null | Key | Default | Extra          |
+-----------+---------+------+-----+---------+----------------+
| id        | int(11) | NO   | PRI | NULL    | auto_increment |
| room_temp | float   | YES  |     | NULL    |                |
| room_hum  | float   | YES  |     | NULL    |                |
| cpu       | float   | YES  |     | NULL    |                |
| time      | int(11) | YES  |     | NULL    |                |
+-----------+---------+------+-----+---------+----------------+

timeはUnixタイムスタンプ(秒)

javascriptについて

DBからデータを取ってくる関数がこちら。

function view(date) {
  if (!date) date = today();
  let room_temp = [];
  let room_hum = [];
  let cpu = [];
  let labels = []

  fetch('http://192.168.3.2/temperature/getData.php?date=' + date)
    .then(function(response) {
      return response.text();
    })
    .then(function(myJson) {
      // console.log(myJson)
      myJson = JSON.parse(myJson);
      // console.log(myJson)
      myJson.forEach((e) => {
        room_temp.push(parseFloat(e.room_temp));
        room_hum.push(parseFloat(e.room_hum));
        cpu.push(parseFloat(e.cpu));
        labels.push(getDatetime(new Date(Number(e.time * 1000))))
      })
      graf(labels, room_temp, room_hum, cpu, date)
    });
}

撮ってきたデータをオブジェクトに格納するのだが、chart.jsが処理できるデータにする必要があるのでここでは以下のような操作をしている。

  • 温度、湿度それぞれの配列へデータを入れていくがその際、DBからの値を文字列から数字に変換して格納している。
  • X軸として配列labelsへ自覚情報を渡しているが、DBに登録しているUnixタイムスタンプは秒なのでミリ秒に直している。

そうしてできたデータをグラフを表示する関数grafへ渡している。

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

nginx1.18 + php7.4.5 エラー対処メモ

目次

  • 前置き
  • 環境

前置き

これはあくまで自分用のメモであり、また行う場合は必ずバックアップをとること

環境

  • Amazon Linux2
  • nginx1.18
  • php7.4.5

エラー

404

  • nginxがnginx.confで設定したrootに対してのパーミッション確認
  • /etc/php-fpm.d/www.confをuser:nginx group:nginx にする
    • その後sudo systemctl restart php-fpm でphp-fpmを再起動
  • /var/log/php-fpmの所有ユーザーは初期ではapacheになっている
    • chown nginx: php-fpm でnginxに変更する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

稼ぎたいなら食わず嫌いしない。上に立つ人こそ色々と試そう。

今時のリーダーは、率先して色々と試し、失敗し、部下を成功の最短ルートで動かすものだ。
これはIT業界に限ったことではない。

フリーランスで年収を上げるのも、専門技術の高さより、幅広い技術の方が年収を上げやすい。
また、副業で稼ぐにも、幅広い技術がいい。

私は旅行代理店のシステム開発で副業していたことがあるが、その時は、PHPとRubyだった。
フリーランスのメインの仕事では、Javaだった。

副業、クラウドソーシングでは、PHPばっかである。
ただ、フリーランスの仕事では、PHPはかなり減った。

PHPの将来性を検索すれば、残るという意見も無くなるという意見もあるが、
それは、副業で考えるか、フリーランスの仕事で考えるか、で変わってくる。

結局、自分で試して、自分で考えるしかない。
この試すという手間を検索して、人の意見を聞いて終わりにしないということである。

Ruby が出たばかりのころ、Perl と Python が消えると騒がれた。
私は全部試して、業務でも使用した。
Ruby の急成長は予想できなかったが、Perl が消える予想は当たった。
当時、Perl の将来性を検索したら、使用している企業が多いから、消えることはないという意見が多かった。

私が消えると言っているのは、稼げないという意味である。

Rust は良い言語だが、PHP はダメだ。 みたいなのは、思い込みである。
PHPの方が楽だし、稼げる。
言いたいことは、状況によるから決めつけるなということ。
そして、自分で試して、情報に流されないということ。

今時、開発言語の選定はかなり難しい。
検索して判断せずに、是非、試してから判断して欲しい。

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

【Laravel】自作ヘルパー

ヘルパーを自作する

ページのタイトル名を返すヘルパーを例にヘルパーの作り方を説明してみる
手順は以下のとおり

 1. helpers.phpを作成
 2. composer.jsonへの追加
 3. composer dump-autoload コマンドの実行

手順1

appフォルダ以下にhelper.phpを作って、作りたいヘルパーを定義

app/helper.php
<?php
function full_title($title=null) {
    $app_name = config('app.name', 'Laravel');
    if ($title != null) {
        return $title . ' - ' . $app_name ;
    }
    return $app_name;
}
?>

手順2

composer.jsonのautoloadの項目にヘルパーを定義したファイルのパスを追加

composer.json
"autoload": {
        ...(省略)...
        "files": [
            "app/helpers.php"
        ]
    }

手順3

composer.jsonにパスを追加しただけでは機能しない
以下のコマンドを実行

% composer dump-autoload

使用例

<title>full_title('Home')</title>

上の例では「Home - Laravel」といったタイトルになる

おわり

簡単でしたね
Laravel側で用意しているヘルパーがたくさんあるので、Readoubleを一読してみてはどうかな?

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

MySQLではなく、MySQLi

MySQLiとは「MySQL improved extension」の略で、PHPからMySQL/MariaDBデータベースを操作するためのインターフェースを提供する、PHPの拡張機能です。
MySQLとの違いは、オブジェクト指向の記述に対応したことです。
なのでMySQLiを使うにはまずオブジェクト(インスタンス)を生成し、メソッドを使ってSQLの実行をする形になります。

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

Laravel Log singleログとdailyログを同時に出力する

目的

  • Laravelのログ出力にて二種のログを同時出力する方法をまとめる

実施環境

  • MacのローカルにLaravelの環境を構築して実施する。環境構築手順の記事はリンクを後述する。
  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.3 Homwbrewを用いて導入
Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提条件

前提情報

  • ログの出力に関して若干の知識がある物として説明を行う。おそらく一旦ログの出力を経験した方が理解が早いかもしれない。下記にログ出力方法の最短方法を記載した記事のリンクを記載する。
  • 今回説明する方法を応用すればdailyログとerrorログを別ファイルに出力することも可能になる考える。
  • 説明の便宜上、アプリ作成部分から記載するが各の環境に沿って読み飛ばしていただきたい。

読後感

  • singleログをアプリ名ディレクトリ/storage/logs/直下のsingle.logsファイルに、dailyログをアプリ名ディレクトリ/storage/logs/直下のdaily-YYYY-MM-DD.logファイルに[YYYY-MM-DD HH:MM:SS] local.DEBUG: testと言う文字列を出力することができる。

singleログとdailyログ

※singleログとdailyログはログチャンネル(ログを行う設定の種類)の名前である。

  • singleログ
    • Laravelアプリのログをアプリ名ディレクトリ/storage/logs/直下のlaravel.logファイルに出力するログのチャンネルのこと。(一個のログファイルのみにログを出力する方法と思っていただきたい。)
    • デフォルトではこのチャンネルが有効になっている。
  • dailyログ
    • Laravelアプリのログをアプリ名ディレクトリ/storage/logs/直下のlaravel-YYYY-MM-DD.logに日毎に分けて出力するログのチャンネルのこと。(singleログを日毎に分けた物と思っていただきたい。)
    • デフォルト設定では14日経過したdailyログは削除される。

概要

  1. アプリ作成
  2. ルーティングの記載
  3. UserHomeController作成と記載
  4. singleログ出力確認
  5. ログの設定変更
  6. 確認

詳細

  1. アプリ作成

    1. アプリを作成する任意のディレクトリに移動して下記コマンドを実行して「test」と言う名前のアプリを作成する。

      $ laravel new test
      
  2. ルーティングの記載

    1. testディレクトリで下記コマンドを実行してルーティングファイルを開く。

      $ routes/web.php
      
    2. 開いたルーティングファイルに下記の一行を記載する。

      routes/web.php
      Route::get('/user_home', 'UserHomeController@index');
      
    3. 記載後のルーティングファイルの内容を下記に記載する。

      routes/web.php
      <?php
      
      use Illuminate\Support\Facades\Route;
      
      /*
      |--------------------------------------------------------------------------
      | Web Routes
      |--------------------------------------------------------------------------
      |
      | Here is where you can register web routes for your application. These
      | routes are loaded by the RouteServiceProvider within a group which
      | contains the "web" middleware group. Now create something great!
      |
      */
      
      Route::get('/', function () {
          return view('welcome');
      });
      
      Route::get('/user_home', 'UserHomeController@index');
      
    4. 保存して閉じる。

  3. UserHomeController作成と記載

    1. testディレクトリで下記コマンドを実行してUserHomeController.phpを作成する。

      $ php artisan make:controller UserHomeController
      
    2. 下記コマンドを実行して先のコマンドで作成したUserHomeController.phpを開く。

      $ vi app/Http/Controllers/UserHomeController.php
      
    3. 開いたUserHomeController.phpを下記の様に修正する。

      test/app/Http/Controllers/UserHomeController.php
      <?php
      
      namespace App\Http\Controllers;
      
      use Illuminate\Http\Request;
      //下記を追記する、下記の宣言がないとエラーになる
      use Illuminate\Support\Facades\Log;
      
      class UserHomeController extends Controller
      {
          //下記を追記する
          public function index()
          {
              //Log::debug('ログとして出力したい文字列')の様に記載する
              Log::debug('test');
              return redirect('/');
          }
          //上記までを追記する。
      }
      
    4. 保存して閉じる。

  4. singleログ出力確認

    1. testディレクトリで下記コマンドを実行してまだlogファイルが存在していないことを確認する。「No such file or directory」が出力されればsingle.logファイルとdaily.logは存在しない。

      $ ls storage/logs/laravel.log
      >ls: storage/logs/laravel.log: No such file or directory
      
    2. testディレクトリで下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    3. ブラウザからtestアプリにアクセスする。デフォルトだとhttp://127.0.0.1:8000/にアクセスするとブラウザでtestアプリが表示できる。

    4. 下記画面が表示されていることを確認する。

      Laravel.png

    5. http://127.0.0.1:8000/user_homeにアクセスしてみる。(アクセスしても何も起こらず、先の画像と同じLaravelのトップページが表示されると思うがその動作は期待する動作である。)

    6. testディレクトリで下記コマンドを実行してログファイルを開く。

      $ less storage/logs/laravel.log
      
    7. 下記の一行が記載されていることを確認する。(YYYY-MM-DD HH:MM:SSはみなさんがhttp://127.0.0.1:8000/user_homeにアクセスした時間が入る。)

      test/storage/logs/laravel.log
      [YYYY-MM-DD HH:MM:SS] local.DEBUG: test 
      
  5. ログの設定変更

    1. testディレクトリで下記コマンドを実行してログの設定ファイルを開く。

      $ vi config/logging.php
      
    2. 開いた設定ファイルを下記の様に修正する。

      test/config/logging.php
      <?php
      
      use Monolog\Handler\NullHandler;
      use Monolog\Handler\StreamHandler;
      use Monolog\Handler\SyslogUdpHandler;
      
      return [
      
          /*
          |--------------------------------------------------------------------------
          | Default Log Channel
          |--------------------------------------------------------------------------
          |
          | This option defines the default log channel that gets used when writing
          | messages to the logs. The name specified in this option should match
          | one of the channels defined in the "channels" configuration array.
          |
          */
      
          'default' => env('LOG_CHANNEL', 'stack'),
      
          /*
          |--------------------------------------------------------------------------
          | Log Channels
          |--------------------------------------------------------------------------
          |
          | Here you may configure the log channels for your application. Out of
          | the box, Laravel uses the Monolog PHP logging library. This gives
          | you a variety of powerful log handlers / formatters to utilize.
          |
          | Available Drivers: "single", "daily", "slack", "syslog",
          |                    "errorlog", "monolog",
          |                    "custom", "stack"
          |
          */
      
          'channels' => [
              'stack' => [
                  'driver' => 'stack',
                  //下記修正・追記する
                  'channels' => [
                      'single',
                      'daily',
                  ],
                  'ignore_exceptions' => false,
              ],
      
              'single' => [
                  'driver' => 'single',
                  //下記を修正する
                  'path' => storage_path('logs/single.log'),
                  'level' => 'debug',
              ],
      
              'daily' => [
                  'driver' => 'daily',
                  //下記を修正する
                  'path' => storage_path('logs/daily.log'),
                  'level' => 'debug',
                  'days' => 14,
              ],
      
              'slack' => [
                  'driver' => 'slack',
                  'url' => env('LOG_SLACK_WEBHOOK_URL'),
                  'username' => 'Laravel Log',
                  'emoji' => ':boom:',
                  'level' => 'critical',
              ],
      
              'papertrail' => [
                  'driver' => 'monolog',
                  'level' => 'debug',
                  'handler' => SyslogUdpHandler::class,
                  'handler_with' => [
                      'host' => env('PAPERTRAIL_URL'),
                      'port' => env('PAPERTRAIL_PORT'),
                  ],
              ],
      
              'stderr' => [
                  'driver' => 'monolog',
                  'handler' => StreamHandler::class,
                  'formatter' => env('LOG_STDERR_FORMATTER'),
                  'with' => [
                      'stream' => 'php://stderr',
                  ],
              ],
      
              'syslog' => [
                  'driver' => 'syslog',
                  'level' => 'debug',
              ],
      
              'errorlog' => [
                  'driver' => 'errorlog',
                  'level' => 'debug',
              ],
      
              'null' => [
                  'driver' => 'monolog',
                  'handler' => NullHandler::class,
              ],
      
              'emergency' => [
                  'path' => storage_path('logs/laravel.log'),
              ],
          ],
      
      ];
      
  6. 確認

    1. testディレクトリで下記コマンドを実行してまだlogファイルが存在していないことを確認する。「No such file or directory」が出力されればsingle.logファイルとdaily.logは存在しない。

      $ ls storage/logs/single.log
      >ls: storage/logs/single.log: No such file or directory
      $ ls storage/logs/daily*
      >ls: storage/logs/daily*: No such file or directory
      
    2. testディレクトリで下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    3. ブラウザからtestアプリにアクセスする。デフォルトだとhttp://127.0.0.1:8000/にアクセスするとブラウザでtestアプリが表示できる。

    4. 下記画面が表示されていることを確認する。

      Laravel.png

    5. http://127.0.0.1:8000/user_homeにアクセスしてみる。(アクセスしても何も起こらず、先の画像と同じLaravelのトップページが表示されると思うがその動作は期待する動作である。)

    6. testディレクトリで下記コマンドを実行してログファイルを開く。

      $ less storage/logs/single.log
      
    7. 二つのファイルに下記の一行が記載されていることを確認する。(YYYY-MM-DD HH:MM:SSはみなさんがhttp://127.0.0.1:8000/user_homeにアクセスした時間が入る。)

      test/storage/logs/laravel.log
      [YYYY-MM-DD HH:MM:SS] local.DEBUG: test 
      
    8. 正常に書き込まれていた場合作業完了となる。

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

[PHP] Zoom APIを利用してミーティングを作成する

今回の題

友人がZoomのAPIを使って何か作りたいというので勉強してまとめてみました。
この記事ではJWTという認証方法でミーティングを作成するところまで行います。
主に私自身のアウトプットが目的ですが、いつか誰かの参考になれば幸いです。
まだ利用途中なので適宜、加筆していきます。

APIキーの取得

以下にアクセスし、サインアップしてください。
Zoomマーケットプレイス

サインアップしたら、画面右上の「Develop」と書かれたドロップダウンから「Build App」を選択して以下のようなページにいきます。

スクリーンショット 2020-07-01 23.41.25.png

画像の赤枠で囲んだ部分をクリックすると、これから作成するアプリの名前を求められるので以下のように適当に埋めておきます。

スクリーンショット 2020-07-01 23.47.40.png

すると以下のようにアプリが作成されました。
必須項目がいくつかあるので適当に入力して、画面右下の「Continue」を押してください。
スクリーンショット 2020-07-01 23.49.33.png

すると、画像のようなAPIキーなどの情報が記されたページに進みます。
これでAPIキーGETです。
スクリーンショット 2020-07-01 23.52.30.png

必要なライブラリの準備

・Guzzleのインストール
APIリクエストを送信する際に使用します。

$ composer require guzzlehttp/guzzle

・JWTのインストール
JWTトークンを生成するのに使用します。

$ composer require lcobucci/jwt

完成コード

<?php
require('vendor/autoload.php');

use GuzzleHttp\Client;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Builder;

const BASE_URI = 'https://api.zoom.us/v2/';

function createJwtToken()
{
    $api_key = 'XXXXXXXXX';
    $api_secret = 'XXXXXXXXX';
    $signer = new Sha256;
    $key = new Key($api_secret);
    $time = time();
    $jwt_token = (new Builder())->setIssuer($api_key)
                            ->expiresAt($time + 3600)
                            ->sign($signer, $key)
                            ->getToken();
    return $jwt_token;
}

function getUserId() 
{
    $method = 'GET';
    $path = 'users';
    $client_params = [
      'base_uri' => BASE_URI,
    ];
    $result = sendRequest($method, $path, $client_params);
    $user_id = $result['users'][0]['id'];
    return $user_id;
}

function createMeeting() 
{
    $user_id = getUserId();
    $params = [
      'topic' => 'テスト',
      'type' => 1,
      'time_zone' => 'Asia/Tokyo',
      'agenda' => 'ズームAPIを試す',
      'settings' => [
        'host_video' => true,
        'participant_video' => true,
        'approval_type' => 0,
        'audio' => 'both',
        'enforce_login' => false,
        'waiting_room' => false,
        'registrants_email_notification' => false
      ]
    ];
    $method = 'POST';
    $path = 'users/'. $user_id .'/meetings';
    $client_params = [
      'base_uri' => BASE_URI,
      'json' => $params
    ];
    $result = sendRequest($method, $path, $client_params);
    return $result;
}

function sendRequest($method, $path, $client_params)
{
    $client = new Client($client_params);
    $jwt_token = createJwtToken();
    $response = $client->request($method, 
                    $path, 
                    [
                      'headers' => [
                        'Content-Type' => 'application/json',
                        'Authorization' => 'Bearer ' . $jwt_token,
                      ]
                    ]);
    $result_json = $response->getBody()->getContents();
    $result = json_decode($result_json, true);
    return $result;
}

$meeting = createMeeting();

最後に定義した$meetingという変数に、作成されたミーティングの情報が以下のような形で入っています。
一部内容を省略したり伏せたりしていますが、大体この通りです。

Array
(
   //略
    [topic] => テスト
    [type] => 1
    [status] => waiting
    [timezone] => Asia/Tokyo
    [agenda] => ズームAPIを試す
    [created_at] => 2020-07-01T20:24:04Z
    [start_url] => https://zoom.us/s/.....
    [join_url] => https://zoom.us/j/......
     //略
    [settings] => Array
        (
            //略
        )

)

start_urlかjoin_urlに記載されたurlからミーティングに参加することができます。
ちなみにこの二つのurlの違いは、
start_url →  ホストとして参加
join_url  →  参加者として参加
です。

一度ここまで

wifiの調子がすこぶる悪いので一旦ここまで。
説明などはまた後日にでも。
個人的な鬼門はJWT。見よう見まねで書いたが理解が足りていない。

参考

Zoom公式 リファレンス
Zoom公式 Create a Meeting
Zoom公式 List Users
Zoom公式 Generating JWTs
JWT(Json Web Token) をPHPで生成する

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

まだまだ丸め誤差と闘わないといけない件について

Difficulty 24の問題でコケてしまった悲しみ。

AtCoder Beginner Contest 165のB問題、問題名「1%」は、高橋くんが100円持っていて、銀行に預けると年利1%(複利)の利子がつくのですが、ある金額X円に達するには何年かかるか答えよ、という問題です。

自分で作った回答コードはこちら。

<?php
fscanf(STDIN, "%d", $X);
// 預金額
$money = 100;
// 利子(%)
$risoku = 1;
// 答え
$ans = 0;
while($X > $money) {
    $money += intval($money * ($risoku / 100));
    $ans++;
}
printf("%d", $ans);

結果、不正解となったコードですが、どこが間違っているか分かりますでしょうか?

利子を変数にして1%じゃなくても答えが出せるんだぜ的にしてしまったのが悪かった。

正解になったのはこちら。

<?php
fscanf(STDIN, "%d", $X);
// 預金額
$money = 100;
// 答え
$ans = 0;
while($X > $money) {
    $money += intdiv($money, 100);
    $ans++;
}
printf("%d", $ans);

変数とか使った私が愚かでした。単純に100で割って端数を切り捨てた整数を返してくれる intdiv を素直に使いました。

ちなみに、不正解となったテストケースは「99-after-contest-01.txt」という名前で、値Xは 974755271730884810 です。
具体的にいくらかというと、 97京4755兆2717億3088万4810円 ということなので、 ビル・ゲイツも真っ青なお金なので1年の誤差なんか気にすんな という気分になりますが、不正解は不正解。
テストケース名から分かる通り、コンテスト後に作られたテストケースなので、コンテスト時はいわゆる 嘘解法(テストケース的にはOKだけどコードとしては実際には間違っている、の意) で通した人も少なからずいるんでしょうね。

不正解のコードと正解のコードでは、3432年後から値に差が出始めます。(■がついているところは同値)
左が不正解のコードで、3431年後の金額、…66199円の1%は…661円なので、3432年後は …66199+…661=…860となるべきところ、…861となっています。

image.png

この影響で、差がどんどん広がり、不正解のコードでは 974755271730884810 に達するのは3757年後と算出するのに対し、正解のコードでは3758年後となるので、不正解となるのです。

image.png

いやぁ、不正解のコード、そんなに悪いコードですかねぇ?、という気分になってしまいますが、いまだに浮動小数点を含む演算には気をつけなければいけないケースがあるんだなぁ、ということは留意しておいた方が良いんですね、というお話でした。

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

【Laravel】配列のバリデーション

フォームのフィールドが配列の場合のバリデーションについてのメモです。

ドット記法

配列に対してバリデーションを行うためにはドット記法を使い、.で配列の階層を表します。また*で配列内すべてを表します。

//photos[profile]フィールドを指定
$validator = Validator::make($request->all(), [
    'photos.profile' => 'required|image',
]);

//keywordフィールドすべてを指定
$validator = Validator::make($request->all(), [
    'keyword.*' => 'required|max:20',
]);

エラーメッセージの取得

Illuminate\Support\ViewErrorBag {
  #bags: array:1 [
    "default" => Illuminate\Support\MessageBag {
      #messages: array:2 [
        "keyword.0" => array:1 [
          0 => "キーワードは、20文字以下で指定してください。"
        ]
        "keyword.1" => array:1 [
          0 => "キーワードは、20文字以下で指定してください。"
        ]
        "keyword.2" => array:1 [
          0 => "キーワードは、20文字以下で指定してください。"
        ]
      ]
      #format: ":message"
    }
  ]
}

配列にバリデーションを設定した場合、エラーメッセージも配列で返ってくるため、取得もドット記法で行います。

foreach ($errors->get('keyword.*') as $messages) {
    foreach ($messages as $message){
       //
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む