tanaka's Programming Memo

プログラミングについてのメモ。

コードをPHPのコーディング規約に則って整形する

AtomPHPのコーディング規約に整形するツール php-cs-fixer を使えるようにしました。

手順

  • ターミナルを起動
  • 以下で、php-cs-fixerをインストール
brew install php-cs-fixer
  • php-cs-fixer -v でバージョンが表示されればインストール完了

次に、Atomにfixerのパッケージをインストールします。php-cs-fixer 専用のものもありますが、他のコードも書く場合は Atom-Beautify の方が便利かと思い、自分はAtom-Beautifyを使っています。

  • Atomを起動
  • [Atom]>[Preferences...]メニューを選択
  • [Install]を選択
  • [Search packages]欄に[beautify]を入力して[Enter]
  • [atom-beautify]を[Install]

以上でインストール完了です。

PHPのファイルをAtomで開いて、 [Packages]>[Atom Beautify]>[Beautify]で、コーディング規約に沿ってインデントなどを調整してくれます。

macのMySQL5.7でエラー

MySQLでmysqldumpなどを使うときに以下のようなエラーが発生。

mysqldump: Couldn't execute 'SHOW VARIABLES LIKE 'gtid\_mode'': Table 'performance_schema.session_variables' doesn't exist (1146)

原因は、データベースの構造が更新されていないことでした。

  • mysqlを起動
  • 以下でデータベースをアップグレード
sudo mysql_upgrade -u root -p

以上で治りました。

以下、参考にしたURLです。

PHPUnit DBUnit のテストでハマったことなどメモ

データベースのテストを行うには、PHPUnitのエクステンションであるDBUnitを利用することができます。

PHPUnit マニュアル – 第8章 データベースのテスト

インストール

ComposerでDBUnitをインストールします。composer.jsonの"require-dev"に、以下を追加します。

"phpunit/dbunit": ">=1.2"

追加したら、 composer update してインストール環境を更新。

継承するクラス

  • PHPUnit_Framework_TestCase ではなく、 PHPUnit_Extensions_Database_TestCase を継承
  • $this->assertEquals()などはそのまま利用できる

必要なメソッド

  • public function getConnection()
    • テスト対象のデータベースへのPDOの接続を定義
    • テストごとに毎回実行される
    • staticでPDOのインスタンスを、pdoの接続をインスタンス変数に記録して、再生成を抑制することができる
  • public function getDataSet()
    • 最初に1度だけ実行
    • データセットをreturnすると、テストのデータベースをその内容に書き換える

DBUnitのgetDataSetが機能しない

テスト開始時に、自動的にTRUNCATEやデータセットが設定されない場合は、setUp()メソッドを実装している可能性があります。

setUp()メソッドはテスト開始時の初期化を行うメソッドですが、これがあるとgetDataSet()が正しく機能しないようです。

設定の変更

開発や本番など、データベースの接続条件を変更したい場合は、bootstrap.xmlを利用すると良いです。

詳しくは、PHPUnit マニュアル – 第8章 データベースのテストの「ヒント: 自前でのデータベーステストケースの抽象化」を参照。

データセット

  • YAMLが便利
  • インデントは空白1つずつで良い

Illuminate DatabaseのEloquent ORMを使う

Illuminate Databaseは、SQLで操作することができますが、Eloquent ORM(Objecte-Relational Model)というオブジェクト操作になぞらえた操作もできます。

Illuminate Databaseをインストールして、データの挿入、取り出しを行う例を示します。データベースエンジンはMySQLを利用しますが、Illuminate Databaseは、PostgresやSQL Serverなどにも対応しています。

インストール

  • 利用するプロジェクトフォルダー内で、composerを使ってインストールする
composer require illuminate/database

データベースの準備

事前に、利用するデータベースを作成しておきます。

mysql -u データベースを作成できるユーザー名 -p
  • パスワードを入力してMySQLを起動
  • データベースを作成。以下は test_eloquentorm というデータベースを作成する例
CREATE DATABASE IF NOT EXISTS test_eloquentorm CHARACTER SET=utf8;
  • 操作するユーザーを特定のユーザーに割り当てる場合、権限を与える。eloquent_userが操作する場合は以下の通り
GRANT SELECT,DELETE,INSERT,UPDATE,CREATE ON test_eloquentorm.* TO 'eloquent_user'@'localhost';

データベースの操作

