もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

cakephp3のDebugkitが使えないと思ったら、たいしたことない理由だった

DebugKitが中々出ないという事象に悩まされていました。

config/bootstrapには

<?php

...

if (Configure::read('debug')) {
    Plugin::load('DebugKit', ['bootstrap' => true, 'routes' => true]);
}

って書いてるし、もちろんdebugはtrue。

それでも出ない。

sqlliteがないだとか、なんだかんだと探していても、結局わからずじまいだったので、コアを漁って調べたところ、しょうもない理由でした。

結果としては、debug_kit/config/bootstrap.phpの以下の行で引っかかって出てませんでした。

<?php
/**
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 */
use Cake\Core\Configure;
use Cake\Core\Plugin;
use Cake\Datasource\ConnectionManager;
use Cake\Event\EventManager;
use Cake\Log\Log;
use Cake\ORM\Query;
use Cake\Routing\DispatcherFactory;
use DebugKit\DebugSql;
use DebugKit\Middleware\DebugKitMiddleware;
use DebugKit\Routing\Filter\DebugBarFilter;
use DebugKit\ToolbarService;

$service = new ToolbarService(EventManager::instance(), (array)Configure::read('DebugKit'));

if (!$service->isEnabled() || php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg') {
   // $servide->isEnabled() が falseとなって出なかった
    return;
}

ToolbarServiceのisEnabled()はなんだろうと思ってみるとこれ。

<?php

...

    /**
     * Check whether or not debug kit is enabled.
     *
     * @return bool
     */
    public function isEnabled()
    {
        $enabled = (bool)Configure::read('debug');

        if ($enabled && !$this->isSuspiciouslyProduction()) {
            return true;
        }
        $force = $this->getConfig('forceEnable');
        if (is_callable($force)) {
            return $force();
        }

        return $force;
    }

そして問題となっていたメソッドは isSuspiciouslyProduction() というやつ。

<?php

...

    /**
     * Returns true if this applications is being executed on a domain with a TLD
     * that is commonly associated with a production environment.
     *
     * @return bool
     */
    protected function isSuspiciouslyProduction()
    {
        $host = explode('.', parse_url('http://' . env('HTTP_HOST'), PHP_URL_HOST));
        $first = current($host);
        $isIP = is_numeric(implode('', $host));

        if (count($host) === 1) {
            return false;
        }

        if ($isIP && in_array($first, ['192', '10', '127'])) {
            // Accessing the app by private IP, this is safe
            return false;
        }

        $tld = end($host);
        $safeTLD = ["localhost", "dev", "invalid", "test", "example", "local"];

        return !in_array($tld, $safeTLD);
    }

見た感じ、今のURLが開発環境のものかを判定してる感じだと思います。

判定材料は、この、isSuspiciouslyProduction() が独断で決めていますが、まぁ大体こんな感じが開発サーバーだよねって感で決めてるみたいです(それとも規約があるのかな?)

現状だと新たにisSuspiciouslyProduction()が判断する開発用ドメインをとるしかないという感じです。

http://www.atmarkit.co.jp/fwin2k/win2ktips/801exampledom/exampledom.html

↑こんなのがありました。知らなかった。勉強になりました。

それに、https://book.cakephp.org/3.0/ja/debug-kit.htmlでも記載している通り、DebugKitはローカル開発環境でのみ動作するように作られているようです。




現状お仕事でお手伝いしている企業は、各個人のローカル上での開発ではなく、開発サーバーを立ててそこに各自vhostを切って作業しています。

その際のURLが、この isSuspiciouslyProduction() が判断する 開発サーバー とは違っていたためDebugkitが出なかったということのようです。

すごい親切な設計でいいとは思うんですが、個人的にConfigure::read('debug')の判定があるので、それで判断させてほしいです。。

それとも設定値だけ変えればなんとかなるよ!みたいな事ありますかね?

もしあったらぜひ教えてください。。

ではでは。

疲れた。

cakephp3でlaravelみたいにrouteファイルを分けてみた

はじめに

こんばんは。最近ちょっとずつcakephp3を触っている僕です。

まだよちよちですが、なんとなく触ってこんな感じかと確かめてます。

で、今回は、laravelみたいにrouteファイルを分けられないかなと挑戦してみました。

cakephp3は基本的にconfigフォルダ直下にroutes.phpがあり、そこで一括管理しているみたいです。

laravelみたいにデフォでrouteファイルが分かれている方が、「僕的には」スッキリしていいかなと感じたので、できるかどうかちょっとやってみました。

※あくまでルーティングファイルをわけるだけの作業です。

