m_shige1979のささやかな抵抗と欲望の日々

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

なんとなく作ったサイト

http://www.it-check-matome.info/


Github(注意すること)

https://github.com/mshige1979

AzurよりSendGridを使用してメールを送信する

Azureでのメール送信はSendGridらしい

詳しくは知らないけどSendGridの送信手段を調べてみました。

環境

CentOS7
PHP7

Azure設定

サービスを追加

f:id:m_shige1979:20170720165411p:plain

アプリ名やパスワードを設定

f:id:m_shige1979:20170720165420p:plain

料金プランを設定

f:id:m_shige1979:20170720165429p:plain

アクセス情報を設定

f:id:m_shige1979:20170720165440p:plain

同意確認を行う

f:id:m_shige1979:20170720165449p:plain

作成ボタンを押下

f:id:m_shige1979:20170720165458p:plain

ダッシュボードに作成されたことを確認

f:id:m_shige1979:20170720165507p:plain

Azureの管理画面より「Manage」を押下してSendGridの管理ページへジャンプ

f:id:m_shige1979:20170720165518p:plain

メールを送信して認証チェックを行う

f:id:m_shige1979:20170720165527p:plain

メール送信完了画面

f:id:m_shige1979:20170720165535p:plain

自分のメールクライアントよりメールを受信してボタンを押下

f:id:m_shige1979:20170720165545p:plain

認証後はそのまま管理画面を表示

f:id:m_shige1979:20170720165648p:plain

APIキー作成画面を表示

f:id:m_shige1979:20170720165657p:plain

作成ボタンを押下して作成画面を表示

f:id:m_shige1979:20170720165708p:plain

APIキー名と権限を設定

f:id:m_shige1979:20170720165719p:plain

キーの値をクリックしてコピーする

f:id:m_shige1979:20170720165729p:plain

Azureの設定画面で資格情報を取得

f:id:m_shige1979:20170720165739p:plain
※パスワードは設定したもの、ユーザ名はこれを使用することでメール送信のパラメータとなります。

送信用設定

yum
udo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
sudo yum install --enablerepo=remi,remi-php70 php php-devel php-mbstring php-pdo php-gd php-xml php-intl
composer
mkdir -p sample1
cd sample1
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === '669656bab3166a7aff8a7506b8cb2d1c292f042046c5a994c43155c0be6190fa0355160742ab2e1c88d40d5be660b410') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
php composer.phar require swiftmailer/swiftmailer
composer.json
{
    "require": {
        "swiftmailer/swiftmailer": "5.3.0"
    }
}

実装

sendmail1.php
<?php
include_once "vendor/autoload.php";

// 本文
$text = "Hi!\nHow are you?\n";

// 送信元
$from = array(
    '送信元メールアドレス' => 'sample_from'
);

// 送信先
$to = array(
    '送信先メールアドレス' => 'sample_to',
);

// タイトル
$subject = 'Example PHP Email';

// ユーザ情報
$username = '資格情報のユーザ名';
$password = '資格情報のパスワード';

// 送信設定
$transport = Swift_SmtpTransport::newInstance('smtp.sendgrid.net', 587);
$transport->setUsername($username);
$transport->setPassword($password);
$swift = Swift_Mailer::newInstance($transport);

// メッセージ生成
$message = new Swift_Message($subject);

// 送信情報設定
$message->setFrom($from);
$message->setBody($text, 'text/plain');
$message->setTo($to);

// 送信処理
if ($recipients = $swift->send($message, $failures)) {
   echo "送信成功";
} else {
    echo "送信失敗";
}
sendmail2.php
<?php

// 接続情報
$url = 'https://api.sendgrid.com/';
$user = '資格情報より取得したユーザ名';
$pass = '資格情報より取得したパスワード';

// 送信情報
$params = array(
    'api_user' => $user,
    'api_key' => $pass,
    'to' => '送信先のメールアドレス',
    'subject' => 'testing from curl',
    'text' => 'testing body',
    'from' => '送信元のメールアドレス',
);

// リクエスト先設定
$request = $url.'api/mail.send.json';

// curl初期化
$session = curl_init($request);

// curl設定(POST送信)
curl_setopt ($session, CURLOPT_POST, true);

// curl設定(パラメータ設定)
curl_setopt ($session, CURLOPT_POSTFIELDS, $params);