Eloquent ORMを使って、以下を行うコードを示します。

  • データベースに接続
  • テーブル'users'がなければ作成
  • ユーザーを追加
  • 全てのデータを取り出して、名前と作成日時を出力
  • ex.php を作成して、以下のコードを書く
<?php
// 必要なクラスを自動的に読み込む
require __DIR__ . '/vendor/autoload.php';

// 必要なクラスを宣言
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Database\Eloquent\Model as Eloquent;

// 操作したいテーブルに対して、Eloquentクラスを継承したクラスを定義
// デフォルトでは、クラス名を小文字にして複数形にしたテーブル名を自動的に参照。以下の例ではusers
class User extends Eloquent{};

// Illuminate Databaseを設定
$capsule = new Capsule;
// データベースに接続
$capsule->addConnection([
    'driver' => 'mysql',
    'host' => 'localhost',
    'database' => 'test_eloquentorm',
    'username' => '',                           // ユーザー名
    'password' => '',                            // パスワード
    'charset' => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix' => ''
]);

// Illuminate DatabaseのインスタンスをCapsuleでアクセス可能にする
$capsule->setAsGlobal();
// Eloquent ORMを起動
$capsule->bootEloquent();

// スキーマのインスタンスを取得
$schema = $capsule->schema();
// テーブル users がないかを確認
if (!$schema->hasTable('users')) {
    // なければテーブルの作成
    // カラムの指定方法は http://readouble.com/laravel/5/0/dev/ja/schema.html
    $schema->create('users', function($table) {
        $table->increments('id');
        $table->string('name', 100);
        $table->timestamps();
    });
}

// 名前 という名前で新規ユーザーを追加
$user = new User;
$user->name = "名前";
$user->save();

// データを取り出す
$lists = User::all();
foreach($lists as $user) {
    echo $user->name.":".$user->created_at."\n";
}
?>


以上です。各コードの説明は参照URLを参照してください。

事前に作成したテーブルを利用するには

例えば、事前に'custom_table'という名前でテーブルを作成していた場合は、以下のように、モデルクラスの$tableプロパティにテーブル名を設定します。

class User extends Eloquent {
    protected $table = 'custom_table';
}

参考URL

Twigの勉強メモ-SlimPHPで動かす for mac

SlimPHPのテンプレートエンジンとして、Twigを利用する手順をまとめておきます。

掲載したコードは Templates - Slim Framework のものを、SlimPHPのSkeletonプロジェクトで動くように書き換えたものです。

SlimPHPプロジェクトの作成

  • composerはグローバルで動くものとする
  • ターミナルを起動
  • プロジェクトフォルダーを作りたいフォルダーに移動
  • 以下でプロジェクトを作成(slim-twig-test フォルダーをプロジェクトフォルダーにする場合の例)
composer create-project slim/slim-skeleton slim-twig-test
cd slim-twig-test
composer require slim/twig-view
  • 作成したフォルダー内に出来上がる composer.jsonテキストエディタで開く
  • "require" 欄から "slim/php-view" の行は不要なので削除して、上書き保存
  • ターミナルで以下を実行して、インストール環境を更新する
composer update

設定ファイルを準備する

テンプレート用のパスと、Twigのオプションを設定します。PHPの組み込みサーバーで動作しているかどうかで、デバッグと本番設定を切り替えられるようにします。本番用は cache を利用し、デバッグ用は debug フラグをtrueにして、更新を監視するようにします。

  • /src/settings.php を開く
  • 以下のように修正
<?php
return [
    'settings' => [
        'displayErrorDetails' => true, // set to false in production

        // Twig settings
        'view' => [
            'template_path' => __DIR__ . '/../templates/',
            'options' => [
                'cache' => __DIR__ . '/../cache/'
            ]
        ],

        // Monolog settings
        'logger' => [
            'name' => 'slim-app',
            'path' => __DIR__ . '/../logs/app.log',
        ],
    ],
];
  • PHPの組み込みサーバーで起動した時のテスト用の設定として、/srcフォルダー内に settings-debug.php を作成して、以下のようにする
<?php
return [
    'settings' => [
        'displayErrorDetails' => true, // set to false in production

        // Twig settings
        'view' => [
            'template_path' => __DIR__ . '/../templates/',
            'options' => [
                'debug' => true
            ]
        ],

        // Monolog settings
        'logger' => [
            'name' => 'slim-app',
            'path' => __DIR__ . '/../logs/app.log',
        ],
    ],
];
  • 設定を読み込むために /public/index.php を開いて以下の通りにする
