20200626のPHPに関する記事は15件です。

MacにPHP環境を(再)構築する & Webサーバー:Apacheの設定をする

やりたいこと

MacにPHP環境を構築する。Macには始めからPHPがインストールされているが、任意のバージョンを入れたい。この記事 でAmazon Linux 2にphp7.4をインストールしたので、自分のMac PCにphp7.4.Xをインストールしたい。

やったこと

現在のバージョンを確認

とりあえず今入っているphpのバージョンを確認する。

$ php -v
PHP 7.X.X (cli) (built: Jun 12 2020 00:04:10) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.X.X, Copyright (c), by Zend Technologies

Homebrewをupgrade

本題とは直接関係ありませんが、Homebrewをupgradeします。Homebrewのインストールが済んでいない方はこちらから。

$ sudo brew upgrade

PHP7.Xを検索

Homebrewを使ってインストールする。まずは現在インストールできるphpのバージョンを確認する。
Homebrewのインストールが済んでいない方はこちらから。

$ brew search php@7
==> Formulae
php@7.2                             php@7.3                        php@7.4

PHP7.4をインストール

Homebrewを使ってphp7.4をインストールできることを確認できたので、実際にインストールする。

$ sudo brew install php@7.4

PHPのPATHを設定

環境変数にインストールしたPHPのPATHを設定する。

$ sudo echo 'export PATH="/usr/local/opt/php@7.4/bin:$PATH"' >> ~/.bash_profile
$ sudo echo 'export PATH="/usr/local/opt/php@7.4/sbin:$PATH"' >> ~/.bash_profile

php@7.4の部分(2箇所)は、適宜インストールしたいバージョンに置き換えてください。

PHPを起動

$ brew services start php

再起動後は一度ターミナルを閉じないといけないらしい。

再度バージョンを確認する

ターミナルを再起動し、バージョンを確認する。