// curl設定(ヘッダー不要、レスポンス必要?)
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);

// 送信して終了
$response = curl_exec($session);
curl_close($session);

// 処理結果
print_r ($response);

※WEB APIを使用してメール送信するサンプルでもユーザとパスワードは資格情報のものを使用します。

所感

参考にしたドキュメントの通りにやることでなんとかできた。
最近は日本語のドキュメントが豊富なので環境を用意するのも簡単で助かります。
一部英語でわからないこともあるけど
まあなんとかなるかな…

Azure Strage File ApiをPHPでBLOBを取得

仕事がやっと

落ち着いた感じがする。
特別忙しいというわけではないがタイミングなどの問題もあってブログを書くことを忘れてしまっているのはマズイ
忘れていることもあるので勉強を再開してみる。

Azure Strage File

簡単にいうとAWSのS3と思う、厳密には違うと思うけど一部のデータを保存したり取得したりするのでそのレベルの認識で覚えておく
今回は設定して登録したデータをphpで取得することを行う。

事前準備

Windows Azureへアカウントを作成しておく
Linuxの環境(vagrantなどを作成しておく)

Azureの設定

ストレージアカウントを選択する

f:id:m_shige1979:20170717081218p:plain

追加ボタンを押下

f:id:m_shige1979:20170717081231p:plain

アカウント名と他の設定を行い、作成ボタンを押下

f:id:m_shige1979:20170717081242p:plain
f:id:m_shige1979:20170717081254p:plain

作成されたことを確認し、選択する

f:id:m_shige1979:20170717081305p:plain

コンテナを追加する

f:id:m_shige1979:20170717081318p:plain

コンテナ名を設定してOKを押下

f:id:m_shige1979:20170717081330p:plain

アクセスキーをコピーする

f:id:m_shige1979:20170717081341p:plain

準備

php
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
sudo yum install --enablerepo=remi,remi-php70 php php-devel php-mbstring php-pdo php-gd php-xml
compoer
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === '669656bab3166a7aff8a7506b8cb2d1c292f042046c5a994c43155c0be6190fa0355160742ab2e1c88d40d5be660b410') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
compoer.json
{
  "require": {
    "microsoft/windowsazure": "^0.4"
  }
}

設定情報

setting.php
<?php

$connectionString = "DefaultEndpointsProtocol=https;AccountName=mshige1979sample1;AccountKey=事前にコピーしたアクセスキー";

ファイルを追加

sample_add.php
<?php
require_once 'vendor/autoload.php';
require_once 'setting.php';

use WindowsAzure\Common\ServicesBuilder;
use MicrosoftAzure\Storage\Common\ServiceException;

// 接続文字列を使用して、Blobサービスのインスタンスを作成する
$blobRestProxy = ServicesBuilder::getInstance()->createBlobService($connectionString);

// 取得処理
try {
    // ファイル名
    $date = "testdata_" . date("YmdHis") . ".txt";
    echo $date . "\n";

    // データの中身
    $data = md5(uniqid());;
    echo $data . "\n";

    // 追加処理
    $blobRestProxy->createBlockBlob("test01", $date, $data);

} catch(ServiceException $e) {
    // 例外処理
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."\n";
}

$ php sample_add.php
testdata_20170716233116.txt
08a8e1964d2c0dadc558bd4f54b3de5a
$ php sample_add.php
testdata_20170716233127.txt
6ff9937a10cbc58bee43367299f49a80
$ 

一覧取得

sample1.php
<?php
require_once 'vendor/autoload.php';
require_once 'setting.php';

use WindowsAzure\Common\ServicesBuilder;
use MicrosoftAzure\Storage\Common\ServiceException;

// 接続文字列を使用して、Blobサービスのインスタンスを作成する
$blobRestProxy = ServicesBuilder::getInstance()->createBlobService($connectionString);

// 取得処理
try {
    // コンテナのオブジェクトを取得
    $blob_list = $blobRestProxy->listBlobs("test01");
    $blobs = $blob_list->getBlobs();

    // 一覧を出力
    foreach ($blobs as $blob) {
        echo $blob->getName().": ".$blob->getUrl()."\n";
    }
} catch(ServiceException $e) {
    // 例外処理
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."\n";
}