<?php
if (PHP_SAPI == 'cli-server') {
    // To help the built-in PHP dev server, check if the request was actually for
    // something which should probably be served as a static file
    $file = __DIR__ . $_SERVER['REQUEST_URI'];
    if (is_file($file)) {
        return false;
    }

    $settings = require __DIR__ . '/../src/settings-debug.php';
}
else {
    $settings = require __DIR__ . '/../src/settings.php';
}

require __DIR__ . '/../vendor/autoload.php';

session_start();

// Instantiate the app
$app = new \Slim\App($settings);

// Set up dependencies
require __DIR__ . '/../src/dependencies.php';

// Register middleware
require __DIR__ . '/../src/middleware.php';

// Register routes
require __DIR__ . '/../src/routes.php';

// Run app
$app->run();

コンテナにTwigを設定

SlimPHPのコンテナにTwigを追加します。

  • /src/dependencies.php を開く
  • 以下のようにする
<?php
// DIC configuration

$container = $app->getContainer();

// view renderer
$container['view'] = function ($c) {
    $settings = $c->get('settings')['view'];
    $view = new \Slim\Views\Twig(
        $settings['template_path'],
        $settings['options']
    );
    $view->addExtension(new \Slim\Views\TwigExtension(
        $c['router'],
        $c['request']->getUri()
    ));

    return $view;
};

// monolog
$container['logger'] = function ($c) {
    $settings = $c->get('settings')['logger'];
    $logger = new Monolog\Logger($settings['name']);
    $logger->pushProcessor(new Monolog\Processor\UidProcessor());
    $logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], Monolog\Logger::DEBUG));
    return $logger;
};

ルートを設定

リクエストに応じた処理を呼び出すルートを設定します。今回は、 http://0.0.0.0:8080/hello を呼び出すと、与えられた配列のユーザー一覧を表示して、 http://0.0.0.0:8080/hello/ユーザー名 とすると、指定したユーザーのプロフィールを表示するようにします。

  • /src/routes.php を開いて、以下のようにする
<?php
// Routes

// ユーザー一覧
$app->get('/hello', function($request, $response, $args) {
    // ユーザー一覧
    $users = [
        'Tama',
        'Kotta',
        'Tanaka'
    ];

    return $this->view->render($response, 'list.html', [
        'list' => $users
    ]);
})->setName('list');

// 指定のユーザーのプロフィールを表示
$app->get('/hello/{name}', function ($request, $response, $args) {
    return $this->view->render($response, 'profile.html', [
        'name' => $args['name']
    ]);
})->setName('profile');

/hello で呼び出された場合

  • list.html をテンプレートに利用
  • $users 配列に設定したデータを 'list' という名前でテンプレートエンジンに渡す
  • /hello のパスを list という名前で記録しておく

/hello/{name} で呼び出された場合

  • profile.html をテンプレートに利用
  • パスの{name}の場所のデータを、 'name' という名前でテンプレートエンジンに渡す
  • /hello/{name} のパスを profile という名前で記録しておく

テンプレートの作成

Twigのサンプルテンプレートを準備して、ルートに対応させます。

レイアウトテンプレートの作成

ユーザー一覧ページとプロフィールページのひな形となるレイアウト layout.html を作成します。

  • /templates フォルダー内に layout.html を作成
  • 以下のコードを書く
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>SlimPHP Twig Test-{% block title %}{% endblock %}</title>
</head>
<body>
    <h1>{% block pagetitle %}{% endblock %}</h1>

    <div id='content'>
        {% block content %}
        {% endblock %}
    </div>

    <hr />

    <div id='footer'>
        &copy; Copyright 2016 by YuTanaka
    </div>
</body>
</html>
  • {% block <ブロック名> %}から{% endblock %}までの箇所を、他のテンプレートで指定した内容に差し替えることが可能
  • 以下のようなブロックを定義している。list.htmlとprofile.htmlの {% block ・・・%} で囲んだ範囲が置きかわる
    • title ブロック
    • pagetitle ブロック
    • content ブロック

ユーザー一覧ページ用のテンプレートの作成

  • /templates フォルダー内に list.html を新規に作成
  • 以下のTwigテンプレートコードを入力
{% extends "layout.html" %}

{% block title %}User List{% endblock %}