やってみた

cakephp3のroute管理は、RoutingMiddlewareとしてMiddleware登録されているみたいです。

デフォルトでApplication.phpに記載されていると思います。

<?php
   ...

$middlewareQueue->add(new RoutingMiddleware($this));

そして、Applicationクラスが継承している BaseApplicationクラス にroute判定する際に実行されるメソッドが存在します。

<?php
    ...

    /**
     * {@inheritDoc}
     *
     * By default this will load `config/routes.php` for ease of use and backwards compatibility.
     *
     * @param \Cake\Routing\RouteBuilder $routes A route builder to add routes into.
     * @return void
     */
    public function routes($routes)
    {
        if (!Router::$initialized) {
            // Prevent routes from being loaded again
            Router::$initialized = true;

            require $this->configDir . '/routes.php';
        }
    }

この、routes()は、Middlewareとして登録されたRoutingMiddlewareの __invoke() メソッド内で呼び出されるloadRoutes()によって実行されています。

というわけで、routesメソッドをまるっとオーバライドして、以下みたいに変えます。

<?php
    ....

    public function routes($routes)
    {
        if (!Router::$initialized) {
            require $this->configDir . '/routes.php';
            require $this->configDir . '/routes_api.php';
            // Prevent routes from being loaded again
            Router::$initialized = true;
        }
    }

requireしている箇所に一個足しているだけですね。

次にrequireしているroutes_api.phpを作成します。

<?php

use Cake\Routing\RouteBuilder;
use Cake\Routing\Router;
use Cake\Routing\Route\DashedRoute;

Router::defaultRouteClass(DashedRoute::class);

Router::prefix('api', function (RouteBuilder $routes) {
    $routes->get('/test', ['controller' => 'test', 'action' => 'index']);
});

prefixを指定することでnamespaceも App/Controller/Api にすることが出来ます。

prefix-routing

最後にroutingで設定しているコントローラを作成します。

<?php

namespace App\Controller\Api;

use App\Controller\AppController;

class TestController extends AppController
{
    public function index()
    {
        return $this->response->withType('application/json')->withStringBody(json_encode([
            'controller' => 'test',
            'action'     => 'index',
            'prefix'     => 'api',
            'url'        => '/api/test',
            'result'     => 1
        ]));
    }
}

f:id:kojirooooocks:20180415230531p:plain

出来ました。

終わりに

とまぁちょっと思いついたんでここまでやってみました。

実運用で使うかは微妙ですが。。

plugin機能使えばいいわけだし。

ただpluginはディレクトリツリーが深くなっちゃうので、そこがネックかなーと。

なんか他に方法あれば教えてください。

ではでは。

cakephp3.5を少し触ってみた

はじめに

こんばんは。

大したことない記事ですが、最近cakephp3.5を触る機会があったんで、ちょろっとした記事ですが、自分のために残しておこうかなと思いました。

まだドキュメント見ながらですが、なんとなくやっております。

今回やったのは、環境ごとに設定を切り分ける方法と、Middlewareの使用方法です。

やってみた

環境ごとに設定を切り分ける

なんか色々やる方法があるっぽいですが、自分がやったのはbootstrap.phpで切り替える方法でした。

cakephp3.5はdotenvをデフォルトサポートしているようなので、それ利用しない手はないと思って、dotenvを呼び出すところを以下のように修正しました。

<?php

...

if (env("ENVIRONMENT") === 'production') {
    $dotEnvFilePath = CONFIG . '.env.production';
    $appFileKey     = 'app_production';
} elseif (env("ENVIRONMENT") === 'staging') {
    $dotEnvFilePath = CONFIG . '.env.staging';
    $appFileKey     = 'app_staging';
} else {
    $dotEnvFilePath = CONFIG . '.env.develop';
    $appFileKey     = 'app_develop';
}
$dotenv = new \josegonzalez\Dotenv\Loader([$dotEnvFilePath]);
$dotenv->parse()->toEnv();

try {
    Configure::config('default', new PhpConfig());
    Configure::load($appFileKey, 'default', false);
} catch (\Exception $e) {
    exit($e->getMessage() . "\n");
}

...

これで、切り替えできました。

Middleware

テスト環境・ステージング環境とかで、Basic認証かけたいみたいな事よくあって、実際今仕事いただいている会社でもそんな状況です。

現状は .htaccess .htpasswd で実装しているんですが、cakephp3上で再現したほうがめんどくさくないなと思って、やりました。

また、全体にかけないといけないので、これはMiddlewareかなと。

  1. Application.phpに以下を追加