$ php sample_list.php
testdata_20170716233116.txt: https://mshige1979sample1.blob.core.windows.net/test01/testdata_20170716233116.txt
testdata_20170716233127.txt: https://mshige1979sample1.blob.core.windows.net/test01/testdata_20170716233127.txt
$

読込処理

sample_get.php
<?php
require_once 'vendor/autoload.php';
require_once 'setting.php';

use WindowsAzure\Common\ServicesBuilder;
use MicrosoftAzure\Storage\Common\ServiceException;

// 接続文字列を使用して、Blobサービスのインスタンスを作成する
$blobRestProxy = ServicesBuilder::getInstance()->createBlobService($connectionString);

// 取得処理
try {
    // ファイル名
    $date = "testdata_20170716233116.txt";

    // 取得処理
    $blob = $blobRestProxy->getBlob("test01", $date);
    echo stream_get_contents($blob->getContentStream()) . "\n";

} catch(ServiceException $e) {
    // 例外処理
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."\n";
}

$ php sample_get.php
08a8e1964d2c0dadc558bd4f54b3de5a
$

まとめ

AWSとAzureはそれぞれ勝手が違う。
このレベルのことは基本として理解しておきたい、やっていないからわかりませんということは仕方がないにしても
ITに携わるものとして新しい技術に触れ続けていくことは重要と思うので…

IBM BluemixでCloud Foundry アプリケーションのアプリを作成する

イメージはHeroku

と同じである程度の環境を持っているものと考えています。

手順

ダッシュボードを開く

f:id:m_shige1979:20161016044946p:plain

計算よりアプリを作成して、「php」ものを指定する

f:id:m_shige1979:20161016044955p:plain

アプリ名を指定してアプリを作成する

f:id:m_shige1979:20161016045007p:plain

開始タブでデプロイ情報や初期状態のファイルをダウンロード

f:id:m_shige1979:20161016045018p:plain

URLを確認

f:id:m_shige1979:20161016045030p:plain

デプロイ

CFツールによるデプロイこれとは別にもう一つデプロイする方法がある

ログイン
$ cf login
API エンドポイント: https://api.au-syd.bluemix.net

Email> hogehoge

Password>
認証中です...
OK

組織 hogehogeをターゲットにしました

スペース dev をターゲットにしました



API エンドポイント:   https://api.au-syd.bluemix.net (API バージョン: 2.54.0)
ユーザー:             hogehoge
組織:                 hogehoge
スペース:             dev
$
プッシュ
$ cf push sample-php01
$ 

所感

アプリサーバになんか特殊なことが必要でないのであれば手堅く終わらせる必要がある。
この方法で有償になる感じですけど短期間なら問題なさそうに見える。
SSLの制限があるか確認する必要がる。

php5.6にvldをインストールして試す

これ

PHPはどのように動くのか ~PHPコアから読み解く仕組みと定石

PHPはどのように動くのか ~PHPコアから読み解く仕組みと定石

少しずつ読んでるけどオペコードのくだりがいまいち理解できないので実際に実行して試して見る。

環境

ホスト

vagrant

OS

CentOS6.8

php

php5.6
※php7系でのvldインストールはコンパイルエラーになる

インストール

phpのためのリポジトリ
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
sudo rpm -Uvh https://mirror.webtatic.com/yum/el6/latest.rpm
phpインストール
sudo yum install  \
  php56w php56w-bcmath php56w-cli php56w-common \
  php56w-dba php56w-devel php56w-embedded php56w-enchant \
  php56w-fpm php56w-gd php56w-imap php56w-interbase php56w-intl \
  php56w-ldap php56w-mbstring php56w-mcrypt php56w-mssql php56w-mysqlnd \
  php56w-odbc php56w-opcache php56w-pdo php56w-pear php56w-pecl-apcu \
  php56w-pecl-gearman php56w-pecl-geoip php56w-pecl-imagick php56w-pecl-memcache \
  php56w-pecl-xdebug php56w-pgsql php56w-phpdbg php56w-process \
  php56w-pspell php56w-recode php56w-snmp php56w-soap php56w-tidy \
  php56w-xml php56w-xmlrpc
  
vldインストール
sudo pecl install channel://pecl.php.net/vld-0.13.0

