<?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>コードリーディング | きいちログ</title>
	<atom:link href="https://wptech.kiichiro.work/tag/%E3%82%B3%E3%83%BC%E3%83%89%E3%83%AA%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0/feed/" rel="self" type="application/rss+xml" />
	<link>https://wptech.kiichiro.work</link>
	<description>WordPressとかAWSとかPHPとか</description>
	<lastBuildDate>Fri, 20 Dec 2024 14:29:19 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>【Laravel】Laravel SanctumがCSRFトークンの検証で何やってるかをちゃんと理解する</title>
		<link>https://wptech.kiichiro.work/760fab7xr3/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Thu, 19 Dec 2024 14:41:21 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3254</guid>

					<description><![CDATA[この記事は Laravel Advent Calendar 2024 19日目の投稿です。 Laravel - Qiita Advent Calendar 2024 - QiitaCalendar page for Qi [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>この記事は Laravel Advent Calendar 2024 19日目の投稿です。</p>




<a rel="noopener" href="https://qiita.com/advent-calendar/2024/laravel" title="Laravel - Qiita Advent Calendar 2024 - Qiita" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fqiita.com%2Fadvent-calendar%2F2024%2Flaravel?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">Laravel - Qiita Advent Calendar 2024 - Qiita</div><div class="blogcard-snippet external-blogcard-snippet">Calendar page for Qiita Advent Calendar 2024 regarding Laravel.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://qiita.com/advent-calendar/2024/laravel" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">qiita.com</div></div></div></div></a>



<p>Laravel Sanctum が簡単な認証システムを提供してくれるのは良く知られているんですが、認証に関する面倒なことを考えずに済む程度には抽象化されてしまっていて、裏で何をやっているのかを考えたことがありませんでした。</p>



<p>今回はそんな Laravel Sanctum が CSRF トークンの検証で何をやっているのかを追っていこうと思います。</p>



<div class="wp-block-cocoon-blocks-icon-box common-icon-box block-box information-box">
<p>Laravel Sanctum のインストールについては公式ドキュメントに記載があります。詳細はそちらを御覧ください。</p>




<a rel="noopener" href="https://laravel.com/docs/11.x/sanctum#installation" title="Laravel Sanctum - Laravel 11.x - The PHP Framework For Web Artisans" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Flaravel.com%2Fdocs%2F11.x%2Fsanctum%23installation?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">Laravel Sanctum - Laravel 11.x - The PHP Framework For Web Artisans</div><div class="blogcard-snippet external-blogcard-snippet">Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://laravel.com/docs/11.x/sanctum#installation" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">laravel.com</div></div></div></div></a>
</div>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2" checked><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">Middleware</a></li><li><a href="#toc2" tabindex="0">VerifyCsrfToken</a><ol><li><a href="#toc3" tabindex="0">CSRFトークンの検証</a></li><li><a href="#toc4" tabindex="0">CSRFトークンの付与</a></li></ol></li><li><a href="#toc5" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">Middleware</span></h2>



<p>Sanctum インストール時に <code>bootstrap/app.php</code> に以下の記述を追加したと思います。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="bootstrap/app.php" data-lang="PHP"><code>-&gt;withMiddleware(function (Middleware $middleware) {
    $middleware-&gt;statefulApi();
})</code></pre></div>



<p>これは、特定のミドルウェアの有効化フラグを true にします。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Configuration/Middleware.php" data-lang="PHP"><code>/**
 * Indicate that Sanctum&#39;s frontend state middleware should be enabled.
 *
 * @return $this
 */
public function statefulApi()
{
    $this-&gt;statefulApi = true;

    return $this;
}</code></pre></div>



<p>ここが true になっていると、api ミドルウェアグループを利用してるエンドポイントは EnsureFrontendRequestsAreStateful というミドルウェアを通るようになります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Configuration/Middleware.php" data-lang="PHP" data-line="19"><code>/**
 * Get the middleware groups.
 *
 * @return array
 */
public function getMiddlewareGroups()
{
    $middleware = [
        &#39;web&#39; =&gt; array_values(array_filter([
            \Illuminate\Cookie\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            $this-&gt;authenticatedSessions ? &#39;auth.session&#39; : null,
        ])),
        &#39;api&#39; =&gt; array_values(array_filter([
            $this-&gt;statefulApi ? \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class : null,
            $this-&gt;apiLimiter ? &#39;throttle:&#39;.$this-&gt;apiLimiter : null,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ])),
    ];
(省略)
}</code></pre></div>



<p>EnsureFrontendRequestsAreStateful では複数のミドルウェアを通していますが、ほとんどが セッションにまつわるミドルウェアで web ミドルウェアグループで定義されているミドルウェアとほぼ同じです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Laravel/Sanctum/Http/Middleware/EnsureFrontendRequestsAreStateful.php" data-lang="PHP" data-line="13,32"><code>/**
 * Handle the incoming requests.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  callable  $next
 * @return \Illuminate\Http\Response
 */
public function handle($request, $next)
{
    $this-&gt;configureSecureCookieSessions();

    return (new Pipeline(app()))-&gt;send($request)-&gt;through(
        static::fromFrontend($request) ? $this-&gt;frontendMiddleware() : []
    )-&gt;then(function ($request) use ($next) {
        return $next($request);
    });
}

(省略)

/**
 * Get the middleware that should be applied to requests from the &quot;frontend&quot;.
 *
 * @return array
 */
