EC-CUBE / ec-cube2

EC-CUBE official repository version 2
https://www.ec-cube.net
Other
86 stars 98 forks source link

【ご意見ください】PHP8での Warning 解消方針について #1045

Open nanasess opened 3 weeks ago

nanasess commented 3 weeks ago

refs #999

現在、 PHP8 で発生している Warning も解消していきたいのですが、商品一覧ページを対応してみたところ、以下のパッチのようなコードになってしまいます。 特に $arrErr の未定義要素のチェックに三項演算子を使用していますが、もう少し簡潔な記述ができないものかと悩んでおります。 良いアイディアがありましたらご意見ください🙇‍♂️

diff --git a/data/Smarty/templates/default/products/list.tpl b/data/Smarty/templates/default/products/list.tpl
index 79300d2b6..03cc18850 100644
--- a/data/Smarty/templates/default/products/list.tpl
+++ b/data/Smarty/templates/default/products/list.tpl
@@ -139,7 +139,7 @@
         <!--{/if}-->

         <!--{assign var=id value=$arrProduct.product_id}-->
-        <!--{assign var=arrErr value=$arrProduct.arrErr}-->
+        <!--{assign var=arrErr value=$arrProduct.arrErr|isset ? $arrProduct.arrErr : []}-->
         <!--▼商品-->
         <form name="product_form<!--{$id|h}-->" action="?">
             <input type="hidden" name="<!--{$smarty.const.TRANSACTION_ID_NAME}-->" value="<!--{$transactionid}-->" />
@@ -204,10 +204,10 @@
                                             <!--▼規格1-->
                                             <dt><!--{$tpl_class_name1[$id]|h}-->:</dt>
                                             <dd>
-                                                <select name="classcategory_id1" style="<!--{$arrErr.classcategory_id1|sfGetErrorColor}-->">
-                                                    <!--{html_options options=$arrClassCat1[$id] selected=$arrProduct.classcategory_id1}-->
+                                                <select name="classcategory_id1" style="<!--{sfGetErrorColor($arrErr.classcategory_id1|isset ? $arrErr.classcategory_id1 : '')}-->">
+                                                    <!--{html_options options=$arrClassCat1[$id] selected=$arrProduct.classcategory_id1|isset ? $arrProduct.classcategory_id1 : null}-->
                                                 </select>
-                                                <!--{if $arrErr.classcategory_id1 != ""}-->
+                                                <!--{if $arrErr.classcategory_id1|isset}-->
                                                     <p class="attention">※ <!--{$tpl_class_name1[$id]}-->を入力して下さい。</p>
                                                 <!--{/if}-->
                                             </dd>
@@ -218,9 +218,9 @@
                                             <!--▼規格2-->
                                             <dt><!--{$tpl_class_name2[$id]|h}-->:</dt>
                                             <dd>
-                                                <select name="classcategory_id2" style="<!--{$arrErr.classcategory_id2|sfGetErrorColor}-->">
+                                                <select name="classcategory_id2" style="<!--{sfGetErrorColor($arrErr.classcategory_id2|isset ? $arrErr.classcategory_id2 : '')}-->">
                                                 </select>
-                                                <!--{if $arrErr.classcategory_id2 != ""}-->
+                                                <!--{if $arrErr.classcategory_id2|isset}-->
                                                     <p class="attention">※ <!--{$tpl_class_name2[$id]}-->を入力して下さい。</p>
                                                 <!--{/if}-->
                                             </dd>
@@ -231,8 +231,8 @@
                             <!--{/if}-->
                             <div class="cartin clearfix">
                                 <div class="quantity">
-                                    数量:<input type="text" name="quantity" class="box" value="<!--{$arrProduct.quantity|default:1|h}-->" maxlength="<!--{$smarty.const.INT_LEN}-->" style="<!--{$arrErr.quantity|sfGetErrorColor}-->" />
-                                    <!--{if $arrErr.quantity != ""}-->
+                                    数量:<input type="text" name="quantity" class="box" value="<!--{$arrProduct.quantity|default:1|h}-->" maxlength="<!--{$smarty.const.INT_LEN}-->" style="<!--{$arrErr.quantity|isset ? $arrErr.quantity : null|sfGetErrorColor}-->" />
+                                    <!--{if $arrErr.quantity|isset}-->
                                         <br /><span class="attention"><!--{$arrErr.quantity}--></span>
                                     <!--{/if}-->
                                 </div>