<?php

    public function middleware($middlewareQueue)
    {
        if (env('IS_PRODUCTION') === false) {
            $middlewareQueue->add(BasicAuthenticateMiddleware::class);
        }
        
        ...
    }
  1. Middleware\BasicAuthenticateMiddleware.phpを作成
<?php

namespace App\Middleware;

/**
 * Basic認証ミドルウェア
 * Class BasicAuthenticateMiddleware
 * @package App\Middleware
 */
class BasicAuthenticateMiddleware
{
    public function __invoke($request, $response, $next)
    {
        if (isset($_SERVER['PHP_AUTH_USER'])) {
            if (!($_SERVER['PHP_AUTH_USER'] === env("BASIC_AUTH_USER") && $_SERVER['PHP_AUTH_PW'] === env("BASIC_AUTH_PASS"))) {
                $this->_basicUnauthorized();
            }
        } else {
            $this->_basicUnauthorized();
        }

        return $next($request, $response);
    }

    protected function _basicUnauthorized()
    {
        header('WWW-Authenticate: Basic realm="Please enter your ID and password"');
        header('HTTP/1.0 401 Unauthorized');
        die("Authorization Required");
    }
}

簡単でした。

終わりに

自分がcakephpをガッツリ触ってたのは1.2で、2系は少しだけ触っていたので、なんか色々できるようになってるなぁと思いました。

今はDIコンテナはないのかなぁと探してるんですが、rochamarcelo/cake-pimple-di がいいかなーと思い、ちょろちょろ触っております。

本当はSentryネタ上げたかったんですが、プライベートが忙しくて全く手を付けられていません。。。

今日はつかれたからこれで終わりますぅ。

Sentry触ってみた(JS)

はじめに

こんばんは。

今回は前回の記事の続きで、Sentryを触ってみました。

次はJSのバグレポートをあげられるようにしてみようと思います。

JSは、PHPより更に楽です。

やってみた

Sentryにログインして、ダッシュボードまで行きます。

New Projectを選択して、JavaScriptを選んで、Create Projectを実行します。

PHPの場合とほぼほぼ変わりません。

f:id:kojirooooocks:20180408211839p:plain

f:id:kojirooooocks:20180408211850p:plain

次に、セットアップです。

セットアップは簡単で、以下の2行を追加するだけです。

<script src="https://cdn.ravenjs.com/3.24.0/raven.min.js" crossorigin="anonymous"></script>
<script>Raven.config(PUBLIC_DSNのURL).install();</script>

これで、jsのエラーログをSentryが検知してくれます。

以前の記事で作った、LaravelProjectにわざとjsのエラーが起きるように配置してみます。

f:id:kojirooooocks:20180408213410p:plain

実行すると当然エラーですね。

f:id:kojirooooocks:20180408211916p:plain

ダッシュボードで確認してみます。

f:id:kojirooooocks:20180408211932p:plain

f:id:kojirooooocks:20180408212437p:plain

f:id:kojirooooocks:20180408212449p:plain]

ちゃんと上がってました!!

OSバージョンとかもちゃんと取れてていいですね。

また、今回作ったJavascriptのプロジェクトのnotificationのslack通知を有効にすれば、slackにも通知できます。

f:id:kojirooooocks:20180408212523p:plain

こんなかんじで。

やっぱり連続で同じエラー検知は、通知飛んでこなかったですね。

そういう仕様なのかな?

終わりに

phpより簡単でしたね。

ちなみに前回のphpはlaravelのフレームワークを使ったので、laravel-sentryというパッケージを使用しましたが、フレームワーク使用していないピュアなPHPのプロジェクトも当然エラー検知できます。

slackだと、ほぼほぼラグなしに通知が飛んで来るんですが、現在お仕事で使っているチャットツールがchatworkなんですよねぇ。

sentryはサポートしてないんで、zapierとかつかってムリヤリ通知飛ばすようには作ってみたのですが、zapierは無料だとなかなか厳しい制限があるので、実用には向いてないです。

自分の中では、lambda使えばなんとかなるかなーと考えてますが、このあたりの解決策募集中です。。。

おまけ

前々回の記事で、1週間に書く記事を1記事に変更しましたが、なんとか2記事かけました。

ノルマは下げたけど目標は下げたくないので、出来る限りがんばりますー!

さて、積み本読もう。

Sentry触ってみた(PHP)

参考サイト

はじめに

こんばんは。

今日はSentryっていうツールを触ってみたので、ちょっと書いておこうと思います。

