<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>php-tips.com</title>
	<atom:link href="https://php-tips.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://php-tips.com</link>
	<description></description>
	<lastBuildDate>Mon, 25 Jan 2021 00:08:32 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>
<site xmlns="com-wordpress:feed-additions:1">64529879</site>	<item>
		<title>Xdebug 3 アップグレードガイド 私訳</title>
		<link>https://php-tips.com/2020/12/28/xdebug3-upgrade-guid/</link>
					<comments>https://php-tips.com/2020/12/28/xdebug3-upgrade-guid/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Mon, 28 Dec 2020 04:58:39 +0000</pubDate>
				<category><![CDATA[php]]></category>
		<category><![CDATA[xdebug]]></category>
		<category><![CDATA[xdebug3]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1355</guid>
					<description><![CDATA[PHP 8に対応した、Xdebug 3がリリースされました。2から設定方法が変更されていますので、iniファイルの変更が必要です。 アップグレード方法はドキュメントに記載されています。 2021/1/25追記: 本家にマ [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>PHP 8に対応した、Xdebug 3がリリースされました。2から設定方法が変更されていますので、iniファイルの変更が必要です。<br />
アップグレード方法は<a href="https://xdebug.org/docs/upgrade_guide" rel="noopener noreferrer" target="_blank">ドキュメント</a>に記載されています。</p>
<p>2021/1/25追記: 本家にマージされました。</p>
<blockquote><p>
<a href="https://xdebug.org/docs/upgrade_guide/ja">Xdebug: Documentation » Xdebug 2 から 3 へのアップグレード</a>
</p></blockquote>
<p>訳の修正は、Github経由でPRを送れますので、誤訳があれば指摘お願いします。<a href="https://github.com/xdebug/xdebug.org/blob/master/html/docs/include/features/ja/upgrade_guide.html">xdebug.org/upgrade_guide.html at master · xdebug/xdebug.org</a></p>
<hr />
<p>私訳ですがアップグレードガイドを訳しましたので参考にどうぞ。</p>
<p><a href="https://gist.github.com/nojimage/02e93e8a266f706d4a6d47405fcb34b0" rel="noopener noreferrer" target="_blank">Gist: Xdebug 3 アップグレードガイド</a></p>
<p><script src="https://gist.github.com/nojimage/02e93e8a266f706d4a6d47405fcb34b0.js"></script></p>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2020/12/28/xdebug3-upgrade-guid/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1355</post-id>	</item>
		<item>
		<title>Symfony のコマンドラインアプリケーションを CakePHP コマンドラインアプリケーションとして動かす</title>
		<link>https://php-tips.com/2019/12/17/running-symfony-console-app-on-cakephp/</link>
					<comments>https://php-tips.com/2019/12/17/running-symfony-console-app-on-cakephp/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Mon, 16 Dec 2019 15:00:36 +0000</pubDate>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[cakephp3]]></category>
		<category><![CDATA[cli]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[symfony]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1299</guid>
					<description><![CDATA[この記事は、 CakePHP Advent Calendar 2019 17日目の記事です。 Symfony のコマンドラインアプリケーションを CakePHP コマンドラインアプリケーションとして動かす方法を解説します [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>この記事は、 <a href="https://qiita.com/advent-calendar/2019/cakephp">CakePHP Advent Calendar 2019</a> 17日目の記事です。</p>
<p>Symfony のコマンドラインアプリケーションを CakePHP コマンドラインアプリケーションとして動かす方法を解説します。</p>
<p>（余談ここから）昨日（12/16 JST）ついに、CakePHPの次期バージョンである4.0がstableリリースされました！ > <a href="https://bakery.cakephp.org/2019/12/15/cakephp_400_released.html">CakePHP 4.0.0 Released — Bakery</a><br />
3.xから色々と整理され、さらに良いフレームワークになったと思います。（余談ここまで）</p>
<hr />
<p>TL;DR: cakephp/migrations の実装が参考になるよ。</p>
<hr />
<p>現在開発中のWebアプリケーションで、定時にいくつかのタスクを実行したいという要求がありました。<br />
これまではcrontabへタスクごとに登録していましたが、今後タスク数が増えることと、実行の有無や実行時間をWebアプリ側から制御できるようにしたかったので、良い方法がないか検討していました。</p>
<span id="lavarycrunz"><h2>lavary/crunz</h2></span>
<p>そのような要件を満たすPHPライブラリとして <a href="https://github.com/lavary/crunz">lavary/crunz: A PHP-based job scheduler</a> を使用すればよいのではと考えました。</p>
<p>このライブラリは、Symfonyのコマンドラインアプリケーションとして作られていますが、どうせならCakePHPと統合して実行できるようにしたい。というのが今回のお話です。</p>
<span id=""><h2>類似の仕組み</h2></span>
<p>実は、<a href="https://github.com/cakephp/migrations">cakephp/migrations</a> プラグインも同じように、Symfonyアプリケーションである <a href="https://github.com/cakephp/phinx">Phinx</a> をCakePHPのShellでラップして動作するようになっています。</p>
<p>今回の実装にあたり、<code>Migrations\Shell\MigrationsShell</code> の手法を参考にしました。</p>
<p>Symfonyのコマンドラインアプリケーションを、CakePHPコマンドラインアプリケーションとして動かすためには、以下の3つを行います。</p>
<ul>
<li>Shell::initializeメソッドで、Symfonyアプリケーションを起動するために必要な定数などの初期化</li>
<li>Shell::runCommandメソッドをオーバーライドして、Symfonyアプリケーションへコマンドラインの引数をバイパスできるようにする</li>
<li>Shell::mainメソッドで、Symfonyアプリケーションを生成して実行する</li>
</ul>
<p>以下、今回作成したラッパーである CrobJobsShell を例示として説明します。</p>
<span id="initialize"><h2>1. initializeで定数などを準備する</h2></span>
<p>通常、crunzは、<code>vendor/bin/crunz</code> のスクリプトを通じて実行します。ここにcrunzを実行するために必要な定数が定義されています。</p>
<pre><code class="language-php">// （前略
// @TODO Remove in v2
if (!\defined('CRUNZ_BIN_DIR')) {
    \define('CRUNZ_BIN_DIR', __DIR__);
}
if (!\defined('CRUNZ_BIN')) {
    \define('CRUNZ_BIN', __FILE__);
}
// （後略
</code></pre>
<p><a href="https://github.com/lavary/crunz/blob/1.12.x/crunz#L17-L23">crunz/crunz at 1.12.x · lavary/crunz</a></p>
<p><code>CRUNZ_BIN</code>, <code>CRUNZ_BIN_DIR</code> がcrunz実行に必要な定数です。これをShellの <code>initialize</code> にて初期化するよう記述します。(<code>CRUNZ_VERSION</code>は、このShell独自の定数です。</p>
<pre><code class="language-php">    public function initialize()
    {
        if (!defined('CRUNZ_BIN')) {
            define('CRUNZ_BIN', ROOT . '/vendor/bin/crunz');
        }
        // NOTE: Remove in crunz v2
        if (!defined('CRUNZ_BIN_DIR')) {
            define('CRUNZ_BIN_DIR', dirname(CRUNZ_BIN));
        }
        if (!defined('CRUNZ_VERSION')) {
            define('CRUNZ_VERSION', '1.12.2');
        }
        parent::initialize();
    }
</code></pre>
<p><a href="https://github.com/nojimage/cakephp-cron-jobs/blob/40f9aa50e00a50e5b21eb429dd8ec552df822531/src/Shell/CronJobsShell.php#L30-L44">Shell/CronJobsShell.php#L30-L44</a></p>
<span id="runCommandSymfony"><h2>2. runCommandをオーバーライドしてコマンド引数をSymfonyアプリケーションへバイパスする</h2></span>
<p>Symfonyのコンソールアプリケーションもコマンドラインから引数を与えて実行することができます。例えばcrunzでは、</p>
<pre><code class="language-sh"># スケジュールされたタスクの実行
vendor/bin/crunz schedule:run
# スケジュールされたタスクの表示
vendor/bin/crunz schedule:list
</code></pre>
<p>といったサブコマンドがあります。これをCakePHPのシェルを通しても同じように扱えるようにします。</p>
<pre><code class="language-sh"># これで動くようにしたい
bin/cake CronJobs schedule:run
bin/cake CronJobs schedule:list
</code></pre>
<p>そのままですと、CakePHPのShellでは第2引数(<code>schedule:run</code> の部分)はサブコマンドとして解釈され、同名のメソッドが実行されてしまいます。</p>
<p>今回は、Symfonyアプリケーションに全てを任せたいので、CakePHPのコマンドライン引数のパースを回避し、Symfonyアプリケーションへ引数を渡してやる必要があります。</p>
<p>CakePHPのShellでは、<code>runCommand</code> メソッドにより引数のパースと実行するメソッドの決定が行われています。(Web側でいうルーティング処理)<br />
この <code>runCommand</code> を書き換えることで、ルーティング処理をスキップして、Symfonyアプリケーションへ引数を渡すことができるようになります。</p>
<pre><code class="language-php">    public function runCommand($argv, $autoMethod = false, $extra = [])
    {
        array_unshift($argv, 'crunz');
        $this-&gt;argv = $argv;
        return parent::runCommand($argv, $autoMethod, $extra);
    }
</code></pre>
<p><a href="https://github.com/nojimage/cakephp-cron-jobs/blob/40f9aa50e00a50e5b21eb429dd8ec552df822531/src/Shell/CronJobsShell.php#L52-L61">Shell/CronJobsShell.php#L52-L61</a></p>
<p>上記のように、コマンドライン引数 <code>$argv</code> に、サブコマンドとして存在しない任意の文字列(今回は &#8220;crunz&#8221;)を先頭に付与することで、ルーティング処理をスキップします。</p>
<p>また、後で引数をSymfonyアプリケーションへ渡すために、一時的にオブジェクトプロパティに格納します。</p>
<span id="mainSymfony"><h2>3. mainでSymfonyアプリケーションを実行する</h2></span>
<p>上記の2点で、Symfonyアプリケーションを実行するための下準備が整いました。<br />
最後に、mainメソッドでSymfonyアプリケーションの起動を行います。</p>
<pre><code class="language-php">    public function main()
    {
        $app = $this-&gt;getApp(); // 起動したいSymfonyアプリケーションインスタンスを取得
        $input = new ArgvInput($this-&gt;argv); // コマンドライン引数を渡す
        $app-&gt;setAutoExit(false); // 終了コードを取得するためAutoExitはoff
        $exitCode = $app-&gt;run($input, $this-&gt;getOutput()); // アプリケーションを実行
        return $exitCode === 0; // 終了コード判定
    }
    /**
     * @return \Crunz\Application
     */
    protected function getApp()
    {
        return new Application('CakePHP Cron Scheduler via Crunz', CRUNZ_VERSION);
    }
    /**
     * @return \Symfony\Component\Console\Output\ConsoleOutput
     */
    protected function getOutput()
    {
        return new ConsoleOutput();
    }
</code></pre>
<p><a href="https://github.com/nojimage/cakephp-cron-jobs/blob/40f9aa50e00a50e5b21eb429dd8ec552df822531/src/Shell/CronJobsShell.php#L69-L99">Shell/CronJobsShell.php#L69-L99</a></p>
<p>mainメソッドでは、起動したいSymfonyアプリケーションを取得して、コマンドライン引数とコンソール出力をセットして実行するだけです。<br />
（アプリケーションインスタンス取得とコンソール出力メソッドを別メソッドにわけているのはテスト時のモック注入のためです。</p>
<p>また、実行結果の判定を行うためにSymfonyアプリケーションの終了ステータスコードを取得できるよう、autoExitを無効化しています。</p>
<p>以上のようにして作成したのが、 <a href="https://github.com/nojimage/cakephp-cron-jobs">elstc/cakephp-cron-jobs</a> となります。</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/php-tips.com/wp-content/uploads/2019/12/cakephp-symfony.png?resize=514%2C73" alt="" width="514" height="73" class="alignnone size-full wp-image-1312" srcset="https://i0.wp.com/php-tips.com/wp-content/uploads/2019/12/cakephp-symfony.png?w=514&amp;ssl=1 514w, https://i0.wp.com/php-tips.com/wp-content/uploads/2019/12/cakephp-symfony.png?resize=300%2C43&amp;ssl=1 300w" sizes="(max-width: 514px) 100vw, 514px" /></p>
<p>このプラグインはCrunzの単純なラッパーですので、Crunzの機能はそのまま使えます。<br />
また、プラグイン独自の機能として、<a href="https://github.com/nojimage/cakephp-cron-jobs#usage">タスクの登録はイベントを通じて行える</a>ようになっています。</p>
<p>Symfonyで作成された便利なライブラリはたくさんありますので、皆さんもCakePHPと統合するプラグインを書いてみましょう。</p>
<p>それではクリスマスまであと1週間、今年もあと2週間。ちょっと早いですがみなさまよいお年を。</p>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2019/12/17/running-symfony-console-app-on-cakephp/feed/</wfw:commentRss>
			<slash:comments>38</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1299</post-id>	</item>
		<item>
		<title>CakePHPのビューテンプレート編集を爆速ちょっと楽にする PhpStorm Live Templates</title>
		<link>https://php-tips.com/2019/12/06/cakephp-live-template-on-phpstorm/</link>
					<comments>https://php-tips.com/2019/12/06/cakephp-live-template-on-phpstorm/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Fri, 06 Dec 2019 12:29:47 +0000</pubDate>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[live template]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[phpstorm]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1289</guid>
					<description><![CDATA[この記事は、 CakePHP Advent Calendar 2019 3日目の記事です、が、6日に公開しました。 PhpStorm Live Template? 公式にヘルプがあるよ。 Live templates &#038; [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>この記事は、 <a href="https://qiita.com/advent-calendar/2019/cakephp">CakePHP Advent Calendar 2019</a> 3日目の記事です、が、6日に公開しました。</p>
<p><img data-recalc-dims="1" fetchpriority="high" decoding="async" src="https://i0.wp.com/php-tips.com/wp-content/uploads/2019/12/code-pe.gif?resize=580%2C361" alt="" width="580" height="361" class="alignnone size-full wp-image-1290" /></p>
<span id="PhpStorm_Live_Template"><h2>PhpStorm Live Template?</h2></span>
<p>公式にヘルプがあるよ。</p>
<ul>
<li><a href="https://www.jetbrains.com/help/phpstorm/using-live-templates.html">Live templates &#8211; Help | PhpStorm</a></li>
<li><a href="https://pleiades.io/help/phpstorm/using-live-templates.html">ライブ・テンプレート &#8211; 公式ヘルプ | PhpStorm</a></li>
</ul>
<span id="CakePHP"><h2>CakePHPのビューテンプレートを書くのをちょっと楽にする</h2></span>
<p>CakePHPのテンプレートは素のPHPです。こんな感じですね。</p>
<pre><code class="language-php">&lt;h1 class="post-title"&gt;&lt;?= h($post-&gt;title) ?&gt;&lt;/h1&gt;
&lt;div class="post-content"&gt;
    &lt;?= nl2br(h($post-&gt;body)) ?&gt;
&lt;/div&gt;
&lt;?php if ($post-&gt;tags) : ?&gt;
    &lt;ul class="post-tags"&gt;
    &lt;?php foreach ($post-&gt;tags as $tag) : ?&gt;
        &lt;li&gt;&lt;?= h($tag-&gt;name) ?&gt;&lt;/li&gt;
    &lt;?php endforeach; ?&gt;
    &lt;/ul&gt;
&lt;?php endif; ?&gt;
</code></pre>
<p>テンプレートを書くとき、 <code>&lt;?=</code> やら、 <code>&lt;?php if</code> やら <code>&lt;?php foreach</code> をよく書いていると思います。<br />
毎回毎回、これを書くのだるいですよね。閉じタグまで含めると特に。</p>
<p>そんなときは、PhpStormのLive Templatesを使って楽をしましょう。</p>
<span id=""><h2>ライブテンプレートを追加する</h2></span>
<p><code>Preferences</code> > <code>Editor</code> > <code>Live Templates</code> でライブテンプレートを追加します。</p>
<p>追加の仕方はヘルプを読んでね。</p>
<ul>
<li><a href="https://www.jetbrains.com/help/phpstorm/creating-and-editing-live-templates.html">Creating live templates &#8211; Help | PhpStorm</a></li>
<li><a href="https://pleiades.io/help/phpstorm/creating-and-editing-live-templates.html">ライブテンプレートを作成する &#8211; 公式ヘルプ | PhpStorm</a></li>
</ul>
<p>ポイントはHTMLコンテキストでの適用にすること。ダイアログの下の方のここをクリックして設定するんだ。</p>
<p><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/php-tips.com/wp-content/uploads/2019/12/SS-2019-12-06-20.08.39.png?resize=580%2C501" alt="" width="580" height="501" class="alignnone size-full wp-image-1291" srcset="https://i0.wp.com/php-tips.com/wp-content/uploads/2019/12/SS-2019-12-06-20.08.39.png?w=605&amp;ssl=1 605w, https://i0.wp.com/php-tips.com/wp-content/uploads/2019/12/SS-2019-12-06-20.08.39.png?resize=300%2C259&amp;ssl=1 300w" sizes="(max-width: 580px) 100vw, 580px" /></p>
<span id="lt"><h2><code>&lt;?=</code></h2></span>
<p><code>pe&lt;tabキー&gt;</code> で、 <code>&lt;?=</code> になるように設定するよ。</p>
<p>Abbreviation: <code>pe</code><br />
Description: <code>php short echo</code> (これは説明文なので適当でいい</p>
<p>Template text:</p>
<pre><code>&lt;?= $END$ ?&gt;
</code></pre>
<p><code>$END$</code> は、変換後にキャレットがその位置に移動するんだ。</p>
<span id="ltphp_if__:_gtltphp_endif_gt"><h2><code>&lt;?php if () : ?&gt;&lt;?php endif; ?&gt;</code></h2></span>
<p><code>pif&lt;tabキー&gt;</code> で、 ifブロックを生成できるようにするよ。</p>
<p>Abbreviation: <code>pif</code><br />
Description: <code>php if</code> (これは説明文なので適当でいい</p>
<p>Template text:</p>
<pre><code>&lt;?php if ($CONDITIONS$) : ?&gt;
    $END$
&lt;?php endif; ?&gt;
</code></pre>
<span id="ltphp_foreach__:_gtltphp_endforeach_gt"><h2><code>&lt;?php foreach () : ?&gt;&lt;?php endforeach; ?&gt;</code></h2></span>
<p><code>pfore&lt;tabキー&gt;</code> で、foreachブロックを生成できるようにするよ。<br />
これは、ライブテンプレートの<code>PHP</code>の中に、<code>fore</code>, <code>forek</code> があるからそれをコピーして作成するといいよ。</p>
<span id="_1"><h3>キーなし</h3></span>
<p>Abbreviation: <code>pfore</code><br />
Description: <code>php foreach(iterable_expr as $value) {...}</code> (これは説明文なので適当でいい</p>
<p>Template text:</p>
<pre><code>&lt;?php foreach ($ITERABLE$ as $VAR_VALUE$) : ?&gt;
    $END$
&lt;?php endforeach; ?&gt;
</code></pre>
<span id="_2"><h3>キーあり</h3></span>
<p>Abbreviation: <code>pforek</code><br />
Description: <code>php foreach(iterable_expr as $key =&gt; $value) {...}</code> (これは説明文なので適当でいい</p>
<p>Template text:</p>
<pre><code>&lt;?php foreach ($ITERABLE$ as $VAR_KEY$ =&gt; $VAR_VALUE$) : ?&gt;
    $END$
&lt;?php endforeach; ?&gt;
</code></pre>
<span id="_3"><h2>おまけ</h2></span>
<p>PhpStorm 2019.3 で、<code>&lt;?</code> って入力すると <code>&lt;?php</code> に展開するようになったけど、<code>&lt;?=</code> を入力したいときにとても困る。<br />
無効にするオプションは検討されているみたいだけど、実装されるまでの間は次のLive Templateを設定しておくといいかも。</p>
<p>Abbreviation: <code>&lt;?php=</code><br />
Description: <code>php short echo open</code> (これは説明文なので適当でいい</p>
<p>Template text:</p>
<pre><code>&lt;?= $END$ ?&gt;
</code></pre>
<p>コンテキストはPHPで。</p>
<span id="_4"><h2>まとめ</h2></span>
<p>CakePHP あんまり関係なかった。</p>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2019/12/06/cakephp-live-template-on-phpstorm/feed/</wfw:commentRss>
			<slash:comments>37</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1289</post-id>	</item>
		<item>
		<title>CakePHP 3.x の PHP 5.6向けUUID実装では衝突がおきるかもねというお話</title>
		<link>https://php-tips.com/2019/12/02/collision-uuid-cakephp3x-with-php56/</link>
					<comments>https://php-tips.com/2019/12/02/collision-uuid-cakephp3x-with-php56/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Sun, 01 Dec 2019 15:00:43 +0000</pubDate>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[cakephp3]]></category>
		<category><![CDATA[php56]]></category>
		<category><![CDATA[random_compat]]></category>
		<category><![CDATA[uuid]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1267</guid>
					<description><![CDATA[この記事は、 CakePHP Advent Calendar 2019 2日目の記事です。 1日目の記事は、hgsgtkさんの「来たるCakePHP 4.0 を知ろう &#8211; Qiita」でした。 TL;DR:  [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>この記事は、 <a href="https://qiita.com/advent-calendar/2019/cakephp">CakePHP Advent Calendar 2019</a> 2日目の記事です。</p>
<p>1日目の記事は、hgsgtkさんの「<a href="https://qiita.com/hgsgtk/items/5445ceab193947409bcc">来たるCakePHP 4.0 を知ろう &#8211; Qiita</a>」でした。</p>
<hr />
<p>TL;DR: PHP 5.6 を使っている人は <a href="https://github.com/paragonie/random_compat">paragonie/random_compat</a> を入れましょう。</p>
<hr />
<p>先月に、UUIDの衝突に関する話題が盛り上がりました。</p>
<p><a href="https://speakerdeck.com/tanakahisateru/10miao-dechong-tu-suruuuidfalsezuo-rifang">10秒で衝突するUUIDの作り方 &#8211; Speaker Deck</a></p>
<p>この話のキモは、UUIDの生成に擬似乱数関数の <code>mt_rand</code> を使用しているとシードにより乱数が固定化するので、同じUUIDが生成されてしまうということです。</p>
<p>特にプログラムコード中で不用意に <code>mt_srand</code> を呼び出していると、衝突しやすくなります。</p>
<hr />
<p>CakePHP にも <code>Cake\Utility\Text::uuid</code> というメソッドがあり、UUIDv4が生成できるようになっています。データベーステーブルでidフィールドを<code>char(36)</code>で定義すると、レコード追加時にこのメソッドが呼ばれてUUIDがセットされるようになっています。</p>
<p><code>Cake\Utility\Text::uuid</code> は、</p>
<pre><code class="language-php">    public static function uuid()
    {
        $random = function_exists('random_int') ? 'random_int' : 'mt_rand';
        return sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            // 32 bits for "time_low"
            $random(0, 65535),
            $random(0, 65535),
            // 16 bits for "time_mid"
            $random(0, 65535),
            // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
            $random(0, 4095) | 0x4000,
            // 16 bits, 8 bits for "clk_seq_hi_res",
            // 8 bits for "clk_seq_low",
            // two most significant bits holds zero and one for variant DCE1.1
            $random(0, 0x3fff) | 0x8000,
            // 48 bits for "node"
            $random(0, 65535),
            $random(0, 65535),
            $random(0, 65535)
        );
    }
</code></pre>
<p><a href="https://github.com/cakephp/cakephp/blob/c24643b7b64c0d77726fabe53728bba86d4f1718/src/Utility/Text.php#L61-L83">src/Utility/Text.php L61-83</a></p>
<p>と実装されており、<code>random_int</code>関数のない環境（&lt; PHP 7.1）では、mt_rand関数が使用されます。</p>
<p>というわけで、<code>Cake\Utility\Text::uuid()</code> を使用した <a href="https://gist.github.com/nojimage/df00550e9cbc412e479ec9d54939cb67">衝突のサンプルコード</a> を書いてみました。</p>
<p>サンプルコード抜粋</p>
<pre><code class="language-php">use Cake\Utility\Text;
function make_seed()
{
    list($usec, $sec) = explode(' ', microtime());
    return $sec + $usec * 1000000;
}
$repeats = isset($argv[1]) ? (int)$argv[1] : 1;
for ($i = 0; $i &lt; $repeats; $i++) {
    mt_srand(make_seed()); // !! HAHAHAHAHHHAHA !!
    echo Text::uuid() . "\n";
}
</code></pre>
<p>サンプルコードでは不用意な <code>mt_srand</code> の使用例として、ありがちなマイクロ秒をシードとして与えています。<br />
なお、シードとしてマイクロ秒を与えなくても <code>mt_srand</code> が受け取るのは32bit整数の範囲なので、「10秒で衝突するUUIDの作り方」にあるように <code>mt_srand</code> を呼び出している時点で65,536回試行すればだいたい衝突します。</p>
<p>この問題は、CakePHP 3.x を PHP5.6環境で利用している時に発生します。PHP 7.1以降では、<code>random_int</code> 関数が使用されるため <code>mt_rand</code> に起因する問題は起こりません。<br />
（CakePHP 4では、PHP 7.2以降のためこの問題は発生しません。）</p>
<p><code>Cake\Utility\Text::uuid()</code> の呼び出し前に、 <code>mt_srand</code> が存在しない場合は衝突しにくくなりますが、分散環境でPID、時間が一致する場合は同じ乱数シードとなるので同じUUIDが生成されることがあります。<br />
（参考: <a href="https://twitter.com/zeriyoshi/status/1199639847457046529">https://twitter.com/zeriyoshi/status/1199639847457046529</a>）</p>
<hr />
<p>というわけで、UUIDv4の生成にはmt_randを使わないようにする必要があります。そのためには、PHP 5.6でも <code>random_int</code> 等の関数を使うことができるようになる <a href="https://github.com/paragonie/random_compat">paragonie/random_compat</a> パッケージを入れましょう。</p>
<p>以下のように、</p>
<pre><code class="language-sh">composer require "paragonie/random_compat":"^2.0|9.99.99"
</code></pre>
<p>としてrandom_compatをインストールすれば、PHP7以降の環境では、何もしないバージョンである <code>v9.99.99</code> が入りますので、将来的にPHPをバージョンアップする場合でも安心です。</p>
<span id=""><h2>補足</h2></span>
<p>いちおう、この問題については Issue を上げていますので、将来のバージョン（3.9以降）では何らかの対策がされると思います。</p>
<p><a href="https://github.com/cakephp/cakephp/issues/13944">Collision uuid in PHP 5.6 · Issue #13944 · cakephp/cakephp</a><br />
<a href="https://github.com/cakephp/cakephp/pull/13958">Deprecation of mt_rand in uuid generation by nojimage · Pull Request #13958 · cakephp/cakephp</a></p>
<span id="_1"><h2>追記</h2></span>
<p>3.9以降、paragonie/random_compatが必須となり、uuid生成でmt_randは使用されなくなります。</p>
<p><a href="https://github.com/cakephp/cakephp/pull/13958/files">Deprecation of mt_rand in uuid generation by nojimage · Pull Request #13958 · cakephp/cakephp</a></p>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2019/12/02/collision-uuid-cakephp3x-with-php56/feed/</wfw:commentRss>
			<slash:comments>37</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1267</post-id>	</item>
		<item>
		<title>PHPカンファレンス福岡2019 マネクラからの挑戦状 Webサイトコードゴルフをやってみた</title>
		<link>https://php-tips.com/2019/07/07/phpconfuk2019-mc-challenge/</link>
					<comments>https://php-tips.com/2019/07/07/phpconfuk2019-mc-challenge/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Sun, 07 Jul 2019 08:52:56 +0000</pubDate>
				<category><![CDATA[php]]></category>
		<category><![CDATA[未分類]]></category>
		<category><![CDATA[lolipop]]></category>
		<category><![CDATA[phpconfuk]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1257</guid>
					<description><![CDATA[PHPカンファレンス福岡2019で「マネクラからの挑戦状」というイベントがありました、現地ではマシンを持っていってなかったためやってませんでしたが、おうちでやってみました。 マネクラからの挑戦状 Webサイトコードゴルフ [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>PHPカンファレンス福岡2019で「マネクラからの挑戦状」というイベントがありました、現地ではマシンを持っていってなかったためやってませんでしたが、おうちでやってみました。</p>
<p><a href="https://github.com/pepabo/lolipop-mc-codegolf-challenge">マネクラからの挑戦状 Webサイトコードゴルフ pepabo/lolipop-mc-codegolf-challenge</a></p>
<p>私の<del>戦闘力</del>スコアは、58655です。開催後なので非公認スコアですね。↓↓↓↓↓↓↓↓↓↓↓</p>
<p><a href="https://gist.github.com/nojimage/85e4212008440f937acab11c34750f5f">phpconfuk2019-codegolf https://github.com/pepabo/lolipop-mc-codegolf-challenge</a></p>
<span id=""><h2>コードゴルフのルール</h2></span>
<p><a href="https://mc.lolipop.jp/">ロリポップ！マネージクラウド</a>上に、指定のソースコードから生成されるWebサイトを設置して、それのレンダリング結果を変えずにコードを修正していき、公開ディレクトリ内のファイルサイズを小さくしていくというルールです。詳しいレギュレーションはソースコードのREADMEに書いてあってこんな感じ</p>
<pre><code>## レギュレーション
- PHPで作成されたサイト https://phpconfuk-codegolf-php.lolipop.io/ （正解サイト）のソースコードをお渡しします（このリポジトリです）。
- サイトの **表示を一切変えずに** Webサイトを構成する全コード全ファイルの合計ファイルサイズを小さくして、あなたの[ロリポップ！マネージドクラウド](https://mc.lolipop.jp/)の `PHPプロジェクト` にデプロイしてください。
- 最も小さいファイルサイズになった人が優勝です
- コンテナ内の `/var/www/html` 内にWebサイトを表示するのに必要な全てのコード、およびファイルを設置してください
    - `/var/www` 等への設置は禁止です
- Makefile内の `check` タスク内のコマンドの変更は禁止です
-
</code></pre>
<p><a href="https://github.com/pepabo/lolipop-mc-codegolf-challenge#%E3%83%AC%E3%82%AE%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">pepabo/lolipop-mc-codegolf-challenge#レギュレーション</a></p>
<span id="_1"><h2>どんなふうにすすめたか</h2></span>
<p>私はこんなふうにコードを小さくしていきました。</p>
<ul>
<li>とりあえずそのままデプロイして表示確認</li>
<li>チェックスクリプトを確認</li>
<li>コードを追って表示に使っているファイルを確認</li>
<li>composer依存を排除</li>
<li>テンプレートを圧縮</li>
<li>DB依存を排除</li>
<li>画像ファイルを最適化</li>
<li>HTMLから不要CSSスタイルを除去</li>
<li>HTMLを短縮化</li>
</ul>
<span id="_2"><h2>チェックスクリプトを確認</h2></span>
<p>まずはスコア算出はどのようにしているのか確認します。スコア算出に使用されるコマンド <code>make check</code> で実行されるのはこんなコマンドでした。</p>
<pre><code class="language-sh">check:
    @ssh -p ${SSH_PORT} ${SSH_USER}@${SSH_HOST} 'find /var/www/html -type f -not -iwholename "*/.git/*" -not -name "Makefile" -not -name ".env" | xargs cat | wc -c'
</code></pre>
<p>サーバー上の <code>/var/www/html</code> ディレクトリから、<code>.git/</code> <code>Makefile</code> <code>.env</code> を除外してそのファイルサイズ合計を求めています。</p>
<p><small>※ここで<code>.git/</code>に表示ファイル全部入れればいいじゃないって気付いた勘のいいお子様はキライだよ</small></p>
<span id="_3"><h2>コードを追って表示に使っているファイルを確認</h2></span>
<p>表示開始ファイルである <code>index.php</code> から順に読んでいって使用しているファイルを確認しました。<br />
初期状態で表示に使用しているファイルは</p>
<ul>
<li>.htaccess</li>
<li>index.php</li>
<li>vendor/* (依存composer ライブラリ一式)</li>
<li>model/token.php</li>
<li>view/home.liquid</li>
<li>mc.png</li>
</ul>
<p>ということがわかりました。<br />
それ以外で <code>/var/ww/html</code> にデプロイされているファイルは不要なので <code>make deploy</code> コマンドを修正してデプロイ時に削除するようにします。<br />
.htaccessファイルについても中身はdisplay_errorsをonにしているだけなので表示には不要と判断しました。</p>
<p>インストール手順で、<code>composer.phar</code> をカレントディレクトリに配置する手順が書いてあり、composer.pharもデプロイされていたのでこれだけで結構減らせますね。</p>
<span id="composer"><h2>composer依存を排除</h2></span>
<p>composer.jsonを見るとこのスクリプトは以下のライブラリに依存していることがわかりました。</p>
<pre><code>    "morris/lessql": "^0.4.1",
    "vlucas/phpdotenv": "^3.4",
    "league/route": "^4.2",
    "zendframework/zend-diactoros": "^2.1",
    "zendframework/zend-httphandlerrunner": "^1.1",
    "liquid/liquid": "^1.4"
</code></pre>
<p>それぞれ、以下のようなライブラリです。</p>
<ul>
<li><code>vlucas/phpdotenv</code> .envファイルのパース</li>
<li><code>league/route</code>, <code>zendframework/zend-diactoros</code>, <code>zendframework/zend-httphandlerrunner</code> HTTP/ルーティング関連ライブラリ</li>
<li><code>morris/lessql</code> SQLライブラリ</li>
<li><code>liquid/liquid</code> テンプレートエンジン</li>
</ul>
<p><code>morris/lessql</code>は素のPDOを利用した書き方に変更して依存を外します。</p>
<p><code>liquid/liquid</code> もテンプレートファイル（view/home.liquid）の <code>{{ reversed }}</code> という文字列を置き換えているだけなので、プレーンなPHPで置換可能です。</p>
<p>リクエスト周りで使用している <code>league/route</code>, <code>zendframework/zend-diactoros</code>, <code>zendframework/zend-httphandlerrunner</code>は、レギュレーションのチェッカーからのリクエストを見たところ、単純にGETリクエストが送られて来ていただけのようでしたので、依存を排除しました。</p>
<p><code>vlucas/phpdotenv</code>は、.envファイルをパースして環境変数に入れてくれるものですが、ロリポップ！マネージクラウドのコントロールパネルにはインスタンスに環境変数を設定できる機能があったので、そちらから環境変数を指定するようにして、依存を排除しました。</p>
<span id="_4"><h2>テンプレートを圧縮</h2></span>
<p>表示テンプレートである <code>view/home.liquid</code> を見ると結構なファイルサイズと無駄なCSSスタイルが仕込まれていることがわかりました。</p>
<p>CSSスタイルを除外していくのは時間がかかると判断し、テンプレートを圧縮した状態でアップし、実行時に展開して使用すればいいと考えました。</p>
<p>マネージクラウドのインスタンス上で、<code>php -m</code>コマンドを叩いて使える拡張モジュールを確認したところbzip2拡張が使えるようだったのでbzip2で圧縮するようにしました。</p>
<p><a href="https://www.php.net/manual/ja/book.bzip2.php">PHP: Bzip2 &#8211; Manual</a></p>
<p>bzip2コマンドで圧縮したファイルを伸張するのは <code>bzdecompress</code> と <code>file_get_contents</code> で簡単にできます。</p>
<pre><code class="language-php">bzdecompress(file_get_contents('v.bz2'));
</code></pre>
<span id="DB"><h2>DB依存を排除</h2></span>
<p>ここではたと気付きます。この表示ロジックにDBは必要ないんじゃねと。<br />
元々のロジックは以下のようなものでした。</p>
<ol>
<li>tokensテーブルの全レコードを削除</li>
<li>クエリストリングとして入力されたキーと値をtokensテーブルに格納</li>
<li>値降順、キー降順でソートしてDBからレコードを全件取得</li>
<li>取得したレコードから3番目の値があればその値を逆から表示する、3番目の値がなければ空文字を表示する</li>
</ol>
<p>要は、クエリストリングをキーと値でソートして、3番目の値を返せばよいのです。<br />
というわけでこんな感じのコードになりました。</p>
<pre><code class="language-php">$p=$_GET;
krsort($p);
arsort($p);
$v=array_values($p);
strrev($v[2] ?? '');
</code></pre>
<p>もとのSQLのソートが<code>value DESC, key DESC</code> でしたので、キー→値の順にソートを掛けることで値のソート順が優先されるようにします。<br />
<code>krsort</code>が与えられた連想配列をキーの逆順で並べ替える関数、<code>arsort</code>が与えられた連想配列を値の逆順で並べ替える関数ですね。</p>
<p><code>array_values</code>で値だけの配列を取り出して、最後にPHP7から使えるnull合体演算子 <code>??</code> で、クエリストリングに2つ以下の値しか与えられなかった場合の対処をしてから、文字列を反転する関数 <code>strrev</code> に与えます。</p>
<p><a href="https://www.php.net/manual/ja/language.operators.comparison.php#language.operators.comparison.coalesce">Null 合体演算子</a></p>
<span id="_5"><h2>画像ファイルを最適化</h2></span>
<p>上記の最適化でコードが減り、画像ファイルのサイズがかなりのウェイトを占めるようになったので画像の最適化をしてみました。</p>
<p><a href="https://pngmini.com/">ImageAlpha — image minifier (like JPEG with transparency!)</a> とかを利用して画像を最適化しようとしましたが、アルファチャンネルを無くしたり色数を減らすとチェックに通らなくなったため画像の修正は断念しました。</p>
<p>それでも、なんやらかんやらで 59750 → 57990 に減らせました。</p>
<span id="HTMLCSS"><h2>HTMLから不要CSSスタイルを除去</h2></span>
<p>テンプレートファイルをみると明らかに使用していないスタイルが記述されていたので適用されているスタイルだけにクリーンアップしました。</p>
<span id="HTML"><h2>HTMLを短縮化</h2></span>
<p><code>html</code>とか<code>head</code>とか<code>body</code>とか属性の<code>"</code>などブラウザが補完してくれるタグを取っていきます。どんどん壊れていくHTMLを見てソウルジェムが黒く濁っていく気分を味わえました。</p>
<span id="_6"><h2>まとめ</h2></span>
<p>以上のような感じでコードを半日ちょっと掛けて最適化した結果が↓になります。</p>
<p><script src="https://gist.github.com/nojimage/85e4212008440f937acab11c34750f5f.js"></script></p>
<p>このチャレンジをやってみて、わりとモダンなコードをどんどん<del>クソコード</del>プレーンなPHPコードにしていく感じが溜まりませんでした。（汚れきった大人になった感）<br />
また、ロリポップ！マネージクラウドを使うのは初めてだったので、これを通してコントロールパネルの感じや実行の感じを体感できてよかったです。よい問題をありがとうございました。</p>
<p>参考に @tenkomaさんの解答例もどうぞ <a href="https://tenkoma.hatenablog.com/entry/2019/07/07/141155">PHPカンファレンス福岡2019 マネクラからの挑戦状 Webサイトコードゴルフ スコア60230(非公認)の解説 &#8211; Engineer as a Lifestyle @tenkoma</a></p>
<p>他の人の解答例ももっと見たいので、やった方はTwitterなどで晒してもらえると嬉しいです。</p>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2019/07/07/phpconfuk2019-mc-challenge/feed/</wfw:commentRss>
			<slash:comments>37</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1257</post-id>	</item>
		<item>
		<title>Composer post-install-cmd で dev の時のみ実行するスクリプトを定義する</title>
		<link>https://php-tips.com/2019/01/30/composer-scripts-dev/</link>
					<comments>https://php-tips.com/2019/01/30/composer-scripts-dev/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Wed, 30 Jan 2019 00:00:26 +0000</pubDate>
				<category><![CDATA[php]]></category>
		<category><![CDATA[--no-dev]]></category>
		<category><![CDATA[composer]]></category>
		<category><![CDATA[post-install-cmd]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1254</guid>
					<description><![CDATA[require-dev のパッケージに依存したスクリプトを post-install-cmd 等のフックで dev時のみ実行したい（&#8211;no-dev時には実行したくない）ときは [ $COMPOSER_DEV_ [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>require-dev のパッケージに依存したスクリプトを post-install-cmd 等のフックで dev時のみ実行したい（&#8211;no-dev時には実行したくない）ときは <code>[ $COMPOSER_DEV_MODE -eq 0 ] ||</code> を実行したいスクリプトの前に付与します。</p>
<p>ネタ元: <a href="https://stackoverflow.com/questions/13087088/composer-run-scripts-only-in-dev-environment/52182686#52182686">php &#8211; Composer &#8211; run scripts only in dev environment &#8211; Stack Overflow</a> の <a href="https://stackoverflow.com/a/52182686">回答</a></p>
<p>例: phpcsの installed_paths に cakephp/cakephp-codesniffer を追加する</p>
<pre><code class="language-json">// (composer.json)
    "require-dev": {
        "cakephp/cakephp-codesniffer": "^3.0"
    },
    "scripts": {
        "post-install-cmd": {
            "[ $COMPOSER_DEV_MODE -eq 0 ] || phpcs --config-set installed_paths $(composer config vendor-dir)/cakephp/cakephp-codesniffer"
        }
    }
</code></pre>
<pre><code class="language-sh"># 実行
composer install --no-dev # -&gt; cakephp/cakephp-codesniffer はインストールされず、post-install-cmdのphpcs --config-setも実行されない
composer install # -&gt; cakephp/cakephp-codesniffer がインストールされ、post-install-cmdのphpcs --config-setが実行される
</code></pre>
<p>composer のスクリプト実行時にセットされる環境変数 <code>$COMPOSER_DEV_MODE</code> は、<code>--no-dev</code> フラグがついているときに <code>0</code> それ以外は <code>1</code> がセットされるようになっています。 <a href="https://getcomposer.org/doc/articles/scripts.md#defining-scripts">Defining scripts &#8211; composer</a></p>
<p><code>--no-dev</code> 時は、<code>[ $COMPOSER_DEV_MODE -eq 0 ]</code> が真となるので、以降のスクリプトが実行されないという仕組みです。</p>
<p>ide-helper等をpost-*-cmdで実行するときにも使えますね。</p>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2019/01/30/composer-scripts-dev/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1254</post-id>	</item>
		<item>
		<title>CakePHP 3 でURL情報からコントローラークラスを取得する</title>
		<link>https://php-tips.com/2018/12/17/cakephp-3-create-controller-from-url/</link>
					<comments>https://php-tips.com/2018/12/17/cakephp-3-create-controller-from-url/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Sun, 16 Dec 2018 23:00:32 +0000</pubDate>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[cakephp3]]></category>
		<category><![CDATA[controller]]></category>
		<category><![CDATA[routing]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1245</guid>
					<description><![CDATA[この記事は、 CakePHP Advent Calendar 2018 17日目の記事です。 前日の記事は chinpei215 さんの CakePHP2 のデバッグツールバーをプリチーにする &#8211; Qiita [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>この記事は、 <a href="https://qiita.com/advent-calendar/2018/cakephp">CakePHP Advent Calendar 2018</a> 17日目の記事です。</p>
<p>前日の記事は chinpei215 さんの <a href="https://qiita.com/chinpei215/items/f3b66baaa50c6955a0d9">CakePHP2 のデバッグツールバーをプリチーにする &#8211; Qiita</a> でした。</p>
<hr />
<p>TL;DR: <a href="https://gist.github.com/nojimage/8e9ccef4051ba3856db6f17c13a3d0af">ControllerResolver.php &#8211; gist</a></p>
<hr />
<p>CakePHP 3.4以降の話。</p>
<p>権限コントロールまわりのアレコレのために、URL情報からコントローラークラスを取得したかったのでやってみました。</p>
<p>ここで言う <code>URL情報</code> とは、CakePHPの <code>Router::url()</code> や <code>HtmlHelper::link()</code> などに渡される配列や文字列のデータです。</p>
<pre><code class="language-php">$url = [
    'controller' =&gt; 'Users',
    'action' =&gt; 'index',
];
$url = '/users/add';
</code></pre>
<p>CakePHPにおいて配列形式のURL指定の場合、現在のリクエストのコンテキストが影響します。</p>
<pre><code class="language-php">// `/users/index` へのリクエストを扱っているとき (UsersController::add() での処理中)
$url = ['action' =&gt; 'add']; // -&gt; controller='Users', action='add' と解釈
// `/posts/edit/1` へのリクエストを扱っているとき (PostsController::edit() での処理中)
$url = ['action' =&gt; 'add']; // -&gt; controller='Posts', action='add' と解釈
</code></pre>
<p>このあたりは、 <a href="https://github.com/cakephp/cakephp/blob/3.7.0/src/Routing/Router.php#L631"><code>Router::url()</code></a> がよろしくやってくれます。</p>
<hr />
<p>さて、URLからコントローラーの取得ですが、CakePHPのコアでもディスパッチャー部分でURLをパースしてコントローラークラスを取得しているので、どのようにやっているか見てみましょう。</p>
<p>CakePHPではリクエストは以下のような流れでやってきます。</p>
<ol>
<li>webroot/index.php</li>
<li><code>\App\Application</code> 等で登録したmiddleware
<ul>
<li><code>RoutingMiddleware</code> で requestオブジェクトにURLをパースした結果(controllerとかactionとか)をセット。</li>
</ul>
</li>
<li><code>\Cake\Http\ActionDispatcher::dispatch()</code></li>
<li><code>\Cake\Http\ActionDispatcher::_invoke()</code>
<ul>
<li>ここでコントローラークラスの実行</li>
</ul>
</li>
</ol>
<p>リクエストからコントローラーの生成は <code>\Cake\Http\ActionDispatcher::dispatch()</code> で、 <code>\Cake\Http\ContorllerFacotry</code> を利用して行われています。</p>
<p><a href="https://github.com/cakephp/cakephp/blob/3.7.0/src/Http/ActionDispatcher.php#L91">cakephp/ActionDispatcher.php at 3.7.0 · cakephp/cakephp</a></p>
<p><a href="https://api.cakephp.org/3.7/class-Cake.Http.ControllerFactory.html"><code>\Cake\Http\ContorllerFacotry</code></a> には、2つの公開メソッド、</p>
<ul>
<li>リクエストからコントローラーオブジェクトを生成する <code>create(ServerRequest $request, Response $response): Controller</code></li>
<li>リクエストからコントローラークラス名を取得する <code>getControllerClass(ServerRequest $request): string</code></li>
</ul>
<p>があります。</p>
<p>この <code>\Cake\Http\ContorllerFacotry</code> を使えば、URLからコントローラークラスを取得できそうなので、あとはメソッドに渡す <code>ServerRequest</code> をURLから生成できればよさそうです。</p>
<p><code>ServerRequest</code>は、コンストラクタにURLの文字列を与えれば生成できますが、<code>Router</code>クラスによるURLのパース情報を別途与える必要があります。</p>
<p>なので、</p>
<ol>
<li>URL情報(配列/文字列)を文字列のURLに変換</li>
<li><code>ServerRequest</code> オブジェクトを生成</li>
<li><code>Router::parseRequest()</code> でリクエストからルーティング情報取得してリクエストに付与</li>
<li><code>ContorllerFacotry</code> のメソッドにリクエストオブジェクトを渡してコントローラー取得</li>
</ol>
<p>のようにしたら、コントローラーを取得できそうです。</p>
<p>コードは以下のようになります。</p>
<pre><code class="language-php">use Cake\Http\ControllerFactory;
use Cake\Http\Response;
use Cake\Http\ServerRequest;
use Cake\Routing\Router;
// $url は URL情報(配列/文字列)
// URL情報(配列/文字列)を文字列のURLに変換
$urlString = Router::url($url);
// `ServerRequest` オブジェクトを生成
$request = (new ServerRequest($urlString));
// `Router::parseRequest()` でリクエストからルーティング情報取得してリクエストに付与
$params = Router::parseRequest($request);
$request = $request-&gt;withAttribute('params', $params);
// コントローラー取得(オブジェクト)
$controller = (new ContorllerFacotry())-&gt;create($request, new Response());
// コントローラー取得(コントローラークラス名)
$controller = (new ContorllerFacotry())-&gt;getControllerClass($request);
</code></pre>
<p>以上のものをクラス化したコードを以下に上げています。</p>
<p><a href="https://gist.github.com/nojimage/8e9ccef4051ba3856db6f17c13a3d0af">ControllerResolver.php &#8211; gist</a></p>
<p><small><br />
(自分のユースケースでは、コントローラーオブジェクトが欲しかったので <code>ContorllerFacotry::create()</code> を使用しています。<br />
(本当は、URLからコントローラークラスが取得したいのではなく、そのURLのコントローラーで扱っているモデルクラスが取得したかったマン。<br />
(<a href="https://github.com/cakephp/authorization">cakephp/authorization</a> で、権限チェックを行っているところで、権限のない場合はリンクを表示させないとかやりたかったマン。<br />
</small></p>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2018/12/17/cakephp-3-create-controller-from-url/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1245</post-id>	</item>
		<item>
		<title>PHPUnitで名前空間付きのモッククラスを作る</title>
		<link>https://php-tips.com/2018/10/25/phpunit-generate-mock-class-with-namespace/</link>
					<comments>https://php-tips.com/2018/10/25/phpunit-generate-mock-class-with-namespace/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Thu, 25 Oct 2018 05:46:46 +0000</pubDate>
				<category><![CDATA[php]]></category>
		<category><![CDATA[メモ]]></category>
		<category><![CDATA[mock]]></category>
		<category><![CDATA[namespace]]></category>
		<category><![CDATA[phpunit]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1235</guid>
					<description><![CDATA[TL;DR: class_alias を使う。 PHPUnit 5.7 \Awesome\GeartInterface というインターフェースから \Awesome\MockClass という名前でモッククラスを作成した [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>TL;DR: <code>class_alias</code> を使う。</p>
<hr />
<p>PHPUnit 5.7</p>
<p><code>\Awesome\GeartInterface</code> というインターフェースから <code>\Awesome\MockClass</code> という名前でモッククラスを作成したい。</p>
<p><code>setMockClassName</code>で<code>\Awesome\MockClass</code>を指定すると名前空間付のクラスは作成できないと怒られる。</p>
<pre><code class="language-php">$mock = $this-&gt;getMockBuilder(\Awesome\GeartInterface::class)
    -&gt;setMockClassName('\Awesome\MockClass')
    -&gt;getMock(); // =&gt; Error!
</code></pre>
<p>setMockClassNameには名前空間を指定できないので <code>class_alias</code> で指定してやる。</p>
<pre><code class="language-php">$this-&gt;getMockBuilder(\Awesome\GeartInterface::class)
    -&gt;getMock();
class_alias(get_class($mock), '\Awesome\MockClass');
$this-&gt;assertInstanceOf('\Awesome\MockClass', $mock); // =&gt; OK!
</code></pre>
<p>これ、CakePHP3のObjectRegistryを継承したクラスのテストで、任意のクラスがロードできることを保証するテストを書いたときに使ったです。</p>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2018/10/25/phpunit-generate-mock-class-with-namespace/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1235</post-id>	</item>
		<item>
		<title>CakePHP3 beforeSaveでassociatedを書き換える時の注意点</title>
		<link>https://php-tips.com/2018/10/25/cakephp3-rewrite-associated-on-beforesave/</link>
					<comments>https://php-tips.com/2018/10/25/cakephp3-rewrite-associated-on-beforesave/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Thu, 25 Oct 2018 00:00:38 +0000</pubDate>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[メモ]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[cakephp3]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1233</guid>
					<description><![CDATA[TL;DR: beforeSaveでassociatedを書き換えるときは、ドット記法は使えないので配列で指定する。 Authors hasMany Posts hasMany Comments のリレーションがあるとす [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>TL;DR: beforeSaveでassociatedを書き換えるときは、ドット記法は使えないので配列で指定する。</p>
<hr />
<p><code>Authors</code> hasMany <code>Posts</code> hasMany <code>Comments</code> のリレーションがあるとする。</p>
<p>Authorsから、Posts、Commentsをまとめてsaveしたい場合、通常のsaveであれば<code>associated</code>に指定してやればよい。</p>
<pre><code class="language-php">$this-&gt;Authors-&gt;save($author, [
    'associated' =&gt; ['Posts.Comments'],
]);
</code></pre>
<p>通常はこれでよいとして、特殊な要件により通常はPostsまでの保存で、あるときだけCommentsも保存すると言った場合、<code>Model.beforeSave</code>イベントにフックしてassociatedを書き換えることを検討する。</p>
<p>単純に以下のコードでは動かない。</p>
<pre><code class="language-php">// Listenerクラスで
public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)
{
    $table = $event-&gt;getSubject();
    if ($table instanceof AuthorsTable) {
        $options['associated'][] = 'Posts.Comments';
    }
    return true;
}
</code></pre>
<p>上記だと <code>InvalidArgumentException : Cannot save Posts.Comments, it is not associated to Authors</code> と怒られる。</p>
<p>なぜならば <code>associated</code> オプションはbeforeSaveに入る前に正規化されているため、ドット記法ではなく以下のように配列化しておく必要がある。</p>
<pre><code class="language-php">public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)
{
    $table = $event-&gt;getSubject();
    if ($table instanceof AuthorsTable) {
        $options['associated']['Posts'][] = 'Comments';
    }
    return true;
}
</code></pre>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2018/10/25/cakephp3-rewrite-associated-on-beforesave/feed/</wfw:commentRss>
			<slash:comments>37</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1233</post-id>	</item>
		<item>
		<title>Chronosを1.2へバージョンアップしよう</title>
		<link>https://php-tips.com/2018/08/10/update-to-chronos-1-2/</link>
					<comments>https://php-tips.com/2018/08/10/update-to-chronos-1-2/#comments</comments>
		<dc:creator><![CDATA[nojimage]]></dc:creator>
		<pubDate>Fri, 10 Aug 2018 10:58:38 +0000</pubDate>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[cakephp3]]></category>
		<category><![CDATA[chronos]]></category>
		<category><![CDATA[test]]></category>
		<guid isPermaLink="false">http://php-tips.com/?p=1226</guid>
					<description><![CDATA[composer require cakephp/chronos:^1.2 setTestNow()メソッドがクラスを横断して時刻セットできるようなった！ CakePHP 3.2以降、時刻操作クラスとして cakephp [&#8230;]]]></description>
										<content:encoded><![CDATA[<pre><code>composer require cakephp/chronos:^1.2
</code></pre>
<ul>
<li><code>setTestNow()</code>メソッドがクラスを横断して時刻セットできるようなった！</li>
</ul>
<hr />
<p>CakePHP 3.2以降、時刻操作クラスとして <a href="https://github.com/cakephp/chronos">cakephp/chronos</a> が採用されています。<br />
CakePHP内ではChronosを継承した <code>\Cake\I18n\FrozenTime</code>, <code>\Cake\I18n\Time</code>, <code>\Cake\I18n\FrozenDate</code>, <code>\Cake\I18n\Date</code> を使用でき、データベースの時刻系のフィールドはこれらのクラスへマッピングされます。</p>
<p>また、Chronosにはテストを容易にするために<code>setTestNow()</code>というメソッドがあり、各クラスの現在時刻を指定の時間へ固定することができます。</p>
<pre><code class="language-php">use \Cake\I18n\FrozenTime;
FrozenTime::setTestNow('1975-08-08 11:22:33');
$time = new FrozenTime('1 hour ago'); // 1975-08-08 12:22:33
$now = FrozenTime::now();
FrozenTime::setTestNow($now);
sleep(10); // 10秒待つ
$currentTime = FrozenTime::now(); // 固定されているので $currentTime == $now
</code></pre>
<span id="Chronos_1.1_"><h2>Chronos 1.1 までの問題</h2></span>
<p>しかし、Chronos 1.1 までは、<code>setTestNow()</code>は各クラスごとにセットしなければなりませんでした。<br />
以下のように<code>FrozenTime::setTestNow</code>は<code>Time</code>クラスへ影響しません。</p>
<pre><code class="language-php">use \Cake\I18n\FrozenTime;
use \Cake\I18n\Time;
FrozenTime::setTestNow('1975-08-08 11:22:33');
$time = new FrozenTime('1 hour ago'); // 1975-08-08 12:22:33
$time = new Time('1 hour ago'); // 現在時刻の1時間後
</code></pre>
<p>CakePHP 3.2以降のデフォルトでは、時刻系のフィールドは <code>FrozenTime</code> で処理されるようになっています。<br />
通常は <code>FrozenTime::setTestNow</code> で時刻セットを行えばよいのですが、1ヶ所だけ <code>Time</code> が使われる部分があります。</p>
<p><code>TimestampBehavior</code> の内部では、<code>Time</code>が使用されています。ですので、テスト時に <code>FrozenTime</code> のみ時刻を固定した状態で <code>created</code> などの <code>TimestampBehavior</code> によりセットされる時刻フィールドをみると、固定した時刻ではなく実行時の時刻が入っていることになります。</p>
<p>これに対処するには、<code>FrozenTime</code>, <code>Time</code> の双方にテスト時刻をセットすればよいのですが、冗長な記述となっていました。</p>
<span id="Chronos_1.2_"><h2>Chronos 1.2 ではこの問題が改善された</h2></span>
<p>Chronos 1.2以降、<code>setTestNow</code>での設定はすべてのChronosのサブクラスへ伝播するようになりました。</p>
<pre><code class="language-php">use \Cake\I18n\FrozenTime;
use \Cake\I18n\Time;
FrozenTime::setTestNow('1975-08-08 11:22:33');
$time = new FrozenTime('1 hour ago'); // 1975-08-08 12:22:33
$time = new Time('1 hour ago'); // 1975-08-08 12:22:33
</code></pre>
<p>これにより、各時刻クラスへの<code>setTestNow</code>の漏れがなくなり重複した記述を減らせるので、今すぐChronos 1.2以降へバージョンアップすることをお勧めします。</p>
]]></content:encoded>
					<wfw:commentRss>https://php-tips.com/2018/08/10/update-to-chronos-1-2/feed/</wfw:commentRss>
			<slash:comments>36</slash:comments>
		<post-id xmlns="com-wordpress:feed-additions:1">1226</post-id>	</item>
	</channel>
</rss>