diff --git a/data/class/SC_CartSession.php b/data/class/SC_CartSession.php
index 645d99764..7e938a0d0 100644
--- a/data/class/SC_CartSession.php
+++ b/data/class/SC_CartSession.php
@@ -157,6 +157,22 @@ class SC_CartSession
                     }
                 }
             }
+        } else {
+            $this->cartSession[$product_type_id] = [];
+        }
+
+        // カート内商品の最大要素番号までの要素が存在しない場合、要素を追加しておく
+        for ($i = 0; $i <= $max; $i++) {
+            if (!array_key_exists($i, $this->cartSession[$product_type_id])) {
+                $this->cartSession[$product_type_id][$i] = [
+                    'price' => 0,
+                    'quantity' => 0,
+                    'productsClass' => [
+                        'product_id' => null,
+                        'product_class_id' => null,
+                    ],
+                ];
+            }
         }

         return $max;
diff --git a/data/class/helper/SC_Helper_Maker.php b/data/class/helper/SC_Helper_Maker.php
index 3370ca6df..2f989797b 100644
--- a/data/class/helper/SC_Helper_Maker.php
+++ b/data/class/helper/SC_Helper_Maker.php
@@ -45,9 +45,9 @@ class SC_Helper_Maker
         if (!$has_deleted) {
             $where .= ' AND del_flg = 0';
         }
-        $arrRet = $objQuery->select('*', 'dtb_maker', $where, [$maker_id]);
+        $arrRet = $objQuery->getRow('*', 'dtb_maker', $where, [$maker_id]);

-        return $arrRet[0];
+        return $arrRet;
     }

     /**
diff --git a/data/class/pages/products/LC_Page_Products_List.php b/data/class/pages/products/LC_Page_Products_List.php
index c97724b12..6d740a387 100644
--- a/data/class/pages/products/LC_Page_Products_List.php
+++ b/data/class/pages/products/LC_Page_Products_List.php
@@ -338,7 +338,7 @@ class LC_Page_Products_List extends LC_Page_Ex
         if (strlen($arrSearchData['maker_id']) > 0) {
             $objMaker = new SC_Helper_Maker_Ex();
             $maker = $objMaker->getMaker($arrSearchData['maker_id']);
-            $arrSearch['maker'] = $maker['name'];
+            $arrSearch['maker'] = isset($maker['name']) ? $maker['name'] : null;
         }

         // 商品名検索条件
nanasess commented 3 weeks ago

個別の案件では、 <!--{if $arrErr.quantity != ""}--> でエラーの有無をチェックせずに、<span class="attention"><!--{$arrErr.quantity}--></span> が直接埋め込まれているコードが大量にあったため、以下のような modifier を作成しました

/**
 * $arrErrに対してのみ利用する想定のmodifier
 * $arrErrの中から指定したKeyのエラーを探して返す
 *
 * php8でのWarning対応のために作成。
 * エラーが発生したときのみ$arrErrにKeyが設定される設計なので、テンプレート側でKeyが含まれない可能性を考慮する必要がある。
 * 用途を$arrErrに限定することを明示するためにfind_errというmodifier名にしている。
 *
 * @param ?array $arrErr $arrErr配列。 nullのケースがありうる。
 * @param string $key 探したいエラーのKey。
 *
 * @return string Keyに該当するValueを返す。Keyが存在しない場合はnullを返す。
 *
 * @example <span class="attention"><!--{$arrErr|find_err:"shop_kana"}--></span>
 */
function smarty_modifier_find_err(?array $arrErr, string $key)
{
    if (!is_array($arrErr)) {
        return null;
    }

    return $arrErr[$key] ?? null;
}

これなら簡潔に書けて正規表現で一括置換もできるので便利なのですが、 modifier の名前がいまいちなのと、直感的に何をしているか解りづらいので却下かなぁと。

nanasess commented 3 weeks ago

|default を使えばずいぶん簡潔にできそう。 $arrErr が null の場合があるので、そこだけ PHP で空の配列を作成しておく必要がありそう。

