20190822のPHPに関する記事は13件です。

[Laravel - デバッグ]自分で書いたプログラムをただ(var_dump()、var_export()、print_r())、dd()するよりもわかりやすくデバッグする(dump server)

前置き

今回は、(var_dump()、var_export()、print_r())は表示形式が少し異なるだけなので、var_export()とdd()を主に使います。

準備

テスト用にController,bladeを作成する。

Controller

SampleController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SampleController extends Controller
{
    public function sample(Request $request)
    {
        return view('sample');
    }
}

blade

中身空のsample.blade.phpを作成する。

routes/web.php

Route::get('/sample', 'SampleController@sample')->name('sample');

準備が完了しました。これで/sampleへアクセスすると、真っ白な画面が表示されます。

普通にvar_dump()、dd()を使ってみる。

var_dump()

SampleControllerの中身を変更する

public function sample(Request $request)
{
    $string = 'あいうえお';
    var_dump($string);

    $int = 1;
    var_dump($int);

    $array = ['name' => '太郎'];
    var_dump($array);

    $time = new Carbon();
    var_dump($time);

    return view('sample');
}

/sampleへアクセスすると、変数の中身の情報が表示されています。

Screenshot_1.png

dd()

SampleControllerの中身を変更する

public function sample(Request $request)
{
    $string = 'あいうえお';
    var_dump($string);

    $int = 1;
    var_dump($int);

    $array = ['name' => '太郎'];
    dd($array);

    $time = new Carbon();
    var_dump($time);

    return view('sample');
}

/sampleへアクセスすると、変数の中身の情報が表示されています。
dd()は実行された直後に処理が終了します。変数の中身が、複雑な配列な際や、そこまでの処理が正常に動作しているかの確認時に重宝します。

Screenshot_2.png

(本題)Laravelでは、もっといい方法がある。

Laravelには、デバッグ用にdump serverというものが用意されている。こちらを使う。
コマンドをたたく。

コマンド
root@75b98e12b61d:/var/www# php artisan dump-server

Laravel Var Dump Server
=======================


 [OK] Server listening on tcp://127.0.0.1:9912


 // Quit the server with CONTROL-C.

dump-serverが起動できました。dump-serverは、出力内容をサーバー側で受け取り、表示することが可能になります。

このままの状態で、先ほどのdd()入りのコードを実行してみます。/sampleへアクセスします。

GET http://192.168.99.100/sample
--------------------------------

 ------------ -------------------------------------------
  date         Thu, 22 Aug 2019 14:14:59 +0000
  controller   "SampleController"
  source       SampleController.php on line 19
  file         app/Http/Controllers/SampleController.php
 ------------ -------------------------------------------

array:1 [
  "name" => "太郎"
]

コマンドプロンプトでddの内容が表示されます。それ以外にも、日時やコントローラーの情報などが記述されています。ddとvar_dumpをdump()メソッドを使うように変えます。

public function sample(Request $request)
{
    $string = 'あいうえお';
    dump($string);

    $int = 1;
    dump($int);

    $array = ['name' => '太郎'];
    dump($array);

    $time = new Carbon();
    dump($time);

    return view('sample');
}

/sampleへアクセスします。

GET http://192.168.99.100/sample
--------------------------------

 ------------ -------------------------------------------
  date         Thu, 22 Aug 2019 14:18:14 +0000
  controller   "SampleController"
  source       SampleController.php on line 13
  file         app/Http/Controllers/SampleController.php
 ------------ -------------------------------------------

"あいうえお"

 ------------ -------------------------------------------
  date         Thu, 22 Aug 2019 14:18:14 +0000
  controller   "SampleController"
  source       SampleController.php on line 16
  file         app/Http/Controllers/SampleController.php
 ------------ -------------------------------------------

1

 ------------ -------------------------------------------
  date         Thu, 22 Aug 2019 14:18:14 +0000
  controller   "SampleController"
  source       SampleController.php on line 19
  file         app/Http/Controllers/SampleController.php
 ------------ -------------------------------------------

array:1 [
  "name" => "太郎"
]

 ------------ -------------------------------------------
  date         Thu, 22 Aug 2019 14:18:14 +0000
  controller   "SampleController"
  source       SampleController.php on line 22
  file         app/Http/Controllers/SampleController.php
 ------------ -------------------------------------------

Carbon\Carbon @1566483494^ {#446
  date: 2019-08-22 14:18:14.934425 UTC (+00:00)
}

ddを使うと、そこまでの内容を表示し処理を終了してくれます。

まとめ

var_dump()を使うと、viewのほうで場合によって、エラーが出ることがあったりしたり、見にくかったりするけど、dump-serverを使うと、きれいにわかりやすく簡単に変数の値を見ることができた。

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

Laravelでコマンド入れる時ってどこで動かすの?

Laravelでコマンドを使っていろいろなことをするときに、「コマンドラインで」って言われることが多いのだけれども、具体的にWindowsのコマンドプロンプトなの?sshでつないでlinux上で動かすの?ってちょっとわからない時が多すぎる気がする。
いつかまた泣きそうになる気がするので、泣く前に見られる情報を書いておく。
尚、環境は以下の通り。
・ホストOS:Windows10
・開発環境:Homestead→Vagrant上でLinuxが起動
            DBはMySQL