Build process completed successfully
Installing '/usr/lib64/php/modules/vld.so'
install ok: channel://pecl.php.net/vld-0.13.0
configuration option "php_ini" is not set to php.ini location
You should add "extension=vld.so" to php.ini
php.iniにモジュールを追加
sudo vi /etc/php.ini
----
; vld
extension=vld.so
----

実行1

sample1.php
<?php
echo "hello, world";
sample1を実行
$ php -d vld.active=1 -d vld.execute=0 sample1.php
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /home/vagrant/sample1.php
function name:  (null)
number of ops:  3
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   EXT_STMT
         1        ECHO                                                     'hello%2C+world'
   4     2      > RETURN                                                   1

branch: #  0; line:     2-    4; sop:     0; eop:     2; out1:  -2
path #1: 0,
$

実行2

sample2.php
<?php
$aaa = 10;
echo $aaa;

$aaa .= "ccc";
print $aaa;

$bbb = [111, 222, 333];
echo $bbb[0];
echo $bbb[1];
echo $bbb[2];
sample2を実行
$ php -d vld.active=1 -d vld.execute=0 sample2.php
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /home/vagrant/sample2.php
function name:  (null)
number of ops:  24
compiled vars:  !0 = $aaa, !1 = $bbb
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   EXT_STMT
         1        ASSIGN                                                   !0, 10
   3     2        EXT_STMT
         3        ECHO                                                     !0
   5     4        EXT_STMT
         5        ASSIGN_CONCAT                                 0          !0, 'ccc'
   6     6        EXT_STMT
         7        PRINT                                            ~2      !0
         8        FREE                                                     ~2
   8     9        EXT_STMT
        10        INIT_ARRAY                                       ~3      111
        11        ADD_ARRAY_ELEMENT                                ~3      222
        12        ADD_ARRAY_ELEMENT                                ~3      333
        13        ASSIGN                                                   !1, ~3
   9    14        EXT_STMT
        15        FETCH_DIM_R                                      $5      !1, 0
        16        ECHO                                                     $5
  10    17        EXT_STMT
        18        FETCH_DIM_R                                      $6      !1, 1
        19        ECHO                                                     $6
  11    20        EXT_STMT
        21        FETCH_DIM_R                                      $7      !1, 2
        22        ECHO                                                     $7
  13    23      > RETURN                                                   1

branch: #  0; line:     2-   13; sop:     0; eop:    23; out1:  -2
path #1: 0,
$

所感

そこまで厳密に理解する気はないけどあったら便利かもくらいは理解しておきたい。
最近はもうパフォーマンスが上がってきているのでそこまで気にすることはないような気もするので

centos6.8にphp7をyumでインストールする

phpenvなどもありますけど

あれコンパイルなどが必要で時間がかかるのでサクッとphpを入れて使いたい場合はyumでインストールしたい

環境

ホスト

vagrant

OS

CentOS6.8

インストール

リポジトリ
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
sudo rpm -Uvh https://mirror.webtatic.com/yum/el6/latest.rpm

リポジトリを設定する

yum
sudo yum install php70w php70w-bcmath php70w-cli php70w-common \
  php70w-dba php70w-devel php70w-embedded php70w-enchant \
  php70w-fpm php70w-gd php70w-imap php70w-interbase php70w-intl \
  php70w-ldap php70w-mbstring php70w-mcrypt \
  php70w-mysqlnd php70w-odbc php70w-opcache php70w-pdo \
  php70w-pdo_dblib php70w-pear php70w-pecl-apcu php70w-pecl-imagick \
  php70w-pecl-redis php70w-pecl-xdebug php70w-pgsql php70w-phpdbg \
  php70w-process php70w-pspell php70w-recode php70w-snmp php70w-soap \
  php70w-tidy php70w-xml php70w-xmlrpc

※とりあえず必要そうなものを調べるの面倒なんでいれられそうなやつ全部

結果

$ php -v
PHP 7.0.10 (cli) (built: Aug 20 2016 08:13:48) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
$

cakephp3で複数データをインサート

データを登録する際

1件ずつ登録するか、一括で登録するか…

環境

CentOS7.x
php7.0.3
cakephp3.2.3

一括で登録

sample1

src/Shell/Sample1Shell.php

<?php
namespace App\Shell;

use Cake\Console\Shell;
use Cake\ORM\TableRegistry;

/**
 * Sample1 shell command.
 */
class Sample1Shell extends Shell
{