thx: https://x.com/tadsan/status/1850198026817823213

diff --git a/data/Smarty/templates/default/products/list.tpl b/data/Smarty/templates/default/products/list.tpl
index 79300d2b6..24deaeae6 100644
--- a/data/Smarty/templates/default/products/list.tpl
+++ b/data/Smarty/templates/default/products/list.tpl
@@ -139,7 +139,7 @@
         <!--{/if}-->

         <!--{assign var=id value=$arrProduct.product_id}-->
-        <!--{assign var=arrErr value=$arrProduct.arrErr}-->
+        <!--{assign var=arrErr value=$arrProduct.arrErr|default:[]}-->
         <!--▼商品-->
         <form name="product_form<!--{$id|h}-->" action="?">
             <input type="hidden" name="<!--{$smarty.const.TRANSACTION_ID_NAME}-->" value="<!--{$transactionid}-->" />
@@ -204,10 +204,10 @@
                                             <!--▼規格1-->
                                             <dt><!--{$tpl_class_name1[$id]|h}-->:</dt>
                                             <dd>
-                                                <select name="classcategory_id1" style="<!--{$arrErr.classcategory_id1|sfGetErrorColor}-->">
-                                                    <!--{html_options options=$arrClassCat1[$id] selected=$arrProduct.classcategory_id1}-->
+                                                <select name="classcategory_id1" style="<!--{sfGetErrorColor($arrErr.classcategory_id1|default:'')}-->">
+                                                    <!--{html_options options=$arrClassCat1[$id] selected=$arrProduct.classcategory_id1|default:''}-->
                                                 </select>
-                                                <!--{if $arrErr.classcategory_id1 != ""}-->
+                                                <!--{if $arrErr.classcategory_id1|isset}-->
                                                     <p class="attention">※ <!--{$tpl_class_name1[$id]}-->を入力して下さい。</p>
                                                 <!--{/if}-->
                                             </dd>
@@ -218,9 +218,9 @@
                                             <!--▼規格2-->
                                             <dt><!--{$tpl_class_name2[$id]|h}-->:</dt>
                                             <dd>
-                                                <select name="classcategory_id2" style="<!--{$arrErr.classcategory_id2|sfGetErrorColor}-->">
+                                                <select name="classcategory_id2" style="<!--{sfGetErrorColor($arrErr.classcategory_id2|default:'')}-->">
                                                 </select>
-                                                <!--{if $arrErr.classcategory_id2 != ""}-->
+                                                <!--{if $arrErr.classcategory_id2|isset}-->
                                                     <p class="attention">※ <!--{$tpl_class_name2[$id]}-->を入力して下さい。</p>
                                                 <!--{/if}-->
                                             </dd>
@@ -231,8 +231,8 @@
                             <!--{/if}-->
                             <div class="cartin clearfix">
                                 <div class="quantity">
-                                    数量:<input type="text" name="quantity" class="box" value="<!--{$arrProduct.quantity|default:1|h}-->" maxlength="<!--{$smarty.const.INT_LEN}-->" style="<!--{$arrErr.quantity|sfGetErrorColor}-->" />
-                                    <!--{if $arrErr.quantity != ""}-->
+                                    数量:<input type="text" name="quantity" class="box" value="<!--{$arrProduct.quantity|default:1|h}-->" maxlength="<!--{$smarty.const.INT_LEN}-->" style="<!--{sfGetErrorColor($arrErr.quantity|default:'')}-->" />
+                                    <!--{if $arrErr.quantity|isset}-->
                                         <br /><span class="attention"><!--{$arrErr.quantity}--></span>
                                     <!--{/if}-->
                                 </div>
diff --git a/data/class/SC_CartSession.php b/data/class/SC_CartSession.php
index 645d99764..7e938a0d0 100644
--- a/data/class/SC_CartSession.php
+++ b/data/class/SC_CartSession.php
@@ -157,6 +157,22 @@ class SC_CartSession
                     }
                 }
             }