関係ないけど、VSCodeが使いやすくてびっくりだよ。

簡単に言っておくと

vagrantのコマンド→Windowsのコマンドプロンプトで実行
プロジェクトに対して実行するコマンド→linuxのコンソールで実行

各論

Vagrantの起動(Vagrant up)

さすがにこれはWindowsのコマンドプロンプトから起動ってわかるね。
Homesteadをインストールしたフォルダーに移動してからコマンド実行してください。

C:{Homesteadをインストールしたフォルダ}>vagrant up

Vagrantの終了(Vagrant halt)

当然起動と同じ、Windowsのコマンドプロンプトです。Homesteadをインストールしたフォルダに移動してコマンド実行してください。

C:{Homesteadをインストールしたフォルダ}>vagrant halt

Laravelプロジェクトの作成(composer)

これはVagrant上のlinuxから実行します。
Vagrant上のLinuxにアクセスするには、Vagrant sshでVagrant上のlinuxのコンソールを開きます。
Vagrant sshはWindows10のコマンドプロンプトから入れます。

C:{Homesteadをインストールしたフォルダ}>vagrant ssh

そうするとあら不思議、今までWindows10のコマンドプロンプトだった画面がlinuxのコンソール画面に早変わり!

image.png

linux上でコマンドを入力する場合は、vagrant sshでlinuxに接続してからコマンド実行する。

尚、laravelプロジェクトを作成するときはlinux上でプロジェクトを作成するディレクトリに移動してからコマンドを実行する。
Homesteadのデフォルトの開発ディレクトリはcodeなので、通常はcodeディレクトリに移動してからプロジェクトを作成する。

※note
Homesteadでは、VagrantでホストOS(ここではWindows10)とゲストOS(ここではlinux Ubuntu)で共有する共有フォルダーが設定される。
[設定される共有フォルダー]
home_vagrabt_code C:¥{ユーザーフォルダ}¥code
vagrant C:¥{ユーザーフォルダ}¥Homestead
これらのフォルダーはホストOSからもゲストOSからも参照・更新することができる。

【コマンド例】

vagrant@homestead:~$ cd code
vagrant@homestead:~code$ composer create-project laraavel/laravel testProject

artisanコマンド

linuxのコンソール画面で実行する。
プロジェクトのフォルダに移動してから実行する。実行した結果は移動したプロジェクト内に反映される。

【コマンド例】

vagrant@homestead:~/code/myProject$ php artisan make:test testCode

MySQLを立ち上げる

linuxコンソールで実行する。
MySQLコマンドはどのディレクトリにいても実行できます。

【コマンド例:homesteadのhomeディレクトリで実行】

vagrant@homestead:~$ mysql -u homestead -p

デフォルトの設定値では、ユーザーは「homestead」、パスワードは「secret」です。
変更する場合は、プロジェクト内の.envファイル内の「DB_USERNAME」、「DB_PASSWORD」の設定値を変更してみ。

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

docker-composeで、一つのWebサーバーコンテナ上に複数のLaravelアプリを立ち上げる

同じDBを参照する、2つのLaravelアプリをdocker-composeで立ち上げる方法について書いていきます。一つのWebサーバー上で2つのLaravelアプリが動いていて、それぞれが同じDBを見ている、といった構成です。

ググったらいくらでも出てくるかな?と思ったら、Laradocを使った方法しかなかったので意外でした。

コンテナの構成

コンテナ関連のディレクトリ構成は、以下の記事を参考にしています。今回やることは、以下の記事のゴールの状態から、立ち上げるLaravelアプリを1つ追加する、みたいなイメージです。

Laravelの開発環境をDockerを使って構築する

docker-compose.ymlを変更

webサーバーにnginxを使っている場合、docker-compose.ymlは以下のような記述になっているかと思います。

docker-compose.yml
version: "3"
services:
  app:
    context: ./docker/php
    // アプリコンテナの記述

  web:
    image: nginx:1.17-alpine
    depends_on:
      app
    // portsなどの記述

別のLaravelアプリを立ち上げる場合は、起動するアプリコンテナを一つ増やします。記述例としてはこんな感じ。

docker-compose.yml
version: "3"
services:
  app:
    context: ./docker/php
    // アプリコンテナの記述

  other:
    context: ./docker/php
    // アプリコンテナの記述

  web:
    image: nginx:1.17-alpine
    depends_on:
      app
      other
    ports:
      3500:80
      3501:79
    volumes:
      - ./app:/work/app
      - ./other:/work/other
      - ./logs:/var/log/nginx
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf

これにより、docker-compose upを実行すると、otherコンテナが立ち上がります。参照するDockerfileは、appと同じものをにするか、新しく定義するかはお好みで。

また、nginxコンテナのdepends_onにotherコンテナを指定し、加えてportsの設定を一つ増やします。これにより、「localhost:3500」でサーバーコンテナの80番ポートに、「localhost:3501」で79番ポートにアクセスできるようになります。

あと、volumesにも変更を加えています。docker-compose.ymlがあるディレクトリの、app/配下がwebサーバーコンテナの/work/appに、other/配下が/work/otherに配置されるようにしています。

default.confの変更