protected function frontendMiddleware()
{
    $middleware = array_values(array_filter(array_unique([
        config(&#39;sanctum.middleware.encrypt_cookies&#39;, \Illuminate\Cookie\Middleware\EncryptCookies::class),
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        config(&#39;sanctum.middleware.validate_csrf_token&#39;, config(&#39;sanctum.middleware.verify_csrf_token&#39;, \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class)),
        config(&#39;sanctum.middleware.authenticate_session&#39;),
    ])));

    array_unshift($middleware, function ($request, $next) {
        $request-&gt;attributes-&gt;set(&#39;sanctum&#39;, true);

        return $next($request);
    });

    return $middleware;
}</code></pre></div>



<p>その中から今回見ていくのは、VerifyCsrfToken になります。</p>



<h2 class="wp-block-heading"><span id="toc2">VerifyCsrfToken</span></h2>



<p>では、VerifyCsrfToken を見ていきます。</p>



<h3 class="wp-block-heading"><span id="toc3">CSRFトークンの検証</span></h3>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php" data-lang="PHP" data-line="13,14,15,16"><code>/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 *
 * @throws \Illuminate\Session\TokenMismatchException
 */
public function handle($request, Closure $next)
{
    if (
        $this-&gt;isReading($request) ||
        $this-&gt;runningUnitTests() ||
        $this-&gt;inExceptArray($request) ||
        $this-&gt;tokensMatch($request)
    ) {
        return tap($next($request), function ($response) use ($request) {
            if ($this-&gt;shouldAddXsrfTokenCookie()) {
                $this-&gt;addCookieToResponse($request, $response);
            }
        });
    }

    throw new TokenMismatchException(&#39;CSRF token mismatch.&#39;);
}</code></pre></div>



<p>VerifyCsrfToken では、まず検証の対象外かどうかを判定するために以下を走査しています。</p>



<ul class="wp-block-list">
<li>HTTP リクエストのメソッドが HEAD, GET, OPTIONS のいずれかかどうか</li>



<li>コンソールで実行されているかどうか
<ul class="wp-block-list">
<li><code>APP_RUNNING_IN_CONSOLE</code></li>



<li>SAPI が cli または phpdbg</li>
</ul>
</li>



<li>ユニットテストで実行されているかどうか
<ul class="wp-block-list">
<li><code>APP_ENV</code> が testing</li>
</ul>
</li>



<li>除外対象 URI かどうか
<ul class="wp-block-list">
<li><a href="https://laravel.com/docs/11.x/csrf#csrf-excluding-uris">Excluding URIs From CSRF Protection</a></li>
</ul>
</li>
</ul>



<p>検証の対象だった場合は、リクエストされた CSRF トークンの整合性を検証します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php" data-lang="PHP"><code>/**
 * Determine if the session and input CSRF tokens match.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
protected function tokensMatch($request)
{
    $token = $this-&gt;getTokenFromRequest($request);

    return is_string($request-&gt;session()-&gt;token()) &&
            is_string($token) &&
            hash_equals($request-&gt;session()-&gt;token(), $token);
}</code></pre></div>



<p>ここでは、まずリクエストからトークンを取得します。フォームからのリクエストなどでボディに、<code>_token</code> があればそれを利用し、なければヘッダから <code>X-CSRF-TOKEN</code> または <code>X-XSRF-TOKEN</code> を取得します。<br>そして、そのトークンとサーバー側のセッションストレージに保存されているトークンを比較しています。この検証に失敗すると、TokenMismatchException が投げられ、ステータスコード 419 でレスポンスが返される、ということになります。</p>



<h3 class="wp-block-heading"><span id="toc4">CSRFトークンの付与</span></h3>



<p>CSRF トークンは Set-Cookie レスポンスヘッダで付与されます。これも VerifyCsrfToken でやっています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php" data-lang="PHP" data-line="20,45,62"><code>/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 *
 * @throws \Illuminate\Session\TokenMismatchException
 */
public function handle($request, Closure $next)
{
    if (
        $this-&gt;isReading($request) ||
        $this-&gt;runningUnitTests() ||
        $this-&gt;inExceptArray($request) ||
        $this-&gt;tokensMatch($request)
    ) {
        return tap($next($request), function ($response) use ($request) {
            if ($this-&gt;shouldAddXsrfTokenCookie()) {
                $this-&gt;addCookieToResponse($request, $response);
            }
        });
    }

    throw new TokenMismatchException(&#39;CSRF token mismatch.&#39;);
}

(省略)

/**
 * Add the CSRF token to the response cookies.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Symfony\Component\HttpFoundation\Response  $response
 * @return \Symfony\Component\HttpFoundation\Response
 */
protected function addCookieToResponse($request, $response)
{
    $config = config(&#39;session&#39;);

    if ($response instanceof Responsable) {
        $response = $response-&gt;toResponse($request);
    }

    $response-&gt;headers-&gt;setCookie($this-&gt;newCookie($request, $config));

    return $response;
}

(省略)

/**
 * Create a new &quot;XSRF-TOKEN&quot; cookie that contains the CSRF token.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  array  $config
 * @return \Symfony\Component\HttpFoundation\Cookie
 */
protected function newCookie($request, $config)
{
    return new Cookie(
        &#39;XSRF-TOKEN&#39;,
        $request-&gt;session()-&gt;token(),
        $this-&gt;availableAt(60 * $config[&#39;lifetime&#39;]),
        $config[&#39;path&#39;],
        $config[&#39;domain&#39;],
        $config[&#39;secure&#39;],
        false,
        false,
        $config[&#39;same_site&#39;] ?? null,
        $config[&#39;partitioned&#39;] ?? false
    );
}
</code></pre></div>



<p>ここでは、<code>XSRF-TOKEN</code> でセッショントークンを返すようにしています。</p>



<h2 class="wp-block-heading"><span id="toc5">まとめ</span></h2>



<ul class="wp-block-list">
<li>Laravel Sanctum インストールの手順を行うと、api ミドルウェアグループでセッションの検証ができるようになる</li>



<li>CSRF トークンの検証は、VerifyCsrfToken ミドルウェアで行っている
<ul class="wp-block-list">
<li>対象でなければ検証しない</li>



<li>対象であればトークンとセッションデータを比較する</li>
</ul>
</li>



<li>CSRF トークンは Set-Cookie レスポンスヘッダに <code>XSRF-TOKEN</code> を付与している</li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【WordPress】ACFのJavaScript APIを使ってselect2フィールドをカスタマイズする</title>
		<link>https://wptech.kiichiro.work/629mgaoh70/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Tue, 02 Jul 2024 08:43:24 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3117</guid>

					<description><![CDATA[WordPress の カスタムフィールドのプラグインである ACF (Advanced Custom Fields) には、多数のフックや JavaScript API が用意されています。 ACF &#124; Resourc [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>WordPress の カスタムフィールドのプラグインである ACF (Advanced Custom Fields) には、多数のフックや JavaScript API が用意されています。</p>




<a rel="noopener" href="https://www.advancedcustomfields.com/resources" title="ACF | Resources, Documentation, API, How to &amp; Tutorial Articles" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://www.advancedcustomfields.com/wp-content/uploads/2024/11/acf-social-202411.jpg" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">ACF | Resources, Documentation, API, How to & Tutorial Articles</div><div class="blogcard-snippet external-blogcard-snippet">Discover code, documentation and ideas in this comprehensive resource section. Find everything from Getting Started, Fie...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://www.advancedcustomfields.com/resources/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.advancedcustomfields.com</div></div></div></div></a>




<a rel="noopener" href="https://www.advancedcustomfields.com/resources/javascript-api" title="ACF | JavaScript API" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://www.advancedcustomfields.com/wp-content/uploads/2024/11/acf-social-202411.jpg" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">ACF | JavaScript API</div><div class="blogcard-snippet external-blogcard-snippet">Learn how to use ACF&#039;s JavaScript library including functions, actions and filters and models.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://www.advancedcustomfields.com/resources/javascript-api/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.advancedcustomfields.com</div></div></div></div></a>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-4" checked><label class="toc-title" for="toc-checkbox-4">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">やること</a></li><li><a href="#toc2" tabindex="0">準備</a></li><li><a href="#toc3" tabindex="0">コーディング</a></li><li><a href="#toc4" tabindex="0">まとめと感想</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">やること</span></h2>



<p>今回は、JavaScript API とフックの組み合わせで select2 フィールドをカスタマイズしてみます。具体的には、投稿のプルダウンをラジオボタンで選択したカテゴリーのみに絞って表示させる、ということをやってみます。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="869" height="609" src="https://wptech.kiichiro.work/wp-content/uploads/2024/06/b369a3c9b4aec3fe3c4118709b3019ae.png" alt="カテゴリーのラジオボタンと投稿オブジェクトのプルダウンが表示されている。ラジオボタンで選択したカテゴリーの投稿だけプルダウンに表示するようにする、という説明が入っている" class="wp-image-3122" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/06/b369a3c9b4aec3fe3c4118709b3019ae.png 869w, https://wptech.kiichiro.work/wp-content/uploads/2024/06/b369a3c9b4aec3fe3c4118709b3019ae-300x210.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2024/06/b369a3c9b4aec3fe3c4118709b3019ae-768x538.png 768w" sizes="(max-width: 869px) 100vw, 869px" /></figure>



<h2 class="wp-block-heading"><span id="toc2">準備</span></h2>



<p>ACF でカテゴリーのラジオボタンと投稿のプルダウンを用意します。表示する条件は固定ページにしておきます。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="224" src="https://wptech.kiichiro.work/wp-content/uploads/2024/07/95bee59c1204c3775aeeee29604c35f0-1024x224.png" alt="ACFのフィールド。カテゴリーのラジオボタンと投稿の投稿オブジェクト" class="wp-image-3128" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/07/95bee59c1204c3775aeeee29604c35f0-1024x224.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/95bee59c1204c3775aeeee29604c35f0-300x66.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/95bee59c1204c3775aeeee29604c35f0-768x168.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/95bee59c1204c3775aeeee29604c35f0.png 1436w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>このときの各フィールドのキー (<code>field_667d1f74494c8</code> や <code>field_667d1d6343e57</code>) を控えておきます。</p>



<p>また、初期データとして、投稿とカテゴリーをいくつか用意しておきます。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="168" src="https://wptech.kiichiro.work/wp-content/uploads/2024/07/23f6e6de9d5fd3c97cdbf4f29655c3fa-1024x168.png" alt="固定ページ編集ページで表示されるカテゴリーと投稿のカスタムフィールド" class="wp-image-3131" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/07/23f6e6de9d5fd3c97cdbf4f29655c3fa-1024x168.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/23f6e6de9d5fd3c97cdbf4f29655c3fa-300x49.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/23f6e6de9d5fd3c97cdbf4f29655c3fa-768x126.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/23f6e6de9d5fd3c97cdbf4f29655c3fa-1536x252.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/23f6e6de9d5fd3c97cdbf4f29655c3fa.png 1906w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>投稿のプルダウンをクリックすると、全ての投稿が選択肢として表示されます。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="671" height="307" src="https://wptech.kiichiro.work/wp-content/uploads/2024/07/ac5e711ec26fce716af67d234a591730.png" alt="投稿のプルダウンクリック時に全ての投稿が選択肢として表示されている" class="wp-image-3135" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/07/ac5e711ec26fce716af67d234a591730.png 671w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/ac5e711ec26fce716af67d234a591730-300x137.png 300w" sizes="(max-width: 671px) 100vw, 671px" /></figure>



<h2 class="wp-block-heading"><span id="toc3">コーディング</span></h2>



<p>select2 フィールドのプルダウンはクリックの都度、Ajax 通信で表示する選択肢を取得しています。この Ajax のリクエストに介入する JavaScript のフックに <code>select2_ajax_data</code> というのが用意されています。</p>




<a rel="noopener" href="https://www.advancedcustomfields.com/resources/javascript-api/#filters-select2_ajax_data" title="ACF | JavaScript API" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://www.advancedcustomfields.com/wp-content/uploads/2024/11/acf-social-202411.jpg" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">ACF | JavaScript API</div><div class="blogcard-snippet external-blogcard-snippet">Learn how to use ACF&#039;s JavaScript library including functions, actions and filters and models.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://www.advancedcustomfields.com/resources/javascript-api/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.advancedcustomfields.com</div></div></div></div></a>



<div class="hcb_wrap"><pre class="prism line-numbers lang-js" data-file="acf-input.js" data-lang="JavaScript" data-line="25"><code>getAjaxData: function (params) {
  // vars
  var ajaxData = {
    action: this.get(&#39;ajaxAction&#39;),
    s: params.term || &#39;&#39;,
    paged: params.page || 1
  };

  // field helper
  var field = this.get(&#39;field&#39;);
  if (field) {
    ajaxData.field_key = field.get(&#39;key&#39;);
    if (field.get(&#39;nonce&#39;)) {
      ajaxData.nonce = field.get(&#39;nonce&#39;);
    }
  }

  // callback
  var callback = this.get(&#39;ajaxData&#39;);
  if (callback) {
    ajaxData = callback.apply(this, [ajaxData, params]);
  }

  // filter
  ajaxData = acf.applyFilters(&#39;select2_ajax_data&#39;, ajaxData, this.data, this.$el, field || false, this);

  // return
  return acf.prepareForAjax(ajaxData);
},</code></pre></div>



<p>このフックを使ってラジオボタンで選択されたカテゴリーの情報を POST に追加するようにします。適当な JS ファイルに以下の記述をします。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-js" data-file="acf-js-api-usage.js" data-lang="JavaScript"><code>// 素の JS
acf.add_filter(&#39;select2_ajax_data&#39;, function (data, args, $input, field, instance) {
    if (&#39;field_667d1d6343e57&#39; !== data.field_key) {
        // 投稿のプルダウンでなければ何もしない
        return data;
    }

    // カテゴリーのラジオボタンで選択された value を取得する
    const selectedCat = document.querySelector(&#39;input[name=&quot;acf[field_667d1f74494c8]&quot;]:checked&#39;);
    if (selectedCat) {
        data.cat = selectedCat.value;
    }
    return data;
});

// jQuery
jQuery(document).ready(function ($) {
    acf.add_filter(&#39;select2_ajax_data&#39;, function (data, args, $input, field, instance) {
        if (&#39;field_667d1d6343e57&#39; !== data.field_key) {
            return data;
        }
        const $selectedCat = $(&#39;input[name=&quot;acf[field_667d1f74494c8]&quot;]:checked&#39;);
        if ($selectedCat) {
            data.cat = $selectedCat.val();
        }
        return data;
    });
});</code></pre></div>



<p>フィールド作成時に控えておいたキーはユニークになっているので、セレクタとして使用しています。</p>



<p>次に、PHP 側のフックを使って、作成した JS ファイルの enqueue を行います。注意点として、ACF の JavaScript API を利用するには、ハンドル名 <code>acf-input</code> があらかじめ読み込まれている必要があるので、引数に追加するのを忘れないようにします。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>add_action(&#39;admin_enqueue_scripts&#39;, function ($hook_suffix) {
    if (&#39;post.php&#39; === $hook_suffix || &#39;post-new.php&#39; === $hook_suffix) {
        wp_register_script(&#39;acf-js-api-usage&#39;, plugin_dir_url(__FILE__) . &#39;/acf-js-api-usage.js&#39;, [&#39;acf-input&#39;]);
        wp_enqueue_script(&#39;acf-js-api-usage&#39;);
    }
});</code></pre></div>



<p>PHP 側では POST したカテゴリー情報をハンドリングする処理も必要です。ACF で、<code>acf/fields/post_object/query/key={$key}</code> というフックが用意されているので、これを利用します。</p>




<a rel="noopener" href="https://www.advancedcustomfields.com/resources/acf-fields-post_object-query" title="ACF | acf/fields/post_object/query" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://www.advancedcustomfields.com/wp-content/uploads/2024/11/acf-social-202411.jpg" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">ACF | acf/fields/post_object/query</div><div class="blogcard-snippet external-blogcard-snippet">Filters the $args used to query posts in the Post Object field.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://www.advancedcustomfields.com/resources/acf-fields-post_object-query/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.advancedcustomfields.com</div></div></div></div></a>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>add_filter(&#39;acf/fields/post_object/query/key=field_667d1d6343e57&#39;, function($args, $field, $post_id) {
    $args[&#39;cat&#39;] = $_POST[&#39;cat&#39;];
    return $args;
}, 10, 3);</code></pre></div>



<p>このフィルターフックで返却する <code>$args</code> は最終的に WP_Query の引数になります。今回はカテゴリーで絞り込むので、POST した値を <code>$args['cat']</code> に代入します。</p>



<p>画面を確認してみると、ラジオボタンで選択したカテゴリーの投稿のみが表示されていることがわかります。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="554" height="294" src="https://wptech.kiichiro.work/wp-content/uploads/2024/07/d29dc7be02d3d11f0cd2b7f69423eb9b.png" alt="" class="wp-image-3157" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/07/d29dc7be02d3d11f0cd2b7f69423eb9b.png 554w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/d29dc7be02d3d11f0cd2b7f69423eb9b-300x159.png 300w" sizes="(max-width: 554px) 100vw, 554px" /></figure>



<h2 class="wp-block-heading"><span id="toc4">まとめと感想</span></h2>



<p>今回、ACF のコードを数年ぶりに読みました。私にとって、WordPress のプラグインの中では読みやすい部類だと感じました。ドキュメントもしっかりメンテされている (と思っている) ので、迷子になることもなかったです。</p>



<p>ACF の JavaScript API とフックは、設定画面では出来ないようなフィールドの複雑なカスタマイズやフィールドの表示条件を動的に変更することが出来るので、わりと便利な機能だと思っています。ただ、認知度が低い気がします。日本語の記事もほとんど見当たらなかったですし。だいぶニッチな機能なので仕方ないと言えばそうなのですが。。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【Laravel】laravel-modulesでmiddlewareの登録をする</title>
		<link>https://wptech.kiichiro.work/90uszwt39n/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Tue, 25 Jun 2024 13:34:06 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3072</guid>

					<description><![CDATA[Laravel で手っ取り早くモジュラモノリスやるときは laravel-modules を良く使います。 GitHub - nWidart/laravel-modules: Module Management In L [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Laravel で手っ取り早くモジュラモノリスやるときは laravel-modules を良く使います。</p>




<a rel="noopener" href="https://github.com/nWidart/laravel-modules" title="GitHub - nWidart/laravel-modules: Module Management In Laravel" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://opengraph.githubassets.com/06f58c73de0840869955726bca7cd951c8af998c95dad257fab9389a8fe6b1d8/nWidart/laravel-modules" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">GitHub - nWidart/laravel-modules: Module Management In Laravel</div><div class="blogcard-snippet external-blogcard-snippet">Module Management In Laravel. Contribute to nWidart/laravel-modules development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/nWidart/laravel-modules" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<p>Modules 配下に各モジュールを配置しているためファイルがモジュール毎にまとまっており、内部設計的にはシンプルでわかりやすいと思います。ただし、モジュール間でファイルを相互に参照出来るので、依存関係の検証は別の仕組みでなんとかする必要があります。</p>



<p>今回は、laravel-modules でモジュール内に作成した middleware を登録する方法をまとめてみます。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-6" checked><label class="toc-title" for="toc-checkbox-6">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">準備</a></li><li><a href="#toc2" tabindex="0">bootstrap/app.php で登録する</a></li><li><a href="#toc3" tabindex="0">RouteServiceProvider で登録する</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">準備</span></h2>



<p>Laravel と laravel-modules をインストールしておきます。今回は両方とも11です。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>$ composer create-project laravel/laravel .
$ composer require nwidart/laravel-modules</code></pre></div>



<p>忘れがちな設定ですが、extra セクションに merge-plugin を追加します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="composer.json"><code>&quot;extra&quot;: {
    &quot;laravel&quot;: {
        &quot;dont-discover&quot;: []
    },
    &quot;merge-plugin&quot;: {
        &quot;include&quot;: [
            &quot;Modules/*/composer.json&quot;
        ]
    }
},</code></pre></div>



<p>適当な名前のモジュールと middleware を作っておきます。middleware は artisan コマンドを使うと簡単に生成出来ます。</p>




<a rel="noopener" href="https://laravelmodules.com/docs/v11/artisan-commands#module-make-middleware" title="Redirecting to https://laravelmodules.com/docs/1/v11/artisan-commands" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Flaravelmodules.com%2Fdocs%2Fv11%2Fartisan-commands%23module-make-middleware?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">Redirecting to https://laravelmodules.com/docs/1/v11/artisan-commands</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://laravelmodules.com/docs/v11/artisan-commands#module-make-middleware" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">laravelmodules.com</div></div></div></div></a>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>$ php artisan module:make Posts
$ php artisan module:make-middleware PostsMiddleware Posts</code></pre></div>



<p>作成した middleware に適当な処理を追加しておきます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="PostsMiddleware.php" data-lang="PHP" data-line="15"><code>&lt;?php

namespace Modules\Posts\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class PostsMiddleware
{
    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, Closure $next)
    {
        echo &#39;Hello, world!! &#39;;
        return $next($request);
    }
}</code></pre></div>



<h2 class="wp-block-heading"><span id="toc2">bootstrap/app.php で登録する</span></h2>



<p>middleware は bootstrap/app.php の <strong>withMiddleware</strong> セクション内で登録するのが正攻法です。</p>




<a rel="noopener" href="https://laravel.com/docs/11.x/middleware#registering-middleware" title="Middleware - Laravel 11.x - The PHP Framework For Web Artisans" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://laravel.com/images/og/laravel-docs.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">Middleware - Laravel 11.x - The PHP Framework For Web Artisans</div><div class="blogcard-snippet external-blogcard-snippet">Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://laravel.com/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">laravel.com</div></div></div></div></a>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="bootstrap/app.php" data-lang="PHP" data-line="14,15,16,17,18,19,20,21"><code>&lt;?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    -&gt;withRouting(
        web: __DIR__.&#39;/../routes/web.php&#39;,
        commands: __DIR__.&#39;/../routes/console.php&#39;,
        health: &#39;/up&#39;,
    )
    -&gt;withMiddleware(function (Middleware $middleware) {
        // グローバルミドルウェアで登録
        $middleware-&gt;append(\Modules\Posts\Http\Middleware\PostsMiddleware::class);

        // ミドルウェアグループに登録
        $middleware-&gt;appendToGroup(&#39;group&#39;, [\Modules\Posts\Http\Middleware\PostsMiddleware::class]);

        // エイリアスとして登録
        $middleware-&gt;alias([&#39;post&#39; =&gt; \Modules\Posts\Http\Middleware\PostsMiddleware::class ]);
    })
    -&gt;withExceptions(function (Exceptions $exceptions) {
        //
    })-&gt;create();
</code></pre></div>



<p>以下のようなルートを用意して GET リクエストを投げてみます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Modules/Posts/routes/web.php" data-lang="PHP"><code>&lt;?php

use Illuminate\Support\Facades\Route;

Route::get(&#39;/posts/global&#39;, function() {
    return &#39;(global)&#39;;
});

Route::get( &#39;/posts/alias&#39;, function() {
    return &#39;(alias)&#39;;
})-&gt;middleware(&#39;post&#39;);

Route::middleware([&#39;group&#39;])-&gt;group(function() {
    Route::get( &#39;/posts/group&#39;, function() {
        return &#39;(group)&#39;;
    });
});</code></pre></div>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>$ curl http://localhost:8000/posts/global
Hello, world!! (global)%

$ curl http://localhost:8000/posts/alias 
Hello, world!! Hello, world!! (alias)%

$ curl http://localhost:8000/posts/group
Hello, world!! Hello, world!! (group)%</code></pre></div>



<p>alias と group はグローバルミドルウェアと重複して登録しているので2回出力がありますが、ちゃんと動いていることが確認出来ます。</p>



<p>ただし、グローバルミドルウェアとして登録されているので、モジュール配下のルート以外で定義されたルートでも当該 middleware は適用されてしまいます。この点だけは注意が必要です。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="routes/web.php" data-lang="PHP"><code>&lt;?php

use Illuminate\Support\Facades\Route;

Route::get(&#39;/global&#39;, function() {
    return &#39;(global common)&#39;;
});</code></pre></div>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>$ curl http://localhost:8000/global     
Hello, world!! (global common)%</code></pre></div>



<h2 class="wp-block-heading"><span id="toc3">RouteServiceProvider で登録する</span></h2>



<p>別の方法として、RouteServiceProvider に記述することも出来ます。</p>



<p>この RouteServiceProvider は <code>Illuminate\Foundation\Support\Providers\RouteServiceProvider</code> クラスを継承しており、register 内で map メソッドを呼び出します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Support/Providers/RouteServiceProvider.php" data-lang="PHP" data-line="14,37,38,39"><code>/**
 * Register any application services.
 *
 * @return void
 */
public function register()
{
    $this-&gt;booted(function () {
        $this-&gt;setRootControllerNamespace();

        if ($this-&gt;routesAreCached()) {
            $this-&gt;loadCachedRoutes();
        } else {
            $this-&gt;loadRoutes();

            $this-&gt;app-&gt;booted(function () {
                $this-&gt;app[&#39;router&#39;]-&gt;getRoutes()-&gt;refreshNameLookups();
                $this-&gt;app[&#39;router&#39;]-&gt;getRoutes()-&gt;refreshActionLookups();
            });
        }
    });
}

/**
 * Load the application routes.
 *
 * @return void
 */
protected function loadRoutes()
{
    if (! is_null(self::$alwaysLoadRoutesUsing)) {
        $this-&gt;app-&gt;call(self::$alwaysLoadRoutesUsing);
    }

    if (! is_null($this-&gt;loadRoutesUsing)) {
        $this-&gt;app-&gt;call($this-&gt;loadRoutesUsing);
    } elseif (method_exists($this, &#39;map&#39;)) {
        $this-&gt;app-&gt;call([$this, &#39;map&#39;]);
    }
}</code></pre></div>



<p>RouteServiceProvider の map メソッド内では、web 及び api ルートに関する定義がされているので、ここで middleware の登録をすると良さそうです。今回はエイリアスを登録して web ルートに適用してみます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="RouteServiceProvider" data-lang="PHP" data-line="26,39"><code>&lt;?php

namespace Modules\Posts\Providers;

use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Modules\Posts\Http\Middleware\PostsMiddleware;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * Called before routes are registered.
     *
     * Register any model bindings or pattern based filters.
     */
    public function boot(): void
    {
        parent::boot();
    }

    /**
     * Define the routes for the application.
     */
    public function map(): void
    {
        Route::aliasMiddleware(&#39;post&#39;, PostsMiddleware::class);
        $this-&gt;mapApiRoutes();

        $this-&gt;mapWebRoutes();
    }

    /**
     * Define the &quot;web&quot; routes for the application.
     *
     * These routes all receive session state, CSRF protection, etc.
     */
    protected function mapWebRoutes(): void
    {
        Route::middleware([&#39;web&#39;, &#39;post&#39;])-&gt;group(module_path(&#39;Posts&#39;, &#39;/routes/web.php&#39;));
    }

    /**
     * Define the &quot;api&quot; routes for the application.
     *
     * These routes are typically stateless.
     */
    protected function mapApiRoutes(): void
    {
        Route::middleware(&#39;api&#39;)-&gt;prefix(&#39;api&#39;)-&gt;name(&#39;api.&#39;)-&gt;group(module_path(&#39;Posts&#39;, &#39;/routes/api.php&#39;));
    }
}
</code></pre></div>



<p>GET リクエストを投げてみます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>$ curl http://localhost:8000/posts/global
Hello, world!! (global)%</code></pre></div>



<p>middleware が適用されていました。モジュール内で作成した middleware をモジュール内で閉じたい場合はこの方法が良さそうな気がします。</p>



<p>ただし、モジュール配下の RouteServiceProvider 内で登録した middleware のエイリアスはモジュール外でも使えるということと、モジュール外で使った場合、当該モジュールが disabled だとエイリアスが見つからず500エラーを返してしまう点は注意が必要です。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="routes/web.php" data-lang="PHP" data-line="7"><code>&lt;?php

use Illuminate\Support\Facades\Route;

Route::get(&#39;/global&#39;, function() {
    return &#39;(global common)&#39;;
})-&gt;middleware(&#39;post&#39;);</code></pre></div>



<div class="hcb_wrap"><pre class="prism line-numbers lang-json" data-file="modules_statuses.json" data-lang="JSON"><code>{
    &quot;Posts&quot;: false
}</code></pre></div>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>$ curl -I  http://localhost:8000/global
HTTP/1.1 500 Internal Server Error</code></pre></div>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【Laravel】preventAccessingMissingAttributesの挙動+おまけ</title>
		<link>https://wptech.kiichiro.work/87w6kxhjov/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Fri, 31 May 2024 08:27:44 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=2999</guid>

					<description><![CDATA[目次 preventAccessingMissingAttributesの挙動おまけ preventAccessingMissingAttributesの挙動 Laravel の Eloquent を利用してデータを取得 [&#8230;]]]></description>
										<content:encoded><![CDATA[

  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-8" checked><label class="toc-title" for="toc-checkbox-8">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">preventAccessingMissingAttributesの挙動</a></li><li><a href="#toc2" tabindex="0">おまけ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">preventAccessingMissingAttributesの挙動</span></h2>



<p>Laravel の Eloquent を利用してデータを取得する場合、特に列の指定がなければすべての列情報を取得してきます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>$user = User::find(1);

// App\Models\User {
//    id: 5,
//    name: &quot;kiichiro&quot;,
//    email: &quot;kiichiro@example.com&quot;,
//    email_verified_at: &quot;2024-05-28 06:44:34&quot;,
//    password: &quot;$2y$12$jJuJzdMPZ/gSwVSp3DzNE.1SwACJGqPLsWLWP3HGUZRhP1Q/9zJs2&quot;,
//    remember_token: &quot;KjmFVc1rUZ&quot;,
//    created_at: &quot;2024-05-28 06:44:35&quot;,
//    updated_at: &quot;2024-05-28 06:51:32&quot;,
// }</code></pre></div>



<p>列の指定があれば指定された列情報のみを取得します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>$user = User::select([&#39;id&#39;, &#39;name&#39;])-&gt;find(1);

// App\Models\User {
//     id: 1,
//     name: &quot;kiichiro&quot;,
// }
</code></pre></div>



<p>このとき、取得しなかったプロパティへのアクセスが発生すると、null を返します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>$user-&gt;email; // null</code></pre></div>



<p>この挙動故に、アクセスしたプロパティをそもそも取得していないのか、取得した上で null なのかが判断出来ず、バグになってしまうのはよくある話ですね。</p>



<p>これを解決するには <code>AppServiceProvider</code> に <code>Model::preventAccessingMissingAttributes</code> を呼び出すと良いらしいです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="AppServiceProvider.php" data-lang="PHP" data-line="3"><code>public function boot(): void
{
    Model::preventAccessingMissingAttributes();
}</code></pre></div>



<p>取得していないプロパティへのアクセスが発生した場合は <code>MissingAttributeException</code> を投げてくれます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP" data-line="4"><code>$user = User::select([&#39;id&#39;, &#39;name&#39;])-&gt;find(1);
$user-&gt;email;

// Illuminate\Database\Eloquent\MissingAttributeException  The attribute [email] either does not exist or was not retrieved for model [App\Models\User].</code></pre></div>



<p>ただし、列情報を取得していなくても動的プロパティとしてセットしたり、<code>fill</code> でプロパティを埋めた場合はこの例外は投げられません。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>$user = User::select([&#39;id&#39;, &#39;name&#39;])-&gt;find(1);
$user-&gt;email = &#39;kiichiro@example.com&#39;;
$user-&gt;email;
// &#39;kiichiro@example.com&#39;

$user = User::select([&#39;id&#39;, &#39;name&#39;])-&gt;find(1);
$user-&gt;fill([&#39;email&#39; =&gt; &#39;kiichiro@example.com&#39;]);
$user-&gt;email;
// &#39;kiichiro@example.com&#39;</code></pre></div>



<p>プロパティへのアクセスは基本的に <code>attributes</code> という配列のキーに相当するものがあるかどうかを判別しています。判別の結果、<code>attributes</code> になければ<code> <code>MissingAttributeException</code></code> を投げ、<code>attributes</code> にあればその値を返します。よって、動的プロパティとしてセットした場合は <code>attributes</code> にあるので値がそのまま返ってくるということのようです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate\Database\Eloquent\Concerns\HasAttributes.php" data-lang="PHP" data-line="9,27,40,55"><code>/**
 * Dynamically retrieve attributes on the model.
 *
 * @param  string  $key
 * @return mixed
 */
public function __get($key)
{
    return $this-&gt;getAttribute($key);
}

/**
 * Get an attribute from the model.
 *
 * @param  string  $key
 * @return mixed
 */
public function getAttribute($key)
{
    if (! $key) {
        return;
    }

    // If the attribute exists in the attribute array or has a &quot;get&quot; mutator we will
    // get the attribute&#39;s value. Otherwise, we will proceed as if the developers
    // are asking for a relationship&#39;s value. This covers both types of values.
    if ($this-&gt;hasAttribute($key)) {
        return $this-&gt;getAttributeValue($key);
    }

    // Here we will determine if the model base class itself contains this given key
    // since we don&#39;t want to treat any of those methods as relationships because
    // they are all intended as helper methods and none of these are relations.
    if (method_exists(self::class, $key)) {
        return $this-&gt;throwMissingAttributeExceptionIfApplicable($key);
    }

    return $this-&gt;isRelation($key) || $this-&gt;relationLoaded($key)
                ? $this-&gt;getRelationValue($key)
                : $this-&gt;throwMissingAttributeExceptionIfApplicable($key);
}

/**
 * Determine whether an attribute exists on the model.
 *
 * @param  string  $key
 * @return bool
 */
public function hasAttribute($key)
{
    if (! $key) {
        return false;
    }

    return array_key_exists($key, $this-&gt;attributes) ||
        array_key_exists($key, $this-&gt;casts) ||
        $this-&gt;hasGetMutator($key) ||
        $this-&gt;hasAttributeMutator($key) ||
        $this-&gt;isClassCastable($key);
}</code></pre></div>



<h2 class="wp-block-heading"><span id="toc2">おまけ</span></h2>



<p><code>preventAccessingMissingAttributes</code> は Laravel 9系で実装された機能なので、Laravel 9 の公式ドキュメントには記載があるのですが、なぜか10以降のドキュメントから削除されていました。</p>




<a rel="noopener" href="https://laravel.com/docs/9.x/eloquent#configuring-eloquent-strictness" title="Eloquent: Getting Started - Laravel 9.x - The PHP Framework For Web Artisans" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://laravel.com/images/og/laravel-docs.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">Eloquent: Getting Started - Laravel 9.x - The PHP Framework For Web Artisans</div><div class="blogcard-snippet external-blogcard-snippet">Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://laravel.com/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">laravel.com</div></div></div></div></a>




<a rel="noopener" href="https://laravel.com/docs/10.x/eloquent#configuring-eloquent-strictness" title="Eloquent: Getting Started - Laravel 10.x - The PHP Framework For Web Artisans" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://laravel.com/images/og/laravel-docs.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">Eloquent: Getting Started - Laravel 10.x - The PHP Framework For Web Artisans</div><div class="blogcard-snippet external-blogcard-snippet">Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://laravel.com/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">laravel.com</div></div></div></div></a>



<p>どうやら以下のコミットで削除されたようです。</p>




<a rel="noopener" href="https://github.com/laravel/docs/commit/616edf4f7dcc58e069a304ba49c654551bdcf460" title="wip · laravel/docs@616edf4" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://opengraph.githubassets.com/7b39e0b7da077a007cfe5b2685ada2acb20177921ab68d9f3b7a87c35ec8c036/laravel/docs/commit/616edf4f7dcc58e069a304ba49c654551bdcf460" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">wip · laravel/docs@616edf4</div><div class="blogcard-snippet external-blogcard-snippet">The Laravel documentation. Contribute to laravel/docs development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/laravel/docs/commit/616edf4f7dcc58e069a304ba49c654551bdcf460" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<p>Eloquent ORM の機能を制限するガードレールとして設定したいという需要がありそうなので、ドキュメントの削除はミスかなと思ったのですが、同じようなことを思った人が既にいたようでプルリクエストが上がっていました。</p>




<a rel="noopener" href="https://github.com/laravel/docs/pull/9260" title="[10.x] Adding Missing Content of `Configuring Eloquent Strictness` by devajmeireles · Pull Request #9260 · laravel/docs" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://opengraph.githubassets.com/3cd3872fcdb93b0fb1368e07f79ef153ceb5f4958e48b2c2d46f2f4156566e30/laravel/docs/pull/9260" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">[10.x] Adding Missing Content of `Configuring Eloquent Strictness` by devajmeireles · Pull Request #9260 · laravel/docs</div><div class="blogcard-snippet external-blogcard-snippet">I used 9.x as a base to include the missing content in 10.x</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/laravel/docs/pull/9260" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<p>コメントに削除した理由についての言及がありました。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>I don&#8217;t really like this feature so I removed it from the docs.</p>
<cite><a href="https://github.com/laravel/docs/pull/9260#issuecomment-1892644460">https://github.com/laravel/docs/pull/9260#issuecomment-1892644460</a></cite></blockquote>



<p>まあ、好みじゃないならしょうがないですよね。。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【Laravel】MacroableでPHPのオーバーロードを学ぶ</title>
		<link>https://wptech.kiichiro.work/5804m1k8bh/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Sat, 11 Mar 2023 15:49:20 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=1442</guid>

					<description><![CDATA[PHPのオーバーロードを説明する上でLaravelのMacroableがちょうど良いと思ったので。 マクロについて Laravel内部の一部のクラスには、独自に定義したメソッドを追加できる「マクロ」という機能が備わってい [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>PHPのオーバーロードを説明する上でLaravelのMacroableがちょうど良いと思ったので。</p>



<h1 class="wp-block-heading">マクロについて</h1>



<p>Laravel内部の一部のクラスには、独自に定義したメソッドを追加できる「マクロ」という機能が備わっています。</p>



<p>マクロを追加したいクラスにMacroableをuse宣言することによって、当該クラスにマクロを登録することが出来ます。<br>例えば <code>Illuminate\Support\Collection</code> はMacroableをuseしているので、以下のようにしてマクロの登録が行えます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="AppServiceProvider.php" data-lang="PHP"><code>public function boot()
{
    Collection::macro(&#39;foo&#39;, function() {
        $this-&gt;map(function($value) {
            // 処理
        });
    });
}</code></pre></div>



<p>よくあるマクロの使いどころとしては、<code>Collection</code> や <code>Response</code> といったFacadesに対して、汎用的に再利用したい処理がある場合などかと思っています。</p>



<p>このマクロはPHPのオーバーロードを利用して実現されています。</p>



<h1 class="wp-block-heading">オーバーロードについて</h1>



<p>PHPにおけるオーバーロードとは、プロパティやメソッドを動的に作成する手法であり、マジックメソッドを利用します。また、オーバーロードメソッドは宣言されていないプロパティやメソッドが呼び出された際に起動します。</p>




<a rel="noopener" href="https://www.php.net/manual/ja/language.oop5.overloading.php" title="PHP: &#12458;&#12540;&#12496;&#12540;&#12525;&#12540;&#12489; - Manual" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://wptech.kiichiro.work/wp-content/uploads/cocoon-resources/blog-card-cache/ae2f27571af4ab50ab3f001c87ae24dd.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">PHP: &#12458;&#12540;&#12496;&#12540;&#12525;&#12540;&#12489; - Manual</div><div class="blogcard-snippet external-blogcard-snippet">オーバーロード</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://www.php.net/manual/ja/language.oop5.overloading.php" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.php.net</div></div></div></div></a>



<p>未宣言のメソッドに関しては <code>__call</code> というマジックメソッドを用意しておくことで起動出来ます (staticは <code>__callStatic</code> )。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>// __callを宣言していないクラス
class Bar {}

$bar = new Bar();
$bar-&gt;bar(); // PHP Fatal error:  Uncaught Error: Call to undefined method Bar::bar()

// __callを宣言したクラス
class Foo {
    public function __call($method, $parameters) {
        echo &#39;Method: &#39; . $method;
    }
}

$foo = new Foo();
$foo-&gt;foo(); // Method: foo</code></pre></div>



<p>また、同名で引数の型や数の異なるメソッドを宣言する(一般的な)オーバーロードとは解釈が異なります。</p>



<h1 class="wp-block-heading">コードリーディング</h1>



<p>実際に以下の流れでコードを見ていきます。</p>



<ol class="wp-block-list">
<li>マクロを登録する</li>



<li>マクロを呼び出す</li>
</ol>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-10" checked><label class="toc-title" for="toc-checkbox-10">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"></li><li><a href="#toc1" tabindex="0">マクロを登録する</a></li><li><a href="#toc2" tabindex="0">マクロを呼び出す</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">マクロを登録する</span></h2>



<p>まずはマクロの登録時の処理を見ていきます。登録時の処理はMacroableに定義されています。</p>



<p>登録時に呼び出すmacroメソッドですが、第一引数の呼び出す際のメソッド名をkeyに、第二引数のクロージャを valueとして <code>$macro</code> というプロパティに詰め込んでいます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate\Support\Traits\Macroable.php" data-lang="PHP"><code>/**
 * The registered string macros.
 *
 * @var array
 */
protected static $macros = [];

/**
 * Register a custom macro.
 *
 * @param  string  $name
 * @param  object|callable  $macro
 * @return void
 */
public static function macro($name, $macro)
{
    static::$macros[$name] = $macro;
}</code></pre></div>



<p>冒頭で例に上げたCollectionクラスは、Macroableをuseしていますので、 <code>Collection::macro</code> を呼び出すことで、<code>$macro</code> にメソッド名 <code>foo</code> とクロージャが登録されることになります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="AppServiceProvider.php" data-lang="PHP"><code>public function boot()
{
    Collection::macro(&#39;foo&#39;, function() {
        $this-&gt;map(function($value) {
            // 処理
        });
    });
}</code></pre></div>



<h2 class="wp-block-heading"><span id="toc2">マクロを呼び出す</span></h2>



<p>登録したマクロはメソッド名を <code>-></code> または <code>::</code> で呼び出すことで利用が出来ます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>// インスタンス化して呼び出す
collect()-&gt;foo();

// staticメソッドとして呼び出す
Collection::foo();</code></pre></div>



<p>このとき、Collectionにfooメソッドは宣言されていないのでオーバーロードされ、該当するマジックメソッドが呼び出されます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate\Support\Traits\Macroable.php" data-lang="PHP"><code>/**
 * Dynamically handle calls to the class.
 *
 * @param  string  $method
 * @param  array  $parameters
 * @return mixed
 *
 * @throws \BadMethodCallException
 */
public static function __callStatic($method, $parameters)
{
    if (! static::hasMacro($method)) {
        throw new BadMethodCallException(sprintf(
            &#39;Method %s::%s does not exist.&#39;, static::class, $method
        ));
    }

    $macro = static::$macros[$method];

    if ($macro instanceof Closure) {
        $macro = $macro-&gt;bindTo(null, static::class);
    }

    return $macro(...$parameters);
}

/**
 * Dynamically handle calls to the class.
 *
 * @param  string  $method
 * @param  array  $parameters
 * @return mixed
 *
 * @throws \BadMethodCallException
 */
public function __call($method, $parameters)
{
    if (! static::hasMacro($method)) {
        throw new BadMethodCallException(sprintf(
            &#39;Method %s::%s does not exist.&#39;, static::class, $method
        ));
    }

    $macro = static::$macros[$method];

    if ($macro instanceof Closure) {
        $macro = $macro-&gt;bindTo($this, static::class);
    }

    return $macro(...$parameters);
}</code></pre></div>



<p><code>__call</code> でも <code>__callStatic</code> でも概ねやっていることは同じで、</p>



<ol class="wp-block-list">
<li><code>hasMacro</code> で呼び出されたメソッド名がマクロとして登録されているか(<code>$macro</code>に該当するメソッド名が存在するか)を検証する
<ul class="wp-block-list">
<li>存在しなければBadMethodCallExceptionをthrowする</li>
</ul>
</li>



<li>クロージャをバインドする
<ul class="wp-block-list">
<li><code>__call</code> : 呼び出されたメソッドを持つオブジェクトにクロージャをバインドする</li>



<li><code>__callStatic</code> : バインドを解除する</li>
</ul>
</li>



<li>当該メソッドを引き渡されたパラメータで実行する</li>
</ol>



<p>という流れになります。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【Laravel】EloquentのsaveではCarbonオブジェクトをStringに変換している</title>
		<link>https://wptech.kiichiro.work/59bqu2qa9d/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Tue, 27 Dec 2022 09:12:16 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=1242</guid>

					<description><![CDATA[目次 はじめにコードリーディングfillsaveおわりに はじめに Eloquentを利用すると、以下の①、②のどちらのパターンでも保存することが出来ます。 ②では、datetime型のカラムに文字列ではなくCarbon [&#8230;]]]></description>
										<content:encoded><![CDATA[

  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-12" checked><label class="toc-title" for="toc-checkbox-12">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">はじめに</a></li><li><a href="#toc2" tabindex="0">コードリーディング</a><ol><li><a href="#toc3" tabindex="0">fill</a></li><li><a href="#toc4" tabindex="0">save</a></li></ol></li><li><a href="#toc5" tabindex="0">おわりに</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">はじめに</span></h2>



<p>Eloquentを利用すると、以下の①、②のどちらのパターンでも保存することが出来ます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>$foo = Foo::getModel();

// ①明示的にStringを渡す
$foo-&gt;fill([
    &#39;datetime&#39; =&gt; Carbon::now()-&gt;toDateTimeString(),
    &#39;date&#39;     =&gt; Carbon::now()-&gt;toDateString(),
])-&gt;save();

// ②Carbonオブジェクトのまま渡す
$foo-&gt;fill([
    &#39;datetime&#39; =&gt; Carbon::now(),
    &#39;date&#39;     =&gt; Carbon::now(),
])-&gt;save();</code></pre></div>



<p>②では、datetime型のカラムに文字列ではなくCarbonオブジェクトを渡していますが、おそらくSQLを組み立てる際にキャストして文字列を組み立てているのだろうな、とは思っていました。</p>



<p>ただ、どちらでもOKというのが腑に落ちなかったので、該当箇所のコードリーディングをしてみます。</p>



<h2 class="wp-block-heading"><span id="toc2">コードリーディング</span></h2>



<h3 class="wp-block-heading"><span id="toc3">fill</span></h3>



<p>fillでは主に、「入力されたattributesがassignableであれば <code>$attributes</code> 配列(property)に詰め込む」という処理を行っています。</p>




<a rel="noopener" href="https://github.com/laravel/framework/blob/9.x/src/Illuminate/Database/Eloquent/Model.php#L499-L547" title="framework/src/Illuminate/Database/Eloquent/Model.php at 9.x · laravel/framework" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fgithub.com%2Flaravel%2Fframework%2Fblob%2F9.x%2Fsrc%2FIlluminate%2FDatabase%2FEloquent%2FModel.php%23L499-L547?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">framework/src/Illuminate/Database/Eloquent/Model.php at 9.x · laravel/framework</div><div class="blogcard-snippet external-blogcard-snippet">The Laravel Framework. Contribute to laravel/framework development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/laravel/framework/blob/9.x/src/Illuminate/Database/Eloquent/Model.php#L499-L547" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<p>実際に詰め込んでいるのは、 <code>HasAttributes</code> traitです。</p>




<a rel="noopener" href="https://github.com/laravel/framework/blob/9.x/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L938-L993" title="framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php at 9.x · laravel/framework" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fgithub.com%2Flaravel%2Fframework%2Fblob%2F9.x%2Fsrc%2FIlluminate%2FDatabase%2FEloquent%2FConcerns%2FHasAttributes.php%23L938-L993?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php at 9.x · laravel/framework</div><div class="blogcard-snippet external-blogcard-snippet">The Laravel Framework. Contribute to laravel/framework development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/laravel/framework/blob/9.x/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L938-L993" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="HasAttributes.php" data-lang="PHP"><code>$this-&gt;attributes[$key] = $value;</code></pre></div>



<h3 class="wp-block-heading"><span id="toc4">save</span></h3>



<p>saveでは、fillで詰め込んだattributesを元にSQLを組み立て実行します。</p>




<a rel="noopener" href="https://github.com/laravel/framework/blob/9.x/src/Illuminate/Database/Eloquent/Model.php#L1097-L1144" title="framework/src/Illuminate/Database/Eloquent/Model.php at 9.x · laravel/framework" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fgithub.com%2Flaravel%2Fframework%2Fblob%2F9.x%2Fsrc%2FIlluminate%2FDatabase%2FEloquent%2FModel.php%23L1097-L1144?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">framework/src/Illuminate/Database/Eloquent/Model.php at 9.x · laravel/framework</div><div class="blogcard-snippet external-blogcard-snippet">The Laravel Framework. Contribute to laravel/framework development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/laravel/framework/blob/9.x/src/Illuminate/Database/Eloquent/Model.php#L1097-L1144" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<p>途中は省略しますが、insert実行までの一連の処理を追っていくと、Connectionクラス の <code>prepareBindings</code> に到達します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Connection.php" data-lang="PHP"><code>    /**
     * Prepare the query bindings for execution.
     *
     * @param  array  $bindings
     * @return array
     */
    public function prepareBindings(array $bindings)
    {
        $grammar = $this-&gt;getQueryGrammar();

        foreach ($bindings as $key =&gt; $value) {
            // We need to transform all instances of DateTimeInterface into the actual
            // date string. Each query grammar maintains its own date string format
            // so we&#39;ll just ask the grammar for the format to get from the date.
            if ($value instanceof DateTimeInterface) {
                $bindings[$key] = $value-&gt;format($grammar-&gt;getDateFormat());
            } elseif (is_bool($value)) {
                $bindings[$key] = (int) $value;
            }
        }

        return $bindings;
    }</code></pre></div>



<p>ここまで引き渡してきたattributeの値が <code>DateTimeInterface</code> の実装である場合、フォーマットされた文字列で詰め込み直しています。Carbonは <code>DateTimeInterface</code> を実装しているので、ここまで来てようやく文字列に変換される、ということでした。</p>



<p>フォーマットは Grammerクラス に定義されている <code>Y-m-d H:i:s</code> になるようです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Grammer.php" data-lang="PHP"><code>    /**
     * Get the format for database stored dates.
     *
     * @return string
     */
    public function getDateFormat()
    {
        return &#39;Y-m-d H:i:s&#39;;
    }</code></pre></div>



<p>ConnectionとGrammerクラスは、DB接続とSQLの構文を抽象化しており、databases.phpで指定しているdriverによって解決されるクラスが変わります。</p>



<h2 class="wp-block-heading"><span id="toc5">おわりに</span></h2>



<p>Carbonオブジェクトを渡した場合、SQLを組み立てる最終段階で文字列に変換されることが確認出来ました。Eloquentを利用する際はどちらを使っても仕様上は問題なさそうですね。</p>



<p>ちなみに created_at と updated_at のように自動的に挿入されるカラムはCarbonオブジェクトをそのまま attributes に突っ込んでいました。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="HasTimestamps.php" data-lang="PHP"><code>    /**
     * Update the creation and update timestamps.
     *
     * @return $this
     */
    public function updateTimestamps()
    {
        $time = $this-&gt;freshTimestamp();

        $updatedAtColumn = $this-&gt;getUpdatedAtColumn();

        if (! is_null($updatedAtColumn) && ! $this-&gt;isDirty($updatedAtColumn)) {
            $this-&gt;setUpdatedAt($time);
        }

        $createdAtColumn = $this-&gt;getCreatedAtColumn();

        if (! $this-&gt;exists && ! is_null($createdAtColumn) && ! $this-&gt;isDirty($createdAtColumn)) {
            $this-&gt;setCreatedAt($time);
        }

        return $this;
    }</code></pre></div>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="HasTimestamps.php" data-lang="PHP"><code>    /**
     * Get a fresh timestamp for the model.
     *
     * @return \Illuminate\Support\Carbon
     */
    public function freshTimestamp()
    {
        return Date::now();
    }</code></pre></div>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【WordPress】「永続オブジェクトキャッシュを使用してください」が気になったのでコアを覗いてみた</title>
		<link>https://wptech.kiichiro.work/966iksg49s/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Wed, 30 Nov 2022 16:50:14 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">http://13.115.157.198/?p=801</guid>

					<description><![CDATA[はじめに WordPress 6.1から、サイトヘルスステータスにて永続オブジェクトキャッシュの使用がレコメンドされるようになったようです。 こちらの対応としては、ホスティングプロバイダーが提供するキャッシュサーバーを確 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h1 class="wp-block-heading">はじめに</h1>



<p>WordPress 6.1から、サイトヘルスステータスにて永続オブジェクトキャッシュの使用がレコメンドされるようになったようです。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://wptech.kiichiro.work/wp-content/uploads/2022/11/d2dd353fa2093e3321c1b6f535d89cfb-1024x306.png" alt="永続オブジェクトキャッシュを使用してくださいという、サイトヘルスステータスの結果が記載されています"/></figure>



<p>こちらの対応としては、ホスティングプロバイダーが提供するキャッシュサーバーを確認し、 W3 Total Cache Plugin などのキャッシュ系プラグインを介してオブジェクトキャッシュを使用する設定を行う、ということになるようです。<br><a rel="noopener" href="https://ja.wordpress.org/support/article/optimization/#persistent-object-cache" target="_blank">https://ja.wordpress.org/support/article/optimization/#persistent-object-cache</a></p>



<p>気になったので、今回はサイトヘルスステータスで何をチェックしているかを見ていきます。</p>



<p>※オブジェクトキャッシュが何をしているか、というところまでは言及しません。</p>



<h1 class="wp-block-heading">コードを読む</h1>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-14" checked><label class="toc-title" for="toc-checkbox-14">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"></li><li><a href="#toc1" tabindex="0">コアコードを読む</a></li><li><a href="#toc2" tabindex="0">プラグインのコードを読む</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">コアコードを読む</span></h2>



<p>サイトヘルスステータスでテストする項目は <code>WP_Site_Health::get_tests</code> で列挙されています。<br>6.1から追加されたのは、 <code>persistent_object_cache</code> というテストです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="class-wp-site-health.php" data-lang="PHP"><code>$tests[&#39;direct&#39;][&#39;persistent_object_cache&#39;] = array(
    &#39;label&#39; =&gt; __( &#39;Persistent object cache&#39; ),
    &#39;test&#39;  =&gt; &#39;persistent_object_cache&#39;,
);</code></pre></div>



<p>ここに列挙されているテストは <code>get_test_</code> というprefixがついたfunctionとして用意されており、サイトヘルスステータスの画面を開くたびに実行されます。<br>今回の場合だと、 <code>get_test_persistent_object_cache</code> というfunctionになります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="class-wp-site-health.php" data-lang="PHP"><code>/**
 * Tests if the site uses persistent object cache and recommends to use it if not.
 *
 * @since 6.1.0
 *
 * @return array The test result.
 */
public function get_test_persistent_object_cache() {
(省略)
    if ( wp_using_ext_object_cache() ) {
        return $result;
    }
(省略)
    return $result;
}</code></pre></div>



<p>どうやらこのfunction内で呼び出している <code>wp_using_ext_object_cache</code> というfunctionでオブジェクトキャッシュの判定を行っているようです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="load.php" data-lang="PHP"><code>/**
 * Toggle `$_wp_using_ext_object_cache` on and off without directly
 * touching global.
 *
 * @since 3.7.0
 *
 * @global bool $_wp_using_ext_object_cache
 *
 * @param bool $using Whether external object cache is being used.
 * @return bool The current &#39;using&#39; setting.
 */
function wp_using_ext_object_cache( $using = null ) {
	global $_wp_using_ext_object_cache;
	$current_using = $_wp_using_ext_object_cache;
	if ( null !== $using ) {
		$_wp_using_ext_object_cache = $using;
	}
	return $current_using;
}</code></pre></div>



<p>ここでは、globalの <code>$_wp_using_ext_object_cache</code> が初期化されているかどうかを判定しています。<br>初期化処理は <code>load.php</code> の <code>wp_start_object_cache</code> というfunction内で行っています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="load.php" data-lang="PHP"><code>/**
 * Start the WordPress object cache.
 *
 * If an object-cache.php file exists in the wp-content directory,
 * it uses that drop-in as an external object cache.
 *
 * @since 3.0.0
 * @access private
 *
 * @global array $wp_filter Stores all of the filters.
 */
function wp_start_object_cache() {
	global $wp_filter;
	static $first_init = true;

	// Only perform the following checks once.

	/**
	 * Filters whether to enable loading of the object-cache.php drop-in.
	 *
	 * This filter runs before it can be used by plugins. It is designed for non-web
	 * runtimes. If false is returned, object-cache.php will never be loaded.
	 *
	 * @since 5.8.0
	 *
	 * @param bool $enable_object_cache Whether to enable loading object-cache.php (if present).
	 *                                  Default true.
	 */
	if ( $first_init && apply_filters( &#39;enable_loading_object_cache_dropin&#39;, true ) ) {
		if ( ! function_exists( &#39;wp_cache_init&#39; ) ) {
			/*
			 * This is the normal situation. First-run of this function. No
			 * caching backend has been loaded.
			 *
			 * We try to load a custom caching backend, and then, if it
			 * results in a wp_cache_init() function existing, we note
			 * that an external object cache is being used.
			 */
			if ( file_exists( WP_CONTENT_DIR . &#39;/object-cache.php&#39; ) ) {
				require_once WP_CONTENT_DIR . &#39;/object-cache.php&#39;;
				if ( function_exists( &#39;wp_cache_init&#39; ) ) {
					wp_using_ext_object_cache( true );
				}
(省略)
}</code></pre></div>



<p><code>wp_cache_init</code> は <code>cache.php</code> 内で定義されていますが、この時点では読み込まれていません。<br>wp-content 配下に <code>object-cache.php</code> が存在する場合は読み込む、(その中で) <code>wp_cache_init</code> が定義されていれば、<code>wp_using_ext_object_cache</code> にtrueを渡し、<code>$_wp_using_ext_object_cache</code> をtrueで初期化する、という流れになっています。</p>



<h2 class="wp-block-heading"><span id="toc2">プラグインのコードを読む</span></h2>



<p>ということで、この <code>object-cache.php</code> が肝であることがわかったのですが、このファイルはコアコードには存在しません。<br>ではどこに存在するかというと、オブジェクトキャッシュを提供する各プラグイン内のようです。</p>



<p>以下は W3 Total Cache Plugin を有効化すると生成される <code>object-cache.php</code> です。<br>この中で <code>wp_cache_init</code> が定義されています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="object-cache.php" data-lang="PHP"><code>/**
 * Init cache
 *
 * @return void
 */
function wp_cache_init() {
    $GLOBALS[&#39;wp_object_cache&#39;] =
        \W3TC\Dispatcher::component( &#39;ObjectCache_WpObjectCache&#39; );
}</code></pre></div>



<p>リファレンスにもある通り、、オブジェクトキャッシュ提供するプラグインをインストールしない限りオブジェクトキャッシュは非永続であるとのことだそうです。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>By default, the object cache is non-persistent. This means that data stored in the cache resides in memory only and only for the duration of the request. Cached data will not be stored persistently across page loads unless you install a&nbsp;<a href="https://developer.wordpress.org/reference/classes/wp_object_cache/#persistent-caching">persistent caching</a>&nbsp;plugin.</p>
<cite>https://developer.wordpress.org/reference/classes/wp_object_cache/</cite></blockquote>



<h1 class="wp-block-heading">おわりに</h1>



<p>サイトヘルスステータスでは、オブジェクトキャッシュのプラグインを利用しているかどうかをチェックして永続オブジェクトキャッシュの利用を判定していました。<br>そもそも永続オブジェクトキャッシュはWordPress標準では提供しておらず、利用したければプラグインをインストールすること、そしてそれを推奨しているようです(ver 6.1以降)。<br>言い換えると、オブジェクトキャッシュ自体は抽象化されおり、各ホスティングプロバイダーの都合によって独自実装する余地を残しているとも言えるかと思います。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【Laravel】orderedUuidはversion4っぽいorderedなUUIDを生成している</title>
		<link>https://wptech.kiichiro.work/815pp4qdsi/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Tue, 23 Aug 2022 16:20:41 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">http://13.115.157.198/?p=364</guid>

					<description><![CDATA[Str::orderedUuid()は先頭の48ビットでタイムスタンプで表しているため、順序が保証されるらしい。 はじめに Laravelのmigrationsでテーブルを作成する際は、 $table-&#62;id()  [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Str::orderedUuid()は先頭の48ビットでタイムスタンプで表しているため、順序が保証されるらしい。</p>



<h1 class="wp-block-heading">はじめに</h1>



<p>Laravelのmigrationsでテーブルを作成する際は、 <code>$table-&gt;id()</code> としてidカラムがPKになることが多いかと思いですが、AUTO_INCREMENTだと都合が悪い場合もあります。その代替としては、GUIDをPKとする方法が一般的かと思われます。</p>



<p>ただし、アプリケーションでランダムに作成されたGUIDはそれ単体では連続性が保証されないので、<br>INSERTのオーバーヘッドが増加する可能性があります。</p>



<p>Laravelでは <code>Str::orderedUuid()</code> というメソッドでtime-orderedなUUID(version 4)を生成することが出来ます。<br>これを利用してorderedなUUIDをPKにすることで、INSERT時のデメリットを解消出来るということなのですが、</p>



<ul class="wp-block-list">
<li>そもそもUUIDなのにtime-orderedってどういうことなのか？</li>



<li>どのようにtime-orderedを実現しているのか？</li>
</ul>



<p>というところが気になったのでソースを深ぼって見ました。</p>



<h1 class="wp-block-heading">Str::orderedUuid</h1>



<p>とりあえず <code>Str:orderedUuid</code> の中身を見てみる。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Support/Str.php" data-lang="PHP"><code>/**
 * Generate a time-ordered UUID (version 4).
 *
 * @return \Ramsey\Uuid\UuidInterface
 */
public static function orderedUuid()
{
    if (static::$uuidFactory) {
        return call_user_func(static::$uuidFactory);
    }

    $factory = new UuidFactory;

    $factory-&gt;setRandomGenerator(new CombGenerator(
        $factory-&gt;getRandomGenerator(),
        $factory-&gt;getNumberConverter()
    ));

    $factory-&gt;setCodec(new TimestampFirstCombCodec(
        $factory-&gt;getUuidBuilder()
    ));

    return $factory-&gt;uuid4();
}</code></pre></div>



<p>RandomGeneratorにCombGeneratorを、CodecにTimestampFirstCombCodecを指定し、<code>UuidFactory::uuid4</code> を呼び出しています。RandomGeneratorとCodecがどのように使われているかは後述するとして、<code>UuidFactory::uuid4</code> の中身を見てみます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Ramsey/Uuid/UuidFactory.php" data-lang="PHP"><code>public function uuid4(): UuidInterface
{
    $bytes = $this-&gt;randomGenerator-&gt;generate(16);

    return $this-&gt;uuidFromBytesAndVersion($bytes, 4);
}</code></pre></div>



<p>ここで先程指定したRandomGeneratorのgenerateを呼び出しています。そこで生成されたバイト文字列とバージョンを示す4を引数として <code>uuidFromBytesAndVersion</code> を呼び出しています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Ramsey/Uuid/UuidFactory.php" data-lang="PHP"><code>/**
 * Returns an RFC 4122 variant Uuid, created from the provided bytes and version
 *
 * @param string $bytes The byte string to convert to a UUID
 * @param int $version The RFC 4122 version to apply to the UUID
 *
 * @return UuidInterface An instance of UuidInterface, created from the
 *     byte string and version
 *
 * @psalm-pure
 */
private function uuidFromBytesAndVersion(string $bytes, int $version): UuidInterface
{
    /** @var array $unpackedTime */
    $unpackedTime = unpack(&#39;n*&#39;, substr($bytes, 6, 2));
    $timeHi = (int) $unpackedTime[1];
    $timeHiAndVersion = pack(&#39;n*&#39;, BinaryUtils::applyVersion($timeHi, $version));

    /** @var array $unpackedClockSeq */
    $unpackedClockSeq = unpack(&#39;n*&#39;, substr($bytes, 8, 2));
    $clockSeqHi = (int) $unpackedClockSeq[1];
    $clockSeqHiAndReserved = pack(&#39;n*&#39;, BinaryUtils::applyVariant($clockSeqHi));

    $bytes = substr_replace($bytes, $timeHiAndVersion, 6, 2);
    $bytes = substr_replace($bytes, $clockSeqHiAndReserved, 8, 2);

    if ($this-&gt;isDefaultFeatureSet) {
        return LazyUuidFromString::fromBytes($bytes);
    }

    return $this-&gt;uuid($bytes);
}</code></pre></div>



<p><code>uuidFromBytesAndVersion</code> では引き渡されたバイト文字列のうち、versionとvariantを示す桁を置換しています。最後にそのバイト文字列からUuidクラスをnewして返却しています。</p>



<p>要するに <code>Str:orderedUuid</code> では、「RandomGeneratorで生成されたバイト文字列からUuidを生成している」ということのようです。</p>



<h1 class="wp-block-heading">CombGenerator</h1>



<p>CombGeneratorは <strong>COMB</strong> と呼ばれる連続性を持ったGUIDを生成します。<br>当該クラスのPhpDocに生成方法についての記述があります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>/**
 * CombGenerator generates COMBs (combined UUID/timestamp)
 *
 * The CombGenerator, when used with the StringCodec (and, by proxy, the
 * TimestampLastCombCodec) or the TimestampFirstCombCodec, combines the current
 * timestamp with a UUID (hence the name &quot;COMB&quot;). The timestamp either appears
 * as the first or last 48 bits of the COMB, depending on the codec used.
 *
 * By default, COMBs will have the timestamp set as the last 48 bits of the
 * identifier.
 * (略)
 */</code></pre></div>



<p>DeepL翻訳にそのまま突っ込んだのが以下。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><em>CombGenerator は StringCodec (そして、その代理として<br>TimestampLastCombCodec) または TimestampFirstCombCodec と共に使われる場合、CombGenerator は現在のタイムスタンプをUUIDと結合します。<br>タイムスタンプとUUIDを組み合わせます（&#8221;COMB &#8220;という名前に由来します）。タイムスタンプは<br>タイムスタンプは、使用するコーデックに応じて、COMBの最初または最後の48ビットとして表示されます。<br>デフォルトでは、COMBはタイムスタンプを識別子の最後の48ビットとして設定されます。</em></p>
</blockquote>



<p>CombGeneratorがやっていることは、COMBと呼ばれるUUIDとtimestampを組み合わせた (combined) バイト文字列を生成するところまでで、timestampの48ビットをどこに割り当てるかはCodecによりけり、ということが窺えます。以下はその処理を司るメソッドです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Ramsey/Uuid/Generator/CombGenerator.php" data-lang="PHP"><code>/**
 * @throws InvalidArgumentException if $length is not a positive integer
 *     greater than or equal to CombGenerator::TIMESTAMP_BYTES
 *
 * @inheritDoc
 */
public function generate(int $length): string
{
    if ($length &lt; self::TIMESTAMP_BYTES) {
        throw new InvalidArgumentException(
            &#39;Length must be a positive integer greater than or equal to &#39; . self::TIMESTAMP_BYTES
        );
    }

    $hash = &#39;&#39;;
    if (self::TIMESTAMP_BYTES &gt; 0 && $length &gt; self::TIMESTAMP_BYTES) {
        $hash = $this-&gt;randomGenerator-&gt;generate($length - self::TIMESTAMP_BYTES);
    }

    $lsbTime = str_pad(
        $this-&gt;converter-&gt;toHex($this-&gt;timestamp()),
        self::TIMESTAMP_BYTES * 2,
        &#39;0&#39;,
        STR_PAD_LEFT
    );

    return (string) hex2bin(
        str_pad(
            bin2hex($hash),
            $length - self::TIMESTAMP_BYTES,
            &#39;0&#39;
        )
        . $lsbTime
    );
}</code></pre></div>



<p>大まかには、以下の流れで処理されていきます。</p>



<ol class="wp-block-list">
<li>生成する文字列からTIMESTAMP_BYTESの桁数を除いた桁数の文字列を生成する</li>



<li>現在のtimestampを16進表記に置き換えた文字列を生成する</li>



<li>1の文字列の末尾に2を結合する</li>
</ol>



<h1 class="wp-block-heading">TimestampFirstCombCodec</h1>



<p>生成したUuidクラスを文字列に変換する際にCodecによるencodeが呼び出されます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Ramsey/Uuid/Uuid.php" data-lang="PHP"><code>/**
 * @psalm-return non-empty-string
 */
public function toString(): string
{
    return $this-&gt;codec-&gt;encode($this);
}</code></pre></div>



<p>ここに来てようやく TimestampFirstCombCodec が使われるのです。</p>



<p>encodeの処理自体は比較的シンプルで、</p>



<ol class="wp-block-list">
<li>CombGeneratorで生成したバイト文字列の末尾から48ビット(timestamp)と先頭の48ビットを入れ替える</li>



<li>UUIDのフォーマットに合わせて桁を区切り、 &#8220;-&#8221; でつなげる</li>
</ol>



<p>というものになっています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Ramsey/Uuid/Codec/TimestampFirstCombCodec.php" data-lang="PHP"><code>/**
 * @psalm-return non-empty-string
 * @psalm-suppress MoreSpecificReturnType we know that the retrieved `string` is never empty
 * @psalm-suppress LessSpecificReturnStatement we know that the retrieved `string` is never empty
 */
public function encode(UuidInterface $uuid): string
{
    $bytes = $this-&gt;swapBytes($uuid-&gt;getFields()-&gt;getBytes());

    return sprintf(
        &#39;%08s-%04s-%04s-%04s-%012s&#39;,
        bin2hex(substr($bytes, 0, 4)),
        bin2hex(substr($bytes, 4, 2)),
        bin2hex(substr($bytes, 6, 2)),
        bin2hex(substr($bytes, 8, 2)),
        bin2hex(substr($bytes, 10))
    );
}

/**
 * Swaps bytes according to the timestamp-first COMB rules
 */
private function swapBytes(string $bytes): string
{
    $first48Bits = substr($bytes, 0, 6);
    $last48Bits = substr($bytes, -6);

    $bytes = substr_replace($bytes, $last48Bits, 0, 6);
    $bytes = substr_replace($bytes, $first48Bits, -6);

    return $bytes;
}</code></pre></div>



<h1 class="wp-block-heading">結論</h1>



<p><code>Str::orderedUuid</code> が生成する文字列は、timestampとUUIDを組み合わせて得られる連続性が保証されたUUIDである、といえます。</p>



<p>冒頭の問には以下の通りの回答となるかと思います。</p>



<ul class="wp-block-list">
<li>そもそもUUIDなのにtime-orderedってどういうことなのか？<br>→ versionを示す桁は4になるが、UUID version 4と似て非なるもの</li>



<li>どのようにtime-orderedを実現しているのか？<br>→ 先頭48ビットでタイムスタンプを表してtime-orderedを実現している</li>
</ul>



<h1 class="wp-block-heading">おわりに</h1>



<p>正直なところ、<code>Str::orderedUuid</code> については「ソート出来るUUID」ぐらいの認識しかなく、あまり疑問を持たずに使ってしまっていました。。<br>「良くわからないものはちゃんと調べてから使う」というのは当たり前のようで案外出来ないものですね。</p>



<h1 class="wp-block-heading">参考資料</h1>



<p><strong>Laravel: The mysterious “Ordered UUID”</strong><br><a href="https://itnext.io/laravel-the-mysterious-ordered-uuid-29e7500b4f8">https://itnext.io/laravel-the-mysterious-ordered-uuid-29e7500b4f8</a></p>



<p><strong>The Cost of GUIDs as Primary Keys</strong><br><a href="https://www.informit.com/articles/printerfriendly/25862">https://www.informit.com/articles/printerfriendly/25862</a></p>



<p><strong>RFC 4122: A Universally Unique IDentifier (UUID) URN Namespace</strong><br><a href="https://www.rfc-editor.org/rfc/rfc4122.html#section-4.1.1">https://www.rfc-editor.org/rfc/rfc4122.html#section-4.1</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【WordPress】WPScanによるxmlrpcの検証方法</title>
		<link>https://wptech.kiichiro.work/4yeiux7wqq/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Thu, 07 Jan 2021 15:05:12 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">http://13.115.157.198/?p=149</guid>

					<description><![CDATA[WordPressではXML-RPCの機能がデフォルトでは有効になっています。最新のWordPressのバージョンでは、XML-RPCに取って代わるREST APIが機能として存在するため、用途はほぼ皆無であると思います [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>WordPressではXML-RPCの機能がデフォルトでは有効になっています。<br>最新のWordPressのバージョンでは、XML-RPCに取って代わるREST APIが機能として存在するため、用途はほぼ皆無であると思います。</p>



<p>「WordPress xmlrpc」とググると、ブルートフォースアタックなどの脆弱性の温床になりかねないということで、無効化する方法が出てきます。</p>



<p>公式プラグインの WPScan ではXML-RPCが有効であるかどうかの検証をしていますが、その検証の仕方が予期しないもので、対応がなかなかうまく行かなかった話を紹介します。</p>



<span id="more-149"></span>



<p>結論から言うと、Webサーバーの設定でxmlrpc.phpへのアクセスを拒否する方法が簡単です。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-18" checked><label class="toc-title" for="toc-checkbox-18">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">WPScanとは</a></li><li><a href="#toc2" tabindex="0">XML-RPC機能の無効化</a><ol><li><a href="#toc3" tabindex="0">Webサーバーでアクセスを拒否する</a></li><li><a href="#toc4" tabindex="0">hookでXML-RPCの有効フラグを無効に変更する</a></li></ol></li><li><a href="#toc5" tabindex="0">WPScanによるXML-RPCの検証方法</a><ol><li><a href="#toc6" tabindex="0">XML-RPCのエンドポイントが存在することを確認する</a></li><li><a href="#toc7" tabindex="0">ログインを試みる</a></li><li><a href="#toc8" tabindex="0">デモのメソッドを呼び出す</a></li></ol></li><li><a href="#toc9" tabindex="0">デモのメソッドを削除する</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">WPScanとは</span></h2>



<p><a rel="noopener" href="https://ja.wordpress.org/plugins/wpscan/" target="_blank">WPScan</a>はプラグインやテーマ、WordPressのコアの脆弱性を検出するプラグインです。<br>コード以外にも重要なファイルがpublicになっていないか、XML-RPCが有効になっていないかを検証してくれます。</p>



<p>スキャンをするためには、WPScanのAPIキーが必要となります。<br>プランによって一日にできるスキャンの回数が変わりますが、無料版でも十分使えます。</p>



<h2 class="wp-block-heading"><span id="toc2">XML-RPC機能の無効化</span></h2>



<p>冒頭で説明した通り、WordPressのXML-RPC機能は用途が無い上に脆弱性をはらんでいるので無効化することに越したことは無いです。</p>



<p>実際に無効化する良くある方法としては、</p>



<ol class="wp-block-list">
<li>Webサーバーでアクセスを拒否する</li>



<li>hookでXML-RPCの有効フラグを無効に変更する</li>
</ol>



<p>という2パターンが主流かと思います。</p>



<h3 class="wp-block-heading"><span id="toc3">Webサーバーでアクセスを拒否する</span></h3>



<p>nginxの場合は以下のような記述を追加するだけでOKです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="nginx.conf"><code>location = /xmlrpc.php {
      deny all;
}</code></pre></div>



<h3 class="wp-block-heading"><span id="toc4">hookでXML-RPCの有効フラグを無効に変更する</span></h3>



<p>XML-RPCの有効フラグを上書きできるfilterが用意されています。</p>



<p>プラグインに以下の記述を追加してあげてください。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>add_filter( &#39;xmlrpc_enabled&#39;, &#39;__return_false&#39; );</code></pre></div>



<h2 class="wp-block-heading"><span id="toc5">WPScanによるXML-RPCの検証方法</span></h2>



<p>WPScanではXML-RPCが有効かどうかを3段階で検証しています。</p>



<p>ここからはWPScanの中身を見ていきます。</p>



<h3 class="wp-block-heading"><span id="toc6">XML-RPCのエンドポイントが存在することを確認する</span></h3>



<p>まず、XML-RPCのエンドポイントにGETリクエストをしてエンドポイントが存在しているかどうかを確認しています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="check.php" data-lang="PHP"><code>$url             = get_site_url() . &#39;/xmlrpc.php&#39;;

// First check if the xmlrpc.php file returns a 405 code.
$is_available      = wp_remote_get( $url, array( &#39;timeout&#39; =&gt; 5 ) );
$is_available_code = wp_remote_retrieve_response_code( $is_available );

if ( 405 !== $is_available_code ) return;</code></pre></div>



<p>レスポンスコードが405以外だった場合は無効化されていると判断して、そのまま返却されます。</p>



<p>Webサーバーでアクセス拒否にした場合は、この時点で無効になっていると判定されます。<br>hookで上書きの場合は、GETアクセスに対しては何も手当をしていないので次の処理に進みます。</p>



<h3 class="wp-block-heading"><span id="toc7">ログインを試みる</span></h3>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="check.php" data-lang="PHP"><code>// Try an authenticated request.
$authenticated_body     = &#39;&lt;?xml version=&quot;1.0&quot; encoding=&quot;iso-8859-1&quot;?&gt;&lt;methodCall&gt;&lt;methodName&gt;wp.getUsers&lt;/methodName&gt;&lt;params&gt;&lt;param&gt;&lt;value&gt;1&lt;/value&gt;&lt;/param&gt;&lt;param&gt;&lt;value&gt;username&lt;/value&gt;&lt;/param&gt;&lt;param&gt;&lt;value&gt;password&lt;/value&gt;&lt;/param&gt;&lt;/params&gt;&lt;/methodCall&gt;&#39;;
$authenticated_response = wp_remote_post( $url, array( &#39;body&#39; =&gt; $authenticated_body ) );</code></pre></div>



<p>次に、<code>wp.getUsers</code> というメソッドの呼び出しをし、ログインを試します。<br>ここでようやく <code>xmlrpc_enabled</code> を通るので無効化されていると判定されてくれれば良いのですが。。</p>



<h3 class="wp-block-heading"><span id="toc8">デモのメソッドを呼び出す</span></h3>



<p>WordPressのXML-RPCにはデモ用のメソッドが2つ用意されています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="class-wp-xmlrpc-server.php" data-lang="PHP"><code>&#39;demo.sayHello&#39;                    =&gt; &#39;this:sayHello&#39;,
&#39;demo.addTwoNumbers&#39;               =&gt; &#39;this:addTwoNumbers&#39;,</code></pre></div>



<p>WPScanでは、最後にデモ用のメソッドを呼び出して確認しています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="check.php"><code>// Try an unauthenticated request.
$unauthenticated_body     = &#39;&lt;?xml version=&quot;1.0&quot; encoding=&quot;iso-8859-1&quot;?&gt;&lt;methodCall&gt;&lt;methodName&gt;demo.sayHello&lt;/methodName&gt;&lt;params&gt;&lt;param&gt;&lt;/param&gt;&lt;/params&gt;&lt;/methodCall&gt;&#39;;
$unauthenticated_response = wp_remote_post( $url, array( &#39;body&#39; =&gt; $unauthenticated_body ) );

if ( preg_match( &#39;/&lt;string&gt;Hello!&lt;\/string&gt;/&#39;, $unauthenticated_response[&#39;body&#39;] ) ) {
			$this-&gt;add_vulnerability( __( &#39;The XML-RPC interface is partly disabled, but still allows unauthenticated requests.&#39;, &#39;wpscan&#39; ), &#39;low&#39;, sanitize_title( $url ) );
}</code></pre></div>



<p>デモ用のメソッドはXML-RPCが有効か無効かに関わらず、レスポンスが返ってきます。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://wptech.kiichiro.work/wp-content/uploads/2021/01/image-8-1024x590.webp" alt="postman result"/><figcaption class="wp-element-caption">フラグが無効になっているにも関わらず。。</figcaption></figure>



<p>このままだとWPScanはXML-RPCが一部有効と判定してしまって許してくれないようです。。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://wptech.kiichiro.work/wp-content/uploads/2021/01/image-9-1024x110.webp" alt="xml-rpc enabled"/></figure>



<p>XML-RPCを無効化する本来の目的はブルートフォースアタックなどの攻撃を回避するためであって、WPScanに許してもらうわけではないので、hookでやるにしても<code>xmlrpc_enabled</code> だけで足りているとは思うのですが、なんだか気持ち悪いので解消方法を考えてみました。</p>



<h2 class="wp-block-heading"><span id="toc9">デモのメソッドを削除する</span></h2>



<p>デモのメソッドは <code>wp_xmlrpc_server</code> クラスの <code>$methods</code> に格納されています。</p>



<p>これを上書きする<code>xmlrpc_methods</code> というfilterがあるのでそれを利用します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>add_filter( &#39;xmlrpc_methods&#39;, &#39;remove_demo_method&#39;, 10 );
public function remove_demo_method( $methods ) {
	unset( $methods[&#39;demo.sayHello&#39;] );
	return $methods;
}</code></pre></div>



<p>再度、リクエストを投げてみます。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://wptech.kiichiro.work/wp-content/uploads/2021/01/image-10-1024x40.webp" alt="xml-rpc disabled"/></figure>



<p>該当のメソッドが存在しないことが確認できました。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【WordPress】WordPress 5.6以降から実装されるアプリケーションパスワード</title>
		<link>https://wptech.kiichiro.work/post-13/</link>
					<comments>https://wptech.kiichiro.work/post-13/#comments</comments>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Fri, 30 Oct 2020 14:42:41 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">http://13.115.157.198/?p=13</guid>

					<description><![CDATA[WordPressのREST APIは、WordPressに保存された投稿データ等をシステム間で連携するときのインタフェースとして利用する場面が多いです。これまでは、AWS WAFで対向システムのIPのみ許可するとか独自 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>WordPressのREST APIは、WordPressに保存された投稿データ等をシステム間で連携するときのインタフェースとして利用する場面が多いです。<br>これまでは、AWS WAFで対向システムのIPのみ許可するとか独自でOAuthを用意する等、認証周りについて頭を悩ませることが多かったのですが、アプリケーションパスワードが実装されることによって悩みが一つ減る感じになるのかなという感覚です。</p>



<p><a href="https://ja.wordpress.org/2020/10/23/wordpress-5-6-beta-1/">5.6ベータのリリースノート</a>によると、アプリケーションパスワードを試すことができるようになってるみたいなので、今回はコアの中身がどう変わっていてどのように利用できるかを調査してみようと思います。</p>



<p>※「アプリケーションパスワードが何なのか？」「今までの認証方式は？」という話は他所ですでに出てるので、ここでは言及しないつもりです。</p>



<span id="more-13"></span>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-20" checked><label class="toc-title" for="toc-checkbox-20">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">コードの差分</a></li><li><a href="#toc2" tabindex="0">使い方</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">コードの差分</span></h2>



<p>default-filters.phpにユーザー情報をチェックするhookが追加されてます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>add_filter( &#39;determine_current_user&#39;, &#39;wp_validate_application_password&#39;, 20 );</code></pre></div>



<p>callbackでは、ヘッダに <code>PHP_AUTH_USER </code>が設定されているときのみ整合性をチェックしているようです。<br>wp-json配下に別途Webサーバー側でBasic認証をかけている場合は微妙な動きになりそうではあります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>/**
 * Validates the application password credentials passed via Basic Authentication.
 *
 * @since 5.6.0
 *
 * @param int|bool $input_user User ID if one has been determined, false otherwise.
 * @return int|bool The authenticated user ID if successful, false otherwise.
 */
function wp_validate_application_password( $input_user ) {
	// Don&#39;t authenticate twice.
	if ( ! empty( $input_user ) ) {
		return $input_user;
	}

	if ( ! wp_is_application_passwords_available() ) {
		return $input_user;
	}

	// Check that we&#39;re trying to authenticate
	if ( ! isset( $_SERVER[&#39;PHP_AUTH_USER&#39;] ) ) {
		return $input_user;
	}

	$authenticated = wp_authenticate_application_password( null, $_SERVER[&#39;PHP_AUTH_USER&#39;], $_SERVER[&#39;PHP_AUTH_PW&#39;] );

	if ( $authenticated instanceof WP_User ) {
		return $authenticated-&gt;ID;
	}

	// If it wasn&#39;t a user what got returned, just pass on what we had received originally.
	return $input_user;
}</code></pre></div>



<p>user-edit.phpには、アプリケーションパスワードを設定するフォームが追加されています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>		&lt;?php if ( wp_is_application_passwords_available_for_user( $user_id ) ) : ?&gt;
	&lt;div class=&quot;application-passwords hide-if-no-js&quot; id=&quot;application-passwords-section&quot;&gt;
		&lt;h2&gt;&lt;?php _e( &#39;Application Passwords&#39; ); ?&gt;&lt;/h2&gt;
		&lt;p&gt;&lt;?php _e( &#39;Application passwords allow authentication via non-interactive systems, such as XML-RPC or the REST API, without providing your actual password. Application passwords can be easily revoked. They cannot be used for traditional logins to your website.&#39; ); ?&gt;&lt;/p&gt;
		&lt;div class=&quot;create-application-password&quot;&gt;
			&lt;label for=&quot;new_application_password_name&quot; class=&quot;screen-reader-text&quot;&gt;&lt;?php _e( &#39;New Application Password Name&#39; ); ?&gt;&lt;/label&gt;
			&lt;input type=&quot;text&quot; size=&quot;30&quot; id=&quot;new_application_password_name&quot; name=&quot;new_application_password_name&quot; placeholder=&quot;&lt;?php esc_attr_e( &#39;New Application Password Name&#39; ); ?&gt;&quot; class=&quot;input&quot; /&gt;

			&lt;?php
			/**
			 * Fires in the create Application Passwords form.
			 *
			 * @since 5.6.0
			 *
			 * @param WP_User $profileuser The current WP_User object.
			 */
			do_action( &#39;wp_create_application_password_form&#39;, $profileuser );
			?&gt;

			&lt;?php submit_button( __( &#39;Add New&#39; ), &#39;secondary&#39;, &#39;do_new_application_password&#39;, false ); ?&gt;
		&lt;/div&gt;

		&lt;div class=&quot;application-passwords-list-table-wrapper&quot;&gt;
			&lt;?php
			$application_passwords_list_table = _get_list_table( &#39;WP_Application_Passwords_List_Table&#39;, array( &#39;screen&#39; =&gt; &#39;application-passwords-user&#39; ) );
			$application_passwords_list_table-&gt;prepare_items();
			$application_passwords_list_table-&gt;display();
			?&gt;
		&lt;/div&gt;
	&lt;/div&gt;</code></pre></div>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" src="https://wptech.kiichiro.work/wp-content/uploads/2020/10/image-4.webp" alt="アプリケーションパスワードの設定画面" class="wp-image-968" width="768" height="125" srcset="https://wptech.kiichiro.work/wp-content/uploads/2020/10/image-4.webp 1024w, https://wptech.kiichiro.work/wp-content/uploads/2020/10/image-4-300x49.webp 300w, https://wptech.kiichiro.work/wp-content/uploads/2020/10/image-4-768x125.webp 768w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<h2 class="wp-block-heading"><span id="toc2">使い方</span></h2>



<p>実際に使ってみます。<br>前提としてSSL化されている状態であることとなっているので、localhostでやる場合は、<br><code>WP_ENVIRONMENT_TYPE</code> にlocalを指定するか、アプリケーションパスワードを有効化する必要があります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>define(&#39;WP_ENVIRONMENT_TYPE&#39;, &#39;local&#39;);

または

add_filter(&#39;wp_is_application_passwords_available&#39;, &#39;__return_true&#39;);</code></pre></div>



<p><br>適当なアプリケーション名を入力してAdd Newすればパスワードを生成してくれるのでそれを使います。</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" src="https://wptech.kiichiro.work/wp-content/uploads/2020/10/image-5.webp" alt="アプリケーションパスワード一覧画面" class="wp-image-969" width="768" height="265" srcset="https://wptech.kiichiro.work/wp-content/uploads/2020/10/image-5.webp 1024w, https://wptech.kiichiro.work/wp-content/uploads/2020/10/image-5-300x103.webp 300w, https://wptech.kiichiro.work/wp-content/uploads/2020/10/image-5-768x265.webp 768w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>この状態でcurlを叩いてみます。</p>



<p>ユーザー名と生成したパスワードの組み合わせが正しければ200が返却されます。<br>誤っていれば401が返却され、アプリケーションパスワードでの認証が効いていることが確認できます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>// 正しい場合
$ curl -Iu USER:PASS http://localhost/wp-json/wp/v2/posts
HTTP/1.1 200 OK

// 誤っている場合
$ curl -Iu USER:PASS http://localhost/wp-json/wp/v2/posts
HTTP/1.1 401 Unauthorized</code></pre></div>



<p>ちなみにユーザーとパスワードがヘッダに存在しない場合も200が返却されます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>$ curl -I http://localhost/wp-json/wp/v2/posts
HTTP/1.1 200 OK</code></pre></div>



<p>これの正しい回避方法がいまいちわかってませんが、<code>PHP_AUTH_USER</code> がない場合は空を代入してあげるくらいでしょうか。<br></p>
]]></content:encoded>
					
					<wfw:commentRss>https://wptech.kiichiro.work/post-13/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
