<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PHP 8 &#8211; Larry的午茶時光</title>
	<atom:link href="https://blog.yuyansoftware.com.tw/tag/php-8/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.yuyansoftware.com.tw</link>
	<description></description>
	<lastBuildDate>Sun, 17 Mar 2024 09:19:24 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://blog.yuyansoftware.com.tw/wp-content/uploads/2022/10/favicon-45x45.png</url>
	<title>PHP 8 &#8211; Larry的午茶時光</title>
	<link>https://blog.yuyansoftware.com.tw</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Laravel 官方網站關於第三方 API 串接的教程</title>
		<link>https://blog.yuyansoftware.com.tw/2022/08/integrate-third-party-api-in-laravel/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Sun, 21 Aug 2022 10:49:06 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Link7]]></category>
		<category><![CDATA[PHP 8]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=11421</guid>

					<description><![CDATA[前陣子看到 Laravel 官方部落格有一篇關於 API 串接，很好的文章。larry 覺得這篇文章很好是在於，除了 API 串接的架構我們可以參考，另外關於 PHP 的寫法 (包含 PHP 8 的新語法)，還有一些軟體工程的概念，都很值得我們參考。]]></description>
										<content:encoded><![CDATA[
<p>前陣子看到 Laravel 官方部落格有一篇關於 API 串接，很好的文章<br><a rel="noreferrer noopener" href="https://laravel-news.com/working-with-data-in-api-integrations" target="_blank">https://laravel-news.com/working-with-data-in-api-integrations</a></p>



<p>larry 覺得這篇文章很好是在於，除了 API 串接的架構我們可以參考，另外關於 PHP 的寫法 (包含 PHP 8 的新語法)，還有一些軟體工程的概念，都很值得我們參考。</p>



<p>這篇教程是以範例的方式來進行。首先，新增一個 service<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/MedicalTrust/MedicalTrustService.php</mark></code></p>



<p>Laravel service container 的觀念，要做 service container binding</p>



<pre class="wp-block-code"><code>// app/Providers/AppServiceProvider.php

public function boot()
{
    $this-&gt;app-&gt;singleton(MedicalTrustService::class,
        fn () =&gt; new MedicalTrustService(
	    baseUrl: ‘......’,
            apiToken: ‘.....’,
	),
    );
}</code></pre>



<p>注意教程範例中，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 本身就是 concrete class，也沒有 implement 任何介面，其實不用特別寫 binding (系統會自動 resolve)。他這樣寫應該是為了餵參數進去，如果參數寫在 service 裡面，binding 這一段其實可以省掉。</p>



<p>新增一個 trait<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/Concerns/BuildsBaseRequest.php</mark></code></p>



<p>這邊用的是 Laravel HTTP Client 的用法<br><a href="https://laravel.com/docs/9.x/http-client" target="_blank" rel="noreferrer noopener">https://laravel.com/docs/9.x/http-client</a></p>



<pre class="wp-block-code"><code>trait BuildsBaseRequest
{
    public function buildRequestWithToken(): PendingRequest
    {
        //…
    }

    public function buildRequestWithDigestAuth(): PendingRequest
    {
        //…
    }
}</code></pre>



<p>新增一個 trait<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/Concerns/CanSendGetRequest.php</mark></code></p>



<pre class="wp-block-code"><code>trait CanSendGetRequest
{
    public function get(PendingRequest $request, string $url): Response
    {
        return $request-&gt;get(
    	    url: $url,
    	);
    }
}</code></pre>



<p>新增一個 trait<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/Concerns/CanSendPostRequest.php</mark></code></p>



<pre class="wp-block-code"><code>trait CanSendPostRequest
{
    public function post(PendingRequest $request, string $url, array $payload = &#91;]): Response
    {
        return $request-&gt;post(
    	    url: $url,
    	    data: $payload,
    	);
    }
}</code></pre>



<p>注意這份教程所有輸入參數到 function，都使用 PHP 8 Named Arguments 的語法。</p>



<p>還記得本文最上方新增的<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/MedicalTrust/MedicalTrustService.php</mark></code><br>此時就可以把這些 trait 都用上</p>



<pre class="wp-block-code"><code>class MedicalTrustService
{
    use BuildBaseRequest;
    use CanSendGetRequests;
    use CanSendPostRequests;
 
    // PHP 8 的 Constructor property promotion
    public function __construct(
        private readonly string $baseUrl,
        private readonly string $apiToken,
    ) {}

    // ...
}
</code></pre>



<p>注意這份教程所有 class constructor，都使用 PHP 8 Constructor property promotion 的語法。</p>



<p>新增一個 service<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Services/MedicalTrust/Resources/DentalResource.php</mark></code></p>



<pre class="wp-block-code"><code>class DentalResource
{
    public function __construct(
        private readonly MedicalTrustService $service,
    ) {}
 
    public function list(string $identifier): Response
    {
        return $this-&gt;service-&gt;get(
    	    request: $this-&gt;service-&gt;buildRequestWithToken(),
    	    url: "/dental/{$identifier}/records",
    	);
    }
 
    public function addRecord(string $identifier, array $data = &#91;]): Response
    {
        return $this-&gt;service-&gt;post(
            request: $this-&gt;service-&gt;buildRequestWithToken(),
    	    url: "/dental/{$identifier}/records",
    	    payload: $data,
    	);
    }
}
</code></pre>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 注入了 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code>，也可以說是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 再包裝 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code>。</p>



<p>注意這種寫法，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 把牙醫的 API 實作完全分離出去了。也就是如果我們要新增，例如復健科，要另新增一個 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">RehabResource.php</mark></code></p>



<p>教程的作者提到，這樣寫可以，但要輸入 payload 時，很有可能是長長一段 json 或是 array，所以教程的作者希望把 API 輸入做成 object。下面重寫了 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">function addRecord</mark></code>，把 payload 用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">NewDentalTreatment</mark></code> object 去實作 (larry 本篇重點是了解 whole picture，資料結構做成物件的細節我們先不看)。</p>



<pre class="wp-block-code"><code>class DentalResource
{
    // 省略

    public function addRecord(string $identifier, NewDentalTreatment $request): Response
    {
        return $this-&gt;service-&gt;post(
      	    request: $this-&gt;service-&gt;buildRequestWithToken(),
      	    url: "/dental/{$identifier}/records",
      	    payload: $request-&gt;toArray(),
        );
    }
}
</code></pre>



<p>在調用端 (caller side)，我們新增一個&nbsp;<br><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">app/Http/Controllers/Dental/Crowns/StoreController.php</mark></code></p>



<pre class="wp-block-code"><code>class StoreController
{
    public function __construct(
        private readonly DentalResource $api,
	private readonly DentalTreatmentFactory $factory,
    ) {}
 
    public function __invoke(NewCrownRequest $request): RedirectResponse
    {
        // do some request validation here
	// if fails, throw Exception

    	$treatment = $this-&gt;api-&gt;addRecord(
    	    identifier: $request-&gt;get('patient'),
    	    request: $this-&gt;factory-&gt;make(
  	        attributes: $request-&gt;validated(),
  	    ),
  	);

  	// ...
    }
}
</code></pre>



<p>所以整個架構是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 注入 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code>，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 注入 controller。注意 controller function 的參數是 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">NewCrownRequest</mark></code>。</p>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">NewCrownRequest</mark></code> 是 custom form request，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">$request-&gt;validated()</mark></code> 將 post 資料餵給 factory，產生 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">NewDentalTreatment</mark></code> object，餵給 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code>。</p>



<p>Again，我們這篇主要是理解 whole picture，所以 factory method 如何產生 object 這一段我們暫時不看。</p>



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



<p>基本上 get / post 等基礎工程寫在 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code>，它是一個非常通用的 service。<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 做的是針對這組 API，做 post 參數的格式，並將 payload 物件化，開有語意的 function 給 caller 端使用。將 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 注入 controller 就可以使用了。</p>



<p>本篇除了參考原教程作者串 API 的架構，另外就是參考他 PHP 的寫法。例如 trait 的使用，為什麼要用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">MedicalTrustService</mark></code> 注入 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">DentalResource</mark></code> 這樣的分層。他也大量使用 PHP 8 的新寫法 Named Arguments 以及 Constructor property promotion。輸入的 array 資料物件化的部分也可以參考一下。</p>



<p>larry 最後想提的是，我們要參考的是原教程為什麼要這樣寫的精神，而不是拘泥於它的架構。實際上我們架構如何設計與撰寫，應該還是以實際的狀況和需求來判斷。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PHP 8 的新功能與優化：Named arguments，Constructor property promotion，Nullsafe operator，Just-In-Time (JIT) compilation</title>
		<link>https://blog.yuyansoftware.com.tw/2021/09/php-8-features-optimizations/</link>
		
		<dc:creator><![CDATA[Larry]]></dc:creator>
		<pubDate>Sat, 18 Sep 2021 07:14:19 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Link4]]></category>
		<category><![CDATA[PHP 8]]></category>
		<guid isPermaLink="false">https://blog.yuyansoftware.com.tw/?p=8499</guid>

					<description><![CDATA[PHP 8 推出了一些新功能與優化，修正了 PHP 語言長期的字串 / 數字比較問題，推出了 Just-In-Time (JIT) compilation，加快執行效率。]]></description>
										<content:encoded><![CDATA[
<p>PHP 8 正式版在去年 (2020年) 11月釋出，PHP 7 的最後一個版本就是 PHP 7.4 了。PHP 8 推出了一些新功能、新寫法，修正了 PHP 語言長期的字串 / 數字比較問題，推出了 Just-In-Time (JIT) compilation，加快執行效率。本篇文章就照著官方文件的順序，來走一遍 PHP 8.0 的新功能。</p>



<p><a href="https://www.php.net/releases/8.0/en.php" target="_blank" rel="noreferrer noopener">https://www.php.net/releases/8.0/en.php</a></p>



<h4 class="wp-block-heading">Named Arguments 命名參數</h4>



<p>舉例來講，<code><span class="has-inline-color has-vivid-cyan-blue-color">function htmlspecialchars</span></code> 的 prototype</p>



<pre class="wp-block-code"><code>function htmlspecialchars(
    string $string,
    int $flags = ENT_COMPAT,
    string $encoding = null,
    bool $double_encode = true
)</code></pre>



<p>如果只是要指定其中一個參數 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">double_encode</mark></code>，其他用預設值，PHP 8 可以這樣寫</p>



<pre class="wp-block-code"><code>htmlspecialchars($string, double_encode: false);</code></pre>



<p>注意 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">flags</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">encoding</mark></code> 都不用輸入。官方文件並提到，使用 Named Arguments 時參數<strong>不需要依照順序</strong>。</p>



<p>另外，如上方範例，Named Arguments 可以跟傳統輸入參數的方式混用。可以混用又沒有參數輸入的順序限制，會不會造成 ambiguous。如果用 coding convention 去限制，又會造成開發上的成本。larry 個人是對這個新功能持保留態度。</p>



<h4 class="wp-block-heading">Constructor property promotion</h4>



<p>在 PHP 8，constructor arguments 可以直接宣告成 class 的屬性。如下範例，注意 constructor arguments 前直接加了 public modifier。</p>



<pre class="wp-block-code"><code>class Point {
  public function __construct(
    public float $x = 0.0,
    public float $y = 0.0,
    public float $z = 0.0,
  ) {}
}</code></pre>



<p>這樣 x, y, z 就直接宣告成 class 的屬性，不需要另外宣告</p>



<pre class="wp-block-code"><code>class Point {
  // 不需要
  public float $x;
  public float $y;
  public float $z;
  ...
}</code></pre>



<p>這樣的確簡化了很多程式碼，但少了明文定義 class 的屬性。這樣真的不會造成混淆嗎？而且一樣，如果用 coding convention 去限制，又會造成開發上的成本。對這個新功能我還是持保留態度。</p>



<h4 class="wp-block-heading">Match expression (match 表達式)</h4>



<pre class="wp-block-code"><code>echo match (8.0) {
  '8.0' =&gt; "Oh no!",
  8.0 =&gt; "This is what I expected",
};
// print “This is what I expected”</code></pre>



<p><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">match</mark></code> 表達式是 PHP 8 新出的。與 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">switch</mark></code> 判斷式比較，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">match</mark></code> 表達式會直接 return 數值，並用 strict comparison 做輸入/選項比較。相對的，<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">switch</mark></code> 判斷式是 loose comparison。所以如果用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">switch</mark></code>，輸入 8.0，會先找到字串的 &#8216;8.0&#8217;。</p>



<p>寫法跟應用上 larry 還是不太習慣。<code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">match</mark></code> 大括號裡的東西比較像 array 的寫法，卻用大括號包住。應用上也有點 array 的味道，那直接使用 array 相關 function 反而還清楚一點。</p>



<h4 class="wp-block-heading">Nullsafe operator</h4>



<pre class="wp-block-code"><code>$country = $session?-&gt;user?-&gt;getAddress()?-&gt;country;</code></pre>



<p>如果 <code><span class="has-inline-color has-vivid-cyan-blue-color">$session</span></code> 有值，return <code><span class="has-inline-color has-vivid-cyan-blue-color">$session-&gt;user</span></code>。如果 <code><span class="has-inline-color has-vivid-cyan-blue-color">$session-&gt;user</span></code> 有值，return <code><span class="has-inline-color has-vivid-cyan-blue-color">$session-&gt;user-&gt;getAddress()</span></code>，依此類推。從左到右，每一個問號會評估一次是否為 null，如果不為 null，繼續往右走。如果為 null，中斷在為 null 的部分，並 return null。</p>



<p>讀者可以評估一下上面 sample code，如果以 if else 來寫，或以三元運算子來寫，會有多少程式碼。用 nullsafe operator 除了簡潔以外，語意上也清楚。這個新功能 larry 會考慮使用。</p>



<h4 class="wp-block-heading">Saner string to number comparisons 較健康的字串 / 數字比較</h4>



<pre class="wp-block-code"><code>0 == 'foobar';
// PHP 7 和 PHP 7 以前會 return true
// PHP 8 會 return false</code></pre>



<p>在 PHP 7 和 PHP 7 以前，上面 sample code 比較的方式是字串 &#8220;foobar&#8221; 先嘗試 cast 成數字，發現沒辦法 cast，所以 cast 成 0，所以上面比較式 return true。</p>



<p>這部分是 PHP 語言長期的一個缺陷，尤其當使用 <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">in_array</mark></code>, <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">switch</mark></code> 等包含 implicit comparison 時，最好自己先判斷會不會有字串 / 數字比較的情況，不然 <meta charset="utf-8"></meta>implicit comparison 判斷可能有誤。</p>



<p>PHP 8 修正了這個錯誤，除了將字串 cast 成數字外，也會將要比較的數字 cast 成字串，字串對字串比較。也就是比之前多了一個比較條件。</p>



<h4 class="wp-block-heading">Just-In-Time (JIT) compilation</h4>



<p>PHP 是直譯式語言 (interpreted language)，要先裝好 PHP 套件，執行時 PHP code 會在 PHP 套件的環境裡，也可以說是透過 PHP 套件，不用編譯，直接執行。</p>



<p>PHP 8 推出的 <strong>Just-In-Time</strong> (JIT) compilation，會在執行期間，自動將某些 code 編譯，主要是 JIT 引擎分析會頻繁執行的 code<meta charset="utf-8">，進行編譯，以達成效能的最大化。</p>



<p>原本 PHP 要透過一個 Zend virtual machine 與 CPU 溝通。有了 JIT compiler，部分程式碼會被編譯，快取在記憶體裡。<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">當第二次執行該段 code，又編譯碼的快取還在的話，就會繞過 Zend virtual machine，直接執行<meta charset="utf-8">編譯碼與 CPU 溝通。</mark></p>



<p>PHP 8 的 JIT compiler 有兩種：Tracing JIT 與 Function JIT。</p>



<p><strong>Function JIT</strong> 會編譯整個 PHP functions，但不會去分析程式碼結構。<strong>Tracing JIT</strong> 則會去找到這些重複又頻繁執行的 code，進行編譯。<meta charset="utf-8">Tracing JIT 因為編譯的總量少，能加快編譯的速度 (節省主機資源)，並在有限的記憶體 buffer，可以快取對整體效能最有幫助的編譯碼。</p>



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



<p>PHP 8 大部份新寫法類型的功能，larry 是持保留態度，感覺對開發上沒有實質的幫助。PHP 8 修正了 PHP 語言長期的字串 / 數字比較問題，這件事需要知道一下。JIT 的部分也要知道，如果使用 PHP 8，<meta charset="utf-8">JIT 的部分一定要正確 configure，開啟使用。</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