次に、nginxの設定ファイルを変更しています。変更前はこんな感じになっているはず。

default.conf
server {
    listen 80;
    root /work/app/public;
    index index.php;
    charset utf-8;

    location / {
        root /work/app/public;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /work/app/public/index.php;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

80番ポートにアクセスが来るとnginxがそれに対応する、みたいな標準的な設定です。80番ポートはapp/に配置されているLaravelアプリに対応するようになっています。

今回、otherコンテナに配置するLaravelアプリは79番ポートで受け付けられるようにするので、以下のように記述を追加します。

default.conf
server {
  // 80番ポートの設定
}

server {
    listen 79;
    root /work/other/public;
    index index.php;
    charset utf-8;

    location / {
        root /work/other/public;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass other:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /work/other/public/index.php;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

listenを79番に、rootを/work/other/publicに変更しています。docker-compose.ymlのvolumesに対応するようにrootを設定しましょう。(publicはLaravelの公開ディレクトリです)。

webサーバーコンテナの中が、/work/other/(Laravelのディレクトリ群)のような構成になることを想定した記述ですね。

また、「location ~ .php」の中の記述も少し変更しています。

まず、「fastcgi_pass」がapp:9000から、other:9000になっています。app、otherはアプリコンテナのことで、PHP-FPMとの通信ポートをそれぞれ指定しています。

あと、「fastcgi_param SCRIPT_FILENAME」以下の記述も変更しており、Laravelアプリのpublicフォルダ内のindex.phpを参照するようにしています。ここがズレているとphpファイルが正しく読み込まれず、「File not Found」と表示されて悲しい気持ちになるので忘れずに変更しておいてください。

今回は直接パスを記述していますが、「\$document_root/index.php」みたいな書き方もあります。($document_rootにはrootで指定したパスが入る)

これで、app/配下、other配下にLaravelをインストールし、.envやディレクトリの権限変更を正しく行えば、localhost:10080、localhost:10079にアクセスするとそれぞれLaravelのWelcomeページが表示されるはずです。

今回は、それぞれのアプリが同じDBを参照するようにしたいので、.envを変更して参照先が同じになるよう設定しておきましょう。

ページが表示されねぇ!みたいなときは、./logs配下のaccess.logやerror.logをヒントに対応してください。

まとめ

docker-composeでバシッと環境が整うと気持ちいいですよね。nginxの設定は基本的なものだけしか記述していないので、勉強がてらいろいろイジり倒そうと思います。

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

StaticPress S3のスクリプトをAWS SDK for PHP3に対応させて書く

急ですが、EC2上に構築しているWordPressの記事をHTMLに変換してS3のバケットに送るということをやりたくなりました。
StaticPressとStaticPress S3を試したのですが、ファイルをS3に送るところだけ別でいろいろとしたいなぁ、と考えました。
(この記事に載せるには「いろいろ」は大人の事情でかけません…。なので、バージョン上げた部分だけ書きます)

ファイルをS3に送信するスクリプトは「StaticPress S3」のドキュメントにて公開されていました。
http://ja.staticpress.net/2013/06/11/69/
現在は2019年なので、内容が古いです。
AWS SDK for PHPのバージョンが2の時の内容ですね。

現在はAWS SDK for PHPはバージョン3になってます。
https://aws.amazon.com/jp/sdk-for-php/
なので、このスクリプトを3を使って書き直してみました。
PHP7.3
AWS SDK for PHPはpharで落としてきて読み込んでいます。

<?php
require_once('aws.phar');
use Aws\S3\S3Client;  
use Aws\Exception\AwsException;
use Aws\Credentials\Credentials;

$key = 'Your Access Key';
$secret = 'Your Secret Key';
$region = 'ap-northeast-1'; 
$bucket = 'bucket_name';
$dir = '/var/www/html/fugafuga/';

/**
 * 指定されたディレクトリのファイル一覧を取得
 */
function get_dir_file_list($dir)
{
    $check_dirs = [$dir];
    $file_paths = [];
    $file_name = [];

    while( $check_dirs ) 
    {
        $dir_path = $check_dirs[0] ;

        if( is_dir ( $dir_path ) && $handle = opendir ( $dir_path ) ) 
        {
            while( ( $file = readdir ( $handle ) ) !== false ) 
            {
                if( in_array ( $file, [ ".", ".." ] ) !== false ) 
                {
                    continue;
                } 

                $path = rtrim ( $dir_path, "/" ) . "/" . $file ;

                if ( filetype ( $path ) === "dir" ) 
                {
                    $check_dirs[] = $path ;
                } 
                else 
                {
                    $file_name[] = $path;
                }
            }
        }
        array_shift( $check_dirs ) ;
    }

    return $file_name;
}

/**
 * ここから実際の処理
 */
try 
{
    $credentials = new Credentials($key, $secret);
    // Create a S3Client
    $s3Client = new S3Client([
        'region' => $region,
        'version' => 'latest',
        'credentials' => $credentials,
    ]);

    $files = get_dir_file_list($dir);
    $info = new Finfo(FILEINFO_MIME_TYPE);
    foreach ($files as $filename) 
    {
        $filetype = $info->file($filename);
        $filebody = fopen($filename, 'r');
        if ( $filetype == 'text/plain') 
        {
            if (preg_match('/\.css$/i', $filename)) 
            {
                $filetype = 'text/css';
            } 
            else if(preg_match('/\.js$/i', $filename)) 
            {
                $filetype = 'application/x-javascript';
            } 
            else if(preg_match('/\.html?$/i', $filename)) 
            {
                $filetype = 'text/html';
            }
            else if(preg_match('/\.xml$/i', $filename)) 
            {
                $filetype = 'application/xml';
            }
        }

        echo $filetype . ':' . str_replace($dir, '', $filename) . "\n";

        $response = $s3Client->putObject([
            'Bucket' => $bucket,
            'Key' => str_replace($dir, '', $filename),
            'Body' => $filebody,
            'ContentType' => $filetype,
//          'StorageClass' => 'STANDARD',
//          'ACL' => 'public-read',
        ]);

    }
} catch(S3Exception $e) {
    echo $e->getMessage();
}

ディレクトリ下のファイルを取得する関数を書き直しました。
拡張子である程度しぼって取得するほうがよかったかもなぁ、とも思います。ContentTypeも決めているので…。

コメントにしてあるStorageClassとACLは必須パラメータではないです。

APIドキュメントはこちらを。
https://docs.aws.amazon.com/aws-sdk-php/v3/api/index.html

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

PhpStormでCRLF/LF/CRを切り替える

改行コード 「CRLF」「LF」「CR」の違い。

CR(Carriage Return キャリッジリターン)

カーソルを左端の位置に戻すこと

LF(Line Feed ラインフィード)

カーソルを新しい行に移動すること

CRLF

CRとLFを組み合わせたもの。左端にカーソルを戻して改行する。

PhpStormでのCRLF/LF/CR切替方法

PhpStorm右下のCRLFをクリックで切替可能

CRLF.gif

備忘録

PhpStorm上でシェルスクリプトを書くとき、CRLFだと改行が入りシェルが実行されないので、LFに変えてから書きましょう。

参考URL

備忘録:改行コード「LF」と「CR」と「CRLF」の違い
https://marusunrise2.blogspot.com/2014/06/lfcrcrlf.html

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

PHPの配列をjson_encodeしたら、全角文字がユニコードになってハマった話。

タイトルのとおりの現象で、少々ハマりました。

結論としては、json_encodeの第2引数にJSON_UNESCAPED_UNICODEを指定すれば解決。

$array = [
 'ほげ',
 'ふが',
];
$json = json_encode( $array, JSON_UNESCAPED_UNICODE );

みたいな感じで。

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

【初心者向け】Laravel プロジェクトを clone してブラウザ表示するまで

Laravel のプロジェクトを clone してきて、SQLite3 に接続して、ブラウザで表示する方法は意外とまとまっていなかったので記事にしました。

推奨環境

  • MacOS Mojave バージョン10.14.2
  • Laravel Framework 5.8.31
  • sqlite3 3.24.0

やり方

1. 対象の Laravel プロジェクトを clone してくる

$ git clone リポジトリ URL

2. プロジェクトのリポジトリに移動し、vendor ディレクトリがないことを確認

$ cd projectName
$ ls

3. Composer がインストールされていなかったら

$ brew install composer

4. ライブラリインストール

$ composer install

vendor ディレクトリが作成されるはず。

5. ディレクトリ下の.env.exampleの内容を.envにコピー

$ cp .env.example .env

clone してきた Laravel プロジェクトは .env ではなく .env.example というファイル名になっているので変更する必要がある。

6. アプリケーションキー(APP_KEY)を設定する

$ php artisan key:generate

.env ファイルの中身を見ると、APP_KEY の内容が APP_KEY=SomeRandomString となっているため、変更する必要がある。

7. SQLite3 の準備

$ touch database/database.sqlite

database ディレクトリに database.sqlite が作られる。

8. .env ファイルの設定

エディタや vim などで .env ファイルの該当箇所を以下のように書き換える

~略~
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=homestead
# DB_USERNAME=homestead
# DB_PASSWORD=secret
~略~

DB_HOST以下はいらないのでコメントアウト。
SQLite3 の配置場所やファイル名を変更する場合は、DB_DATABASE に絶対パスで指定しましょう。

9. migrate 実行

$ php artisan migrate

10. いつものようにサーバーを立ち上げる

$ php artisan serve

http://127.0.0.1:8000 にアクセス

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

webarenaでubuntu その24

概要

webarenaでubuntu18.04やってみた。
php7.2やってみた。
session認証やってみた。

login.php

<?php
    session_start();
    header ('Content-Type: text/html; charset=Shift_JIS');
?>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
</head>
<body>
<form action="index.php" method="POST">
    ログインID:<input type="text" size="15" name="fLoginID"><br>
    パスワード:<input type="password" size="8" name="fPassword"><br>
    <input type="submit" value="ログイン">
</form>
</body>
</html>

index.php

<?php
    session_start();
    if ($_SERVER['REQUEST_METHOD'] === 'POST')
    {
        $fLoginID = $_POST['fLoginID'];
        $fPassword = $_POST['fPassword'];
        $db = new PDO("mysql:host=localhost;dbname=mydb;", 'arena', 'pass');
        $sql = "SELECT pass FROM test where flag='1' AND ID='$fLoginID'";
        $res = $db->query($sql);
        if ($res->rowCount() != 1)
        {
            header ('Content-Type: text/html; charset=Shift_JIS');
            print "アカウントが違う、もしくはユーザ登録されていません。";
            print '<a href="../">こちらから登録してください。</a>';
            session_destroy();
            exit;
        }
        $user = $res->fetch();
        if ($user["pass"] != $fPassword)
        {
            header ('Content-Type: text/html; charset=Shift_JIS');
            print "パスワードが違います。";
            session_destroy();
            exit;
        }
        $_SESSION['sLogin'] = "ok";
        $_SESSION['sId'] = $fLoginID;
        header("Location: contents.php");
    }
    else if ($_SESSION['sLogin'] <> "ok")
    {
        header("Location: login.php");
    }
    else
    {
        header("Location: contents.php");
    }
?>

以上。

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

BEAR.Sundayのモジュールを実装・CI・公開する

TL;DR

https://github.com/piotzkhider/Ray.FirebaseModule

記事のゴール

FirebaseをBEAR.Sundayで利用するためのモジュールを実装・公開する

既存のパッケージがあった…

https://github.com/ray-di/Ray.FirebaseModule

内容としてはまさにこれが欲しかったんだけど、3年前からメンテされていないようで使われているFirebaseのライブラリも現在主流のものとは異なっている様子。

こういうときどうしたらいいんだろう…
依存しているライブラリまでガッツリ変えちゃうようなPRを送る?
別ライブラリとして実装する?

とりあえず今回はさくっとcomposerでインストールして使いたかったので別ライブラリとして実装する。

追記

既存のライブラリをForkしてガッツリ内容を変えたPRを提出して

  • マージされるまではcomposer.jsonのrepositoryにForkして実装したGitHubを使う
  • リジェクトされたら別ライブラリとしてpackagistにsubmitする
  • マージされたらcomposer require

でよかったのかも?

参考:
packagistを使わないcomposerのgitリポジトリの作成
https://getcomposer.org/doc/05-repositories.md#vcs

実装

https://github.com/koriym/Koriym.PhpSkeleton

上記のスケルトンを利用してプロジェクトを作成する
CIやら静的(動的)解析ツールやらの設定が色々同梱されているので最低限のことは全部やってくれる。
PSR-12とは設定がずれている部分があったので、気になる人は下記を参考にして下さい。

.php_cs.dist
-'return_type_declaration' => ['space_before' => 'one'],
+'return_type_declaration' => ['space_before' => 'none'],

テスト

$ composer test  // phpunit
$ composer tests // php-cs-fixer & phpcs & phpmd & phpstan & phpunit

どちらもエラーが出なくなったらGitHubにcommit & push

README.mdの作成

https://github.com/ray-di/Ray.DbalModule/blob/1.x/README.md

上記を参考にREADMEを作る

バッジを設定する

こういうやつ↓
image.png

参考:
https://qiita.com/dtan4/items/13b0ea9edf5b99926446

Travis CI

設定ファイル(.travis.yml)がスケルトンに入っているのでCIとしての設定はほとんど必要なし。
ただし今回はNightlyバージョンのPHPは必要ないので設定から削る

.travis.yml
 php:
 - 7.1
 - 7.2
 - 7.3
- - nightly

 ...

 jobs:
 ...
   allow_failures:
-   - php: nightly

画像の赤枠部分をクリックすることでバッジのコードを取得できる。
スクリーンショット 2019-08-22 12.00.07.png

このままだとLowest dependenciesにエラーが出ているようなので修正が必要
image.png

Lowest dependencies?

通常与えられた条件の最新のバージョンがインストールされます。テストは最新の依存だけをテストすることになり。^や~で指定した最低のバージョンのテストは行われません。

これが問題になるのは、他のパッケージが特定のパッケージの低いバージョンのものに依存していて、また他のパッケージではその高いバージョンのみにある機能を使用している場合です。依存解決は低いバージョンで行われ、最新バージョンに依存したパッケージはエラーを引き起こしたりBugの元になったりします。

参考:
Composerで低いバージョンの依存テスト

エラーが出ないよう修正する

> vendor/bin/phpunit
PHPUnit 7.5.0 by Sebastian Bergmann and contributors.

EEEEE                                                               5 / 5 (100%)

Time: 43 ms, Memory: 4.00MB

There were 5 errors:

1) Piotzkhider\FirebaseModule\FirebaseModuleTest::testModule
Error: Class 'PhpParser\ParserFactory' not found

/PhpStormProjects/Ray.FirebaseModule/vendor/ray/aop/src/ParserFactory.php:13
/PhpStormProjects/Ray.FirebaseModule/vendor/ray/aop/src/Compiler.php:35
/PhpStormProjects/Ray.FirebaseModule/vendor/ray/di/src/Injector.php:32
...

phpstan-shimに存在しているこの問題のよう。
issueによるとbootstrap.phpでこの問題は解決しているようなので、bootstrap.phpが存在するバージョンを最低バージョンに指定する。

参考:
http://blog.a-way-out.net/blog/2015/06/19/composer-version-tilde-and-caret/

composer.json
    "require-dev": {
        "phpunit/phpunit": "^7.5",
        "squizlabs/php_codesniffer": "^3.2",
        "friendsofphp/php-cs-fixer": "^2.12",
        "phpmd/phpmd": "^2.6",
-        "phpstan/phpstan-shim": "^0.10"
+        "phpstan/phpstan-shim": "^0.10.2"
    },
> vendor/bin/phpunit
PHPUnit 7.5.0 by Sebastian Bergmann and contributors.

EEEEE                                                               5 / 5 (100%)

Time: 109 ms, Memory: 12.00MB

There were 5 errors:

1) Piotzkhider\FirebaseModule\FirebaseModuleTest::testModule
Error: Class 'Google\Cloud\Core\ServiceBuilder' not found

/Users/ysato/PhpStormProjects/Ray.FirebaseModule/vendor/kreait/firebase-php/src/Firebase/Factory.php:370
/Users/ysato/PhpStormProjects/Ray.FirebaseModule/vendor/kreait/firebase-php/src/Firebase/Factory.php:344
/Users/ysato/PhpStormProjects/Ray.FirebaseModule/vendor/kreait/firebase-php/src/Firebase/Factory.php:165
...

1つ目のエラーは解消されたが、また別のエラーが発生しているよう。
こちらも前回のエラーと同様に、低いバージョンでは必要となるファイルが存在していないことが問題になっているので、存在するバージョンを指定する。

composer.json
    "require": {
        "php": ">=7.1.0",
        "ray/di": "^2.9",
        "kreait/firebase-php": "^4.30",
+        "google/cloud-core": "^1.19"
    },
> vendor/bin/phpunit
PHPUnit 7.5.0 by Sebastian Bergmann and contributors.

.....                                                               5 / 5 (100%)

Time: 123 ms, Memory: 16.00MB

無事ローカルでのエラーも消え、Travis CIでのエラーがなくなりました!
image.png

scrutinizer

こちらも設定ファイル(.scrutinizer.yml)がスケルトンに入っているのでCIとしての設定は特に必要なし
画像の赤枠内の各バッジをクリックすることでバッジのコードを取得できる。
スクリーンショット 2019-08-22 12.04.46.png

coverageがNot enabled

スケルトンの設定でTravis CIで出力されたカバレッジがscrutinizerに送られているが、設定がデグレしているようなので修正。

.travis.yml
    script:
-    - ./vendor/bin/phpunit -v --coverage-clover ./build/logs/clover.xml
+    - ./vendor/bin/phpunit -v --coverage-clover=coverage.clover
    after_script:
    - wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover;

ついでにPRも出しておきます

image.png

全て正常に表示されました!

Packagistで公開

Submit Packageしておしまい!

image.png

と思ったのですが、タグをつけてあげないとdev-masterになってしまうため、minimum-stability:devでないとインストールすることができなくなるようです。
(ブランチ名でもバージョン制御をしてくれるようですが、そちらもdev扱いになってしまいます)
1.0.0としてタグを登録します。

$ git tag 1.0.0 [revision-number]
$ git push origin 1.0.0

参考:
https://getcomposer.org/doc/articles/versions.md#tags
git tagの使い方まとめ

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

PHP For Beginnersチュートリアル その15 会員登録フォームにパスワードリセットを実装する

このシリーズの目的

体系的なwebコーディングの訓練ができるようになるためにPHPの初学のきっかけかつ、PHPでログインフォームやフォームを実装することができるようになるために

PHP For Beginners

上記のチュートリアルを進めているのでその備忘録。

前回

内容

今回のチュートリアル

How To Create Forgot Password System In PHP & MySQLi [2018]

このチュートリアルでやること

・ログインフォームにパスワードリセットを実装する
(その12で作成した成果物に追加する)

成果物

forgotPassword.php
<?php 

    $msg = "";
    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\Exception;
    use PHPMailer\PHPMailer\SMPT;

    require_once 'function.php';


    if (isset($_POST['email'])) {
        $conn = new mysqli('localhost','root','','register');

        $email = $conn->real_escape_string($_POST['email']);

        $sql = $conn->query("SELECT id FROM users WHERE email='$email' ");
        if ($sql->num_rows > 0) {

            $token = generateNewString();

            $conn->query("UPDATE users SET token='$token',
                            tokenExpire = DATE_ADD(NOW(), INTERVAL 5 MINUTE)
                            WHERE email = '$email'
                ");

            mb_language("japanese");
            mb_internal_encoding("UTF-8");
            require 'vendor/autoload.php';
            require 'Mailtrap-config.php';


                $mail = new PHPMailer();

                // Server

                $mail->SMTPDebug = 0; //本番では0とかにする。
                $mail->isSMTP();
                $mail->SMTPAuth = true;
                $mail->Host = MAIL_HOST;
                $mail->Username = MAIL_USERNAME;
                $mail->Password = MAIL_PASSWORD;
                $mail->SMTPSecure = MAIL_ENCRPT;
                $mail->Port = SMTP_PORT;

                // Recipients
                $mail->setFrom(FROM_MAIL);
                // $toname = mb_encode_mimeheader("$name", 'ISO-2022-JP', 'B', "\n");
                $mail->addAddress($email);
                // $mail->addAttachment($attachment);

                // Content
                $mail->Subject = mb_encode_mimeheader("Reset Your Password", "ISO-2022-JP", "UTF-8");
                $mail->Body = mb_convert_encoding("
                    パスワードリセットのリクエストがありましたので以下のリンクをクリックしてパスワードのリセットを行ってください。:<br><br>
                    <a href='http://localhost/Laravel/PHPMailer/Training/phptutorial17/resetPassword.php?email=$email&token=$token'>パスワードリセットはこちらから</a><br>

                    七花 京
                ","JIS","UTF-8");
                $mail->CharSet = 'ISO-2022-JP';
                $mail->Encoding = "7bit";

                // Select HTML or NOT

                $mail->isHTML(true);

               if ($mail->send())
                    exit(json_encode(array("status" => 1, "msg" => 'Please Check Your Email Inbox!')));

                 else 
                    exit(json_encode(array("status" => 0, "msg" => 'Something Wrong Just Happened')));

                } else
                    exit(json_encode(array("status" => 0, "msg" => 'Please Check Your Input!')));
    }

 ?>



 <!DOCTYPE html>
 <html>
 <head>
    <title>Forgot Password?</title>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js">
    </script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" type="text/javascript">
    </script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js">
    </script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js">
    </script>
    <link rel="stylesheet" type="text/css" href="css/forgotPassword.css">
 </head>

 <body>
    <div class="container">
        <div class="row justify-content-center">
            <div class="form col-md-6">
                <form action="forgotPassword.php" method="post" enctype="multipart/form-data">
                    <input  class="form-control" id="email" placeholder="Your Email Address"><br><br>
                    <input type="button" class="btn btn-primary" value="Reset Your Password"><br><br>

                    <p id="response"></p>
                </form>
            </div>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.4.1.min.js"integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="crossorigin="anonymous"></script>

    <script>
        let email = $('#email');

            $(document).ready(function () {
                $(".btn-primary").on('click',function () {
                if(email.val() !="") {

                    email.css("border","1px solid green");

                    $.ajax({
                        url:'forgotPassword.php',
                        method:'POST',
                        dataType:'json',
                        data:{
                            email:email.val()
                        }, success:function(response) {
                            if (!response.success) 
                                $("#response").html(response.msg).css('color','red');

                            else

                            $("#response").html(response.msg).css('color','green');
                        }
                    });

                } else
                    email.css("border","1px solid red");

            });
        });
    </script>
 </body>
 </html>
function.php
<?php 
    function generateNewString($len = 10) {
        $token = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789!$/()*';
        $token = str_shuffle($token);
        $token = substr($token, 0, $len);

        return $token;

    }

    function redirectToLoginPage() {
    header('location:login.php');
    exit();
}

 ?>

resetPassword.php

<?php
    require_once "function.php";

    if (isset($_GET['email']) && isset($_GET['token'])) {
        $conn = new mysqli('localhost','root','','register');

        $email = $conn->real_escape_string($_GET['email']);
        $token = $conn->real_escape_string($_GET['token']);

        $sql = $conn->query("SELECT id FROM users WHERE
            email='$email' AND token='$token' AND token<>'' AND tokenExpire > NOW()
        ");

        if ($sql->num_rows > 0) {
            $newPassword = generateNewString();
            $newPasswordEncrypted = password_hash($newPassword, PASSWORD_BCRYPT);
            $conn->query("UPDATE users SET token='', password = '$newPasswordEncrypted'
                WHERE email='$email'
            ");

            echo "Your New Password Is $newPassword<br><a href='login.php'>Click Here To Log In</a>";
        } else
            redirectToLoginPage();
    } else {
        redirectToLoginPage();
    }
?>

動作

https://youtu.be/oHF7gYZt0RA

手順

・大枠のアルゴリズムを考える

1.まず、パスワードリセット申請フォーム(以下forgotPassword.php)に入力されたEmailアドレスが、登録情報が登録されているテーブルに存在するか検証

2.検証の結果、存在するのであれば任意の時間制限付きのトークンを発行し、再登録フォームへのリンク誘導を添えたEmailを送信する。
(その12でやったEmail認証リンクの仕組みに時間制限トークンを導入する)

3.リンクをクリックした際にトークン及びEmail認証が正常に通った場合、ランダム文字列でパスワードを作りそれを新しいパスワードとして、成功メッセージとともに画面上に表示し、同時にそれをハッシュ化してデータベースに新しいパスワードとして登録し直す。

なお、通常の実装の場合はこれの後にユーザーがログインして任意のパスワードに自分で登録し直すかまたは、3の段階でユーザーに新しいパスワードを登録させることが考えられる。

・各段階でのアルゴリズムを考える

*データベースの接続などは割愛。

1.forgotPassword.php

・フォームに入力された文字列をエスケープする
・データベースにアクセスする
・エスケープした文字列を変数に代入する
・メールアドレスを検索フォームにし、入力されたメールアドレスがテーブルに存在する時はidを返す。存在しない時はエラーメッセージを表示する

・idが返ってきたらトークンを生成し、検索フォームをメールアドレスにし、データベースに再度アクセスして該当するメールアドレスが存在するデータに時間制限付きのトークンを発行する

・認証リンク付きのメールアドレスを送信し、送信完了の旨のメッセージを表示する。メールが送信できない場合はエラーメッセージを表示する

・PHPMaillerなどを用いて、テーブルに登録されたユーザー宛に認証リンクが記載されたメールを送信する。

2.function.php

・ランダム文字列を生成する処理とリダイレクト処理を書いておいて、requireなどで呼び出せるようにしていおく。
文字列生成とリダイレクトはこれまでのチュートリアルでやったことなのでここでは割愛。

3.resetPassword.php

その12 で作ったものと同じ処理をさせる。

・トークンとメールアドレスを取得する(認証リンクが踏まれているか確認する)。取得できたらデータベースに接続し、取得したメールアドレス及びトークンをエスケープして変数に代入する

・データベースに接続し、メールアドレス・トークン(空ではないという条件を付随する)・トークンに設定された有効期限が現在の時刻より先であるという条件で検索しする

・該当するデータが有ればデータベースに接続し、新しいパスワードを生成する。それをハッシュ化し、データのトークンを削除し、新しく生成したパスワードを登録する。該当するデータがない場合はログインフォームへリダイレクトする

・生成したパスワードとログインフォームへのリンクを表示する

今回のコードの注釈

tokenExpire周りについて
>query("UPDATE users SET token='$token',
        tokenExpire = DATE_ADD(NOW(), INTERVAL 5 MINUTE)
                        WHERE email = '$email'
                ");

今回新しく出てきたのはDATE_ADD(NOW(), INTERVAL)の形。
DATE_ADDは日付と時刻を取得する関数、第一引数に取得する日付及び時刻を設定する。
今回のNOW()は現在の日付と時刻を取得する、つまり処理が行われた時点での日付と時刻を取得することになる。
INTERVALには任意の時間を設定することで第一引数に指定した時刻から設定した分先の時間を取得する。
つまり、今回はINTERVALに5分と設定したので処理をが行われた時点の日付と時刻から5分先の時間を取得し、tokenExpaireに代入するということになる。

<>演算子
$sql = $conn->query("SELECT id FROM users WHERE
            email='$email' AND token='$token' AND token <> '' AND tokenExpire > NOW()
        ");


<>は左辺と右辺が等しくないということを表す比較演算子及び配列演算子。
今回は万が一他者にメールアドレスと認証リンク及び空のトークン利用されてパスワードをリセットされないようにするために用いている。

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

Laravel【準備編】

環境:mac
初laravelのため、備忘録です。

phpのバージョンを確認

$ php -v

こんな風に出てきます。
スクリーンショット 2019-08-22 2.01.23.png

composerの用意

composerを移動、パーミッション設定

##ダウンロードしたcomposer.pharが入っているフォルダに移動します。
$ cd /Users/ユーザー名/Downloads 
※ユーザー名には自分のユーザー名を入れてください。

##composer.pharをbinに移動します
$ sudo mv composer.phar /usr/local/bin/composer
※binがなかったら作成しましょう。
$ udo mkdir /usr/local/bin

##パーミッション設定
$ chmod a+x /usr/local/bin/composer

Laravelをインストール

$ composer global require "laravel/installer"

環境変数を設定

$ echo export PATH=\"$HOME/.composer/vendor/bin:\$PATH\" >> ~/.bash_profile

環境変数を読み込み

$ source ~/.bash_profile

プロジェクトのテンプレートを生成

$ laravel new app

ディレクトリ移動

$ cd app

ローカルサーバーを起動

$ php artisan serve

ブラウザで開く

http://127.0.0.1:8000
へアクセスし、この画面が表示されれば成功です。
スクリーンショット 2019-08-22 1.58.57.png

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

初めてのLaravel【準備編】

環境:mac
初laravelのため、備忘録です。

phpのバージョンを確認

$ php -v

こんな風に出てきます。
スクリーンショット 2019-08-22 2.01.23.png

composerの用意

composerを移動、パーミッション設定

##ダウンロードしたcomposer.pharが入っているフォルダに移動します。
$ cd /Users/ユーザー名/Downloads 
※ユーザー名には自分のユーザー名を入れてください。

##composer.pharをbinに移動します
$ sudo mv composer.phar /usr/local/bin/composer
※binがなかったら作成しましょう。
$ sudo mkdir /usr/local/bin

##パーミッション設定
$ chmod a+x /usr/local/bin/composer

Laravelをインストール

$ composer global require "laravel/installer"

環境変数を設定

$ echo export PATH=\"$HOME/.composer/vendor/bin:\$PATH\" >> ~/.bash_profile

環境変数を読み込み

$ source ~/.bash_profile

プロジェクトのテンプレートを生成

$ laravel new app

ディレクトリ移動

$ cd app

ローカルサーバーを起動

$ php artisan serve

ブラウザで開く

http://127.0.0.1:8000
へアクセスし、この画面が表示されれば成功です。
スクリーンショット 2019-08-22 1.58.57.png

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

【cakePHP3.x】常時SSL化対応

cakePHPで作ったPJをさくらレンタルサーバに反映させたのですが、さくらの案内通りだとうまくいかなかったので.htaccessファイルのメモ

さくらの案内

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

実際はwebroot/.htaccessを下記に変更

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む