{% block pagetitle %}User List{% endblock %}
{% block content %}
    <ul>
        {% for item in list %}
        <li><a href="{{ path_for('profile', {'name': item}) }}">{{item}}</a></li>
        {% endfor %}
    </ul>
{% endblock %}
  • layout.html を継承することを宣言
  • タイトルとページタイトルに User List を設定
  • content ブロックに、ulタグを作成
  • {% for item in list %} とすることで、 routes.php で list に渡した配列をループして、要素を item に入れて {% endfor %} との間をループ
  • path_for()メソッドは、 slim/twig-view による拡張メソッド。解釈は以下の通り
    • 第1引数に、ルート名を渡す。ルート名は routes.php のルート定義で->setName()したもの
    • 第2引数に、ルートのプレースホルダーに対応するデータをオブジェクトで渡す
    • この例では、クリックしたらプロファイルページを呼び出したいので、 profile のルートを指定して、'name'のプレースホルダーをループで取り出すitemに置き換える
  • {{item}}とすると、itemの中身を表示

詳細ページ用のテンプレートの作成

  • /templates フォルダー内に profie.html を新規に作成
  • 以下のTwigテンプレートコードを入力
{% extends "layout.html" %}

{% block title %}Profile{% endblock %}

{% block pagetitle %}{{name}}'s Profile{% endblock %}
{% block content %}
<p>これは「{{name}}」のプロフィールです。</p>
<p><a href="{{ path_for('list') }}">一覧へ</a></p>
{% endblock %}
  • layout.html を継承することを宣言
  • タイトルにProfileを設定
  • ページタイトルに、渡された name プロパティと、続けて 's Profile を設定
  • content ブロックでは、「name プロパティのプロフィールです。」と表示する段落と、一覧に戻るリンクを設定
  • 一覧へ戻るためのリンクは、listのルートをそのまま使う

テスト

準備ができたら、PHPの組み込みサーバーを起動して、動作を確認します。

  • サーバーを起動
php -S 0.0.0.0:8080 -t public public/index.php
  • ブラウザーhttp://0.0.0.0:8080/hello にアクセス
  • 一覧が表示されるのを確認
  • 任意の名前をクリック
  • クリックした名前のプロフィールが表示されることを確認
  • 「一覧へ」をクリックして、一覧ページに戻ることを確認

Twigの勉強メモ-Introduction-

PHP用のテンプレートエンジンであるTwigについての自分向けの勉強メモです。

Introduction - Documentation - Twig - The flexible, fast, and secure PHP template engine

  • Twigは、PHP用の自由度が高く、高速で、安全なテンプレートエンジン
  • 既存のテンプレートエンジンから移行がしやすい
  • Twigの特徴
    • 高速:テンプレートを効率的なPHPコードにコンパイルするので、オーバーヘッドは通常のPHPより小さい
    • 安全性:Twigには、信頼性の低いテンプレートコードを評価するsandboxモードがある。これにより、ユーザーがテンプレートのデザインを変更できるようなアプリケーションのテンプレート言語として利用できる。
    • 柔軟性:Twigは柔軟なlexer(辞書)とparser(語彙解析器)によって構築されている。これにより、Twigで定義する独自のタグやフィルターによって、Twig自身のDLSを作成することができる。
  • Twigは多くのオープンソースプロジェクトで利用されたり、Slimのようなフレームワークに対応している。

要件

  • PHP 5.2.7以降

インストール方法

composerを推奨

基本的なAPIの使い方

  • テンプレートの配列を渡して Twig_Loader_Array クラスを生成
  • ローダーなどの動作設定を渡して Twig_Environment クラスを生成
  • Twig_Environmentのインスタンスから render() を呼び出す。第1引数にテンプレートのインデックス、第2引数に描画のためのパラメータを渡す
  • テンプレートは通常はファイルシステム上に記録しておく。Twigでもファイルシステムローダーを利用することができる
  • Twig_Loader_Filesystem にテンプレートファイルが入っているフォルダーへのパスを指定
  • Twig_Environment には、第1引数に上記のインスタンス、第2引数にオプションの連想配列を渡す
  • render() の第1引数にはテンプレートのファイル名、第2引数に描画のためのパラメータを連想配列で渡す

Sentinel5.2以降でilluminate/support/Str.phpがrandom_bytesでエラー

Sentinelで認証しようとしたところ、以下のようなエラーが発生しました。

Fatal error: Call to undefined function Illuminate\Support\random_bytes() in /Users/user/project/vendor/illuminate/support/Str.php on line 233

原因を調べると、PHP5とPHP7の命令の違いのようです。それを解決するためのComposerパッケージをインストールすることで治りました。以下を、Composer.jsonの"require"に追加して、 composer update で直りました。

        "paragonie/random_compat": "~1.1"

以上です。