    /**
     * main() method.
     *
     * @return bool|int Success or error code.
     */
    public function main() 
    {

        // サンプルデータ準備
        $postList = [
            [
                "title" => "aaa1",
                "body" => "hogehoge1",
                "created" => date("Y-m-d H:i:s", strtotime("now")),
                "modified" => date("Y-m-d H:i:s", strtotime("now")),
            ],
            [
                "title" => "aaa2",
                "body" => "hogehoge2",
                "created" => date("Y-m-d H:i:s", strtotime("now")),
                "modified" => date("Y-m-d H:i:s", strtotime("now")),
            ],
            [
                "title" => "aaa3",
                "body" => "hogehoge3",
                "created" => date("Y-m-d H:i:s", strtotime("now")),
                "modified" => date("Y-m-d H:i:s", strtotime("now")),
            ],
        ];

        // テーブルオブジェクトを取得
        $posts = TableRegistry::get("Posts");
        // クエリーオブジェクトを取得
        $oQuery = $posts->query();

        // 配列単位にデータを設定
        foreach($postList as $post){
            // 1行ずつ設定
            $oQuery
                ->insert(['title', 'body', 'created', 'modified']) // キーを指定
                ->values($post);    // 明細を設定
        }

        // 実行
        $oQuery->execute();
    }
}

※モデルのsaveではないのでcreatedやmodifiedは任意で設定する必要があります。

SQL
INSERT INTO posts (
  title, body, created, modified
) VALUES 
 ('aaa1', 'hogehoge1', '2016-03-08 13:08:31', '2016-03-08 13:08:31'), 
  ('aaa2', 'hogehoge2', '2016-03-08 13:08:31', '2016-03-08 13:08:31'), 
  ('aaa3', 'hogehoge3', '2016-03-08 13:08:31', '2016-03-08 13:08:31')

一括で実行されています

一件ずつ登録

sample2

src/Shell/Sample2Shell.php

<?php
namespace App\Shell;

use Cake\Console\Shell;
use Cake\ORM\TableRegistry;

/**
 * Sample2 shell command.
 */
class Sample2Shell extends Shell
{

    /**
     * main() method.
     *
     * @return bool|int Success or error code.
     */
    public function main() 
    {
        // サンプルデータ準備
        $postList = [
            [
                "title" => "bbb1",
                "body" => "foofoo1",
            ],
            [
                "title" => "bbb2",
                "body" => "foofoo2",
            ],
            [
                "title" => "bbb3",
                "body" => "foofoo3",
            ],
        ];

        // テーブルオブジェクトを取得
        $posts = TableRegistry::get("Posts");

        // データを設定して複数のエンティティを取得
        $postsEntities = $posts->newEntities($postList);

        // データ数分保存する
        foreach($postsEntities as $entity){
            // 保存
            $posts->save($entity, ['atomic' => false]);
        }
    }
}

※createdなどは自動設定してくれる

SQL
INSERT INTO posts (title, body, created, modified) VALUES ('bbb1', 'foofoo1', '2016-03-08 13:23:24', '2016-03-08 13:23:24')
INSERT INTO posts (title, body, created, modified) VALUES ('bbb2', 'foofoo2', '2016-03-08 13:23:24', '2016-03-08 13:23:24')
INSERT INTO posts (title, body, created, modified) VALUES ('bbb3', 'foofoo3', '2016-03-08 13:23:24', '2016-03-08 13:23:24')

一件ずつ実行

所感

一括で登録できた方が楽なのでそちらを使う事になると思われる。変なデータになっていないかは別の方法でチェックして対応する
ビジネスロジックの場所に困りそうな状況になりつつあるけどまあ最初はコントローラーを困らせていこう(^ ^)

cakephp3でエラー画面

エラー画面を多少はカスタマイズしたい…

エラー内容自体はともかく、デフォルトのフォーマットはマズイし…

環境

CentOS7.x
php7.0.3
cakephp3.2.3

実装

エラー用コントローラーを作成

src/Controller/AppErrorController.php

<?php

namespace App\Controller;

use Cake\Controller\ErrorController;
use Cake\Event\Event;

/**
 * 独自のエラーコントローラー
 * Class AppErrorController
 * @package App\Controller
 */