現在お仕事手伝わせていただいているプロジェクトは結構年代物のプロジェクトで、比較的レガシーな環境で動いています。

testコードもほぼほぼないので、E2E的なテストツールや、バグレポート(検知)を取れるようなツールがないかなーといろいろ探して見つけたのがSentryでした。

フロントエンドのエラーの検知含め、PHPのエラーの検知も可能ということで、これはいいと思い触ってみました。

f:id:kojirooooocks:20180406035118p:plain

やってみた

アカウント作成

コチラからアカウント作成します。

確認メールも飛んできますので、Confirmを押してください。

f:id:kojirooooocks:20180406035102p:plain

言語選択

アカウント作成が終わると、以下のページに遷移します。

f:id:kojirooooocks:20180406035142p:plain

ここで、バグ検知を行う言語を選択できます。

言語以外にも、特有のFWもサポートしているようです。

今回はLaravelでいきたいと思います。

Laravelを選択して、Create Projectを押します。

f:id:kojirooooocks:20180406035156p:plain

そうすると、Laravelでの導入方法が記載されたドキュメントページへ移動します。

Sentry-laravelセットアップ

laraveのプロジェクト作成については、割愛します。

自分が上げた昔の記事にlaradockでの開発環境構築を乗せています。

laradockで簡単Laravel開発環境構築

インストールとセットアップはドキュメントにあるとおりにやっていきます。

1. install

$ composer require sentry/sentry-laravel
Using version ^0.8.0 for sentry/sentry-laravel
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 2 installs, 0 updates, 0 removals
  - Installing sentry/sentry (1.8.4): Downloading (100%)         
  - Installing sentry/sentry-laravel (0.8.0): Downloading (100%)         
Writing lock file
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nunomaduro/collision
Discovered Package: sentry/sentry-laravel
Package manifest generated successfully.

2. setup

config/app.php
$ vi config/app.php

// providersに追加 
Sentry\SentryLaravel\SentryLaravelServiceProvider::class

// aliasに追加
'Sentry' => Sentry\SentryLaravel\SentryFacade::class
app/Exceptions/Handler.php
$ vi app/Exceptions/Handler.php

// report()を以下のように修正
public function report(Exception $exception)
{
    if (app()->bound('sentry') && $this->shouldReport($exception)) {
        app('sentry')->captureException($exception);
    }

    parent::report($exception);
}
config/sentry.php作成
$ php artisan vendor:publish --provider="Sentry\SentryLaravel\SentryLaravelServiceProvider"
.env修正
$ vi .env

// 以下を追加
SENTRY_DSN=My Sentry DSN URL

エラーが出るように修正してみる

セットアップが終わったら、ダッシュボードに移動します。

f:id:kojirooooocks:20180406035255p:plain

今はまだ何もレポートが上がってない状態です。

では、実際にエラーレポートが検知されるようなコードを挟んでみましょう。

routes/web.phpを以下のように修正します。

Route::get('/', function () {
    return view('aaaa');
//    return view('welcome');
}); 

存在しないテンプレートを表示しようとしているので、laravel側では InvalidArgumentExceptionが出ます。

f:id:kojirooooocks:20180406035310p:plain

現状はテストモードなので、laravelがエラーページを表示してくれますが、本番環境の場合は、システムエラーが発生しました的なエラーページが表示されて、ユーザーサポートかなにかをフックにエンジニアがバグに気づく感じかと思います。

Sentryダッシュボードで確認

では、Sentryダッシュボードを再度見てみます。

f:id:kojirooooocks:20180406035321p:plain

一件来てます!!

f:id:kojirooooocks:20180406035331p:plain

エラーが発生しているコードの場所までキチンと見えます!

Sentryがエラーを検知したらSlackに通知してみる

エラー出るかどうかSentryのダッシュボードを見張っとくわけにもいかないので、新たなissueが上がったらslackに通知するようにしたいです。

連携は簡単にできるみたいなんで、やってみます。

例としてslackでcentry-notificationという名前のチャンネルを作成しました。

そのチャンネルにSentrySentry Notificationsというアプリをセットします。

f:id:kojirooooocks:20180406035430p:plain

f:id:kojirooooocks:20180406035444p:plain

次ページにSentry上での設定項目が書かれているので、そのとおりに設定します。

以下の画像遷移通りに行けば完了です。

f:id:kojirooooocks:20180406035457p:plain

f:id:kojirooooocks:20180406035514p:plain

f:id:kojirooooocks:20180406040003p:plain

f:id:kojirooooocks:20180406040013p:plain

チャンネルにSentryが追加されました。

では早速、エラー出してみましょう。