+        } else {
+            $this->cartSession[$product_type_id] = [];
+        }
+
+        // カート内商品の最大要素番号までの要素が存在しない場合、要素を追加しておく
+        for ($i = 0; $i <= $max; $i++) {
+            if (!array_key_exists($i, $this->cartSession[$product_type_id])) {
+                $this->cartSession[$product_type_id][$i] = [
+                    'price' => 0,
+                    'quantity' => 0,
+                    'productsClass' => [
+                        'product_id' => null,
+                        'product_class_id' => null,
+                    ],
+                ];
+            }
         }

         return $max;
diff --git a/data/class/helper/SC_Helper_Maker.php b/data/class/helper/SC_Helper_Maker.php
index 3370ca6df..2f989797b 100644
--- a/data/class/helper/SC_Helper_Maker.php
+++ b/data/class/helper/SC_Helper_Maker.php
@@ -45,9 +45,9 @@ class SC_Helper_Maker
         if (!$has_deleted) {
             $where .= ' AND del_flg = 0';
         }
-        $arrRet = $objQuery->select('*', 'dtb_maker', $where, [$maker_id]);
+        $arrRet = $objQuery->getRow('*', 'dtb_maker', $where, [$maker_id]);

-        return $arrRet[0];
+        return $arrRet;
     }

     /**
diff --git a/data/class/pages/products/LC_Page_Products_List.php b/data/class/pages/products/LC_Page_Products_List.php
index c97724b12..6d740a387 100644
--- a/data/class/pages/products/LC_Page_Products_List.php
+++ b/data/class/pages/products/LC_Page_Products_List.php
@@ -338,7 +338,7 @@ class LC_Page_Products_List extends LC_Page_Ex
         if (strlen($arrSearchData['maker_id']) > 0) {
             $objMaker = new SC_Helper_Maker_Ex();
             $maker = $objMaker->getMaker($arrSearchData['maker_id']);
-            $arrSearch['maker'] = $maker['name'];
+            $arrSearch['maker'] = isset($maker['name']) ? $maker['name'] : null;
         }

         // 商品名検索条件
seasoftjapan commented 3 weeks ago

$arrSearch['maker'] = isset($maker['name']) ? $maker['name'] : null; は、Null 合体演算子で書けません?

seasoftjapan commented 3 weeks ago

$arrErr.quantity とかは、Smarty でなんとかして欲しい気もしますが、そんな動きは無いですかね・・・

nanasess commented 3 weeks ago

@seasoftjapan

$arrSearch['maker'] = isset($maker['name']) ? $maker['name'] : null; は、Null 合体演算子で書けません?

$arrSearch['maker'] = $maker['name'] ?? null;

でいけますね。ありがとうございます!

nanasess commented 3 weeks ago

@seasoftjapan

$arrErr.quantity とかは、Smarty でなんとかして欲しい気もしますが、そんな動きは無いですかね・・・

Smarty の中の人は、

といった対応を推奨しているようです。 https://github.com/smarty-php/smarty/discussions/749#discussioncomment-3426453

$arrErr は値を入れておくわけにもいかないので悩ましいですね

seasoftjapan commented 3 weeks ago

| 面倒なら黙らせる

diff --git data/class/SC_View.php data/class/SC_View.php
index bab1244bc..26854e0cd 100644
--- data/class/SC_View.php
+++ data/class/SC_View.php
@@ -85,6 +85,7 @@ public function init()
         $this->registFilter();
         // smarty:nodefaultsの後方互換を維持
         $this->_smarty->registerFilter('pre', [$this, 'lower_compatibility_smarty']);
+        $this->_smarty->muteUndefinedOrNullWarnings();
     }

     // テンプレートに値を割り当てる

黙りますね。(PHP 8.2 で確認。)

default 修飾子でテンプレートの記述量を増やすよりは、サクッと黙らせた方が良い気もしますがどうでしょうか。

実行コストが若干気になりますが、商品一覧画面でざっとトレースした感じですと、少なくとも現状のように Warning をログに吐く状態よりはマシな様子です。

nanasess commented 3 weeks ago

@seasoftjapan 今後、 Warning が Fatal へ昇格する可能性を考えると、できるだけ対応しておきたい想いはあります。 とはいえ、現状は 2.13 からのバージョンアップが多いことを考えると、デフォルトでは黙らせておいた方がよさそうですね。 tpl が1000ページくらいあったりすると、さすがに対応するのしんどいので。。。

seasoftjapan commented 3 weeks ago

確かに、今の Smarty の実装だと、Fatal になったら動作しなそうですね。 技術的には PHP コードにコンパイルするときに対処できるはずなので、個人的にはそう舵切りして欲しいものですが。

Smarty 5 は null 合体演算子にも対応しているようですので、これを使える段階になったら、回避方法に新たな選択肢が加わりそうに思います。

ちなみに、Smarty のコード読んだら |default:'' は、 |default でも良いみたいですね。

default_modifiers に default を加えるとかも妄想しましたが、流石に現状では muteUndefinedOrNullWarnings() の方がマシだと思います。

nanasess commented 3 weeks ago

@seasoftjapan

Smarty 5 は null 合体演算子にも対応しているようですので、

いま、 Smarty 5.4.1 なので使えると思います!

seasoftjapan commented 3 weeks ago

いま、 Smarty 5.4.1 なので使えると思います!

Smarty 5 は PHP 7 サポート外で、Smarty 4 かと思っていました。 composer.lock 確認したら、確かに 5.4.1 ですね。

今の時点で Warning 回避のために大量に書き足すのは抵抗ありますが、いよいよ Fatal になって、その時点の muteUndefinedOrNullWarnings() も駄目となったら、default で書き足すよりも意図が分かりやすそうですね。パフォーマンスの影響も少なそうですし。

bbkids commented 3 weeks ago

PHP8 以降で大量に発生する Warning の対処で、 個人的には、ちまちまと 三項演算子 や |default:'' を駆使しまくって対処しておりましたが、 $this->_smarty->muteUndefinedOrNullWarnings();で、黙らせる事ができるんですね!すばらしいですね。

それほど遠くない今後Warning から Fatal への昇格がぞくぞくとありえるので、 実運用では$this->_smarty->muteUndefinedOrNullWarnings();で黙らせつつ、 ここではサクッと対処出来るところから順次対処して頂いた方がありがたいです。 かなりボリュームがあると思いますが。

個人的に対処すればいいのではという考えもあるのですが、 公式で対処していかないと決済モジュール等も追随してくれないので、少しずつでも公式ここで対処して欲しいというのが希望です。

併せてWeeklyビルドだと、決済モジュール側が追随してくれないので年一でもよいので版を上げて欲しいです。

nanasess commented 1 week ago

null 合体演算子(??) を利用すると以下のようなパッチとなります。

diff --git a/data/Smarty/templates/default/products/list.tpl b/data/Smarty/templates/default/products/list.tpl
index 79300d2b6..388168579 100644
--- a/data/Smarty/templates/default/products/list.tpl
+++ b/data/Smarty/templates/default/products/list.tpl
@@ -139,7 +139,7 @@
         <!--{/if}-->

         <!--{assign var=id value=$arrProduct.product_id}-->
-        <!--{assign var=arrErr value=$arrProduct.arrErr}-->
+        <!--{assign var=arrErr value=$arrProduct.arrErr ?? []}-->
         <!--▼商品-->
         <form name="product_form<!--{$id|h}-->" action="?">
             <input type="hidden" name="<!--{$smarty.const.TRANSACTION_ID_NAME}-->" value="<!--{$transactionid}-->" />
@@ -204,10 +204,10 @@
                                             <!--▼規格1-->
                                             <dt><!--{$tpl_class_name1[$id]|h}-->:</dt>
                                             <dd>
-                                                <select name="classcategory_id1" style="<!--{$arrErr.classcategory_id1|sfGetErrorColor}-->">
-                                                    <!--{html_options options=$arrClassCat1[$id] selected=$arrProduct.classcategory_id1}-->
+                                                <select name="classcategory_id1" style="<!--{sfGetErrorColor($arrErr.classcategory_id1 ?? '')}-->">
+                                                    <!--{html_options options=$arrClassCat1[$id] selected=$arrProduct.classcategory_id1 ?? ''}-->
                                                 </select>
-                                                <!--{if $arrErr.classcategory_id1 != ""}-->
+                                                <!--{if $arrErr.classcategory_id1|isset}-->
                                                     <p class="attention">※ <!--{$tpl_class_name1[$id]}-->を入力して下さい。</p>
                                                 <!--{/if}-->
                                             </dd>
@@ -218,9 +218,9 @@
                                             <!--▼規格2-->
                                             <dt><!--{$tpl_class_name2[$id]|h}-->:</dt>
                                             <dd>
-                                                <select name="classcategory_id2" style="<!--{$arrErr.classcategory_id2|sfGetErrorColor}-->">
+                                                <select name="classcategory_id2" style="<!--{sfGetErrorColor($arrErr.classcategory_id2 ?? '')}-->">
                                                 </select>
-                                                <!--{if $arrErr.classcategory_id2 != ""}-->
+                                                <!--{if $arrErr.classcategory_id2|isset}-->
                                                     <p class="attention">※ <!--{$tpl_class_name2[$id]}-->を入力して下さい。</p>
                                                 <!--{/if}-->
                                             </dd>
@@ -231,8 +231,8 @@
                             <!--{/if}-->
                             <div class="cartin clearfix">
                                 <div class="quantity">
-                                    数量:<input type="text" name="quantity" class="box" value="<!--{$arrProduct.quantity|default:1|h}-->" maxlength="<!--{$smarty.const.INT_LEN}-->" style="<!--{$arrErr.quantity|sfGetErrorColor}-->" />
-                                    <!--{if $arrErr.quantity != ""}-->
+                                    数量:<input type="text" name="quantity" class="box" value="<!--{$arrProduct.quantity|default:1|h}-->" maxlength="<!--{$smarty.const.INT_LEN}-->" style="<!--{sfGetErrorColor($arrErr.quantity ?? '')}-->" />
+                                    <!--{if $arrErr.quantity|isset}-->
                                         <br /><span class="attention"><!--{$arrErr.quantity}--></span>
                                     <!--{/if}-->
                                 </div>
diff --git a/data/class/SC_CartSession.php b/data/class/SC_CartSession.php
index 645d99764..7e938a0d0 100644
--- a/data/class/SC_CartSession.php
+++ b/data/class/SC_CartSession.php
@@ -157,6 +157,22 @@ class SC_CartSession
                     }
                 }
             }
+        } else {
+            $this->cartSession[$product_type_id] = [];
+        }
+
+        // カート内商品の最大要素番号までの要素が存在しない場合、要素を追加しておく
+        for ($i = 0; $i <= $max; $i++) {
+            if (!array_key_exists($i, $this->cartSession[$product_type_id])) {
+                $this->cartSession[$product_type_id][$i] = [
+                    'price' => 0,
+                    'quantity' => 0,
+                    'productsClass' => [
+                        'product_id' => null,
+                        'product_class_id' => null,
+                    ],
+                ];
+            }
         }

         return $max;
diff --git a/data/class/helper/SC_Helper_Maker.php b/data/class/helper/SC_Helper_Maker.php
index 3370ca6df..2f989797b 100644
--- a/data/class/helper/SC_Helper_Maker.php
+++ b/data/class/helper/SC_Helper_Maker.php
@@ -45,9 +45,9 @@ class SC_Helper_Maker
         if (!$has_deleted) {
             $where .= ' AND del_flg = 0';
         }
-        $arrRet = $objQuery->select('*', 'dtb_maker', $where, [$maker_id]);
+        $arrRet = $objQuery->getRow('*', 'dtb_maker', $where, [$maker_id]);

-        return $arrRet[0];
+        return $arrRet;
     }

     /**
diff --git a/data/class/pages/products/LC_Page_Products_List.php b/data/class/pages/products/LC_Page_Products_List.php
index c97724b12..43f663bd5 100644
--- a/data/class/pages/products/LC_Page_Products_List.php
+++ b/data/class/pages/products/LC_Page_Products_List.php
@@ -338,7 +338,7 @@ class LC_Page_Products_List extends LC_Page_Ex
         if (strlen($arrSearchData['maker_id']) > 0) {
             $objMaker = new SC_Helper_Maker_Ex();
             $maker = $objMaker->getMaker($arrSearchData['maker_id']);
-            $arrSearch['maker'] = $maker['name'];
+            $arrSearch['maker'] = $maker['name'] ?? null;
         }

         // 商品名検索条件
nanasess commented 1 week ago

方針をまとめると、以下のような感じですね