$ php -v
PHP 7.4.7 (cli) (built: Jun 12 2020 00:04:10) ( 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

php7.4.7が入りました。めでたし。

Apacheの設定ファイル: httpd.conf を更新

PHPをlocalhostで使うために、Macに標準インストールされているApacheにPHPの設定を行う。
はじめに、/etc/apache2/ 配下にあるhttpd.confファイルを更新する。

$ cp /etc/apache2/httpd.conf /etc/apache2/httpd.default.conf
$ sudo vim /etc/apache2/httpd.conf

ずらっーーーーーーといろいろ書かれたテキストファイルが開かれる。
このページを読んでいる方は自分のような初学者がほとんどだと思うので、ここで必要最低限のvimの使い方も書いときます。
ベテランの方にはくどく感じられると思うけど、まぁこのページ読まんよね(´・ω・`)
vimを開くとコマンドモードになっているので、

/php7_module [ENTER]

と入力する([ENTER]はキー)。/文字列は"文字列"を検索するコマンド。
\#LoadModule php7_module 略...が出てくるので、次に iを入力する。 iは挿入モードに入るコマンド。
出てきた次の行に1行追加する。

httpd.conf
#LoadModule php7_module libexec/apache2/libphp7.so <-この行の下に
LoadModule php7_module /usr/local/Cellar/php/7.4.7/lib/httpd/modules/libphp7.so <- この行を追記

php7_module7.4.7libphp7は自分の環境にインストールされているphpのバージョンに置き換えてください。

次に、[ESC]キーを押して、コマンドモードに戻る。

/dir_module [ENTER]

<IfModule dir_module>が出てくるので、iを入力し、挿入モードへ。出てきた次の行に追記する。

httpd.conf
<IfModule dir_module>
    DirectoryIndex index.php index.html <- index.phpを追加
</IfModule>

次に、[ESC]->/mime_module [ENTER]->iと進む。
<IfModule mime_module>が出てくるので、この項目の最下行にスクロールし、1行追加する。

httpd.conf
<IfModule mime_module>
    ~~~略~~~
    #AddOutputFilter INCLUDES .shtml <-この行の下に
    AddType application/x-httpd-php .php <- この行を追記
</IfModule>

続いて、このファイル(httpd.conf)の最下行までスクロールし、以下3行を追加する。

httpd.conf
<IfModule php7_module>
PHPINIDir /usr/local/etc/php/7.4/
</IfModule>

php77.4は自分の環境にインストールされているphpのバージョンに置き換えてください。
次に、[ESC]->ZZと進む。ZZは上書き保存し、vimを終了するコマンド。

長かったですね。。これでApacheの設定ファイルの更新は終わりです。あともうちょっと!

Apacheを再起動

設定を反映するために、Apacheを再起動する。

$ sudo apachectl restart

正しく動くか確認

ローカル環境のデフォルトのドキュメントルート /Library/WebServer/Documents/ 配下にテストファイルを置く。

$ cd /Library/WebServer/Documents
$ vim phpinfo.php

もうvimは使えますね。挿入モードに入り、以下を記述する。

phpinfo.php
<?php

phpinfo();

?>

コマンドモードに戻り、上書き保存し、vimを終了する。
最後に、ブラウザのURL欄に localhost/phpinfo.phpを入力し、ドヤ顔でエンターを押すとインストールされているphpの情報のページが表示されます。めでたしめでたし!

ここまで上手く行けば、あとは、/Library/WebServer/Documents/配下にphpファイルを置いて、ブラウザでlocalhost/<file name>.phpにアクセスすれば確認できる!!!やったね!!!!!

参考

こちらのサイトを参考にさせていただきました。ありがとうございました。

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

CentOS とOSXでphp, fetch,javascriptの挙動が違う

OSXはこれで行けたが、CentOSではダメ

Javascript

function checkNewOrder(uuid) {
            var res = fetch("./test.php", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    //引数が色々あって...。
                })
            }).then((response) => {
                if (response.ok)
                    return response.json(); // レスポンスをテキストとして変換する
            }).then((rcvData) => {
                console.log("rcvData", rcvData); //とりあえず確認
                if (rcvData.length > 0) {
                    let msg = "新たにデータが入りました<br>\n";
                    //rcvData[i]["NAME"]とかを使って色々処理
                }
            });
        }

php

$dbh->beginTransaction();
$stmt = $dbh->query("select * from TableName;");
$stmt->execute();
$dbh->commit();
$result = $stmt->fetchall(PDO::FETCH_ASSOC);
echo json_encode($result, JSON_UNESCAPED_UNICODE);

CentOSだとJSの console.log("rcvData", rcvData);のところで「rcvDataって何?」ってなってしまう。

結局こうやって対応できた

Javascript

function checkNewOrder(uuid) {
            var res = fetch("./test.php", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    //引数が色々あって...。
                })
            }).then((response) => {
                if (response.ok)
                    return response.text(); // jsonからtextに変更した
            }).then((rcvData) => {
                var rcv = JSON.stringify(rcvData); //JSONオブジェクトに変換
                if (rcv.length > 0) {
                    let msg = "新たにデータが入りました<br>\n";
                    //rcvData[i]["NAME"]とかを使って色々処理
                }
            });
        }

response.json();response.text();にしてJSON.stringifyでオブジェクト化するとCentOSでOKだった。
OSXとCentOS で挙動が違うのはなぜ?

でも、結局こっちかな

function checkNewOrder(uuid) {
    const response = fetch("./checkNewOrder.php", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            "uuid": ???; //パラメータとか
        })
    }).then((response) => {
        if (response.ok)
            return response.json(); // JSON
       // return response.text(); 
    }).then((response) => {
        console.log("response", response);
        //var rcvData=JSON.parse(response); //result.text()  だったらこっち
        //何らかの処理;
    });
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

書籍「テスト駆動開発」をPHPで写経するためのDocker環境構築手順

はじめに

会社でテスト駆動開発の輪読会をやることになり、
あわせて書籍の中に出てくるコード(書籍内ではJavaで記述されている)をPHPで写経して理解を深めようと考えていました。

この勉強会のためだけに、自分のMacにcomposerをインストールするのは嫌だったので、
使い捨てのコンテナでcomposerおよびphpunitを動かす方法を調べました。

同じことを考える人が少なくとも10人くらいはいると思うので、記録に残しておきます。

対象の方

  • 環境構築に時間をかけずに、さっさとPHPで書籍「テスト駆動開発」の写経をはじめたい方

前提条件

  • PHPおよびPHPUnitで写経したい
  • ローカルPCにcomposerをインストールしたくない
  • ローカルPCにDockerをインストール済み

各種バージョン

PHP 7.4.7
PHPUnit 9.2.5
Composer version 1.10.7

手順

ご自身の環境にあわせて、適当なディレクトリを作り、手順を開始してください。

composer.json の作成

docker run --rm -it -v $PWD:/app composer:latest composer init

ls
# composer.json

phpunit のインストール

docker run --rm -it -v $PWD:/app composer:latest composer require --dev phpunit/phpunit

ls
# composer.json composer.lock   vendor

コード用ディレクトリ作成

mkdir src
mkdir test

ls
# composer.json composer.lock   src     test        vendor

composer.json に autoload の記載を追加

vi composer.json

記述例は以下の通りです。

{
    "name": "root/app",
    "require": {},
    "require-dev": {
        "phpunit/phpunit": "^9.2"
    },
    "autoload": {
        "psr-4": { "App\\": "src" }
    }
}

composer dump-autoload の実行

docker run --rm -it -v $PWD:/app composer:latest composer dump-autoload

ls vendor/autoload.php
# autoload.php ファイルが作成されていることを確認する

phpunit.xml の作成

vi phpunit.xml
<?xml version="1.0" encoding="UTF-8" ?>
<phpunit colors="true"
         verbose="true"
         bootstrap="vendor/autoload.php">
    <testsuites>
        <testsuite name="Sample">
            <directory>test</directory>
        </testsuite>
    </testsuites>
</phpunit>

テストコード、テスト対象コードを書く

サンプルコード

phpunit 実行

docker run --rm -it -v $PWD:/app composer:latest vendor/bin/phpunit
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

phpの"->"を説明してみる。

オブジェクト指向を学んでいて、頻繁に こういう記述を目にします。

index.php
echo $curry->name;

今回は、自分の言葉で自分の言葉で->の意味を説明して見ます。
結論から先に言うと、「curryという実体が、設計図内にあるnameにアクセスする」です。(ドルマークは、書式スタイルが変わってしまうので、省略しています。)これだけだとわかりずらいので、設計図と実体を表したコードを記述しました。↓

index.php
class Menu{
public $name;
}
$curry=new Menu();
$curry->name='CURRY';
echo $curry->name;

最初にMenuという設計図を作成し、設計図をもとに、curryという実体を作っていきます。curryは、設計図のnameにアクセスし、(curry->name)その値をCURRYと定義しました。

つまり、->は、インスタンスのプロパティ(設計図の材料。今回はname。)を参照する時に用いられるものです。

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

PHP8.0.0α1がリリースされたのでさっそくJITの威力を体感する(した)

2020/06/25、PHP8.0.0α1がリリースされました
PHP8系の初のバージョンです。
ただし名前のとおりα版であり、まだまだ実環境で使えるものではありません。
今後は2020/08/04にフィーチャーフリーズ、即ち新機能の取り込みが終了し、その後はβやRCで徐々に完成度を高めながら、2020/11/26に正式版がリリースされる予定です。

そんなわけでPHP8の目玉、JITの性能を試してみることにしましょう。

今回はXAMPPに導入してWebサーバとして動かすことができなかったので、以下のベンチマークはコマンドラインで実行した結果となります。
きっとそのうちXAMPPも対応してくれるはず。

インストール

QA ReleasesからVS16 x64 Thread Safeのzipをダウンロード。
適当なディレクトリに解凍。
php.ini-developmentphp.iniにコピー。

php.iniを変更。
memory_limit = 1024Mにする
date.timezone = "Asia.Tokyo"にする
extension_dir = "ext"のコメントアウトを外す

ベンチマークの設定

デフォルトの設定
・上の『php.iniを変更』のまま。ほぼ初期状態。

opcache有効の設定
・『デフォルトの設定』に対して、以下を追加する。
zend_extension=opcache
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=0x7FFFBFFF

JIT有効の設定
・『opcache有効の設定』に対して、以下を追加する。
opcache.jit_buffer_size = 128M

ベンチマーク結果

プログラムは最後に載せておきますが、JITのRFCで使われていたマンデルブロ集合を計算するやつです。
使用したCPUはIntel i7-9700 3.00GHz。
測定結果の単位は秒です。

デフォルト opcache有効 JIT有効 参考7.4.7
0.814755 0.355585 0.106190 0.960383
0.818260 0.356907 0.106928 0.938955
0.822746 0.353799 0.106061 0.940920
0.817202 0.353423 0.106768 0.951347
0.819391 0.353574 0.106117 0.936791

本当かよ?????????

まずPHP7.4.7からPHP8にアップデートするだけで処理時間が1割削減されています。
ただでさえ新機能てんこ盛りだってのに、そのうえ速度も上がるとかどうなってるんだPHP8。

次いでopcacheを有効にすると処理時間が半分になります。

最後にJITを有効にしたら、処理時間がopcache有効状態の30%になりました。
30%縮まりました、ではありません。
なんだこれ。

ということで、JITを有効にするだけで、処理速度がPHP8デフォルト設定の13%になりました。
どういうことかというと、元々1分かかっていた処理が8秒で終わるようになります。
足枷を外したとかいうレベルじゃねーぞ。
これ本当に計算してるのか?
計算結果が固定値だから結果だけどこかに保存してるとかじゃないよな?

しかもこれ、opcacheやJITの設定はほぼ初期値で、とりあえず有効にしただけという状態ですからね。
チューニングすればさらに早くなることでしょう。
面倒なので今回はそこまでやってませんが。

プリロード

PHP7.4.0で入ったプリロードで更なる高速化を図ってみましょう。

opcache.preload="path/to/preload.php"を設定
preload.phpの中身はopcache_compile_file('path/to/mandelbrot.php');

Error Preloading is not supported on Windows

はい。

実はWindowsではプリロードを使えません。

PHP7.4.0リリース時点ではWindowsでもプリロードを使えていたのですが、その後PHP7.4.2で提供が中止されてしまいました。
実はWindowsのプリロードはASLRのせいで本当のプリロードじゃないんだとかいうことらしいですがよくわかりません。

そんなわけで*nix勢あとよろ。

感想

あくまでCPUだけをがりがり使うプログラムに対しての検証結果です。
ファイルやデータベースへのアクセスが多くなる一般的なプログラムについては、また異なる結果になるでしょう。
またLaravelなど普通のWebアプリがどの程度高速化されるかも、今回は調べられていません。

とはいえ、そうはいってもさすがにこの結果は驚異的です。
もはや環境を作れないので試していませんが、PHP5.6からPHP7で速度が倍以上になったという過去もあります。
全部合わせるとPHP8が処理にかかる時間はPHP5の5%とかです。
PHP8はもはや、PHP5時代とは別次元の速度を手にいれました。

ベンチマークプログラム

マンデルブロ集合を計算するやつのほぼコピペです。

実行はコマンドラインからpath/to/php8/php.exe path/to/mandelbrot.phpとするだけ。

mandelbrot.php
define("BAILOUT", 16);
define("MAX_ITERATIONS", 5000); // 1000だと早すぎたので

class Mandelbrot
{
    public function __construct()
    {
        $output = '';
        $d1 = microtime(1);
        for ($y = -39; $y < 39; $y++) {
            for ($x = -39; $x < 39; $x++) {
                if ($this->iterate($x/40.0, $y/40.0) == 0) {
                    $output .= '*';
                } else {
                    $output .= ' ';
                }
            }
            $output .= "\n";
        }
        $d2 = microtime(1);
        $diff = $d2 - $d1;
        echo $output; // 出力は最後にまとめた
        printf("\nPHP Elapsed %0.6f\n", $diff);
    }

    public function iterate($x, $y)
    {
        $cr = $y-0.5;
        $ci = $x;
        $zr = 0.0;
        $zi = 0.0;
        $i = 0;
        while (true) {
            $i++;
            $temp = $zr * $zi;
            $zr2 = $zr * $zr;
            $zi2 = $zi * $zi;
            $zr = $zr2 - $zi2 + $cr;
            $zi = $temp + $temp + $ci;
            if ($zi2 + $zr2 > BAILOUT) {
                return $i;
            }
            if ($i > MAX_ITERATIONS) {
                return 0;
            }
        }
    }
}

$m = new Mandelbrot();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP gzip圧縮転送支援スクリプト

主にmod_deflateモジュールによる圧縮転送が利用できないサーバで圧縮転送を行なうためのスクリプトです。
対象ファイルがリクエストされた際にPHPスクリプトでGZIP圧縮して送信しますが、一度圧縮したファイルはサーバ上にキャッシュファイルとして保存し2度目以降のリクエスト時にはキャッシュを利用しますので、mod_deflateでの圧縮負荷を抑えたい場合にも利用できるかと思います。

本スクリプトで圧縮可能なデータは静的ファイルのみで、CMS等で動的に出力されるHTMLデータは対象外となります。

設置方法

gzip圧縮転送したいファイルのあるディレクトリにgzenc.phpを置き、同階層の .htaccess に以下を追記してください。

.htaccessへの追記内容

.htaccess
#BEGIN gzenc.php
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_URI} ^.*\.(html?|css|js|xml|txt)$ [NC]
    RewriteRule ^.*$ gzenc.php?f=$0 [L]
</IfModule>
#END gzenc.php

mod_rewriteが利用できることが前提になります。
指定した拡張子のファイルに対してアクセスがあった際、URLを変えずそのファイル名をgzenc.phpへ渡すよう設定しています。

gzenc.php

gzenc.php
<?php

// 有効な拡張子とcontent-typeのリスト
// .htaccessへのRewriteCond %{REQUEST_URI}設定とペアになるよう指定
$enabledExtensions = [
    'html'  => 'text/html',
    'htm'   => 'text/html',
    'css'   => 'text/css',
    'js'    => 'application/javascript',
    'xml'   => 'application/xml',
    'txt'   => 'text/plain',
];

// レスポンスへのETag出力及びリクエストのIf-None-Matchとの照合
define('USE_ETAG', false); // 使用:true / 非使用:false

// Accept-Encodingを確認しての圧縮可否判断(falseの場合はAccept-Encodingに関わらず常に圧縮)
define('CHECK_ACCEPT_ENCODING', true); // 使用:true / 非使用:false

// GZIP圧縮レベル
define('COMPRESS_LEVEL', -1); // -1(デフォルト), 0(非圧縮), 1(低圧縮低負荷) ~ 9(高圧縮高負荷)
/**
 *
 */

// キャッシュ保存ディレクトリ
define('CACHE_DIR', __DIR__. '/gzenc_cache');

// キャッシュ保存ディレクトリが無ければ作成してダミーhtml作成
if(!file_exists(CACHE_DIR)) {
    mkdir(CACHE_DIR, 0700);
    touch(CACHE_DIR. '/index.html');
}

// ファイル名取得
if(isset($_GET['f'])) {
    $filePath = realpath($_GET['f']);
    // ディレクトリトラバーサル対策
    // 指定ファイルパスが本スクリプトのカレント配下でない場合は終了
    if(strpos($filePath, __DIR__) !== 0) notFoundExit();
} else {
    // ファイル名未指定時は終了
    notFoundExit();
}

// 指定ファイルが存在しなければ終了
if(!file_exists($filePath)) notFoundExit();

// 指定ファイルの拡張子取得
$ext = strtolower(preg_replace('/.*\.(\w+)$/', "$1", $filePath));

// 取得した拡張子が有効リストに無ければ終了
if(!array_key_exists($ext, $enabledExtensions)) notFoundExit();

// リクエストヘッダAccept-Encodingにgzipが含まれていなければ元ファイルをそのまま渡して終了
if(CHECK_ACCEPT_ENCODING &&
    in_array('gzip', preg_split('/,\s+/', filter_input(INPUT_SERVER, 'HTTP_ACCEPT_ENCODING'))) === false) {
    header(
        sprintf('Content-type: %s; charset=%s',
            $enabledExtensions[$ext],
            mb_detect_encoding(file_get_contents($filePath, false, NULL, 0, 2048), 'utf-8, sjis, euc, jis')
        )
    );
    readfile($filePath);
    exit;
}

// キャッシュファイルパス
$md5FilePath = md5($filePath);
$cacheFilePath = CACHE_DIR. '/'. $md5FilePath. '.gz';
$jsonFilePath = CACHE_DIR. '/'. $md5FilePath. '.json';

// キャッシュファイルの有無確認
if(file_exists($cacheFilePath)) {
    // キャッシュより実体または本スクリプトのほうが新しい場合
    // 実体または設定が更新されているとみなしキャッシュを削除
    if(filemtime($cacheFilePath) < filemtime($filePath) ||
        filemtime($cacheFilePath) < filemtime(__FILE__)
    ) {
        unlink($cacheFilePath);
        unlink($jsonFilePath);
    }
}

// クライアントからのIf-None-Matchを取得
$ifNoneMatch = filter_input(INPUT_SERVER, 'HTTP_IF_NONE_MATCH');

// キャッシュファイルあり
if(file_exists($cacheFilePath)) {
    // キャッシュに付随するプロパティファイルを取得
    $properties = file_exists($jsonFilePath) ?
        json_decode(file_get_contents($jsonFilePath), true) : [];

    // ETagチェック
    if(isset($properties['etag']))
        checkEtag($ifNoneMatch, isset($properties['etag']) ? $properties['etag'] : '');

    // レスポンスヘッダ出力
    header('Content-type: '. $enabledExtensions[$ext].
        (isset($properties['charset']) && $properties['charset'] ?
            "; charset={$properties['charset']}" : '') );
    header('Content-Encoding: gzip');
    if(isset($properties['content_length']))
        header('Content-Length: '. $properties['content_length']);
    if(USE_ETAG && isset($properties['etag']))
        header('Etag: '. $properties['etag']);
    if(!USE_ETAG && isset($properties['last_modified']))
        header('Last-Modified: '. $properties['last_modified']);

    // データ出力
    readfile($cacheFilePath);
}

// キャッシュファイル無し
else {
    // ファイル読込み
    $file = file_get_contents($filePath);

    // エンコーディング取得
    $enc = mb_detect_encoding($file, 'utf-8, sjis, euc, jis');

    // gzencode
    $file = gzencode($file, COMPRESS_LEVEL);

    // キャッシュファイル保存
    file_put_contents($cacheFilePath, $file);

    $eTag = sprintf('"%s"', md5($file). '-gzip');
    $lastModified = gmdate('D, d M Y H:i:s T', filemtime($filePath));
    $contentLength = strlen($file);

    // プロパティファイル保存
    file_put_contents($jsonFilePath,
        json_encode([
            'charset' => $enc,
            'etag' => $eTag,
            'content_length' => $contentLength,
            'last_modified' => $lastModified,
        ])
    );

    // ETagチェック
    checkEtag($ifNoneMatch, $eTag);

    // レスポンスヘッダ出力
    header('Content-type: '. $enabledExtensions[$ext]. ($enc ? "; charset={$enc}" : '') );
    header('Content-Encoding: gzip');
    header('Content-Length: '. $contentLength);
    if(USE_ETAG) header('Etag: '. $eTag);
    if(!USE_ETAG) header('Last-Modified: '. $lastModified);

    // データ出力
    echo $file;
}

exit;

// ETagチェック
function checkEtag($ifNoneMatch, $eTag) {
    if(USE_ETAG && $ifNoneMatch !== '' && $eTag !== '' && $ifNoneMatch === $eTag) {
        // If-None-MatchとETagが共に空ではなく双方が同一なら304を返して終了
        header('HTTP/1.1 304 Not Modified');
        exit;
    }
}

// NotFound
function notFoundExit() {
    header('HTTP/1.1 404 Not Found');
    print '<html><body>Not Found</body></html>';
    exit;
}

キャッシュディレクトリ

実行するとスクリプトと同階層にキャッシュファイル保存用にgzenc_cacheディレクトリを作成します。
一度圧縮したコンテンツデータはその中へ保存して二度目以降のリクエスト時にはキャッシュファイルを利用します。

スクリプトの設定項目など

// レスポンスへのETag出力及びリクエストのIf-None-Matchとの照合
define('USE_ETAG', false); // 使用:true / 非使用:false

trueに設定するとETagを出力するようになります。
ETag込みのブラウザキャッシュは内容の変化に対しては追従させやすくなりますが、ブラウザは次回表示時にブラウザキャッシュの内容が更新されていないかサーバに対して問い合わせるプロセスが発生しますので、何度も呼ばれる割に更新頻度は低いといったファイルが多い場合はかえってもたつきを感じることもあるかもしれません。

// Accept-Encodingを確認しての圧縮可否判断(falseの場合はAccept-Encodingに関わらず常に圧縮)
define('CHECK_ACCEPT_ENCODING', true); // 使用:true / 非使用:false

通常はtrueのままで大丈夫です。
trueの場合はブラウザから受け取るAccept-Encodingの内容によって圧縮データと非圧縮データどちらを送信するかを振り分けます。
サーバ側でWAFを使用している場合など稀にAccept-Encodingが削除されている場合があり、そういった場合でも圧縮転送を行いたい場合はfalseにします。
falseにすることで強制的に圧縮したデータを送るようになりますが、非対応のごく古いブラウザなどでは表示できなくなります。

使用中止する場合

設置の際に .htaccessに追記した部分、及びgzenc.phpとキャッシュディレクトリgzenc_cacheを削除して下さい。

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

jsonのPHP、JSでのエンコードとか

php

fetchall(PDO::FETCH_ASSOC) — 結果セットに 返された際のカラム名で添字を付けた配列を返す。
json_decode — JSON形式の文字列をオブジェクトもしくは連想配列に変換する
json_encode — 値をJSON形式にして返す

JSON

JSON.parse — JSON形式で書かれた文字列をJavaScriptのJSONオブジェクトに変換する
JSON.stringfy — JSONオブジェクトを文字列に変換する

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

Laravelで"have you enable the php_fileinfo extension?"のエラーでハマったときの解決法(WIN)

概要

laravelでアプリケーションを制作していて、画像ファイルのアップロードの際に
Unable to guess the MIME type as no guessers are available (have you enable the php_fileinfo extension?).
のエラーメッセージが出て解決するのに苦労した話を聞いてほしくて記事にする。
error.png

環境

OS: Windows10 バージョン1909
Laravel: 7.16.1
PHP: 7.4.1
開発環境にMAMPを使用

解決策

php.iniに
extension=php_fileinfo.dll
を追記する

解決までの奮闘記

まずはエラーメッセージの内容を把握(エラーメッセージで検索をかけるなどする)
どうもphp_fileinfoという拡張モジュールがないからMIME typeが推測できねぇ、おまえphp_fileinfo持ってる?
みたいなことを言われてるらしい

ほう

そこでphp_fileinfoで検索するといくつかの参考になりそうなサイトが出てきました。
【PHP】PHP extension(PHP拡張モジュール)の確認と有効化(ext-fileinfo、ext-gd編)
PHP: Fileinfo - Manual

記事の中では
php.iniというファイルにいる
;extension=php_fileinfo.dll
extension=php_fileinfo.dll こう
コメントを外して有効化しましょうということでした。

ですが、php初心者の私はphp.iniがどこにいるかわかりません。
php.ini どこphp.ini MAMPなどで検索して以下のような参考サイトを見つけました。

php.iniファイルの場所/PHPのバージョン確認・変更 - MAMPの使い方
もういい加減覚えよう。php.iniはどこにあるのか

記事によるとMAMPでOpen web start pageした後のphpinfoの中にphp.iniのありかが書かれているようでした。
私の場合は以下のような場所
image.png

よっしゃ見つけた。
と思って意気揚々とphp.iniを開いたのですが
いくら探せどphp_fileinfoという文字列が見つかりません。

fileinfoで検索したときはどこのサイトも
;extension=php_fileinfo.dll
extension=php_fileinfo.dll こう
と言ってます。ですがコメントを外そうにもそもそもないのだから始末が悪い
勝手に追記していいものか判断がつかなかったので、もう少し調べて見ることにしました。

いくつかのサイトやディレクトリを漁っているうちに
C:\MAMP\bin\php\php7.4.1\ext
の中にphp_fileinfo.dllがあるのを発見しました。
しかし上記php.iniの場所は
C:\MAMP\conf\php7.4.1\php.ini
であり微妙に違います。
ですが、MAMPのphpのバイナリはC:\MAMP\bin\php\php7.4.1\のなかにphp.exeがあるので
コンフィグファイルとバイナリが別の場所にいるだけで
コンフィグファイルに書いとけばphpがよしなにやってくれるのでは思い
C:\MAMP\conf\php7.4.1\php.ini の中に
extension=php_fileinfo.dll を追記しました。
image.png

そしてサーバー再起動し、いざ画像のアップロードを試したところ
無事アップロードできましたとさ
めでたしめでたし

あとがき

なぜphp.iniにphp_fileinfoの記載がなかったのか未だ謎ですが
phpのバージョンなのかMAMPが原因なのかわかりません。知っている方いたら教えてください。
phpが嫌いになるとこでした、無事解決してよかったです。
同じような不具合に悩まれてる方の助けになれば幸いです。

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

PHPのパスワード暗号化(md5とSHA1)&(password_hashとpassword_verify)

PHPでログインフォームを作成する際に、パスワードを暗号化する方法はいくつかあります。

私がudemyなどの動画教材で観た限りでは、md5SHA1でハッシュを返す方法が多かったのですが、
現在はどちらも「非推奨」との事でしたので、コードの書き換えを行いました。

md5の暗号化 (Before)

md5で暗号化した場合は、認証の際にもう一度md5をかけてイコール判定を行います。

Account.php
class Account {

    private $con;
    private $errorArray;

    public function __construct($con) {
      $this->con = $con;
      $this->errorArray = array();
    }

    // アカウント登録:データベースへの挿入
    private function insertUserDetails($username, $email, $pass) {
        $encryptedPw = md5($pw);
        $result = mysqli_query($this->con, "INSERT INTO users VALUES (NULL, '$username', '$email', '$encryptedPw')");

        return $result;
    }

    // ログイン処理:ユーザーネームとパスワード判定
    public function login($username, $pass) {
        $pass = md5($pass);
        $query = mysqli_query($this->con, "SELECT * FROM users WHERE username='$username' AND password='$pass'");

        if (mysqli_num_rows($query) == 1) {
          return true;
        } else {
          array_push($this->errorArray, Constants::$loginFailed);
          return false;
        }
    }


}

password_hashとpassword_verifyの暗号化 (After)

password_hashで暗号化する場合は、password_verifyで認証を行います。

Account.php
<?php
  class Account {

    // 省略

    // アカウント登録:DB挿入
    private function insertUserDetails($un, $fn, $ln, $em, $pw) {
        $encryptedPw = password_hash($pw, PASSWORD_DEFAULT); // 変更
        $date = date("Y-m-d");
        $result = mysqli_query($this->con, "INSERT INTO users VALUES (NULL, '$un', '$fn', '$ln', '$em', '$encryptedPw', '$date')");

        return $result;
    }

    // ログイン処理:ユーザーネームとパスワード判定
    public function login($username, $pass) {
        $query = mysqli_query($this->con, "SELECT password FROM users WHERE username='$username'");
        $checkPass = mysqli_fetch_row($query);

        if (password_verify($pass, $checkPass[0])) {
            return true;
        } else {
            array_push($this->errorArray, Constants::$loginFailed);
            return false;
        }
    }


引数のパスワードに対して、データベース上のパスワードと一致するかをpassword_verifyを用いて判定します。

参考

password_hash
password_verify
安全なパスワードハッシュ

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

Laravel v7で「Formクラスが無い」とエラーが出た時の対処法

formファサードを使おうと思ったら「Formクラスが無い」とゆうエラーがでた

そういえば手動でcomposer.jsonをいじらなきゃいけないことを思い出し、下記をcomposer.jsonに追加。

"require": {
        "laravelcollective/html": " 7.17.2"
    },

追加したら$ composer updateをする。

しかし下記のエラーが出る

Your requirements could not be resolved to an installable set of packages.
  Problem 1
    - Conclusion: remove laravel/framework v7.17.2
    - Conclusion: don't install laravel/framework v7.17.2
    - Conclusion: don't install laravel/framework v7.17.1
    - Conclusion: don't install laravel/framework v7.17.0
    - Conclusion: don't install laravel/framework v7.16.1
    - Conclusion: don't install laravel/framework v7.16.0
    - Conclusion: don't install laravel/framework v7.15.0
    - Conclusion: don't install laravel/framework v7.14.1
    - Conclusion: don't install laravel/framework v7.14.0
    - Conclusion: don't install laravel/framework v7.13.0
    - Conclusion: don't install laravel/framework v7.12.0
    - Conclusion: don't install laravel/framework v7.11.0
    - Conclusion: don't install laravel/framework v7.10.3
    - Conclusion: don't install laravel/framework v7.10.2
    以下省略

どうやらこの投稿日時点では"laravelcollective/html"のversionがlaravelのv7に対応していないみたい。。

解決としてはlaravelcollective/html"の最新versionを指定してあげれば解決する。

"require": {
        "laravelcollective/html": " 6.1.2"
    },

もう一度$ composer updateをする。
これでいけるはず。

あとは他の記事にもあるように、app.phpファイルに下記を追加。

   'providers' => [
        Collective\Html\HtmlServiceProvider::class,
    ],

    'aliases' => [
        'Form' => Collective\Html\FormFacade::class,
        'Html' => Collective\Html\HtmlFacade::class,
    ],

解決です。

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

【Laravel】Email Verification(メール確認)の実装

概要

一般的なアプリケーションで会員登録する場合、
 1. 仮登録する
 2. メールにURLが届く
 3. URLをクリックして登録が完了する
という流れがありますよね
これができるようにLaravelのファイルをいじっていきましょう

前提

Laravel6以上で行います
あらかじめAuth機能を使えるようにしておいてください

% composer require laravel/ui
% php artisan ui vue --auth
% npm install && npm run dev

また、メールが送れるようにconfig/mail.phpや.envファイルを変更しておいてください

方法

さすがLaravelさん、こういう時のためにちゃんと用意してあるんですね
なんと、いじるファイルはUser.phpとweb.phpの2つだけ
必要な手順は以下の通り

  • マイグレーション
  • User.phpの編集
  • web.phpの編集

マイグレーション

% php artisan migrate

を実行してください
すでに実行している場合は必要ありません
これによって、メール確認に必要なemail_verified_atカラムを含んだusersテーブルが作成されます

User.php

app/User.php
class User extends Authenticatable implements MustVerifyEmail

Userクラスにimplements MustVerifyEmailを追加してください
これに必要なuseはすでに書かれてあります(さすが)
これによって、メール確認に必要な処理(URL生成やメール送信など)が追加されます

web.php

routes/web.php
Auth::routes(['verify' => true]);

Auth::routes()の引数に['verify' => true]を追加してください
これによって、メール確認に関連するルーティングが使用されます

カスタマイズ

ページの保護

仮登録や本登録を行うということは、本登録したユーザーだけがアクセスできるページがあるわけです
middlewareにverifiedを指定することで、ルートを保護できます
例を以下に示します

routes/web.php
Route::get('welcome', function() {
        ......
    })->middleware('verified');

Route::group(['middleware' => ['auth', 'verified']], function() {
    Route::get('index', HelloController@index);
    // このようにして保護したいルートをここに書く
}

ビュー

必要なビューは前提で実行したコマンドによって、すでに生成されています
メール確認のビューはresources/views/auth/verify.blade.phpにあります
自分の好きなようにカスタマイズしましょう

確認後のリダイレクト

メールアドレスを確認した後、自動的に表示させるページがあるといいですよね
デフォルトでは'/home'にリダイレクトすることになっていますが、これを変更するにためには、VerificationController.phpのredirectToにリダイレクト先を指定してください

RouteServiceProvider.phpのHOMEを変更する手段もありますが、こちらは、他のところでRouteServiceProvider::HOMEを参照している部分も一緒に変更できます

app/Http/Controllers/Auth/VerificationController.php
protected $redirectTo = '/home';
app/Providers/RouteServiceProvider.php
public const HOME = '/home'

おわり

簡単でしたね
公式ドキュメントReadoubleを読めばいいので、ここで説明する必要なかったかもしれませんが、備忘録ということで書いてみました

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

PHPでデバッグのやり方

デバッグのやり方を簡単に記述

error_log('hello', 3, "./debug.log");

公式ドキュメント
https://www.php.net/manual/ja/function.error-log.php

logを閲覧する

tail -f debug.log

変数の中身を見る

echo "<pre>";
var_dump($sample);
echo "<pre>";

echo "<pre>";は出力の時に、改行をして見やすくするために入れる。

応用

error_log(var_export($sample), 3, "./debug.log");

これで指定のファイルに、変数の中身を出力して確認ができる。

以上

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

【Laravel】UnitTest、FeatureTest、BrowserTestの違い

はじめに

Laravelにおいて、
テストコードは3種類存在します。

・UnitTest
・FeatureTest
・BrowserTest

この3つの違いがよく分からなかったので、
自分なりにしっくりくるように簡単に整理しました。

UnitTest

/tests/Unit配下にテストを配置する。
一番粒度の細かいテスト。

Service、Model、Middleware、Policyなど、
自分で作ったクラス1つ1つと対応するように
テストクラス(ファイル)を1つ1つ作る。

その対象クラスのメソッド1つ1つの動作を検証するための
テストケースをコードに書いていく。

Controllerクラスのテストは
UnitTestではなくFeatureTestとするほうがしっくりくる。

FeatureTest

/tests/Feature配下にテストを配置する。
UnitTestより粒度を大きくしたテスト。

1つのHTTPリクエスト単位の動きをテストする。
=ルーティング1つごとにテストクラス(ファイル)を作る
=コントローラのアクションメソッド単位でのテスト

UnitTestでテストした各クラスを結合した状態で
コントローラの動き(つまりリクエストを受けてレスポンスを返すまで)をテストするというイメージ。

1つのコントローラアクションに対応したテストクラス(ファイル)の中で、
・ログインした状態でアクセス時の動作
・未ログインアクセス時の動作
・検索パラメータ付けたアクセス時の動作
・ユーザに特定ロールを付けた状態の動作
など1つ1つのケースをテストケースとしてコードを書く。

BrowserTest

/tests/Browser配下にテストを配置する。

実際にブラウザを使って画面にリクエスト、
ボタンをクリック、
マウスホバー、
フォーム送信などの操作をするテストをコードで実行できる。

FeatureTestにさらに
・複数HTTPリクエストの遷移
・画面操作時のJsの動作
のような観点を加えてテストできるイメージ。

Unit、FeatureはPHPUnitで動作するテストなのに対し、
BrowserTestはlaravel/duskというパッケージを使ったテスト。

参考

https://readouble.com/laravel/7.x/ja/testing.html
https://readouble.com/laravel/7.x/ja/dusk.html

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

PHP学習者に送る記事 #1 〜変数・定数・エラー〜

はじめに

こんにちわ、takumi(@takumidiary)です
この記事はPHPを学習し始めて間もない方や、もう一度学び直したい方向けに書いています。僕自身も初歩的な部分を見落としている可能性があるため、アウトプットとしてこの記事を執筆しました。しかし、ある程度PHPを学習された方でも新たな発見があるかも...

この記事の対象者

  • PHPをこれから学習する方
  • PHPの基礎を学び直したい方

間違いがあればご指摘いただけるとありがたいです!

環境

OS : mac Catalina vaersion10.15.5
ディベロッパーツール : MAMP
PHP : 7.3.11

目次

1.PHPってどんな言語?
 1-1.静的と動的
 1-2.PHPプログラムが動く仕組み
2.PHPの基本的構文
 2-1.PHPブロック
 2-2.識別子
3.変数
 3-1.PHPの変数
 3-2.スコープ
  3-2-1.グローバルスコープ
  3-2-2.ローカルスコープ
 3-3.スーパーグローバル変数
4.定数
 4-1.定義済み定数
5.エラー
 5-1.エラーの表示
6.おわりに

1. PHPってどんな言語?

PHPという言語は簡単に言うと
WEBサイトの作成を目的とした動的なプログラミング言語

WEBサイトだったらHTML・CSSで作れるじゃん!と思った方もいるかもしれませんが、PHPの魅力は、動的なサイトを作ることができる点だと思っています。

では動的なサイトとはどのようなものを言うのでしょうか

1-1. 静的と動的

実際の例として僕がプログラムした静的なサイトと動的なサイトを比較してみましょう。

静的なサイト

以下が、コードとその実行結果になります。
※CSSは省略

index.html
<p>静的</p>
<form action="" method="post">
    <textarea name="comment" id="" cols="30" rows="10" placeholder="メッセージを入力"></textarea>
    <input class="submit" type="submit" name="submit" value="送信する">
</form>
<a href="">PHP学習中です!</a>
<a href="">学習記録を残します</a>

スクリーンショット 2020-06-15 18.23.11.png

静的なサイトの例としてHTMLを用いました。上記のコードを実行してみるとわかると思いますが、テキストエリアに文字を入力して”送信する”ボタンを押しても何も起こりませんよね。

HTMLでは文字やボタンをブラウザに表示するだけで何も応答はしてくれません。

動的なサイト

PHPのコードは他にもありますが、表面的な部分のみ表示します。

index.php
<p>動的</p>
<form action="" method="post">
    <textarea name="comment" id="" cols="30" rows="10" placeholder="メッセージを入力"></textarea>
    <input class="submit" type="submit" name="submit" value="送信する">
</form>
<?php foreach($mymsg as $key => $val){ ?>
    <a href=""><?php echo $val['comment']; ?></a>
<?php } ?>

スクリーンショット 2020-06-15 18.59.20.png
1枚目の画像ではテキストエリアに文字を入力しました。
スクリーンショット 2020-06-15 18.59.26.png
2枚目の画像は1枚目で”送信する”ボタンを押した結果になります。

テキストエリアに入力した文字がボタンの下に表示されていますね。
HTMLのみではこのような動作は実現できません。HTMLは決められたことしかできない頑固者といったところでしょうか笑。一方、PHPはブラウザの操作によって画面に表示する文字や画像などを変更できる柔軟なプログラミング言語なんです。

PHPを使えばブログアプリやフリマアプリなどといったWEBサービスが作れるようになります。

1-2. PHPプログラムが動く仕組み

先ほどPHPは動的な言語と説明しましたが、具体的にどのような仕組みで動作するのか説明していきます。
以下が具体的な順序になります

スクリーンショット 2020-06-15 20.45.24.png

  1. WEBブラウザで操作を行う。(クリックするなど)
  2. WEBブラウザが必要なプログラムをサーバに要求(リクエスト)する。
  3. サーバは要求に対して必要なPHPプログラムを探してとってくる。
  4. サーバがとってきたPHPプラグラムをWEBブラウザに返す(レスポンス)。

PHPはこのようにして動作しているんです。そのため、PHPで書いたプログラムをブラウザに表示するにはWEBサーバが必要になります。PHPプログラムを画面に表示するやり方は以下のサイトを参考にしてみてください。
MAMPを使用したPHP実行環境の構築

2. PHPの基本的構文

変数や演算子のルールを説明する前にPHPの構文について知っておいて欲しいことを紹介します。

2-1. PHPブロック

index.php
<?php                  
$a = '10';
?>
<p><?php echo $a;?></p>

PHPは開始タグ<?phpと終了タグ?>の間にプログラムを書いていきます。この開始タグと終了タグで囲まれた部分をPHPブロックと呼びます。上のコードは二つのブロックがありますね。

みなさん気づいたかもしれませんが、PHPタグがpタグで囲まれていますよね。1-1.静的と動的で紹介したコードもaタグで囲まれています。このようにPHPはHTMLに埋め込んで書くことができます。

例えば、WEBサービスでここは表示を変えたくない(静的)場合はHTMLで書いて、操作によって表示を変えたい(動的)場合はその部分だけPHPで書く、といったことができます。

2-2. 識別子

学習を進めていくと、変数・関数・クラスなどに名前をつけることがあります。名前に使うことができる文字のルールが決まっていて、これを識別子と言います。識別子は、英数字、アンダースコアなどの文字や記号を組み合わせた1文字以上の文字列のことです。以下のルールがあります。

  • 任意の文字、数字、アンダースコアで構成される
  • 先頭は数字NG

先頭は数字NGだけ覚えてもらえばOKです。

3. 変数

変数とは、値を入れておくような箱のことを言います。プログラミングではこの変数を頻繁に使用するので使い方や書き方を覚えておきましょう。

3-1. PHPの変数

  • $が先頭に来る
  • $のあとは識別子
  • 大文字と小文字は区別する
index.php
$a = 1;
$A = 2;
$_a = 3;
$a_b = 4;
$2a = 5; //エラーが出る

3-2. スコープ

スコープとは、変数や関数を参照できる範囲のことです。スコープにはグローバルスコープとローカルスコープがあります。イメージとしては以下の図のような感じです。
スクリーンショット 2020-06-25 9.28.02.png

3-2-1. グローバルスコープ

基本的にPHPブロックの中はグローバルスコープとされています。
別々のブロックであっても変数を参照することができます。

index.php
<?php $var = 5; ?>
<p><?php echo $var; ?></p>

上記のコードは一つ目のブロックで変数$varに5という数値を代入しています。
二つ目のブロックでは変数$varを出力しています。別々のブロックですが5がpタグに出力されます。

グローバルスコープは同じファイルだけでなく別ファイルのグローバルスコープに定義された変数や関数を使うこともできます。

別ファイルを参照する

別ファイルのグローバルスコープに定義された変数を使ってみましょう。
require('ファイル名')とすると指定したファイルの内容を呼び出すことができます。

index1.php
<?php $var = 5; ?>
index2.php
<?php
require('index1.php');
echo $var; //5が出力される
?>

index2.phpは次のコードと同じです

index2.php
<?php
$var = 5;
echo $var; //5が出力される
?>

3-2-2. ローカルスコープ

ローカルスコープは関数やクラスのメソッド内のスコープになります。ローカルスコープ内ではグローバルスコープの変数や関数を参照することはできません。(外側からアクセスできない)

index.php
1  <?php
2  $var = 5;
3  function sample(){
4    $var = 10;
5    $local = 30;
6  }
7  echo $var; //5が出力
8  echo $local; //エラーが出る
9  ?>

3~6行に関数があり、その中がローカルスコープになっています。
関数内(ローカルスコープ)で定義した変数はグローバルスコープでは未定義となり、エラーが出てしまいます。

3-3. スーパーグローバル変数

スーパーグローバル変数とはPHP側で既に定義されており、様々なスコープから参照できる万能変数です。こちらの記事を参照してください。
PHP(スーパーグローバル変数、セッション)

4. 定数

変数は値を後から変更することができますが、定数は変更が効きません。定義方法はdefine()関数やconstを使います。

index.php
<?php
define('name','太郎');
echo name; //太郎と出力

const hoby = '鬼退治';
echo hoby; //鬼退治と出力
?>

define(引数1,引数2)  
引数1に定数の名前、引数2に値を入れて使います。

const 定数の名前 = 値
constを使った定数もdefine()関数と意味は同じになります。

4-1. 定義済み定数

変数同様、定数もPHP側で定義されたものがあります。
var_dump(get_defined_constants())とコードを書いて実行してみましょう。

大量の定数が画面に現れましたね
すべて定数です(多すぎ...)

5. エラー

プログラミングにはエラーがつきものです。PHPのエラーは比較的わかりやすくエラー内容を教えてくれるので、解決しやすいです。PHPのエラーには以下の種類があります。

  • 文法が間違っている時のエラー(パースエラー)
  • 実行が中断されるエラー(Fatal Errorなど)
  • 実行されるけど注意・警告を受けるエラー(Warningなど)

5-1. エラーの表示

プログラムを学習していると100%の確率でエラーに出くわします。そして、そのエラーが何が原因で起きているのかわからなければ対処できません。プログラムを学習する際は必ずエラーの表示設定を行っておきましょう。

PHPでのエラー表示を紹介します。

画面に表示する方法

※エラー表示の文は最初の2行です。

index.php
<?php
error_reporting(E_ALL);
ini_set('display_errors','On');
echo $var;
?>

error_reporting(E_ALL)は、すべてのエラーを報告しますという意味です。
ini_set('display_errors','On')は画面表示しますという意味です。

試しに、echo $var;として定義していない変数を出力してみました。
スクリーンショット 2020-06-26 7.54.56.png
こんな感じで画面にエラー表示をされることができました。

別ファイルにエラー表示(推奨)

続いて別のPHPファイルにエラーを出力していく方法です。
画面表示よりも実行の邪魔にならずに、エラーログを溜めておけるのでこちらの方法を推奨します。
※エラー表示の文は最初の3行です。

index.php
<?php
error_reporting(E_ALL);
ini_set('log_errors','On');
ini_set('error_log','php.log');
echo $var;
?>

ini_set('log_errors','On');でエラーログを出力しますという意味です。
ini_set('error_log','php.log');は第一引数でログのファイル名を指定して、第二引数は実際のファイル名になります。

実行結果をみてみましょう。
スクリーンショット 2020-06-26 8.13.05.png
php.logというファイルにエラーが出力されているのがわかります。

エラーの設定は他にもたくさんあるので色々試してみてください。

6. おわりに

PHPは型の宣言もなく自由度が高い言語なのでプログラミング初心者でも理解しやすいと思います。WEBサービスなんかも意外と簡単に自分で作れちゃうので、動画教材や参考書などで学習してみてはいかがでしょうか?

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

PHPStormからDockerのPHPコンテナでPHPUnitを実行する方法

はじめに

ローカル環境でDockerを利用しているときに、
PHPStormの画面上からPHPUnitを実行できるようにするための
設定方法を紹介します。

大きく2つの設定が必要になります。

  • PHP interpreterを設定
  • PHPUnitのinterpreterを設定

PHP interpreterを設定

・「Settings」->「Languages & Frameworks」->「PHP」->「CLI Intepreter」->「...」をクリック
1.PNG

・左上の「+」->「From Docker, Vagrant, VM, WSL, Remote...]をクリック
2.PNG

・「Docker Compose」のラジオボタンを選択する
→「Configuration file」にdocker-compose.ymlのパスを設定
→「Service」でphp-fpmのコンテナを選択
→「OK」クリック
※「Docker」を選択すると、PHPUnitでDBコンテナを利用する際に接続できないので「Docker Compose」で設定する
2.PNG

PHPUnitのinterpreterを設定

・「Settings」->「Languages & Frameworks」->「PHP」->「Test Frameworks」->左上の「+」->「PHPUnit by Remote Interpreter」をクリック
4.PNG

・先ほど登録したDockerコンテナのInterpreterを選択
3.PNG

・「Path to script」にDockerコンテナのautoload.phpのパスを設定
4.PNG

・「Default configuration file」にphpunit.xmlのパスを設定
5.PNG

・PHPStomのテストクラスファイルを開いて、左にある緑の三角マークをクリックでテスト実行
9.PNG

・テスト結果確認
PHPStormの左下の「Run」タブが表示され、そこにテスト結果が表示されます。
キャプチャ.PNG

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