class AppErrorController extends ErrorController
{
    /**
     * 描画前処理
     *
     * @param Event $event
     */
    public function beforeRender(Event $event)
    {
        // エラーの共通レイアウト名を指定
        // src/Template/Layout/error_layout.ctp
        $this->viewBuilder()->layout('error_layout');

        // エラーテンプレートパスを指定
        $this->viewBuilder()->templatePath('Error');
    }
}

※描画前にテンプレートパスやレイアウトを指定する

レンダラーを作成

src/Error/AppExceptionRenderer.php

<?php
namespace App\Error;

use Cake\Error\ExceptionRenderer;
use Exception;
use Cake\Log\Log;
use App\Controller\AppErrorController;

/**
 * 独自例外用レンダラー
 *
 * Class AppExceptionRenderer
 * @package App\Error
 */
class AppExceptionRenderer extends ExceptionRenderer
{

    /**
     * 独自のコントローラーを指定
     * @param Exception $exception
     * @return AppErrorController
     */
    protected function _getController()
    {
        // 独自のコントローラーを指定
        return new AppErrorController();
    }

    /**
     * 独自のテンプレート名を指定
     *
     * @param Exception $exception
     * @param string $method
     * @param int $code
     * @return string
     */
    protected function _template(Exception $exception, $method, $code)
    {
        // src/Template/Error/error_custom.ctp
        return $this->template = "error_custom";
    }
}

※テンプレートを指定

設定ファイルを編集

config/app.php

<?php
return [
    'Error' => [
        'errorLevel' => E_ALL & ~E_DEPRECATED,
        //'exceptionRenderer' => 'Cake\Error\ExceptionRenderer',
        'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
        'skipLog' => [],
        'log' => true,
        'trace' => true,
    ],

※レンダラーを変更

エラー用レイアウトを作成

src/Template/Layout/error_layout.ctp

<!DOCTYPE html>
<html>
<head>
    <?= $this->Html->charset() ?>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>
        <?= $this->fetch('title') ?>
    </title>
    <?= $this->Html->meta('icon') ?>
    <?= $this->fetch('meta') ?>

    <link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/app.css">
    <?= $this->fetch('css') ?>

</head>
<body>
    <header id="header">
        <nav class="navbar navbar-default">
            <div class="container">
                <a class="navbar-brand" id="logo" href="/">
                    CakePHP3 Demo
                </a>
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav navbar-right hidden-sm">
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-12">
                <div style="">
                    <?= $this->Flash->render() ?>
                </div>
            </div>
        </div>
    </div>
    <div class="container-fluid">
        <?= $this->fetch('content') ?>
    </div>
    <footer>
    </footer>
    <script src="/js/jquery-2.2.1.min.js"></script>
    <script src="/bootstrap/js/bootstrap.min.js"></script>
    <?= $this->fetch('script') ?>
</body>
</html>

エラー用テンプレートを作成
<?php
use Cake\Core\Configure;
use Cake\Error\Debugger;
?>
<div class="row">
    <div class="col-md-12">
        <div style="width: 800px;margin: 0 auto;">
            <h2><?= __d('cake', 'An Internal Error Has Occurred') ?></h2>
            <p class="error">
                <strong><?= __d('cake', 'Error') ?>: </strong>
                <?= h($message) ?>
            </p>
            <?php if (!empty($error->queryString)) : ?>
                <p class="notice">
                    <strong>SQL Query: </strong>
                    <?= h($error->queryString) ?>
                </p>
            <?php endif; ?>
            <?php if (!empty($error->params)) : ?>
                    <strong>SQL Query Params: </strong>
                    <?= Debugger::dump($error->params) ?>
            <?php endif; ?>
            <?php if ($error instanceof Error) : ?>
                    <strong>Error in: </strong>
                    <?= sprintf('%s, line %s', str_replace(ROOT, 'ROOT', $error->getFile()), $error->getLine()) ?>
            <?php endif; ?>
            <?php
                echo $this->element('auto_table_warning');

                if (extension_loaded('xdebug')):
                    xdebug_print_function_stack();
                endif;

                $this->end();
            ?>

        </div>
    </div>
</div>

画面

f:id:m_shige1979:20160308004400p:plain
スタックトレースはおいおい対応する

所感

エラー画面って難しいと思います。よくわからないのでログ吐きまくって調べてしまった。
まだおかしい部分があるのですか今回はこれでなんとかする。