ではまた同じようにエラー出してみます。

f:id:kojirooooocks:20180406035608p:plain

出ました!!

終わりに

これすごくいいですね。

ただ、なんかハマったのが、同じPHPエラーの場合は、毎回Slackへ通知されないような感じがしました。

これはもしかしたらFreeプランだからなのかな?

いや、俺の設定の可能性もある・・・

なんにせよかなり使えます。

まぁ、最近のPHPのプロジェクトだとログ監視とかは普通に行われているとは思うのですが、そうではない現場では結構使えるような気がします。

次はJSのバグ検知もやってみたいと思います。

では。

ちょっとした勉強のコツを読んだ

こんばんは。積み本ばかりで、大変なので、どんどん紹介したいと思っています。

今回から積み本消化記事にしようと思います。

まず読んだのはコチラ。

ちょっとした勉強のコツ (PHP文庫)

ちょっとした勉強のコツ (PHP文庫)

あんま勉強が続かないというか勉強のやり方が上手くない僕にとって、結構刺さる内容のタイトルだったので、購入しました。

いつも通り、いいなと思ったところを抜粋していって、未来の自分に残してあげたいと思います。

本の内容も少なかったので、少なめです。

抜粋リスト

集中

時間が長くなればなるほど学習の公立は低下する。収穫逓減の法則は学習、勉強にも当てはまるようだ。

収穫逓減の法則ってなんだ?と思い調べると、この時間をかければこれくらいの生産量があるという計算のもと、時間をかけるだけそれに比例して生産量も同じように上がっていく、というわけではないっていうことのようです。

つまり、時間で量を稼ごうとしても、かけるに見合った量を稼げるというわけではないってことですね。

やるならばメリハリつけて、やりましょうってことか。

ブタモキニノボル

まちがいでもいい、ほめられると、思いがけない力を発揮するのが人間である。できるようになるには、だれか、上手に褒めてくれる人がまわりにいる必要がある。

本でも紹介されているんですが、こいう現象を心理学でピグマリオン効果っていうようです。

自分はエンジニアになった時、ぽーんとOJTで現場に放り投げれた感あるので、だれかに褒められるっていうのが、あまりないのですが、以前のゲーム会社にいたときのCTOの人には、「あのときはすごく頼りになりました!」ってよく言われました。

自分から見たらそのCTOの人は雲の上の存在で、技術力も天と地なんですが、それでもその人にそういう風に言われたら、「この人の期待を裏切らないようにしたい」と思ってよく勉強していました。

これもピグマリオン効果なのかな。

自分も誰かに何かを教えるときは、まず褒めるっていうところから入ろうと思いました。

いざ立て

頭をはたらかせるのには、姿勢が大切である。いちばん合理的な姿勢は立ったときだから、腰をかけないで、立つのがよいといったのである。

近代社会は座業によって動いている。人はほとんど立たなくなった。それが知的活力を失わせているのではないのだろうか。頭をフルにはたらかせようとする者、すべからく、立て。

スタンディングデスクとか流行っているのにはちゃんとした裏付けがあるんだなと思いました。

終わりに

すごい簡単にまとめてしまった。。。

まぁ、この本で印象に残ったことをこれだけしか抜き出せないあたり、自分が勉強がうまくないって証拠なのかもしれません。

そういえば、本の中で頻繁に「ながら」のことについて書かれていることが印象的でした。

ラジオを聴きながら、音楽を聴きながら、移動しながら、何かしながら勉強するっていうのはやはりいいということみたいです。

自分のスタイルは、この本で紹介されているのとは正反対で、椅子に座って、リクライニングにもたれながら本を読んだりしてます。

プログラムを書くときは流石に違いますが・・w

せっかく勉強のコツとして「ながら」のコツを教わったので、できるところから実践していこうと思います。

次も本呼んだ系の記事のつもりです。

おつかれさまでしたー

設定ファイルを記述する言語TOMLを触ってみた。

こんばんは。

以前Rustの記事を上げた時に、設定ファイルでTOMLという拡張子のファイルを触りました。

僕は初めて聞いたんですが、結構有名なのかな・・・?

感触的にはiniファイル的な書き方だったので、PHPのパーサあるのかなとpakagist探すとやはり有りました。

yosymfony/toml

以前の記事ではちょろっと紹介しただけだったので、せっかくなので今回触ってみます。

参考サイト

設定ファイル記述言語 TOML

インストール

$ mkdir toml_test
$ cd toml_test
$ composer require yosymfony/toml
Using version ^1.0 for yosymfony/toml
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 2 installs, 0 updates, 0 removals
  - Installing yosymfony/parser-utils (v1.0.0): Downloading (100%)         
  - Installing yosymfony/toml (v1.0.1): Downloading (100%)         
Writing lock file
Generating autoload files

やってみた

1. 簡単な読み込み

$ touch test.toml
$ touch sample.php

test.toml

AWS_SECRET_KEY="1234567890qwertyuiop"

sample.php

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

use Yosymfony\Toml\Toml;

$toml_data = Toml::ParseFile('test.toml');
print_r($toml_data);

実行してみる

$ php sample.php
Array
(
    [AWS_SECRET_KEY] => 1234567890qwertyuiop
)

なるほどなるほど。という感じですね。

これくらいは簡単な感じです。

もう少し複雑な設定ファイル用意してみます。

2. testのtomlファイルを複雑にしてみる

test.toml

AWS_SECRET_KEY = "1234567890qwertyuiop" # AWSのシークレットキー
USE_DATABASE   = true                   # データベースを使用するかどうか
BARTH_DAY      = 1984-01-21             # 誕生日

# 簡易メモ
MEMO = """
データベースはMYSQLにしました。
AWSも使ってます。
SendGridとかも使いたいな。"""

# コメント
COMMENT = """
こんにちは\
Kojirockです。\
フリーランスのエンジニアやってます。\
お仕事ください\
"""

# データーベース設定
[DATABASE]
    PORT     = 3306
    USER     = "kojirock"
    PASSWORD = "1234567890qwertyuiop"
    HOST     = "localhost"

# 家族構成
[FAMILY.MYSELF]
    name = "kojirock"
[FAMILY.WIFE]
    name = "nyoro"
[FAMILY.SON]
    name = "yutoton"

# 好きなもの
[[LIKE]]
    [[LIKE.MUSIC]]
        name = "エリック・クラプトン"
    [[LIKE.MUSIC]]
        name = "モンパチ"

    [[LIKE.FOOD]]
        name = "ラーメン"

    [[LIKE.MOVIE]]
        name = "バック・トゥ・ザ・フューチャー"

実行してみる

$ php sample.php
Array
(
    [AWS_SECRET_KEY] => 1234567890qwertyuiop
    [USE_DATABASE] => 1
    [BARTH_DAY] => DateTime Object
        (
            [date] => 1984-01-21 00:00:00.000000
            [timezone_type] => 3
            [timezone] => Asia/Tokyo
        )

    [MEMO] => データベースはMYSQLにしました。
AWSも使ってます。
SendGridとかも使いたいな。
    [COMMENT] => こんにちはKojirockです。フリーランスのエンジニアやってます。お仕事ください
    [DATABASE] => Array
        (
            [PORT] => 3306
            [USER] => kojirock
            [PASSWORD] => 1234567890qwertyuiop
            [HOST] => localhost
        )

    [FAMILY] => Array
        (
            [MYSELF] => Array
                (
                    [name] => kojirock
                )

            [WIFE] => Array
                (
                    [name] => nyoro
                )

            [SON] => Array
                (
                    [name] => yutoton
                )

        )

    [LIKE] => Array
        (
            [0] => Array
                (
                    [MUSIC] => Array
                        (
                            [0] => Array
                                (
                                    [name] => エリック・クラプトン
                                )

                            [1] => Array
                                (
                                    [name] => モンパチ
                                )

                        )

                    [FOOD] => Array
                        (
                            [0] => Array
                                (
                                    [name] => ラーメン
                                )

                        )

                    [MOVIE] => Array
                        (
                            [0] => Array
                                (
                                    [name] => バック・トゥ・ザ・フューチャー
                                )

                        )

                )

        )

)

datetimeが返ってくるのいいですね。それにbooleanも判定してくれるのも地味に嬉しい。

(print_rで表示しているので、1になっていますがUSE__DATABASEの型はbooleanでした)

コメントも # で書けて直感的でした。

この判定ってphp7以上だからかな?と思ったので、php5系にして、おなじtomlを読み込んでみます。

3. php5系にして試してみる。

phpenvで5系に戻す

$ phpenv versions
  system
  5.6.9
* 7.1.5 (set by /path/to/.anyenv/envs/phpenv/version)

$ phpenv local 5.6.9
5.6.9

$ php -v
PHP 5.6.9 (cli) (built: Feb 27 2018 00:47:13) 
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2015, by Zend Technologies
    with Xdebug v2.5.3, Copyright (c) 2002-2017, by Derick Rethans

yosymfony/tomlのバージョンを下げる

$ cat composer.json
{
    "require": {
        "yosymfony/toml": "0.x-dev"
    }
}

$ composer update
Download composer.phar ...
All settings correct for using Composer
Downloading...
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 0 installs, 1 update, 1 removal
  - Removing yosymfony/parser-utils (v1.0.0)
  - Removing yosymfony/toml (v1.0.1)
  - Installing yosymfony/toml (0.x-dev b30fa1f): Cloning b30fa1fc0a from cache
Writing lock file
Generating autoload files

sample.phpの修正

php5系は Toml::ParseFile() は使えないようなので、 Toml::parse を使います。

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

use Yosymfony\Toml\Toml;

$toml_data = Toml::parse('test.toml');
var_dump($toml_data);

実行してみる

$ php sample.php
Array
(
    [AWS_SECRET_KEY] => 1234567890qwertyuiop
    [USE_DATABASE] => 1
    [BARTH_DAY] => DateTime Object
        (
            [date] => 1984-01-21 00:00:00.000000
            [timezone_type] => 3
            [timezone] => Asia/Tokyo
        )

    [MEMO] => データベースはMYSQLにしました。
AWSも使ってます。
SendGridとかも使いたいな。
    [COMMENT] => こんにちはKojirockです。フリーランスのエンジニアやってます。お仕事ください
    [DATABASE] => Array
        (
            [PORT] => 3306
            [USER] => kojirock
            [PASSWORD] => 1234567890qwertyuiop
            [HOST] => localhost
        )

    [FAMILY] => Array
        (
            [MYSELF] => Array
                (
                    [name] => kojirock
                )

            [WIFE] => Array
                (
                    [name] => nyoro
                )

            [SON] => Array
                (
                    [name] => yutoton
                )

        )

    [LIKE] => Array
        (
            [0] => Array
                (
                    [MUSIC] => Array
                        (
                            [0] => Array
                                (
                                    [name] => エリック・クラプトン
                                )

                            [1] => Array
                                (
                                    [name] => モンパチ
                                )

                        )

                    [FOOD] => Array
                        (
                            [0] => Array
                                (
                                    [name] => ラーメン
                                )

                        )

                    [MOVIE] => Array
                        (
                            [0] => Array
                                (
                                    [name] => バック・トゥ・ザ・フューチャー
                                )

                        )

                )

        )

)

php5系でも問題ないみたいですね。

レガシーな環境でもなんとか使えそうです。

4. Tomlファイル作ってみる

パーサだけではなく、ビルダーもあるみたいです。

今回作成した test.toml と同じ内容のデータを1から作成してみます。

sample_2.php

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

use Yosymfony\Toml\TomlBuilder;

$builder = new TomlBuilder();
$results = $builder->addValue("AWS_SECRET_KEY", "1234567890qwertyuiop", "AWSのシークレットキー")
            ->addValue("USE_DATABASE", true, "データベースを使用するかどうか")
            ->addValue("BARTH_DAY", new \Datetime('1984-01-21'), "誕生日")

            ->addComment("簡易メモ")
            ->addValue("MEMO", "データベースはMYSQLにしました。\nAWSも使ってます。\nSendGridとかも使いたいな。")

            ->addComment("コメント")
            ->addValue("COMMENT", "こんにちはKojirockです。フリーランスのエンジニアやってます。お仕事ください")

            ->addComment("データーベース設定")
            ->addTable("DATABASE")
                ->addValue("PORT", 3306)
                ->addValue("USER", "kojirock")
                ->addValue("PASSWORD", "1234567890qwertyuiop")
                ->addValue("HOST", "localhost")

            ->addComment("家族構成")
            ->addTable("FAMILY.MYSELF")
                ->addValue("name", "kojirock")
            ->addTable('FAMILY.WIFE')
                ->addValue("name", "nyoro")
            ->addTable('FAMILY.SON')
                ->addValue("name", "yutoto")

            ->addComment("好きなもの")
            ->addArrayTables("LIKE")
                ->addArrayTables('LIKE.MUSIC')
                    ->addValue("name", "エリック・クラプトン")
                ->addArrayTables('LIKE.MUSIC')
                    ->addValue("name", "モンパチ")

                ->addArrayTables('LIKE.FOOD')
                    ->addValue("name", "ラーメン")

                ->addArrayTables('LIKE.MOVIE')
                    ->addValue("name", "バック・トゥ・ザ・フューチャー")

            ->getTomlString();
print_r($results);

実行してみる

$ php sample_2.php
AWS_SECRET_KEY = "1234567890qwertyuiop" #AWSのシークレットキー
USE_DATABASE = true #データベースを使用するかどうか
BARTH_DAY = 1984-01-21T00:00:00Z #誕生日
#簡易メモ
MEMO = "データベースはMYSQLにしました。\nAWSも使ってます。\nSendGridとかも使いたいな。"
#コメント
COMMENT = "こんにちはKojirockです。フリーランスのエンジニアやってます。お仕事ください"
#データーベース設定

[DATABASE]
PORT = 3306
USER = "kojirock"
PASSWORD = "1234567890qwertyuiop"
HOST = "localhost"
#家族構成

[FAMILY.MYSELF]
name = "kojirock"

[FAMILY.WIFE]
name = "nyoro"

[FAMILY.SON]
name = "yutoto"
#好きなもの

[[LIKE]]

[[LIKE.MUSIC]]
name = "エリック・クラプトン"

[[LIKE.MUSIC]]
name = "モンパチ"

[[LIKE.FOOD]]
name = "ラーメン"

[[LIKE.MOVIE]]
name = "バック・トゥ・ザ・フューチャー"

流石に完全に一緒というわけにはいけませんが、それっぽい内容になったみたいです。

5. test_2.tomlを作成して確認する

この内容を test_2.toml という名前で保存して、最初に作成した test.toml の出力と同じか確認してみます。

test_2.tomlを作成

AWS_SECRET_KEY = "1234567890qwertyuiop" #AWSのシークレットキー
USE_DATABASE = true #データベースを使用するかどうか
BARTH_DAY = 1984-01-21T00:00:00Z #誕生日
#簡易メモ
MEMO = "データベースはMYSQLにしました。\nAWSも使ってます。\nSendGridとかも使いたいな。"
#コメント
COMMENT = "こんにちはKojirockです。フリーランスのエンジニアやってます。お仕事ください"
#データーベース設定

[DATABASE]
PORT = 3306
USER = "kojirock"
PASSWORD = "1234567890qwertyuiop"
HOST = "localhost"
#家族構成

[FAMILY.MYSELF]
name = "kojirock"

[FAMILY.WIFE]
name = "nyoro"

[FAMILY.SON]
name = "yutoto"
#好きなもの

[[LIKE]]

[[LIKE.MUSIC]]
name = "エリック・クラプトン"

[[LIKE.MUSIC]]
name = "モンパチ"

[[LIKE.FOOD]]
name = "ラーメン"

[[LIKE.MOVIE]]
name = "バック・トゥ・ザ・フューチャー"

sample_3.phpを作成

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

use Yosymfony\Toml\Toml;

$toml_data = Toml::parseFile('test_2.toml');
print_r($toml_data);

実行してみる

$ php sample_3.php
Array
(
    [AWS_SECRET_KEY] => 1234567890qwertyuiop
    [USE_DATABASE] => 1
    [BARTH_DAY] => DateTime Object
        (
            [date] => 1984-01-21 00:00:00.000000
            [timezone_type] => 2
            [timezone] => Z
        )

    [MEMO] => データベースはMYSQLにしました。
AWSも使ってます。
SendGridとかも使いたいな。
    [COMMENT] => こんにちはKojirockです。フリーランスのエンジニアやってます。お仕事ください
    [DATABASE] => Array
        (
            [PORT] => 3306
            [USER] => kojirock
            [PASSWORD] => 1234567890qwertyuiop
            [HOST] => localhost
        )

    [FAMILY] => Array
        (
            [MYSELF] => Array
                (
                    [name] => kojirock
                )

            [WIFE] => Array
                (
                    [name] => nyoro
                )

            [SON] => Array
                (
                    [name] => yutoto
                )

        )

    [LIKE] => Array
        (
            [0] => Array
                (
                    [MUSIC] => Array
                        (
                            [0] => Array
                                (
                                    [name] => エリック・クラプトン
                                )

                            [1] => Array
                                (
                                    [name] => モンパチ
                                )

                        )

                    [FOOD] => Array
                        (
                            [0] => Array
                                (
                                    [name] => ラーメン
                                )

                        )

                    [MOVIE] => Array
                        (
                            [0] => Array
                                (
                                    [name] => バック・トゥ・ザ・フューチャー
                                )

                        )

                )

        )

)

test.tomlと内容は一緒でした!

終わりに

初tomlでしたが、色々出来て面白かったです。

参考サイトでも言われていますが、JSONと非常に似ているなと感じました。

最近は設定ファイル系の管理はdotenvでやってたのですが、これを気にtomlに乗り換えるのも有りかと思いました。

お疲れ様でしたー。